phy-sun4i-usb: peripheral-mode + a31 otg workaround
Subsystems (2)
v5 cover phy-sun4i-usb: peripheral-mode + a31 otg workaround
Hi Kishon,
The "USB: Fix of_usb_get_dr_mode_by_phy with a shared phy block" patch on which this series depends is in usb-next now:
Can you please merge this series?
Regards,
Hans
PATCH RESEND v5 1/2 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.
---
Changes in v2:
-Adjust for of_usb_get_dr_mode_by_phy now taking an args0 parameter Changes in v3:
-Only toggle the phy vbus-det bit on id-change on systems without vbus-det
when in otg mode
Changes in v4:
-No changes
Changes in v5:
-Added Kishon's Acked-by
--- drivers/phy/phy-sun4i-usb.c | 68 ++++++++++++++++++++++++++++++--------------- 1 file changed, 46 insertions(+), 22 deletions(-)
drivers/phy/phy-sun4i-usb.c +46 -22
@@ -40,6 +40,7 @@#include <linux/power_supply.h> #include <linux/regulator/consumer.h> #include <linux/reset.h> +#include <linux/usb/of.h> #include <linux/workqueue.h> #define REG_ISCR 0x00
@@ -109,6 +110,7 @@ struct sun4i_usb_phy_cfg {struct sun4i_usb_phy_data {void __iomem *base;const struct sun4i_usb_phy_cfg *cfg; +enum usb_dr_mode dr_mode;struct mutex mutex;struct sun4i_usb_phy {struct phy *phy;
@@ -119,6 +121,7 @@ struct sun4i_usb_phy_data {bool regulator_on;int index; } phys[MAX_PHYS ]; +int first_phy;/* phy0 / otg related variables */ struct extcon_dev *extcon;bool phy0_init;
@@ -285,16 +288,10 @@ static int sun4i_usb_phy_init(struct phy *_phy)sun4i_usb_phy0_update_iscr (_phy,0 ,ISCR_DPDM_PULLUP_EN );sun4i_usb_phy0_update_iscr (_phy,0 ,ISCR_ID_PULLUP_EN ); -if (data->id_det_gpio) { -/* OTG mode, force ISCR and cable state updates */ - data->id_det =-1 ; - data->vbus_det =-1 ; -queue_delayed_work (system_wq, &data->detect,0 ); - }else { -/* Host only mode */ -sun4i_usb_phy0_set_id_detect (_phy,0 ); -sun4i_usb_phy0_set_vbus_detect (_phy,1 ); - } +/* Force ISCR and cable state updates */ + data->id_det =-1 ; + data->vbus_det =-1 ; +queue_delayed_work (system_wq, &data->detect,0 ); }return 0 ;
@@ -319,6 +316,19 @@ static int sun4i_usb_phy_exit(struct phy *_phy)return 0 ; } +static int sun4i_usb_phy0_get_id_det (struct sun4i_usb_phy_data *data) +{ +switch (data->dr_mode) { +case USB_DR_MODE_OTG : +return gpiod_get_value_cansleep (data->id_det_gpio); +case USB_DR_MODE_HOST : +return 0 ; +case USB_DR_MODE_PERIPHERAL : +default : +return 1 ; + } +} +static int sun4i_usb_phy0_get_vbus_det (struct sun4i_usb_phy_data *data) {if (data->vbus_det_gpio)
@@ -414,7 +424,10 @@ static void sun4i_usb_phy0_id_vbus_det_scan(struct work_struct *work)struct phy *phy0 = data->phys[0 ].phy;int id_det, vbus_det, id_notify =0 , vbus_notify =0 ; - id_det =gpiod_get_value_cansleep (data->id_det_gpio); +if (phy0 ==NULL ) +return ; + + id_det =sun4i_usb_phy0_get_id_det (data); vbus_det =sun4i_usb_phy0_get_vbus_det (data);mutex_lock (&phy0->mutex);
@@ -430,7 +443,8 @@ static void sun4i_usb_phy0_id_vbus_det_scan(struct work_struct *work)* without vbus detection report vbus low for long enough for * the musb-ip to end the current device session. */ -if (!sun4i_usb_phy0_have_vbus_det (data) && id_det ==0 ) { +if (data->dr_mode ==USB_DR_MODE_OTG && + !sun4i_usb_phy0_have_vbus_det (data) && id_det ==0 ) {sun4i_usb_phy0_set_vbus_detect (phy0,0 );msleep (200 );sun4i_usb_phy0_set_vbus_detect (phy0,1 );
@@ -456,7 +470,8 @@ static void sun4i_usb_phy0_id_vbus_det_scan(struct work_struct *work)* without vbus detection report vbus low for long enough to * the musb-ip to end the current host session. */ -if (!sun4i_usb_phy0_have_vbus_det (data) && id_det ==1 ) { +if (data->dr_mode ==USB_DR_MODE_OTG && + !sun4i_usb_phy0_have_vbus_det (data) && id_det ==1 ) {mutex_lock (&phy0->mutex);sun4i_usb_phy0_set_vbus_detect (phy0,0 );msleep (1000 );
@@ -501,7 +516,8 @@ static struct phy *sun4i_usb_phy_xlate(struct device *dev, {struct sun4i_usb_phy_data *data =dev_get_drvdata (dev); -if (args->args[0 ] >= data->cfg->num_phys) +if (args->args[0 ] < data->first_phy || + args->args[0 ] >= data->cfg->num_phys)return ERR_PTR (-ENODEV );return data->phys[args->args[0 ]].phy;
@@ -575,13 +591,17 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)return -EPROBE_DEFER ; } -/* vbus_det without id_det makes no sense, and is not supported */ -if (sun4i_usb_phy0_have_vbus_det (data) && !data->id_det_gpio) { -dev_err (dev,"usb0_id_det missing or invalid\n" ); -return -ENODEV ; - } - -if (data->id_det_gpio) { + data->dr_mode =of_usb_get_dr_mode_by_phy (np,0 ); +switch (data->dr_mode) { +case USB_DR_MODE_OTG : +/* otg without id_det makes no sense, and is not supported */ +if (!data->id_det_gpio) { +dev_err (dev,"usb0_id_det missing or invalid\n" ); +return -ENODEV ; + } +/* fall through */ +case USB_DR_MODE_HOST : +case USB_DR_MODE_PERIPHERAL : data->extcon =devm_extcon_dev_allocate (dev, sun4i_usb_phy0_cable);if (IS_ERR (data->extcon))
@@ -592,9 +612,13 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)dev_err (dev,"failed to register extcon: %d\n" , ret);return ret; } +break ; +default : +dev_info (dev,"dr_mode unknown, not registering usb phy0\n" ); + data->first_phy =1 ; } -for (i =0 ; i < data->cfg->num_phys; i++) { +for (i = data->first_phy; i < data->cfg->num_phys; i++) {struct sun4i_usb_phy *phy = data->phys + i;char name[16 ];
Signature · 1 line
2.7.4
PATCH RESEND v5 2/2 phy-sun4i-usb: Add workaround for missing Vbus det interrupts on A31
In reply to Hans de GoedeThe A31 companion pmic (axp221) does not generate vbus change interrupts when the board is driving vbus, so we must poll when using the pmic for vbus-det _and_ we're driving vbus.
---
Changes in v2:
-No changes
Changes in v3:
-No changes
Changes in v4:
-No changes
Changes in v5:
-Rebase on top of latest linux-phy/fixes
-Added Kishon's Acked-by
--- drivers/phy/phy-sun4i-usb.c | 34 ++++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-)
drivers/phy/phy-sun4i-usb.c +24 -10
@@ -95,6 +95,7 @@enum sun4i_usb_phy_type { sun4i_a10_phy, + sun6i_a31_phy, sun8i_a33_phy, sun8i_h3_phy, };
@@ -125,7 +126,6 @@ struct sun4i_usb_phy_data {/* phy0 / otg related variables */ struct extcon_dev *extcon;bool phy0_init; -bool phy0_poll;struct gpio_desc *id_det_gpio;struct gpio_desc *vbus_det_gpio;struct power_supply *vbus_power_supply;
@@ -353,6 +353,24 @@ static bool sun4i_usb_phy0_have_vbus_det(struct sun4i_usb_phy_data *data)return data->vbus_det_gpio || data->vbus_power_supply; } +static bool sun4i_usb_phy0_poll (struct sun4i_usb_phy_data *data) +{ +if ((data->id_det_gpio && data->id_det_irq <=0 ) || + (data->vbus_det_gpio && data->vbus_det_irq <=0 )) +return true; + +/* +* The A31 companion pmic (axp221) does not generate vbus change +* interrupts when the board is driving vbus, so we must poll +* when using the pmic for vbus-det _and_ we're driving vbus. +*/ +if (data->cfg->type == sun6i_a31_phy && + data->vbus_power_supply && data->phys[0 ].regulator_on) +return true; + +return false; +} +static int sun4i_usb_phy_power_on (struct phy *_phy) {struct sun4i_usb_phy *phy =phy_get_drvdata (_phy);
@@ -374,7 +392,7 @@ static int sun4i_usb_phy_power_on(struct phy *_phy) phy->regulator_on = true;/* We must report Vbus high within OTG_TIME_A_WAIT_VRISE msec. */ -if (phy->index ==0 && data->vbus_det_gpio && data->phy0_poll) +if (phy->index ==0 &&sun4i_usb_phy0_poll (data))mod_delayed_work (system_wq, &data->detect,DEBOUNCE_TIME );return 0 ;
@@ -395,7 +413,7 @@ static int sun4i_usb_phy_power_off(struct phy *_phy)* phy0 vbus typically slowly discharges, sometimes this causes the * Vbus gpio to not trigger an edge irq on Vbus off, so force a rescan. */ -if (phy->index ==0 && data->vbus_det_gpio && !data->phy0_poll) +if (phy->index ==0 && !sun4i_usb_phy0_poll (data))mod_delayed_work (system_wq, &data->detect,POLL_TIME );return 0 ;
@@ -483,7 +501,7 @@ static void sun4i_usb_phy0_id_vbus_det_scan(struct work_struct *work)if (vbus_notify)extcon_set_cable_state_ (data->extcon,EXTCON_USB , vbus_det); -if (data->phy0_poll) +if (sun4i_usb_phy0_poll (data))queue_delayed_work (system_wq, &data->detect,POLL_TIME ); }
@@ -668,11 +686,6 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev) } data->id_det_irq =gpiod_to_irq (data->id_det_gpio); - data->vbus_det_irq =gpiod_to_irq (data->vbus_det_gpio); -if ((data->id_det_gpio && data->id_det_irq <=0 ) || - (data->vbus_det_gpio && data->vbus_det_irq <=0 )) - data->phy0_poll = true; -if (data->id_det_irq >0 ) { ret = devm_request_irq(dev, data->id_det_irq, sun4i_usb_phy0_id_vbus_det_irq,
@@ -684,6 +697,7 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev) } } + data->vbus_det_irq =gpiod_to_irq (data->vbus_det_gpio);if (data->vbus_det_irq >0 ) { ret = devm_request_irq(dev, data->vbus_det_irq, sun4i_usb_phy0_id_vbus_det_irq,
@@ -735,7 +749,7 @@ static const struct sun4i_usb_phy_cfg sun5i_a13_cfg = {static const struct sun4i_usb_phy_cfg sun6i_a31_cfg = { .num_phys =3 , - .type = sun4i_a10_phy, + .type = sun6i_a31_phy, .disc_thresh =3 , .phyctl_offset =REG_PHYCTL_A10 , .dedicated_clocks = true,
Signature · 1 line
2.7.4
v5 cover phy-sun4i-usb: peripheral-mode + a31 otg workaround
In reply to Hans de GoedeHi,
Quoted 8 lines · Hans de Goede
Hi Kishon,
The "USB: Fix
of_usb_get_dr_mode_by_phywith a shared phy block" patch on which this series depends is in usb-next now:Can you please merge this series?
This is not applying cleanly against linux-phy -next. Can you rebase and send?
Thanks
Kishon
v5 cover phy-sun4i-usb: peripheral-mode + a31 otg workaround
Hi,
Quoted 5 lines · Kishon Vijay Abraham I
Hi,
Quoted 2 lines · Hans de Goede
Hi Kishon,
Quoted 8 lines
Quoted 6 lines
The "USB: Fix
of_usb_get_dr_mode_by_phywith a shared phy block" patch on which this series depends is in usb-next now:Can you please merge this series?
This is not applying cleanly against linux-phy -next. Can you rebase and send?
I just tried to rebase, the problem is that your -next branch does not contain the fixes from your -fixes branch, specifically it is missing this commit:
Which is causing the conflict you're seeing when trying to merge the patches I send. I can send you fixed patches which apply cleanly to your linux-phy/next but those then will not merge properly with linus' tree.
So it is probably better to at least cherry-pick the commit I've linked to above into your -next, to avoid future merge problems. If you cherry-pick that commit then the patches I've send you should apply cleanly.
Regards,
Hans
v5 cover phy-sun4i-usb: peripheral-mode + a31 otg workaround
Quoted 30 lines · Hans de Goede
Hi,
Quoted 8 lines · Kishon Vijay Abraham I
Hi,
Quoted 5 lines · Hans de Goede
Hi Kishon,
The "USB: Fix
of_usb_get_dr_mode_by_phywith a shared phy block" patch on which this series depends is in usb-next now:Quoted 6 lines
Quoted 4 lines
Can you please merge this series?
This is not applying cleanly against linux-phy -next. Can you rebase and send?
I just tried to rebase, the problem is that your -next branch does not contain the fixes from your -fixes branch, specifically it is missing this commit:
Which is causing the conflict you're seeing when trying to merge the patches I send. I can send you fixed patches which apply cleanly to your linux-phy/next but those then will not merge properly with linus' tree.
Quoted 4 lines
So it is probably better to at least cherry-pick the commit I've linked to above into your -next, to avoid future merge problems. If you cherry-pick that commit then the patches I've send you should apply cleanly.
Ah okay.. I'll rebase my next onto my fixes. Thanks for letting me know this.
Thanks
Kishon