diff mbox series

[v2] usb: cdns3: gadget: Fix g_audio use case when connected to Super-Speed host

Message ID 20191030121607.21739-1-rogerq@ti.com (mailing list archive)
State Mainlined
Commit 94e259f81a714c96f381d362228fc4743db49742
Headers show
Series [v2] usb: cdns3: gadget: Fix g_audio use case when connected to Super-Speed host | expand

Commit Message

Roger Quadros Oct. 30, 2019, 12:16 p.m. UTC
Take into account gadget driver's speed limit when programming
controller speed.

Fixes: commit 7733f6c32e36 ("usb: cdns3: Add Cadence USB3 DRD Driver")
Signed-off-by: Roger Quadros <rogerq@ti.com>
Acked-by: Peter Chen <peter.chen@nxp.com>
---

Changelog:
v2
- Add Fixes line

 drivers/usb/cdns3/gadget.c | 31 ++++++++++++++++++++++++++-----
 1 file changed, 26 insertions(+), 5 deletions(-)

Comments

Greg KH Oct. 30, 2019, 1:30 p.m. UTC | #1
On Wed, Oct 30, 2019 at 02:16:07PM +0200, Roger Quadros wrote:
> Take into account gadget driver's speed limit when programming
> controller speed.
> 
> Fixes: commit 7733f6c32e36 ("usb: cdns3: Add Cadence USB3 DRD Driver")

No need for "commit", doesn't the documentation say the correct format?
I haven't looked in a while...

I can edit it out this time...

greg k-h
Roger Quadros Oct. 30, 2019, 2:20 p.m. UTC | #2
On 30/10/2019 15:30, Greg KH wrote:
> On Wed, Oct 30, 2019 at 02:16:07PM +0200, Roger Quadros wrote:
>> Take into account gadget driver's speed limit when programming
>> controller speed.
>>
>> Fixes: commit 7733f6c32e36 ("usb: cdns3: Add Cadence USB3 DRD Driver")
> 
> No need for "commit", doesn't the documentation say the correct format?
> I haven't looked in a while...
> 

Sorry, my bad.

> I can edit it out this time...

Thanks!
> 
> greg k-h
>
Felipe Balbi Oct. 31, 2019, 8:55 a.m. UTC | #3
Hi,

Roger Quadros <rogerq@ti.com> writes:

