X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fusb%2Fhost%2Fehci-hub.c;h=2d36a8deb2e61c9389c99d0b50f91825f9250ed6;hb=refs%2Fheads%2Fvserver;hp=452c73ede7df58e8d5ec343a3732479ccb00d4d9;hpb=9bf4aaab3e101692164d49b7ca357651eb691cb6;p=linux-2.6.git diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index 452c73ede..2d36a8deb 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -1,6 +1,6 @@ /* - * Copyright (c) 2001-2002 by David Brownell - * + * Copyright (C) 2001-2004 by David Brownell + * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your @@ -30,29 +30,48 @@ #ifdef CONFIG_PM -static int ehci_hub_suspend (struct usb_hcd *hcd) +static int ehci_bus_suspend (struct usb_hcd *hcd) { struct ehci_hcd *ehci = hcd_to_ehci (hcd); - struct usb_device *root = hcd_to_bus (&ehci->hcd)->root_hub; int port; - int status = 0; + int mask; - if (root->dev.power.power_state != 0) - return 0; if (time_before (jiffies, ehci->next_statechange)) - return -EAGAIN; + msleep(5); port = HCS_N_PORTS (ehci->hcs_params); spin_lock_irq (&ehci->lock); - /* suspend any active/unsuspended ports, maybe allow wakeup */ + /* stop schedules, clean any completed work */ + if (HC_IS_RUNNING(hcd->state)) { + ehci_quiesce (ehci); + hcd->state = HC_STATE_QUIESCING; + } + ehci->command = readl (&ehci->regs->command); + if (ehci->reclaim) + ehci->reclaim_ready = 1; + ehci_work(ehci); + + /* Unlike other USB host controller types, EHCI doesn't have + * any notion of "global" or bus-wide suspend. The driver has + * to manually suspend all the active unsuspended ports, and + * then manually resume them in the bus_resume() routine. + */ + ehci->bus_suspended = 0; while (port--) { - u32 t1 = readl (&ehci->regs->port_status [port]); - u32 t2 = t1; + u32 __iomem *reg = &ehci->regs->port_status [port]; + u32 t1 = readl (reg) & ~PORT_RWC_BITS; + u32 t2 = t1; - if ((t1 & PORT_PE) && !(t1 & PORT_OWNER)) + /* keep track of which ports we suspend */ + if ((t1 & PORT_PE) && !(t1 & PORT_OWNER) && + !(t1 & PORT_SUSPEND)) { t2 |= PORT_SUSPEND; - if (ehci->hcd.remote_wakeup) + set_bit(port, &ehci->bus_suspended); + } + + /* enable remote wakeup on all ports */ + if (device_may_wakeup(&hcd->self.root_hub->dev)) t2 |= PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E; else t2 &= ~(PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E); @@ -60,72 +79,88 @@ static int ehci_hub_suspend (struct usb_hcd *hcd) if (t1 != t2) { ehci_vdbg (ehci, "port %d, %08x -> %08x\n", port + 1, t1, t2); - writel (t2, &ehci->regs->port_status [port]); + writel (t2, reg); } } - /* stop schedules, then turn off HC and clean any completed work */ - if (hcd->state == USB_STATE_RUNNING) - ehci_ready (ehci); - ehci->command = readl (&ehci->regs->command); - writel (ehci->command & ~CMD_RUN, &ehci->regs->command); - if (ehci->reclaim) - ehci->reclaim_ready = 1; - ehci_work(ehci, NULL); - (void) handshake (&ehci->regs->status, STS_HALT, STS_HALT, 2000); + /* turn off now-idle HC */ + del_timer_sync (&ehci->watchdog); + ehci_halt (ehci); + hcd->state = HC_STATE_SUSPENDED; + + /* allow remote wakeup */ + mask = INTR_MASK; + if (!device_may_wakeup(&hcd->self.root_hub->dev)) + mask &= ~STS_PCD; + writel(mask, &ehci->regs->intr_enable); + readl(&ehci->regs->intr_enable); - root->dev.power.power_state = 3; ehci->next_statechange = jiffies + msecs_to_jiffies(10); spin_unlock_irq (&ehci->lock); - return status; + return 0; } -/* caller owns root->serialize, and should reset/reinit on error */ -static int ehci_hub_resume (struct usb_hcd *hcd) +/* caller has locked the root hub, and should reset/reinit on error */ +static int ehci_bus_resume (struct usb_hcd *hcd) { struct ehci_hcd *ehci = hcd_to_ehci (hcd); - struct usb_device *root = hcd_to_bus (&ehci->hcd)->root_hub; u32 temp; int i; - if (!root->dev.power.power_state) - return 0; if (time_before (jiffies, ehci->next_statechange)) - return -EAGAIN; - - /* re-init operational registers in case we lost power */ - if (readl (&ehci->regs->intr_enable) == 0) { - writel (INTR_MASK, &ehci->regs->intr_enable); - writel (0, &ehci->regs->segment); - writel (ehci->periodic_dma, &ehci->regs->frame_list); - writel ((u32)ehci->async->qh_dma, &ehci->regs->async_next); - /* FIXME will this work even (pci) vAUX was lost? */ - } + msleep(5); + spin_lock_irq (&ehci->lock); + + /* Ideally and we've got a real resume here, and no port's power + * was lost. (For PCI, that means Vaux was maintained.) But we + * could instead be restoring a swsusp snapshot -- so that BIOS was + * the last user of the controller, not reset/pm hardware keeping + * state we gave to it. + */ + temp = readl(&ehci->regs->intr_enable); + ehci_dbg(ehci, "resume root hub%s\n", temp ? "" : " after power loss"); + + /* at least some APM implementations will try to deliver + * IRQs right away, so delay them until we're ready. + */ + writel(0, &ehci->regs->intr_enable); + + /* re-init operational registers */ + writel(0, &ehci->regs->segment); + writel(ehci->periodic_dma, &ehci->regs->frame_list); + writel((u32) ehci->async->qh_dma, &ehci->regs->async_next); /* restore CMD_RUN, framelist size, and irq threshold */ writel (ehci->command, &ehci->regs->command); - /* take ports out of suspend */ + /* Some controller/firmware combinations need a delay during which + * they set up the port statuses. See Bugzilla #8190. */ + mdelay(8); + + /* manually resume the ports we suspended during bus_suspend() */ i = HCS_N_PORTS (ehci->hcs_params); while (i--) { temp = readl (&ehci->regs->port_status [i]); - temp &= ~(PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E); - if (temp & PORT_SUSPEND) { + temp &= ~(PORT_RWC_BITS + | PORT_WKOC_E | PORT_WKDISC_E | PORT_WKCONN_E); + if (test_bit(i, &ehci->bus_suspended) && + (temp & PORT_SUSPEND)) { ehci->reset_done [i] = jiffies + msecs_to_jiffies (20); temp |= PORT_RESUME; } writel (temp, &ehci->regs->port_status [i]); } i = HCS_N_PORTS (ehci->hcs_params); - msleep (20); + mdelay (20); while (i--) { temp = readl (&ehci->regs->port_status [i]); - if ((temp & PORT_SUSPEND) == 0) - continue; - temp &= ~PORT_RESUME; - writel (temp, &ehci->regs->port_status [i]); - ehci_vdbg (ehci, "resumed port %d\n", i + 1); + if (test_bit(i, &ehci->bus_suspended) && + (temp & PORT_SUSPEND)) { + temp &= ~(PORT_RWC_BITS | PORT_RESUME); + writel (temp, &ehci->regs->port_status [i]); + ehci_vdbg (ehci, "resumed port %d\n", i + 1); + } } (void) readl (&ehci->regs->command); @@ -135,19 +170,25 @@ static int ehci_hub_resume (struct usb_hcd *hcd) temp |= CMD_ASE; if (ehci->periodic_sched) temp |= CMD_PSE; - if (temp) - writel (ehci->command | temp, &ehci->regs->command); + if (temp) { + ehci->command |= temp; + writel (ehci->command, &ehci->regs->command); + } - root->dev.power.power_state = 0; ehci->next_statechange = jiffies + msecs_to_jiffies(5); - ehci->hcd.state = USB_STATE_RUNNING; + hcd->state = HC_STATE_RUNNING; + + /* Now we can safely re-enable irqs */ + writel(INTR_MASK, &ehci->regs->intr_enable); + + spin_unlock_irq (&ehci->lock); return 0; } #else -#define ehci_hub_suspend 0 -#define ehci_hub_resume 0 +#define ehci_bus_suspend NULL +#define ehci_bus_resume NULL #endif /* CONFIG_PM */ @@ -167,7 +208,7 @@ static int check_reset_complete ( if (!(port_status & PORT_PE)) { /* with integrated TT, there's nobody to hand it to! */ - if (ehci_is_ARC(ehci)) { + if (ehci_is_TDI(ehci)) { ehci_dbg (ehci, "Failed to enable port %d on root hub TT\n", index+1); @@ -179,6 +220,7 @@ static int check_reset_complete ( // what happens if HCS_N_CC(params) == 0 ? port_status |= PORT_OWNER; + port_status &= ~PORT_RWC_BITS; writel (port_status, &ehci->regs->port_status [index]); } else @@ -197,9 +239,14 @@ ehci_hub_status_data (struct usb_hcd *hcd, char *buf) { struct ehci_hcd *ehci = hcd_to_ehci (hcd); u32 temp, status = 0; + u32 mask; int ports, i, retval = 1; unsigned long flags; + /* if !USB_SUSPEND, root hub timers won't get shut down ... */ + if (!HC_IS_RUNNING(hcd->state)) + return 0; + /* init status to no-changes */ buf [0] = 0; ports = HCS_N_PORTS (ehci->hcs_params); @@ -207,7 +254,19 @@ ehci_hub_status_data (struct usb_hcd *hcd, char *buf) buf [1] = 0; retval++; } - + + /* Some boards (mostly VIA?) report bogus overcurrent indications, + * causing massive log spam unless we completely ignore them. It + * may be relevant that VIA VT8235 controlers, where PORT_POWER is + * always set, seem to clear PORT_OCC and PORT_CSC when writing to + * PORT_POWER; that's surprising, but maybe within-spec. + */ + if (!ignore_oc) + mask = PORT_CSC | PORT_PEC | PORT_OCC; + else + mask = PORT_CSC | PORT_PEC; + // PORT_RESUME from hardware ~= PORT_STAT_C_SUSPEND + /* no hub change reports (bit 0) for now (power, ...) */ /* port N changes (bit N)? */ @@ -217,15 +276,15 @@ ehci_hub_status_data (struct usb_hcd *hcd, char *buf) if (temp & PORT_OWNER) { /* don't report this in GetPortStatus */ if (temp & PORT_CSC) { - temp &= ~PORT_CSC; + temp &= ~PORT_RWC_BITS; + temp |= PORT_CSC; writel (temp, &ehci->regs->port_status [i]); } continue; } if (!(temp & PORT_CONNECT)) ehci->reset_done [i] = 0; - if ((temp & (PORT_CSC | PORT_PEC | PORT_OCC)) != 0 - // PORT_STAT_C_SUSPEND? + if ((temp & mask) != 0 || ((temp & PORT_RESUME) != 0 && time_after (jiffies, ehci->reset_done [i]))) { @@ -236,6 +295,7 @@ ehci_hub_status_data (struct usb_hcd *hcd, char *buf) status = STS_PCD; } } + /* FIXME autosuspend idle root hubs */ spin_unlock_irqrestore (&ehci->lock, flags); return status ? retval : 0; } @@ -265,14 +325,19 @@ ehci_hub_descriptor ( temp = 0x0008; /* per-port overcurrent reporting */ if (HCS_PPC (ehci->hcs_params)) temp |= 0x0001; /* per-port power control */ + else + temp |= 0x0002; /* no power switching */ +#if 0 +// re-enable when we support USB_PORT_FEAT_INDICATOR below. if (HCS_INDICATOR (ehci->hcs_params)) temp |= 0x0080; /* per-port indicators (LEDs) */ - desc->wHubCharacteristics = cpu_to_le16 (temp); +#endif + desc->wHubCharacteristics = (__force __u16)cpu_to_le16 (temp); } /*-------------------------------------------------------------------------*/ -#define PORT_WAKE_BITS (PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E) +#define PORT_WAKE_BITS (PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E) static int ehci_hub_control ( struct usb_hcd *hcd, @@ -287,6 +352,7 @@ static int ehci_hub_control ( u32 temp, status; unsigned long flags; int retval = 0; + unsigned selector; /* * FIXME: support SetPortFeatures USB_PORT_FEAT_INDICATOR. @@ -321,17 +387,20 @@ static int ehci_hub_control ( &ehci->regs->port_status [wIndex]); break; case USB_PORT_FEAT_C_ENABLE: - writel (temp | PORT_PEC, + writel((temp & ~PORT_RWC_BITS) | PORT_PEC, &ehci->regs->port_status [wIndex]); break; case USB_PORT_FEAT_SUSPEND: if (temp & PORT_RESET) goto error; + if (ehci->no_selective_suspend) + break; if (temp & PORT_SUSPEND) { if ((temp & PORT_PE) == 0) goto error; /* resume signaling for 20 msec */ - writel ((temp & ~PORT_WAKE_BITS) | PORT_RESUME, + temp &= ~(PORT_RWC_BITS | PORT_WAKE_BITS); + writel (temp | PORT_RESUME, &ehci->regs->port_status [wIndex]); ehci->reset_done [wIndex] = jiffies + msecs_to_jiffies (20); @@ -342,15 +411,15 @@ static int ehci_hub_control ( break; case USB_PORT_FEAT_POWER: if (HCS_PPC (ehci->hcs_params)) - writel (temp & ~PORT_POWER, + writel (temp & ~(PORT_RWC_BITS | PORT_POWER), &ehci->regs->port_status [wIndex]); break; case USB_PORT_FEAT_C_CONNECTION: - writel (temp | PORT_CSC, + writel((temp & ~PORT_RWC_BITS) | PORT_CSC, &ehci->regs->port_status [wIndex]); break; case USB_PORT_FEAT_C_OVER_CURRENT: - writel (temp | PORT_OCC, + writel((temp & ~PORT_RWC_BITS) | PORT_OCC, &ehci->regs->port_status [wIndex]); break; case USB_PORT_FEAT_C_RESET: @@ -382,7 +451,7 @@ static int ehci_hub_control ( status |= 1 << USB_PORT_FEAT_C_CONNECTION; if (temp & PORT_PEC) status |= 1 << USB_PORT_FEAT_C_ENABLE; - if (temp & PORT_OCC) + if ((temp & PORT_OCC) && !ignore_oc) status |= 1 << USB_PORT_FEAT_C_OVER_CURRENT; /* whoever resumes must GetPortStatus to complete it!! */ @@ -394,7 +463,7 @@ static int ehci_hub_control ( /* stop resume signaling */ temp = readl (&ehci->regs->port_status [wIndex]); - writel (temp & ~PORT_RESUME, + writel (temp & ~(PORT_RWC_BITS | PORT_RESUME), &ehci->regs->port_status [wIndex]); retval = handshake ( &ehci->regs->port_status [wIndex], @@ -415,11 +484,14 @@ static int ehci_hub_control ( ehci->reset_done [wIndex] = 0; /* force reset to complete */ - writel (temp & ~PORT_RESET, + writel (temp & ~(PORT_RWC_BITS | PORT_RESET), &ehci->regs->port_status [wIndex]); + /* REVISIT: some hardware needs 550+ usec to clear + * this bit; seems too long to spin routinely... + */ retval = handshake ( &ehci->regs->port_status [wIndex], - PORT_RESET, 0, 500); + PORT_RESET, 0, 750); if (retval != 0) { ehci_err (ehci, "port %d reset error %d\n", wIndex + 1, retval); @@ -455,7 +527,7 @@ static int ehci_hub_control ( #endif dbg_port (ehci, "GetStatus", wIndex + 1, temp); // we "know" this alignment is good, caller used kmalloc()... - *((u32 *) buf) = cpu_to_le32 (status); + *((__le32 *) buf) = cpu_to_le32 (status); break; case SetHubFeature: switch (wValue) { @@ -468,6 +540,8 @@ static int ehci_hub_control ( } break; case SetPortFeature: + selector = wIndex >> 8; + wIndex &= 0xff; if (!wIndex || wIndex > ports) goto error; wIndex--; @@ -475,12 +549,15 @@ static int ehci_hub_control ( if (temp & PORT_OWNER) break; + temp &= ~PORT_RWC_BITS; switch (wValue) { case USB_PORT_FEAT_SUSPEND: + if (ehci->no_selective_suspend) + break; if ((temp & PORT_PE) == 0 || (temp & PORT_RESET) != 0) goto error; - if (ehci->hcd.remote_wakeup) + if (device_may_wakeup(&hcd->self.root_hub->dev)) temp |= PORT_WAKE_BITS; writel (temp | PORT_SUSPEND, &ehci->regs->port_status [wIndex]); @@ -498,7 +575,7 @@ static int ehci_hub_control ( * transaction translator built in. */ if ((temp & (PORT_PE|PORT_CONNECT)) == PORT_CONNECT - && !ehci_is_ARC(ehci) + && !ehci_is_TDI(ehci) && PORT_USB11 (temp)) { ehci_dbg (ehci, "port %d low speed --> companion\n", @@ -518,6 +595,22 @@ static int ehci_hub_control ( } writel (temp, &ehci->regs->port_status [wIndex]); break; + + /* For downstream facing ports (these): one hub port is put + * into test mode according to USB2 11.24.2.13, then the hub + * must be reset (which for root hub now means rmmod+modprobe, + * or else system reboot). See EHCI 2.3.9 and 4.14 for info + * about the EHCI-specific stuff. + */ + case USB_PORT_FEAT_TEST: + if (!selector || selector > 5) + goto error; + ehci_quiesce(ehci); + ehci_halt(ehci); + temp |= selector << 16; + writel (temp, &ehci->regs->port_status [wIndex]); + break; + default: goto error; }