diff options
Diffstat (limited to 'drivers/usb/host/ohci-hub.c')
-rw-r--r-- | drivers/usb/host/ohci-hub.c | 87 |
1 files changed, 52 insertions, 35 deletions
diff --git a/drivers/usb/host/ohci-hub.c b/drivers/usb/host/ohci-hub.c index 7ea9a7b31155..32bbce9718f0 100644 --- a/drivers/usb/host/ohci-hub.c +++ b/drivers/usb/host/ohci-hub.c | |||
@@ -359,21 +359,24 @@ 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; | 365 | int 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_enable = ohci_readl(ohci, &ohci->regs->intrenable) & | ||
372 | OHCI_INTR_RHSC; | ||
369 | 373 | ||
374 | switch (ohci->hc_control & OHCI_CTRL_HCFS) { | ||
370 | case OHCI_USB_OPER: | 375 | case OHCI_USB_OPER: |
371 | /* If no status changes are pending, enable status-change | 376 | /* If no status changes are pending, enable RHSC interrupts. */ |
372 | * interrupts. | 377 | if (!rhsc_enable && !rhsc_status && !changed) { |
373 | */ | 378 | rhsc_enable = OHCI_INTR_RHSC; |
374 | if (!rhsc && !changed) { | 379 | ohci_writel(ohci, rhsc_enable, &ohci->regs->intrenable); |
375 | rhsc = OHCI_INTR_RHSC; | ||
376 | ohci_writel(ohci, rhsc, &ohci->regs->intrenable); | ||
377 | } | 380 | } |
378 | 381 | ||
379 | /* Keep on polling until we know a device is connected | 382 | /* Keep on polling until we know a device is connected |
@@ -383,7 +386,7 @@ static int ohci_root_hub_state_changes(struct ohci_hcd *ohci, int changed, | |||
383 | if (any_connected || | 386 | if (any_connected || |
384 | !device_may_wakeup(&ohci_to_hcd(ohci) | 387 | !device_may_wakeup(&ohci_to_hcd(ohci) |
385 | ->self.root_hub->dev)) { | 388 | ->self.root_hub->dev)) { |
386 | if (rhsc) | 389 | if (rhsc_enable) |
387 | poll_rh = 0; | 390 | poll_rh = 0; |
388 | } else { | 391 | } else { |
389 | ohci->autostop = 1; | 392 | ohci->autostop = 1; |
@@ -396,34 +399,45 @@ static int ohci_root_hub_state_changes(struct ohci_hcd *ohci, int changed, | |||
396 | ohci->autostop = 0; | 399 | ohci->autostop = 0; |
397 | ohci->next_statechange = jiffies + | 400 | ohci->next_statechange = jiffies + |
398 | STATECHANGE_DELAY; | 401 | STATECHANGE_DELAY; |
399 | } else if (rhsc && time_after_eq(jiffies, | 402 | } else if (time_after_eq(jiffies, |
400 | ohci->next_statechange) | 403 | ohci->next_statechange) |
401 | && !ohci->ed_rm_list | 404 | && !ohci->ed_rm_list |
402 | && !(ohci->hc_control & | 405 | && !(ohci->hc_control & |
403 | OHCI_SCHED_ENABLES)) { | 406 | OHCI_SCHED_ENABLES)) { |
404 | ohci_rh_suspend(ohci, 1); | 407 | ohci_rh_suspend(ohci, 1); |
405 | poll_rh = 0; | 408 | if (rhsc_enable) |
409 | poll_rh = 0; | ||
406 | } | 410 | } |
407 | } | 411 | } |
408 | break; | 412 | break; |
409 | 413 | ||
410 | /* if there is a port change, autostart or ask to be resumed */ | ||
411 | case OHCI_USB_SUSPEND: | 414 | case OHCI_USB_SUSPEND: |
412 | case OHCI_USB_RESUME: | 415 | case OHCI_USB_RESUME: |
416 | /* if there is a port change, autostart or ask to be resumed */ | ||
413 | if (changed) { | 417 | if (changed) { |
414 | if (ohci->autostop) | 418 | if (ohci->autostop) |
415 | ohci_rh_resume(ohci); | 419 | ohci_rh_resume(ohci); |
416 | else | 420 | else |
417 | usb_hcd_resume_root_hub(ohci_to_hcd(ohci)); | 421 | usb_hcd_resume_root_hub(ohci_to_hcd(ohci)); |
418 | } else { | ||
419 | if (!rhsc && (ohci->autostop || | ||
420 | ohci_to_hcd(ohci)->self.root_hub-> | ||
421 | do_remote_wakeup)) | ||
422 | ohci_writel(ohci, OHCI_INTR_RHSC, | ||
423 | &ohci->regs->intrenable); | ||
424 | 422 | ||
425 | /* everything is idle, no need for polling */ | 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) { | ||
426 | poll_rh = 0; | 427 | poll_rh = 0; |
428 | |||
429 | } else { | ||
430 | /* If no status changes are pending, | ||
431 | * enable RHSC interrupts | ||
432 | */ | ||
433 | if (!rhsc_enable && !rhsc_status) { | ||
434 | rhsc_enable = OHCI_INTR_RHSC; | ||
435 | ohci_writel(ohci, rhsc_enable, | ||
436 | &ohci->regs->intrenable); | ||
437 | } | ||
438 | /* Keep polling until RHSC is enabled */ | ||
439 | if (rhsc_enable) | ||
440 | poll_rh = 0; | ||
427 | } | 441 | } |
428 | break; | 442 | break; |
429 | } | 443 | } |
@@ -441,18 +455,22 @@ static inline int ohci_rh_resume(struct ohci_hcd *ohci) | |||
441 | * autostop isn't used when CONFIG_PM is turned off. | 455 | * autostop isn't used when CONFIG_PM is turned off. |
442 | */ | 456 | */ |
443 | 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, |
444 | int any_connected) | 458 | int any_connected, int rhsc_status) |
445 | { | 459 | { |
446 | /* If RHSC is enabled, don't poll */ | 460 | /* If RHSC is enabled, don't poll */ |
447 | if (ohci_readl(ohci, &ohci->regs->intrenable) & OHCI_INTR_RHSC) | 461 | if (ohci_readl(ohci, &ohci->regs->intrenable) & OHCI_INTR_RHSC) |
448 | return 0; | 462 | return 0; |
449 | 463 | ||
450 | /* If no status changes are pending, enable status-change interrupts */ | 464 | /* If status changes are pending, continue polling. |
451 | if (!changed) { | 465 | * Conversely, if no status changes are pending but the RHSC |
452 | ohci_writel(ohci, OHCI_INTR_RHSC, &ohci->regs->intrenable); | 466 | * status bit was set, then RHSC may be broken so continue polling. |
453 | return 0; | 467 | */ |
454 | } | 468 | if (changed || rhsc_status) |
455 | return 1; | 469 | return 1; |
470 | |||
471 | /* It's safe to re-enable RHSC interrupts */ | ||
472 | ohci_writel(ohci, OHCI_INTR_RHSC, &ohci->regs->intrenable); | ||
473 | return 0; | ||
456 | } | 474 | } |
457 | 475 | ||
458 | #endif /* CONFIG_PM */ | 476 | #endif /* CONFIG_PM */ |
@@ -467,6 +485,7 @@ ohci_hub_status_data (struct usb_hcd *hcd, char *buf) | |||
467 | struct ohci_hcd *ohci = hcd_to_ohci (hcd); | 485 | struct ohci_hcd *ohci = hcd_to_ohci (hcd); |
468 | int i, changed = 0, length = 1; | 486 | int i, changed = 0, length = 1; |
469 | int any_connected = 0; | 487 | int any_connected = 0; |
488 | int rhsc_status; | ||
470 | unsigned long flags; | 489 | unsigned long flags; |
471 | 490 | ||
472 | spin_lock_irqsave (&ohci->lock, flags); | 491 | spin_lock_irqsave (&ohci->lock, flags); |
@@ -492,12 +511,10 @@ ohci_hub_status_data (struct usb_hcd *hcd, char *buf) | |||
492 | length++; | 511 | length++; |
493 | } | 512 | } |
494 | 513 | ||
495 | /* Some broken controllers never turn off RHCS in the interrupt | 514 | /* Clear the RHSC status flag before reading the port statuses */ |
496 | * status register. For their sake we won't re-enable RHSC | 515 | ohci_writel(ohci, OHCI_INTR_RHSC, &ohci->regs->intrstatus); |
497 | * interrupts if the flag is already set. | 516 | rhsc_status = ohci_readl(ohci, &ohci->regs->intrstatus) & |
498 | */ | 517 | OHCI_INTR_RHSC; |
499 | if (ohci_readl(ohci, &ohci->regs->intrstatus) & OHCI_INTR_RHSC) | ||
500 | changed = 1; | ||
501 | 518 | ||
502 | /* look at each port */ | 519 | /* look at each port */ |
503 | for (i = 0; i < ohci->num_ports; i++) { | 520 | for (i = 0; i < ohci->num_ports; i++) { |
@@ -517,7 +534,7 @@ ohci_hub_status_data (struct usb_hcd *hcd, char *buf) | |||
517 | } | 534 | } |
518 | 535 | ||
519 | hcd->poll_rh = ohci_root_hub_state_changes(ohci, changed, | 536 | hcd->poll_rh = ohci_root_hub_state_changes(ohci, changed, |
520 | any_connected); | 537 | any_connected, rhsc_status); |
521 | 538 | ||
522 | done: | 539 | done: |
523 | spin_unlock_irqrestore (&ohci->lock, flags); | 540 | spin_unlock_irqrestore (&ohci->lock, flags); |