> Take into account gadget driver's speed limit when programming
> controller speed.
>
> Fixes: commit 7733f6c32e36 ("usb: cdns3: Add Cadence USB3 DRD Driver")
> Signed-off-by: Roger Quadros <rogerq@ti.com>
> Acked-by: Peter Chen <peter.chen@nxp.com>
> ---
>
> Changelog:
> v2
> - Add Fixes line
>
>  drivers/usb/cdns3/gadget.c | 31 ++++++++++++++++++++++++++-----
>  1 file changed, 26 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/usb/cdns3/gadget.c b/drivers/usb/cdns3/gadget.c
> index 40dad4e8d0dc..1c724c20d468 100644
> --- a/drivers/usb/cdns3/gadget.c
> +++ b/drivers/usb/cdns3/gadget.c
> @@ -2338,9 +2338,35 @@ static int cdns3_gadget_udc_start(struct usb_gadget *gadget,
>  {
>  	struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget);
>  	unsigned long flags;
> +	enum usb_device_speed max_speed = driver->max_speed;
>  
>  	spin_lock_irqsave(&priv_dev->lock, flags);
>  	priv_dev->gadget_driver = driver;
> +
> +	/* limit speed if necessary */
> +	max_speed = min(driver->max_speed, gadget->max_speed);
> +
> +	switch (max_speed) {
> +	case USB_SPEED_FULL:
> +		writel(USB_CONF_SFORCE_FS, &priv_dev->regs->usb_conf);
> +		writel(USB_CONF_USB3DIS, &priv_dev->regs->usb_conf);
> +		break;
> +	case USB_SPEED_HIGH:
> +		writel(USB_CONF_USB3DIS, &priv_dev->regs->usb_conf);
> +		break;
> +	case USB_SPEED_SUPER:
> +		break;
> +	default:
> +		dev_err(priv_dev->dev,
> +			"invalid maximum_speed parameter %d\n",
> +			max_speed);
> +		/* fall through */
> +	case USB_SPEED_UNKNOWN:
> +		/* default to superspeed */
> +		max_speed = USB_SPEED_SUPER;
> +		break;
> +	}

I had suggested some simplification for this case statement.
Roger Quadros Oct. 31, 2019, 10:35 a.m. UTC | #4
Hi,

On 31/10/2019 10:55, Felipe Balbi wrote:
> 
> Hi,
> 
> Roger Quadros <rogerq@ti.com> writes:
> 
>> Take into account gadget driver's speed limit when programming
>> controller speed.
>>
>> Fixes: commit 7733f6c32e36 ("usb: cdns3: Add Cadence USB3 DRD Driver")
>> Signed-off-by: Roger Quadros <rogerq@ti.com>
>> Acked-by: Peter Chen <peter.chen@nxp.com>
>> ---
>>
>> Changelog:
>> v2
>> - Add Fixes line
>>
>>   drivers/usb/cdns3/gadget.c | 31 ++++++++++++++++++++++++++-----
>>   1 file changed, 26 insertions(+), 5 deletions(-)
>>
>> diff --git a/drivers/usb/cdns3/gadget.c b/drivers/usb/cdns3/gadget.c
>> index 40dad4e8d0dc..1c724c20d468 100644
>> --- a/drivers/usb/cdns3/gadget.c
>> +++ b/drivers/usb/cdns3/gadget.c
>> @@ -2338,9 +2338,35 @@ static int cdns3_gadget_udc_start(struct usb_gadget *gadget,
>>   {
>>   	struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget);
>>   	unsigned long flags;
>> +	enum usb_device_speed max_speed = driver->max_speed;
>>   
>>   	spin_lock_irqsave(&priv_dev->lock, flags);
>>   	priv_dev->gadget_driver = driver;
>> +
>> +	/* limit speed if necessary */
>> +	max_speed = min(driver->max_speed, gadget->max_speed);
>> +
>> +	switch (max_speed) {
>> +	case USB_SPEED_FULL:
>> +		writel(USB_CONF_SFORCE_FS, &priv_dev->regs->usb_conf);
>> +		writel(USB_CONF_USB3DIS, &priv_dev->regs->usb_conf);
>> +		break;
>> +	case USB_SPEED_HIGH:
>> +		writel(USB_CONF_USB3DIS, &priv_dev->regs->usb_conf);
>> +		break;
>> +	case USB_SPEED_SUPER:
>> +		break;
>> +	default:
>> +		dev_err(priv_dev->dev,
>> +			"invalid maximum_speed parameter %d\n",
>> +			max_speed);
>> +		/* fall through */
>> +	case USB_SPEED_UNKNOWN:
>> +		/* default to superspeed */
>> +		max_speed = USB_SPEED_SUPER;
>> +		break;
>> +	}
> 
> I had suggested some simplification for this case statement.
> 

oops, looks like Greg picked this already.

During more tests today I just observed that this patch causes
the following regression.

Connect EVM to Super-Speed host
Load g_audio. (this enumerates as HS which is fine)
unload g_audio
load g_zero (this enumerates at HS instead of SS).

This is because the speed limit that we set doesn't get cleared.

Now the bits are write only and there is a way to undo USB_CONF_SFORCE_FS
by writing USB_CONF_CFORCE_FS, however there is no corresponding bit
to clear USB_CONF_USB3DIS. Only way seems to be USB_CFG_SWRST which
is a bit harsh IMO.

Pawel,

could you please confirm what is the best way to undo the high-speed
limit?
Felipe Balbi Oct. 31, 2019, 10:55 a.m. UTC | #5
Hi,

Roger Quadros <rogerq@ti.com> writes:

> Hi,
>
> On 31/10/2019 10:55, Felipe Balbi wrote:
>> 
>> Hi,
>> 
>> Roger Quadros <rogerq@ti.com> writes:
>> 
>>> Take into account gadget driver's speed limit when programming
>>> controller speed.
>>>
>>> Fixes: commit 7733f6c32e36 ("usb: cdns3: Add Cadence USB3 DRD Driver")
>>> Signed-off-by: Roger Quadros <rogerq@ti.com>
>>> Acked-by: Peter Chen <peter.chen@nxp.com>
>>> ---
>>>
>>> Changelog:
>>> v2
>>> - Add Fixes line
>>>
>>>   drivers/usb/cdns3/gadget.c | 31 ++++++++++++++++++++++++++-----
>>>   1 file changed, 26 insertions(+), 5 deletions(-)
>>>
>>> diff --git a/drivers/usb/cdns3/gadget.c b/drivers/usb/cdns3/gadget.c
>>> index 40dad4e8d0dc..1c724c20d468 100644
>>> --- a/drivers/usb/cdns3/gadget.c
>>> +++ b/drivers/usb/cdns3/gadget.c
>>> @@ -2338,9 +2338,35 @@ static int cdns3_gadget_udc_start(struct usb_gadget *gadget,
>>>   {
>>>   	struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget);
>>>   	unsigned long flags;
>>> +	enum usb_device_speed max_speed = driver->max_speed;
>>>   
>>>   	spin_lock_irqsave(&priv_dev->lock, flags);
>>>   	priv_dev->gadget_driver = driver;
>>> +
>>> +	/* limit speed if necessary */
>>> +	max_speed = min(driver->max_speed, gadget->max_speed);
>>> +
>>> +	switch (max_speed) {
>>> +	case USB_SPEED_FULL:
>>> +		writel(USB_CONF_SFORCE_FS, &priv_dev->regs->usb_conf);

so this forces the controller to FS

>>> +		writel(USB_CONF_USB3DIS, &priv_dev->regs->usb_conf);

and this disconnects in superspeed? What is this supposed to do?

>>> +		break;
>>> +	case USB_SPEED_HIGH:
>>> +		writel(USB_CONF_USB3DIS, &priv_dev->regs->usb_conf);
>>> +		break;
>>> +	case USB_SPEED_SUPER:
>>> +		break;
>>> +	default:
>>> +		dev_err(priv_dev->dev,
>>> +			"invalid maximum_speed parameter %d\n",
>>> +			max_speed);
>>> +		/* fall through */
>>> +	case USB_SPEED_UNKNOWN:
>>> +		/* default to superspeed */
>>> +		max_speed = USB_SPEED_SUPER;
>>> +		break;
>>> +	}
>> 
>> I had suggested some simplification for this case statement.
>> 
>
> oops, looks like Greg picked this already.
>
> During more tests today I just observed that this patch causes
> the following regression.
>
> Connect EVM to Super-Speed host
> Load g_audio. (this enumerates as HS which is fine)
> unload g_audio
> load g_zero (this enumerates at HS instead of SS).
>
> This is because the speed limit that we set doesn't get cleared.
>
> Now the bits are write only and there is a way to undo USB_CONF_SFORCE_FS
> by writing USB_CONF_CFORCE_FS, however there is no corresponding bit
> to clear USB_CONF_USB3DIS. Only way seems to be USB_CFG_SWRST which
> is a bit harsh IMO.

Isn't bit 0 enough?

/* Reset USB device configuration. */
#define USB_CONF_CFGRST		BIT(0)

Also, now that I look at this more carefully, you should move that code
to udc_set_speed().
Roger Quadros Oct. 31, 2019, 11:02 a.m. UTC | #6
On 31/10/2019 12:55, Felipe Balbi wrote:
> 
> Hi,
> 
> Roger Quadros <rogerq@ti.com> writes:
> 
>> Hi,
>>
>> On 31/10/2019 10:55, Felipe Balbi wrote:
>>>
>>> Hi,
>>>
>>> Roger Quadros <rogerq@ti.com> writes:
>>>
>>>> Take into account gadget driver's speed limit when programming
>>>> controller speed.
>>>>
>>>> Fixes: commit 7733f6c32e36 ("usb: cdns3: Add Cadence USB3 DRD Driver")
>>>> Signed-off-by: Roger Quadros <rogerq@ti.com>
>>>> Acked-by: Peter Chen <peter.chen@nxp.com>
>>>> ---
>>>>
>>>> Changelog:
>>>> v2
>>>> - Add Fixes line
>>>>
>>>>    drivers/usb/cdns3/gadget.c | 31 ++++++++++++++++++++++++++-----
>>>>    1 file changed, 26 insertions(+), 5 deletions(-)
>>>>
>>>> diff --git a/drivers/usb/cdns3/gadget.c b/drivers/usb/cdns3/gadget.c
>>>> index 40dad4e8d0dc..1c724c20d468 100644
>>>> --- a/drivers/usb/cdns3/gadget.c
>>>> +++ b/drivers/usb/cdns3/gadget.c
>>>> @@ -2338,9 +2338,35 @@ static int cdns3_gadget_udc_start(struct usb_gadget *gadget,
>>>>    {
>>>>    	struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget);
>>>>    	unsigned long flags;
>>>> +	enum usb_device_speed max_speed = driver->max_speed;
>>>>    
>>>>    	spin_lock_irqsave(&priv_dev->lock, flags);
>>>>    	priv_dev->gadget_driver = driver;
>>>> +
>>>> +	/* limit speed if necessary */
>>>> +	max_speed = min(driver->max_speed, gadget->max_speed);
>>>> +
>>>> +	switch (max_speed) {
>>>> +	case USB_SPEED_FULL:
>>>> +		writel(USB_CONF_SFORCE_FS, &priv_dev->regs->usb_conf);
> 
> so this forces the controller to FS

Right.

> 
>>>> +		writel(USB_CONF_USB3DIS, &priv_dev->regs->usb_conf);
> 
> and this disconnects in superspeed? What is this supposed to do?
> 

It says "Disconnect USB device in SuperSpeed".

We were asked to set that bit to limit it to HS.

>>>> +		break;
>>>> +	case USB_SPEED_HIGH:
>>>> +		writel(USB_CONF_USB3DIS, &priv_dev->regs->usb_conf);
>>>> +		break;
>>>> +	case USB_SPEED_SUPER:
>>>> +		break;
>>>> +	default:
>>>> +		dev_err(priv_dev->dev,
>>>> +			"invalid maximum_speed parameter %d\n",
>>>> +			max_speed);
>>>> +		/* fall through */
>>>> +	case USB_SPEED_UNKNOWN:
>>>> +		/* default to superspeed */
>>>> +		max_speed = USB_SPEED_SUPER;
>>>> +		break;
>>>> +	}
>>>
>>> I had suggested some simplification for this case statement.
>>>
>>
>> oops, looks like Greg picked this already.
>>
>> During more tests today I just observed that this patch causes
>> the following regression.
>>
>> Connect EVM to Super-Speed host
>> Load g_audio. (this enumerates as HS which is fine)
>> unload g_audio
>> load g_zero (this enumerates at HS instead of SS).
>>
>> This is because the speed limit that we set doesn't get cleared.
>>
>> Now the bits are write only and there is a way to undo USB_CONF_SFORCE_FS
>> by writing USB_CONF_CFORCE_FS, however there is no corresponding bit
>> to clear USB_CONF_USB3DIS. Only way seems to be USB_CFG_SWRST which
>> is a bit harsh IMO.
> 
> Isn't bit 0 enough?
> 
> /* Reset USB device configuration. */
> #define USB_CONF_CFGRST		BIT(0)

Probably not, as explanation of USB3DIS bit says,
"To connect disconnected device, CPU performs
software reset (CFG.SWRST)." which is bit 7. "Device software reset.

But I'll let Pawel comment on this.

> 
> Also, now that I look at this more carefully, you should move that code
> to udc_set_speed().
> 

Agreed. I'll revise the implementation to move it to udc_set_speed()
once I know how to undo the USB3DIS.
Felipe Balbi Oct. 31, 2019, 11:08 a.m. UTC | #7
Hi,

Roger Quadros <rogerq@ti.com> writes:
>>>>> Take into account gadget driver's speed limit when programming
>>>>> controller speed.
>>>>>
>>>>> Fixes: commit 7733f6c32e36 ("usb: cdns3: Add Cadence USB3 DRD Driver")
>>>>> Signed-off-by: Roger Quadros <rogerq@ti.com>
>>>>> Acked-by: Peter Chen <peter.chen@nxp.com>
>>>>> ---
>>>>>
>>>>> Changelog:
>>>>> v2
>>>>> - Add Fixes line
>>>>>
>>>>>    drivers/usb/cdns3/gadget.c | 31 ++++++++++++++++++++++++++-----
>>>>>    1 file changed, 26 insertions(+), 5 deletions(-)
>>>>>
>>>>> diff --git a/drivers/usb/cdns3/gadget.c b/drivers/usb/cdns3/gadget.c
>>>>> index 40dad4e8d0dc..1c724c20d468 100644
>>>>> --- a/drivers/usb/cdns3/gadget.c
>>>>> +++ b/drivers/usb/cdns3/gadget.c
>>>>> @@ -2338,9 +2338,35 @@ static int cdns3_gadget_udc_start(struct usb_gadget *gadget,
>>>>>    {
>>>>>    	struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget);
>>>>>    	unsigned long flags;
>>>>> +	enum usb_device_speed max_speed = driver->max_speed;
>>>>>    
>>>>>    	spin_lock_irqsave(&priv_dev->lock, flags);
>>>>>    	priv_dev->gadget_driver = driver;
>>>>> +
>>>>> +	/* limit speed if necessary */
>>>>> +	max_speed = min(driver->max_speed, gadget->max_speed);
>>>>> +
>>>>> +	switch (max_speed) {
>>>>> +	case USB_SPEED_FULL:
>>>>> +		writel(USB_CONF_SFORCE_FS, &priv_dev->regs->usb_conf);
>> 
>> so this forces the controller to FS
>
> Right.
>
>> 
>>>>> +		writel(USB_CONF_USB3DIS, &priv_dev->regs->usb_conf);
>> 
>> and this disconnects in superspeed? What is this supposed to do?
>> 
>
> It says "Disconnect USB device in SuperSpeed".
>
> We were asked to set that bit to limit it to HS.

I wonder if by "Disconnect USB device in SuperSpeed" means tristating
the PIPE3 interface, therefore forcing RX.Detect to fail.

>>>>> +		break;
>>>>> +	case USB_SPEED_HIGH:
>>>>> +		writel(USB_CONF_USB3DIS, &priv_dev->regs->usb_conf);
>>>>> +		break;
>>>>> +	case USB_SPEED_SUPER:
>>>>> +		break;
>>>>> +	default:
>>>>> +		dev_err(priv_dev->dev,
>>>>> +			"invalid maximum_speed parameter %d\n",
>>>>> +			max_speed);
>>>>> +		/* fall through */
>>>>> +	case USB_SPEED_UNKNOWN:
>>>>> +		/* default to superspeed */
>>>>> +		max_speed = USB_SPEED_SUPER;
>>>>> +		break;
>>>>> +	}
>>>>
>>>> I had suggested some simplification for this case statement.
>>>>
>>>
>>> oops, looks like Greg picked this already.
>>>
>>> During more tests today I just observed that this patch causes
>>> the following regression.
>>>
>>> Connect EVM to Super-Speed host
>>> Load g_audio. (this enumerates as HS which is fine)
>>> unload g_audio
>>> load g_zero (this enumerates at HS instead of SS).
>>>
>>> This is because the speed limit that we set doesn't get cleared.
>>>
>>> Now the bits are write only and there is a way to undo USB_CONF_SFORCE_FS
>>> by writing USB_CONF_CFORCE_FS, however there is no corresponding bit
>>> to clear USB_CONF_USB3DIS. Only way seems to be USB_CFG_SWRST which
>>> is a bit harsh IMO.
>> 
>> Isn't bit 0 enough?
>> 
>> /* Reset USB device configuration. */
>> #define USB_CONF_CFGRST		BIT(0)
>
> Probably not, as explanation of USB3DIS bit says,
> "To connect disconnected device, CPU performs
> software reset (CFG.SWRST)." which is bit 7. "Device software reset.

ouch

> But I'll let Pawel comment on this.

ok

>> Also, now that I look at this more carefully, you should move that code
>> to udc_set_speed().
>> 
>
> Agreed. I'll revise the implementation to move it to udc_set_speed()
> once I know how to undo the USB3DIS.

Sure
Pawel Laszczak Nov. 3, 2019, 8:17 a.m. UTC | #8
Hi,

>>
>> Hi,
>>
>> Roger Quadros <rogerq@ti.com> writes:
>>
>>> Hi,
>>>
>>> On 31/10/2019 10:55, Felipe Balbi wrote:
>>>>
>>>> Hi,
>>>>
>>>> Roger Quadros <rogerq@ti.com> writes:
>>>>
>>>>> Take into account gadget driver's speed limit when programming
>>>>> controller speed.
>>>>>
>>>>> Fixes: commit 7733f6c32e36 ("usb: cdns3: Add Cadence USB3 DRD Driver")
>>>>> Signed-off-by: Roger Quadros <rogerq@ti.com>
>>>>> Acked-by: Peter Chen <peter.chen@nxp.com>
>>>>> ---
>>>>>
>>>>> Changelog:
>>>>> v2
>>>>> - Add Fixes line
>>>>>
>>>>>    drivers/usb/cdns3/gadget.c | 31 ++++++++++++++++++++++++++-----
>>>>>    1 file changed, 26 insertions(+), 5 deletions(-)
>>>>>
>>>>> diff --git a/drivers/usb/cdns3/gadget.c b/drivers/usb/cdns3/gadget.c
>>>>> index 40dad4e8d0dc..1c724c20d468 100644
>>>>> --- a/drivers/usb/cdns3/gadget.c
>>>>> +++ b/drivers/usb/cdns3/gadget.c
>>>>> @@ -2338,9 +2338,35 @@ static int cdns3_gadget_udc_start(struct usb_gadget *gadget,
>>>>>    {
>>>>>    	struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget);
>>>>>    	unsigned long flags;
>>>>> +	enum usb_device_speed max_speed = driver->max_speed;
>>>>>
>>>>>    	spin_lock_irqsave(&priv_dev->lock, flags);
>>>>>    	priv_dev->gadget_driver = driver;
>>>>> +
>>>>> +	/* limit speed if necessary */
>>>>> +	max_speed = min(driver->max_speed, gadget->max_speed);
>>>>> +
>>>>> +	switch (max_speed) {
>>>>> +	case USB_SPEED_FULL:
>>>>> +		writel(USB_CONF_SFORCE_FS, &priv_dev->regs->usb_conf);
>>
>> so this forces the controller to FS
>
>Right.
>
>>
>>>>> +		writel(USB_CONF_USB3DIS, &priv_dev->regs->usb_conf);
>>
>> and this disconnects in superspeed? What is this supposed to do?
>>
>
>It says "Disconnect USB device in SuperSpeed".
>
>We were asked to set that bit to limit it to HS.
>
>>>>> +		break;
>>>>> +	case USB_SPEED_HIGH:
>>>>> +		writel(USB_CONF_USB3DIS, &priv_dev->regs->usb_conf);
>>>>> +		break;
>>>>> +	case USB_SPEED_SUPER:
>>>>> +		break;
>>>>> +	default:
>>>>> +		dev_err(priv_dev->dev,
>>>>> +			"invalid maximum_speed parameter %d\n",
>>>>> +			max_speed);
>>>>> +		/* fall through */
>>>>> +	case USB_SPEED_UNKNOWN:
>>>>> +		/* default to superspeed */
>>>>> +		max_speed = USB_SPEED_SUPER;
>>>>> +		break;
>>>>> +	}
>>>>
>>>> I had suggested some simplification for this case statement.
>>>>
>>>
>>> oops, looks like Greg picked this already.
>>>
>>> During more tests today I just observed that this patch causes
>>> the following regression.
>>>
>>> Connect EVM to Super-Speed host
>>> Load g_audio. (this enumerates as HS which is fine)
>>> unload g_audio
>>> load g_zero (this enumerates at HS instead of SS).
>>>
>>> This is because the speed limit that we set doesn't get cleared.
>>>
>>> Now the bits are write only and there is a way to undo USB_CONF_SFORCE_FS
>>> by writing USB_CONF_CFORCE_FS, however there is no corresponding bit
>>> to clear USB_CONF_USB3DIS. Only way seems to be USB_CFG_SWRST which
>>> is a bit harsh IMO.
>>
>> Isn't bit 0 enough?
>>
>> /* Reset USB device configuration. */
>> #define USB_CONF_CFGRST		BIT(0)
>
>Probably not, as explanation of USB3DIS bit says,
>"To connect disconnected device, CPU performs
>software reset (CFG.SWRST)." which is bit 7. "Device software reset.
>
>But I'll let Pawel comment on this.

Yes it's the only way to return back to SS mode. 
The USB speed is detecting during connecting. 
CFG.SWRST make reconnect. 

Maybe it would be better to add SS support in g_audio function. 

>>
>> Also, now that I look at this more carefully, you should move that code
>> to udc_set_speed().
>>
>
>Agreed. I'll revise the implementation to move it to udc_set_speed()
>once I know how to undo the USB3DIS.
>
>--
>cheers,
>-roger
>Texas Instruments Finland Oy, Porkkalankatu 22, 00180 Helsinki.
>Y-tunnus/Business ID: 0615521-4. Kotipaikka/Domicile: Helsinki
Roger Quadros Nov. 4, 2019, 9:18 a.m. UTC | #9
On 03/11/2019 10:17, Pawel Laszczak wrote:
> Hi,
> 
>>>
>>> Hi,
>>>
>>> Roger Quadros <rogerq@ti.com> writes:
>>>
>>>> Hi,
>>>>
>>>> On 31/10/2019 10:55, Felipe Balbi wrote:
>>>>>
>>>>> Hi,
>>>>>
>>>>> Roger Quadros <rogerq@ti.com> writes:
>>>>>
>>>>>> Take into account gadget driver's speed limit when programming
>>>>>> controller speed.
>>>>>>
>>>>>> Fixes: commit 7733f6c32e36 ("usb: cdns3: Add Cadence USB3 DRD Driver")
>>>>>> Signed-off-by: Roger Quadros <rogerq@ti.com>
>>>>>> Acked-by: Peter Chen <peter.chen@nxp.com>
>>>>>> ---
>>>>>>
>>>>>> Changelog:
>>>>>> v2
>>>>>> - Add Fixes line
>>>>>>
>>>>>>     drivers/usb/cdns3/gadget.c | 31 ++++++++++++++++++++++++++-----
>>>>>>     1 file changed, 26 insertions(+), 5 deletions(-)
>>>>>>
>>>>>> diff --git a/drivers/usb/cdns3/gadget.c b/drivers/usb/cdns3/gadget.c
>>>>>> index 40dad4e8d0dc..1c724c20d468 100644
>>>>>> --- a/drivers/usb/cdns3/gadget.c
>>>>>> +++ b/drivers/usb/cdns3/gadget.c
>>>>>> @@ -2338,9 +2338,35 @@ static int cdns3_gadget_udc_start(struct usb_gadget *gadget,
>>>>>>     {
>>>>>>     	struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget);
>>>>>>     	unsigned long flags;
>>>>>> +	enum usb_device_speed max_speed = driver->max_speed;
>>>>>>
>>>>>>     	spin_lock_irqsave(&priv_dev->lock, flags);
>>>>>>     	priv_dev->gadget_driver = driver;
>>>>>> +
>>>>>> +	/* limit speed if necessary */
>>>>>> +	max_speed = min(driver->max_speed, gadget->max_speed);
>>>>>> +
>>>>>> +	switch (max_speed) {
>>>>>> +	case USB_SPEED_FULL:
>>>>>> +		writel(USB_CONF_SFORCE_FS, &priv_dev->regs->usb_conf);
>>>
>>> so this forces the controller to FS
>>
>> Right.
>>
>>>
>>>>>> +		writel(USB_CONF_USB3DIS, &priv_dev->regs->usb_conf);
>>>
>>> and this disconnects in superspeed? What is this supposed to do?
>>>
>>
>> It says "Disconnect USB device in SuperSpeed".
>>
>> We were asked to set that bit to limit it to HS.
>>
>>>>>> +		break;
>>>>>> +	case USB_SPEED_HIGH:
>>>>>> +		writel(USB_CONF_USB3DIS, &priv_dev->regs->usb_conf);
>>>>>> +		break;
>>>>>> +	case USB_SPEED_SUPER:
>>>>>> +		break;
>>>>>> +	default:
>>>>>> +		dev_err(priv_dev->dev,
>>>>>> +			"invalid maximum_speed parameter %d\n",
>>>>>> +			max_speed);
>>>>>> +		/* fall through */
>>>>>> +	case USB_SPEED_UNKNOWN:
>>>>>> +		/* default to superspeed */
>>>>>> +		max_speed = USB_SPEED_SUPER;
>>>>>> +		break;
>>>>>> +	}
>>>>>
>>>>> I had suggested some simplification for this case statement.
>>>>>
>>>>
>>>> oops, looks like Greg picked this already.
>>>>
>>>> During more tests today I just observed that this patch causes
>>>> the following regression.
>>>>
>>>> Connect EVM to Super-Speed host
>>>> Load g_audio. (this enumerates as HS which is fine)
>>>> unload g_audio
>>>> load g_zero (this enumerates at HS instead of SS).
>>>>
>>>> This is because the speed limit that we set doesn't get cleared.
>>>>
>>>> Now the bits are write only and there is a way to undo USB_CONF_SFORCE_FS
>>>> by writing USB_CONF_CFORCE_FS, however there is no corresponding bit
>>>> to clear USB_CONF_USB3DIS. Only way seems to be USB_CFG_SWRST which
>>>> is a bit harsh IMO.
>>>
>>> Isn't bit 0 enough?
>>>
>>> /* Reset USB device configuration. */
>>> #define USB_CONF_CFGRST		BIT(0)
>>
>> Probably not, as explanation of USB3DIS bit says,
>> "To connect disconnected device, CPU performs
>> software reset (CFG.SWRST)." which is bit 7. "Device software reset.
>>
>> But I'll let Pawel comment on this.
> 
> Yes it's the only way to return back to SS mode.
> The USB speed is detecting during connecting.
> CFG.SWRST make reconnect.

Can this be done as part of udc_start or udc_stop?

> 
> Maybe it would be better to add SS support in g_audio function.

Not every gadget needs SS, so this is not the right way IMO.

> 
>>>
>>> Also, now that I look at this more carefully, you should move that code
>>> to udc_set_speed().
>>>
>>
>> Agreed. I'll revise the implementation to move it to udc_set_speed()
>> once I know how to undo the USB3DIS.
>>
Pawel Laszczak Nov. 4, 2019, 3:16 p.m. UTC | #10
Hi,

>
>On 03/11/2019 10:17, Pawel Laszczak wrote:
>> Hi,
>>
>>>>
>>>> Hi,
>>>>
>>>> Roger Quadros <rogerq@ti.com> writes:
>>>>
>>>>> Hi,
>>>>>
>>>>> On 31/10/2019 10:55, Felipe Balbi wrote:
>>>>>>
>>>>>> Hi,
>>>>>>
>>>>>> Roger Quadros <rogerq@ti.com> writes:
>>>>>>
>>>>>>> Take into account gadget driver's speed limit when programming
>>>>>>> controller speed.
>>>>>>>
>>>>>>> Fixes: commit 7733f6c32e36 ("usb: cdns3: Add Cadence USB3 DRD Driver")
>>>>>>> Signed-off-by: Roger Quadros <rogerq@ti.com>
>>>>>>> Acked-by: Peter Chen <peter.chen@nxp.com>
>>>>>>> ---
>>>>>>>
>>>>>>> Changelog:
>>>>>>> v2
>>>>>>> - Add Fixes line
>>>>>>>
>>>>>>>     drivers/usb/cdns3/gadget.c | 31 ++++++++++++++++++++++++++-----
>>>>>>>     1 file changed, 26 insertions(+), 5 deletions(-)
>>>>>>>
>>>>>>> diff --git a/drivers/usb/cdns3/gadget.c b/drivers/usb/cdns3/gadget.c
>>>>>>> index 40dad4e8d0dc..1c724c20d468 100644
>>>>>>> --- a/drivers/usb/cdns3/gadget.c
>>>>>>> +++ b/drivers/usb/cdns3/gadget.c
>>>>>>> @@ -2338,9 +2338,35 @@ static int cdns3_gadget_udc_start(struct usb_gadget *gadget,
>>>>>>>     {
>>>>>>>     	struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget);
>>>>>>>     	unsigned long flags;
>>>>>>> +	enum usb_device_speed max_speed = driver->max_speed;
>>>>>>>
>>>>>>>     	spin_lock_irqsave(&priv_dev->lock, flags);
>>>>>>>     	priv_dev->gadget_driver = driver;
>>>>>>> +
>>>>>>> +	/* limit speed if necessary */
>>>>>>> +	max_speed = min(driver->max_speed, gadget->max_speed);
>>>>>>> +
>>>>>>> +	switch (max_speed) {
>>>>>>> +	case USB_SPEED_FULL:
>>>>>>> +		writel(USB_CONF_SFORCE_FS, &priv_dev->regs->usb_conf);
>>>>
>>>> so this forces the controller to FS
>>>
>>> Right.
>>>
>>>>
>>>>>>> +		writel(USB_CONF_USB3DIS, &priv_dev->regs->usb_conf);
>>>>
>>>> and this disconnects in superspeed? What is this supposed to do?
>>>>
>>>
>>> It says "Disconnect USB device in SuperSpeed".
>>>
>>> We were asked to set that bit to limit it to HS.
>>>
>>>>>>> +		break;
>>>>>>> +	case USB_SPEED_HIGH:
>>>>>>> +		writel(USB_CONF_USB3DIS, &priv_dev->regs->usb_conf);
>>>>>>> +		break;
>>>>>>> +	case USB_SPEED_SUPER:
>>>>>>> +		break;
>>>>>>> +	default:
>>>>>>> +		dev_err(priv_dev->dev,
>>>>>>> +			"invalid maximum_speed parameter %d\n",
>>>>>>> +			max_speed);
>>>>>>> +		/* fall through */
>>>>>>> +	case USB_SPEED_UNKNOWN:
>>>>>>> +		/* default to superspeed */
>>>>>>> +		max_speed = USB_SPEED_SUPER;
>>>>>>> +		break;
>>>>>>> +	}
>>>>>>
>>>>>> I had suggested some simplification for this case statement.
>>>>>>
>>>>>
>>>>> oops, looks like Greg picked this already.
>>>>>
>>>>> During more tests today I just observed that this patch causes
>>>>> the following regression.
>>>>>
>>>>> Connect EVM to Super-Speed host
>>>>> Load g_audio. (this enumerates as HS which is fine)
>>>>> unload g_audio
>>>>> load g_zero (this enumerates at HS instead of SS).
>>>>>
>>>>> This is because the speed limit that we set doesn't get cleared.
>>>>>
>>>>> Now the bits are write only and there is a way to undo USB_CONF_SFORCE_FS
>>>>> by writing USB_CONF_CFORCE_FS, however there is no corresponding bit
>>>>> to clear USB_CONF_USB3DIS. Only way seems to be USB_CFG_SWRST which
>>>>> is a bit harsh IMO.
>>>>
>>>> Isn't bit 0 enough?
>>>>
>>>> /* Reset USB device configuration. */
>>>> #define USB_CONF_CFGRST		BIT(0)
>>>
>>> Probably not, as explanation of USB3DIS bit says,
>>> "To connect disconnected device, CPU performs
>>> software reset (CFG.SWRST)." which is bit 7. "Device software reset.
>>>
>>> But I'll let Pawel comment on this.
>>
>> Yes it's the only way to return back to SS mode.
>> The USB speed is detecting during connecting.
>> CFG.SWRST make reconnect.
>
>Can this be done as part of udc_start or udc_stop?

I think that it should work in both cases, however because it finish with connection 
so probably the better will be udc_start function. 

>
>>
>> Maybe it would be better to add SS support in g_audio function.
>
>Not every gadget needs SS, so this is not the right way IMO.
>
>>
>>>>
>>>> Also, now that I look at this more carefully, you should move that code
>>>> to udc_set_speed().
>>>>
>>>
>>> Agreed. I'll revise the implementation to move it to udc_set_speed()
>>> once I know how to undo the USB3DIS.
>>>
>
>
>--
>cheers,
>-roger
>Texas Instruments Finland Oy, Porkkalankatu 22, 00180 Helsinki.
>Y-tunnus/Business ID: 0615521-4. Kotipaikka/Domicile: Helsinki
diff mbox series

Patch

diff --git a/drivers/usb/cdns3/gadget.c b/drivers/usb/cdns3/gadget.c
index 40dad4e8d0dc..1c724c20d468 100644
--- a/drivers/usb/cdns3/gadget.c
+++ b/drivers/usb/cdns3/gadget.c
@@ -2338,9 +2338,35 @@  static int cdns3_gadget_udc_start(struct usb_gadget *gadget,
 {
 	struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget);
 	unsigned long flags;
+	enum usb_device_speed max_speed = driver->max_speed;
 
 	spin_lock_irqsave(&priv_dev->lock, flags);
 	priv_dev->gadget_driver = driver;
+
+	/* limit speed if necessary */
+	max_speed = min(driver->max_speed, gadget->max_speed);
+
+	switch (max_speed) {
+	case USB_SPEED_FULL:
+		writel(USB_CONF_SFORCE_FS, &priv_dev->regs->usb_conf);
+		writel(USB_CONF_USB3DIS, &priv_dev->regs->usb_conf);
+		break;
+	case USB_SPEED_HIGH:
+		writel(USB_CONF_USB3DIS, &priv_dev->regs->usb_conf);
+		break;
+	case USB_SPEED_SUPER:
+		break;
+	default:
+		dev_err(priv_dev->dev,
+			"invalid maximum_speed parameter %d\n",
+			max_speed);
+		/* fall through */
+	case USB_SPEED_UNKNOWN:
+		/* default to superspeed */
+		max_speed = USB_SPEED_SUPER;
+		break;
+	}
+
 	cdns3_gadget_config(priv_dev);
 	spin_unlock_irqrestore(&priv_dev->lock, flags);
 	return 0;
@@ -2570,12 +2596,7 @@  static int cdns3_gadget_start(struct cdns3 *cdns)
 	/* Check the maximum_speed parameter */
 	switch (max_speed) {
 	case USB_SPEED_FULL:
-		writel(USB_CONF_SFORCE_FS, &priv_dev->regs->usb_conf);
-		writel(USB_CONF_USB3DIS, &priv_dev->regs->usb_conf);
-		break;
 	case USB_SPEED_HIGH:
-		writel(USB_CONF_USB3DIS, &priv_dev->regs->usb_conf);
-		break;
 	case USB_SPEED_SUPER:
 		break;
 	default: