diff mbox

[v2,7/7] musb: sunxi: Add support for platform_set_mode

Message ID 1471288892-21702-8-git-send-email-hdegoede@redhat.com (mailing list archive)
State New, archived
Headers show

Commit Message

Hans de Goede Aug. 15, 2016, 7:21 p.m. UTC
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(-)

Comments

Bin Liu Aug. 19, 2016, 9:30 p.m. UTC | #1
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
>
Hans de Goede Aug. 21, 2016, 10:10 a.m. UTC | #2
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
>>
Bin Liu Aug. 22, 2016, 2:11 p.m. UTC | #3
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
Hans de Goede Aug. 22, 2016, 3:08 p.m. UTC | #4
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
Bin Liu Aug. 22, 2016, 3:24 p.m. UTC | #5
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
Hans de Goede Aug. 22, 2016, 3:32 p.m. UTC | #6
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
Bin Liu Aug. 22, 2016, 3:38 p.m. UTC | #7
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
Hans de Goede Aug. 22, 2016, 3:55 p.m. UTC | #8
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
Bin Liu Aug. 22, 2016, 4:10 p.m. UTC | #9
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
Hans de Goede Aug. 25, 2016, 5:59 p.m. UTC | #10
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 mbox

Patch

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;