*/
.hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control,
-
+ .hub_irq_enable = ohci_rhsc_enable,
#ifdef CONFIG_PM
.bus_suspend = ohci_bus_suspend,
.bus_resume = ohci_bus_resume,
* basic lifecycle operations
*/
.start = ohci_au1xxx_start,
-#ifdef CONFIG_PM
- /* suspend: ohci_au1xxx_suspend, -- tbd */
- /* resume: ohci_au1xxx_resume, -- tbd */
-#endif /*CONFIG_PM*/
.stop = ohci_stop,
/*
*/
.hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control,
+ .hub_irq_enable = ohci_rhsc_enable,
#ifdef CONFIG_PM
.bus_suspend = ohci_bus_suspend,
.bus_resume = ohci_bus_resume,
size -= temp;
next += temp;
+ temp = scnprintf (next, size, "hub poll timer %s\n",
+ ohci_to_hcd(ohci)->poll_rh ? "ON" : "off");
+ size -= temp;
+ next += temp;
+
/* roothub */
ohci_dump_roothub (ohci, 1, &next, &size);
.get_frame_number = ohci_get_frame,
.hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control,
+ .hub_irq_enable = ohci_rhsc_enable,
#ifdef CONFIG_PM
.bus_suspend = ohci_bus_suspend,
.bus_resume = ohci_bus_resume,
#include "../core/hcd.h"
-#define DRIVER_VERSION "2005 April 22"
+#define DRIVER_VERSION "2006 August 04"
#define DRIVER_AUTHOR "Roman Weissgaerber, David Brownell"
#define DRIVER_DESC "USB 1.1 'Open' Host Controller (OHCI) Driver"
#undef OHCI_VERBOSE_DEBUG /* not always helpful */
/* For initializing controller (mask in an HCFS mode too) */
-#define OHCI_CONTROL_INIT OHCI_CTRL_CBSR
+#define OHCI_CONTROL_INIT OHCI_CTRL_CBSR
#define OHCI_INTR_INIT \
- (OHCI_INTR_MIE | OHCI_INTR_UE | OHCI_INTR_RD | OHCI_INTR_WDH)
+ (OHCI_INTR_MIE | OHCI_INTR_RHSC | OHCI_INTR_UE \
+ | OHCI_INTR_RD | OHCI_INTR_WDH)
#ifdef __hppa__
/* On PA-RISC, PDC can leave IR set incorrectly; ignore it there. */
static const char hcd_name [] = "ohci_hcd";
+#define STATECHANGE_DELAY msecs_to_jiffies(300)
+
#include "ohci.h"
static void ohci_dump (struct ohci_hcd *ohci, int verbose);
disable (ohci);
ohci->regs = hcd->regs;
- ohci->next_statechange = jiffies;
/* REVISIT this BIOS handshake is now moved into PCI "quirks", and
* was never needed for most non-PCI systems ... remove the code?
return -EOVERFLOW;
}
- /* start controller operations */
+ /* use rhsc irqs after khubd is fully initialized */
+ hcd->poll_rh = 1;
+ hcd->uses_new_polling = 1;
+
+ /* start controller operations */
ohci->hc_control &= OHCI_CTRL_RWC;
- ohci->hc_control |= OHCI_CONTROL_INIT | OHCI_USB_OPER;
- ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
+ ohci->hc_control |= OHCI_CONTROL_INIT | OHCI_USB_OPER;
+ ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
hcd->state = HC_STATE_RUNNING;
/* wake on ConnectStatusChange, matching external hubs */
/* Choose the interrupts we care about now, others later on demand */
mask = OHCI_INTR_INIT;
- ohci_writel (ohci, mask, &ohci->regs->intrstatus);
+ ohci_writel (ohci, ~0, &ohci->regs->intrstatus);
ohci_writel (ohci, mask, &ohci->regs->intrenable);
/* handle root hub init quirks ... */
// flush those writes
(void) ohci_readl (ohci, &ohci->regs->control);
+ ohci->next_statechange = jiffies + STATECHANGE_DELAY;
spin_unlock_irq (&ohci->lock);
// POTPGT delay is bits 24-31, in 2 ms units.
/* interrupt for some other device? */
} else if ((ints &= ohci_readl (ohci, ®s->intrenable)) == 0) {
return IRQ_NOTMINE;
- }
+ }
+
+ /* NOTE: vendors didn't always make the same implementation
+ * choices for RHSC. Sometimes it triggers on an edge (like
+ * setting and maybe clearing a port status change bit); and
+ * it's level-triggered on other silicon, active until khubd
+ * clears all active port status change bits. Poll by timer
+ * til it's fully debounced and the difference won't matter.
+ */
+ if (ints & OHCI_INTR_RHSC) {
+ ohci_vdbg (ohci, "rhsc\n");
+ ohci_writel (ohci, OHCI_INTR_RHSC, ®s->intrdisable);
+ hcd->poll_rh = 1;
+ ohci->next_statechange = jiffies + STATECHANGE_DELAY;
+ ohci_writel (ohci, OHCI_INTR_RHSC, ®s->intrstatus);
+ usb_hcd_poll_rh_status(hcd);
+ }
if (ints & OHCI_INTR_UE) {
disable (ohci);
/*-------------------------------------------------------------------------*/
+/* hcd->hub_irq_enable() */
+static void ohci_rhsc_enable (struct usb_hcd *hcd)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci (hcd);
+
+ ohci_writel (ohci, OHCI_INTR_RHSC, &ohci->regs->intrenable);
+}
+
#ifdef CONFIG_PM
#define OHCI_SCHED_ENABLES \
/* no resumes until devices finish suspending */
ohci->next_statechange = jiffies + msecs_to_jiffies (5);
+ /* no timer polling */
+ hcd->poll_rh = 0;
+
done:
/* external suspend vs self autosuspend ... same effect */
if (status == 0)
/* TRSMRCY */
msleep (10);
- /* keep it alive for ~5x suspend + resume costs */
- ohci->next_statechange = jiffies + msecs_to_jiffies (250);
+ /* keep it alive for more than ~5x suspend + resume costs */
+ ohci->next_statechange = jiffies + STATECHANGE_DELAY;
/* maybe turn schedules back on */
enables = 0;
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
int i, changed = 0, length = 1;
- int can_suspend = device_may_wakeup(&hcd->self.root_hub->dev);
+ int can_suspend;
unsigned long flags;
+ can_suspend = device_may_wakeup(&hcd->self.root_hub->dev);
spin_lock_irqsave (&ohci->lock, flags);
/* handle autosuspended root: finish resuming before
for (i = 0; i < ohci->num_ports; i++) {
u32 status = roothub_portstatus (ohci, i);
+ /* can't autosuspend with active ports */
+ if ((status & RH_PS_PES) && !(status & RH_PS_PSS))
+ can_suspend = 0;
+
if (status & (RH_PS_CSC | RH_PS_PESC | RH_PS_PSSC
| RH_PS_OCIC | RH_PS_PRSC)) {
changed = 1;
buf [1] |= 1 << (i - 7);
continue;
}
+ }
- /* can suspend if no ports are enabled; or if all all
- * enabled ports are suspended AND remote wakeup is on.
- */
- if (!(status & RH_PS_CCS))
- continue;
- if ((status & RH_PS_PSS) && can_suspend)
- continue;
+ /* after root hub changes, stop polling after debouncing
+ * for a while and maybe kicking in autosuspend
+ */
+ if (changed) {
+ ohci->next_statechange = jiffies + STATECHANGE_DELAY;
can_suspend = 0;
+ } else if (time_before (jiffies, ohci->next_statechange)) {
+ can_suspend = 0;
+ } else {
+#ifdef CONFIG_PM
+ can_suspend = can_suspend
+ && !ohci->ed_rm_list
+ && ((OHCI_CTRL_HCFS | OHCI_SCHED_ENABLES)
+ & ohci->hc_control)
+ == OHCI_USB_OPER;
+#endif
+ if (hcd->uses_new_polling) {
+ hcd->poll_rh = 0;
+ /* use INTR_RHSC iff INTR_RD won't apply */
+ if (!can_suspend)
+ ohci_writel (ohci, OHCI_INTR_RHSC,
+ &ohci->regs->intrenable);
+ }
}
+
done:
spin_unlock_irqrestore (&ohci->lock, flags);
-#ifdef CONFIG_PM
- /* save power by suspending idle root hubs;
+#ifdef CONFIG_PM
+ /* save power by autosuspending idle root hubs;
* INTR_RD wakes us when there's work
*/
- if (can_suspend
- && !changed
- && !ohci->ed_rm_list
- && ((OHCI_CTRL_HCFS | OHCI_SCHED_ENABLES)
- & ohci->hc_control)
- == OHCI_USB_OPER
- && time_after (jiffies, ohci->next_statechange)
- && usb_trylock_device (hcd->self.root_hub) == 0
- ) {
+ if (can_suspend && usb_trylock_device (hcd->self.root_hub) == 0) {
ohci_vdbg (ohci, "autosuspend\n");
(void) ohci_bus_suspend (hcd);
usb_unlock_device (hcd->self.root_hub);
* basic lifecycle operations
*/
.start = ohci_lh7a404_start,
-#ifdef CONFIG_PM
- /* suspend: ohci_lh7a404_suspend, -- tbd */
- /* resume: ohci_lh7a404_resume, -- tbd */
-#endif /*CONFIG_PM*/
.stop = ohci_stop,
/*
*/
.hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control,
+ .hub_irq_enable = ohci_rhsc_enable,
#ifdef CONFIG_PM
.bus_suspend = ohci_bus_suspend,
.bus_resume = ohci_bus_resume,
int ret;
config = hcd->self.controller->platform_data;
- if (config->otg || config->rwc)
+ if (config->otg || config->rwc) {
+ ohci->hc_control = OHCI_CTRL_RWC;
writel(OHCI_CTRL_RWC, &ohci->regs->control);
+ }
if ((ret = ohci_run (ohci)) < 0) {
dev_err(hcd->self.controller, "can't start\n");
*/
.hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control,
+ .hub_irq_enable = ohci_rhsc_enable,
#ifdef CONFIG_PM
.bus_suspend = ohci_bus_suspend,
.bus_resume = ohci_bus_resume,
*/
.reset = ohci_pci_reset,
.start = ohci_pci_start,
+ .stop = ohci_stop,
+
#ifdef CONFIG_PM
+ /* these suspend/resume entries are for upstream PCI glue ONLY */
.suspend = ohci_pci_suspend,
.resume = ohci_pci_resume,
#endif
- .stop = ohci_stop,
/*
* managing i/o requests and associated device resources
*/
.hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control,
+ .hub_irq_enable = ohci_rhsc_enable,
#ifdef CONFIG_PM
.bus_suspend = ohci_bus_suspend,
.bus_resume = ohci_bus_resume,
*/
.hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control,
+ .hub_irq_enable = ohci_rhsc_enable,
#ifdef CONFIG_PM
.bus_suspend = ohci_bus_suspend,
.bus_resume = ohci_bus_resume,
*/
.hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control,
+ .hub_irq_enable = ohci_rhsc_enable,
#ifdef CONFIG_PM
.bus_suspend = ohci_bus_suspend,
.bus_resume = ohci_bus_resume,
*/
.hub_status_data = ohci_s3c2410_hub_status_data,
.hub_control = ohci_s3c2410_hub_control,
+ .hub_irq_enable = ohci_rhsc_enable,
#ifdef CONFIG_PM
.bus_suspend = ohci_bus_suspend,
.bus_resume = ohci_bus_resume,
* basic lifecycle operations
*/
.start = ohci_sa1111_start,
-#ifdef CONFIG_PM
- /* suspend: ohci_sa1111_suspend, -- tbd */
- /* resume: ohci_sa1111_resume, -- tbd */
-#endif
.stop = ohci_stop,
/*
*/
.hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control,
+ .hub_irq_enable = ohci_rhsc_enable,
#ifdef CONFIG_PM
.bus_suspend = ohci_bus_suspend,
.bus_resume = ohci_bus_resume,