Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * extcon-axp288.c - X-Power AXP288 PMIC extcon cable detection driver
0004  *
0005  * Copyright (c) 2017-2018 Hans de Goede <hdegoede@redhat.com>
0006  * Copyright (C) 2015 Intel Corporation
0007  * Author: Ramakrishna Pallala <ramakrishna.pallala@intel.com>
0008  */
0009 
0010 #include <linux/acpi.h>
0011 #include <linux/module.h>
0012 #include <linux/kernel.h>
0013 #include <linux/io.h>
0014 #include <linux/slab.h>
0015 #include <linux/interrupt.h>
0016 #include <linux/platform_device.h>
0017 #include <linux/property.h>
0018 #include <linux/notifier.h>
0019 #include <linux/extcon-provider.h>
0020 #include <linux/regmap.h>
0021 #include <linux/mfd/axp20x.h>
0022 #include <linux/usb/role.h>
0023 #include <linux/workqueue.h>
0024 
0025 #include <asm/cpu_device_id.h>
0026 #include <asm/intel-family.h>
0027 #include <asm/iosf_mbi.h>
0028 
0029 /* Power source status register */
0030 #define PS_STAT_VBUS_TRIGGER        BIT(0)
0031 #define PS_STAT_BAT_CHRG_DIR        BIT(2)
0032 #define PS_STAT_VBUS_ABOVE_VHOLD    BIT(3)
0033 #define PS_STAT_VBUS_VALID      BIT(4)
0034 #define PS_STAT_VBUS_PRESENT        BIT(5)
0035 
0036 /* BC module global register */
0037 #define BC_GLOBAL_RUN           BIT(0)
0038 #define BC_GLOBAL_DET_STAT      BIT(2)
0039 #define BC_GLOBAL_DBP_TOUT      BIT(3)
0040 #define BC_GLOBAL_VLGC_COM_SEL      BIT(4)
0041 #define BC_GLOBAL_DCD_TOUT_MASK     (BIT(6)|BIT(5))
0042 #define BC_GLOBAL_DCD_TOUT_300MS    0
0043 #define BC_GLOBAL_DCD_TOUT_100MS    1
0044 #define BC_GLOBAL_DCD_TOUT_500MS    2
0045 #define BC_GLOBAL_DCD_TOUT_900MS    3
0046 #define BC_GLOBAL_DCD_DET_SEL       BIT(7)
0047 
0048 /* BC module vbus control and status register */
0049 #define VBUS_CNTL_DPDM_PD_EN        BIT(4)
0050 #define VBUS_CNTL_DPDM_FD_EN        BIT(5)
0051 #define VBUS_CNTL_FIRST_PO_STAT     BIT(6)
0052 
0053 /* BC USB status register */
0054 #define USB_STAT_BUS_STAT_MASK      (BIT(3)|BIT(2)|BIT(1)|BIT(0))
0055 #define USB_STAT_BUS_STAT_SHIFT     0
0056 #define USB_STAT_BUS_STAT_ATHD      0
0057 #define USB_STAT_BUS_STAT_CONN      1
0058 #define USB_STAT_BUS_STAT_SUSP      2
0059 #define USB_STAT_BUS_STAT_CONF      3
0060 #define USB_STAT_USB_SS_MODE        BIT(4)
0061 #define USB_STAT_DEAD_BAT_DET       BIT(6)
0062 #define USB_STAT_DBP_UNCFG      BIT(7)
0063 
0064 /* BC detect status register */
0065 #define DET_STAT_MASK           (BIT(7)|BIT(6)|BIT(5))
0066 #define DET_STAT_SHIFT          5
0067 #define DET_STAT_SDP            1
0068 #define DET_STAT_CDP            2
0069 #define DET_STAT_DCP            3
0070 
0071 enum axp288_extcon_reg {
0072     AXP288_PS_STAT_REG      = 0x00,
0073     AXP288_PS_BOOT_REASON_REG   = 0x02,
0074     AXP288_BC_GLOBAL_REG        = 0x2c,
0075     AXP288_BC_VBUS_CNTL_REG     = 0x2d,
0076     AXP288_BC_USB_STAT_REG      = 0x2e,
0077     AXP288_BC_DET_STAT_REG      = 0x2f,
0078 };
0079 
0080 enum axp288_extcon_irq {
0081     VBUS_FALLING_IRQ = 0,
0082     VBUS_RISING_IRQ,
0083     MV_CHNG_IRQ,
0084     BC_USB_CHNG_IRQ,
0085     EXTCON_IRQ_END,
0086 };
0087 
0088 static const unsigned int axp288_extcon_cables[] = {
0089     EXTCON_CHG_USB_SDP,
0090     EXTCON_CHG_USB_CDP,
0091     EXTCON_CHG_USB_DCP,
0092     EXTCON_USB,
0093     EXTCON_NONE,
0094 };
0095 
0096 struct axp288_extcon_info {
0097     struct device *dev;
0098     struct regmap *regmap;
0099     struct regmap_irq_chip_data *regmap_irqc;
0100     struct usb_role_switch *role_sw;
0101     struct work_struct role_work;
0102     int irq[EXTCON_IRQ_END];
0103     struct extcon_dev *edev;
0104     struct extcon_dev *id_extcon;
0105     struct notifier_block id_nb;
0106     unsigned int previous_cable;
0107     bool vbus_attach;
0108 };
0109 
0110 static const struct x86_cpu_id cherry_trail_cpu_ids[] = {
0111     X86_MATCH_INTEL_FAM6_MODEL(ATOM_AIRMONT,    NULL),
0112     {}
0113 };
0114 
0115 /* Power up/down reason string array */
0116 static const char * const axp288_pwr_up_down_info[] = {
0117     "Last wake caused by user pressing the power button",
0118     "Last wake caused by a charger insertion",
0119     "Last wake caused by a battery insertion",
0120     "Last wake caused by SOC initiated global reset",
0121     "Last wake caused by cold reset",
0122     "Last shutdown caused by PMIC UVLO threshold",
0123     "Last shutdown caused by SOC initiated cold off",
0124     "Last shutdown caused by user pressing the power button",
0125 };
0126 
0127 /*
0128  * Decode and log the given "reset source indicator" (rsi)
0129  * register and then clear it.
0130  */
0131 static void axp288_extcon_log_rsi(struct axp288_extcon_info *info)
0132 {
0133     unsigned int val, i, clear_mask = 0;
0134     unsigned long bits;
0135     int ret;
0136 
0137     ret = regmap_read(info->regmap, AXP288_PS_BOOT_REASON_REG, &val);
0138     if (ret < 0) {
0139         dev_err(info->dev, "failed to read reset source indicator\n");
0140         return;
0141     }
0142 
0143     bits = val & GENMASK(ARRAY_SIZE(axp288_pwr_up_down_info) - 1, 0);
0144     for_each_set_bit(i, &bits, ARRAY_SIZE(axp288_pwr_up_down_info))
0145         dev_dbg(info->dev, "%s\n", axp288_pwr_up_down_info[i]);
0146     clear_mask = bits;
0147 
0148     /* Clear the register value for next reboot (write 1 to clear bit) */
0149     regmap_write(info->regmap, AXP288_PS_BOOT_REASON_REG, clear_mask);
0150 }
0151 
0152 /*
0153  * The below code to control the USB role-switch on devices with an AXP288
0154  * may seem out of place, but there are 2 reasons why this is the best place
0155  * to control the USB role-switch on such devices:
0156  * 1) On many devices the USB role is controlled by AML code, but the AML code
0157  *    only switches between the host and none roles, because of Windows not
0158  *    really using device mode. To make device mode work we need to toggle
0159  *    between the none/device roles based on Vbus presence, and this driver
0160  *    gets interrupts on Vbus insertion / removal.
0161  * 2) In order for our BC1.2 charger detection to work properly the role
0162  *    mux must be properly set to device mode before we do the detection.
0163  */
0164 
0165 /* Returns the id-pin value, note pulled low / false == host-mode */
0166 static bool axp288_get_id_pin(struct axp288_extcon_info *info)
0167 {
0168     enum usb_role role;
0169 
0170     if (info->id_extcon)
0171         return extcon_get_state(info->id_extcon, EXTCON_USB_HOST) <= 0;
0172 
0173     /* We cannot access the id-pin, see what mode the AML code has set */
0174     role = usb_role_switch_get_role(info->role_sw);
0175     return role != USB_ROLE_HOST;
0176 }
0177 
0178 static void axp288_usb_role_work(struct work_struct *work)
0179 {
0180     struct axp288_extcon_info *info =
0181         container_of(work, struct axp288_extcon_info, role_work);
0182     enum usb_role role;
0183     bool id_pin;
0184     int ret;
0185 
0186     id_pin = axp288_get_id_pin(info);
0187     if (!id_pin)
0188         role = USB_ROLE_HOST;
0189     else if (info->vbus_attach)
0190         role = USB_ROLE_DEVICE;
0191     else
0192         role = USB_ROLE_NONE;
0193 
0194     ret = usb_role_switch_set_role(info->role_sw, role);
0195     if (ret)
0196         dev_err(info->dev, "failed to set role: %d\n", ret);
0197 }
0198 
0199 static bool axp288_get_vbus_attach(struct axp288_extcon_info *info)
0200 {
0201     int ret, pwr_stat;
0202 
0203     ret = regmap_read(info->regmap, AXP288_PS_STAT_REG, &pwr_stat);
0204     if (ret < 0) {
0205         dev_err(info->dev, "failed to read vbus status\n");
0206         return false;
0207     }
0208 
0209     return !!(pwr_stat & PS_STAT_VBUS_VALID);
0210 }
0211 
0212 static int axp288_handle_chrg_det_event(struct axp288_extcon_info *info)
0213 {
0214     int ret, stat, cfg;
0215     u8 chrg_type;
0216     unsigned int cable = info->previous_cable;
0217     bool vbus_attach = false;
0218 
0219     ret = iosf_mbi_block_punit_i2c_access();
0220     if (ret < 0)
0221         return ret;
0222 
0223     vbus_attach = axp288_get_vbus_attach(info);
0224     if (!vbus_attach)
0225         goto no_vbus;
0226 
0227     /* Check charger detection completion status */
0228     ret = regmap_read(info->regmap, AXP288_BC_GLOBAL_REG, &cfg);
0229     if (ret < 0)
0230         goto dev_det_ret;
0231     if (cfg & BC_GLOBAL_DET_STAT) {
0232         dev_dbg(info->dev, "can't complete the charger detection\n");
0233         goto dev_det_ret;
0234     }
0235 
0236     ret = regmap_read(info->regmap, AXP288_BC_DET_STAT_REG, &stat);
0237     if (ret < 0)
0238         goto dev_det_ret;
0239 
0240     chrg_type = (stat & DET_STAT_MASK) >> DET_STAT_SHIFT;
0241 
0242     switch (chrg_type) {
0243     case DET_STAT_SDP:
0244         dev_dbg(info->dev, "sdp cable is connected\n");
0245         cable = EXTCON_CHG_USB_SDP;
0246         break;
0247     case DET_STAT_CDP:
0248         dev_dbg(info->dev, "cdp cable is connected\n");
0249         cable = EXTCON_CHG_USB_CDP;
0250         break;
0251     case DET_STAT_DCP:
0252         dev_dbg(info->dev, "dcp cable is connected\n");
0253         cable = EXTCON_CHG_USB_DCP;
0254         break;
0255     default:
0256         dev_warn(info->dev, "unknown (reserved) bc detect result\n");
0257         cable = EXTCON_CHG_USB_SDP;
0258     }
0259 
0260 no_vbus:
0261     iosf_mbi_unblock_punit_i2c_access();
0262 
0263     extcon_set_state_sync(info->edev, info->previous_cable, false);
0264     if (info->previous_cable == EXTCON_CHG_USB_SDP)
0265         extcon_set_state_sync(info->edev, EXTCON_USB, false);
0266 
0267     if (vbus_attach) {
0268         extcon_set_state_sync(info->edev, cable, vbus_attach);
0269         if (cable == EXTCON_CHG_USB_SDP)
0270             extcon_set_state_sync(info->edev, EXTCON_USB,
0271                         vbus_attach);
0272 
0273         info->previous_cable = cable;
0274     }
0275 
0276     if (info->role_sw && info->vbus_attach != vbus_attach) {
0277         info->vbus_attach = vbus_attach;
0278         /* Setting the role can take a while */
0279         queue_work(system_long_wq, &info->role_work);
0280     }
0281 
0282     return 0;
0283 
0284 dev_det_ret:
0285     iosf_mbi_unblock_punit_i2c_access();
0286 
0287     if (ret < 0)
0288         dev_err(info->dev, "failed to detect BC Mod\n");
0289 
0290     return ret;
0291 }
0292 
0293 static int axp288_extcon_id_evt(struct notifier_block *nb,
0294                 unsigned long event, void *param)
0295 {
0296     struct axp288_extcon_info *info =
0297         container_of(nb, struct axp288_extcon_info, id_nb);
0298 
0299     /* We may not sleep and setting the role can take a while */
0300     queue_work(system_long_wq, &info->role_work);
0301 
0302     return NOTIFY_OK;
0303 }
0304 
0305 static irqreturn_t axp288_extcon_isr(int irq, void *data)
0306 {
0307     struct axp288_extcon_info *info = data;
0308     int ret;
0309 
0310     ret = axp288_handle_chrg_det_event(info);
0311     if (ret < 0)
0312         dev_err(info->dev, "failed to handle the interrupt\n");
0313 
0314     return IRQ_HANDLED;
0315 }
0316 
0317 static int axp288_extcon_enable(struct axp288_extcon_info *info)
0318 {
0319     int ret = 0;
0320 
0321     ret = iosf_mbi_block_punit_i2c_access();
0322     if (ret < 0)
0323         return ret;
0324 
0325     regmap_update_bits(info->regmap, AXP288_BC_GLOBAL_REG,
0326                         BC_GLOBAL_RUN, 0);
0327     /* Enable the charger detection logic */
0328     regmap_update_bits(info->regmap, AXP288_BC_GLOBAL_REG,
0329                     BC_GLOBAL_RUN, BC_GLOBAL_RUN);
0330 
0331     iosf_mbi_unblock_punit_i2c_access();
0332 
0333     return ret;
0334 }
0335 
0336 static void axp288_put_role_sw(void *data)
0337 {
0338     struct axp288_extcon_info *info = data;
0339 
0340     cancel_work_sync(&info->role_work);
0341     usb_role_switch_put(info->role_sw);
0342 }
0343 
0344 static int axp288_extcon_find_role_sw(struct axp288_extcon_info *info)
0345 {
0346     const struct software_node *swnode;
0347     struct fwnode_handle *fwnode;
0348 
0349     if (!x86_match_cpu(cherry_trail_cpu_ids))
0350         return 0;
0351 
0352     swnode = software_node_find_by_name(NULL, "intel-xhci-usb-sw");
0353     if (!swnode)
0354         return -EPROBE_DEFER;
0355 
0356     fwnode = software_node_fwnode(swnode);
0357     info->role_sw = usb_role_switch_find_by_fwnode(fwnode);
0358     fwnode_handle_put(fwnode);
0359 
0360     return info->role_sw ? 0 : -EPROBE_DEFER;
0361 }
0362 
0363 static int axp288_extcon_probe(struct platform_device *pdev)
0364 {
0365     struct axp288_extcon_info *info;
0366     struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
0367     struct device *dev = &pdev->dev;
0368     struct acpi_device *adev;
0369     int ret, i, pirq;
0370 
0371     info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
0372     if (!info)
0373         return -ENOMEM;
0374 
0375     info->dev = &pdev->dev;
0376     info->regmap = axp20x->regmap;
0377     info->regmap_irqc = axp20x->regmap_irqc;
0378     info->previous_cable = EXTCON_NONE;
0379     INIT_WORK(&info->role_work, axp288_usb_role_work);
0380     info->id_nb.notifier_call = axp288_extcon_id_evt;
0381 
0382     platform_set_drvdata(pdev, info);
0383 
0384     ret = axp288_extcon_find_role_sw(info);
0385     if (ret)
0386         return ret;
0387 
0388     if (info->role_sw) {
0389         ret = devm_add_action_or_reset(dev, axp288_put_role_sw, info);
0390         if (ret)
0391             return ret;
0392 
0393         adev = acpi_dev_get_first_match_dev("INT3496", NULL, -1);
0394         if (adev) {
0395             info->id_extcon = extcon_get_extcon_dev(acpi_dev_name(adev));
0396             put_device(&adev->dev);
0397             if (IS_ERR(info->id_extcon))
0398                 return PTR_ERR(info->id_extcon);
0399 
0400             dev_info(dev, "controlling USB role\n");
0401         } else {
0402             dev_info(dev, "controlling USB role based on Vbus presence\n");
0403         }
0404     }
0405 
0406     ret = iosf_mbi_block_punit_i2c_access();
0407     if (ret < 0)
0408         return ret;
0409 
0410     info->vbus_attach = axp288_get_vbus_attach(info);
0411 
0412     axp288_extcon_log_rsi(info);
0413 
0414     iosf_mbi_unblock_punit_i2c_access();
0415 
0416     /* Initialize extcon device */
0417     info->edev = devm_extcon_dev_allocate(&pdev->dev,
0418                           axp288_extcon_cables);
0419     if (IS_ERR(info->edev)) {
0420         dev_err(&pdev->dev, "failed to allocate memory for extcon\n");
0421         return PTR_ERR(info->edev);
0422     }
0423 
0424     /* Register extcon device */
0425     ret = devm_extcon_dev_register(&pdev->dev, info->edev);
0426     if (ret) {
0427         dev_err(&pdev->dev, "failed to register extcon device\n");
0428         return ret;
0429     }
0430 
0431     for (i = 0; i < EXTCON_IRQ_END; i++) {
0432         pirq = platform_get_irq(pdev, i);
0433         if (pirq < 0)
0434             return pirq;
0435 
0436         info->irq[i] = regmap_irq_get_virq(info->regmap_irqc, pirq);
0437         if (info->irq[i] < 0) {
0438             dev_err(&pdev->dev,
0439                 "failed to get virtual interrupt=%d\n", pirq);
0440             ret = info->irq[i];
0441             return ret;
0442         }
0443 
0444         ret = devm_request_threaded_irq(&pdev->dev, info->irq[i],
0445                 NULL, axp288_extcon_isr,
0446                 IRQF_ONESHOT | IRQF_NO_SUSPEND,
0447                 pdev->name, info);
0448         if (ret) {
0449             dev_err(&pdev->dev, "failed to request interrupt=%d\n",
0450                             info->irq[i]);
0451             return ret;
0452         }
0453     }
0454 
0455     if (info->id_extcon) {
0456         ret = devm_extcon_register_notifier_all(dev, info->id_extcon,
0457                             &info->id_nb);
0458         if (ret)
0459             return ret;
0460     }
0461 
0462     /* Make sure the role-sw is set correctly before doing BC detection */
0463     if (info->role_sw) {
0464         queue_work(system_long_wq, &info->role_work);
0465         flush_work(&info->role_work);
0466     }
0467 
0468     /* Start charger cable type detection */
0469     ret = axp288_extcon_enable(info);
0470     if (ret < 0)
0471         return ret;
0472 
0473     device_init_wakeup(dev, true);
0474     platform_set_drvdata(pdev, info);
0475 
0476     return 0;
0477 }
0478 
0479 static int __maybe_unused axp288_extcon_suspend(struct device *dev)
0480 {
0481     struct axp288_extcon_info *info = dev_get_drvdata(dev);
0482 
0483     if (device_may_wakeup(dev))
0484         enable_irq_wake(info->irq[VBUS_RISING_IRQ]);
0485 
0486     return 0;
0487 }
0488 
0489 static int __maybe_unused axp288_extcon_resume(struct device *dev)
0490 {
0491     struct axp288_extcon_info *info = dev_get_drvdata(dev);
0492 
0493     /*
0494      * Wakeup when a charger is connected to do charger-type
0495      * connection and generate an extcon event which makes the
0496      * axp288 charger driver set the input current limit.
0497      */
0498     if (device_may_wakeup(dev))
0499         disable_irq_wake(info->irq[VBUS_RISING_IRQ]);
0500 
0501     return 0;
0502 }
0503 
0504 static SIMPLE_DEV_PM_OPS(axp288_extcon_pm_ops, axp288_extcon_suspend,
0505              axp288_extcon_resume);
0506 
0507 static const struct platform_device_id axp288_extcon_table[] = {
0508     { .name = "axp288_extcon" },
0509     {},
0510 };
0511 MODULE_DEVICE_TABLE(platform, axp288_extcon_table);
0512 
0513 static struct platform_driver axp288_extcon_driver = {
0514     .probe = axp288_extcon_probe,
0515     .id_table = axp288_extcon_table,
0516     .driver = {
0517         .name = "axp288_extcon",
0518         .pm = &axp288_extcon_pm_ops,
0519     },
0520 };
0521 module_platform_driver(axp288_extcon_driver);
0522 
0523 MODULE_AUTHOR("Ramakrishna Pallala <ramakrishna.pallala@intel.com>");
0524 MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
0525 MODULE_DESCRIPTION("X-Powers AXP288 extcon driver");
0526 MODULE_LICENSE("GPL v2");