X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fusb%2Fhost%2Fehci-hcd.c;h=e25adf501967c50c357081c8c6c3fffe18bb801f;hb=c7b5ebbddf7bcd3651947760f423e3783bbe6573;hp=7a52853df491480ccd93cb00bb313c709e350ef2;hpb=5273a3df6485dc2ad6aa7ddd441b9a21970f003b;p=linux-2.6.git diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 7a52853df..e25adf501 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2002 by David Brownell + * Copyright (c) 2000-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 @@ -69,6 +69,7 @@ * * HISTORY: * + * 2004-05-10 Root hub and PCI suspend/resume support; remote wakeup. (db) * 2004-02-24 Replace pci_* with generic dma_* API calls (dsaxena@plexity.net) * 2003-12-29 Rewritten high speed iso transfer support (by Michal Sojka, * , updates by DB). @@ -96,7 +97,7 @@ * 2001-June Works with usb-storage and NEC EHCI on 2.4 */ -#define DRIVER_VERSION "2003-Dec-29" +#define DRIVER_VERSION "2004-May-10" #define DRIVER_AUTHOR "David Brownell" #define DRIVER_DESC "USB 2.0 'Enhanced' Host Controller (EHCI) Driver" @@ -128,7 +129,7 @@ static int log2_irq_thresh = 0; // 0 to 6 module_param (log2_irq_thresh, int, S_IRUGO); MODULE_PARM_DESC (log2_irq_thresh, "log2 IRQ latency, 1-64 microframes"); -#define INTR_MASK (STS_IAA | STS_FATAL | STS_ERR | STS_INT) +#define INTR_MASK (STS_IAA | STS_FATAL | STS_PCD | STS_ERR | STS_INT) /*-------------------------------------------------------------------------*/ @@ -154,7 +155,7 @@ MODULE_PARM_DESC (log2_irq_thresh, "log2 IRQ latency, 1-64 microframes"); * before driver shutdown. But it also seems to be caused by bugs in cardbus * bridge shutdown: shutting down the bridge before the devices using it. */ -static int handshake (u32 *ptr, u32 mask, u32 done, int usec) +static int handshake (u32 __iomem *ptr, u32 mask, u32 done, int usec) { u32 result; @@ -201,6 +202,7 @@ static int ehci_reset (struct ehci_hcd *ehci) dbg_cmd (ehci, "reset", command); writel (command, &ehci->regs->command); ehci->hcd.state = USB_STATE_HALT; + ehci->next_statechange = jiffies; return handshake (&ehci->regs->command, CMD_RESET, 0, 250 * 1000); } @@ -241,6 +243,8 @@ static void ehci_ready (struct ehci_hcd *ehci) /*-------------------------------------------------------------------------*/ +static void ehci_work(struct ehci_hcd *ehci, struct pt_regs *regs); + #include "ehci-hub.c" #include "ehci-mem.c" #include "ehci-q.c" @@ -248,8 +252,6 @@ static void ehci_ready (struct ehci_hcd *ehci) /*-------------------------------------------------------------------------*/ -static void ehci_work(struct ehci_hcd *ehci, struct pt_regs *regs); - static void ehci_watchdog (unsigned long param) { struct ehci_hcd *ehci = (struct ehci_hcd *) param; @@ -287,22 +289,26 @@ static void ehci_watchdog (unsigned long param) static int bios_handoff (struct ehci_hcd *ehci, int where, u32 cap) { if (cap & (1 << 16)) { - int msec = 500; + int msec = 5000; + struct pci_dev *pdev = to_pci_dev(ehci->hcd.self.controller); /* request handoff to OS */ - cap &= 1 << 24; - pci_write_config_dword (to_pci_dev(ehci->hcd.self.controller), where, cap); + cap |= 1 << 24; + pci_write_config_dword(pdev, where, cap); /* and wait a while for it to happen */ do { - wait_ms (10); + msleep(10); msec -= 10; - pci_read_config_dword (to_pci_dev(ehci->hcd.self.controller), where, &cap); + pci_read_config_dword(pdev, where, &cap); } while ((cap & (1 << 16)) && msec); if (cap & (1 << 16)) { ehci_err (ehci, "BIOS handoff failed (%d, %04x)\n", where, cap); - return 1; + // some BIOS versions seem buggy... + // return 1; + ehci_warn (ehci, "continuing after BIOS bug...\n"); + return 0; } ehci_dbg (ehci, "BIOS handoff succeeded\n"); } @@ -334,8 +340,8 @@ static int ehci_hc_reset (struct usb_hcd *hcd) spin_lock_init (&ehci->lock); - ehci->caps = (struct ehci_caps *) hcd->regs; - ehci->regs = (struct ehci_regs *) (hcd->regs + + ehci->caps = hcd->regs; + ehci->regs = (hcd->regs + HC_LENGTH (readl (&ehci->caps->hc_capbase))); dbg_hcs_params (ehci, "reset"); dbg_hcc_params (ehci, "reset"); @@ -412,7 +418,7 @@ static int ehci_start (struct usb_hcd *hcd) else // N microframes cached ehci->i_thresh = 2 + HCC_ISOC_THRES (hcc_params); - ehci->reclaim = 0; + ehci->reclaim = NULL; ehci->next_uframe = -1; /* controller state: unknown --> reset */ @@ -422,18 +428,22 @@ static int ehci_start (struct usb_hcd *hcd) ehci_mem_cleanup (ehci); return retval; } - writel (INTR_MASK, &ehci->regs->intr_enable); writel (ehci->periodic_dma, &ehci->regs->frame_list); #ifdef CONFIG_PCI if (hcd->self.controller->bus == &pci_bus_type) { struct pci_dev *pdev; + u16 port_wake; pdev = to_pci_dev(hcd->self.controller); /* Serial Bus Release Number is at PCI 0x60 offset */ pci_read_config_byte(pdev, 0x60, &sbrn); + /* port wake capability, reported by boot firmware */ + pci_read_config_word(pdev, 0x62, &port_wake); + hcd->can_wakeup = (port_wake & 1) != 0; + /* help hc dma work well with cachelines */ pci_set_mwi (pdev); @@ -455,7 +465,7 @@ static int ehci_start (struct usb_hcd *hcd) * its dummy is used in hw_alt_next of many tds, to prevent the qh * from automatically advancing to the next td after short reads. */ - ehci->async->qh_next.qh = 0; + ehci->async->qh_next.qh = NULL; ehci->async->hw_next = QH_NEXT (ehci->async->qh_dma); ehci->async->hw_info1 = cpu_to_le32 (QH_HEAD); ehci->async->hw_token = cpu_to_le32 (QTD_STS_HALT); @@ -513,7 +523,7 @@ static int ehci_start (struct usb_hcd *hcd) /* wire up the root hub */ bus = hcd_to_bus (hcd); - bus->root_hub = udev = usb_alloc_dev (NULL, bus, 0); + udev = usb_alloc_dev (NULL, bus, 0); if (!udev) { done2: ehci_mem_cleanup (ehci); @@ -523,7 +533,8 @@ done2: /* * Start, enabling full USB 2.0 functionality ... usb 1.1 devices * are explicitly handed to companion controller(s), so no TT is - * involved with the root hub. + * involved with the root hub. (Except where one is integrated, + * and there's no companion controller unless maybe for USB OTG.) */ ehci->reboot_notifier.notifier_call = ehci_reboot; register_reboot_notifier (&ehci->reboot_notifier); @@ -546,16 +557,17 @@ done2: * and device drivers may start it running. */ udev->speed = USB_SPEED_HIGH; - if (hcd_register_root (hcd) != 0) { + if (hcd_register_root (udev, hcd) != 0) { if (hcd->state == USB_STATE_RUNNING) ehci_ready (ehci); ehci_reset (ehci); - bus->root_hub = 0; usb_put_dev (udev); retval = -ENODEV; goto done2; } + writel (INTR_MASK, &ehci->regs->intr_enable); /* Turn On Interrupts */ + create_debug_files (ehci); return 0; @@ -566,6 +578,7 @@ done2: static void ehci_stop (struct usb_hcd *hcd) { struct ehci_hcd *ehci = hcd_to_ehci (hcd); + u8 rh_ports, port; ehci_dbg (ehci, "stop\n"); @@ -577,7 +590,16 @@ static void ehci_stop (struct usb_hcd *hcd) return; } del_timer_sync (&ehci->watchdog); + + /* Turn off port power on all root hub ports. */ + rh_ports = HCS_N_PORTS (ehci->hcs_params); + for (port = 1; port <= rh_ports; port++) { + ehci_hub_control(hcd, ClearPortFeature, USB_PORT_FEAT_POWER, + port, NULL, 0); + } + ehci_reset (ehci); + writel (0, &ehci->regs->intr_enable); /* let companion controllers work when we aren't */ writel (0, &ehci->regs->configured_flag); @@ -615,41 +637,26 @@ static int ehci_get_frame (struct usb_hcd *hcd) /* suspend/resume, section 4.3 */ +/* These routines rely on PCI to handle powerdown and wakeup, and + * transceivers that don't need any software attention to set up + * the right sort of wakeup. + */ + static int ehci_suspend (struct usb_hcd *hcd, u32 state) { struct ehci_hcd *ehci = hcd_to_ehci (hcd); - int ports; - int i; - - ehci_dbg (ehci, "suspend to %d\n", state); - ports = HCS_N_PORTS (ehci->hcs_params); + while (time_before (jiffies, ehci->next_statechange)) + msleep (100); - // FIXME: This assumes what's probably a D3 level suspend... - - // FIXME: usb wakeup events on this bus should resume the machine. - // pci config register PORTWAKECAP controls which ports can do it; - // bios may have initted the register... - - /* suspend each port, then stop the hc */ - for (i = 0; i < ports; i++) { - int temp = readl (&ehci->regs->port_status [i]); - - if ((temp & PORT_PE) == 0 - || (temp & PORT_OWNER) != 0) - continue; - ehci_dbg (ehci, "suspend port %d", i); - temp |= PORT_SUSPEND; - writel (temp, &ehci->regs->port_status [i]); - } - - if (hcd->state == USB_STATE_RUNNING) - ehci_ready (ehci); - writel (readl (&ehci->regs->command) & ~CMD_RUN, &ehci->regs->command); - -// save pci FLADJ value +#ifdef CONFIG_USB_SUSPEND + (void) usb_suspend_device (hcd->self.root_hub, state); +#else + /* FIXME lock root hub */ + (void) ehci_hub_suspend (hcd); +#endif - /* who tells PCI to reduce power consumption? */ + // save (PCI) FLADJ in case of Vaux power loss return 0; } @@ -657,40 +664,22 @@ static int ehci_suspend (struct usb_hcd *hcd, u32 state) static int ehci_resume (struct usb_hcd *hcd) { struct ehci_hcd *ehci = hcd_to_ehci (hcd); - int ports; - int i; - - ehci_dbg (ehci, "resume\n"); - - ports = HCS_N_PORTS (ehci->hcs_params); - - // FIXME: if controller didn't retain state, - // return and let generic code clean it up - // test configured_flag ? - - /* resume HC and each port */ -// restore pci FLADJ value - // khubd and drivers will set HC running, if needed; - hcd->state = USB_STATE_RUNNING; - // FIXME Philips/Intel/... etc don't really have a "READY" - // state ... turn on CMD_RUN too - for (i = 0; i < ports; i++) { - int temp = readl (&ehci->regs->port_status [i]); - - if ((temp & PORT_PE) == 0 - || (temp & PORT_SUSPEND) != 0) - continue; - ehci_dbg (ehci, "resume port %d", i); - temp |= PORT_RESUME; - writel (temp, &ehci->regs->port_status [i]); - readl (&ehci->regs->command); /* unblock posted writes */ - - wait_ms (20); - temp &= ~PORT_RESUME; - writel (temp, &ehci->regs->port_status [i]); - } - readl (&ehci->regs->command); /* unblock posted writes */ - return 0; + int retval; + + // maybe restore (PCI) FLADJ + + while (time_before (jiffies, ehci->next_statechange)) + msleep (100); + +#ifdef CONFIG_USB_SUSPEND + retval = usb_resume_device (hcd->self.root_hub); +#else + /* FIXME lock root hub */ + retval = ehci_hub_resume (hcd); +#endif + if (retval == 0) + hcd->self.controller->power.power_state = 0; + return retval; } #endif @@ -706,9 +695,18 @@ static void ehci_work (struct ehci_hcd *ehci, struct pt_regs *regs) timer_action_done (ehci, TIMER_IO_WATCHDOG); if (ehci->reclaim_ready) end_unlink_async (ehci, regs); + + /* another CPU may drop ehci->lock during a schedule scan while + * it reports urb completions. this flag guards against bogus + * attempts at re-entrant schedule scanning. + */ + if (ehci->scanning) + return; + ehci->scanning = 1; scan_async (ehci, regs); if (ehci->next_uframe != -1) scan_periodic (ehci, regs); + ehci->scanning = 0; /* the IO watchdog guards against hardware or driver bugs that * misplace IRQs, and should let us run completely without IRQs. @@ -730,12 +728,6 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd, struct pt_regs *regs) status = readl (&ehci->regs->status); - /* shared irq */ - if (status == 0) { - spin_unlock (&ehci->lock); - return IRQ_NONE; - } - /* e.g. cardbus physical eject */ if (status == ~(u32) 0) { ehci_dbg (ehci, "device removed\n"); @@ -743,8 +735,10 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd, struct pt_regs *regs) } status &= INTR_MASK; - if (!status) /* irq sharing? */ - goto done; + if (!status) { /* irq sharing? */ + spin_unlock(&ehci->lock); + return IRQ_NONE; + } /* clear (just) interrupts */ writel (status, &ehci->regs->status); @@ -752,7 +746,7 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd, struct pt_regs *regs) bh = 0; #ifdef EHCI_VERBOSE_DEBUG - /* unrequested/ignored: Port Change Detect, Frame List Rollover */ + /* unrequested/ignored: Frame List Rollover */ dbg_status (ehci, "irq", status); #endif @@ -774,6 +768,34 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd, struct pt_regs *regs) bh = 1; } + /* remote wakeup [4.3.1] */ + if ((status & STS_PCD) && ehci->hcd.remote_wakeup) { + 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); + + while (i--) { + status = readl (&ehci->regs->port_status [i]); + if (status & PORT_OWNER) + continue; + if (!(status & PORT_RESUME) + || ehci->reset_done [i] != 0) + continue; + + /* start 20 msec resume signaling from this port, + * and make khubd collect PORT_STAT_C_SUSPEND to + * stop that signaling. + */ + ehci->reset_done [i] = jiffies + msecs_to_jiffies (20); + mod_timer (&ehci->hcd.rh_timer, + ehci->reset_done [i] + 1); + ehci_dbg (ehci, "port %d remote wakeup\n", i + 1); + } + } + /* PCI errors [4.15.2.4] */ if (unlikely ((status & STS_FATAL) != 0)) { ehci_err (ehci, "fatal error\n"); @@ -787,7 +809,6 @@ dead: if (bh) ehci_work (ehci, regs); -done: spin_unlock (&ehci->lock); return IRQ_HANDLED; } @@ -814,7 +835,6 @@ static int ehci_urb_enqueue ( struct ehci_hcd *ehci = hcd_to_ehci (hcd); struct list_head qtd_list; - urb->transfer_flags &= ~EHCI_STATE_UNLINK; INIT_LIST_HEAD (&qtd_list); switch (usb_pipetype (urb->pipe)) { @@ -914,7 +934,6 @@ static int ehci_urb_dequeue (struct usb_hcd *hcd, struct urb *urb) // wait till next completion, do it then. // completion irqs can wait up to 1024 msec, - urb->transfer_flags |= EHCI_STATE_UNLINK; break; } spin_unlock_irqrestore (&ehci->lock, flags); @@ -965,7 +984,7 @@ idle_timeout: goto rescan; case QH_STATE_IDLE: /* fully unlinked */ if (list_empty (&qh->qtd_list)) { - qh_put (ehci, qh); + qh_put (qh); break; } /* else FALL THROUGH */ @@ -978,7 +997,7 @@ idle_timeout: list_empty (&qh->qtd_list) ? "" : "(has tds)"); break; } - dev->ep [epnum] = 0; + dev->ep[epnum] = NULL; done: spin_unlock_irqrestore (&ehci->lock, flags); return; @@ -1029,6 +1048,8 @@ static const struct hc_driver ehci_driver = { */ .hub_status_data = ehci_hub_status_data, .hub_control = ehci_hub_control, + .hub_suspend = ehci_hub_suspend, + .hub_resume = ehci_hub_resume, }; /*-------------------------------------------------------------------------*/