diff options
author | Sylvain Rochet <sylvain.rochet@finsecur.com> | 2015-02-12 12:54:07 -0500 |
---|---|---|
committer | Felipe Balbi <balbi@ti.com> | 2015-03-11 11:19:37 -0400 |
commit | 112bf24471b50f806cbcbead7bd485da70401b83 (patch) | |
tree | 2ecf7803a92a71d37a91d4ddc6f3802287b39281 /drivers/usb/gadget | |
parent | a64ef71ddc13fc76a6f4af8c61e0106a62004380 (diff) |
usb: gadget: atmel_usba_udc: Add suspend/resume with wakeup support
This patch add suspend/resume with wakeup support for Atmel USBA.
On suspend: We stay continuously clocked if Vbus signal is not
available. If Vbus signal is available we set the Vbus signal as a wake
up source then we stop the USBA itself and all clocks used by USBA.
On resume: We recover clocks and USBA if we stopped them. If a device is
currently connected at resume time we enable the controller.
Signed-off-by: Sylvain Rochet <sylvain.rochet@finsecur.com>
Acked-by: Boris Brezillon <boris.brezillon@free-electrons.com>
Signed-off-by: Felipe Balbi <balbi@ti.com>
Diffstat (limited to 'drivers/usb/gadget')
-rw-r--r-- | drivers/usb/gadget/udc/atmel_usba_udc.c | 57 |
1 files changed, 57 insertions, 0 deletions
diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.c b/drivers/usb/gadget/udc/atmel_usba_udc.c index 999e2f2cd2cc..d019b6c9d74d 100644 --- a/drivers/usb/gadget/udc/atmel_usba_udc.c +++ b/drivers/usb/gadget/udc/atmel_usba_udc.c | |||
@@ -2177,6 +2177,7 @@ static int usba_udc_probe(struct platform_device *pdev) | |||
2177 | ret = usb_add_gadget_udc(&pdev->dev, &udc->gadget); | 2177 | ret = usb_add_gadget_udc(&pdev->dev, &udc->gadget); |
2178 | if (ret) | 2178 | if (ret) |
2179 | return ret; | 2179 | return ret; |
2180 | device_init_wakeup(&pdev->dev, 1); | ||
2180 | 2181 | ||
2181 | usba_init_debugfs(udc); | 2182 | usba_init_debugfs(udc); |
2182 | for (i = 1; i < udc->num_ep; i++) | 2183 | for (i = 1; i < udc->num_ep; i++) |
@@ -2192,6 +2193,7 @@ static int __exit usba_udc_remove(struct platform_device *pdev) | |||
2192 | 2193 | ||
2193 | udc = platform_get_drvdata(pdev); | 2194 | udc = platform_get_drvdata(pdev); |
2194 | 2195 | ||
2196 | device_init_wakeup(&pdev->dev, 0); | ||
2195 | usb_del_gadget_udc(&udc->gadget); | 2197 | usb_del_gadget_udc(&udc->gadget); |
2196 | 2198 | ||
2197 | for (i = 1; i < udc->num_ep; i++) | 2199 | for (i = 1; i < udc->num_ep; i++) |
@@ -2201,10 +2203,65 @@ static int __exit usba_udc_remove(struct platform_device *pdev) | |||
2201 | return 0; | 2203 | return 0; |
2202 | } | 2204 | } |
2203 | 2205 | ||
2206 | #ifdef CONFIG_PM | ||
2207 | static int usba_udc_suspend(struct device *dev) | ||
2208 | { | ||
2209 | struct usba_udc *udc = dev_get_drvdata(dev); | ||
2210 | |||
2211 | /* Not started */ | ||
2212 | if (!udc->driver) | ||
2213 | return 0; | ||
2214 | |||
2215 | mutex_lock(&udc->vbus_mutex); | ||
2216 | |||
2217 | if (!device_may_wakeup(dev)) { | ||
2218 | usba_stop(udc); | ||
2219 | goto out; | ||
2220 | } | ||
2221 | |||
2222 | /* | ||
2223 | * Device may wake up. We stay clocked if we failed | ||
2224 | * to request vbus irq, assuming always on. | ||
2225 | */ | ||
2226 | if (gpio_is_valid(udc->vbus_pin)) { | ||
2227 | usba_stop(udc); | ||
2228 | enable_irq_wake(gpio_to_irq(udc->vbus_pin)); | ||
2229 | } | ||
2230 | |||
2231 | out: | ||
2232 | mutex_unlock(&udc->vbus_mutex); | ||
2233 | return 0; | ||
2234 | } | ||
2235 | |||
2236 | static int usba_udc_resume(struct device *dev) | ||
2237 | { | ||
2238 | struct usba_udc *udc = dev_get_drvdata(dev); | ||
2239 | |||
2240 | /* Not started */ | ||
2241 | if (!udc->driver) | ||
2242 | return 0; | ||
2243 | |||
2244 | if (device_may_wakeup(dev) && gpio_is_valid(udc->vbus_pin)) | ||
2245 | disable_irq_wake(gpio_to_irq(udc->vbus_pin)); | ||
2246 | |||
2247 | /* If Vbus is present, enable the controller and wait for reset */ | ||
2248 | mutex_lock(&udc->vbus_mutex); | ||
2249 | udc->vbus_prev = vbus_is_present(udc); | ||
2250 | if (udc->vbus_prev) | ||
2251 | usba_start(udc); | ||
2252 | mutex_unlock(&udc->vbus_mutex); | ||
2253 | |||
2254 | return 0; | ||
2255 | } | ||
2256 | #endif | ||
2257 | |||
2258 | static SIMPLE_DEV_PM_OPS(usba_udc_pm_ops, usba_udc_suspend, usba_udc_resume); | ||
2259 | |||
2204 | static struct platform_driver udc_driver = { | 2260 | static struct platform_driver udc_driver = { |
2205 | .remove = __exit_p(usba_udc_remove), | 2261 | .remove = __exit_p(usba_udc_remove), |
2206 | .driver = { | 2262 | .driver = { |
2207 | .name = "atmel_usba_udc", | 2263 | .name = "atmel_usba_udc", |
2264 | .pm = &usba_udc_pm_ops, | ||
2208 | .of_match_table = of_match_ptr(atmel_udc_dt_ids), | 2265 | .of_match_table = of_match_ptr(atmel_udc_dt_ids), |
2209 | }, | 2266 | }, |
2210 | }; | 2267 | }; |