aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/host
diff options
context:
space:
mode:
authorJingoo Han <jg1.han@samsung.com>2011-05-20 07:48:33 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2011-06-06 19:32:36 -0400
commit1acb30ef28c4cb08bc6f7bc7f68ee7eebd4b9c84 (patch)
tree1fee509918944509fd79cc6cab8dfb267c38d67f /drivers/usb/host
parentdb383d69c620b0af61504a257da3c928417d47af (diff)
USB: ehci-s5p: add PM support
This patch adds power management support such as suspend and resume functions. Signed-off-by: Jingoo Han <jg1.han@samsung.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/host')
-rw-r--r--drivers/usb/host/ehci-s5p.c95
1 files changed, 95 insertions, 0 deletions
diff --git a/drivers/usb/host/ehci-s5p.c b/drivers/usb/host/ehci-s5p.c
index e3374c8f7b3..b3958b3d316 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
193static 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
225static 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
281static const struct dev_pm_ops s5p_ehci_pm_ops = {
282 .suspend = s5p_ehci_suspend,
283 .resume = s5p_ehci_resume,
284};
285
192static struct platform_driver s5p_ehci_driver = { 286static 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