diff options
Diffstat (limited to 'drivers/usb/host/ehci-s5p.c')
-rw-r--r-- | drivers/usb/host/ehci-s5p.c | 95 |
1 files changed, 95 insertions, 0 deletions
diff --git a/drivers/usb/host/ehci-s5p.c b/drivers/usb/host/ehci-s5p.c index e3374c8f7b3f..b3958b3d3163 100644 --- a/drivers/usb/host/ehci-s5p.c +++ b/drivers/usb/host/ehci-s5p.c | |||
@@ -189,6 +189,100 @@ static void s5p_ehci_shutdown(struct platform_device *pdev) | |||
189 | hcd->driver->shutdown(hcd); | 189 | hcd->driver->shutdown(hcd); |
190 | } | 190 | } |
191 | 191 | ||
192 | #ifdef CONFIG_PM | ||
193 | static int s5p_ehci_suspend(struct device *dev) | ||
194 | { | ||
195 | struct s5p_ehci_hcd *s5p_ehci = dev_get_drvdata(dev); | ||
196 | struct usb_hcd *hcd = s5p_ehci->hcd; | ||
197 | struct ehci_hcd *ehci = hcd_to_ehci(hcd); | ||
198 | struct platform_device *pdev = to_platform_device(dev); | ||
199 | struct s5p_ehci_platdata *pdata = pdev->dev.platform_data; | ||
200 | unsigned long flags; | ||
201 | int rc = 0; | ||
202 | |||
203 | if (time_before(jiffies, ehci->next_statechange)) | ||
204 | msleep(20); | ||
205 | |||
206 | /* | ||
207 | * Root hub was already suspended. Disable irq emission and | ||
208 | * mark HW unaccessible. The PM and USB cores make sure that | ||
209 | * the root hub is either suspended or stopped. | ||
210 | */ | ||
211 | ehci_prepare_ports_for_controller_suspend(ehci, device_may_wakeup(dev)); | ||
212 | spin_lock_irqsave(&ehci->lock, flags); | ||
213 | ehci_writel(ehci, 0, &ehci->regs->intr_enable); | ||
214 | (void)ehci_readl(ehci, &ehci->regs->intr_enable); | ||
215 | |||
216 | clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); | ||
217 | spin_unlock_irqrestore(&ehci->lock, flags); | ||
218 | |||
219 | if (pdata && pdata->phy_exit) | ||
220 | pdata->phy_exit(pdev, S5P_USB_PHY_HOST); | ||
221 | |||
222 | return rc; | ||
223 | } | ||
224 | |||
225 | static int s5p_ehci_resume(struct device *dev) | ||
226 | { | ||
227 | struct s5p_ehci_hcd *s5p_ehci = dev_get_drvdata(dev); | ||
228 | struct usb_hcd *hcd = s5p_ehci->hcd; | ||
229 | struct ehci_hcd *ehci = hcd_to_ehci(hcd); | ||
230 | struct platform_device *pdev = to_platform_device(dev); | ||
231 | struct s5p_ehci_platdata *pdata = pdev->dev.platform_data; | ||
232 | |||
233 | if (pdata && pdata->phy_init) | ||
234 | pdata->phy_init(pdev, S5P_USB_PHY_HOST); | ||
235 | |||
236 | if (time_before(jiffies, ehci->next_statechange)) | ||
237 | msleep(100); | ||
238 | |||
239 | /* Mark hardware accessible again as we are out of D3 state by now */ | ||
240 | set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); | ||
241 | |||
242 | if (ehci_readl(ehci, &ehci->regs->configured_flag) == FLAG_CF) { | ||
243 | int mask = INTR_MASK; | ||
244 | |||
245 | ehci_prepare_ports_for_controller_resume(ehci); | ||
246 | if (!hcd->self.root_hub->do_remote_wakeup) | ||
247 | mask &= ~STS_PCD; | ||
248 | ehci_writel(ehci, mask, &ehci->regs->intr_enable); | ||
249 | ehci_readl(ehci, &ehci->regs->intr_enable); | ||
250 | return 0; | ||
251 | } | ||
252 | |||
253 | usb_root_hub_lost_power(hcd->self.root_hub); | ||
254 | |||
255 | (void) ehci_halt(ehci); | ||
256 | (void) ehci_reset(ehci); | ||
257 | |||
258 | /* emptying the schedule aborts any urbs */ | ||
259 | spin_lock_irq(&ehci->lock); | ||
260 | if (ehci->reclaim) | ||
261 | end_unlink_async(ehci); | ||
262 | ehci_work(ehci); | ||
263 | spin_unlock_irq(&ehci->lock); | ||
264 | |||
265 | ehci_writel(ehci, ehci->command, &ehci->regs->command); | ||
266 | ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag); | ||
267 | ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */ | ||
268 | |||
269 | /* here we "know" root ports should always stay powered */ | ||
270 | ehci_port_power(ehci, 1); | ||
271 | |||
272 | hcd->state = HC_STATE_SUSPENDED; | ||
273 | |||
274 | return 0; | ||
275 | } | ||
276 | #else | ||
277 | #define s5p_ehci_suspend NULL | ||
278 | #define s5p_ehci_resume NULL | ||
279 | #endif | ||
280 | |||
281 | static const struct dev_pm_ops s5p_ehci_pm_ops = { | ||
282 | .suspend = s5p_ehci_suspend, | ||
283 | .resume = s5p_ehci_resume, | ||
284 | }; | ||
285 | |||
192 | static struct platform_driver s5p_ehci_driver = { | 286 | static struct platform_driver s5p_ehci_driver = { |
193 | .probe = s5p_ehci_probe, | 287 | .probe = s5p_ehci_probe, |
194 | .remove = __devexit_p(s5p_ehci_remove), | 288 | .remove = __devexit_p(s5p_ehci_remove), |
@@ -196,6 +290,7 @@ static struct platform_driver s5p_ehci_driver = { | |||
196 | .driver = { | 290 | .driver = { |
197 | .name = "s5p-ehci", | 291 | .name = "s5p-ehci", |
198 | .owner = THIS_MODULE, | 292 | .owner = THIS_MODULE, |
293 | .pm = &s5p_ehci_pm_ops, | ||
199 | } | 294 | } |
200 | }; | 295 | }; |
201 | 296 | ||