@@ -189,13 +189,108 @@ static void s5p_ehci_shutdown(struct platform_device *pdev)
189
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
286
static struct platform_driver s5p_ehci_driver = {
193
287
.probe = s5p_ehci_probe ,
194
288
.remove = __devexit_p (s5p_ehci_remove ),
195
289
.shutdown = s5p_ehci_shutdown ,
196
290
.driver = {
197
291
.name = "s5p-ehci" ,
198
292
.owner = THIS_MODULE ,
293
+ .pm = & s5p_ehci_pm_ops ,
199
294
}
200
295
};
201
296
0 commit comments