diff options
-rw-r--r-- | drivers/usb/host/ohci-hub.c | 51 |
1 files changed, 32 insertions, 19 deletions
diff --git a/drivers/usb/host/ohci-hub.c b/drivers/usb/host/ohci-hub.c index a150e85c901a..32bbce9718f0 100644 --- a/drivers/usb/host/ohci-hub.c +++ b/drivers/usb/host/ohci-hub.c | |||
@@ -359,17 +359,15 @@ static void ohci_finish_controller_resume(struct usb_hcd *hcd) | |||
359 | 359 | ||
360 | /* Carry out polling-, autostop-, and autoresume-related state changes */ | 360 | /* Carry out polling-, autostop-, and autoresume-related state changes */ |
361 | static int ohci_root_hub_state_changes(struct ohci_hcd *ohci, int changed, | 361 | static int ohci_root_hub_state_changes(struct ohci_hcd *ohci, int changed, |
362 | int any_connected) | 362 | int any_connected, int rhsc_status) |
363 | { | 363 | { |
364 | int poll_rh = 1; | 364 | int poll_rh = 1; |
365 | int rhsc_status, rhsc_enable; | 365 | int rhsc_enable; |
366 | 366 | ||
367 | /* Some broken controllers never turn off RHCS in the interrupt | 367 | /* Some broken controllers never turn off RHCS in the interrupt |
368 | * status register. For their sake we won't re-enable RHSC | 368 | * status register. For their sake we won't re-enable RHSC |
369 | * interrupts if the interrupt bit is already active. | 369 | * interrupts if the interrupt bit is already active. |
370 | */ | 370 | */ |
371 | rhsc_status = ohci_readl(ohci, &ohci->regs->intrstatus) & | ||
372 | OHCI_INTR_RHSC; | ||
373 | rhsc_enable = ohci_readl(ohci, &ohci->regs->intrenable) & | 371 | rhsc_enable = ohci_readl(ohci, &ohci->regs->intrenable) & |
374 | OHCI_INTR_RHSC; | 372 | OHCI_INTR_RHSC; |
375 | 373 | ||
@@ -421,14 +419,23 @@ static int ohci_root_hub_state_changes(struct ohci_hcd *ohci, int changed, | |||
421 | ohci_rh_resume(ohci); | 419 | ohci_rh_resume(ohci); |
422 | else | 420 | else |
423 | usb_hcd_resume_root_hub(ohci_to_hcd(ohci)); | 421 | usb_hcd_resume_root_hub(ohci_to_hcd(ohci)); |
422 | |||
423 | /* If remote wakeup is disabled, stop polling */ | ||
424 | } else if (!ohci->autostop && | ||
425 | !ohci_to_hcd(ohci)->self.root_hub-> | ||
426 | do_remote_wakeup) { | ||
427 | poll_rh = 0; | ||
428 | |||
424 | } else { | 429 | } else { |
425 | if (!rhsc_enable && !rhsc_status && (ohci->autostop || | 430 | /* If no status changes are pending, |
426 | ohci_to_hcd(ohci)->self.root_hub-> | 431 | * enable RHSC interrupts |
427 | do_remote_wakeup)) { | 432 | */ |
433 | if (!rhsc_enable && !rhsc_status) { | ||
428 | rhsc_enable = OHCI_INTR_RHSC; | 434 | rhsc_enable = OHCI_INTR_RHSC; |
429 | ohci_writel(ohci, rhsc_enable, | 435 | ohci_writel(ohci, rhsc_enable, |
430 | &ohci->regs->intrenable); | 436 | &ohci->regs->intrenable); |
431 | } | 437 | } |
438 | /* Keep polling until RHSC is enabled */ | ||
432 | if (rhsc_enable) | 439 | if (rhsc_enable) |
433 | poll_rh = 0; | 440 | poll_rh = 0; |
434 | } | 441 | } |
@@ -448,22 +455,22 @@ static inline int ohci_rh_resume(struct ohci_hcd *ohci) | |||
448 | * autostop isn't used when CONFIG_PM is turned off. | 455 | * autostop isn't used when CONFIG_PM is turned off. |
449 | */ | 456 | */ |
450 | static int ohci_root_hub_state_changes(struct ohci_hcd *ohci, int changed, | 457 | static int ohci_root_hub_state_changes(struct ohci_hcd *ohci, int changed, |
451 | int any_connected) | 458 | int any_connected, int rhsc_status) |
452 | { | 459 | { |
453 | int rhsc_status; | ||
454 | |||
455 | /* If RHSC is enabled, don't poll */ | 460 | /* If RHSC is enabled, don't poll */ |
456 | if (ohci_readl(ohci, &ohci->regs->intrenable) & OHCI_INTR_RHSC) | 461 | if (ohci_readl(ohci, &ohci->regs->intrenable) & OHCI_INTR_RHSC) |
457 | return 0; | 462 | return 0; |
458 | 463 | ||
459 | /* If no status changes are pending, enable RHSC interrupts */ | 464 | /* If status changes are pending, continue polling. |
460 | rhsc_status = ohci_readl(ohci, &ohci->regs->intrstatus) & | 465 | * Conversely, if no status changes are pending but the RHSC |
461 | OHCI_INTR_RHSC; | 466 | * status bit was set, then RHSC may be broken so continue polling. |
462 | if (!changed && !rhsc_status) { | 467 | */ |
463 | ohci_writel(ohci, OHCI_INTR_RHSC, &ohci->regs->intrenable); | 468 | if (changed || rhsc_status) |
464 | return 0; | 469 | return 1; |
465 | } | 470 | |
466 | return 1; | 471 | /* It's safe to re-enable RHSC interrupts */ |
472 | ohci_writel(ohci, OHCI_INTR_RHSC, &ohci->regs->intrenable); | ||
473 | return 0; | ||
467 | } | 474 | } |
468 | 475 | ||
469 | #endif /* CONFIG_PM */ | 476 | #endif /* CONFIG_PM */ |
@@ -478,6 +485,7 @@ ohci_hub_status_data (struct usb_hcd *hcd, char *buf) | |||
478 | struct ohci_hcd *ohci = hcd_to_ohci (hcd); | 485 | struct ohci_hcd *ohci = hcd_to_ohci (hcd); |
479 | int i, changed = 0, length = 1; | 486 | int i, changed = 0, length = 1; |
480 | int any_connected = 0; | 487 | int any_connected = 0; |
488 | int rhsc_status; | ||
481 | unsigned long flags; | 489 | unsigned long flags; |
482 | 490 | ||
483 | spin_lock_irqsave (&ohci->lock, flags); | 491 | spin_lock_irqsave (&ohci->lock, flags); |
@@ -503,6 +511,11 @@ ohci_hub_status_data (struct usb_hcd *hcd, char *buf) | |||
503 | length++; | 511 | length++; |
504 | } | 512 | } |
505 | 513 | ||
514 | /* Clear the RHSC status flag before reading the port statuses */ | ||
515 | ohci_writel(ohci, OHCI_INTR_RHSC, &ohci->regs->intrstatus); | ||
516 | rhsc_status = ohci_readl(ohci, &ohci->regs->intrstatus) & | ||
517 | OHCI_INTR_RHSC; | ||
518 | |||
506 | /* look at each port */ | 519 | /* look at each port */ |
507 | for (i = 0; i < ohci->num_ports; i++) { | 520 | for (i = 0; i < ohci->num_ports; i++) { |
508 | u32 status = roothub_portstatus (ohci, i); | 521 | u32 status = roothub_portstatus (ohci, i); |
@@ -521,7 +534,7 @@ ohci_hub_status_data (struct usb_hcd *hcd, char *buf) | |||
521 | } | 534 | } |
522 | 535 | ||
523 | hcd->poll_rh = ohci_root_hub_state_changes(ohci, changed, | 536 | hcd->poll_rh = ohci_root_hub_state_changes(ohci, changed, |
524 | any_connected); | 537 | any_connected, rhsc_status); |
525 | 538 | ||
526 | done: | 539 | done: |
527 | spin_unlock_irqrestore (&ohci->lock, flags); | 540 | spin_unlock_irqrestore (&ohci->lock, flags); |