diff options
Diffstat (limited to 'drivers/usb/host/ohci-hub.c')
-rw-r--r-- | drivers/usb/host/ohci-hub.c | 277 |
1 files changed, 172 insertions, 105 deletions
diff --git a/drivers/usb/host/ohci-hub.c b/drivers/usb/host/ohci-hub.c index 0bb972b58336..ec75774abeac 100644 --- a/drivers/usb/host/ohci-hub.c +++ b/drivers/usb/host/ohci-hub.c | |||
@@ -36,27 +36,25 @@ | |||
36 | 36 | ||
37 | /*-------------------------------------------------------------------------*/ | 37 | /*-------------------------------------------------------------------------*/ |
38 | 38 | ||
39 | #ifdef CONFIG_PM | 39 | /* hcd->hub_irq_enable() */ |
40 | static void ohci_rhsc_enable (struct usb_hcd *hcd) | ||
41 | { | ||
42 | struct ohci_hcd *ohci = hcd_to_ohci (hcd); | ||
43 | |||
44 | ohci_writel (ohci, OHCI_INTR_RHSC, &ohci->regs->intrenable); | ||
45 | } | ||
40 | 46 | ||
41 | #define OHCI_SCHED_ENABLES \ | 47 | #define OHCI_SCHED_ENABLES \ |
42 | (OHCI_CTRL_CLE|OHCI_CTRL_BLE|OHCI_CTRL_PLE|OHCI_CTRL_IE) | 48 | (OHCI_CTRL_CLE|OHCI_CTRL_BLE|OHCI_CTRL_PLE|OHCI_CTRL_IE) |
43 | 49 | ||
44 | static void dl_done_list (struct ohci_hcd *, struct pt_regs *); | 50 | static void dl_done_list (struct ohci_hcd *, struct pt_regs *); |
45 | static void finish_unlinks (struct ohci_hcd *, u16 , struct pt_regs *); | 51 | static void finish_unlinks (struct ohci_hcd *, u16 , struct pt_regs *); |
46 | static int ohci_restart (struct ohci_hcd *ohci); | ||
47 | 52 | ||
48 | static int ohci_bus_suspend (struct usb_hcd *hcd) | 53 | static int ohci_rh_suspend (struct ohci_hcd *ohci, int autostop) |
54 | __releases(ohci->lock) | ||
55 | __acquires(ohci->lock) | ||
49 | { | 56 | { |
50 | struct ohci_hcd *ohci = hcd_to_ohci (hcd); | ||
51 | int status = 0; | 57 | int status = 0; |
52 | unsigned long flags; | ||
53 | |||
54 | spin_lock_irqsave (&ohci->lock, flags); | ||
55 | |||
56 | if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))) { | ||
57 | spin_unlock_irqrestore (&ohci->lock, flags); | ||
58 | return -ESHUTDOWN; | ||
59 | } | ||
60 | 58 | ||
61 | ohci->hc_control = ohci_readl (ohci, &ohci->regs->control); | 59 | ohci->hc_control = ohci_readl (ohci, &ohci->regs->control); |
62 | switch (ohci->hc_control & OHCI_CTRL_HCFS) { | 60 | switch (ohci->hc_control & OHCI_CTRL_HCFS) { |
@@ -72,15 +70,16 @@ static int ohci_bus_suspend (struct usb_hcd *hcd) | |||
72 | ohci_dbg (ohci, "needs reinit!\n"); | 70 | ohci_dbg (ohci, "needs reinit!\n"); |
73 | goto done; | 71 | goto done; |
74 | case OHCI_USB_SUSPEND: | 72 | case OHCI_USB_SUSPEND: |
75 | ohci_dbg (ohci, "already suspended\n"); | 73 | if (!ohci->autostop) { |
76 | goto done; | 74 | ohci_dbg (ohci, "already suspended\n"); |
75 | goto done; | ||
76 | } | ||
77 | } | 77 | } |
78 | ohci_dbg (ohci, "suspend root hub\n"); | 78 | ohci_dbg (ohci, "%s root hub\n", |
79 | autostop ? "auto-stop" : "suspend"); | ||
79 | 80 | ||
80 | /* First stop any processing */ | 81 | /* First stop any processing */ |
81 | if (ohci->hc_control & OHCI_SCHED_ENABLES) { | 82 | if (!autostop && (ohci->hc_control & OHCI_SCHED_ENABLES)) { |
82 | int limit; | ||
83 | |||
84 | ohci->hc_control &= ~OHCI_SCHED_ENABLES; | 83 | ohci->hc_control &= ~OHCI_SCHED_ENABLES; |
85 | ohci_writel (ohci, ohci->hc_control, &ohci->regs->control); | 84 | ohci_writel (ohci, ohci->hc_control, &ohci->regs->control); |
86 | ohci->hc_control = ohci_readl (ohci, &ohci->regs->control); | 85 | ohci->hc_control = ohci_readl (ohci, &ohci->regs->control); |
@@ -90,27 +89,22 @@ static int ohci_bus_suspend (struct usb_hcd *hcd) | |||
90 | * then the last WDH could take 6+ msec | 89 | * then the last WDH could take 6+ msec |
91 | */ | 90 | */ |
92 | ohci_dbg (ohci, "stopping schedules ...\n"); | 91 | ohci_dbg (ohci, "stopping schedules ...\n"); |
93 | limit = 2000; | 92 | ohci->autostop = 0; |
94 | while (limit > 0) { | 93 | spin_unlock_irq (&ohci->lock); |
95 | udelay (250); | 94 | msleep (8); |
96 | limit =- 250; | 95 | spin_lock_irq (&ohci->lock); |
97 | if (ohci_readl (ohci, &ohci->regs->intrstatus) | ||
98 | & OHCI_INTR_SF) | ||
99 | break; | ||
100 | } | ||
101 | dl_done_list (ohci, NULL); | ||
102 | mdelay (7); | ||
103 | } | 96 | } |
104 | dl_done_list (ohci, NULL); | 97 | dl_done_list (ohci, NULL); |
105 | finish_unlinks (ohci, ohci_frame_no(ohci), NULL); | 98 | finish_unlinks (ohci, ohci_frame_no(ohci), NULL); |
106 | ohci_writel (ohci, ohci_readl (ohci, &ohci->regs->intrstatus), | ||
107 | &ohci->regs->intrstatus); | ||
108 | 99 | ||
109 | /* maybe resume can wake root hub */ | 100 | /* maybe resume can wake root hub */ |
110 | if (device_may_wakeup(&ohci_to_hcd(ohci)->self.root_hub->dev)) | 101 | if (device_may_wakeup(&ohci_to_hcd(ohci)->self.root_hub->dev) || |
102 | autostop) | ||
111 | ohci->hc_control |= OHCI_CTRL_RWE; | 103 | ohci->hc_control |= OHCI_CTRL_RWE; |
112 | else | 104 | else { |
105 | ohci_writel (ohci, OHCI_INTR_RHSC, &ohci->regs->intrdisable); | ||
113 | ohci->hc_control &= ~OHCI_CTRL_RWE; | 106 | ohci->hc_control &= ~OHCI_CTRL_RWE; |
107 | } | ||
114 | 108 | ||
115 | /* Suspend hub ... this is the "global (to this bus) suspend" mode, | 109 | /* Suspend hub ... this is the "global (to this bus) suspend" mode, |
116 | * which doesn't imply ports will first be individually suspended. | 110 | * which doesn't imply ports will first be individually suspended. |
@@ -121,13 +115,12 @@ static int ohci_bus_suspend (struct usb_hcd *hcd) | |||
121 | (void) ohci_readl (ohci, &ohci->regs->control); | 115 | (void) ohci_readl (ohci, &ohci->regs->control); |
122 | 116 | ||
123 | /* no resumes until devices finish suspending */ | 117 | /* no resumes until devices finish suspending */ |
124 | ohci->next_statechange = jiffies + msecs_to_jiffies (5); | 118 | if (!autostop) { |
119 | ohci->next_statechange = jiffies + msecs_to_jiffies (5); | ||
120 | ohci->autostop = 0; | ||
121 | } | ||
125 | 122 | ||
126 | done: | 123 | done: |
127 | /* external suspend vs self autosuspend ... same effect */ | ||
128 | if (status == 0) | ||
129 | usb_hcd_suspend_root_hub(hcd); | ||
130 | spin_unlock_irqrestore (&ohci->lock, flags); | ||
131 | return status; | 124 | return status; |
132 | } | 125 | } |
133 | 126 | ||
@@ -139,25 +132,19 @@ static inline struct ed *find_head (struct ed *ed) | |||
139 | return ed; | 132 | return ed; |
140 | } | 133 | } |
141 | 134 | ||
135 | static int ohci_restart (struct ohci_hcd *ohci); | ||
136 | |||
142 | /* caller has locked the root hub */ | 137 | /* caller has locked the root hub */ |
143 | static int ohci_bus_resume (struct usb_hcd *hcd) | 138 | static int ohci_rh_resume (struct ohci_hcd *ohci) |
139 | __releases(ohci->lock) | ||
140 | __acquires(ohci->lock) | ||
144 | { | 141 | { |
145 | struct ohci_hcd *ohci = hcd_to_ohci (hcd); | 142 | struct usb_hcd *hcd = ohci_to_hcd (ohci); |
146 | u32 temp, enables; | 143 | u32 temp, enables; |
147 | int status = -EINPROGRESS; | 144 | int status = -EINPROGRESS; |
148 | unsigned long flags; | 145 | int autostopped = ohci->autostop; |
149 | |||
150 | if (time_before (jiffies, ohci->next_statechange)) | ||
151 | msleep(5); | ||
152 | |||
153 | spin_lock_irqsave (&ohci->lock, flags); | ||
154 | |||
155 | if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))) { | ||
156 | spin_unlock_irqrestore (&ohci->lock, flags); | ||
157 | return -ESHUTDOWN; | ||
158 | } | ||
159 | |||
160 | 146 | ||
147 | ohci->autostop = 0; | ||
161 | ohci->hc_control = ohci_readl (ohci, &ohci->regs->control); | 148 | ohci->hc_control = ohci_readl (ohci, &ohci->regs->control); |
162 | 149 | ||
163 | if (ohci->hc_control & (OHCI_CTRL_IR | OHCI_SCHED_ENABLES)) { | 150 | if (ohci->hc_control & (OHCI_CTRL_IR | OHCI_SCHED_ENABLES)) { |
@@ -177,7 +164,8 @@ static int ohci_bus_resume (struct usb_hcd *hcd) | |||
177 | ohci->hc_control |= OHCI_USB_RESUME; | 164 | ohci->hc_control |= OHCI_USB_RESUME; |
178 | ohci_writel (ohci, ohci->hc_control, &ohci->regs->control); | 165 | ohci_writel (ohci, ohci->hc_control, &ohci->regs->control); |
179 | (void) ohci_readl (ohci, &ohci->regs->control); | 166 | (void) ohci_readl (ohci, &ohci->regs->control); |
180 | ohci_dbg (ohci, "resume root hub\n"); | 167 | ohci_dbg (ohci, "%s root hub\n", |
168 | autostopped ? "auto-start" : "resume"); | ||
181 | break; | 169 | break; |
182 | case OHCI_USB_RESUME: | 170 | case OHCI_USB_RESUME: |
183 | /* HCFS changes sometime after INTR_RD */ | 171 | /* HCFS changes sometime after INTR_RD */ |
@@ -192,16 +180,24 @@ static int ohci_bus_resume (struct usb_hcd *hcd) | |||
192 | ohci_dbg (ohci, "lost power\n"); | 180 | ohci_dbg (ohci, "lost power\n"); |
193 | status = -EBUSY; | 181 | status = -EBUSY; |
194 | } | 182 | } |
195 | spin_unlock_irqrestore (&ohci->lock, flags); | 183 | #ifdef CONFIG_PM |
196 | if (status == -EBUSY) { | 184 | if (status == -EBUSY) { |
197 | (void) ohci_init (ohci); | 185 | if (!autostopped) { |
198 | return ohci_restart (ohci); | 186 | spin_unlock_irq (&ohci->lock); |
187 | (void) ohci_init (ohci); | ||
188 | status = ohci_restart (ohci); | ||
189 | spin_lock_irq (&ohci->lock); | ||
190 | } | ||
191 | return status; | ||
199 | } | 192 | } |
193 | #endif | ||
200 | if (status != -EINPROGRESS) | 194 | if (status != -EINPROGRESS) |
201 | return status; | 195 | return status; |
196 | if (autostopped) | ||
197 | goto skip_resume; | ||
198 | spin_unlock_irq (&ohci->lock); | ||
202 | 199 | ||
203 | temp = ohci->num_ports; | 200 | temp = ohci->num_ports; |
204 | enables = 0; | ||
205 | while (temp--) { | 201 | while (temp--) { |
206 | u32 stat = ohci_readl (ohci, | 202 | u32 stat = ohci_readl (ohci, |
207 | &ohci->regs->roothub.portstatus [temp]); | 203 | &ohci->regs->roothub.portstatus [temp]); |
@@ -234,17 +230,21 @@ static int ohci_bus_resume (struct usb_hcd *hcd) | |||
234 | /* Sometimes PCI D3 suspend trashes frame timings ... */ | 230 | /* Sometimes PCI D3 suspend trashes frame timings ... */ |
235 | periodic_reinit (ohci); | 231 | periodic_reinit (ohci); |
236 | 232 | ||
233 | /* the following code is executed with ohci->lock held and | ||
234 | * irqs disabled if and only if autostopped is true | ||
235 | */ | ||
236 | |||
237 | skip_resume: | ||
237 | /* interrupts might have been disabled */ | 238 | /* interrupts might have been disabled */ |
238 | ohci_writel (ohci, OHCI_INTR_INIT, &ohci->regs->intrenable); | 239 | ohci_writel (ohci, OHCI_INTR_INIT, &ohci->regs->intrenable); |
239 | if (ohci->ed_rm_list) | 240 | if (ohci->ed_rm_list) |
240 | ohci_writel (ohci, OHCI_INTR_SF, &ohci->regs->intrenable); | 241 | ohci_writel (ohci, OHCI_INTR_SF, &ohci->regs->intrenable); |
241 | ohci_writel (ohci, ohci_readl (ohci, &ohci->regs->intrstatus), | ||
242 | &ohci->regs->intrstatus); | ||
243 | 242 | ||
244 | /* Then re-enable operations */ | 243 | /* Then re-enable operations */ |
245 | ohci_writel (ohci, OHCI_USB_OPER, &ohci->regs->control); | 244 | ohci_writel (ohci, OHCI_USB_OPER, &ohci->regs->control); |
246 | (void) ohci_readl (ohci, &ohci->regs->control); | 245 | (void) ohci_readl (ohci, &ohci->regs->control); |
247 | msleep (3); | 246 | if (!autostopped) |
247 | msleep (3); | ||
248 | 248 | ||
249 | temp = ohci->hc_control; | 249 | temp = ohci->hc_control; |
250 | temp &= OHCI_CTRL_RWC; | 250 | temp &= OHCI_CTRL_RWC; |
@@ -254,10 +254,14 @@ static int ohci_bus_resume (struct usb_hcd *hcd) | |||
254 | (void) ohci_readl (ohci, &ohci->regs->control); | 254 | (void) ohci_readl (ohci, &ohci->regs->control); |
255 | 255 | ||
256 | /* TRSMRCY */ | 256 | /* TRSMRCY */ |
257 | msleep (10); | 257 | if (!autostopped) { |
258 | msleep (10); | ||
259 | spin_lock_irq (&ohci->lock); | ||
260 | } | ||
261 | /* now ohci->lock is always held and irqs are always disabled */ | ||
258 | 262 | ||
259 | /* keep it alive for ~5x suspend + resume costs */ | 263 | /* keep it alive for more than ~5x suspend + resume costs */ |
260 | ohci->next_statechange = jiffies + msecs_to_jiffies (250); | 264 | ohci->next_statechange = jiffies + STATECHANGE_DELAY; |
261 | 265 | ||
262 | /* maybe turn schedules back on */ | 266 | /* maybe turn schedules back on */ |
263 | enables = 0; | 267 | enables = 0; |
@@ -291,6 +295,45 @@ static int ohci_bus_resume (struct usb_hcd *hcd) | |||
291 | return 0; | 295 | return 0; |
292 | } | 296 | } |
293 | 297 | ||
298 | #ifdef CONFIG_PM | ||
299 | |||
300 | static int ohci_bus_suspend (struct usb_hcd *hcd) | ||
301 | { | ||
302 | struct ohci_hcd *ohci = hcd_to_ohci (hcd); | ||
303 | int rc; | ||
304 | |||
305 | spin_lock_irq (&ohci->lock); | ||
306 | |||
307 | if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))) | ||
308 | rc = -ESHUTDOWN; | ||
309 | else | ||
310 | rc = ohci_rh_suspend (ohci, 0); | ||
311 | spin_unlock_irq (&ohci->lock); | ||
312 | return rc; | ||
313 | } | ||
314 | |||
315 | static int ohci_bus_resume (struct usb_hcd *hcd) | ||
316 | { | ||
317 | struct ohci_hcd *ohci = hcd_to_ohci (hcd); | ||
318 | int rc; | ||
319 | |||
320 | if (time_before (jiffies, ohci->next_statechange)) | ||
321 | msleep(5); | ||
322 | |||
323 | spin_lock_irq (&ohci->lock); | ||
324 | |||
325 | if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))) | ||
326 | rc = -ESHUTDOWN; | ||
327 | else | ||
328 | rc = ohci_rh_resume (ohci); | ||
329 | spin_unlock_irq (&ohci->lock); | ||
330 | |||
331 | /* poll until we know a device is connected or we autostop */ | ||
332 | if (rc == 0) | ||
333 | usb_hcd_poll_rh_status(hcd); | ||
334 | return rc; | ||
335 | } | ||
336 | |||
294 | #endif /* CONFIG_PM */ | 337 | #endif /* CONFIG_PM */ |
295 | 338 | ||
296 | /*-------------------------------------------------------------------------*/ | 339 | /*-------------------------------------------------------------------------*/ |
@@ -302,20 +345,11 @@ ohci_hub_status_data (struct usb_hcd *hcd, char *buf) | |||
302 | { | 345 | { |
303 | struct ohci_hcd *ohci = hcd_to_ohci (hcd); | 346 | struct ohci_hcd *ohci = hcd_to_ohci (hcd); |
304 | int i, changed = 0, length = 1; | 347 | int i, changed = 0, length = 1; |
305 | int can_suspend = device_may_wakeup(&hcd->self.root_hub->dev); | 348 | int any_connected = 0, rhsc_enabled = 1; |
306 | unsigned long flags; | 349 | unsigned long flags; |
307 | 350 | ||
308 | spin_lock_irqsave (&ohci->lock, flags); | 351 | spin_lock_irqsave (&ohci->lock, flags); |
309 | 352 | ||
310 | /* handle autosuspended root: finish resuming before | ||
311 | * letting khubd or root hub timer see state changes. | ||
312 | */ | ||
313 | if (unlikely((ohci->hc_control & OHCI_CTRL_HCFS) != OHCI_USB_OPER | ||
314 | || !HC_IS_RUNNING(hcd->state))) { | ||
315 | can_suspend = 0; | ||
316 | goto done; | ||
317 | } | ||
318 | |||
319 | /* undocumented erratum seen on at least rev D */ | 353 | /* undocumented erratum seen on at least rev D */ |
320 | if ((ohci->flags & OHCI_QUIRK_AMD756) | 354 | if ((ohci->flags & OHCI_QUIRK_AMD756) |
321 | && (roothub_a (ohci) & RH_A_NDP) > MAX_ROOT_PORTS) { | 355 | && (roothub_a (ohci) & RH_A_NDP) > MAX_ROOT_PORTS) { |
@@ -339,6 +373,9 @@ ohci_hub_status_data (struct usb_hcd *hcd, char *buf) | |||
339 | for (i = 0; i < ohci->num_ports; i++) { | 373 | for (i = 0; i < ohci->num_ports; i++) { |
340 | u32 status = roothub_portstatus (ohci, i); | 374 | u32 status = roothub_portstatus (ohci, i); |
341 | 375 | ||
376 | /* can't autostop if ports are connected */ | ||
377 | any_connected |= (status & RH_PS_CCS); | ||
378 | |||
342 | if (status & (RH_PS_CSC | RH_PS_PESC | RH_PS_PSSC | 379 | if (status & (RH_PS_CSC | RH_PS_PESC | RH_PS_PSSC |
343 | | RH_PS_OCIC | RH_PS_PRSC)) { | 380 | | RH_PS_OCIC | RH_PS_PRSC)) { |
344 | changed = 1; | 381 | changed = 1; |
@@ -346,39 +383,72 @@ ohci_hub_status_data (struct usb_hcd *hcd, char *buf) | |||
346 | buf [0] |= 1 << (i + 1); | 383 | buf [0] |= 1 << (i + 1); |
347 | else | 384 | else |
348 | buf [1] |= 1 << (i - 7); | 385 | buf [1] |= 1 << (i - 7); |
349 | continue; | ||
350 | } | 386 | } |
351 | |||
352 | /* can suspend if no ports are enabled; or if all all | ||
353 | * enabled ports are suspended AND remote wakeup is on. | ||
354 | */ | ||
355 | if (!(status & RH_PS_CCS)) | ||
356 | continue; | ||
357 | if ((status & RH_PS_PSS) && can_suspend) | ||
358 | continue; | ||
359 | can_suspend = 0; | ||
360 | } | 387 | } |
361 | done: | ||
362 | spin_unlock_irqrestore (&ohci->lock, flags); | ||
363 | 388 | ||
364 | #ifdef CONFIG_PM | 389 | /* NOTE: vendors didn't always make the same implementation |
365 | /* save power by suspending idle root hubs; | 390 | * choices for RHSC. Sometimes it triggers on an edge (like |
366 | * INTR_RD wakes us when there's work | 391 | * setting and maybe clearing a port status change bit); and |
392 | * it's level-triggered on other silicon, active until khubd | ||
393 | * clears all active port status change bits. If it's still | ||
394 | * set (level-triggered) we must disable it and rely on | ||
395 | * polling until khubd re-enables it. | ||
367 | */ | 396 | */ |
368 | if (can_suspend | 397 | if (ohci_readl (ohci, &ohci->regs->intrstatus) & OHCI_INTR_RHSC) { |
369 | && !changed | 398 | ohci_writel (ohci, OHCI_INTR_RHSC, &ohci->regs->intrdisable); |
370 | && !ohci->ed_rm_list | 399 | (void) ohci_readl (ohci, &ohci->regs->intrdisable); |
371 | && ((OHCI_CTRL_HCFS | OHCI_SCHED_ENABLES) | 400 | rhsc_enabled = 0; |
372 | & ohci->hc_control) | ||
373 | == OHCI_USB_OPER | ||
374 | && time_after (jiffies, ohci->next_statechange) | ||
375 | && usb_trylock_device (hcd->self.root_hub) == 0 | ||
376 | ) { | ||
377 | ohci_vdbg (ohci, "autosuspend\n"); | ||
378 | (void) ohci_bus_suspend (hcd); | ||
379 | usb_unlock_device (hcd->self.root_hub); | ||
380 | } | 401 | } |
381 | #endif | 402 | hcd->poll_rh = 1; |
403 | |||
404 | /* carry out appropriate state changes */ | ||
405 | switch (ohci->hc_control & OHCI_CTRL_HCFS) { | ||
406 | |||
407 | case OHCI_USB_OPER: | ||
408 | /* keep on polling until we know a device is connected | ||
409 | * and RHSC is enabled */ | ||
410 | if (!ohci->autostop) { | ||
411 | if (any_connected) { | ||
412 | if (rhsc_enabled) | ||
413 | hcd->poll_rh = 0; | ||
414 | } else { | ||
415 | ohci->autostop = 1; | ||
416 | ohci->next_statechange = jiffies + HZ; | ||
417 | } | ||
418 | |||
419 | /* if no devices have been attached for one second, autostop */ | ||
420 | } else { | ||
421 | if (changed || any_connected) { | ||
422 | ohci->autostop = 0; | ||
423 | ohci->next_statechange = jiffies + | ||
424 | STATECHANGE_DELAY; | ||
425 | } else if (time_after_eq (jiffies, | ||
426 | ohci->next_statechange) | ||
427 | && !ohci->ed_rm_list | ||
428 | && !(ohci->hc_control & | ||
429 | OHCI_SCHED_ENABLES)) { | ||
430 | ohci_rh_suspend (ohci, 1); | ||
431 | } | ||
432 | } | ||
433 | break; | ||
434 | |||
435 | /* if there is a port change, autostart or ask to be resumed */ | ||
436 | case OHCI_USB_SUSPEND: | ||
437 | case OHCI_USB_RESUME: | ||
438 | if (changed) { | ||
439 | if (ohci->autostop) | ||
440 | ohci_rh_resume (ohci); | ||
441 | else | ||
442 | usb_hcd_resume_root_hub (hcd); | ||
443 | } else { | ||
444 | /* everything is idle, no need for polling */ | ||
445 | hcd->poll_rh = 0; | ||
446 | } | ||
447 | break; | ||
448 | } | ||
449 | |||
450 | done: | ||
451 | spin_unlock_irqrestore (&ohci->lock, flags); | ||
382 | 452 | ||
383 | return changed ? length : 0; | 453 | return changed ? length : 0; |
384 | } | 454 | } |
@@ -550,9 +620,6 @@ static int ohci_hub_control ( | |||
550 | break; | 620 | break; |
551 | case USB_PORT_FEAT_SUSPEND: | 621 | case USB_PORT_FEAT_SUSPEND: |
552 | temp = RH_PS_POCI; | 622 | temp = RH_PS_POCI; |
553 | if ((ohci->hc_control & OHCI_CTRL_HCFS) | ||
554 | != OHCI_USB_OPER) | ||
555 | usb_hcd_resume_root_hub(hcd); | ||
556 | break; | 623 | break; |
557 | case USB_PORT_FEAT_C_SUSPEND: | 624 | case USB_PORT_FEAT_C_SUSPEND: |
558 | temp = RH_PS_PSSC; | 625 | temp = RH_PS_PSSC; |
@@ -581,14 +648,14 @@ static int ohci_hub_control ( | |||
581 | break; | 648 | break; |
582 | case GetHubStatus: | 649 | case GetHubStatus: |
583 | temp = roothub_status (ohci) & ~(RH_HS_CRWE | RH_HS_DRWE); | 650 | temp = roothub_status (ohci) & ~(RH_HS_CRWE | RH_HS_DRWE); |
584 | *(__le32 *) buf = cpu_to_le32 (temp); | 651 | put_unaligned(cpu_to_le32 (temp), (__le32 *) buf); |
585 | break; | 652 | break; |
586 | case GetPortStatus: | 653 | case GetPortStatus: |
587 | if (!wIndex || wIndex > ports) | 654 | if (!wIndex || wIndex > ports) |
588 | goto error; | 655 | goto error; |
589 | wIndex--; | 656 | wIndex--; |
590 | temp = roothub_portstatus (ohci, wIndex); | 657 | temp = roothub_portstatus (ohci, wIndex); |
591 | *(__le32 *) buf = cpu_to_le32 (temp); | 658 | put_unaligned(cpu_to_le32 (temp), (__le32 *) buf); |
592 | 659 | ||
593 | #ifndef OHCI_VERBOSE_DEBUG | 660 | #ifndef OHCI_VERBOSE_DEBUG |
594 | if (*(u16*)(buf+2)) /* only if wPortChange is interesting */ | 661 | if (*(u16*)(buf+2)) /* only if wPortChange is interesting */ |