Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * ISP1704 USB Charger Detection driver
0004  *
0005  * Copyright (C) 2010 Nokia Corporation
0006  * Copyright (C) 2012 - 2013 Pali Rohár <pali@kernel.org>
0007  */
0008 
0009 #include <linux/kernel.h>
0010 #include <linux/module.h>
0011 #include <linux/err.h>
0012 #include <linux/init.h>
0013 #include <linux/types.h>
0014 #include <linux/device.h>
0015 #include <linux/sysfs.h>
0016 #include <linux/platform_device.h>
0017 #include <linux/power_supply.h>
0018 #include <linux/delay.h>
0019 #include <linux/of.h>
0020 
0021 #include <linux/gpio/consumer.h>
0022 #include <linux/usb/otg.h>
0023 #include <linux/usb/ulpi.h>
0024 #include <linux/usb/ch9.h>
0025 #include <linux/usb/gadget.h>
0026 
0027 /* Vendor specific Power Control register */
0028 #define ISP1704_PWR_CTRL        0x3d
0029 #define ISP1704_PWR_CTRL_SWCTRL     (1 << 0)
0030 #define ISP1704_PWR_CTRL_DET_COMP   (1 << 1)
0031 #define ISP1704_PWR_CTRL_BVALID_RISE    (1 << 2)
0032 #define ISP1704_PWR_CTRL_BVALID_FALL    (1 << 3)
0033 #define ISP1704_PWR_CTRL_DP_WKPU_EN (1 << 4)
0034 #define ISP1704_PWR_CTRL_VDAT_DET   (1 << 5)
0035 #define ISP1704_PWR_CTRL_DPVSRC_EN  (1 << 6)
0036 #define ISP1704_PWR_CTRL_HWDETECT   (1 << 7)
0037 
0038 #define NXP_VENDOR_ID           0x04cc
0039 
0040 static u16 isp170x_id[] = {
0041     0x1704,
0042     0x1707,
0043 };
0044 
0045 struct isp1704_charger {
0046     struct device           *dev;
0047     struct power_supply     *psy;
0048     struct power_supply_desc    psy_desc;
0049     struct gpio_desc        *enable_gpio;
0050     struct usb_phy          *phy;
0051     struct notifier_block       nb;
0052     struct work_struct      work;
0053 
0054     /* properties */
0055     char            model[8];
0056     unsigned        present:1;
0057     unsigned        online:1;
0058     unsigned        current_max;
0059 };
0060 
0061 static inline int isp1704_read(struct isp1704_charger *isp, u32 reg)
0062 {
0063     return usb_phy_io_read(isp->phy, reg);
0064 }
0065 
0066 static inline int isp1704_write(struct isp1704_charger *isp, u32 reg, u32 val)
0067 {
0068     return usb_phy_io_write(isp->phy, val, reg);
0069 }
0070 
0071 static void isp1704_charger_set_power(struct isp1704_charger *isp, bool on)
0072 {
0073     gpiod_set_value(isp->enable_gpio, on);
0074 }
0075 
0076 /*
0077  * Determine is the charging port DCP (dedicated charger) or CDP (Host/HUB
0078  * chargers).
0079  *
0080  * REVISIT: The method is defined in Battery Charging Specification and is
0081  * applicable to any ULPI transceiver. Nothing isp170x specific here.
0082  */
0083 static inline int isp1704_charger_type(struct isp1704_charger *isp)
0084 {
0085     u8 reg;
0086     u8 func_ctrl;
0087     u8 otg_ctrl;
0088     int type = POWER_SUPPLY_TYPE_USB_DCP;
0089 
0090     func_ctrl = isp1704_read(isp, ULPI_FUNC_CTRL);
0091     otg_ctrl = isp1704_read(isp, ULPI_OTG_CTRL);
0092 
0093     /* disable pulldowns */
0094     reg = ULPI_OTG_CTRL_DM_PULLDOWN | ULPI_OTG_CTRL_DP_PULLDOWN;
0095     isp1704_write(isp, ULPI_CLR(ULPI_OTG_CTRL), reg);
0096 
0097     /* full speed */
0098     isp1704_write(isp, ULPI_CLR(ULPI_FUNC_CTRL),
0099             ULPI_FUNC_CTRL_XCVRSEL_MASK);
0100     isp1704_write(isp, ULPI_SET(ULPI_FUNC_CTRL),
0101             ULPI_FUNC_CTRL_FULL_SPEED);
0102 
0103     /* Enable strong pull-up on DP (1.5K) and reset */
0104     reg = ULPI_FUNC_CTRL_TERMSELECT | ULPI_FUNC_CTRL_RESET;
0105     isp1704_write(isp, ULPI_SET(ULPI_FUNC_CTRL), reg);
0106     usleep_range(1000, 2000);
0107 
0108     reg = isp1704_read(isp, ULPI_DEBUG);
0109     if ((reg & 3) != 3)
0110         type = POWER_SUPPLY_TYPE_USB_CDP;
0111 
0112     /* recover original state */
0113     isp1704_write(isp, ULPI_FUNC_CTRL, func_ctrl);
0114     isp1704_write(isp, ULPI_OTG_CTRL, otg_ctrl);
0115 
0116     return type;
0117 }
0118 
0119 /*
0120  * ISP1704 detects PS/2 adapters as charger. To make sure the detected charger
0121  * is actually a dedicated charger, the following steps need to be taken.
0122  */
0123 static inline int isp1704_charger_verify(struct isp1704_charger *isp)
0124 {
0125     int ret = 0;
0126     u8  r;
0127 
0128     /* Reset the transceiver */
0129     r = isp1704_read(isp, ULPI_FUNC_CTRL);
0130     r |= ULPI_FUNC_CTRL_RESET;
0131     isp1704_write(isp, ULPI_FUNC_CTRL, r);
0132     usleep_range(1000, 2000);
0133 
0134     /* Set normal mode */
0135     r &= ~(ULPI_FUNC_CTRL_RESET | ULPI_FUNC_CTRL_OPMODE_MASK);
0136     isp1704_write(isp, ULPI_FUNC_CTRL, r);
0137 
0138     /* Clear the DP and DM pull-down bits */
0139     r = ULPI_OTG_CTRL_DP_PULLDOWN | ULPI_OTG_CTRL_DM_PULLDOWN;
0140     isp1704_write(isp, ULPI_CLR(ULPI_OTG_CTRL), r);
0141 
0142     /* Enable strong pull-up on DP (1.5K) and reset */
0143     r = ULPI_FUNC_CTRL_TERMSELECT | ULPI_FUNC_CTRL_RESET;
0144     isp1704_write(isp, ULPI_SET(ULPI_FUNC_CTRL), r);
0145     usleep_range(1000, 2000);
0146 
0147     /* Read the line state */
0148     if (!isp1704_read(isp, ULPI_DEBUG)) {
0149         /* Disable strong pull-up on DP (1.5K) */
0150         isp1704_write(isp, ULPI_CLR(ULPI_FUNC_CTRL),
0151                 ULPI_FUNC_CTRL_TERMSELECT);
0152         return 1;
0153     }
0154 
0155     /* Is it a charger or PS/2 connection */
0156 
0157     /* Enable weak pull-up resistor on DP */
0158     isp1704_write(isp, ULPI_SET(ISP1704_PWR_CTRL),
0159             ISP1704_PWR_CTRL_DP_WKPU_EN);
0160 
0161     /* Disable strong pull-up on DP (1.5K) */
0162     isp1704_write(isp, ULPI_CLR(ULPI_FUNC_CTRL),
0163             ULPI_FUNC_CTRL_TERMSELECT);
0164 
0165     /* Enable weak pull-down resistor on DM */
0166     isp1704_write(isp, ULPI_SET(ULPI_OTG_CTRL),
0167             ULPI_OTG_CTRL_DM_PULLDOWN);
0168 
0169     /* It's a charger if the line states are clear */
0170     if (!(isp1704_read(isp, ULPI_DEBUG)))
0171         ret = 1;
0172 
0173     /* Disable weak pull-up resistor on DP */
0174     isp1704_write(isp, ULPI_CLR(ISP1704_PWR_CTRL),
0175             ISP1704_PWR_CTRL_DP_WKPU_EN);
0176 
0177     return ret;
0178 }
0179 
0180 static inline int isp1704_charger_detect(struct isp1704_charger *isp)
0181 {
0182     unsigned long   timeout;
0183     u8      pwr_ctrl;
0184     int     ret = 0;
0185 
0186     pwr_ctrl = isp1704_read(isp, ISP1704_PWR_CTRL);
0187 
0188     /* set SW control bit in PWR_CTRL register */
0189     isp1704_write(isp, ISP1704_PWR_CTRL,
0190             ISP1704_PWR_CTRL_SWCTRL);
0191 
0192     /* enable manual charger detection */
0193     isp1704_write(isp, ULPI_SET(ISP1704_PWR_CTRL),
0194             ISP1704_PWR_CTRL_SWCTRL
0195             | ISP1704_PWR_CTRL_DPVSRC_EN);
0196     usleep_range(1000, 2000);
0197 
0198     timeout = jiffies + msecs_to_jiffies(300);
0199     do {
0200         /* Check if there is a charger */
0201         if (isp1704_read(isp, ISP1704_PWR_CTRL)
0202                 & ISP1704_PWR_CTRL_VDAT_DET) {
0203             ret = isp1704_charger_verify(isp);
0204             break;
0205         }
0206     } while (!time_after(jiffies, timeout) && isp->online);
0207 
0208     /* recover original state */
0209     isp1704_write(isp, ISP1704_PWR_CTRL, pwr_ctrl);
0210 
0211     return ret;
0212 }
0213 
0214 static inline int isp1704_charger_detect_dcp(struct isp1704_charger *isp)
0215 {
0216     if (isp1704_charger_detect(isp) &&
0217             isp1704_charger_type(isp) == POWER_SUPPLY_TYPE_USB_DCP)
0218         return true;
0219     else
0220         return false;
0221 }
0222 
0223 static void isp1704_charger_work(struct work_struct *data)
0224 {
0225     struct isp1704_charger  *isp =
0226         container_of(data, struct isp1704_charger, work);
0227     static DEFINE_MUTEX(lock);
0228 
0229     mutex_lock(&lock);
0230 
0231     switch (isp->phy->last_event) {
0232     case USB_EVENT_VBUS:
0233         /* do not call wall charger detection more times */
0234         if (!isp->present) {
0235             isp->online = true;
0236             isp->present = 1;
0237             isp1704_charger_set_power(isp, 1);
0238 
0239             /* detect wall charger */
0240             if (isp1704_charger_detect_dcp(isp)) {
0241                 isp->psy_desc.type = POWER_SUPPLY_TYPE_USB_DCP;
0242                 isp->current_max = 1800;
0243             } else {
0244                 isp->psy_desc.type = POWER_SUPPLY_TYPE_USB;
0245                 isp->current_max = 500;
0246             }
0247 
0248             /* enable data pullups */
0249             if (isp->phy->otg->gadget)
0250                 usb_gadget_connect(isp->phy->otg->gadget);
0251         }
0252 
0253         if (isp->psy_desc.type != POWER_SUPPLY_TYPE_USB_DCP) {
0254             /*
0255              * Only 500mA here or high speed chirp
0256              * handshaking may break
0257              */
0258             if (isp->current_max > 500)
0259                 isp->current_max = 500;
0260 
0261             if (isp->current_max > 100)
0262                 isp->psy_desc.type = POWER_SUPPLY_TYPE_USB_CDP;
0263         }
0264         break;
0265     case USB_EVENT_NONE:
0266         isp->online = false;
0267         isp->present = 0;
0268         isp->current_max = 0;
0269         isp->psy_desc.type = POWER_SUPPLY_TYPE_USB;
0270 
0271         /*
0272          * Disable data pullups. We need to prevent the controller from
0273          * enumerating.
0274          *
0275          * FIXME: This is here to allow charger detection with Host/HUB
0276          * chargers. The pullups may be enabled elsewhere, so this can
0277          * not be the final solution.
0278          */
0279         if (isp->phy->otg->gadget)
0280             usb_gadget_disconnect(isp->phy->otg->gadget);
0281 
0282         isp1704_charger_set_power(isp, 0);
0283         break;
0284     default:
0285         goto out;
0286     }
0287 
0288     power_supply_changed(isp->psy);
0289 out:
0290     mutex_unlock(&lock);
0291 }
0292 
0293 static int isp1704_notifier_call(struct notifier_block *nb,
0294         unsigned long val, void *v)
0295 {
0296     struct isp1704_charger *isp =
0297         container_of(nb, struct isp1704_charger, nb);
0298 
0299     schedule_work(&isp->work);
0300 
0301     return NOTIFY_OK;
0302 }
0303 
0304 static int isp1704_charger_get_property(struct power_supply *psy,
0305                 enum power_supply_property psp,
0306                 union power_supply_propval *val)
0307 {
0308     struct isp1704_charger *isp = power_supply_get_drvdata(psy);
0309 
0310     switch (psp) {
0311     case POWER_SUPPLY_PROP_PRESENT:
0312         val->intval = isp->present;
0313         break;
0314     case POWER_SUPPLY_PROP_ONLINE:
0315         val->intval = isp->online;
0316         break;
0317     case POWER_SUPPLY_PROP_CURRENT_MAX:
0318         val->intval = isp->current_max;
0319         break;
0320     case POWER_SUPPLY_PROP_MODEL_NAME:
0321         val->strval = isp->model;
0322         break;
0323     case POWER_SUPPLY_PROP_MANUFACTURER:
0324         val->strval = "NXP";
0325         break;
0326     default:
0327         return -EINVAL;
0328     }
0329     return 0;
0330 }
0331 
0332 static enum power_supply_property power_props[] = {
0333     POWER_SUPPLY_PROP_PRESENT,
0334     POWER_SUPPLY_PROP_ONLINE,
0335     POWER_SUPPLY_PROP_CURRENT_MAX,
0336     POWER_SUPPLY_PROP_MODEL_NAME,
0337     POWER_SUPPLY_PROP_MANUFACTURER,
0338 };
0339 
0340 static inline int isp1704_test_ulpi(struct isp1704_charger *isp)
0341 {
0342     int vendor;
0343     int product;
0344     int i;
0345     int ret;
0346 
0347     /* Test ULPI interface */
0348     ret = isp1704_write(isp, ULPI_SCRATCH, 0xaa);
0349     if (ret < 0)
0350         return ret;
0351 
0352     ret = isp1704_read(isp, ULPI_SCRATCH);
0353     if (ret < 0)
0354         return ret;
0355 
0356     if (ret != 0xaa)
0357         return -ENODEV;
0358 
0359     /* Verify the product and vendor id matches */
0360     vendor = isp1704_read(isp, ULPI_VENDOR_ID_LOW);
0361     vendor |= isp1704_read(isp, ULPI_VENDOR_ID_HIGH) << 8;
0362     if (vendor != NXP_VENDOR_ID)
0363         return -ENODEV;
0364 
0365     product = isp1704_read(isp, ULPI_PRODUCT_ID_LOW);
0366     product |= isp1704_read(isp, ULPI_PRODUCT_ID_HIGH) << 8;
0367 
0368     for (i = 0; i < ARRAY_SIZE(isp170x_id); i++) {
0369         if (product == isp170x_id[i]) {
0370             sprintf(isp->model, "isp%x", product);
0371             return product;
0372         }
0373     }
0374 
0375     dev_err(isp->dev, "product id %x not matching known ids", product);
0376 
0377     return -ENODEV;
0378 }
0379 
0380 static int isp1704_charger_probe(struct platform_device *pdev)
0381 {
0382     struct isp1704_charger  *isp;
0383     int         ret = -ENODEV;
0384     struct power_supply_config psy_cfg = {};
0385 
0386     isp = devm_kzalloc(&pdev->dev, sizeof(*isp), GFP_KERNEL);
0387     if (!isp)
0388         return -ENOMEM;
0389 
0390     isp->enable_gpio = devm_gpiod_get(&pdev->dev, "nxp,enable",
0391                       GPIOD_OUT_HIGH);
0392     if (IS_ERR(isp->enable_gpio)) {
0393         ret = PTR_ERR(isp->enable_gpio);
0394         dev_err(&pdev->dev, "Could not get reset gpio: %d\n", ret);
0395         return ret;
0396     }
0397 
0398     if (pdev->dev.of_node)
0399         isp->phy = devm_usb_get_phy_by_phandle(&pdev->dev, "usb-phy", 0);
0400     else
0401         isp->phy = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2);
0402 
0403     if (IS_ERR(isp->phy)) {
0404         ret = PTR_ERR(isp->phy);
0405         dev_err(&pdev->dev, "usb_get_phy failed\n");
0406         goto fail0;
0407     }
0408 
0409     isp->dev = &pdev->dev;
0410     platform_set_drvdata(pdev, isp);
0411 
0412     isp1704_charger_set_power(isp, 1);
0413 
0414     ret = isp1704_test_ulpi(isp);
0415     if (ret < 0) {
0416         dev_err(&pdev->dev, "isp1704_test_ulpi failed\n");
0417         goto fail1;
0418     }
0419 
0420     isp->psy_desc.name      = "isp1704";
0421     isp->psy_desc.type      = POWER_SUPPLY_TYPE_USB;
0422     isp->psy_desc.properties    = power_props;
0423     isp->psy_desc.num_properties    = ARRAY_SIZE(power_props);
0424     isp->psy_desc.get_property  = isp1704_charger_get_property;
0425 
0426     psy_cfg.drv_data        = isp;
0427 
0428     isp->psy = power_supply_register(isp->dev, &isp->psy_desc, &psy_cfg);
0429     if (IS_ERR(isp->psy)) {
0430         ret = PTR_ERR(isp->psy);
0431         dev_err(&pdev->dev, "power_supply_register failed\n");
0432         goto fail1;
0433     }
0434 
0435     /*
0436      * REVISIT: using work in order to allow the usb notifications to be
0437      * made atomically in the future.
0438      */
0439     INIT_WORK(&isp->work, isp1704_charger_work);
0440 
0441     isp->nb.notifier_call = isp1704_notifier_call;
0442 
0443     ret = usb_register_notifier(isp->phy, &isp->nb);
0444     if (ret) {
0445         dev_err(&pdev->dev, "usb_register_notifier failed\n");
0446         goto fail2;
0447     }
0448 
0449     dev_info(isp->dev, "registered with product id %s\n", isp->model);
0450 
0451     /*
0452      * Taking over the D+ pullup.
0453      *
0454      * FIXME: The device will be disconnected if it was already
0455      * enumerated. The charger driver should be always loaded before any
0456      * gadget is loaded.
0457      */
0458     if (isp->phy->otg->gadget)
0459         usb_gadget_disconnect(isp->phy->otg->gadget);
0460 
0461     if (isp->phy->last_event == USB_EVENT_NONE)
0462         isp1704_charger_set_power(isp, 0);
0463 
0464     /* Detect charger if VBUS is valid (the cable was already plugged). */
0465     if (isp->phy->last_event == USB_EVENT_VBUS &&
0466             !isp->phy->otg->default_a)
0467         schedule_work(&isp->work);
0468 
0469     return 0;
0470 fail2:
0471     power_supply_unregister(isp->psy);
0472 fail1:
0473     isp1704_charger_set_power(isp, 0);
0474 fail0:
0475     dev_err(&pdev->dev, "failed to register isp1704 with error %d\n", ret);
0476 
0477     return ret;
0478 }
0479 
0480 static int isp1704_charger_remove(struct platform_device *pdev)
0481 {
0482     struct isp1704_charger *isp = platform_get_drvdata(pdev);
0483 
0484     usb_unregister_notifier(isp->phy, &isp->nb);
0485     power_supply_unregister(isp->psy);
0486     isp1704_charger_set_power(isp, 0);
0487 
0488     return 0;
0489 }
0490 
0491 #ifdef CONFIG_OF
0492 static const struct of_device_id omap_isp1704_of_match[] = {
0493     { .compatible = "nxp,isp1704", },
0494     { .compatible = "nxp,isp1707", },
0495     {},
0496 };
0497 MODULE_DEVICE_TABLE(of, omap_isp1704_of_match);
0498 #endif
0499 
0500 static struct platform_driver isp1704_charger_driver = {
0501     .driver = {
0502         .name = "isp1704_charger",
0503         .of_match_table = of_match_ptr(omap_isp1704_of_match),
0504     },
0505     .probe = isp1704_charger_probe,
0506     .remove = isp1704_charger_remove,
0507 };
0508 
0509 module_platform_driver(isp1704_charger_driver);
0510 
0511 MODULE_ALIAS("platform:isp1704_charger");
0512 MODULE_AUTHOR("Nokia Corporation");
0513 MODULE_DESCRIPTION("ISP170x USB Charger driver");
0514 MODULE_LICENSE("GPL");