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 peripheral-only mode

Use the new of_usb_get_dr_mode_by_phy() function to get the dr_mode
from the musb controller node instead of assuming that having an id_det
gpio means otg mode, and not having one means host mode.

Implement peripheral-only mode by adding a sun4i_usb_phy0_get_id_det
helper which looks at the dr_mode, always registering our extcon and
always monitoring vbus.

If dr_mode is not specified in the dts, do not register phy0 as we then
do not know how to treat it. This is actually a good thing as this means
we will not be registering phy0 on devices where the otg controller is
not enabled in the devicetree.

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

authored by

Hans de Goede and committed by
Kishon Vijay Abraham I
b33ecca8 29b4817d

+46 -22
+46 -22
drivers/phy/phy-sun4i-usb.c
··· 40 40 #include <linux/power_supply.h> 41 41 #include <linux/regulator/consumer.h> 42 42 #include <linux/reset.h> 43 + #include <linux/usb/of.h> 43 44 #include <linux/workqueue.h> 44 45 45 46 #define REG_ISCR 0x00 ··· 111 110 struct sun4i_usb_phy_data { 112 111 void __iomem *base; 113 112 const struct sun4i_usb_phy_cfg *cfg; 113 + enum usb_dr_mode dr_mode; 114 114 struct mutex mutex; 115 115 struct sun4i_usb_phy { 116 116 struct phy *phy; ··· 122 120 bool regulator_on; 123 121 int index; 124 122 } phys[MAX_PHYS]; 123 + int first_phy; 125 124 /* phy0 / otg related variables */ 126 125 struct extcon_dev *extcon; 127 126 bool phy0_init; ··· 288 285 sun4i_usb_phy0_update_iscr(_phy, 0, ISCR_DPDM_PULLUP_EN); 289 286 sun4i_usb_phy0_update_iscr(_phy, 0, ISCR_ID_PULLUP_EN); 290 287 291 - if (data->id_det_gpio) { 292 - /* OTG mode, force ISCR and cable state updates */ 293 - data->id_det = -1; 294 - data->vbus_det = -1; 295 - queue_delayed_work(system_wq, &data->detect, 0); 296 - } else { 297 - /* Host only mode */ 298 - sun4i_usb_phy0_set_id_detect(_phy, 0); 299 - sun4i_usb_phy0_set_vbus_detect(_phy, 1); 300 - } 288 + /* Force ISCR and cable state updates */ 289 + data->id_det = -1; 290 + data->vbus_det = -1; 291 + queue_delayed_work(system_wq, &data->detect, 0); 301 292 } 302 293 303 294 return 0; ··· 314 317 clk_disable_unprepare(phy->clk); 315 318 316 319 return 0; 320 + } 321 + 322 + static int sun4i_usb_phy0_get_id_det(struct sun4i_usb_phy_data *data) 323 + { 324 + switch (data->dr_mode) { 325 + case USB_DR_MODE_OTG: 326 + return gpiod_get_value_cansleep(data->id_det_gpio); 327 + case USB_DR_MODE_HOST: 328 + return 0; 329 + case USB_DR_MODE_PERIPHERAL: 330 + default: 331 + return 1; 332 + } 317 333 } 318 334 319 335 static int sun4i_usb_phy0_get_vbus_det(struct sun4i_usb_phy_data *data) ··· 442 432 struct phy *phy0 = data->phys[0].phy; 443 433 int id_det, vbus_det, id_notify = 0, vbus_notify = 0; 444 434 445 - id_det = gpiod_get_value_cansleep(data->id_det_gpio); 435 + if (phy0 == NULL) 436 + return; 437 + 438 + id_det = sun4i_usb_phy0_get_id_det(data); 446 439 vbus_det = sun4i_usb_phy0_get_vbus_det(data); 447 440 448 441 mutex_lock(&phy0->mutex); ··· 461 448 * without vbus detection report vbus low for long enough for 462 449 * the musb-ip to end the current device session. 463 450 */ 464 - if (!sun4i_usb_phy0_have_vbus_det(data) && id_det == 0) { 451 + if (data->dr_mode == USB_DR_MODE_OTG && 452 + !sun4i_usb_phy0_have_vbus_det(data) && id_det == 0) { 465 453 sun4i_usb_phy0_set_vbus_detect(phy0, 0); 466 454 msleep(200); 467 455 sun4i_usb_phy0_set_vbus_detect(phy0, 1); ··· 488 474 * without vbus detection report vbus low for long enough to 489 475 * the musb-ip to end the current host session. 490 476 */ 491 - if (!sun4i_usb_phy0_have_vbus_det(data) && id_det == 1) { 477 + if (data->dr_mode == USB_DR_MODE_OTG && 478 + !sun4i_usb_phy0_have_vbus_det(data) && id_det == 1) { 492 479 mutex_lock(&phy0->mutex); 493 480 sun4i_usb_phy0_set_vbus_detect(phy0, 0); 494 481 msleep(1000); ··· 534 519 { 535 520 struct sun4i_usb_phy_data *data = dev_get_drvdata(dev); 536 521 537 - if (args->args[0] >= data->cfg->num_phys) 522 + if (args->args[0] < data->first_phy || 523 + args->args[0] >= data->cfg->num_phys) 538 524 return ERR_PTR(-ENODEV); 539 525 540 526 return data->phys[args->args[0]].phy; ··· 609 593 return -EPROBE_DEFER; 610 594 } 611 595 612 - /* vbus_det without id_det makes no sense, and is not supported */ 613 - if (sun4i_usb_phy0_have_vbus_det(data) && !data->id_det_gpio) { 614 - dev_err(dev, "usb0_id_det missing or invalid\n"); 615 - return -ENODEV; 616 - } 617 - 618 - if (data->id_det_gpio) { 596 + data->dr_mode = of_usb_get_dr_mode_by_phy(np, 0); 597 + switch (data->dr_mode) { 598 + case USB_DR_MODE_OTG: 599 + /* otg without id_det makes no sense, and is not supported */ 600 + if (!data->id_det_gpio) { 601 + dev_err(dev, "usb0_id_det missing or invalid\n"); 602 + return -ENODEV; 603 + } 604 + /* fall through */ 605 + case USB_DR_MODE_HOST: 606 + case USB_DR_MODE_PERIPHERAL: 619 607 data->extcon = devm_extcon_dev_allocate(dev, 620 608 sun4i_usb_phy0_cable); 621 609 if (IS_ERR(data->extcon)) ··· 630 610 dev_err(dev, "failed to register extcon: %d\n", ret); 631 611 return ret; 632 612 } 613 + break; 614 + default: 615 + dev_info(dev, "dr_mode unknown, not registering usb phy0\n"); 616 + data->first_phy = 1; 633 617 } 634 618 635 - for (i = 0; i < data->cfg->num_phys; i++) { 619 + for (i = data->first_phy; i < data->cfg->num_phys; i++) { 636 620 struct sun4i_usb_phy *phy = data->phys + i; 637 621 char name[16]; 638 622