USB: gadget: atmel_usba_udc: Enable/disable USB PLL on Vbus change
Subsystems (3)
PATCH 1/1 USB: gadget: atmel_usba_udc: Enable/disable USB PLL on Vbus change
Prepare_enable on rising edge, disable_unprepare on falling edge. Reduce power consumption if USB PLL is not already necessary for OHCI or EHCI. If USB host is not connected we can sleep with USB PLL stopped.
This driver does not support suspend/resume yet, not suspending if we are still attached to an USB host is fine for what I need, this patch allow suspending with USB PLL stopped when USB device is not currently used.
--- drivers/usb/gadget/udc/atmel_usba_udc.c | 37 +++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 9 deletions(-)
drivers/usb/gadget/udc/atmel_usba_udc.c +28 -9
@@ -1723,6 +1723,7 @@ static irqreturn_t usba_vbus_irq(int irq, void *devid) {struct usba_udc *udc = devid;int vbus; +int ret;/* debounce */ udelay (10 );
@@ -1736,6 +1737,15 @@ static irqreturn_t usba_vbus_irq(int irq, void *devid) vbus =vbus_is_present (udc);if (vbus != udc->vbus_prev) {if (vbus) { + ret =clk_prepare_enable (udc->pclk); +if (ret) + goto out; + ret =clk_prepare_enable (udc->hclk); +if (ret) { +clk_disable_unprepare (udc->pclk); + goto out; + } +toggle_bias (1 );usba_writel (udc,CTRL ,USBA_ENABLE_MASK );usba_writel (udc,INT_ENB ,USBA_END_OF_RESET );
@@ -1744,6 +1754,10 @@ static irqreturn_t usba_vbus_irq(int irq, void *devid)reset_all_endpoints (udc);toggle_bias (0 );usba_writel (udc,CTRL ,USBA_DISABLE_MASK ); + +clk_disable_unprepare (udc->hclk); +clk_disable_unprepare (udc->pclk); +if (udc->driver->disconnect) {spin_unlock (&udc->lock); udc->driver->disconnect (&udc->gadget);
@@ -1772,15 +1786,6 @@ static int atmel_usba_start(struct usb_gadget *gadget, udc->driver = driver;spin_unlock_irqrestore (&udc->lock, flags); - ret =clk_prepare_enable (udc->pclk); -if (ret) -return ret; - ret =clk_prepare_enable (udc->hclk); -if (ret) { -clk_disable_unprepare (udc->pclk); -return ret; - } - udc->vbus_prev =0 ;if (gpio_is_valid (udc->vbus_pin))enable_irq (gpio_to_irq (udc->vbus_pin));
@@ -1788,13 +1793,27 @@ static int atmel_usba_start(struct usb_gadget *gadget,/* If Vbus is present, enable the controller and wait for reset */ spin_lock_irqsave (&udc->lock, flags);if (vbus_is_present (udc) && udc->vbus_prev ==0 ) { + ret =clk_prepare_enable (udc->pclk); +if (ret) + goto out; + ret =clk_prepare_enable (udc->hclk); +if (ret) { +clk_disable_unprepare (udc->pclk); + goto out; + } +toggle_bias (1 );usba_writel (udc,CTRL ,USBA_ENABLE_MASK );usba_writel (udc,INT_ENB ,USBA_END_OF_RESET ); + + udc->vbus_prev =1 ; }spin_unlock_irqrestore (&udc->lock, flags);return 0 ; +out: +spin_unlock_irqrestore (&udc->lock, flags); +return ret; }static int atmel_usba_stop (struct usb_gadget *gadget)
Signature · 1 line
2.1.4
PATCH 1/1 USB: gadget: atmel_usba_udc: Enable/disable USB PLL on Vbus change
Hi,
Quoted 9 lines · Sylvain Rochet
Prepare_enableon rising edge,disable_unprepareon falling edge. Reduce power consumption if USB PLL is not already necessary for OHCI or EHCI. If USB host is not connected we can sleep with USB PLL stopped.This driver does not support suspend/resume yet, not suspending if we are still attached to an USB host is fine for what I need, this patch allow suspending with USB PLL stopped when USB device is not currently used.
Same as your previous patch, the maintainers are:
Felipe Balbi <[email protected]> (maintainer:USB GADGET/PERIPH...) Greg Kroah-Hartman <[email protected]> (supporter:USB SUBSYSTEM)
drivers/usb/gadget/udc/atmel_usba_udc.c static irqreturn_t usba_vbus_irq(int irq, void *devid) · line 1746
Quoted 11 lines
drivers/usb/gadget/udc/atmel_usba_udc.c +7
-0+ ret =clk_prepare_enable (udc->pclk); +if (ret) + goto out; + ret =clk_prepare_enable (udc->hclk); +if (ret) { +clk_disable_unprepare (udc->pclk); + goto out;
You probably got a warning at some point in time, you can't use clk_prepare or clk_unprepare in an irq handler as they may sleep (that is exactly the point of having clk_prepare ans clk_enable)
So, use clk_enable and clk_disable here.
drivers/usb/gadget/udc/atmel_usba_udc.c static irqreturn_t usba_vbus_irq(int irq, void *devid) · line 1760
Quoted 7 lines
drivers/usb/gadget/udc/atmel_usba_udc.c +4
-0reset_all_endpoints (udc);toggle_bias (0 );usba_writel (udc,CTRL ,USBA_DISABLE_MASK ); + +clk_disable_unprepare (udc->hclk); +clk_disable_unprepare (udc->pclk); +
Ditto
Quoted 6 lines
drivers/usb/gadget/udc/atmel_usba_udc.c +0
-0if (udc->driver->disconnect) {spin_unlock (&udc->lock); udc->driver->disconnect (&udc->gadget);@@ -1772,15 +1786,6 @@ static int atmel_usba_start(struct usb_gadget *gadget, udc->driver = driver;spin_unlock_irqrestore (&udc->lock, flags);
drivers/usb/gadget/udc/atmel_usba_udc.c static int atmel_usba_start(struct usb_gadget *gadget, · line 1783
Quoted 10 lines
drivers/usb/gadget/udc/atmel_usba_udc.c +0
-9- ret =clk_prepare_enable (udc->pclk); -if (ret) -return ret; - ret =clk_prepare_enable (udc->hclk); -if (ret) { -clk_disable_unprepare (udc->pclk); -return ret; - } -
Keep clk_prepare and clk_unprepare here.
Quoted 6 lines
drivers/usb/gadget/udc/atmel_usba_udc.c +0
-0udc->vbus_prev =0 ;if (gpio_is_valid (udc->vbus_pin))enable_irq (gpio_to_irq (udc->vbus_pin));@@ -1788,13 +1793,27 @@ static int atmel_usba_start(struct usb_gadget *gadget,/* If Vbus is present, enable the controller and wait for reset */ spin_lock_irqsave (&udc->lock, flags);
drivers/usb/gadget/udc/atmel_usba_udc.c static int atmel_usba_start(struct usb_gadget *gadget, · line 1804
Quoted 10 lines
drivers/usb/gadget/udc/atmel_usba_udc.c +9
-0if (vbus_is_present (udc) && udc->vbus_prev ==0 ) { + ret =clk_prepare_enable (udc->pclk); +if (ret) + goto out; + ret =clk_prepare_enable (udc->hclk); +if (ret) { +clk_disable_unprepare (udc->pclk); + goto out; + } +
clk_enable/clk_disable here.
Quoted 14 lines
drivers/usb/gadget/udc/atmel_usba_udc.c +5
-0toggle_bias (1 );usba_writel (udc,CTRL ,USBA_ENABLE_MASK );usba_writel (udc,INT_ENB ,USBA_END_OF_RESET ); + + udc->vbus_prev =1 ; }spin_unlock_irqrestore (&udc->lock, flags);return 0 ; +out: +spin_unlock_irqrestore (&udc->lock, flags); +return ret; }
Signature · 3 lines
Alexandre Belloni, Free Electrons Embedded Linux, Kernel and Android engineering http://free-electrons.com
PATCH 1/1 USB: gadget: atmel_usba_udc: Enable/disable USB PLL on Vbus change
Hi,
Quoted 35 lines · On Sat, 17 Jan 2015 02:42:31 +0100 Alexandre Belloni…
Hi,
Quoted 9 lines · Sylvain Rochet
Prepare_enableon rising edge,disable_unprepareon falling edge. Reduce power consumption if USB PLL is not already necessary for OHCI or EHCI. If USB host is not connected we can sleep with USB PLL stopped.This driver does not support suspend/resume yet, not suspending if we are still attached to an USB host is fine for what I need, this patch allow suspending with USB PLL stopped when USB device is not currently used.
Same as your previous patch, the maintainers are:
Felipe Balbi <[email protected]> (maintainer:USB GADGET/PERIPH...) Greg Kroah-Hartman <[email protected]> (supporter:USB SUBSYSTEM)drivers/usb/gadget/udc/atmel_usba_udc.c static irqreturn_t usba_vbus_irq(int irq, void *devid) · line 1746
Quoted 11 lines
drivers/usb/gadget/udc/atmel_usba_udc.c +7
-0+ ret =clk_prepare_enable (udc->pclk); +if (ret) + goto out; + ret =clk_prepare_enable (udc->hclk); +if (ret) { +clk_disable_unprepare (udc->pclk); + goto out;You probably got a warning at some point in time, you can't use
clk_prepareorclk_unpreparein an irq handler as they may sleep (that is exactly the point of havingclk_prepareansclk_enable)
Quoted 1 line
So, use
clk_enableandclk_disablehere.
You're right, except that calling enable/disable on the PLL clk in irq context is pretty much useless since the activation/deactivation code of the PLL is in prepare/unprepare, so you won't save much power by doing that (gating the peripheral clk should save a bit though).
To solve that issue I thought we could move to a threaded_irq (where we can safely sleep), but you'll also have to take of not calling prepare/unprepare in sections where at least one spinlock is taken (for the same reason => you cannot sleep with while you hold spinlocks).
Best Regards,
Boris
Signature · 3 lines
Boris Brezillon, Free Electrons Embedded Linux and Kernel engineering http://free-electrons.com
PATCH 1/1 USB: gadget: atmel_usba_udc: Enable/disable USB PLL on Vbus change
Hello Boris,
Quoted 10 lines · Boris Brezillon
You're right, except that calling enable/disable on the PLL clk in irq context is pretty much useless since the activation/deactivation code of the PLL is in prepare/unprepare, so you won't save much power by doing that (gating the peripheral clk should save a bit though).
To solve that issue I thought we could move to a
threaded_irq(where we can safely sleep), but you'll also have to take of not calling prepare/unprepare in sections where at least one spinlock is taken (for the same reason => you cannot sleep with while you hold spinlocks).
Hummm, I must admit I waited for such a useful tip, adding the prepare/unprepare in interrupt context looked dirty at first sight, thank you very much :)
I will try with a threaded_irq.
Sylvain
v2 cover USB: gadget: atmel_usba_udc: Enable/disable USB PLL on Vbus change
Prepare_enable on rising edge, disable_unprepare on falling edge. Reduce power consumption if USB PLL is not already necessary for OHCI or EHCI. If USB host is not connected we can sleep with USB PLL stopped.
This driver does not support suspend/resume yet, not suspending if we are still attached to an USB host is fine for what I need, this patch allow suspending with USB PLL stopped when USB device is not currently used.
Change since v1:
* Using a threaded irq and mutex instead of spinclock as suggested
* Moved a silently fixed bug in a separate patch (1/2)
Sylvain Rochet (2):
USB: gadget: atmel_usba_udc: Fixed vbus_prev initial state
USB: gadget: atmel_usba_udc: Enable/disable USB PLL on Vbus change
drivers/usb/gadget/udc/atmel_usba_udc.c | 97 ++++++++++++++++++++++++--------- drivers/usb/gadget/udc/atmel_usba_udc.h | 4 ++ 2 files changed, 75 insertions(+), 26 deletions(-)
Signature · 1 line
2.1.4
PATCH v2 1/2 USB: gadget: atmel_usba_udc: Fixed vbus_prev initial state
If vbus gpio is high at init, we should set vbus_prev to true accordingly to the current vbus state. Without that, we skip the first vbus interrupt because the saved vbus state is not consistent.
--- drivers/usb/gadget/udc/atmel_usba_udc.c | 2 ++ 1 file changed, 2 insertions(+)
drivers/usb/gadget/udc/atmel_usba_udc.c +2 -0
@@ -1791,6 +1791,8 @@ static int atmel_usba_start(struct usb_gadget *gadget,toggle_bias (1 );usba_writel (udc,CTRL ,USBA_ENABLE_MASK );usba_writel (udc,INT_ENB ,USBA_END_OF_RESET ); + + udc->vbus_prev =1 ; }spin_unlock_irqrestore (&udc->lock, flags);
Signature · 1 line
2.1.4
PATCH v2 2/2 USB: gadget: atmel_usba_udc: Enable/disable USB PLL on Vbus change
In reply to Sylvain RochetPrepare_enable on rising edge, disable_unprepare on falling edge. Reduce power consumption if USB PLL is not already necessary for OHCI or EHCI. If USB host is not connected we can sleep with USB PLL stopped.
This driver does not support suspend/resume yet, not suspending if we are still attached to an USB host is fine for what I need, this patch allow suspending with USB PLL stopped when USB device is not currently used.
--- drivers/usb/gadget/udc/atmel_usba_udc.c | 95 ++++++++++++++++++++++++--------- drivers/usb/gadget/udc/atmel_usba_udc.h | 4 ++ 2 files changed, 73 insertions(+), 26 deletions(-)
drivers/usb/gadget/udc/atmel_usba_udc.c +69 -26
@@ -315,6 +315,38 @@ static inline void usba_cleanup_debugfs(struct usba_udc *udc) }#endif +static int start_clock (struct usba_udc *udc) +{ +int ret; + +if (udc->clocked) +return 0 ; + + ret =clk_prepare_enable (udc->pclk); +if (ret) +return ret; + ret =clk_prepare_enable (udc->hclk); +if (ret) { +clk_disable_unprepare (udc->pclk); +return ret; + } + + udc->clocked = true; +return ret; +} + +static int stop_clock (struct usba_udc *udc) +{ +if (!udc->clocked) +return 0 ; + +clk_disable_unprepare (udc->hclk); +clk_disable_unprepare (udc->pclk); + + udc->clocked = false; +return 0 ; +} +static int vbus_is_present (struct usba_udc *udc) {if (gpio_is_valid (udc->vbus_pin))
@@ -1719,42 +1751,55 @@ static irqreturn_t usba_udc_irq(int irq, void *devid)return IRQ_HANDLED ; } -static irqreturn_t usba_vbus_irq (int irq,void *devid) +static irqreturn_t usba_vbus_irq_thread (int irq,void *devid) {struct usba_udc *udc = devid;int vbus; +int ret;/* debounce */ udelay (10 ); -spin_lock (&udc->lock); +mutex_lock (&udc->vbus_mutex);/* May happen if Vbus pin toggles during probe() */ -if (!udc->driver) +spin_lock (&udc->lock); +if (!udc->driver) { +spin_unlock (&udc->lock); goto out; + } +spin_unlock (&udc->lock); vbus =vbus_is_present (udc);if (vbus != udc->vbus_prev) {if (vbus) { + ret =start_clock (udc); +if (ret) + goto out; + +spin_lock (&udc->lock);toggle_bias (1 );usba_writel (udc,CTRL ,USBA_ENABLE_MASK );usba_writel (udc,INT_ENB ,USBA_END_OF_RESET ); +spin_unlock (&udc->lock); }else { +spin_lock (&udc->lock); udc->gadget.speed =USB_SPEED_UNKNOWN ;reset_all_endpoints (udc);toggle_bias (0 );usba_writel (udc,CTRL ,USBA_DISABLE_MASK ); -if (udc->driver->disconnect) { -spin_unlock (&udc->lock); +spin_unlock (&udc->lock); + +stop_clock (udc); + +if (udc->driver->disconnect) udc->driver->disconnect (&udc->gadget); -spin_lock (&udc->lock); - } } udc->vbus_prev = vbus; } out: -spin_unlock (&udc->lock); +mutex_unlock (&udc->vbus_mutex);return IRQ_HANDLED ; }
@@ -1762,7 +1807,7 @@ out:static int atmel_usba_start (struct usb_gadget *gadget,struct usb_gadget_driver *driver) { -int ret; +int ret =0 ; struct usba_udc *udc = container_of(gadget, struct usba_udc, gadget);unsigned long flags;
@@ -1772,31 +1817,29 @@ static int atmel_usba_start(struct usb_gadget *gadget, udc->driver = driver;spin_unlock_irqrestore (&udc->lock, flags); - ret =clk_prepare_enable (udc->pclk); -if (ret) -return ret; - ret =clk_prepare_enable (udc->hclk); -if (ret) { -clk_disable_unprepare (udc->pclk); -return ret; - } - +mutex_lock (&udc->vbus_mutex); udc->vbus_prev =0 ;if (gpio_is_valid (udc->vbus_pin))enable_irq (gpio_to_irq (udc->vbus_pin));/* If Vbus is present, enable the controller and wait for reset */ -spin_lock_irqsave (&udc->lock, flags);if (vbus_is_present (udc) && udc->vbus_prev ==0 ) { + ret =start_clock (udc); +if (ret) + goto out; + +spin_lock_irqsave (&udc->lock, flags);toggle_bias (1 );usba_writel (udc,CTRL ,USBA_ENABLE_MASK );usba_writel (udc,INT_ENB ,USBA_END_OF_RESET ); +spin_unlock_irqrestore (&udc->lock, flags); udc->vbus_prev =1 ; } -spin_unlock_irqrestore (&udc->lock, flags); -return 0 ; +out: +mutex_unlock (&udc->vbus_mutex); +return ret; }static int atmel_usba_stop (struct usb_gadget *gadget)
@@ -1816,8 +1859,7 @@ static int atmel_usba_stop(struct usb_gadget *gadget)toggle_bias (0 );usba_writel (udc,CTRL ,USBA_DISABLE_MASK ); -clk_disable_unprepare (udc->hclk); -clk_disable_unprepare (udc->pclk); +stop_clock (udc); udc->driver =NULL ;
@@ -1997,6 +2039,7 @@ static int usba_udc_probe(struct platform_device *pdev)return PTR_ERR (hclk);spin_lock_init (&udc->lock); +mutex_init (&udc->vbus_mutex); udc->pdev = pdev; udc->pclk = pclk; udc->hclk = hclk;
@@ -2049,9 +2092,9 @@ static int usba_udc_probe(struct platform_device *pdev)if (gpio_is_valid (udc->vbus_pin)) {if (!devm_gpio_request (&pdev->dev, udc->vbus_pin,"atmel_usba_udc" )) { - ret =devm_request_irq (&pdev->dev, -gpio_to_irq (udc->vbus_pin), - usba_vbus_irq,0 , + ret =devm_request_threaded_irq (&pdev->dev, +gpio_to_irq (udc->vbus_pin),NULL , + usba_vbus_irq_thread,IRQF_NO_SUSPEND |IRQF_ONESHOT ,"atmel_usba_udc" , udc);if (ret) { udc->vbus_pin = -ENODEV ;
drivers/usb/gadget/udc/atmel_usba_udc.h +4 -0
@@ -308,6 +308,9 @@ struct usba_udc {/* Protect hw registers from concurrent modifications */ spinlock_t lock; +/* Mutex to prevent concurrent start or stop */ +struct mutex vbus_mutex; +void __iomem *regs;void __iomem *fifo;
@@ -321,6 +324,7 @@ struct usba_udc {struct clk *pclk;struct clk *hclk;struct usba_ep *usba_ep; +bool clocked;u16 devstatus;
Signature · 1 line
2.1.4
PATCH 2/2 USB: gadget: atmel_usba_udc: Enable/disable USB PLL on Vbus change
Quoted 68 lines · On Sun, 18 Jan 2015 15:51:21 +0100 Sylvain Rochet…
Prepare_enableon rising edge,disable_unprepareon falling edge. Reduce power consumption if USB PLL is not already necessary for OHCI or EHCI. If USB host is not connected we can sleep with USB PLL stopped.This driver does not support suspend/resume yet, not suspending if we are still attached to an USB host is fine for what I need, this patch allow suspending with USB PLL stopped when USB device is not currently used.
Signed-off-by Sylvain Rochet <[email protected]>--- drivers/usb/gadget/udc/atmel_usba_udc.c | 95 ++++++++++++++++++++++++--------- drivers/usb/gadget/udc/atmel_usba_udc.h | 4 ++ 2 files changed, 73 insertions(+), 26 deletions(-)drivers/usb/gadget/udc/atmel_usba_udc.c +34
-1@@ -315,6 +315,38 @@ static inline void usba_cleanup_debugfs(struct usba_udc *udc) }#endif +static int start_clock (struct usba_udc *udc) +{ +int ret; + +if (udc->clocked) +return 0 ; + + ret =clk_prepare_enable (udc->pclk); +if (ret) +return ret; + ret =clk_prepare_enable (udc->hclk); +if (ret) { +clk_disable_unprepare (udc->pclk); +return ret; + } + + udc->clocked = true; +return ret; +} + +static int stop_clock (struct usba_udc *udc) +{ +if (!udc->clocked) +return 0 ; + +clk_disable_unprepare (udc->hclk); +clk_disable_unprepare (udc->pclk); + + udc->clocked = false; +return 0 ; +} +static int vbus_is_present (struct usba_udc *udc) {if (gpio_is_valid (udc->vbus_pin))@@ -1719,42 +1751,55 @@ static irqreturn_t usba_udc_irq(int irq, void *devid)return IRQ_HANDLED ; } -static irqreturn_t usba_vbus_irq (int irq,void *devid) +static irqreturn_t usba_vbus_irq_thread (int irq,void *devid) {struct usba_udc *udc = devid;int vbus; +int ret;
drivers/usb/gadget/udc/atmel_usba_udc.c static irqreturn_t usba_udc_irq(int irq, void *devid) · line 1766
Quoted 10 lines
drivers/usb/gadget/udc/atmel_usba_udc.c +2
-2/* debounce */ udelay (10 ); -spin_lock (&udc->lock); +mutex_lock (&udc->vbus_mutex);/* May happen if Vbus pin toggles during probe() */ -if (!udc->driver) +spin_lock (&udc->lock);
Since this lock is taken in irq context (usba_udc_irq) and this handler is not called in irq context anymore you should use spin_lock_irqsave/unlock_irqrestore here.
Signature · 3 lines
Boris Brezillon, Free Electrons Embedded Linux and Kernel engineering http://free-electrons.com
v3 cover USB: gadget: atmel_usba_udc: Enable/disable USB PLL on Vbus change
Prepare_enable on rising edge, disable_unprepare on falling edge. Reduce power consumption if USB PLL is not already necessary for OHCI or EHCI. If USB host is not connected we can sleep with USB PLL stopped.
This driver does not support suspend/resume yet, not suspending if we are still attached to an USB host is fine for what I need, this patch allow suspending with USB PLL stopped when USB device is not currently used.
Changes since v2:
* Use spin_lock_irqsave/unlock_irqrestore instead of spin_lock/unlock in threaded interrupt because we are not in irq context anymore
* Removed useless and probably harmful IRQF_NO_SUSPEND from devm_request_threaded_irq() flags
Changes since v1:
* Using a threaded irq and mutex instead of spinclock as suggested
* Moved a silently fixed bug in a separate patch (1/2)
Sylvain Rochet (2):
USB: gadget: atmel_usba_udc: Fixed vbus_prev initial state
USB: gadget: atmel_usba_udc: Enable/disable USB PLL on Vbus change
drivers/usb/gadget/udc/atmel_usba_udc.c | 98 ++++++++++++++++++++++++--------- drivers/usb/gadget/udc/atmel_usba_udc.h | 4 ++ 2 files changed, 76 insertions(+), 26 deletions(-)
Signature · 1 line
2.1.4
PATCH v3 1/2 USB: gadget: atmel_usba_udc: Fixed vbus_prev initial state
If vbus gpio is high at init, we should set vbus_prev to true accordingly to the current vbus state. Without that, we skip the first vbus interrupt because the saved vbus state is not consistent.
--- drivers/usb/gadget/udc/atmel_usba_udc.c | 2 ++ 1 file changed, 2 insertions(+)
drivers/usb/gadget/udc/atmel_usba_udc.c +2 -0
@@ -1791,6 +1791,8 @@ static int atmel_usba_start(struct usb_gadget *gadget,toggle_bias (1 );usba_writel (udc,CTRL ,USBA_ENABLE_MASK );usba_writel (udc,INT_ENB ,USBA_END_OF_RESET ); + + udc->vbus_prev =1 ; }spin_unlock_irqrestore (&udc->lock, flags);
Signature · 1 line
2.1.4
PATCH v3 1/2 USB: gadget: atmel_usba_udc: Fixed vbus_prev initial state
Le 18/01/2015 18:24, Sylvain Rochet a ?crit :
Quoted 5 lines
If vbus gpio is high at init, we should set
vbus_prevto true accordingly to the current vbus state. Without that, we skip the first vbus interrupt because the saved vbus state is not consistent.Signed-off-by Sylvain Rochet <[email protected]>
Indeed:
We can also add the following tags:
Bye,
Quoted 18 lines
--- drivers/usb/gadget/udc/atmel_usba_udc.c | 2 ++ 1 file changed, 2 insertions(+)drivers/usb/gadget/udc/atmel_usba_udc.c +2
-0@@ -1791,6 +1791,8 @@ static int atmel_usba_start(struct usb_gadget *gadget,toggle_bias (1 );usba_writel (udc,CTRL ,USBA_ENABLE_MASK );usba_writel (udc,INT_ENB ,USBA_END_OF_RESET ); + + udc->vbus_prev =1 ; }spin_unlock_irqrestore (&udc->lock, flags);
Signature · 1 line
Nicolas Ferre
PATCH v3 1/2 USB: gadget: atmel_usba_udc: Fixed vbus_prev initial state
Quoted 5 lines · Nicolas Ferre
Le 18/01/2015 18:24, Sylvain Rochet a ?crit :
Quoted 4 lines
If vbus gpio is high at init, we should set
vbus_prevto true accordingly to the current vbus state. Without that, we skip the first vbus interrupt because the saved vbus state is not consistent.
Quoted 8 lines
Quoted 1 line
Signed-off-by Sylvain Rochet <[email protected]>Indeed:
Acked-by Nicolas Ferre <[email protected]>We can also add the following tags:
Fixes 914a3f3b3754 ("USB: add atmel_usba_udc driver")Cc <[email protected]> # 2.6.x-ish
Please resend with the proper stable format. Also, this 2.6.x-ish should be 2.6.24+, git describe helps a lot figuring these out.
Signature · 8 lines
balbi -------------- next part -------------- A non-text attachment was scrubbed... Name: signature.asc Type: application/pgp-signature Size: 819 bytes Desc: Digital signature URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20150119/d7afe913/attachment-0001.sig>
PATCH v3 1/2 USB: gadget: atmel_usba_udc: Fixed vbus_prev initial state
Hello,
Quoted 11 lines · Felipe Balbi
Quoted 10 lines · Nicolas Ferre
Le 18/01/2015 18:24, Sylvain Rochet a ?crit :
Quoted 5 lines
If vbus gpio is high at init, we should set
vbus_prevto true accordingly to the current vbus state. Without that, we skip the first vbus interrupt because the saved vbus state is not consistent.Signed-off-by Sylvain Rochet <[email protected]>Indeed:
Acked-by Nicolas Ferre <[email protected]>
Quoted 6 lines
Quoted 3 lines
We can also add the following tags:
Fixes 914a3f3b3754 ("USB: add atmel_usba_udc driver")Cc <[email protected]> # 2.6.x-ishPlease resend with the proper stable format. Also, this 2.6.x-ish should be 2.6.24+, git describe helps a lot figuring these out.
Unfortunately I failed to understand what I should do. The "Also" keyword means for me that there are two differents things to fix:
1. proper stable format
2. 2.6.x-ish ? 2.6.24+
2. does not imply fixing 1., isn'it ?
I read SubmittingPatches thoroughly and I can't find a reference to stable format.
Sylvain
PATCH v3 1/2 USB: gadget: atmel_usba_udc: Fixed vbus_prev initial state
Le 20/01/2015 12:02, Sylvain Rochet a ?crit :
Quoted 21 lines
Hello,
Quoted 11 lines · Felipe Balbi
Quoted 10 lines · Nicolas Ferre
Le 18/01/2015 18:24, Sylvain Rochet a ?crit :
Quoted 5 lines
If vbus gpio is high at init, we should set
vbus_prevto true accordingly to the current vbus state. Without that, we skip the first vbus interrupt because the saved vbus state is not consistent.Signed-off-by Sylvain Rochet <[email protected]>Indeed:
Acked-by Nicolas Ferre <[email protected]>Quoted 6 lines
Quoted 3 lines
We can also add the following tags:
Fixes 914a3f3b3754 ("USB: add atmel_usba_udc driver")Cc <[email protected]> # 2.6.x-ishPlease resend with the proper stable format. Also, this 2.6.x-ish should be 2.6.24+, git describe helps a lot figuring these out.
Quoted 7 lines
Unfortunately I failed to understand what I should do. The "Also" keyword means for me that there are two differents things to fix:
1. proper stable format
2. 2.6.x-ish ? 2.6.24+2. does not imply fixing 1., isn'it ?
Well, I think it does. My understanding is that fixing '2' is the solution.
Quoted 4 lines
I read SubmittingPatches thoroughly and I can't find a reference to stable format.
Sylvain
Bye,
Signature · 1 line
Nicolas Ferre
PATCH v3 2/2 USB: gadget: atmel_usba_udc: Enable/disable USB PLL on Vbus change
In reply to Sylvain RochetPrepare_enable on rising edge, disable_unprepare on falling edge. Reduce power consumption if USB PLL is not already necessary for OHCI or EHCI. If USB host is not connected we can sleep with USB PLL stopped.
This driver does not support suspend/resume yet, not suspending if we are still attached to an USB host is fine for what I need, this patch allow suspending with USB PLL stopped when USB device is not currently used.
--- drivers/usb/gadget/udc/atmel_usba_udc.c | 96 ++++++++++++++++++++++++--------- drivers/usb/gadget/udc/atmel_usba_udc.h | 4 ++ 2 files changed, 74 insertions(+), 26 deletions(-)
drivers/usb/gadget/udc/atmel_usba_udc.c +70 -26
@@ -315,6 +315,38 @@ static inline void usba_cleanup_debugfs(struct usba_udc *udc) }#endif +static int start_clock (struct usba_udc *udc) +{ +int ret; + +if (udc->clocked) +return 0 ; + + ret =clk_prepare_enable (udc->pclk); +if (ret) +return ret; + ret =clk_prepare_enable (udc->hclk); +if (ret) { +clk_disable_unprepare (udc->pclk); +return ret; + } + + udc->clocked = true; +return ret; +} + +static int stop_clock (struct usba_udc *udc) +{ +if (!udc->clocked) +return 0 ; + +clk_disable_unprepare (udc->hclk); +clk_disable_unprepare (udc->pclk); + + udc->clocked = false; +return 0 ; +} +static int vbus_is_present (struct usba_udc *udc) {if (gpio_is_valid (udc->vbus_pin))
@@ -1719,42 +1751,56 @@ static irqreturn_t usba_udc_irq(int irq, void *devid)return IRQ_HANDLED ; } -static irqreturn_t usba_vbus_irq (int irq,void *devid) +static irqreturn_t usba_vbus_irq_thread (int irq,void *devid) {struct usba_udc *udc = devid;int vbus; +int ret; +unsigned long flags;/* debounce */ udelay (10 ); -spin_lock (&udc->lock); +mutex_lock (&udc->vbus_mutex);/* May happen if Vbus pin toggles during probe() */ -if (!udc->driver) +spin_lock_irqsave (&udc->lock, flags); +if (!udc->driver) { +spin_unlock_irqrestore (&udc->lock, flags); goto out; + } +spin_unlock_irqrestore (&udc->lock, flags); vbus =vbus_is_present (udc);if (vbus != udc->vbus_prev) {if (vbus) { + ret =start_clock (udc); +if (ret) + goto out; + +spin_lock_irqsave (&udc->lock, flags);toggle_bias (1 );usba_writel (udc,CTRL ,USBA_ENABLE_MASK );usba_writel (udc,INT_ENB ,USBA_END_OF_RESET ); +spin_unlock_irqrestore (&udc->lock, flags); }else { +spin_lock_irqsave (&udc->lock, flags); udc->gadget.speed =USB_SPEED_UNKNOWN ;reset_all_endpoints (udc);toggle_bias (0 );usba_writel (udc,CTRL ,USBA_DISABLE_MASK ); -if (udc->driver->disconnect) { -spin_unlock (&udc->lock); +spin_unlock_irqrestore (&udc->lock, flags); + +stop_clock (udc); + +if (udc->driver->disconnect) udc->driver->disconnect (&udc->gadget); -spin_lock (&udc->lock); - } } udc->vbus_prev = vbus; } out: -spin_unlock (&udc->lock); +mutex_unlock (&udc->vbus_mutex);return IRQ_HANDLED ; }
@@ -1762,7 +1808,7 @@ out:static int atmel_usba_start (struct usb_gadget *gadget,struct usb_gadget_driver *driver) { -int ret; +int ret =0 ; struct usba_udc *udc = container_of(gadget, struct usba_udc, gadget);unsigned long flags;
@@ -1772,31 +1818,29 @@ static int atmel_usba_start(struct usb_gadget *gadget, udc->driver = driver;spin_unlock_irqrestore (&udc->lock, flags); - ret =clk_prepare_enable (udc->pclk); -if (ret) -return ret; - ret =clk_prepare_enable (udc->hclk); -if (ret) { -clk_disable_unprepare (udc->pclk); -return ret; - } - +mutex_lock (&udc->vbus_mutex); udc->vbus_prev =0 ;if (gpio_is_valid (udc->vbus_pin))enable_irq (gpio_to_irq (udc->vbus_pin));/* If Vbus is present, enable the controller and wait for reset */ -spin_lock_irqsave (&udc->lock, flags);if (vbus_is_present (udc) && udc->vbus_prev ==0 ) { + ret =start_clock (udc); +if (ret) + goto out; + +spin_lock_irqsave (&udc->lock, flags);toggle_bias (1 );usba_writel (udc,CTRL ,USBA_ENABLE_MASK );usba_writel (udc,INT_ENB ,USBA_END_OF_RESET ); +spin_unlock_irqrestore (&udc->lock, flags); udc->vbus_prev =1 ; } -spin_unlock_irqrestore (&udc->lock, flags); -return 0 ; +out: +mutex_unlock (&udc->vbus_mutex); +return ret; }static int atmel_usba_stop (struct usb_gadget *gadget)
@@ -1816,8 +1860,7 @@ static int atmel_usba_stop(struct usb_gadget *gadget)toggle_bias (0 );usba_writel (udc,CTRL ,USBA_DISABLE_MASK ); -clk_disable_unprepare (udc->hclk); -clk_disable_unprepare (udc->pclk); +stop_clock (udc); udc->driver =NULL ;
@@ -1997,6 +2040,7 @@ static int usba_udc_probe(struct platform_device *pdev)return PTR_ERR (hclk);spin_lock_init (&udc->lock); +mutex_init (&udc->vbus_mutex); udc->pdev = pdev; udc->pclk = pclk; udc->hclk = hclk;
@@ -2049,9 +2093,9 @@ static int usba_udc_probe(struct platform_device *pdev)if (gpio_is_valid (udc->vbus_pin)) {if (!devm_gpio_request (&pdev->dev, udc->vbus_pin,"atmel_usba_udc" )) { - ret =devm_request_irq (&pdev->dev, -gpio_to_irq (udc->vbus_pin), - usba_vbus_irq,0 , + ret =devm_request_threaded_irq (&pdev->dev, +gpio_to_irq (udc->vbus_pin),NULL , + usba_vbus_irq_thread,IRQF_ONESHOT ,"atmel_usba_udc" , udc);if (ret) { udc->vbus_pin = -ENODEV ;
drivers/usb/gadget/udc/atmel_usba_udc.h +4 -0
@@ -308,6 +308,9 @@ struct usba_udc {/* Protect hw registers from concurrent modifications */ spinlock_t lock; +/* Mutex to prevent concurrent start or stop */ +struct mutex vbus_mutex; +void __iomem *regs;void __iomem *fifo;
@@ -321,6 +324,7 @@ struct usba_udc {struct clk *pclk;struct clk *hclk;struct usba_ep *usba_ep; +bool clocked;u16 devstatus;
Signature · 1 line
2.1.4
PATCH v3 2/2 USB: gadget: atmel_usba_udc: Enable/disable USB PLL on Vbus change
Le 18/01/2015 18:24, Sylvain Rochet a ?crit :
Quoted 1 line
Prepare_enableon rising edge,disable_unprepareon falling edge. Reduce
Please re-write which "edge" we are talking about: "... falling edge of the Vbus signal" for example.
Quoted 1 line
power consumption if USB PLL is not already necessary for OHCI or EHCI.
Is a verb missing in the previous sentence?
Quoted 6 lines
If USB host is not connected we can sleep with USB PLL stopped.
This driver does not support suspend/resume yet, not suspending if we are still attached to an USB host is fine for what I need, this patch allow suspending with USB PLL stopped when USB device is not currently used.
Can you please make it more clear. Several separated sentences seem necessary here.
Quoted 65 lines
Signed-off-by Sylvain Rochet <[email protected]>--- drivers/usb/gadget/udc/atmel_usba_udc.c | 96 ++++++++++++++++++++++++--------- drivers/usb/gadget/udc/atmel_usba_udc.h | 4 ++ 2 files changed, 74 insertions(+), 26 deletions(-)drivers/usb/gadget/udc/atmel_usba_udc.c +35
-2@@ -315,6 +315,38 @@ static inline void usba_cleanup_debugfs(struct usba_udc *udc) }#endif +static int start_clock (struct usba_udc *udc) +{ +int ret; + +if (udc->clocked) +return 0 ; + + ret =clk_prepare_enable (udc->pclk); +if (ret) +return ret; + ret =clk_prepare_enable (udc->hclk); +if (ret) { +clk_disable_unprepare (udc->pclk); +return ret; + } + + udc->clocked = true; +return ret; +} + +static int stop_clock (struct usba_udc *udc) +{ +if (!udc->clocked) +return 0 ; + +clk_disable_unprepare (udc->hclk); +clk_disable_unprepare (udc->pclk); + + udc->clocked = false; +return 0 ; +} +static int vbus_is_present (struct usba_udc *udc) {if (gpio_is_valid (udc->vbus_pin))@@ -1719,42 +1751,56 @@ static irqreturn_t usba_udc_irq(int irq, void *devid)return IRQ_HANDLED ; } -static irqreturn_t usba_vbus_irq (int irq,void *devid) +static irqreturn_t usba_vbus_irq_thread (int irq,void *devid) {struct usba_udc *udc = devid;int vbus; +int ret; +unsigned long flags;/* debounce */ udelay (10 ); -spin_lock (&udc->lock);
drivers/usb/gadget/udc/atmel_usba_udc.c static irqreturn_t usba_udc_irq(int irq, void *devid) · line 1772
Quoted 10 lines
drivers/usb/gadget/udc/atmel_usba_udc.c +6
-1+mutex_lock (&udc->vbus_mutex);/* May happen if Vbus pin toggles during probe() */ -if (!udc->driver) +spin_lock_irqsave (&udc->lock, flags); +if (!udc->driver) { +spin_unlock_irqrestore (&udc->lock, flags); goto out; + } +spin_unlock_irqrestore (&udc->lock, flags);
I'm not sure that the protection by spin_lock is needed above.
Quoted 136 lines
drivers/usb/gadget/udc/atmel_usba_udc.c +29
-23vbus =vbus_is_present (udc);if (vbus != udc->vbus_prev) {if (vbus) { + ret =start_clock (udc); +if (ret) + goto out; + +spin_lock_irqsave (&udc->lock, flags);toggle_bias (1 );usba_writel (udc,CTRL ,USBA_ENABLE_MASK );usba_writel (udc,INT_ENB ,USBA_END_OF_RESET ); +spin_unlock_irqrestore (&udc->lock, flags); }else { +spin_lock_irqsave (&udc->lock, flags); udc->gadget.speed =USB_SPEED_UNKNOWN ;reset_all_endpoints (udc);toggle_bias (0 );usba_writel (udc,CTRL ,USBA_DISABLE_MASK ); -if (udc->driver->disconnect) { -spin_unlock (&udc->lock); +spin_unlock_irqrestore (&udc->lock, flags); + +stop_clock (udc); + +if (udc->driver->disconnect) udc->driver->disconnect (&udc->gadget); -spin_lock (&udc->lock); - } } udc->vbus_prev = vbus; } out: -spin_unlock (&udc->lock); +mutex_unlock (&udc->vbus_mutex);return IRQ_HANDLED ; }@@ -1762,7 +1808,7 @@ out:static int atmel_usba_start (struct usb_gadget *gadget,struct usb_gadget_driver *driver) { -int ret; +int ret =0 ; struct usba_udc *udc = container_of(gadget, struct usba_udc, gadget);unsigned long flags;@@ -1772,31 +1818,29 @@ static int atmel_usba_start(struct usb_gadget *gadget, udc->driver = driver;spin_unlock_irqrestore (&udc->lock, flags); - ret =clk_prepare_enable (udc->pclk); -if (ret) -return ret; - ret =clk_prepare_enable (udc->hclk); -if (ret) { -clk_disable_unprepare (udc->pclk); -return ret; - } - +mutex_lock (&udc->vbus_mutex); udc->vbus_prev =0 ;if (gpio_is_valid (udc->vbus_pin))enable_irq (gpio_to_irq (udc->vbus_pin));/* If Vbus is present, enable the controller and wait for reset */ -spin_lock_irqsave (&udc->lock, flags);if (vbus_is_present (udc) && udc->vbus_prev ==0 ) { + ret =start_clock (udc); +if (ret) + goto out; + +spin_lock_irqsave (&udc->lock, flags);toggle_bias (1 );usba_writel (udc,CTRL ,USBA_ENABLE_MASK );usba_writel (udc,INT_ENB ,USBA_END_OF_RESET ); +spin_unlock_irqrestore (&udc->lock, flags); udc->vbus_prev =1 ; } -spin_unlock_irqrestore (&udc->lock, flags); -return 0 ; +out: +mutex_unlock (&udc->vbus_mutex); +return ret; }static int atmel_usba_stop (struct usb_gadget *gadget)@@ -1816,8 +1860,7 @@ static int atmel_usba_stop(struct usb_gadget *gadget)toggle_bias (0 );usba_writel (udc,CTRL ,USBA_DISABLE_MASK ); -clk_disable_unprepare (udc->hclk); -clk_disable_unprepare (udc->pclk); +stop_clock (udc); udc->driver =NULL ;@@ -1997,6 +2040,7 @@ static int usba_udc_probe(struct platform_device *pdev)return PTR_ERR (hclk);spin_lock_init (&udc->lock); +mutex_init (&udc->vbus_mutex); udc->pdev = pdev; udc->pclk = pclk; udc->hclk = hclk;@@ -2049,9 +2093,9 @@ static int usba_udc_probe(struct platform_device *pdev)if (gpio_is_valid (udc->vbus_pin)) {if (!devm_gpio_request (&pdev->dev, udc->vbus_pin,"atmel_usba_udc" )) { - ret =devm_request_irq (&pdev->dev, -gpio_to_irq (udc->vbus_pin), - usba_vbus_irq,0 , + ret =devm_request_threaded_irq (&pdev->dev, +gpio_to_irq (udc->vbus_pin),NULL , + usba_vbus_irq_thread,IRQF_ONESHOT ,"atmel_usba_udc" , udc);if (ret) { udc->vbus_pin = -ENODEV ;drivers/usb/gadget/udc/atmel_usba_udc.h +3
-0@@ -308,6 +308,9 @@ struct usba_udc {/* Protect hw registers from concurrent modifications */ spinlock_t lock; +/* Mutex to prevent concurrent start or stop */ +struct mutex vbus_mutex; +void __iomem *regs;void __iomem *fifo;
drivers/usb/gadget/udc/atmel_usba_udc.h struct usba_udc { · line 329
Quoted 6 lines
drivers/usb/gadget/udc/atmel_usba_udc.h +1
-0@@ -321,6 +324,7 @@ struct usba_udc {struct clk *pclk;struct clk *hclk;struct usba_ep *usba_ep; +bool clocked;u16 devstatus;
Otherwise, it looks okay, so once little corrections done, you can add my:
Thanks, bye,
Signature · 1 line
Nicolas Ferre
PATCH v3 2/2 USB: gadget: atmel_usba_udc: Enable/disable USB PLL on Vbus change
Hello Nicolas,
Quoted 12 lines · Nicolas Ferre
Le 18/01/2015 18:24, Sylvain Rochet a ?crit :
Quoted 1 line
Prepare_enableon rising edge,disable_unprepareon falling edge. ReducePlease re-write which "edge" we are talking about: "... falling edge of the Vbus signal" for example.
Quoted 1 line
power consumption if USB PLL is not already necessary for OHCI or EHCI.
Is a verb missing in the previous sentence?
Quoted 2 lines
If USB host is not connected we can sleep with USB PLL stopped.
Quoted 7 lines
Quoted 4 lines
This driver does not support suspend/resume yet, not suspending if we are still attached to an USB host is fine for what I need, this patch allow suspending with USB PLL stopped when USB device is not currently used.
Can you please make it more clear. Several separated sentences seem necessary here.
Maybe :)
Proposal:
Start clocks on rising edge of the Vbus signal, stop clocks on falling edge of the Vbus signal.
If USB PLL is not necessary for other USB drivers (e.g. OHCI and EHCI) it will reduce power consumption by switching off the USB PLL if no USB Host is currently connected to this USB Device.
We are using Vbus GPIO signal to detect Host presence. If Vbus signal is not available then the device stay continuously clocked.
Note this driver does not support suspend/resume yet, it may stay clocked if USB Host is still connected when suspending. For what I need, forbidding suspend from userland if we are still attached to an USB host is fine, but we might as well add suspend/resume to this driver in the future.
Quoted 10 lines
drivers/usb/gadget/udc/atmel_usba_udc.c static irqreturn_t usba_udc_irq(int irq, void *devid) · line 1772
Quoted 8 lines
drivers/usb/gadget/udc/atmel_usba_udc.c +5
-1/* May happen if Vbus pin toggles during probe() */ -if (!udc->driver) +spin_lock_irqsave (&udc->lock, flags); +if (!udc->driver) { +spin_unlock_irqrestore (&udc->lock, flags); goto out; + } +spin_unlock_irqrestore (&udc->lock, flags);I'm not sure that the protection by
spin_lockis needed above.
I'm not sure too, it was already in a spinlock area, I obviously kept it because it was not the purpose of this patch.
This seem to be in mirror of atmel_usba_start() which does:
spin_lock_irqsave(&udc->lock, flags); udc->devstatus = 1 << USB_DEVICE_SELF_POWERED; udc->driver = driver; spin_unlock_irqrestore(&udc->lock, flags);
? but vbus_pin IRQ is not yet enabled.
Same for atmel_usba_stop() which disable vbus_pin IRQ before setting udc->driver to NULL, but without spinlock this time (why?, this should be consistent IMHO).
I don't know if it is guaranteed that IRQ does not fire nor continue to run after disable_irq() returns, especially for threaded IRQ.
If the following sequence might happen:
atmel_usba_stop()
disable_irq(vbus)
usba_vbus_irq_thread() called lately
check for (!udc->driver) and continue
udc->driver = NULL;
if (udc->driver->disconnect)
*CRASH*Then the patch should be modified to protect udc->driver with the vbus mutex.
In this case the previous implementation wasn't perfect too, the atmel_usba_stop() does not lock around the NULLification of driver,
Moreover the spinlock is (and was) unlocked in VBUS interrupt just before calling udc->driver->disconnect, which makes udc->driver actually not locked anywere.
If the previous sequence possible ? If no, udc->driver does not need locking, if yes, it is currently not locked enough.
Sylvain
PATCH v3 2/2 USB: gadget: atmel_usba_udc: Enable/disable USB PLL on Vbus change
Quoted 58 lines · On Mon, 19 Jan 2015 18:46:31 +0100 Sylvain Rochet…
Hello Nicolas,
Quoted 12 lines · Nicolas Ferre
Le 18/01/2015 18:24, Sylvain Rochet a ?crit :
Quoted 1 line
Prepare_enableon rising edge,disable_unprepareon falling edge. ReducePlease re-write which "edge" we are talking about: "... falling edge of the Vbus signal" for example.
Quoted 1 line
power consumption if USB PLL is not already necessary for OHCI or EHCI.
Is a verb missing in the previous sentence?
Quoted 2 lines
If USB host is not connected we can sleep with USB PLL stopped.
Quoted 7 lines
Quoted 4 lines
This driver does not support suspend/resume yet, not suspending if we are still attached to an USB host is fine for what I need, this patch allow suspending with USB PLL stopped when USB device is not currently used.
Can you please make it more clear. Several separated sentences seem necessary here.
Maybe :)
Proposal:
Start clocks on rising edge of the Vbus signal, stop clocks on falling edge of the Vbus signal.
If USB PLL is not necessary for other USB drivers (e.g. OHCI and EHCI) it will reduce power consumption by switching off the USB PLL if no USB Host is currently connected to this USB Device.
We are using Vbus GPIO signal to detect Host presence. If Vbus signal is not available then the device stay continuously clocked.
Note this driver does not support suspend/resume yet, it may stay clocked if USB Host is still connected when suspending. For what I need, forbidding suspend from userland if we are still attached to an USB host is fine, but we might as well add suspend/resume to this driver in the future.
Quoted 10 lines
drivers/usb/gadget/udc/atmel_usba_udc.c static irqreturn_t usba_udc_irq(int irq, void *devid) · line 1772
Quoted 8 lines
drivers/usb/gadget/udc/atmel_usba_udc.c +5
-1/* May happen if Vbus pin toggles during probe() */ -if (!udc->driver) +spin_lock_irqsave (&udc->lock, flags); +if (!udc->driver) { +spin_unlock_irqrestore (&udc->lock, flags); goto out; + } +spin_unlock_irqrestore (&udc->lock, flags);I'm not sure that the protection by
spin_lockis needed above.
Quoted 2 lines
I'm not sure too, it was already in a spinlock area, I obviously kept it because it was not the purpose of this patch.
According to the comment placed above this test it's here to prevent any action triggered by the vbus pin irq while the driver is not properly probed yet.
You could use:
set_irq_flags(vbus_irq, IRQF_NOAUTOEN);
before calling devm_request_threaded_irq.
This will keep the irq disabled instead of enabling it at request time. Moreover, this simplify the vbus_irq request code (which disables the irq just after requesting it).
Quoted 11 lines
This seem to be in mirror of
atmel_usba_start()which does:spin_lock_irqsave(&udc->lock, flags); udc->devstatus = 1 << USB_DEVICE_SELF_POWERED; udc->driver = driver; spin_unlock_irqrestore(&udc->lock, flags);? but
vbus_pinIRQ is not yet enabled.
Quoted 7 lines
Same for
atmel_usba_stop()which disablevbus_pinIRQ before settingudc->driverto NULL, but without spinlock this time (why?, this should be consistent IMHO).I don't know if it is guaranteed that IRQ does not fire nor continue to run after
disable_irq()returns, especially for threaded IRQ.
And yes, disable_irq waits for all irq handler termination, including threaded irqs: see [1], [2].
Quoted 17 lines
If the following sequence might happen:
atmel_usba_stop()disable_irq(vbus) usba_vbus_irq_thread() called lately check for (!udc->driver) and continue udc->driver = NULL; if (udc->driver->disconnect) *CRASH*Then the patch should be modified to protect
udc->driverwith the vbus mutex.In this case the previous implementation wasn't perfect too, the
atmel_usba_stop()does not lock around the NULLification of driver,
Quoted 6 lines
Moreover the spinlock is (and was) unlocked in VBUS interrupt just before calling
udc->driver->disconnect, which makesudc->driveractually not locked anywere.If the previous sequence possible ? If no,
udc->driverdoes not need locking, if yes, it is currently not locked enough.
If you rework the vbus_irq request as I suggested I think you can get rid of this !udc->driver test, and thus drop the locking around it ;-).
Best Regards,
Boris
[1]http://lxr.free-electrons.com/source/kernel/irq/manage.c#L432 [2]http://lxr.free-electrons.com/source/kernel/irq/manage.c#L92
Signature · 3 lines
Boris Brezillon, Free Electrons Embedded Linux and Kernel engineering http://free-electrons.com
PATCH v3 2/2 USB: gadget: atmel_usba_udc: Enable/disable USB PLL on Vbus change
Le 19/01/2015 21:15, Boris Brezillon a ?crit :
Quoted 35 lines
Quoted 32 lines · On Mon, 19 Jan 2015 18:46:31 +0100 Sylvain Rochet…
Hello Nicolas,
Quoted 12 lines · Nicolas Ferre
Le 18/01/2015 18:24, Sylvain Rochet a ?crit :
Quoted 1 line
Prepare_enableon rising edge,disable_unprepareon falling edge. ReducePlease re-write which "edge" we are talking about: "... falling edge of the Vbus signal" for example.
Quoted 1 line
power consumption if USB PLL is not already necessary for OHCI or EHCI.
Is a verb missing in the previous sentence?
Quoted 2 lines
If USB host is not connected we can sleep with USB PLL stopped.
Quoted 7 lines
Quoted 4 lines
This driver does not support suspend/resume yet, not suspending if we are still attached to an USB host is fine for what I need, this patch allow suspending with USB PLL stopped when USB device is not currently used.
Can you please make it more clear. Several separated sentences seem necessary here.
Maybe :)
Proposal:
Start clocks on rising edge of the Vbus signal, stop clocks on falling edge of the Vbus signal.
Quoted 6 lines
Quoted 6 lines
If USB PLL is not necessary for other USB drivers (e.g. OHCI and EHCI) it will reduce power consumption by switching off the USB PLL if no USB Host is currently connected to this USB Device.
We are using Vbus GPIO signal to detect Host presence. If Vbus signal is not available then the device stay continuously clocked.
Typo: s/stay/stays/
Quoted 5 lines
Quoted 5 lines
Note this driver does not support suspend/resume yet, it may stay clocked if USB Host is still connected when suspending. For what I need, forbidding suspend from userland if we are still attached to an USB host is fine, but we might as well add suspend/resume to this driver in the future.
Sylvain, thanks for having rephrased this part! It looks clear.
Quoted 75 lines
Quoted 11 lines
Quoted 10 lines
drivers/usb/gadget/udc/atmel_usba_udc.c static irqreturn_t usba_udc_irq(int irq, void *devid) · line 1772
Quoted 8 lines
drivers/usb/gadget/udc/atmel_usba_udc.c +5
-1/* May happen if Vbus pin toggles during probe() */ -if (!udc->driver) +spin_lock_irqsave (&udc->lock, flags); +if (!udc->driver) { +spin_unlock_irqrestore (&udc->lock, flags); goto out; + } +spin_unlock_irqrestore (&udc->lock, flags);I'm not sure that the protection by
spin_lockis needed above.Quoted 2 lines
I'm not sure too, it was already in a spinlock area, I obviously kept it because it was not the purpose of this patch.
According to the comment placed above this test it's here to prevent any action triggered by the vbus pin irq while the driver is not properly probed yet.
You could use:
set_irq_flags(vbus_irq, IRQF_NOAUTOEN);before calling
devm_request_threaded_irq.
This will keep the irq disabled instead of enabling it at request time. Moreover, this simplify thevbus_irqrequest code (which disables the irq just after requesting it).Quoted 11 lines
This seem to be in mirror of
atmel_usba_start()which does:spin_lock_irqsave(&udc->lock, flags); udc->devstatus = 1 << USB_DEVICE_SELF_POWERED; udc->driver = driver; spin_unlock_irqrestore(&udc->lock, flags);? but
vbus_pinIRQ is not yet enabled.Quoted 7 lines
Same for
atmel_usba_stop()which disablevbus_pinIRQ before settingudc->driverto NULL, but without spinlock this time (why?, this should be consistent IMHO).I don't know if it is guaranteed that IRQ does not fire nor continue to run after
disable_irq()returns, especially for threaded IRQ.And yes,
disable_irqwaits for all irq handler termination, including threaded irqs: see [1], [2].Quoted 17 lines
If the following sequence might happen:
atmel_usba_stop()disable_irq(vbus) usba_vbus_irq_thread() called lately check for (!udc->driver) and continue udc->driver = NULL; if (udc->driver->disconnect) *CRASH*Then the patch should be modified to protect
udc->driverwith the vbus mutex.In this case the previous implementation wasn't perfect too, the
atmel_usba_stop()does not lock around the NULLification of driver,Quoted 6 lines
Moreover the spinlock is (and was) unlocked in VBUS interrupt just before calling
udc->driver->disconnect, which makesudc->driveractually not locked anywere.If the previous sequence possible ? If no,
udc->driverdoes not need locking, if yes, it is currently not locked enough.If you rework the
vbus_irqrequest as I suggested I think you can get rid of this !udc->driver test, and thus drop the locking around it ;-).
Quoted 6 lines
Best Regards,
Boris
[1]http://lxr.free-electrons.com/source/kernel/irq/manage.c#L432 [2]http://lxr.free-electrons.com/source/kernel/irq/manage.c#L92
Thanks Sylvain for having described the problem lengthly and Boris for your detailed explanation and suggestion concerning this sequence.
So, if you can re-send a new version and also add the stable tags as suggested by Felipe, it would be really cool.
Bye,
Signature · 1 line
Nicolas Ferre
PATCH 2/2 USB: gadget: atmel_usba_udc: Enable/disable USB PLL on Vbus change
In reply to Sylvain RochetQuoted 23 lines · On Sun, 18 Jan 2015 15:51:21 +0100 Sylvain Rochet…
Prepare_enableon rising edge,disable_unprepareon falling edge. Reduce power consumption if USB PLL is not already necessary for OHCI or EHCI. If USB host is not connected we can sleep with USB PLL stopped.This driver does not support suspend/resume yet, not suspending if we are still attached to an USB host is fine for what I need, this patch allow suspending with USB PLL stopped when USB device is not currently used.
Signed-off-by Sylvain Rochet <[email protected]>--- drivers/usb/gadget/udc/atmel_usba_udc.c | 95 ++++++++++++++++++++++++--------- drivers/usb/gadget/udc/atmel_usba_udc.h | 4 ++ 2 files changed, 73 insertions(+), 26 deletions(-)drivers/usb/gadget/udc/atmel_usba_udc.c +0
-0@@ -315,6 +315,38 @@ static inline void usba_cleanup_debugfs(struct usba_udc *udc) }#endif
[...]
drivers/usb/gadget/udc/atmel_usba_udc.c static int usba_udc_probe(struct platform_device *pdev) · line 2097
Quoted 9 lines
drivers/usb/gadget/udc/atmel_usba_udc.c +3
-3if (gpio_is_valid (udc->vbus_pin)) {if (!devm_gpio_request (&pdev->dev, udc->vbus_pin,"atmel_usba_udc" )) { - ret = devm_request_irq(&pdev->dev, - gpio_to_irq(udc->vbus_pin), - usba_vbus_irq, 0, + ret = devm_request_threaded_irq(&pdev->dev, + gpio_to_irq(udc->vbus_pin), NULL, + usba_vbus_irq_thread, IRQF_NO_SUSPEND | IRQF_ONESHOT,
You should drop the IRQF_NO_SUSPEND flag. If you want to wakeup on VBUS change you should use enable_irq_wake instead.
Signature · 3 lines
Boris Brezillon, Free Electrons Embedded Linux and Kernel engineering http://free-electrons.com