diff options
Diffstat (limited to 'drivers/usb/host/ohci-hub.c')
-rw-r--r-- | drivers/usb/host/ohci-hub.c | 60 |
1 files changed, 32 insertions, 28 deletions
diff --git a/drivers/usb/host/ohci-hub.c b/drivers/usb/host/ohci-hub.c index 7ea9a7b31155..a150e85c901a 100644 --- a/drivers/usb/host/ohci-hub.c +++ b/drivers/usb/host/ohci-hub.c | |||
@@ -362,18 +362,23 @@ static int ohci_root_hub_state_changes(struct ohci_hcd *ohci, int changed, | |||
362 | int any_connected) | 362 | int any_connected) |
363 | { | 363 | { |
364 | int poll_rh = 1; | 364 | int poll_rh = 1; |
365 | int rhsc; | 365 | int rhsc_status, rhsc_enable; |
366 | 366 | ||
367 | rhsc = ohci_readl(ohci, &ohci->regs->intrenable) & OHCI_INTR_RHSC; | 367 | /* Some broken controllers never turn off RHCS in the interrupt |
368 | switch (ohci->hc_control & OHCI_CTRL_HCFS) { | 368 | * status register. For their sake we won't re-enable RHSC |
369 | * interrupts if the interrupt bit is already active. | ||
370 | */ | ||
371 | rhsc_status = ohci_readl(ohci, &ohci->regs->intrstatus) & | ||
372 | OHCI_INTR_RHSC; | ||
373 | rhsc_enable = ohci_readl(ohci, &ohci->regs->intrenable) & | ||
374 | OHCI_INTR_RHSC; | ||
369 | 375 | ||
376 | switch (ohci->hc_control & OHCI_CTRL_HCFS) { | ||
370 | case OHCI_USB_OPER: | 377 | case OHCI_USB_OPER: |
371 | /* If no status changes are pending, enable status-change | 378 | /* If no status changes are pending, enable RHSC interrupts. */ |
372 | * interrupts. | 379 | if (!rhsc_enable && !rhsc_status && !changed) { |
373 | */ | 380 | rhsc_enable = OHCI_INTR_RHSC; |
374 | if (!rhsc && !changed) { | 381 | ohci_writel(ohci, rhsc_enable, &ohci->regs->intrenable); |
375 | rhsc = OHCI_INTR_RHSC; | ||
376 | ohci_writel(ohci, rhsc, &ohci->regs->intrenable); | ||
377 | } | 382 | } |
378 | 383 | ||
379 | /* Keep on polling until we know a device is connected | 384 | /* Keep on polling until we know a device is connected |
@@ -383,7 +388,7 @@ static int ohci_root_hub_state_changes(struct ohci_hcd *ohci, int changed, | |||
383 | if (any_connected || | 388 | if (any_connected || |
384 | !device_may_wakeup(&ohci_to_hcd(ohci) | 389 | !device_may_wakeup(&ohci_to_hcd(ohci) |
385 | ->self.root_hub->dev)) { | 390 | ->self.root_hub->dev)) { |
386 | if (rhsc) | 391 | if (rhsc_enable) |
387 | poll_rh = 0; | 392 | poll_rh = 0; |
388 | } else { | 393 | } else { |
389 | ohci->autostop = 1; | 394 | ohci->autostop = 1; |
@@ -396,34 +401,36 @@ static int ohci_root_hub_state_changes(struct ohci_hcd *ohci, int changed, | |||
396 | ohci->autostop = 0; | 401 | ohci->autostop = 0; |
397 | ohci->next_statechange = jiffies + | 402 | ohci->next_statechange = jiffies + |
398 | STATECHANGE_DELAY; | 403 | STATECHANGE_DELAY; |
399 | } else if (rhsc && time_after_eq(jiffies, | 404 | } else if (time_after_eq(jiffies, |
400 | ohci->next_statechange) | 405 | ohci->next_statechange) |
401 | && !ohci->ed_rm_list | 406 | && !ohci->ed_rm_list |
402 | && !(ohci->hc_control & | 407 | && !(ohci->hc_control & |
403 | OHCI_SCHED_ENABLES)) { | 408 | OHCI_SCHED_ENABLES)) { |
404 | ohci_rh_suspend(ohci, 1); | 409 | ohci_rh_suspend(ohci, 1); |
405 | poll_rh = 0; | 410 | if (rhsc_enable) |
411 | poll_rh = 0; | ||
406 | } | 412 | } |
407 | } | 413 | } |
408 | break; | 414 | break; |
409 | 415 | ||
410 | /* if there is a port change, autostart or ask to be resumed */ | ||
411 | case OHCI_USB_SUSPEND: | 416 | case OHCI_USB_SUSPEND: |
412 | case OHCI_USB_RESUME: | 417 | case OHCI_USB_RESUME: |
418 | /* if there is a port change, autostart or ask to be resumed */ | ||
413 | if (changed) { | 419 | if (changed) { |
414 | if (ohci->autostop) | 420 | if (ohci->autostop) |
415 | ohci_rh_resume(ohci); | 421 | ohci_rh_resume(ohci); |
416 | else | 422 | else |
417 | usb_hcd_resume_root_hub(ohci_to_hcd(ohci)); | 423 | usb_hcd_resume_root_hub(ohci_to_hcd(ohci)); |
418 | } else { | 424 | } else { |
419 | if (!rhsc && (ohci->autostop || | 425 | if (!rhsc_enable && !rhsc_status && (ohci->autostop || |
420 | ohci_to_hcd(ohci)->self.root_hub-> | 426 | ohci_to_hcd(ohci)->self.root_hub-> |
421 | do_remote_wakeup)) | 427 | do_remote_wakeup)) { |
422 | ohci_writel(ohci, OHCI_INTR_RHSC, | 428 | rhsc_enable = OHCI_INTR_RHSC; |
429 | ohci_writel(ohci, rhsc_enable, | ||
423 | &ohci->regs->intrenable); | 430 | &ohci->regs->intrenable); |
424 | 431 | } | |
425 | /* everything is idle, no need for polling */ | 432 | if (rhsc_enable) |
426 | poll_rh = 0; | 433 | poll_rh = 0; |
427 | } | 434 | } |
428 | break; | 435 | break; |
429 | } | 436 | } |
@@ -443,12 +450,16 @@ static inline int ohci_rh_resume(struct ohci_hcd *ohci) | |||
443 | static int ohci_root_hub_state_changes(struct ohci_hcd *ohci, int changed, | 450 | static int ohci_root_hub_state_changes(struct ohci_hcd *ohci, int changed, |
444 | int any_connected) | 451 | int any_connected) |
445 | { | 452 | { |
453 | int rhsc_status; | ||
454 | |||
446 | /* If RHSC is enabled, don't poll */ | 455 | /* If RHSC is enabled, don't poll */ |
447 | if (ohci_readl(ohci, &ohci->regs->intrenable) & OHCI_INTR_RHSC) | 456 | if (ohci_readl(ohci, &ohci->regs->intrenable) & OHCI_INTR_RHSC) |
448 | return 0; | 457 | return 0; |
449 | 458 | ||
450 | /* If no status changes are pending, enable status-change interrupts */ | 459 | /* If no status changes are pending, enable RHSC interrupts */ |
451 | if (!changed) { | 460 | rhsc_status = ohci_readl(ohci, &ohci->regs->intrstatus) & |
461 | OHCI_INTR_RHSC; | ||
462 | if (!changed && !rhsc_status) { | ||
452 | ohci_writel(ohci, OHCI_INTR_RHSC, &ohci->regs->intrenable); | 463 | ohci_writel(ohci, OHCI_INTR_RHSC, &ohci->regs->intrenable); |
453 | return 0; | 464 | return 0; |
454 | } | 465 | } |
@@ -492,13 +503,6 @@ ohci_hub_status_data (struct usb_hcd *hcd, char *buf) | |||
492 | length++; | 503 | length++; |
493 | } | 504 | } |
494 | 505 | ||
495 | /* Some broken controllers never turn off RHCS in the interrupt | ||
496 | * status register. For their sake we won't re-enable RHSC | ||
497 | * interrupts if the flag is already set. | ||
498 | */ | ||
499 | if (ohci_readl(ohci, &ohci->regs->intrstatus) & OHCI_INTR_RHSC) | ||
500 | changed = 1; | ||
501 | |||
502 | /* look at each port */ | 506 | /* look at each port */ |
503 | for (i = 0; i < ohci->num_ports; i++) { | 507 | for (i = 0; i < ohci->num_ports; i++) { |
504 | u32 status = roothub_portstatus (ohci, i); | 508 | u32 status = roothub_portstatus (ohci, i); |