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