root/drivers/power/supply/isp1704_charger.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. isp1704_read
  2. isp1704_write
  3. isp1704_charger_set_power
  4. isp1704_charger_type
  5. isp1704_charger_verify
  6. isp1704_charger_detect
  7. isp1704_charger_detect_dcp
  8. isp1704_charger_work
  9. isp1704_notifier_call
  10. isp1704_charger_get_property
  11. isp1704_test_ulpi
  12. isp1704_charger_probe
  13. isp1704_charger_remove

   1 // SPDX-License-Identifier: GPL-2.0-or-later
   2 /*
   3  * ISP1704 USB Charger Detection driver
   4  *
   5  * Copyright (C) 2010 Nokia Corporation
   6  * Copyright (C) 2012 - 2013 Pali Rohár <pali.rohar@gmail.com>
   7  */
   8 
   9 #include <linux/kernel.h>
  10 #include <linux/module.h>
  11 #include <linux/err.h>
  12 #include <linux/init.h>
  13 #include <linux/types.h>
  14 #include <linux/device.h>
  15 #include <linux/sysfs.h>
  16 #include <linux/platform_device.h>
  17 #include <linux/power_supply.h>
  18 #include <linux/delay.h>
  19 #include <linux/of.h>
  20 
  21 #include <linux/gpio/consumer.h>
  22 #include <linux/usb/otg.h>
  23 #include <linux/usb/ulpi.h>
  24 #include <linux/usb/ch9.h>
  25 #include <linux/usb/gadget.h>
  26 
  27 /* Vendor specific Power Control register */
  28 #define ISP1704_PWR_CTRL                0x3d
  29 #define ISP1704_PWR_CTRL_SWCTRL         (1 << 0)
  30 #define ISP1704_PWR_CTRL_DET_COMP       (1 << 1)
  31 #define ISP1704_PWR_CTRL_BVALID_RISE    (1 << 2)
  32 #define ISP1704_PWR_CTRL_BVALID_FALL    (1 << 3)
  33 #define ISP1704_PWR_CTRL_DP_WKPU_EN     (1 << 4)
  34 #define ISP1704_PWR_CTRL_VDAT_DET       (1 << 5)
  35 #define ISP1704_PWR_CTRL_DPVSRC_EN      (1 << 6)
  36 #define ISP1704_PWR_CTRL_HWDETECT       (1 << 7)
  37 
  38 #define NXP_VENDOR_ID                   0x04cc
  39 
  40 static u16 isp170x_id[] = {
  41         0x1704,
  42         0x1707,
  43 };
  44 
  45 struct isp1704_charger {
  46         struct device                   *dev;
  47         struct power_supply             *psy;
  48         struct power_supply_desc        psy_desc;
  49         struct gpio_desc                *enable_gpio;
  50         struct usb_phy                  *phy;
  51         struct notifier_block           nb;
  52         struct work_struct              work;
  53 
  54         /* properties */
  55         char                    model[8];
  56         unsigned                present:1;
  57         unsigned                online:1;
  58         unsigned                current_max;
  59 };
  60 
  61 static inline int isp1704_read(struct isp1704_charger *isp, u32 reg)
  62 {
  63         return usb_phy_io_read(isp->phy, reg);
  64 }
  65 
  66 static inline int isp1704_write(struct isp1704_charger *isp, u32 reg, u32 val)
  67 {
  68         return usb_phy_io_write(isp->phy, val, reg);
  69 }
  70 
  71 static void isp1704_charger_set_power(struct isp1704_charger *isp, bool on)
  72 {
  73         gpiod_set_value(isp->enable_gpio, on);
  74 }
  75 
  76 /*
  77  * Determine is the charging port DCP (dedicated charger) or CDP (Host/HUB
  78  * chargers).
  79  *
  80  * REVISIT: The method is defined in Battery Charging Specification and is
  81  * applicable to any ULPI transceiver. Nothing isp170x specific here.
  82  */
  83 static inline int isp1704_charger_type(struct isp1704_charger *isp)
  84 {
  85         u8 reg;
  86         u8 func_ctrl;
  87         u8 otg_ctrl;
  88         int type = POWER_SUPPLY_TYPE_USB_DCP;
  89 
  90         func_ctrl = isp1704_read(isp, ULPI_FUNC_CTRL);
  91         otg_ctrl = isp1704_read(isp, ULPI_OTG_CTRL);
  92 
  93         /* disable pulldowns */
  94         reg = ULPI_OTG_CTRL_DM_PULLDOWN | ULPI_OTG_CTRL_DP_PULLDOWN;
  95         isp1704_write(isp, ULPI_CLR(ULPI_OTG_CTRL), reg);
  96 
  97         /* full speed */
  98         isp1704_write(isp, ULPI_CLR(ULPI_FUNC_CTRL),
  99                         ULPI_FUNC_CTRL_XCVRSEL_MASK);
 100         isp1704_write(isp, ULPI_SET(ULPI_FUNC_CTRL),
 101                         ULPI_FUNC_CTRL_FULL_SPEED);
 102 
 103         /* Enable strong pull-up on DP (1.5K) and reset */
 104         reg = ULPI_FUNC_CTRL_TERMSELECT | ULPI_FUNC_CTRL_RESET;
 105         isp1704_write(isp, ULPI_SET(ULPI_FUNC_CTRL), reg);
 106         usleep_range(1000, 2000);
 107 
 108         reg = isp1704_read(isp, ULPI_DEBUG);
 109         if ((reg & 3) != 3)
 110                 type = POWER_SUPPLY_TYPE_USB_CDP;
 111 
 112         /* recover original state */
 113         isp1704_write(isp, ULPI_FUNC_CTRL, func_ctrl);
 114         isp1704_write(isp, ULPI_OTG_CTRL, otg_ctrl);
 115 
 116         return type;
 117 }
 118 
 119 /*
 120  * ISP1704 detects PS/2 adapters as charger. To make sure the detected charger
 121  * is actually a dedicated charger, the following steps need to be taken.
 122  */
 123 static inline int isp1704_charger_verify(struct isp1704_charger *isp)
 124 {
 125         int     ret = 0;
 126         u8      r;
 127 
 128         /* Reset the transceiver */
 129         r = isp1704_read(isp, ULPI_FUNC_CTRL);
 130         r |= ULPI_FUNC_CTRL_RESET;
 131         isp1704_write(isp, ULPI_FUNC_CTRL, r);
 132         usleep_range(1000, 2000);
 133 
 134         /* Set normal mode */
 135         r &= ~(ULPI_FUNC_CTRL_RESET | ULPI_FUNC_CTRL_OPMODE_MASK);
 136         isp1704_write(isp, ULPI_FUNC_CTRL, r);
 137 
 138         /* Clear the DP and DM pull-down bits */
 139         r = ULPI_OTG_CTRL_DP_PULLDOWN | ULPI_OTG_CTRL_DM_PULLDOWN;
 140         isp1704_write(isp, ULPI_CLR(ULPI_OTG_CTRL), r);
 141 
 142         /* Enable strong pull-up on DP (1.5K) and reset */
 143         r = ULPI_FUNC_CTRL_TERMSELECT | ULPI_FUNC_CTRL_RESET;
 144         isp1704_write(isp, ULPI_SET(ULPI_FUNC_CTRL), r);
 145         usleep_range(1000, 2000);
 146 
 147         /* Read the line state */
 148         if (!isp1704_read(isp, ULPI_DEBUG)) {
 149                 /* Disable strong pull-up on DP (1.5K) */
 150                 isp1704_write(isp, ULPI_CLR(ULPI_FUNC_CTRL),
 151                                 ULPI_FUNC_CTRL_TERMSELECT);
 152                 return 1;
 153         }
 154 
 155         /* Is it a charger or PS/2 connection */
 156 
 157         /* Enable weak pull-up resistor on DP */
 158         isp1704_write(isp, ULPI_SET(ISP1704_PWR_CTRL),
 159                         ISP1704_PWR_CTRL_DP_WKPU_EN);
 160 
 161         /* Disable strong pull-up on DP (1.5K) */
 162         isp1704_write(isp, ULPI_CLR(ULPI_FUNC_CTRL),
 163                         ULPI_FUNC_CTRL_TERMSELECT);
 164 
 165         /* Enable weak pull-down resistor on DM */
 166         isp1704_write(isp, ULPI_SET(ULPI_OTG_CTRL),
 167                         ULPI_OTG_CTRL_DM_PULLDOWN);
 168 
 169         /* It's a charger if the line states are clear */
 170         if (!(isp1704_read(isp, ULPI_DEBUG)))
 171                 ret = 1;
 172 
 173         /* Disable weak pull-up resistor on DP */
 174         isp1704_write(isp, ULPI_CLR(ISP1704_PWR_CTRL),
 175                         ISP1704_PWR_CTRL_DP_WKPU_EN);
 176 
 177         return ret;
 178 }
 179 
 180 static inline int isp1704_charger_detect(struct isp1704_charger *isp)
 181 {
 182         unsigned long   timeout;
 183         u8              pwr_ctrl;
 184         int             ret = 0;
 185 
 186         pwr_ctrl = isp1704_read(isp, ISP1704_PWR_CTRL);
 187 
 188         /* set SW control bit in PWR_CTRL register */
 189         isp1704_write(isp, ISP1704_PWR_CTRL,
 190                         ISP1704_PWR_CTRL_SWCTRL);
 191 
 192         /* enable manual charger detection */
 193         isp1704_write(isp, ULPI_SET(ISP1704_PWR_CTRL),
 194                         ISP1704_PWR_CTRL_SWCTRL
 195                         | ISP1704_PWR_CTRL_DPVSRC_EN);
 196         usleep_range(1000, 2000);
 197 
 198         timeout = jiffies + msecs_to_jiffies(300);
 199         do {
 200                 /* Check if there is a charger */
 201                 if (isp1704_read(isp, ISP1704_PWR_CTRL)
 202                                 & ISP1704_PWR_CTRL_VDAT_DET) {
 203                         ret = isp1704_charger_verify(isp);
 204                         break;
 205                 }
 206         } while (!time_after(jiffies, timeout) && isp->online);
 207 
 208         /* recover original state */
 209         isp1704_write(isp, ISP1704_PWR_CTRL, pwr_ctrl);
 210 
 211         return ret;
 212 }
 213 
 214 static inline int isp1704_charger_detect_dcp(struct isp1704_charger *isp)
 215 {
 216         if (isp1704_charger_detect(isp) &&
 217                         isp1704_charger_type(isp) == POWER_SUPPLY_TYPE_USB_DCP)
 218                 return true;
 219         else
 220                 return false;
 221 }
 222 
 223 static void isp1704_charger_work(struct work_struct *data)
 224 {
 225         struct isp1704_charger  *isp =
 226                 container_of(data, struct isp1704_charger, work);
 227         static DEFINE_MUTEX(lock);
 228 
 229         mutex_lock(&lock);
 230 
 231         switch (isp->phy->last_event) {
 232         case USB_EVENT_VBUS:
 233                 /* do not call wall charger detection more times */
 234                 if (!isp->present) {
 235                         isp->online = true;
 236                         isp->present = 1;
 237                         isp1704_charger_set_power(isp, 1);
 238 
 239                         /* detect wall charger */
 240                         if (isp1704_charger_detect_dcp(isp)) {
 241                                 isp->psy_desc.type = POWER_SUPPLY_TYPE_USB_DCP;
 242                                 isp->current_max = 1800;
 243                         } else {
 244                                 isp->psy_desc.type = POWER_SUPPLY_TYPE_USB;
 245                                 isp->current_max = 500;
 246                         }
 247 
 248                         /* enable data pullups */
 249                         if (isp->phy->otg->gadget)
 250                                 usb_gadget_connect(isp->phy->otg->gadget);
 251                 }
 252 
 253                 if (isp->psy_desc.type != POWER_SUPPLY_TYPE_USB_DCP) {
 254                         /*
 255                          * Only 500mA here or high speed chirp
 256                          * handshaking may break
 257                          */
 258                         if (isp->current_max > 500)
 259                                 isp->current_max = 500;
 260 
 261                         if (isp->current_max > 100)
 262                                 isp->psy_desc.type = POWER_SUPPLY_TYPE_USB_CDP;
 263                 }
 264                 break;
 265         case USB_EVENT_NONE:
 266                 isp->online = false;
 267                 isp->present = 0;
 268                 isp->current_max = 0;
 269                 isp->psy_desc.type = POWER_SUPPLY_TYPE_USB;
 270 
 271                 /*
 272                  * Disable data pullups. We need to prevent the controller from
 273                  * enumerating.
 274                  *
 275                  * FIXME: This is here to allow charger detection with Host/HUB
 276                  * chargers. The pullups may be enabled elsewhere, so this can
 277                  * not be the final solution.
 278                  */
 279                 if (isp->phy->otg->gadget)
 280                         usb_gadget_disconnect(isp->phy->otg->gadget);
 281 
 282                 isp1704_charger_set_power(isp, 0);
 283                 break;
 284         default:
 285                 goto out;
 286         }
 287 
 288         power_supply_changed(isp->psy);
 289 out:
 290         mutex_unlock(&lock);
 291 }
 292 
 293 static int isp1704_notifier_call(struct notifier_block *nb,
 294                 unsigned long val, void *v)
 295 {
 296         struct isp1704_charger *isp =
 297                 container_of(nb, struct isp1704_charger, nb);
 298 
 299         schedule_work(&isp->work);
 300 
 301         return NOTIFY_OK;
 302 }
 303 
 304 static int isp1704_charger_get_property(struct power_supply *psy,
 305                                 enum power_supply_property psp,
 306                                 union power_supply_propval *val)
 307 {
 308         struct isp1704_charger *isp = power_supply_get_drvdata(psy);
 309 
 310         switch (psp) {
 311         case POWER_SUPPLY_PROP_PRESENT:
 312                 val->intval = isp->present;
 313                 break;
 314         case POWER_SUPPLY_PROP_ONLINE:
 315                 val->intval = isp->online;
 316                 break;
 317         case POWER_SUPPLY_PROP_CURRENT_MAX:
 318                 val->intval = isp->current_max;
 319                 break;
 320         case POWER_SUPPLY_PROP_MODEL_NAME:
 321                 val->strval = isp->model;
 322                 break;
 323         case POWER_SUPPLY_PROP_MANUFACTURER:
 324                 val->strval = "NXP";
 325                 break;
 326         default:
 327                 return -EINVAL;
 328         }
 329         return 0;
 330 }
 331 
 332 static enum power_supply_property power_props[] = {
 333         POWER_SUPPLY_PROP_PRESENT,
 334         POWER_SUPPLY_PROP_ONLINE,
 335         POWER_SUPPLY_PROP_CURRENT_MAX,
 336         POWER_SUPPLY_PROP_MODEL_NAME,
 337         POWER_SUPPLY_PROP_MANUFACTURER,
 338 };
 339 
 340 static inline int isp1704_test_ulpi(struct isp1704_charger *isp)
 341 {
 342         int vendor;
 343         int product;
 344         int i;
 345         int ret;
 346 
 347         /* Test ULPI interface */
 348         ret = isp1704_write(isp, ULPI_SCRATCH, 0xaa);
 349         if (ret < 0)
 350                 return ret;
 351 
 352         ret = isp1704_read(isp, ULPI_SCRATCH);
 353         if (ret < 0)
 354                 return ret;
 355 
 356         if (ret != 0xaa)
 357                 return -ENODEV;
 358 
 359         /* Verify the product and vendor id matches */
 360         vendor = isp1704_read(isp, ULPI_VENDOR_ID_LOW);
 361         vendor |= isp1704_read(isp, ULPI_VENDOR_ID_HIGH) << 8;
 362         if (vendor != NXP_VENDOR_ID)
 363                 return -ENODEV;
 364 
 365         product = isp1704_read(isp, ULPI_PRODUCT_ID_LOW);
 366         product |= isp1704_read(isp, ULPI_PRODUCT_ID_HIGH) << 8;
 367 
 368         for (i = 0; i < ARRAY_SIZE(isp170x_id); i++) {
 369                 if (product == isp170x_id[i]) {
 370                         sprintf(isp->model, "isp%x", product);
 371                         return product;
 372                 }
 373         }
 374 
 375         dev_err(isp->dev, "product id %x not matching known ids", product);
 376 
 377         return -ENODEV;
 378 }
 379 
 380 static int isp1704_charger_probe(struct platform_device *pdev)
 381 {
 382         struct isp1704_charger  *isp;
 383         int                     ret = -ENODEV;
 384         struct power_supply_config psy_cfg = {};
 385 
 386         isp = devm_kzalloc(&pdev->dev, sizeof(*isp), GFP_KERNEL);
 387         if (!isp)
 388                 return -ENOMEM;
 389 
 390         isp->enable_gpio = devm_gpiod_get(&pdev->dev, "nxp,enable",
 391                                           GPIOD_OUT_HIGH);
 392         if (IS_ERR(isp->enable_gpio)) {
 393                 ret = PTR_ERR(isp->enable_gpio);
 394                 dev_err(&pdev->dev, "Could not get reset gpio: %d\n", ret);
 395                 return ret;
 396         }
 397 
 398         if (pdev->dev.of_node)
 399                 isp->phy = devm_usb_get_phy_by_phandle(&pdev->dev, "usb-phy", 0);
 400         else
 401                 isp->phy = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2);
 402 
 403         if (IS_ERR(isp->phy)) {
 404                 ret = PTR_ERR(isp->phy);
 405                 dev_err(&pdev->dev, "usb_get_phy failed\n");
 406                 goto fail0;
 407         }
 408 
 409         isp->dev = &pdev->dev;
 410         platform_set_drvdata(pdev, isp);
 411 
 412         isp1704_charger_set_power(isp, 1);
 413 
 414         ret = isp1704_test_ulpi(isp);
 415         if (ret < 0) {
 416                 dev_err(&pdev->dev, "isp1704_test_ulpi failed\n");
 417                 goto fail1;
 418         }
 419 
 420         isp->psy_desc.name              = "isp1704";
 421         isp->psy_desc.type              = POWER_SUPPLY_TYPE_USB;
 422         isp->psy_desc.properties        = power_props;
 423         isp->psy_desc.num_properties    = ARRAY_SIZE(power_props);
 424         isp->psy_desc.get_property      = isp1704_charger_get_property;
 425 
 426         psy_cfg.drv_data                = isp;
 427 
 428         isp->psy = power_supply_register(isp->dev, &isp->psy_desc, &psy_cfg);
 429         if (IS_ERR(isp->psy)) {
 430                 ret = PTR_ERR(isp->psy);
 431                 dev_err(&pdev->dev, "power_supply_register failed\n");
 432                 goto fail1;
 433         }
 434 
 435         /*
 436          * REVISIT: using work in order to allow the usb notifications to be
 437          * made atomically in the future.
 438          */
 439         INIT_WORK(&isp->work, isp1704_charger_work);
 440 
 441         isp->nb.notifier_call = isp1704_notifier_call;
 442 
 443         ret = usb_register_notifier(isp->phy, &isp->nb);
 444         if (ret) {
 445                 dev_err(&pdev->dev, "usb_register_notifier failed\n");
 446                 goto fail2;
 447         }
 448 
 449         dev_info(isp->dev, "registered with product id %s\n", isp->model);
 450 
 451         /*
 452          * Taking over the D+ pullup.
 453          *
 454          * FIXME: The device will be disconnected if it was already
 455          * enumerated. The charger driver should be always loaded before any
 456          * gadget is loaded.
 457          */
 458         if (isp->phy->otg->gadget)
 459                 usb_gadget_disconnect(isp->phy->otg->gadget);
 460 
 461         if (isp->phy->last_event == USB_EVENT_NONE)
 462                 isp1704_charger_set_power(isp, 0);
 463 
 464         /* Detect charger if VBUS is valid (the cable was already plugged). */
 465         if (isp->phy->last_event == USB_EVENT_VBUS &&
 466                         !isp->phy->otg->default_a)
 467                 schedule_work(&isp->work);
 468 
 469         return 0;
 470 fail2:
 471         power_supply_unregister(isp->psy);
 472 fail1:
 473         isp1704_charger_set_power(isp, 0);
 474 fail0:
 475         dev_err(&pdev->dev, "failed to register isp1704 with error %d\n", ret);
 476 
 477         return ret;
 478 }
 479 
 480 static int isp1704_charger_remove(struct platform_device *pdev)
 481 {
 482         struct isp1704_charger *isp = platform_get_drvdata(pdev);
 483 
 484         usb_unregister_notifier(isp->phy, &isp->nb);
 485         power_supply_unregister(isp->psy);
 486         isp1704_charger_set_power(isp, 0);
 487 
 488         return 0;
 489 }
 490 
 491 #ifdef CONFIG_OF
 492 static const struct of_device_id omap_isp1704_of_match[] = {
 493         { .compatible = "nxp,isp1704", },
 494         { .compatible = "nxp,isp1707", },
 495         {},
 496 };
 497 MODULE_DEVICE_TABLE(of, omap_isp1704_of_match);
 498 #endif
 499 
 500 static struct platform_driver isp1704_charger_driver = {
 501         .driver = {
 502                 .name = "isp1704_charger",
 503                 .of_match_table = of_match_ptr(omap_isp1704_of_match),
 504         },
 505         .probe = isp1704_charger_probe,
 506         .remove = isp1704_charger_remove,
 507 };
 508 
 509 module_platform_driver(isp1704_charger_driver);
 510 
 511 MODULE_ALIAS("platform:isp1704_charger");
 512 MODULE_AUTHOR("Nokia Corporation");
 513 MODULE_DESCRIPTION("ISP170x USB Charger driver");
 514 MODULE_LICENSE("GPL");

/* [<][>][^][v][top][bottom][index][help] */