/*-------------------------------------------------------------------------*/
+/* 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 \
&ohci->regs->intrstatus);
/* maybe resume can wake root hub */
- if (hcd->remote_wakeup)
+ if (device_may_wakeup(&ohci_to_hcd(ohci)->self.root_hub->dev))
ohci->hc_control |= OHCI_CTRL_RWE;
else
ohci->hc_control &= ~OHCI_CTRL_RWE;
/* 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)
- usb_hcd_suspend_root_hub(hcd);
spin_unlock_irqrestore (&ohci->lock, flags);
return status;
}
(void) ohci_readl (ohci, &ohci->regs->control);
msleep (3);
- temp = OHCI_CONTROL_INIT | OHCI_USB_OPER;
- if (hcd->can_wakeup)
- temp |= OHCI_CTRL_RWC;
+ temp = ohci->hc_control;
+ temp &= OHCI_CTRL_RWC;
+ temp |= OHCI_CONTROL_INIT | OHCI_USB_OPER;
ohci->hc_control = temp;
ohci_writel (ohci, temp, &ohci->regs->control);
(void) ohci_readl (ohci, &ohci->regs->control);
/* 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 = hcd->can_wakeup;
+ 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) && hcd->remote_wakeup)
- 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);
break;
case GetHubStatus:
temp = roothub_status (ohci) & ~(RH_HS_CRWE | RH_HS_DRWE);
- *(__le32 *) buf = cpu_to_le32 (temp);
+ put_unaligned(cpu_to_le32 (temp), (__le32 *) buf);
break;
case GetPortStatus:
if (!wIndex || wIndex > ports)
goto error;
wIndex--;
temp = roothub_portstatus (ohci, wIndex);
- *(__le32 *) buf = cpu_to_le32 (temp);
+ put_unaligned(cpu_to_le32 (temp), (__le32 *) buf);
#ifndef OHCI_VERBOSE_DEBUG
if (*(u16*)(buf+2)) /* only if wPortChange is interesting */