Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

phy-sun4i-usb: Add support for monitoring vbus via a power-supply

On some boards there is no vbus_det gpio pin, instead vbus-detection for
otg can be done via the pmic.

This commit adds support for monitoring vbus_det via the power_supply
exported by the pmic, enabling support for otg on these boards.

Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>

authored by

Hans de Goede and committed by
Kishon Vijay Abraham I
8665c18b 1aedf3a7

+71 -7
+1
Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt
··· 29 29 Optional properties: 30 30 - usb0_id_det-gpios : gpio phandle for reading the otg id pin value 31 31 - usb0_vbus_det-gpios : gpio phandle for detecting the presence of usb0 vbus 32 + - usb0_vbus_power-supply: power-supply phandle for usb0 vbus presence detect 32 33 - usb0_vbus-supply : regulator phandle for controller usb0 vbus 33 34 - usb1_vbus-supply : regulator phandle for controller usb1 vbus 34 35 - usb2_vbus-supply : regulator phandle for controller usb2 vbus
+1
drivers/phy/Kconfig
··· 209 209 depends on ARCH_SUNXI && HAS_IOMEM && OF 210 210 depends on RESET_CONTROLLER 211 211 depends on EXTCON 212 + depends on POWER_SUPPLY 212 213 select GENERIC_PHY 213 214 help 214 215 Enable this to support the transceiver that is part of Allwinner
+69 -7
drivers/phy/phy-sun4i-usb.c
··· 36 36 #include <linux/phy/phy.h> 37 37 #include <linux/phy/phy-sun4i-usb.h> 38 38 #include <linux/platform_device.h> 39 + #include <linux/power_supply.h> 39 40 #include <linux/regulator/consumer.h> 40 41 #include <linux/reset.h> 41 42 #include <linux/workqueue.h> ··· 109 108 bool phy0_poll; 110 109 struct gpio_desc *id_det_gpio; 111 110 struct gpio_desc *vbus_det_gpio; 111 + struct power_supply *vbus_power_supply; 112 + struct notifier_block vbus_power_nb; 113 + bool vbus_power_nb_registered; 112 114 int id_det_irq; 113 115 int vbus_det_irq; 114 116 int id_det; ··· 356 352 .owner = THIS_MODULE, 357 353 }; 358 354 355 + static int sun4i_usb_phy0_get_vbus_det(struct sun4i_usb_phy_data *data) 356 + { 357 + if (data->vbus_det_gpio) 358 + return gpiod_get_value_cansleep(data->vbus_det_gpio); 359 + 360 + if (data->vbus_power_supply) { 361 + union power_supply_propval val; 362 + int r; 363 + 364 + r = power_supply_get_property(data->vbus_power_supply, 365 + POWER_SUPPLY_PROP_PRESENT, &val); 366 + if (r == 0) 367 + return val.intval; 368 + } 369 + 370 + /* Fallback: report vbus as high */ 371 + return 1; 372 + } 373 + 374 + static bool sun4i_usb_phy0_have_vbus_det(struct sun4i_usb_phy_data *data) 375 + { 376 + return data->vbus_det_gpio || data->vbus_power_supply; 377 + } 378 + 359 379 static void sun4i_usb_phy0_id_vbus_det_scan(struct work_struct *work) 360 380 { 361 381 struct sun4i_usb_phy_data *data = ··· 388 360 int id_det, vbus_det, id_notify = 0, vbus_notify = 0; 389 361 390 362 id_det = gpiod_get_value_cansleep(data->id_det_gpio); 391 - if (data->vbus_det_gpio) 392 - vbus_det = gpiod_get_value_cansleep(data->vbus_det_gpio); 393 - else 394 - vbus_det = 1; /* Report vbus as high */ 363 + vbus_det = sun4i_usb_phy0_get_vbus_det(data); 395 364 396 365 mutex_lock(&phy0->mutex); 397 366 ··· 403 378 * without vbus detection report vbus low for long enough for 404 379 * the musb-ip to end the current device session. 405 380 */ 406 - if (!data->vbus_det_gpio && id_det == 0) { 381 + if (!sun4i_usb_phy0_have_vbus_det(data) && id_det == 0) { 407 382 sun4i_usb_phy0_set_vbus_detect(phy0, 0); 408 383 msleep(200); 409 384 sun4i_usb_phy0_set_vbus_detect(phy0, 1); ··· 429 404 * without vbus detection report vbus low for long enough to 430 405 * the musb-ip to end the current host session. 431 406 */ 432 - if (!data->vbus_det_gpio && id_det == 1) { 407 + if (!sun4i_usb_phy0_have_vbus_det(data) && id_det == 1) { 433 408 mutex_lock(&phy0->mutex); 434 409 sun4i_usb_phy0_set_vbus_detect(phy0, 0); 435 410 msleep(1000); ··· 455 430 return IRQ_HANDLED; 456 431 } 457 432 433 + static int sun4i_usb_phy0_vbus_notify(struct notifier_block *nb, 434 + unsigned long val, void *v) 435 + { 436 + struct sun4i_usb_phy_data *data = 437 + container_of(nb, struct sun4i_usb_phy_data, vbus_power_nb); 438 + struct power_supply *psy = v; 439 + 440 + /* Properties on the vbus_power_supply changed, scan vbus_det */ 441 + if (val == PSY_EVENT_PROP_CHANGED && psy == data->vbus_power_supply) 442 + mod_delayed_work(system_wq, &data->detect, DEBOUNCE_TIME); 443 + 444 + return NOTIFY_OK; 445 + } 446 + 458 447 static struct phy *sun4i_usb_phy_xlate(struct device *dev, 459 448 struct of_phandle_args *args) 460 449 { ··· 485 446 struct device *dev = &pdev->dev; 486 447 struct sun4i_usb_phy_data *data = dev_get_drvdata(dev); 487 448 449 + if (data->vbus_power_nb_registered) 450 + power_supply_unreg_notifier(&data->vbus_power_nb); 488 451 if (data->id_det_irq >= 0) 489 452 devm_free_irq(dev, data->id_det_irq, data); 490 453 if (data->vbus_det_irq >= 0) ··· 563 522 data->vbus_det_gpio = NULL; 564 523 } 565 524 525 + if (of_find_property(np, "usb0_vbus_power-supply", NULL)) { 526 + data->vbus_power_supply = devm_power_supply_get_by_phandle(dev, 527 + "usb0_vbus_power-supply"); 528 + if (IS_ERR(data->vbus_power_supply)) 529 + return PTR_ERR(data->vbus_power_supply); 530 + 531 + if (!data->vbus_power_supply) 532 + return -EPROBE_DEFER; 533 + } 534 + 566 535 /* vbus_det without id_det makes no sense, and is not supported */ 567 - if (data->vbus_det_gpio && !data->id_det_gpio) { 536 + if (sun4i_usb_phy0_have_vbus_det(data) && !data->id_det_gpio) { 568 537 dev_err(dev, "usb0_id_det missing or invalid\n"); 569 538 return -ENODEV; 570 539 } ··· 669 618 sun4i_usb_phy_remove(pdev); /* Stop detect work */ 670 619 return ret; 671 620 } 621 + } 622 + 623 + if (data->vbus_power_supply) { 624 + data->vbus_power_nb.notifier_call = sun4i_usb_phy0_vbus_notify; 625 + data->vbus_power_nb.priority = 0; 626 + ret = power_supply_reg_notifier(&data->vbus_power_nb); 627 + if (ret) { 628 + sun4i_usb_phy_remove(pdev); /* Stop detect work */ 629 + return ret; 630 + } 631 + data->vbus_power_nb_registered = true; 672 632 } 673 633 674 634 phy_provider = devm_of_phy_provider_register(dev, sun4i_usb_phy_xlate);