diff options
author | Hans de Goede <hdegoede@redhat.com> | 2016-08-08 15:55:39 -0400 |
---|---|---|
committer | Kishon Vijay Abraham I <kishon@ti.com> | 2016-08-12 05:29:10 -0400 |
commit | b33ecca87df99fa6fff8a1d455de96f436934dcf (patch) | |
tree | 702cb28c5298050a8b159993742fc618491ad496 | |
parent | 29b4817d4018df78086157ea3a55c1d9424a7cfc (diff) |
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>
-rw-r--r-- | drivers/phy/phy-sun4i-usb.c | 68 |
1 files changed, 46 insertions, 22 deletions
diff --git a/drivers/phy/phy-sun4i-usb.c b/drivers/phy/phy-sun4i-usb.c index 0a45bc6088ae..8c7eb335622e 100644 --- a/drivers/phy/phy-sun4i-usb.c +++ b/drivers/phy/phy-sun4i-usb.c | |||
@@ -40,6 +40,7 @@ | |||
40 | #include <linux/power_supply.h> | 40 | #include <linux/power_supply.h> |
41 | #include <linux/regulator/consumer.h> | 41 | #include <linux/regulator/consumer.h> |
42 | #include <linux/reset.h> | 42 | #include <linux/reset.h> |
43 | #include <linux/usb/of.h> | ||
43 | #include <linux/workqueue.h> | 44 | #include <linux/workqueue.h> |
44 | 45 | ||
45 | #define REG_ISCR 0x00 | 46 | #define REG_ISCR 0x00 |
@@ -110,6 +111,7 @@ struct sun4i_usb_phy_cfg { | |||
110 | struct sun4i_usb_phy_data { | 111 | struct sun4i_usb_phy_data { |
111 | void __iomem *base; | 112 | void __iomem *base; |
112 | const struct sun4i_usb_phy_cfg *cfg; | 113 | const struct sun4i_usb_phy_cfg *cfg; |
114 | enum usb_dr_mode dr_mode; | ||
113 | struct mutex mutex; | 115 | struct mutex mutex; |
114 | struct sun4i_usb_phy { | 116 | struct sun4i_usb_phy { |
115 | struct phy *phy; | 117 | struct phy *phy; |
@@ -120,6 +122,7 @@ struct sun4i_usb_phy_data { | |||
120 | bool regulator_on; | 122 | bool regulator_on; |
121 | int index; | 123 | int index; |
122 | } phys[MAX_PHYS]; | 124 | } phys[MAX_PHYS]; |
125 | int first_phy; | ||
123 | /* phy0 / otg related variables */ | 126 | /* phy0 / otg related variables */ |
124 | struct extcon_dev *extcon; | 127 | struct extcon_dev *extcon; |
125 | bool phy0_init; | 128 | bool phy0_init; |
@@ -285,16 +288,10 @@ static int sun4i_usb_phy_init(struct phy *_phy) | |||
285 | sun4i_usb_phy0_update_iscr(_phy, 0, ISCR_DPDM_PULLUP_EN); | 288 | sun4i_usb_phy0_update_iscr(_phy, 0, ISCR_DPDM_PULLUP_EN); |
286 | sun4i_usb_phy0_update_iscr(_phy, 0, ISCR_ID_PULLUP_EN); | 289 | sun4i_usb_phy0_update_iscr(_phy, 0, ISCR_ID_PULLUP_EN); |
287 | 290 | ||
288 | if (data->id_det_gpio) { | 291 | /* Force ISCR and cable state updates */ |
289 | /* OTG mode, force ISCR and cable state updates */ | 292 | data->id_det = -1; |
290 | data->id_det = -1; | 293 | data->vbus_det = -1; |
291 | data->vbus_det = -1; | 294 | queue_delayed_work(system_wq, &data->detect, 0); |
292 | queue_delayed_work(system_wq, &data->detect, 0); | ||
293 | } else { | ||
294 | /* Host only mode */ | ||
295 | sun4i_usb_phy0_set_id_detect(_phy, 0); | ||
296 | sun4i_usb_phy0_set_vbus_detect(_phy, 1); | ||
297 | } | ||
298 | } | 295 | } |
299 | 296 | ||
300 | return 0; | 297 | return 0; |
@@ -319,6 +316,19 @@ static int sun4i_usb_phy_exit(struct phy *_phy) | |||
319 | return 0; | 316 | return 0; |
320 | } | 317 | } |
321 | 318 | ||
319 | static int sun4i_usb_phy0_get_id_det(struct sun4i_usb_phy_data *data) | ||
320 | { | ||
321 | switch (data->dr_mode) { | ||
322 | case USB_DR_MODE_OTG: | ||
323 | return gpiod_get_value_cansleep(data->id_det_gpio); | ||
324 | case USB_DR_MODE_HOST: | ||
325 | return 0; | ||
326 | case USB_DR_MODE_PERIPHERAL: | ||
327 | default: | ||
328 | return 1; | ||
329 | } | ||
330 | } | ||
331 | |||
322 | static int sun4i_usb_phy0_get_vbus_det(struct sun4i_usb_phy_data *data) | 332 | static int sun4i_usb_phy0_get_vbus_det(struct sun4i_usb_phy_data *data) |
323 | { | 333 | { |
324 | if (data->vbus_det_gpio) | 334 | if (data->vbus_det_gpio) |
@@ -432,7 +442,10 @@ static void sun4i_usb_phy0_id_vbus_det_scan(struct work_struct *work) | |||
432 | struct phy *phy0 = data->phys[0].phy; | 442 | struct phy *phy0 = data->phys[0].phy; |
433 | int id_det, vbus_det, id_notify = 0, vbus_notify = 0; | 443 | int id_det, vbus_det, id_notify = 0, vbus_notify = 0; |
434 | 444 | ||
435 | id_det = gpiod_get_value_cansleep(data->id_det_gpio); | 445 | if (phy0 == NULL) |
446 | return; | ||
447 | |||
448 | id_det = sun4i_usb_phy0_get_id_det(data); | ||
436 | vbus_det = sun4i_usb_phy0_get_vbus_det(data); | 449 | vbus_det = sun4i_usb_phy0_get_vbus_det(data); |
437 | 450 | ||
438 | mutex_lock(&phy0->mutex); | 451 | mutex_lock(&phy0->mutex); |
@@ -448,7 +461,8 @@ static void sun4i_usb_phy0_id_vbus_det_scan(struct work_struct *work) | |||
448 | * without vbus detection report vbus low for long enough for | 461 | * without vbus detection report vbus low for long enough for |
449 | * the musb-ip to end the current device session. | 462 | * the musb-ip to end the current device session. |
450 | */ | 463 | */ |
451 | if (!sun4i_usb_phy0_have_vbus_det(data) && id_det == 0) { | 464 | if (data->dr_mode == USB_DR_MODE_OTG && |
465 | !sun4i_usb_phy0_have_vbus_det(data) && id_det == 0) { | ||
452 | sun4i_usb_phy0_set_vbus_detect(phy0, 0); | 466 | sun4i_usb_phy0_set_vbus_detect(phy0, 0); |
453 | msleep(200); | 467 | msleep(200); |
454 | sun4i_usb_phy0_set_vbus_detect(phy0, 1); | 468 | sun4i_usb_phy0_set_vbus_detect(phy0, 1); |
@@ -474,7 +488,8 @@ static void sun4i_usb_phy0_id_vbus_det_scan(struct work_struct *work) | |||
474 | * without vbus detection report vbus low for long enough to | 488 | * without vbus detection report vbus low for long enough to |
475 | * the musb-ip to end the current host session. | 489 | * the musb-ip to end the current host session. |
476 | */ | 490 | */ |
477 | if (!sun4i_usb_phy0_have_vbus_det(data) && id_det == 1) { | 491 | if (data->dr_mode == USB_DR_MODE_OTG && |
492 | !sun4i_usb_phy0_have_vbus_det(data) && id_det == 1) { | ||
478 | mutex_lock(&phy0->mutex); | 493 | mutex_lock(&phy0->mutex); |
479 | sun4i_usb_phy0_set_vbus_detect(phy0, 0); | 494 | sun4i_usb_phy0_set_vbus_detect(phy0, 0); |
480 | msleep(1000); | 495 | msleep(1000); |
@@ -519,7 +534,8 @@ static struct phy *sun4i_usb_phy_xlate(struct device *dev, | |||
519 | { | 534 | { |
520 | struct sun4i_usb_phy_data *data = dev_get_drvdata(dev); | 535 | struct sun4i_usb_phy_data *data = dev_get_drvdata(dev); |
521 | 536 | ||
522 | if (args->args[0] >= data->cfg->num_phys) | 537 | if (args->args[0] < data->first_phy || |
538 | args->args[0] >= data->cfg->num_phys) | ||
523 | return ERR_PTR(-ENODEV); | 539 | return ERR_PTR(-ENODEV); |
524 | 540 | ||
525 | return data->phys[args->args[0]].phy; | 541 | return data->phys[args->args[0]].phy; |
@@ -593,13 +609,17 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev) | |||
593 | return -EPROBE_DEFER; | 609 | return -EPROBE_DEFER; |
594 | } | 610 | } |
595 | 611 | ||
596 | /* vbus_det without id_det makes no sense, and is not supported */ | 612 | data->dr_mode = of_usb_get_dr_mode_by_phy(np, 0); |
597 | if (sun4i_usb_phy0_have_vbus_det(data) && !data->id_det_gpio) { | 613 | switch (data->dr_mode) { |
598 | dev_err(dev, "usb0_id_det missing or invalid\n"); | 614 | case USB_DR_MODE_OTG: |
599 | return -ENODEV; | 615 | /* otg without id_det makes no sense, and is not supported */ |
600 | } | 616 | if (!data->id_det_gpio) { |
601 | 617 | dev_err(dev, "usb0_id_det missing or invalid\n"); | |
602 | if (data->id_det_gpio) { | 618 | return -ENODEV; |
619 | } | ||
620 | /* fall through */ | ||
621 | case USB_DR_MODE_HOST: | ||
622 | case USB_DR_MODE_PERIPHERAL: | ||
603 | data->extcon = devm_extcon_dev_allocate(dev, | 623 | data->extcon = devm_extcon_dev_allocate(dev, |
604 | sun4i_usb_phy0_cable); | 624 | sun4i_usb_phy0_cable); |
605 | if (IS_ERR(data->extcon)) | 625 | if (IS_ERR(data->extcon)) |
@@ -610,9 +630,13 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev) | |||
610 | dev_err(dev, "failed to register extcon: %d\n", ret); | 630 | dev_err(dev, "failed to register extcon: %d\n", ret); |
611 | return ret; | 631 | return ret; |
612 | } | 632 | } |
633 | break; | ||
634 | default: | ||
635 | dev_info(dev, "dr_mode unknown, not registering usb phy0\n"); | ||
636 | data->first_phy = 1; | ||
613 | } | 637 | } |
614 | 638 | ||
615 | for (i = 0; i < data->cfg->num_phys; i++) { | 639 | for (i = data->first_phy; i < data->cfg->num_phys; i++) { |
616 | struct sun4i_usb_phy *phy = data->phys + i; | 640 | struct sun4i_usb_phy *phy = data->phys + i; |
617 | char name[16]; | 641 | char name[16]; |
618 | 642 | ||