Message ID | 1471288892-21702-8-git-send-email-hdegoede@redhat.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Hi, On Mon, Aug 15, 2016 at 09:21:32PM +0200, Hans de Goede wrote: > This allows run-time dr_mode switching support via the "mode" musb > sysfs attribute. > > Signed-off-by: Hans de Goede <hdegoede@redhat.com> > --- > drivers/usb/musb/sunxi.c | 52 ++++++++++++++++++++++++++++++++++++++++++++---- > 1 file changed, 48 insertions(+), 4 deletions(-) > > diff --git a/drivers/usb/musb/sunxi.c b/drivers/usb/musb/sunxi.c > index c6ee166..1fe7451 100644 > --- a/drivers/usb/musb/sunxi.c > +++ b/drivers/usb/musb/sunxi.c > @@ -74,6 +74,7 @@ > #define SUNXI_MUSB_FL_HAS_SRAM 5 > #define SUNXI_MUSB_FL_HAS_RESET 6 > #define SUNXI_MUSB_FL_NO_CONFIGDATA 7 > +#define SUNXI_MUSB_FL_PHY_MODE_PEND 8 > > /* Our read/write methods need access and do not get passed in a musb ref :| */ > static struct musb *sunxi_musb; > @@ -87,6 +88,7 @@ struct sunxi_glue { > struct phy *phy; > struct platform_device *usb_phy; > struct usb_phy *xceiv; > + enum phy_mode phy_mode; > unsigned long flags; > struct work_struct work; > struct extcon_dev *extcon; > @@ -140,6 +142,9 @@ static void sunxi_musb_work(struct work_struct *work) > clear_bit(SUNXI_MUSB_FL_PHY_ON, &glue->flags); > } > } > + > + if (test_and_clear_bit(SUNXI_MUSB_FL_PHY_MODE_PEND, &glue->flags)) > + phy_set_mode(glue->phy, glue->phy_mode); > } > > static void sunxi_musb_set_vbus(struct musb *musb, int is_on) > @@ -341,6 +346,41 @@ static void sunxi_musb_dma_controller_destroy(struct dma_controller *c) > { > } > > +static int sunxi_musb_set_mode(struct musb *musb, u8 mode) > +{ > + struct sunxi_glue *glue = dev_get_drvdata(musb->controller->parent); > + enum phy_mode new_mode; > + > + switch (mode) { > + case MUSB_HOST: new_mode = PHY_MODE_USB_HOST; break; > + case MUSB_PERIPHERAL: new_mode = PHY_MODE_USB_DEVICE; break; > + case MUSB_OTG: new_mode = PHY_MODE_USB_OTG; break; Please fix the code style as commented in patch 4/7. > + default: > + dev_err(musb->controller->parent, > + "Error requested mode not supported by this kernel\n"); > + return -EINVAL; > + } > + > + if (glue->phy_mode == new_mode) > + return 0; > + > + if (musb->port_mode != MUSB_PORT_MODE_DUAL_ROLE) { > + dev_err(musb->controller->parent, > + "Error changing modes is only supported in dual role mode\n"); > + return -EINVAL; > + } > + > + /* > + * phy_set_mode may sleep, and we're called with a spinlock held, > + * so let sunxi_musb_work deal with it. > + */ > + glue->phy_mode = new_mode; > + set_bit(SUNXI_MUSB_FL_PHY_MODE_PEND, &glue->flags); > + schedule_work(&glue->work); When switching from host to peripheral mode, if an usb device is still plugged and enumerated, how do you handle the device disconnect? Regards, -Bin. > + > + return 0; > +} > + > /* > * sunxi musb register layout > * 0x00 - 0x17 fifo regs, 1 long per fifo > @@ -568,6 +608,7 @@ static const struct musb_platform_ops sunxi_musb_ops = { > .writew = sunxi_musb_writew, > .dma_init = sunxi_musb_dma_controller_create, > .dma_exit = sunxi_musb_dma_controller_destroy, > + .set_mode = sunxi_musb_set_mode, > .set_vbus = sunxi_musb_set_vbus, > .pre_root_reset_end = sunxi_musb_pre_root_reset_end, > .post_root_reset_end = sunxi_musb_post_root_reset_end, > @@ -614,21 +655,28 @@ static int sunxi_musb_probe(struct platform_device *pdev) > return -EINVAL; > } > > + glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL); > + if (!glue) > + return -ENOMEM; > + > memset(&pdata, 0, sizeof(pdata)); > switch (usb_get_dr_mode(&pdev->dev)) { > #if defined CONFIG_USB_MUSB_DUAL_ROLE || defined CONFIG_USB_MUSB_HOST > case USB_DR_MODE_HOST: > pdata.mode = MUSB_PORT_MODE_HOST; > + glue->phy_mode = PHY_MODE_USB_HOST; > break; > #endif > #if defined CONFIG_USB_MUSB_DUAL_ROLE || defined CONFIG_USB_MUSB_GADGET > case USB_DR_MODE_PERIPHERAL: > pdata.mode = MUSB_PORT_MODE_GADGET; > + glue->phy_mode = PHY_MODE_USB_DEVICE; > break; > #endif > #ifdef CONFIG_USB_MUSB_DUAL_ROLE > case USB_DR_MODE_OTG: > pdata.mode = MUSB_PORT_MODE_DUAL_ROLE; > + glue->phy_mode = PHY_MODE_USB_OTG; > break; > #endif > default: > @@ -638,10 +686,6 @@ static int sunxi_musb_probe(struct platform_device *pdev) > pdata.platform_ops = &sunxi_musb_ops; > pdata.config = &sunxi_musb_hdrc_config; > > - glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL); > - if (!glue) > - return -ENOMEM; > - > glue->dev = &pdev->dev; > INIT_WORK(&glue->work, sunxi_musb_work); > glue->host_nb.notifier_call = sunxi_musb_host_notifier; > -- > 2.7.4 >
Hi, On 19-08-16 23:30, Bin Liu wrote: > Hi, > > On Mon, Aug 15, 2016 at 09:21:32PM +0200, Hans de Goede wrote: >> This allows run-time dr_mode switching support via the "mode" musb >> sysfs attribute. >> >> Signed-off-by: Hans de Goede <hdegoede@redhat.com> >> --- >> drivers/usb/musb/sunxi.c | 52 ++++++++++++++++++++++++++++++++++++++++++++---- >> 1 file changed, 48 insertions(+), 4 deletions(-) >> >> diff --git a/drivers/usb/musb/sunxi.c b/drivers/usb/musb/sunxi.c >> index c6ee166..1fe7451 100644 >> --- a/drivers/usb/musb/sunxi.c >> +++ b/drivers/usb/musb/sunxi.c >> @@ -74,6 +74,7 @@ >> #define SUNXI_MUSB_FL_HAS_SRAM 5 >> #define SUNXI_MUSB_FL_HAS_RESET 6 >> #define SUNXI_MUSB_FL_NO_CONFIGDATA 7 >> +#define SUNXI_MUSB_FL_PHY_MODE_PEND 8 >> >> /* Our read/write methods need access and do not get passed in a musb ref :| */ >> static struct musb *sunxi_musb; >> @@ -87,6 +88,7 @@ struct sunxi_glue { >> struct phy *phy; >> struct platform_device *usb_phy; >> struct usb_phy *xceiv; >> + enum phy_mode phy_mode; >> unsigned long flags; >> struct work_struct work; >> struct extcon_dev *extcon; >> @@ -140,6 +142,9 @@ static void sunxi_musb_work(struct work_struct *work) >> clear_bit(SUNXI_MUSB_FL_PHY_ON, &glue->flags); >> } >> } >> + >> + if (test_and_clear_bit(SUNXI_MUSB_FL_PHY_MODE_PEND, &glue->flags)) >> + phy_set_mode(glue->phy, glue->phy_mode); >> } >> >> static void sunxi_musb_set_vbus(struct musb *musb, int is_on) >> @@ -341,6 +346,41 @@ static void sunxi_musb_dma_controller_destroy(struct dma_controller *c) >> { >> } >> >> +static int sunxi_musb_set_mode(struct musb *musb, u8 mode) >> +{ >> + struct sunxi_glue *glue = dev_get_drvdata(musb->controller->parent); >> + enum phy_mode new_mode; >> + >> + switch (mode) { >> + case MUSB_HOST: new_mode = PHY_MODE_USB_HOST; break; >> + case MUSB_PERIPHERAL: new_mode = PHY_MODE_USB_DEVICE; break; >> + case MUSB_OTG: new_mode = PHY_MODE_USB_OTG; break; > > Please fix the code style as commented in patch 4/7. Ok I will send a new version with this fixed. > >> + default: >> + dev_err(musb->controller->parent, >> + "Error requested mode not supported by this kernel\n"); >> + return -EINVAL; >> + } >> + >> + if (glue->phy_mode == new_mode) >> + return 0; >> + >> + if (musb->port_mode != MUSB_PORT_MODE_DUAL_ROLE) { >> + dev_err(musb->controller->parent, >> + "Error changing modes is only supported in dual role mode\n"); >> + return -EINVAL; >> + } >> + >> + /* >> + * phy_set_mode may sleep, and we're called with a spinlock held, >> + * so let sunxi_musb_work deal with it. >> + */ >> + glue->phy_mode = new_mode; >> + set_bit(SUNXI_MUSB_FL_PHY_MODE_PEND, &glue->flags); >> + schedule_work(&glue->work); > > When switching from host to peripheral mode, if an usb device is still > plugged and enumerated, how do you handle the device disconnect? The phy code will report vbus low for long enough for the musb to end the current session. It already does this for boards which do not have working vbus detection. Regards, Hans > > Regards, > -Bin. > >> + >> + return 0; >> +} >> + >> /* >> * sunxi musb register layout >> * 0x00 - 0x17 fifo regs, 1 long per fifo >> @@ -568,6 +608,7 @@ static const struct musb_platform_ops sunxi_musb_ops = { >> .writew = sunxi_musb_writew, >> .dma_init = sunxi_musb_dma_controller_create, >> .dma_exit = sunxi_musb_dma_controller_destroy, >> + .set_mode = sunxi_musb_set_mode, >> .set_vbus = sunxi_musb_set_vbus, >> .pre_root_reset_end = sunxi_musb_pre_root_reset_end, >> .post_root_reset_end = sunxi_musb_post_root_reset_end, >> @@ -614,21 +655,28 @@ static int sunxi_musb_probe(struct platform_device *pdev) >> return -EINVAL; >> } >> >> + glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL); >> + if (!glue) >> + return -ENOMEM; >> + >> memset(&pdata, 0, sizeof(pdata)); >> switch (usb_get_dr_mode(&pdev->dev)) { >> #if defined CONFIG_USB_MUSB_DUAL_ROLE || defined CONFIG_USB_MUSB_HOST >> case USB_DR_MODE_HOST: >> pdata.mode = MUSB_PORT_MODE_HOST; >> + glue->phy_mode = PHY_MODE_USB_HOST; >> break; >> #endif >> #if defined CONFIG_USB_MUSB_DUAL_ROLE || defined CONFIG_USB_MUSB_GADGET >> case USB_DR_MODE_PERIPHERAL: >> pdata.mode = MUSB_PORT_MODE_GADGET; >> + glue->phy_mode = PHY_MODE_USB_DEVICE; >> break; >> #endif >> #ifdef CONFIG_USB_MUSB_DUAL_ROLE >> case USB_DR_MODE_OTG: >> pdata.mode = MUSB_PORT_MODE_DUAL_ROLE; >> + glue->phy_mode = PHY_MODE_USB_OTG; >> break; >> #endif >> default: >> @@ -638,10 +686,6 @@ static int sunxi_musb_probe(struct platform_device *pdev) >> pdata.platform_ops = &sunxi_musb_ops; >> pdata.config = &sunxi_musb_hdrc_config; >> >> - glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL); >> - if (!glue) >> - return -ENOMEM; >> - >> glue->dev = &pdev->dev; >> INIT_WORK(&glue->work, sunxi_musb_work); >> glue->host_nb.notifier_call = sunxi_musb_host_notifier; >> -- >> 2.7.4 >>
Hi, On Sun, Aug 21, 2016 at 12:10:26PM +0200, Hans de Goede wrote: > Hi, > > On 19-08-16 23:30, Bin Liu wrote: > >Hi, > > > >On Mon, Aug 15, 2016 at 09:21:32PM +0200, Hans de Goede wrote: > >>This allows run-time dr_mode switching support via the "mode" musb > >>sysfs attribute. > >> > >>Signed-off-by: Hans de Goede <hdegoede@redhat.com> > >>--- > >> drivers/usb/musb/sunxi.c | 52 ++++++++++++++++++++++++++++++++++++++++++++---- > >> 1 file changed, 48 insertions(+), 4 deletions(-) > >> > >>diff --git a/drivers/usb/musb/sunxi.c b/drivers/usb/musb/sunxi.c > >>index c6ee166..1fe7451 100644 > >>--- a/drivers/usb/musb/sunxi.c > >>+++ b/drivers/usb/musb/sunxi.c > >>@@ -74,6 +74,7 @@ > >> #define SUNXI_MUSB_FL_HAS_SRAM 5 > >> #define SUNXI_MUSB_FL_HAS_RESET 6 > >> #define SUNXI_MUSB_FL_NO_CONFIGDATA 7 > >>+#define SUNXI_MUSB_FL_PHY_MODE_PEND 8 > >> > >> /* Our read/write methods need access and do not get passed in a musb ref :| */ > >> static struct musb *sunxi_musb; > >>@@ -87,6 +88,7 @@ struct sunxi_glue { > >> struct phy *phy; > >> struct platform_device *usb_phy; > >> struct usb_phy *xceiv; > >>+ enum phy_mode phy_mode; > >> unsigned long flags; > >> struct work_struct work; > >> struct extcon_dev *extcon; > >>@@ -140,6 +142,9 @@ static void sunxi_musb_work(struct work_struct *work) > >> clear_bit(SUNXI_MUSB_FL_PHY_ON, &glue->flags); > >> } > >> } > >>+ > >>+ if (test_and_clear_bit(SUNXI_MUSB_FL_PHY_MODE_PEND, &glue->flags)) > >>+ phy_set_mode(glue->phy, glue->phy_mode); > >> } > >> > >> static void sunxi_musb_set_vbus(struct musb *musb, int is_on) > >>@@ -341,6 +346,41 @@ static void sunxi_musb_dma_controller_destroy(struct dma_controller *c) > >> { > >> } > >> > >>+static int sunxi_musb_set_mode(struct musb *musb, u8 mode) > >>+{ > >>+ struct sunxi_glue *glue = dev_get_drvdata(musb->controller->parent); > >>+ enum phy_mode new_mode; > >>+ > >>+ switch (mode) { > >>+ case MUSB_HOST: new_mode = PHY_MODE_USB_HOST; break; > >>+ case MUSB_PERIPHERAL: new_mode = PHY_MODE_USB_DEVICE; break; > >>+ case MUSB_OTG: new_mode = PHY_MODE_USB_OTG; break; > > > >Please fix the code style as commented in patch 4/7. > > Ok I will send a new version with this fixed. > > > > >>+ default: > >>+ dev_err(musb->controller->parent, > >>+ "Error requested mode not supported by this kernel\n"); > >>+ return -EINVAL; > >>+ } > >>+ > >>+ if (glue->phy_mode == new_mode) > >>+ return 0; > >>+ > >>+ if (musb->port_mode != MUSB_PORT_MODE_DUAL_ROLE) { > >>+ dev_err(musb->controller->parent, > >>+ "Error changing modes is only supported in dual role mode\n"); > >>+ return -EINVAL; > >>+ } > >>+ > >>+ /* > >>+ * phy_set_mode may sleep, and we're called with a spinlock held, > >>+ * so let sunxi_musb_work deal with it. > >>+ */ > >>+ glue->phy_mode = new_mode; > >>+ set_bit(SUNXI_MUSB_FL_PHY_MODE_PEND, &glue->flags); > >>+ schedule_work(&glue->work); > > > >When switching from host to peripheral mode, if an usb device is still > >plugged and enumerated, how do you handle the device disconnect? > > The phy code will report vbus low for long enough for the musb to end > the current session. It already does this for boards which do not > have working vbus detection. But you didn't disconnect DP/DM, right? then musb detects vbus is gone without receiving disconnect event, this is vbus error case, not a normal teardown. Regards, -Bin. > > Regards, > > Hans > > > > > >Regards, > >-Bin. > > > >>+ > >>+ return 0; > >>+} > >>+ > >> /* > >> * sunxi musb register layout > >> * 0x00 - 0x17 fifo regs, 1 long per fifo > >>@@ -568,6 +608,7 @@ static const struct musb_platform_ops sunxi_musb_ops = { > >> .writew = sunxi_musb_writew, > >> .dma_init = sunxi_musb_dma_controller_create, > >> .dma_exit = sunxi_musb_dma_controller_destroy, > >>+ .set_mode = sunxi_musb_set_mode, > >> .set_vbus = sunxi_musb_set_vbus, > >> .pre_root_reset_end = sunxi_musb_pre_root_reset_end, > >> .post_root_reset_end = sunxi_musb_post_root_reset_end, > >>@@ -614,21 +655,28 @@ static int sunxi_musb_probe(struct platform_device *pdev) > >> return -EINVAL; > >> } > >> > >>+ glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL); > >>+ if (!glue) > >>+ return -ENOMEM; > >>+ > >> memset(&pdata, 0, sizeof(pdata)); > >> switch (usb_get_dr_mode(&pdev->dev)) { > >> #if defined CONFIG_USB_MUSB_DUAL_ROLE || defined CONFIG_USB_MUSB_HOST > >> case USB_DR_MODE_HOST: > >> pdata.mode = MUSB_PORT_MODE_HOST; > >>+ glue->phy_mode = PHY_MODE_USB_HOST; > >> break; > >> #endif > >> #if defined CONFIG_USB_MUSB_DUAL_ROLE || defined CONFIG_USB_MUSB_GADGET > >> case USB_DR_MODE_PERIPHERAL: > >> pdata.mode = MUSB_PORT_MODE_GADGET; > >>+ glue->phy_mode = PHY_MODE_USB_DEVICE; > >> break; > >> #endif > >> #ifdef CONFIG_USB_MUSB_DUAL_ROLE > >> case USB_DR_MODE_OTG: > >> pdata.mode = MUSB_PORT_MODE_DUAL_ROLE; > >>+ glue->phy_mode = PHY_MODE_USB_OTG; > >> break; > >> #endif > >> default: > >>@@ -638,10 +686,6 @@ static int sunxi_musb_probe(struct platform_device *pdev) > >> pdata.platform_ops = &sunxi_musb_ops; > >> pdata.config = &sunxi_musb_hdrc_config; > >> > >>- glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL); > >>- if (!glue) > >>- return -ENOMEM; > >>- > >> glue->dev = &pdev->dev; > >> INIT_WORK(&glue->work, sunxi_musb_work); > >> glue->host_nb.notifier_call = sunxi_musb_host_notifier; > >>-- > >>2.7.4 > >> > -- > To unsubscribe from this list: send the line "unsubscribe linux-usb" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html
Hi, On 22-08-16 16:11, Bin Liu wrote: > Hi, > > On Sun, Aug 21, 2016 at 12:10:26PM +0200, Hans de Goede wrote: >> Hi, >> >> On 19-08-16 23:30, Bin Liu wrote: >>> Hi, >>> >>> On Mon, Aug 15, 2016 at 09:21:32PM +0200, Hans de Goede wrote: >>>> This allows run-time dr_mode switching support via the "mode" musb >>>> sysfs attribute. >>>> >>>> Signed-off-by: Hans de Goede <hdegoede@redhat.com> >>>> --- >>>> drivers/usb/musb/sunxi.c | 52 ++++++++++++++++++++++++++++++++++++++++++++---- >>>> 1 file changed, 48 insertions(+), 4 deletions(-) >>>> >>>> diff --git a/drivers/usb/musb/sunxi.c b/drivers/usb/musb/sunxi.c >>>> index c6ee166..1fe7451 100644 >>>> --- a/drivers/usb/musb/sunxi.c >>>> +++ b/drivers/usb/musb/sunxi.c >>>> @@ -74,6 +74,7 @@ >>>> #define SUNXI_MUSB_FL_HAS_SRAM 5 >>>> #define SUNXI_MUSB_FL_HAS_RESET 6 >>>> #define SUNXI_MUSB_FL_NO_CONFIGDATA 7 >>>> +#define SUNXI_MUSB_FL_PHY_MODE_PEND 8 >>>> >>>> /* Our read/write methods need access and do not get passed in a musb ref :| */ >>>> static struct musb *sunxi_musb; >>>> @@ -87,6 +88,7 @@ struct sunxi_glue { >>>> struct phy *phy; >>>> struct platform_device *usb_phy; >>>> struct usb_phy *xceiv; >>>> + enum phy_mode phy_mode; >>>> unsigned long flags; >>>> struct work_struct work; >>>> struct extcon_dev *extcon; >>>> @@ -140,6 +142,9 @@ static void sunxi_musb_work(struct work_struct *work) >>>> clear_bit(SUNXI_MUSB_FL_PHY_ON, &glue->flags); >>>> } >>>> } >>>> + >>>> + if (test_and_clear_bit(SUNXI_MUSB_FL_PHY_MODE_PEND, &glue->flags)) >>>> + phy_set_mode(glue->phy, glue->phy_mode); >>>> } >>>> >>>> static void sunxi_musb_set_vbus(struct musb *musb, int is_on) >>>> @@ -341,6 +346,41 @@ static void sunxi_musb_dma_controller_destroy(struct dma_controller *c) >>>> { >>>> } >>>> >>>> +static int sunxi_musb_set_mode(struct musb *musb, u8 mode) >>>> +{ >>>> + struct sunxi_glue *glue = dev_get_drvdata(musb->controller->parent); >>>> + enum phy_mode new_mode; >>>> + >>>> + switch (mode) { >>>> + case MUSB_HOST: new_mode = PHY_MODE_USB_HOST; break; >>>> + case MUSB_PERIPHERAL: new_mode = PHY_MODE_USB_DEVICE; break; >>>> + case MUSB_OTG: new_mode = PHY_MODE_USB_OTG; break; >>> >>> Please fix the code style as commented in patch 4/7. >> >> Ok I will send a new version with this fixed. >> >>> >>>> + default: >>>> + dev_err(musb->controller->parent, >>>> + "Error requested mode not supported by this kernel\n"); >>>> + return -EINVAL; >>>> + } >>>> + >>>> + if (glue->phy_mode == new_mode) >>>> + return 0; >>>> + >>>> + if (musb->port_mode != MUSB_PORT_MODE_DUAL_ROLE) { >>>> + dev_err(musb->controller->parent, >>>> + "Error changing modes is only supported in dual role mode\n"); >>>> + return -EINVAL; >>>> + } >>>> + >>>> + /* >>>> + * phy_set_mode may sleep, and we're called with a spinlock held, >>>> + * so let sunxi_musb_work deal with it. >>>> + */ >>>> + glue->phy_mode = new_mode; >>>> + set_bit(SUNXI_MUSB_FL_PHY_MODE_PEND, &glue->flags); >>>> + schedule_work(&glue->work); >>> >>> When switching from host to peripheral mode, if an usb device is still >>> plugged and enumerated, how do you handle the device disconnect? >> >> The phy code will report vbus low for long enough for the musb to end >> the current session. It already does this for boards which do not >> have working vbus detection. > > But you didn't disconnect DP/DM, right? then musb detects vbus is gone > without receiving disconnect event, this is vbus error case, not a normal > teardown. Correct, there is no way to disconnect DP/DM and reporting Vbus low for a while does the trick. Regards, Hans
On Mon, Aug 22, 2016 at 05:08:56PM +0200, Hans de Goede wrote: > Hi, > > On 22-08-16 16:11, Bin Liu wrote: > >Hi, > > > >On Sun, Aug 21, 2016 at 12:10:26PM +0200, Hans de Goede wrote: > >>Hi, > >> > >>On 19-08-16 23:30, Bin Liu wrote: > >>>Hi, > >>> > >>>On Mon, Aug 15, 2016 at 09:21:32PM +0200, Hans de Goede wrote: > >>>>This allows run-time dr_mode switching support via the "mode" musb > >>>>sysfs attribute. > >>>> > >>>>Signed-off-by: Hans de Goede <hdegoede@redhat.com> > >>>>--- > >>>>drivers/usb/musb/sunxi.c | 52 ++++++++++++++++++++++++++++++++++++++++++++---- > >>>>1 file changed, 48 insertions(+), 4 deletions(-) > >>>> > >>>>diff --git a/drivers/usb/musb/sunxi.c b/drivers/usb/musb/sunxi.c > >>>>index c6ee166..1fe7451 100644 > >>>>--- a/drivers/usb/musb/sunxi.c > >>>>+++ b/drivers/usb/musb/sunxi.c > >>>>@@ -74,6 +74,7 @@ > >>>>#define SUNXI_MUSB_FL_HAS_SRAM 5 > >>>>#define SUNXI_MUSB_FL_HAS_RESET 6 > >>>>#define SUNXI_MUSB_FL_NO_CONFIGDATA 7 > >>>>+#define SUNXI_MUSB_FL_PHY_MODE_PEND 8 > >>>> > >>>>/* Our read/write methods need access and do not get passed in a musb ref :| */ > >>>>static struct musb *sunxi_musb; > >>>>@@ -87,6 +88,7 @@ struct sunxi_glue { > >>>> struct phy *phy; > >>>> struct platform_device *usb_phy; > >>>> struct usb_phy *xceiv; > >>>>+ enum phy_mode phy_mode; > >>>> unsigned long flags; > >>>> struct work_struct work; > >>>> struct extcon_dev *extcon; > >>>>@@ -140,6 +142,9 @@ static void sunxi_musb_work(struct work_struct *work) > >>>> clear_bit(SUNXI_MUSB_FL_PHY_ON, &glue->flags); > >>>> } > >>>> } > >>>>+ > >>>>+ if (test_and_clear_bit(SUNXI_MUSB_FL_PHY_MODE_PEND, &glue->flags)) > >>>>+ phy_set_mode(glue->phy, glue->phy_mode); > >>>>} > >>>> > >>>>static void sunxi_musb_set_vbus(struct musb *musb, int is_on) > >>>>@@ -341,6 +346,41 @@ static void sunxi_musb_dma_controller_destroy(struct dma_controller *c) > >>>>{ > >>>>} > >>>> > >>>>+static int sunxi_musb_set_mode(struct musb *musb, u8 mode) > >>>>+{ > >>>>+ struct sunxi_glue *glue = dev_get_drvdata(musb->controller->parent); > >>>>+ enum phy_mode new_mode; > >>>>+ > >>>>+ switch (mode) { > >>>>+ case MUSB_HOST: new_mode = PHY_MODE_USB_HOST; break; > >>>>+ case MUSB_PERIPHERAL: new_mode = PHY_MODE_USB_DEVICE; break; > >>>>+ case MUSB_OTG: new_mode = PHY_MODE_USB_OTG; break; > >>> > >>>Please fix the code style as commented in patch 4/7. > >> > >>Ok I will send a new version with this fixed. > >> > >>> > >>>>+ default: > >>>>+ dev_err(musb->controller->parent, > >>>>+ "Error requested mode not supported by this kernel\n"); > >>>>+ return -EINVAL; > >>>>+ } > >>>>+ > >>>>+ if (glue->phy_mode == new_mode) > >>>>+ return 0; > >>>>+ > >>>>+ if (musb->port_mode != MUSB_PORT_MODE_DUAL_ROLE) { > >>>>+ dev_err(musb->controller->parent, > >>>>+ "Error changing modes is only supported in dual role mode\n"); > >>>>+ return -EINVAL; > >>>>+ } > >>>>+ > >>>>+ /* > >>>>+ * phy_set_mode may sleep, and we're called with a spinlock held, > >>>>+ * so let sunxi_musb_work deal with it. > >>>>+ */ > >>>>+ glue->phy_mode = new_mode; > >>>>+ set_bit(SUNXI_MUSB_FL_PHY_MODE_PEND, &glue->flags); > >>>>+ schedule_work(&glue->work); > >>> > >>>When switching from host to peripheral mode, if an usb device is still > >>>plugged and enumerated, how do you handle the device disconnect? > >> > >>The phy code will report vbus low for long enough for the musb to end > >>the current session. It already does this for boards which do not > >>have working vbus detection. > > > >But you didn't disconnect DP/DM, right? then musb detects vbus is gone > >without receiving disconnect event, this is vbus error case, not a normal > >teardown. > > Correct, there is no way to disconnect DP/DM and reporting Vbus low for > a while does the trick. Without physically disconnecting DP/DM, we still have a way to properly teardown the enumerated devices. Please check musb_softconnect_write() in musb_debugfs.c. Regards, -Bin. > > Regards, > > Hans
HI, On 22-08-16 17:24, Bin Liu wrote: > On Mon, Aug 22, 2016 at 05:08:56PM +0200, Hans de Goede wrote: >> Hi, >> >> On 22-08-16 16:11, Bin Liu wrote: >>> Hi, >>> >>> On Sun, Aug 21, 2016 at 12:10:26PM +0200, Hans de Goede wrote: >>>> Hi, >>>> >>>> On 19-08-16 23:30, Bin Liu wrote: >>>>> Hi, >>>>> >>>>> On Mon, Aug 15, 2016 at 09:21:32PM +0200, Hans de Goede wrote: >>>>>> This allows run-time dr_mode switching support via the "mode" musb >>>>>> sysfs attribute. >>>>>> >>>>>> Signed-off-by: Hans de Goede <hdegoede@redhat.com> >>>>>> --- >>>>>> drivers/usb/musb/sunxi.c | 52 ++++++++++++++++++++++++++++++++++++++++++++---- >>>>>> 1 file changed, 48 insertions(+), 4 deletions(-) >>>>>> >>>>>> diff --git a/drivers/usb/musb/sunxi.c b/drivers/usb/musb/sunxi.c >>>>>> index c6ee166..1fe7451 100644 >>>>>> --- a/drivers/usb/musb/sunxi.c >>>>>> +++ b/drivers/usb/musb/sunxi.c >>>>>> @@ -74,6 +74,7 @@ >>>>>> #define SUNXI_MUSB_FL_HAS_SRAM 5 >>>>>> #define SUNXI_MUSB_FL_HAS_RESET 6 >>>>>> #define SUNXI_MUSB_FL_NO_CONFIGDATA 7 >>>>>> +#define SUNXI_MUSB_FL_PHY_MODE_PEND 8 >>>>>> >>>>>> /* Our read/write methods need access and do not get passed in a musb ref :| */ >>>>>> static struct musb *sunxi_musb; >>>>>> @@ -87,6 +88,7 @@ struct sunxi_glue { >>>>>> struct phy *phy; >>>>>> struct platform_device *usb_phy; >>>>>> struct usb_phy *xceiv; >>>>>> + enum phy_mode phy_mode; >>>>>> unsigned long flags; >>>>>> struct work_struct work; >>>>>> struct extcon_dev *extcon; >>>>>> @@ -140,6 +142,9 @@ static void sunxi_musb_work(struct work_struct *work) >>>>>> clear_bit(SUNXI_MUSB_FL_PHY_ON, &glue->flags); >>>>>> } >>>>>> } >>>>>> + >>>>>> + if (test_and_clear_bit(SUNXI_MUSB_FL_PHY_MODE_PEND, &glue->flags)) >>>>>> + phy_set_mode(glue->phy, glue->phy_mode); >>>>>> } >>>>>> >>>>>> static void sunxi_musb_set_vbus(struct musb *musb, int is_on) >>>>>> @@ -341,6 +346,41 @@ static void sunxi_musb_dma_controller_destroy(struct dma_controller *c) >>>>>> { >>>>>> } >>>>>> >>>>>> +static int sunxi_musb_set_mode(struct musb *musb, u8 mode) >>>>>> +{ >>>>>> + struct sunxi_glue *glue = dev_get_drvdata(musb->controller->parent); >>>>>> + enum phy_mode new_mode; >>>>>> + >>>>>> + switch (mode) { >>>>>> + case MUSB_HOST: new_mode = PHY_MODE_USB_HOST; break; >>>>>> + case MUSB_PERIPHERAL: new_mode = PHY_MODE_USB_DEVICE; break; >>>>>> + case MUSB_OTG: new_mode = PHY_MODE_USB_OTG; break; >>>>> >>>>> Please fix the code style as commented in patch 4/7. >>>> >>>> Ok I will send a new version with this fixed. >>>> >>>>> >>>>>> + default: >>>>>> + dev_err(musb->controller->parent, >>>>>> + "Error requested mode not supported by this kernel\n"); >>>>>> + return -EINVAL; >>>>>> + } >>>>>> + >>>>>> + if (glue->phy_mode == new_mode) >>>>>> + return 0; >>>>>> + >>>>>> + if (musb->port_mode != MUSB_PORT_MODE_DUAL_ROLE) { >>>>>> + dev_err(musb->controller->parent, >>>>>> + "Error changing modes is only supported in dual role mode\n"); >>>>>> + return -EINVAL; >>>>>> + } >>>>>> + >>>>>> + /* >>>>>> + * phy_set_mode may sleep, and we're called with a spinlock held, >>>>>> + * so let sunxi_musb_work deal with it. >>>>>> + */ >>>>>> + glue->phy_mode = new_mode; >>>>>> + set_bit(SUNXI_MUSB_FL_PHY_MODE_PEND, &glue->flags); >>>>>> + schedule_work(&glue->work); >>>>> >>>>> When switching from host to peripheral mode, if an usb device is still >>>>> plugged and enumerated, how do you handle the device disconnect? >>>> >>>> The phy code will report vbus low for long enough for the musb to end >>>> the current session. It already does this for boards which do not >>>> have working vbus detection. >>> >>> But you didn't disconnect DP/DM, right? then musb detects vbus is gone >>> without receiving disconnect event, this is vbus error case, not a normal >>> teardown. >> >> Correct, there is no way to disconnect DP/DM and reporting Vbus low for >> a while does the trick. > > Without physically disconnecting DP/DM, we still have a way to properly > teardown the enumerated devices. Please check musb_softconnect_write() > in musb_debugfs.c. That is manipulating the session bit in the devctl reg, that does not work to switch from device to host role or from host to device role, at least not on allwinner's musb implementation. I've already tried that before writing the code to report VBus low. Regards, Hans
On Mon, Aug 22, 2016 at 05:32:57PM +0200, Hans de Goede wrote: > HI, > > On 22-08-16 17:24, Bin Liu wrote: > >On Mon, Aug 22, 2016 at 05:08:56PM +0200, Hans de Goede wrote: > >>Hi, > >> > >>On 22-08-16 16:11, Bin Liu wrote: > >>>Hi, > >>> > >>>On Sun, Aug 21, 2016 at 12:10:26PM +0200, Hans de Goede wrote: > >>>>Hi, > >>>> > >>>>On 19-08-16 23:30, Bin Liu wrote: > >>>>>Hi, > >>>>> > >>>>>On Mon, Aug 15, 2016 at 09:21:32PM +0200, Hans de Goede wrote: > >>>>>>This allows run-time dr_mode switching support via the "mode" musb > >>>>>>sysfs attribute. > >>>>>> > >>>>>>Signed-off-by: Hans de Goede <hdegoede@redhat.com> > >>>>>>--- > >>>>>>drivers/usb/musb/sunxi.c | 52 ++++++++++++++++++++++++++++++++++++++++++++---- > >>>>>>1 file changed, 48 insertions(+), 4 deletions(-) > >>>>>> > >>>>>>diff --git a/drivers/usb/musb/sunxi.c b/drivers/usb/musb/sunxi.c > >>>>>>index c6ee166..1fe7451 100644 > >>>>>>--- a/drivers/usb/musb/sunxi.c > >>>>>>+++ b/drivers/usb/musb/sunxi.c > >>>>>>@@ -74,6 +74,7 @@ > >>>>>>#define SUNXI_MUSB_FL_HAS_SRAM 5 > >>>>>>#define SUNXI_MUSB_FL_HAS_RESET 6 > >>>>>>#define SUNXI_MUSB_FL_NO_CONFIGDATA 7 > >>>>>>+#define SUNXI_MUSB_FL_PHY_MODE_PEND 8 > >>>>>> > >>>>>>/* Our read/write methods need access and do not get passed in a musb ref :| */ > >>>>>>static struct musb *sunxi_musb; > >>>>>>@@ -87,6 +88,7 @@ struct sunxi_glue { > >>>>>> struct phy *phy; > >>>>>> struct platform_device *usb_phy; > >>>>>> struct usb_phy *xceiv; > >>>>>>+ enum phy_mode phy_mode; > >>>>>> unsigned long flags; > >>>>>> struct work_struct work; > >>>>>> struct extcon_dev *extcon; > >>>>>>@@ -140,6 +142,9 @@ static void sunxi_musb_work(struct work_struct *work) > >>>>>> clear_bit(SUNXI_MUSB_FL_PHY_ON, &glue->flags); > >>>>>> } > >>>>>> } > >>>>>>+ > >>>>>>+ if (test_and_clear_bit(SUNXI_MUSB_FL_PHY_MODE_PEND, &glue->flags)) > >>>>>>+ phy_set_mode(glue->phy, glue->phy_mode); > >>>>>>} > >>>>>> > >>>>>>static void sunxi_musb_set_vbus(struct musb *musb, int is_on) > >>>>>>@@ -341,6 +346,41 @@ static void sunxi_musb_dma_controller_destroy(struct dma_controller *c) > >>>>>>{ > >>>>>>} > >>>>>> > >>>>>>+static int sunxi_musb_set_mode(struct musb *musb, u8 mode) > >>>>>>+{ > >>>>>>+ struct sunxi_glue *glue = dev_get_drvdata(musb->controller->parent); > >>>>>>+ enum phy_mode new_mode; > >>>>>>+ > >>>>>>+ switch (mode) { > >>>>>>+ case MUSB_HOST: new_mode = PHY_MODE_USB_HOST; break; > >>>>>>+ case MUSB_PERIPHERAL: new_mode = PHY_MODE_USB_DEVICE; break; > >>>>>>+ case MUSB_OTG: new_mode = PHY_MODE_USB_OTG; break; > >>>>> > >>>>>Please fix the code style as commented in patch 4/7. > >>>> > >>>>Ok I will send a new version with this fixed. > >>>> > >>>>> > >>>>>>+ default: > >>>>>>+ dev_err(musb->controller->parent, > >>>>>>+ "Error requested mode not supported by this kernel\n"); > >>>>>>+ return -EINVAL; > >>>>>>+ } > >>>>>>+ > >>>>>>+ if (glue->phy_mode == new_mode) > >>>>>>+ return 0; > >>>>>>+ > >>>>>>+ if (musb->port_mode != MUSB_PORT_MODE_DUAL_ROLE) { > >>>>>>+ dev_err(musb->controller->parent, > >>>>>>+ "Error changing modes is only supported in dual role mode\n"); > >>>>>>+ return -EINVAL; > >>>>>>+ } > >>>>>>+ > >>>>>>+ /* > >>>>>>+ * phy_set_mode may sleep, and we're called with a spinlock held, > >>>>>>+ * so let sunxi_musb_work deal with it. > >>>>>>+ */ > >>>>>>+ glue->phy_mode = new_mode; > >>>>>>+ set_bit(SUNXI_MUSB_FL_PHY_MODE_PEND, &glue->flags); > >>>>>>+ schedule_work(&glue->work); > >>>>> > >>>>>When switching from host to peripheral mode, if an usb device is still > >>>>>plugged and enumerated, how do you handle the device disconnect? > >>>> > >>>>The phy code will report vbus low for long enough for the musb to end > >>>>the current session. It already does this for boards which do not > >>>>have working vbus detection. > >>> > >>>But you didn't disconnect DP/DM, right? then musb detects vbus is gone > >>>without receiving disconnect event, this is vbus error case, not a normal > >>>teardown. > >> > >>Correct, there is no way to disconnect DP/DM and reporting Vbus low for > >>a while does the trick. > > > >Without physically disconnecting DP/DM, we still have a way to properly > >teardown the enumerated devices. Please check musb_softconnect_write() > >in musb_debugfs.c. > > That is manipulating the session bit in the devctl reg, that does not > work to switch from device to host role or from host to device role, > at least not on allwinner's musb implementation. I've already tried that > before writing the code to report VBus low. I would think you have to call musb_root_disconnect() first to notify the core to teardown the enumerated devices. > > Regards, > > Hans
Hi, On 22-08-16 17:38, Bin Liu wrote: > On Mon, Aug 22, 2016 at 05:32:57PM +0200, Hans de Goede wrote: <snip> >>>>>>> When switching from host to peripheral mode, if an usb device is still >>>>>>> plugged and enumerated, how do you handle the device disconnect? >>>>>> >>>>>> The phy code will report vbus low for long enough for the musb to end >>>>>> the current session. It already does this for boards which do not >>>>>> have working vbus detection. >>>>> >>>>> But you didn't disconnect DP/DM, right? then musb detects vbus is gone >>>>> without receiving disconnect event, this is vbus error case, not a normal >>>>> teardown. >>>> >>>> Correct, there is no way to disconnect DP/DM and reporting Vbus low for >>>> a while does the trick. >>> >>> Without physically disconnecting DP/DM, we still have a way to properly >>> teardown the enumerated devices. Please check musb_softconnect_write() >>> in musb_debugfs.c. >> >> That is manipulating the session bit in the devctl reg, that does not >> work to switch from device to host role or from host to device role, >> at least not on allwinner's musb implementation. I've already tried that >> before writing the code to report VBus low. > > I would think you have to call musb_root_disconnect() first to notify > the core to teardown the enumerated devices. I tried that it does not help. It only affects the software state, the hardware will still stay in host-mode if it does not see vbus low for long enough). Note this is on devices which lack vbus detection (again this is simply physically not available on the PCB, just like some PCB's miss the id-pin). Normally this never is an issue, because when a host cable gets unplugged from a AB connector we see the id pin go high, disable the boards driving of Vbus and then the phy's Vbus detect will report low to the musb controller. Anyways this is a solved problem, the reporting of Vbus low has been working fine for both boards which lack vbus-detection as well as for role-changing from sysfs. Regards, Hans
On Mon, Aug 22, 2016 at 05:55:50PM +0200, Hans de Goede wrote: > Hi, > > On 22-08-16 17:38, Bin Liu wrote: > >On Mon, Aug 22, 2016 at 05:32:57PM +0200, Hans de Goede wrote: > > <snip> > > >>>>>>>When switching from host to peripheral mode, if an usb device is still > >>>>>>>plugged and enumerated, how do you handle the device disconnect? > >>>>>> > >>>>>>The phy code will report vbus low for long enough for the musb to end > >>>>>>the current session. It already does this for boards which do not > >>>>>>have working vbus detection. > >>>>> > >>>>>But you didn't disconnect DP/DM, right? then musb detects vbus is gone > >>>>>without receiving disconnect event, this is vbus error case, not a normal > >>>>>teardown. > >>>> > >>>>Correct, there is no way to disconnect DP/DM and reporting Vbus low for > >>>>a while does the trick. > >>> > >>>Without physically disconnecting DP/DM, we still have a way to properly > >>>teardown the enumerated devices. Please check musb_softconnect_write() > >>>in musb_debugfs.c. > >> > >>That is manipulating the session bit in the devctl reg, that does not > >>work to switch from device to host role or from host to device role, > >>at least not on allwinner's musb implementation. I've already tried that > >>before writing the code to report VBus low. > > > >I would think you have to call musb_root_disconnect() first to notify > >the core to teardown the enumerated devices. > > I tried that it does not help. It only affects the software state, the > hardware will still stay in host-mode if it does not see vbus low for > long enough). I didn't mean to use musb_root_disconnect() to replace your implementation. I suggest to add this call before lowing vbus to let the core tearing down the numerated devices. > > Note this is on devices which lack vbus detection (again this is simply > physically not available on the PCB, just like some PCB's miss the > id-pin). Normally this never is an issue, because when a host cable gets > unplugged from a AB connector we see the id pin go high, disable the > boards driving of Vbus and then the phy's Vbus detect will report low to > the musb controller. In this case, DP/DM get disconnected before vbus line, so musb_root_disconnect() gets called in handling the disconnect interrupt event. I think it is better simulate this in your usecase, which does not generate disconnect event. Regards, -Bin. > > Anyways this is a solved problem, the reporting of Vbus low has been > working fine for both boards which lack vbus-detection as well as > for role-changing from sysfs. > > Regards, > > Hans
Hi, On 22-08-16 18:10, Bin Liu wrote: > On Mon, Aug 22, 2016 at 05:55:50PM +0200, Hans de Goede wrote: >> Hi, >> >> On 22-08-16 17:38, Bin Liu wrote: >>> On Mon, Aug 22, 2016 at 05:32:57PM +0200, Hans de Goede wrote: >> >> <snip> >> >>>>>>>>> When switching from host to peripheral mode, if an usb device is still >>>>>>>>> plugged and enumerated, how do you handle the device disconnect? >>>>>>>> >>>>>>>> The phy code will report vbus low for long enough for the musb to end >>>>>>>> the current session. It already does this for boards which do not >>>>>>>> have working vbus detection. >>>>>>> >>>>>>> But you didn't disconnect DP/DM, right? then musb detects vbus is gone >>>>>>> without receiving disconnect event, this is vbus error case, not a normal >>>>>>> teardown. >>>>>> >>>>>> Correct, there is no way to disconnect DP/DM and reporting Vbus low for >>>>>> a while does the trick. >>>>> >>>>> Without physically disconnecting DP/DM, we still have a way to properly >>>>> teardown the enumerated devices. Please check musb_softconnect_write() >>>>> in musb_debugfs.c. >>>> >>>> That is manipulating the session bit in the devctl reg, that does not >>>> work to switch from device to host role or from host to device role, >>>> at least not on allwinner's musb implementation. I've already tried that >>>> before writing the code to report VBus low. >>> >>> I would think you have to call musb_root_disconnect() first to notify >>> the core to teardown the enumerated devices. >> >> I tried that it does not help. It only affects the software state, the >> hardware will still stay in host-mode if it does not see vbus low for >> long enough). > > I didn't mean to use musb_root_disconnect() to replace your > implementation. I suggest to add this call before lowing vbus to let the > core tearing down the numerated devices. > >> >> Note this is on devices which lack vbus detection (again this is simply >> physically not available on the PCB, just like some PCB's miss the >> id-pin). Normally this never is an issue, because when a host cable gets >> unplugged from a AB connector we see the id pin go high, disable the >> boards driving of Vbus and then the phy's Vbus detect will report low to >> the musb controller. > > In this case, DP/DM get disconnected before vbus line, so > musb_root_disconnect() gets called in handling the disconnect interrupt > event. I think it is better simulate this in your usecase, which does > not generate disconnect event. Ah right, I ran some tests and indeed the hcd does not see a disconnect on role-change unless musb_root_disconnect() gets called, I'll send a v4 fixing this. Thank you for catching this. Regards, Hans > > Regards, > -Bin. > >> >> Anyways this is a solved problem, the reporting of Vbus low has been >> working fine for both boards which lack vbus-detection as well as >> for role-changing from sysfs. >> >> Regards, >> >> Hans
diff --git a/drivers/usb/musb/sunxi.c b/drivers/usb/musb/sunxi.c index c6ee166..1fe7451 100644 --- a/drivers/usb/musb/sunxi.c +++ b/drivers/usb/musb/sunxi.c @@ -74,6 +74,7 @@ #define SUNXI_MUSB_FL_HAS_SRAM 5 #define SUNXI_MUSB_FL_HAS_RESET 6 #define SUNXI_MUSB_FL_NO_CONFIGDATA 7 +#define SUNXI_MUSB_FL_PHY_MODE_PEND 8 /* Our read/write methods need access and do not get passed in a musb ref :| */ static struct musb *sunxi_musb; @@ -87,6 +88,7 @@ struct sunxi_glue { struct phy *phy; struct platform_device *usb_phy; struct usb_phy *xceiv; + enum phy_mode phy_mode; unsigned long flags; struct work_struct work; struct extcon_dev *extcon; @@ -140,6 +142,9 @@ static void sunxi_musb_work(struct work_struct *work) clear_bit(SUNXI_MUSB_FL_PHY_ON, &glue->flags); } } + + if (test_and_clear_bit(SUNXI_MUSB_FL_PHY_MODE_PEND, &glue->flags)) + phy_set_mode(glue->phy, glue->phy_mode); } static void sunxi_musb_set_vbus(struct musb *musb, int is_on) @@ -341,6 +346,41 @@ static void sunxi_musb_dma_controller_destroy(struct dma_controller *c) { } +static int sunxi_musb_set_mode(struct musb *musb, u8 mode) +{ + struct sunxi_glue *glue = dev_get_drvdata(musb->controller->parent); + enum phy_mode new_mode; + + switch (mode) { + case MUSB_HOST: new_mode = PHY_MODE_USB_HOST; break; + case MUSB_PERIPHERAL: new_mode = PHY_MODE_USB_DEVICE; break; + case MUSB_OTG: new_mode = PHY_MODE_USB_OTG; break; + default: + dev_err(musb->controller->parent, + "Error requested mode not supported by this kernel\n"); + return -EINVAL; + } + + if (glue->phy_mode == new_mode) + return 0; + + if (musb->port_mode != MUSB_PORT_MODE_DUAL_ROLE) { + dev_err(musb->controller->parent, + "Error changing modes is only supported in dual role mode\n"); + return -EINVAL; + } + + /* + * phy_set_mode may sleep, and we're called with a spinlock held, + * so let sunxi_musb_work deal with it. + */ + glue->phy_mode = new_mode; + set_bit(SUNXI_MUSB_FL_PHY_MODE_PEND, &glue->flags); + schedule_work(&glue->work); + + return 0; +} + /* * sunxi musb register layout * 0x00 - 0x17 fifo regs, 1 long per fifo @@ -568,6 +608,7 @@ static const struct musb_platform_ops sunxi_musb_ops = { .writew = sunxi_musb_writew, .dma_init = sunxi_musb_dma_controller_create, .dma_exit = sunxi_musb_dma_controller_destroy, + .set_mode = sunxi_musb_set_mode, .set_vbus = sunxi_musb_set_vbus, .pre_root_reset_end = sunxi_musb_pre_root_reset_end, .post_root_reset_end = sunxi_musb_post_root_reset_end, @@ -614,21 +655,28 @@ static int sunxi_musb_probe(struct platform_device *pdev) return -EINVAL; } + glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL); + if (!glue) + return -ENOMEM; + memset(&pdata, 0, sizeof(pdata)); switch (usb_get_dr_mode(&pdev->dev)) { #if defined CONFIG_USB_MUSB_DUAL_ROLE || defined CONFIG_USB_MUSB_HOST case USB_DR_MODE_HOST: pdata.mode = MUSB_PORT_MODE_HOST; + glue->phy_mode = PHY_MODE_USB_HOST; break; #endif #if defined CONFIG_USB_MUSB_DUAL_ROLE || defined CONFIG_USB_MUSB_GADGET case USB_DR_MODE_PERIPHERAL: pdata.mode = MUSB_PORT_MODE_GADGET; + glue->phy_mode = PHY_MODE_USB_DEVICE; break; #endif #ifdef CONFIG_USB_MUSB_DUAL_ROLE case USB_DR_MODE_OTG: pdata.mode = MUSB_PORT_MODE_DUAL_ROLE; + glue->phy_mode = PHY_MODE_USB_OTG; break; #endif default: @@ -638,10 +686,6 @@ static int sunxi_musb_probe(struct platform_device *pdev) pdata.platform_ops = &sunxi_musb_ops; pdata.config = &sunxi_musb_hdrc_config; - glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL); - if (!glue) - return -ENOMEM; - glue->dev = &pdev->dev; INIT_WORK(&glue->work, sunxi_musb_work); glue->host_nb.notifier_call = sunxi_musb_host_notifier;
This allows run-time dr_mode switching support via the "mode" musb sysfs attribute. Signed-off-by: Hans de Goede <hdegoede@redhat.com> --- drivers/usb/musb/sunxi.c | 52 ++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 48 insertions(+), 4 deletions(-)