summaryrefslogtreecommitdiffstats
path: root/drivers/usb/host
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/host')
-rw-r--r--drivers/usb/host/Kconfig2
-rw-r--r--drivers/usb/host/ehci-dbg.c4
-rw-r--r--drivers/usb/host/ehci-hcd.c86
-rw-r--r--drivers/usb/host/ehci-hub.c106
-rw-r--r--drivers/usb/host/ehci-pci.c42
-rw-r--r--drivers/usb/host/ehci-q.c6
-rw-r--r--drivers/usb/host/ehci.h23
-rw-r--r--drivers/usb/host/ohci-hcd.c35
-rw-r--r--drivers/usb/host/ohci-hub.c172
-rw-r--r--drivers/usb/host/ohci-pnx4008.c9
-rw-r--r--drivers/usb/host/u132-hcd.c14
-rw-r--r--drivers/usb/host/uhci-hcd.c44
12 files changed, 307 insertions, 236 deletions
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index cf10cbc98f8..cc60759083b 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -153,7 +153,7 @@ config USB_U132_HCD
adapter will *NOT* work with PC cards that do not contain an OHCI
controller.
- For those PC cards that contain multiple OHCI controllers only ther
+ For those PC cards that contain multiple OHCI controllers only the
first one is used.
The driver consists of two modules, the "ftdi-elan" module is a
diff --git a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c
index 23b95b2bfe1..34b7a31cd85 100644
--- a/drivers/usb/host/ehci-dbg.c
+++ b/drivers/usb/host/ehci-dbg.c
@@ -754,7 +754,9 @@ show_registers (struct class_device *class_dev, char *buf)
}
if (ehci->reclaim) {
- temp = scnprintf (next, size, "reclaim qh %p\n", ehci->reclaim);
+ temp = scnprintf (next, size, "reclaim qh %p%s\n",
+ ehci->reclaim,
+ ehci->reclaim_ready ? " ready" : "");
size -= temp;
next += temp;
}
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index aac6ec5dd7c..025d3331368 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -111,7 +111,7 @@ static const char hcd_name [] = "ehci_hcd";
#define EHCI_TUNE_MULT_TT 1
#define EHCI_TUNE_FLS 2 /* (small) 256 frame schedule */
-#define EHCI_IAA_MSECS 10 /* arbitrary */
+#define EHCI_IAA_JIFFIES (HZ/100) /* arbitrary; ~10 msec */
#define EHCI_IO_JIFFIES (HZ/10) /* io watchdog > irq_thresh */
#define EHCI_ASYNC_JIFFIES (HZ/20) /* async idle timeout */
#define EHCI_SHRINK_JIFFIES (HZ/200) /* async qh unlink delay */
@@ -126,6 +126,11 @@ static unsigned park = 0;
module_param (park, uint, S_IRUGO);
MODULE_PARM_DESC (park, "park setting; 1-3 back-to-back async packets");
+/* for flakey hardware, ignore overcurrent indicators */
+static int ignore_oc = 0;
+module_param (ignore_oc, bool, S_IRUGO);
+MODULE_PARM_DESC (ignore_oc, "ignore bogus hardware overcurrent indications");
+
#define INTR_MASK (STS_IAA | STS_FATAL | STS_PCD | STS_ERR | STS_INT)
/*-------------------------------------------------------------------------*/
@@ -254,7 +259,6 @@ static void ehci_quiesce (struct ehci_hcd *ehci)
/*-------------------------------------------------------------------------*/
-static void end_unlink_async (struct ehci_hcd *ehci);
static void ehci_work(struct ehci_hcd *ehci);
#include "ehci-hub.c"
@@ -264,37 +268,25 @@ static void ehci_work(struct ehci_hcd *ehci);
/*-------------------------------------------------------------------------*/
-static void ehci_iaa_watchdog (unsigned long param)
+static void ehci_watchdog (unsigned long param)
{
struct ehci_hcd *ehci = (struct ehci_hcd *) param;
unsigned long flags;
- u32 status;
spin_lock_irqsave (&ehci->lock, flags);
- WARN_ON(!ehci->reclaim);
- /* lost IAA irqs wedge things badly; seen first with a vt8235 */
+ /* lost IAA irqs wedge things badly; seen with a vt8235 */
if (ehci->reclaim) {
- status = readl (&ehci->regs->status);
+ u32 status = readl (&ehci->regs->status);
if (status & STS_IAA) {
ehci_vdbg (ehci, "lost IAA\n");
COUNT (ehci->stats.lost_iaa);
writel (STS_IAA, &ehci->regs->status);
- end_unlink_async (ehci);
+ ehci->reclaim_ready = 1;
}
}
- spin_unlock_irqrestore (&ehci->lock, flags);
-}
-
-static void ehci_watchdog (unsigned long param)
-{
- struct ehci_hcd *ehci = (struct ehci_hcd *) param;
- unsigned long flags;
-
- spin_lock_irqsave (&ehci->lock, flags);
-
- /* stop async processing after it's idled a bit */
+ /* stop async processing after it's idled a bit */
if (test_bit (TIMER_ASYNC_OFF, &ehci->actions))
start_unlink_async (ehci, ehci->async);
@@ -345,6 +337,8 @@ static void ehci_port_power (struct ehci_hcd *ehci, int is_on)
static void ehci_work (struct ehci_hcd *ehci)
{
timer_action_done (ehci, TIMER_IO_WATCHDOG);
+ if (ehci->reclaim_ready)
+ end_unlink_async (ehci);
/* another CPU may drop ehci->lock during a schedule scan while
* it reports urb completions. this flag guards against bogus
@@ -379,7 +373,6 @@ static void ehci_stop (struct usb_hcd *hcd)
/* no more interrupts ... */
del_timer_sync (&ehci->watchdog);
- del_timer_sync (&ehci->iaa_watchdog);
spin_lock_irq(&ehci->lock);
if (HC_IS_RUNNING (hcd->state))
@@ -426,10 +419,6 @@ static int ehci_init(struct usb_hcd *hcd)
ehci->watchdog.function = ehci_watchdog;
ehci->watchdog.data = (unsigned long) ehci;
- init_timer(&ehci->iaa_watchdog);
- ehci->iaa_watchdog.function = ehci_iaa_watchdog;
- ehci->iaa_watchdog.data = (unsigned long) ehci;
-
/*
* hw default: 1K periodic list heads, one per frame.
* periodic_size can shrink by USBCMD update if hcc_params allows.
@@ -446,6 +435,7 @@ static int ehci_init(struct usb_hcd *hcd)
ehci->i_thresh = 2 + HCC_ISOC_THRES(hcc_params);
ehci->reclaim = NULL;
+ ehci->reclaim_ready = 0;
ehci->next_uframe = -1;
/*
@@ -556,9 +546,10 @@ static int ehci_run (struct usb_hcd *hcd)
temp = HC_VERSION(readl (&ehci->caps->hc_capbase));
ehci_info (ehci,
- "USB %x.%x started, EHCI %x.%02x, driver %s\n",
+ "USB %x.%x started, EHCI %x.%02x, driver %s%s\n",
((ehci->sbrn & 0xf0)>>4), (ehci->sbrn & 0x0f),
- temp >> 8, temp & 0xff, DRIVER_VERSION);
+ temp >> 8, temp & 0xff, DRIVER_VERSION,
+ ignore_oc ? ", overcurrent ignored" : "");
writel (INTR_MASK, &ehci->regs->intr_enable); /* Turn On Interrupts */
@@ -619,7 +610,7 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd)
/* complete the unlinking of some qh [4.15.2.3] */
if (status & STS_IAA) {
COUNT (ehci->stats.reclaim);
- end_unlink_async (ehci);
+ ehci->reclaim_ready = 1;
bh = 1;
}
@@ -628,9 +619,8 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd)
unsigned i = HCS_N_PORTS (ehci->hcs_params);
/* resume root hub? */
- status = readl (&ehci->regs->command);
- if (!(status & CMD_RUN))
- writel (status | CMD_RUN, &ehci->regs->command);
+ if (!(readl(&ehci->regs->command) & CMD_RUN))
+ usb_hcd_resume_root_hub(hcd);
while (i--) {
int pstatus = readl (&ehci->regs->port_status [i]);
@@ -647,7 +637,6 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd)
*/
ehci->reset_done [i] = jiffies + msecs_to_jiffies (20);
ehci_dbg (ehci, "port %d remote wakeup\n", i + 1);
- usb_hcd_resume_root_hub(hcd);
}
}
@@ -723,14 +712,10 @@ static int ehci_urb_enqueue (
static void unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
{
- // BUG_ON(qh->qh_state != QH_STATE_LINKED);
-
- /* failfast */
- if (!HC_IS_RUNNING (ehci_to_hcd(ehci)->state))
- end_unlink_async (ehci);
-
- /* defer till later if busy */
- else if (ehci->reclaim) {
+ /* if we need to use IAA and it's busy, defer */
+ if (qh->qh_state == QH_STATE_LINKED
+ && ehci->reclaim
+ && HC_IS_RUNNING (ehci_to_hcd(ehci)->state)) {
struct ehci_qh *last;
for (last = ehci->reclaim;
@@ -740,8 +725,12 @@ static void unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
qh->qh_state = QH_STATE_UNLINK_WAIT;
last->reclaim = qh;
- /* start IAA cycle */
- } else
+ /* bypass IAA if the hc can't care */
+ } else if (!HC_IS_RUNNING (ehci_to_hcd(ehci)->state) && ehci->reclaim)
+ end_unlink_async (ehci);
+
+ /* something else might have unlinked the qh by now */
+ if (qh->qh_state == QH_STATE_LINKED)
start_unlink_async (ehci, qh);
}
@@ -763,19 +752,7 @@ static int ehci_urb_dequeue (struct usb_hcd *hcd, struct urb *urb)
qh = (struct ehci_qh *) urb->hcpriv;
if (!qh)
break;
- switch (qh->qh_state) {
- case QH_STATE_LINKED:
- case QH_STATE_COMPLETING:
- unlink_async (ehci, qh);
- break;
- case QH_STATE_UNLINK:
- case QH_STATE_UNLINK_WAIT:
- /* already started */
- break;
- case QH_STATE_IDLE:
- WARN_ON(1);
- break;
- }
+ unlink_async (ehci, qh);
break;
case PIPE_INTERRUPT:
@@ -867,7 +844,6 @@ rescan:
unlink_async (ehci, qh);
/* FALL THROUGH */
case QH_STATE_UNLINK: /* wait for hw to finish? */
- case QH_STATE_UNLINK_WAIT:
idle_timeout:
spin_unlock_irqrestore (&ehci->lock, flags);
schedule_timeout_uninterruptible(1);
diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c
index 2012213c0a2..bfe5f307cba 100644
--- a/drivers/usb/host/ehci-hub.c
+++ b/drivers/usb/host/ehci-hub.c
@@ -34,6 +34,7 @@ static int ehci_bus_suspend (struct usb_hcd *hcd)
{
struct ehci_hcd *ehci = hcd_to_ehci (hcd);
int port;
+ int mask;
if (time_before (jiffies, ehci->next_statechange))
msleep(5);
@@ -48,17 +49,28 @@ static int ehci_bus_suspend (struct usb_hcd *hcd)
}
ehci->command = readl (&ehci->regs->command);
if (ehci->reclaim)
- end_unlink_async (ehci);
+ ehci->reclaim_ready = 1;
ehci_work(ehci);
- /* suspend any active/unsuspended ports, maybe allow wakeup */
+ /* 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 __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;
+ 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
@@ -76,6 +88,13 @@ static int ehci_bus_suspend (struct usb_hcd *hcd)
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);
+
ehci->next_statechange = jiffies + msecs_to_jiffies(10);
spin_unlock_irq (&ehci->lock);
return 0;
@@ -88,7 +107,6 @@ static int ehci_bus_resume (struct usb_hcd *hcd)
struct ehci_hcd *ehci = hcd_to_ehci (hcd);
u32 temp;
int i;
- int intr_enable;
if (time_before (jiffies, ehci->next_statechange))
msleep(5);
@@ -100,31 +118,30 @@ static int ehci_bus_resume (struct usb_hcd *hcd)
* 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");
- /* re-init operational registers in case we lost power */
- if (readl (&ehci->regs->intr_enable) == 0) {
- /* at least some APM implementations will try to deliver
- * IRQs right away, so delay them until we're ready.
- */
- intr_enable = 1;
- writel (0, &ehci->regs->segment);
- writel (ehci->periodic_dma, &ehci->regs->frame_list);
- writel ((u32)ehci->async->qh_dma, &ehci->regs->async_next);
- } else
- intr_enable = 0;
- ehci_dbg(ehci, "resume root hub%s\n",
- intr_enable ? " 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 */
+ /* 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_RWC_BITS
| PORT_WKOC_E | PORT_WKDISC_E | PORT_WKCONN_E);
- if (temp & PORT_SUSPEND) {
+ if (test_bit(i, &ehci->bus_suspended) &&
+ (temp & PORT_SUSPEND)) {
ehci->reset_done [i] = jiffies + msecs_to_jiffies (20);
temp |= PORT_RESUME;
}
@@ -134,11 +151,12 @@ static int ehci_bus_resume (struct usb_hcd *hcd)
mdelay (20);
while (i--) {
temp = readl (&ehci->regs->port_status [i]);
- if ((temp & PORT_SUSPEND) == 0)
- continue;
- temp &= ~(PORT_RWC_BITS | 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);
@@ -157,8 +175,7 @@ static int ehci_bus_resume (struct usb_hcd *hcd)
hcd->state = HC_STATE_RUNNING;
/* Now we can safely re-enable irqs */
- if (intr_enable)
- writel (INTR_MASK, &ehci->regs->intr_enable);
+ writel(INTR_MASK, &ehci->regs->intr_enable);
spin_unlock_irq (&ehci->lock);
return 0;
@@ -218,6 +235,7 @@ 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;
@@ -233,6 +251,18 @@ ehci_hub_status_data (struct usb_hcd *hcd, char *buf)
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)? */
@@ -250,8 +280,7 @@ ehci_hub_status_data (struct usb_hcd *hcd, char *buf)
}
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]))) {
@@ -319,6 +348,7 @@ static int ehci_hub_control (
u32 temp, status;
unsigned long flags;
int retval = 0;
+ unsigned selector;
/*
* FIXME: support SetPortFeatures USB_PORT_FEAT_INDICATOR.
@@ -417,7 +447,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!! */
@@ -506,6 +536,8 @@ static int ehci_hub_control (
}
break;
case SetPortFeature:
+ selector = wIndex >> 8;
+ wIndex &= 0xff;
if (!wIndex || wIndex > ports)
goto error;
wIndex--;
@@ -559,6 +591,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;
}
diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c
index 35e3fab6fc4..4bc7970ba3e 100644
--- a/drivers/usb/host/ehci-pci.c
+++ b/drivers/usb/host/ehci-pci.c
@@ -257,9 +257,7 @@ static int ehci_pci_suspend(struct usb_hcd *hcd, pm_message_t message)
static int ehci_pci_resume(struct usb_hcd *hcd)
{
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
- unsigned port;
struct pci_dev *pdev = to_pci_dev(hcd->self.controller);
- int retval = -EINVAL;
// maybe restore FLADJ
@@ -269,27 +267,19 @@ static int ehci_pci_resume(struct usb_hcd *hcd)
/* Mark hardware accessible again as we are out of D3 state by now */
set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
- /* If CF is clear, we lost PCI Vaux power and need to restart. */
- if (readl(&ehci->regs->configured_flag) != FLAG_CF)
- goto restart;
-
- /* If any port is suspended (or owned by the companion),
- * we know we can/must resume the HC (and mustn't reset it).
- * We just defer that to the root hub code.
+ /* If CF is still set, we maintained PCI Vaux power.
+ * Just undo the effect of ehci_pci_suspend().
*/
- for (port = HCS_N_PORTS(ehci->hcs_params); port > 0; ) {
- u32 status;
- port--;
- status = readl(&ehci->regs->port_status [port]);
- if (!(status & PORT_POWER))
- continue;
- if (status & (PORT_SUSPEND | PORT_RESUME | PORT_OWNER)) {
- usb_hcd_resume_root_hub(hcd);
- return 0;
- }
+ if (readl(&ehci->regs->configured_flag) == FLAG_CF) {
+ int 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);
+ return 0;
}
-restart:
ehci_dbg(ehci, "lost power, restarting\n");
usb_root_hub_lost_power(hcd->self.root_hub);
@@ -303,17 +293,19 @@ restart:
/* emptying the schedule aborts any urbs */
spin_lock_irq(&ehci->lock);
if (ehci->reclaim)
- end_unlink_async (ehci);
+ ehci->reclaim_ready = 1;
ehci_work(ehci);
spin_unlock_irq(&ehci->lock);
- /* restart; khubd will disconnect devices */
- retval = ehci_run(hcd);
-
/* here we "know" root ports should always stay powered */
ehci_port_power(ehci, 1);
- return retval;
+ writel(ehci->command, &ehci->regs->command);
+ writel(FLAG_CF, &ehci->regs->configured_flag);
+ readl(&ehci->regs->command); /* unblock posted writes */
+
+ hcd->state = HC_STATE_SUSPENDED;
+ return 0;
}
#endif
diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c
index 46327272f61..62e46dc60e8 100644
--- a/drivers/usb/host/ehci-q.c
+++ b/drivers/usb/host/ehci-q.c
@@ -967,7 +967,7 @@ static void end_unlink_async (struct ehci_hcd *ehci)
struct ehci_qh *qh = ehci->reclaim;
struct ehci_qh *next;
- iaa_watchdog_done (ehci);
+ timer_action_done (ehci, TIMER_IAA_WATCHDOG);
// qh->hw_next = cpu_to_le32 (qh->qh_dma);
qh->qh_state = QH_STATE_IDLE;
@@ -977,6 +977,7 @@ static void end_unlink_async (struct ehci_hcd *ehci)
/* other unlink(s) may be pending (in QH_STATE_UNLINK_WAIT) */
next = qh->reclaim;
ehci->reclaim = next;
+ ehci->reclaim_ready = 0;
qh->reclaim = NULL;
qh_completions (ehci, qh);
@@ -1051,10 +1052,11 @@ static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
return;
}
+ ehci->reclaim_ready = 0;
cmd |= CMD_IAAD;
writel (cmd, &ehci->regs->command);
(void) readl (&ehci->regs->command);
- iaa_watchdog_start (ehci);
+ timer_action (ehci, TIMER_IAA_WATCHDOG);
}
/*-------------------------------------------------------------------------*/
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
index 6aac39f50e0..74dbc6c8228 100644
--- a/drivers/usb/host/ehci.h
+++ b/drivers/usb/host/ehci.h
@@ -58,6 +58,7 @@ struct ehci_hcd { /* one per controller */
/* async schedule support */
struct ehci_qh *async;
struct ehci_qh *reclaim;
+ unsigned reclaim_ready : 1;
unsigned scanning : 1;
/* periodic schedule support */
@@ -73,6 +74,7 @@ struct ehci_hcd { /* one per controller */
/* per root hub port */
unsigned long reset_done [EHCI_MAX_ROOT_PORTS];
+ unsigned long bus_suspended;
/* per-HC memory pools (could be per-bus, but ...) */
struct dma_pool *qh_pool; /* qh per active urb */
@@ -80,7 +82,6 @@ struct ehci_hcd { /* one per controller */
struct dma_pool *itd_pool; /* itd per iso urb */
struct dma_pool *sitd_pool; /* sitd per split iso urb */
- struct timer_list iaa_watchdog;
struct timer_list watchdog;
unsigned long actions;
unsigned stamp;
@@ -114,21 +115,9 @@ static inline struct usb_hcd *ehci_to_hcd (struct ehci_hcd *ehci)
}
-static inline void
-iaa_watchdog_start (struct ehci_hcd *ehci)
-{
- WARN_ON(timer_pending(&ehci->iaa_watchdog));
- mod_timer (&ehci->iaa_watchdog,
- jiffies + msecs_to_jiffies(EHCI_IAA_MSECS));
-}
-
-static inline void iaa_watchdog_done (struct ehci_hcd *ehci)
-{
- del_timer (&ehci->iaa_watchdog);
-}
-
enum ehci_timer_action {
TIMER_IO_WATCHDOG,
+ TIMER_IAA_WATCHDOG,
TIMER_ASYNC_SHRINK,
TIMER_ASYNC_OFF,
};
@@ -146,6 +135,9 @@ timer_action (struct ehci_hcd *ehci, enum ehci_timer_action action)
unsigned long t;
switch (action) {
+ case TIMER_IAA_WATCHDOG:
+ t = EHCI_IAA_JIFFIES;
+ break;
case TIMER_IO_WATCHDOG:
t = EHCI_IO_JIFFIES;
break;
@@ -162,7 +154,8 @@ timer_action (struct ehci_hcd *ehci, enum ehci_timer_action action)
// async queue SHRINK often precedes IAA. while it's ready
// to go OFF neither can matter, and afterwards the IO
// watchdog stops unless there's still periodic traffic.
- if (time_before_eq(t, ehci->watchdog.expires)
+ if (action != TIMER_IAA_WATCHDOG
+ && t > ehci->watchdog.expires
&& timer_pending (&ehci->watchdog))
return;
mod_timer (&ehci->watchdog, t);
diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c
index 9be6b303e78..a95275a401b 100644
--- a/drivers/usb/host/ohci-hcd.c
+++ b/drivers/usb/host/ohci-hcd.c
@@ -715,13 +715,6 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd)
return IRQ_NOTMINE;
}
- if (ints & OHCI_INTR_RHSC) {
- ohci_vdbg (ohci, "rhsc\n");
- ohci->next_statechange = jiffies + STATECHANGE_DELAY;
- ohci_writel (ohci, OHCI_INTR_RHSC, &regs->intrstatus);
- usb_hcd_poll_rh_status(hcd);
- }
-
if (ints & OHCI_INTR_UE) {
disable (ohci);
ohci_err (ohci, "OHCI Unrecoverable Error, disabled\n");
@@ -731,9 +724,31 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd)
ohci_usb_reset (ohci);
}
- if (ints & OHCI_INTR_RD) {
- ohci_vdbg (ohci, "resume detect\n");
- ohci_writel (ohci, OHCI_INTR_RD, &regs->intrstatus);
+ if (ints & OHCI_INTR_RHSC) {
+ ohci_vdbg(ohci, "rhsc\n");
+ ohci->next_statechange = jiffies + STATECHANGE_DELAY;
+ ohci_writel(ohci, OHCI_INTR_RD | OHCI_INTR_RHSC,
+ &regs->intrstatus);
+
+ /* NOTE: Vendors didn't always make the same implementation
+ * choices for RHSC. Many followed the spec; RHSC triggers
+ * on an edge, like setting and maybe clearing a port status
+ * change bit. With others it's level-triggered, active
+ * until khubd clears all the port status change bits. We'll
+ * always disable it here and rely on polling until khubd
+ * re-enables it.
+ */
+ ohci_writel(ohci, OHCI_INTR_RHSC, &regs->intrdisable);
+ usb_hcd_poll_rh_status(hcd);
+ }
+
+ /* For connect and disconnect events, we expect the controller
+ * to turn on RHSC along with RD. But for remote wakeup events
+ * this might not happen.
+ */
+ else if (ints & OHCI_INTR_RD) {
+ ohci_vdbg(ohci, "resume detect\n");
+ ohci_writel(ohci, OHCI_INTR_RD, &regs->intrstatus);
hcd->poll_rh = 1;
if (ohci->autostop) {
spin_lock (&ohci->lock);
diff --git a/drivers/usb/host/ohci-hub.c b/drivers/usb/host/ohci-hub.c
index 6f113596af6..2441642cb7b 100644
--- a/drivers/usb/host/ohci-hub.c
+++ b/drivers/usb/host/ohci-hub.c
@@ -41,7 +41,11 @@ 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);
+ spin_lock_irq(&ohci->lock);
+ if (!ohci->autostop)
+ del_timer(&hcd->rh_timer); /* Prevent next poll */
+ ohci_writel(ohci, OHCI_INTR_RHSC, &ohci->regs->intrenable);
+ spin_unlock_irq(&ohci->lock);
}
#define OHCI_SCHED_ENABLES \
@@ -50,6 +54,9 @@ static void ohci_rhsc_enable (struct usb_hcd *hcd)
static void dl_done_list (struct ohci_hcd *);
static void finish_unlinks (struct ohci_hcd *, u16);
+#ifdef CONFIG_PM
+static int ohci_restart(struct ohci_hcd *ohci);
+
static int ohci_rh_suspend (struct ohci_hcd *ohci, int autostop)
__releases(ohci->lock)
__acquires(ohci->lock)
@@ -132,8 +139,6 @@ static inline struct ed *find_head (struct ed *ed)
return ed;
}
-static int ohci_restart (struct ohci_hcd *ohci);
-
/* caller has locked the root hub */
static int ohci_rh_resume (struct ohci_hcd *ohci)
__releases(ohci->lock)
@@ -169,7 +174,8 @@ __acquires(ohci->lock)
break;
case OHCI_USB_RESUME:
/* HCFS changes sometime after INTR_RD */
- ohci_info (ohci, "wakeup\n");
+ ohci_dbg(ohci, "%swakeup root hub\n",
+ autostopped ? "auto-" : "");
break;
case OHCI_USB_OPER:
/* this can happen after resuming a swsusp snapshot */
@@ -180,7 +186,6 @@ __acquires(ohci->lock)
ohci_dbg (ohci, "lost power\n");
status = -EBUSY;
}
-#ifdef CONFIG_PM
if (status == -EBUSY) {
if (!autostopped) {
spin_unlock_irq (&ohci->lock);
@@ -190,25 +195,12 @@ __acquires(ohci->lock)
}
return status;
}
-#endif
if (status != -EINPROGRESS)
return status;
if (autostopped)
goto skip_resume;
spin_unlock_irq (&ohci->lock);
- temp = ohci->num_ports;
- while (temp--) {
- u32 stat = ohci_readl (ohci,
- &ohci->regs->roothub.portstatus [temp]);
-
- /* force global, not selective, resume */
- if (!(stat & RH_PS_PSS))
- continue;
- ohci_writel (ohci, RH_PS_POCI,
- &ohci->regs->roothub.portstatus [temp]);
- }
-
/* Some controllers (lucent erratum) need extra-long delays */
msleep (20 /* usb 11.5.1.10 */ + 12 /* 32 msec counter */ + 1);
@@ -216,6 +208,7 @@ __acquires(ohci->lock)
temp &= OHCI_CTRL_HCFS;
if (temp != OHCI_USB_RESUME) {
ohci_err (ohci, "controller won't resume\n");
+ spin_lock_irq(&ohci->lock);
return -EBUSY;
}
@@ -295,8 +288,6 @@ skip_resume:
return 0;
}
-#ifdef CONFIG_PM
-
static int ohci_bus_suspend (struct usb_hcd *hcd)
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
@@ -334,6 +325,83 @@ static int ohci_bus_resume (struct usb_hcd *hcd)
return rc;
}
+/* Carry out polling-, autostop-, and autoresume-related state changes */
+static int ohci_root_hub_state_changes(struct ohci_hcd *ohci, int changed,
+ int any_connected)
+{
+ int poll_rh = 1;
+
+ switch (ohci->hc_control & OHCI_CTRL_HCFS) {
+
+ case OHCI_USB_OPER:
+ /* keep on polling until we know a device is connected
+ * and RHSC is enabled */
+ if (!ohci->autostop) {
+ if (any_connected ||
+ !device_may_wakeup(&ohci_to_hcd(ohci)
+ ->self.root_hub->dev)) {
+ if (ohci_readl(ohci, &ohci->regs->intrenable) &
+ OHCI_INTR_RHSC)
+ poll_rh = 0;
+ } else {
+ ohci->autostop = 1;
+ ohci->next_statechange = jiffies + HZ;
+ }
+
+ /* if no devices have been attached for one second, autostop */
+ } else {
+ if (changed || any_connected) {
+ ohci->autostop = 0;
+ ohci->next_statechange = jiffies +
+ STATECHANGE_DELAY;
+ } else if (time_after_eq(jiffies,
+ ohci->next_statechange)
+ && !ohci->ed_rm_list
+ && !(ohci->hc_control &
+ OHCI_SCHED_ENABLES)) {
+ ohci_rh_suspend(ohci, 1);
+ }
+ }
+ break;
+
+ /* if there is a port change, autostart or ask to be resumed */
+ case OHCI_USB_SUSPEND:
+ case OHCI_USB_RESUME:
+ if (changed) {
+ if (ohci->autostop)
+ ohci_rh_resume(ohci);
+ else
+ usb_hcd_resume_root_hub(ohci_to_hcd(ohci));
+ } else {
+ /* everything is idle, no need for polling */
+ poll_rh = 0;
+ }
+ break;
+ }
+ return poll_rh;
+}
+
+#else /* CONFIG_PM */
+
+static inline int ohci_rh_resume(struct ohci_hcd *ohci)
+{
+ return 0;
+}
+
+/* Carry out polling-related state changes.
+ * autostop isn't used when CONFIG_PM is turned off.
+ */
+static int ohci_root_hub_state_changes(struct ohci_hcd *ohci, int changed,
+ int any_connected)
+{
+ int poll_rh = 1;
+
+ /* keep on polling until RHSC is enabled */
+ if (ohci_readl(ohci, &ohci->regs->intrenable) & OHCI_INTR_RHSC)
+ poll_rh = 0;
+ return poll_rh;
+}
+
#endif /* CONFIG_PM */
/*-------------------------------------------------------------------------*/
@@ -345,7 +413,7 @@ ohci_hub_status_data (struct usb_hcd *hcd, char *buf)
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
int i, changed = 0, length = 1;
- int any_connected = 0, rhsc_enabled = 1;
+ int any_connected = 0;
unsigned long flags;
spin_lock_irqsave (&ohci->lock, flags);
@@ -386,66 +454,8 @@ ohci_hub_status_data (struct usb_hcd *hcd, char *buf)
}
}
- /* 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. If it's still
- * set (level-triggered) we must disable it and rely on
- * polling until khubd re-enables it.
- */
- if (ohci_readl (ohci, &ohci->regs->intrstatus) & OHCI_INTR_RHSC) {
- ohci_writel (ohci, OHCI_INTR_RHSC, &ohci->regs->intrdisable);
- (void) ohci_readl (ohci, &ohci->regs->intrdisable);
- rhsc_enabled = 0;
- }
- hcd->poll_rh = 1;
-
- /* carry out appropriate state changes */
- switch (ohci->hc_control & OHCI_CTRL_HCFS) {
-
- case OHCI_USB_OPER:
- /* keep on polling until we know a device is connected
- * and RHSC is enabled */
- if (!ohci->autostop) {
- if (any_connected) {
- if (rhsc_enabled)
- hcd->poll_rh = 0;
- } else {
- ohci->autostop = 1;
- ohci->next_statechange = jiffies + HZ;
- }
-
- /* if no devices have been attached for one second, autostop */
- } else {
- if (changed || any_connected) {
- ohci->autostop = 0;
- ohci->next_statechange = jiffies +
- STATECHANGE_DELAY;
- } else if (time_after_eq (jiffies,
- ohci->next_statechange)
- && !ohci->ed_rm_list
- && !(ohci->hc_control &
- OHCI_SCHED_ENABLES)) {
- ohci_rh_suspend (ohci, 1);
- }
- }
- break;
-
- /* if there is a port change, autostart or ask to be resumed */
- case OHCI_USB_SUSPEND:
- case OHCI_USB_RESUME:
- if (changed) {
- if (ohci->autostop)
- ohci_rh_resume (ohci);
- else
- usb_hcd_resume_root_hub (hcd);
- } else {
- /* everything is idle, no need for polling */
- hcd->poll_rh = 0;
- }
- break;
- }
+ hcd->poll_rh = ohci_root_hub_state_changes(ohci, changed,
+ any_connected);
done:
spin_unlock_irqrestore (&ohci->lock, flags);
diff --git a/drivers/usb/host/ohci-pnx4008.c b/drivers/usb/host/ohci-pnx4008.c
index 82cb22f002e..2dbb7741490 100644
--- a/drivers/usb/host/ohci-pnx4008.c
+++ b/drivers/usb/host/ohci-pnx4008.c
@@ -262,6 +262,7 @@ static const struct hc_driver ohci_pnx4008_hc_driver = {
*/
.start = ohci_pnx4008_start,
.stop = ohci_stop,
+ .shutdown = ohci_shutdown,
/*
* managing i/o requests and associated device resources
@@ -280,7 +281,11 @@ static const struct hc_driver ohci_pnx4008_hc_driver = {
*/
.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,
+#endif
.start_port_reset = ohci_start_port_reset,
};
@@ -410,8 +415,6 @@ static int __devinit usb_hcd_pnx4008_probe(struct platform_device *pdev)
goto out4;
}
- hcd->self.hcpriv = (void *)hcd;
-
pnx4008_start_hc();
platform_set_drvdata(pdev, hcd);
ohci = hcd_to_ohci(hcd);
diff --git a/drivers/usb/host/u132-hcd.c b/drivers/usb/host/u132-hcd.c
index 32c635ecbf3..ef54e310bfc 100644
--- a/drivers/usb/host/u132-hcd.c
+++ b/drivers/usb/host/u132-hcd.c
@@ -71,7 +71,7 @@ static int distrust_firmware = 1;
module_param(distrust_firmware, bool, 0);
MODULE_PARM_DESC(distrust_firmware, "true to distrust firmware power/overcurren"
"t setup");
-DECLARE_WAIT_QUEUE_HEAD(u132_hcd_wait);
+static DECLARE_WAIT_QUEUE_HEAD(u132_hcd_wait);
/*
* u132_module_lock exists to protect access to global variables
*
@@ -205,13 +205,9 @@ struct u132 {
struct u132_port port[MAX_U132_PORTS];
struct u132_endp *endp[MAX_U132_ENDPS];
};
-int usb_ftdi_elan_read_reg(struct platform_device *pdev, u32 *data);
-int usb_ftdi_elan_read_pcimem(struct platform_device *pdev, u8 addressofs,
- u8 width, u32 *data);
-int usb_ftdi_elan_write_pcimem(struct platform_device *pdev, u8 addressofs,
- u8 width, u32 data);
+
/*
-* these can not be inlines because we need the structure offset!!
+* these cannot be inlines because we need the structure offset!!
* Does anyone have a better way?????
*/
#define u132_read_pcimem(u132, member, data) \
@@ -3045,7 +3041,7 @@ static struct hc_driver u132_hc_driver = {
* This function may be called by the USB core whilst the "usb_all_devices_rwsem"
* is held for writing, thus this module must not call usb_remove_hcd()
* synchronously - but instead should immediately stop activity to the
-* device and ansynchronously call usb_remove_hcd()
+* device and asynchronously call usb_remove_hcd()
*/
static int __devexit u132_remove(struct platform_device *pdev)
{
@@ -3241,7 +3237,7 @@ static int u132_resume(struct platform_device *pdev)
#define u132_resume NULL
#endif
/*
-* this driver is loaded explicitely by ftdi_u132
+* this driver is loaded explicitly by ftdi_u132
*
* the platform_driver struct is static because it is per type of module
*/
diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c
index 45ee6920a85..226bf3de8ed 100644
--- a/drivers/usb/host/uhci-hcd.c
+++ b/drivers/usb/host/uhci-hcd.c
@@ -40,6 +40,7 @@
#include <linux/dma-mapping.h>
#include <linux/usb.h>
#include <linux/bitops.h>
+#include <linux/dmi.h>
#include <asm/uaccess.h>
#include <asm/io.h>
@@ -196,12 +197,42 @@ static int resume_detect_interrupts_are_broken(struct uhci_hcd *uhci)
return 0;
}
+static int remote_wakeup_is_broken(struct uhci_hcd *uhci)
+{
+ static struct dmi_system_id broken_wakeup_table[] = {
+ {
+ .ident = "Asus A7V8X",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK"),
+ DMI_MATCH(DMI_BOARD_NAME, "A7V8X"),
+ DMI_MATCH(DMI_BOARD_VERSION, "REV 1.xx"),
+ }
+ },
+ { }
+ };
+ int port;
+
+ /* One of Asus's motherboards has a bug which causes it to
+ * wake up immediately from suspend-to-RAM if any of the ports
+ * are connected. In such cases we will not set EGSM.
+ */
+ if (dmi_check_system(broken_wakeup_table)) {
+ for (port = 0; port < uhci->rh_numports; ++port) {
+ if (inw(uhci->io_addr + USBPORTSC1 + port * 2) &
+ USBPORTSC_CCS)
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
static void suspend_rh(struct uhci_hcd *uhci, enum uhci_rh_state new_state)
__releases(uhci->lock)
__acquires(uhci->lock)
{
int auto_stop;
- int int_enable;
+ int int_enable, egsm_enable;
auto_stop = (new_state == UHCI_RH_AUTO_STOPPED);
dev_dbg(&uhci_to_hcd(uhci)->self.root_hub->dev,
@@ -217,15 +248,18 @@ __acquires(uhci->lock)
}
/* Enable resume-detect interrupts if they work.
- * Then enter Global Suspend mode, still configured.
+ * Then enter Global Suspend mode if _it_ works, still configured.
*/
+ egsm_enable = USBCMD_EGSM;
uhci->working_RD = 1;
int_enable = USBINTR_RESUME;
- if (resume_detect_interrupts_are_broken(uhci)) {
+ if (remote_wakeup_is_broken(uhci))
+ egsm_enable = 0;
+ if (resume_detect_interrupts_are_broken(uhci) || !egsm_enable)
uhci->working_RD = int_enable = 0;
- }
+
outw(int_enable, uhci->io_addr + USBINTR);
- outw(USBCMD_EGSM | USBCMD_CF, uhci->io_addr + USBCMD);
+ outw(egsm_enable | USBCMD_CF, uhci->io_addr + USBCMD);
mb();
udelay(5);