diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/usb/host/ehci-hcd.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/usb/host/ehci-hcd.c')
-rw-r--r-- | drivers/usb/host/ehci-hcd.c | 1261 |
1 files changed, 1261 insertions, 0 deletions
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c new file mode 100644 index 00000000000..a63bb792e2c --- /dev/null +++ b/drivers/usb/host/ehci-hcd.c @@ -0,0 +1,1261 @@ +/* + * 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 + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/config.h> + +#ifdef CONFIG_USB_DEBUG + #define DEBUG +#else + #undef DEBUG +#endif + +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/dmapool.h> +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/ioport.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/smp_lock.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/timer.h> +#include <linux/list.h> +#include <linux/interrupt.h> +#include <linux/reboot.h> +#include <linux/usb.h> +#include <linux/moduleparam.h> +#include <linux/dma-mapping.h> + +#include "../core/hcd.h" + +#include <asm/byteorder.h> +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/system.h> +#include <asm/unaligned.h> + + +/*-------------------------------------------------------------------------*/ + +/* + * EHCI hc_driver implementation ... experimental, incomplete. + * Based on the final 1.0 register interface specification. + * + * USB 2.0 shows up in upcoming www.pcmcia.org technology. + * First was PCMCIA, like ISA; then CardBus, which is PCI. + * Next comes "CardBay", using USB 2.0 signals. + * + * Contains additional contributions by Brad Hards, Rory Bolt, and others. + * Special thanks to Intel and VIA for providing host controllers to + * test this driver on, and Cypress (including In-System Design) for + * providing early devices for those host controllers to talk to! + * + * 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, + * <sojkam@centrum.cz>, updates by DB). + * + * 2002-11-29 Correct handling for hw async_next register. + * 2002-08-06 Handling for bulk and interrupt transfers is mostly shared; + * only scheduling is different, no arbitrary limitations. + * 2002-07-25 Sanity check PCI reads, mostly for better cardbus support, + * clean up HC run state handshaking. + * 2002-05-24 Preliminary FS/LS interrupts, using scheduling shortcuts + * 2002-05-11 Clear TT errors for FS/LS ctrl/bulk. Fill in some other + * missing pieces: enabling 64bit dma, handoff from BIOS/SMM. + * 2002-05-07 Some error path cleanups to report better errors; wmb(); + * use non-CVS version id; better iso bandwidth claim. + * 2002-04-19 Control/bulk/interrupt submit no longer uses giveback() on + * errors in submit path. Bugfixes to interrupt scheduling/processing. + * 2002-03-05 Initial high-speed ISO support; reduce ITD memory; shift + * more checking to generic hcd framework (db). Make it work with + * Philips EHCI; reduce PCI traffic; shorten IRQ path (Rory Bolt). + * 2002-01-14 Minor cleanup; version synch. + * 2002-01-08 Fix roothub handoff of FS/LS to companion controllers. + * 2002-01-04 Control/Bulk queuing behaves. + * + * 2001-12-12 Initial patch version for Linux 2.5.1 kernel. + * 2001-June Works with usb-storage and NEC EHCI on 2.4 + */ + +#define DRIVER_VERSION "10 Dec 2004" +#define DRIVER_AUTHOR "David Brownell" +#define DRIVER_DESC "USB 2.0 'Enhanced' Host Controller (EHCI) Driver" + +static const char hcd_name [] = "ehci_hcd"; + + +#undef EHCI_VERBOSE_DEBUG +#undef EHCI_URB_TRACE + +#ifdef DEBUG +#define EHCI_STATS +#endif + +/* magic numbers that can affect system performance */ +#define EHCI_TUNE_CERR 3 /* 0-3 qtd retries; 0 == don't stop */ +#define EHCI_TUNE_RL_HS 4 /* nak throttle; see 4.9 */ +#define EHCI_TUNE_RL_TT 0 +#define EHCI_TUNE_MULT_HS 1 /* 1-3 transactions/uframe; 4.10.3 */ +#define EHCI_TUNE_MULT_TT 1 +#define EHCI_TUNE_FLS 2 /* (small) 256 frame schedule */ + +#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 */ + +/* Initial IRQ latency: faster than hw default */ +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"); + +/* initial park setting: slower than hw default */ +static unsigned park = 0; +module_param (park, uint, S_IRUGO); +MODULE_PARM_DESC (park, "park setting; 1-3 back-to-back async packets"); + +#define INTR_MASK (STS_IAA | STS_FATAL | STS_PCD | STS_ERR | STS_INT) + +/*-------------------------------------------------------------------------*/ + +#include "ehci.h" +#include "ehci-dbg.c" + +/*-------------------------------------------------------------------------*/ + +/* + * handshake - spin reading hc until handshake completes or fails + * @ptr: address of hc register to be read + * @mask: bits to look at in result of read + * @done: value of those bits when handshake succeeds + * @usec: timeout in microseconds + * + * Returns negative errno, or zero on success + * + * Success happens when the "mask" bits have the specified value (hardware + * handshake done). There are two failure modes: "usec" have passed (major + * hardware flakeout), or the register reads as all-ones (hardware removed). + * + * That last failure should_only happen in cases like physical cardbus eject + * 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 (void __iomem *ptr, u32 mask, u32 done, int usec) +{ + u32 result; + + do { + result = readl (ptr); + if (result == ~(u32)0) /* card removed */ + return -ENODEV; + result &= mask; + if (result == done) + return 0; + udelay (1); + usec--; + } while (usec > 0); + return -ETIMEDOUT; +} + +/* force HC to halt state from unknown (EHCI spec section 2.3) */ +static int ehci_halt (struct ehci_hcd *ehci) +{ + u32 temp = readl (&ehci->regs->status); + + if ((temp & STS_HALT) != 0) + return 0; + + temp = readl (&ehci->regs->command); + temp &= ~CMD_RUN; + writel (temp, &ehci->regs->command); + return handshake (&ehci->regs->status, STS_HALT, STS_HALT, 16 * 125); +} + +/* put TDI/ARC silicon into EHCI mode */ +static void tdi_reset (struct ehci_hcd *ehci) +{ + u32 __iomem *reg_ptr; + u32 tmp; + + reg_ptr = (u32 __iomem *)(((u8 __iomem *)ehci->regs) + 0x68); + tmp = readl (reg_ptr); + tmp |= 0x3; + writel (tmp, reg_ptr); +} + +/* reset a non-running (STS_HALT == 1) controller */ +static int ehci_reset (struct ehci_hcd *ehci) +{ + int retval; + u32 command = readl (&ehci->regs->command); + + command |= CMD_RESET; + dbg_cmd (ehci, "reset", command); + writel (command, &ehci->regs->command); + ehci_to_hcd(ehci)->state = HC_STATE_HALT; + ehci->next_statechange = jiffies; + retval = handshake (&ehci->regs->command, CMD_RESET, 0, 250 * 1000); + + if (retval) + return retval; + + if (ehci_is_TDI(ehci)) + tdi_reset (ehci); + + return retval; +} + +/* idle the controller (from running) */ +static void ehci_quiesce (struct ehci_hcd *ehci) +{ + u32 temp; + +#ifdef DEBUG + if (!HC_IS_RUNNING (ehci_to_hcd(ehci)->state)) + BUG (); +#endif + + /* wait for any schedule enables/disables to take effect */ + temp = readl (&ehci->regs->command) << 10; + temp &= STS_ASS | STS_PSS; + if (handshake (&ehci->regs->status, STS_ASS | STS_PSS, + temp, 16 * 125) != 0) { + ehci_to_hcd(ehci)->state = HC_STATE_HALT; + return; + } + + /* then disable anything that's still active */ + temp = readl (&ehci->regs->command); + temp &= ~(CMD_ASE | CMD_IAAD | CMD_PSE); + writel (temp, &ehci->regs->command); + + /* hardware can take 16 microframes to turn off ... */ + if (handshake (&ehci->regs->status, STS_ASS | STS_PSS, + 0, 16 * 125) != 0) { + ehci_to_hcd(ehci)->state = HC_STATE_HALT; + return; + } +} + +/*-------------------------------------------------------------------------*/ + +static void ehci_work(struct ehci_hcd *ehci, struct pt_regs *regs); + +#include "ehci-hub.c" +#include "ehci-mem.c" +#include "ehci-q.c" +#include "ehci-sched.c" + +/*-------------------------------------------------------------------------*/ + +static void ehci_watchdog (unsigned long param) +{ + struct ehci_hcd *ehci = (struct ehci_hcd *) param; + unsigned long flags; + + spin_lock_irqsave (&ehci->lock, flags); + + /* lost IAA irqs wedge things badly; seen with a vt8235 */ + if (ehci->reclaim) { + 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); + ehci->reclaim_ready = 1; + } + } + + /* stop async processing after it's idled a bit */ + if (test_bit (TIMER_ASYNC_OFF, &ehci->actions)) + start_unlink_async (ehci, ehci->async); + + /* ehci could run by timer, without IRQs ... */ + ehci_work (ehci, NULL); + + spin_unlock_irqrestore (&ehci->lock, flags); +} + +#ifdef CONFIG_PCI + +/* EHCI 0.96 (and later) section 5.1 says how to kick BIOS/SMM/... + * off the controller (maybe it can boot from highspeed USB disks). + */ +static int bios_handoff (struct ehci_hcd *ehci, int where, u32 cap) +{ + if (cap & (1 << 16)) { + int msec = 5000; + struct pci_dev *pdev = + to_pci_dev(ehci_to_hcd(ehci)->self.controller); + + /* request handoff to OS */ + cap |= 1 << 24; + pci_write_config_dword(pdev, where, cap); + + /* and wait a while for it to happen */ + do { + msleep(10); + msec -= 10; + 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); + // some BIOS versions seem buggy... + // return 1; + ehci_warn (ehci, "continuing after BIOS bug...\n"); + return 0; + } + ehci_dbg (ehci, "BIOS handoff succeeded\n"); + } + return 0; +} + +#endif + +static int +ehci_reboot (struct notifier_block *self, unsigned long code, void *null) +{ + struct ehci_hcd *ehci; + + ehci = container_of (self, struct ehci_hcd, reboot_notifier); + + /* make BIOS/etc use companion controller during reboot */ + writel (0, &ehci->regs->configured_flag); + return 0; +} + + +/* called by khubd or root hub init threads */ + +static int ehci_hc_reset (struct usb_hcd *hcd) +{ + struct ehci_hcd *ehci = hcd_to_ehci (hcd); + u32 temp; + unsigned count = 256/4; + + spin_lock_init (&ehci->lock); + + 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"); + +#ifdef CONFIG_PCI + /* EHCI 0.96 and later may have "extended capabilities" */ + if (hcd->self.controller->bus == &pci_bus_type) { + struct pci_dev *pdev = to_pci_dev(hcd->self.controller); + + switch (pdev->vendor) { + case PCI_VENDOR_ID_TDI: + if (pdev->device == PCI_DEVICE_ID_TDI_EHCI) { + ehci->is_tdi_rh_tt = 1; + tdi_reset (ehci); + } + break; + case PCI_VENDOR_ID_AMD: + /* AMD8111 EHCI doesn't work, according to AMD errata */ + if (pdev->device == 0x7463) { + ehci_info (ehci, "ignoring AMD8111 (errata)\n"); + return -EIO; + } + break; + } + + temp = HCC_EXT_CAPS (readl (&ehci->caps->hcc_params)); + } else + temp = 0; + while (temp && count--) { + u32 cap; + + pci_read_config_dword (to_pci_dev(hcd->self.controller), + temp, &cap); + ehci_dbg (ehci, "capability %04x at %02x\n", cap, temp); + switch (cap & 0xff) { + case 1: /* BIOS/SMM/... handoff */ + if (bios_handoff (ehci, temp, cap) != 0) + return -EOPNOTSUPP; + break; + case 0: /* illegal reserved capability */ + ehci_warn (ehci, "illegal capability!\n"); + cap = 0; + /* FALLTHROUGH */ + default: /* unknown */ + break; + } + temp = (cap >> 8) & 0xff; + } + if (!count) { + ehci_err (ehci, "bogus capabilities ... PCI problems!\n"); + return -EIO; + } + if (ehci_is_TDI(ehci)) + ehci_reset (ehci); +#endif + + /* cache this readonly data; minimize PCI reads */ + ehci->hcs_params = readl (&ehci->caps->hcs_params); + + /* at least the Genesys GL880S needs fixup here */ + temp = HCS_N_CC(ehci->hcs_params) * HCS_N_PCC(ehci->hcs_params); + temp &= 0x0f; + if (temp && HCS_N_PORTS(ehci->hcs_params) > temp) { + ehci_dbg (ehci, "bogus port configuration: " + "cc=%d x pcc=%d < ports=%d\n", + HCS_N_CC(ehci->hcs_params), + HCS_N_PCC(ehci->hcs_params), + HCS_N_PORTS(ehci->hcs_params)); + +#ifdef CONFIG_PCI + if (hcd->self.controller->bus == &pci_bus_type) { + struct pci_dev *pdev; + + pdev = to_pci_dev(hcd->self.controller); + switch (pdev->vendor) { + case 0x17a0: /* GENESYS */ + /* GL880S: should be PORTS=2 */ + temp |= (ehci->hcs_params & ~0xf); + ehci->hcs_params = temp; + break; + case PCI_VENDOR_ID_NVIDIA: + /* NF4: should be PCC=10 */ + break; + } + } +#endif + } + + /* force HC to halt state */ + return ehci_halt (ehci); +} + +static int ehci_start (struct usb_hcd *hcd) +{ + struct ehci_hcd *ehci = hcd_to_ehci (hcd); + u32 temp; + struct usb_device *udev; + struct usb_bus *bus; + int retval; + u32 hcc_params; + u8 sbrn = 0; + int first; + + /* skip some things on restart paths */ + first = (ehci->watchdog.data == 0); + if (first) { + init_timer (&ehci->watchdog); + ehci->watchdog.function = ehci_watchdog; + ehci->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. + */ + ehci->periodic_size = DEFAULT_I_TDPS; + if (first && (retval = ehci_mem_init (ehci, GFP_KERNEL)) < 0) + return retval; + + /* controllers may cache some of the periodic schedule ... */ + hcc_params = readl (&ehci->caps->hcc_params); + if (HCC_ISOC_CACHE (hcc_params)) // full frame cache + ehci->i_thresh = 8; + else // N microframes cached + ehci->i_thresh = 2 + HCC_ISOC_THRES (hcc_params); + + ehci->reclaim = NULL; + ehci->reclaim_ready = 0; + ehci->next_uframe = -1; + + /* controller state: unknown --> reset */ + + /* EHCI spec section 4.1 */ + if ((retval = ehci_reset (ehci)) != 0) { + ehci_mem_cleanup (ehci); + return retval; + } + 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); + } +#endif + + /* + * dedicate a qh for the async ring head, since we couldn't unlink + * a 'real' qh without stopping the async schedule [4.8]. use it + * as the 'reclamation list head' too. + * 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. + */ + if (first) { + 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); + ehci->async->hw_qtd_next = EHCI_LIST_END; + ehci->async->qh_state = QH_STATE_LINKED; + ehci->async->hw_alt_next = QTD_NEXT (ehci->async->dummy->qtd_dma); + } + writel ((u32)ehci->async->qh_dma, &ehci->regs->async_next); + + /* + * hcc_params controls whether ehci->regs->segment must (!!!) + * be used; it constrains QH/ITD/SITD and QTD locations. + * pci_pool consistent memory always uses segment zero. + * streaming mappings for I/O buffers, like pci_map_single(), + * can return segments above 4GB, if the device allows. + * + * NOTE: the dma mask is visible through dma_supported(), so + * drivers can pass this info along ... like NETIF_F_HIGHDMA, + * Scsi_Host.highmem_io, and so forth. It's readonly to all + * host side drivers though. + */ + if (HCC_64BIT_ADDR (hcc_params)) { + writel (0, &ehci->regs->segment); +#if 0 +// this is deeply broken on almost all architectures + if (!pci_set_dma_mask (to_pci_dev(hcd->self.controller), 0xffffffffffffffffULL)) + ehci_info (ehci, "enabled 64bit PCI DMA\n"); +#endif + } + + /* clear interrupt enables, set irq latency */ + if (log2_irq_thresh < 0 || log2_irq_thresh > 6) + log2_irq_thresh = 0; + temp = 1 << (16 + log2_irq_thresh); + if (HCC_CANPARK(hcc_params)) { + /* HW default park == 3, on hardware that supports it (like + * NVidia and ALI silicon), maximizes throughput on the async + * schedule by avoiding QH fetches between transfers. + * + * With fast usb storage devices and NForce2, "park" seems to + * make problems: throughput reduction (!), data errors... + */ + if (park) { + park = min (park, (unsigned) 3); + temp |= CMD_PARK; + temp |= park << 8; + } + ehci_info (ehci, "park %d\n", park); + } + if (HCC_PGM_FRAMELISTLEN (hcc_params)) { + /* periodic schedule size can be smaller than default */ + temp &= ~(3 << 2); + temp |= (EHCI_TUNE_FLS << 2); + switch (EHCI_TUNE_FLS) { + case 0: ehci->periodic_size = 1024; break; + case 1: ehci->periodic_size = 512; break; + case 2: ehci->periodic_size = 256; break; + default: BUG (); + } + } + // Philips, Intel, and maybe others need CMD_RUN before the + // root hub will detect new devices (why?); NEC doesn't + temp |= CMD_RUN; + writel (temp, &ehci->regs->command); + dbg_cmd (ehci, "init", temp); + + /* set async sleep time = 10 us ... ? */ + + /* wire up the root hub */ + bus = hcd_to_bus (hcd); + udev = first ? usb_alloc_dev (NULL, bus, 0) : bus->root_hub; + if (!udev) { +done2: + ehci_mem_cleanup (ehci); + return -ENOMEM; + } + udev->speed = USB_SPEED_HIGH; + udev->state = first ? USB_STATE_ATTACHED : USB_STATE_CONFIGURED; + + /* + * 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. (Except where one is integrated, + * and there's no companion controller unless maybe for USB OTG.) + */ + if (first) { + ehci->reboot_notifier.notifier_call = ehci_reboot; + register_reboot_notifier (&ehci->reboot_notifier); + } + + hcd->state = HC_STATE_RUNNING; + writel (FLAG_CF, &ehci->regs->configured_flag); + readl (&ehci->regs->command); /* unblock posted write */ + + temp = HC_VERSION(readl (&ehci->caps->hc_capbase)); + ehci_info (ehci, + "USB %x.%x %s, EHCI %x.%02x, driver %s\n", + ((sbrn & 0xf0)>>4), (sbrn & 0x0f), + first ? "initialized" : "restarted", + temp >> 8, temp & 0xff, DRIVER_VERSION); + + /* + * From here on, khubd concurrently accesses the root + * hub; drivers will be talking to enumerated devices. + * (On restart paths, khubd already knows about the root + * hub and could find work as soon as we wrote FLAG_CF.) + * + * Before this point the HC was idle/ready. After, khubd + * and device drivers may start it running. + */ + if (first && usb_hcd_register_root_hub (udev, hcd) != 0) { + if (hcd->state == HC_STATE_RUNNING) + ehci_quiesce (ehci); + ehci_reset (ehci); + usb_put_dev (udev); + retval = -ENODEV; + goto done2; + } + + writel (INTR_MASK, &ehci->regs->intr_enable); /* Turn On Interrupts */ + + if (first) + create_debug_files (ehci); + + return 0; +} + +/* always called by thread; normally rmmod */ + +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"); + + /* Turn off port power on all root hub ports. */ + rh_ports = HCS_N_PORTS (ehci->hcs_params); + for (port = 1; port <= rh_ports; port++) + (void) ehci_hub_control(hcd, + ClearPortFeature, USB_PORT_FEAT_POWER, + port, NULL, 0); + + /* no more interrupts ... */ + del_timer_sync (&ehci->watchdog); + + spin_lock_irq(&ehci->lock); + if (HC_IS_RUNNING (hcd->state)) + ehci_quiesce (ehci); + + ehci_reset (ehci); + writel (0, &ehci->regs->intr_enable); + spin_unlock_irq(&ehci->lock); + + /* let companion controllers work when we aren't */ + writel (0, &ehci->regs->configured_flag); + unregister_reboot_notifier (&ehci->reboot_notifier); + + remove_debug_files (ehci); + + /* root hub is shut down separately (first, when possible) */ + spin_lock_irq (&ehci->lock); + if (ehci->async) + ehci_work (ehci, NULL); + spin_unlock_irq (&ehci->lock); + ehci_mem_cleanup (ehci); + +#ifdef EHCI_STATS + ehci_dbg (ehci, "irq normal %ld err %ld reclaim %ld (lost %ld)\n", + ehci->stats.normal, ehci->stats.error, ehci->stats.reclaim, + ehci->stats.lost_iaa); + ehci_dbg (ehci, "complete %ld unlink %ld\n", + ehci->stats.complete, ehci->stats.unlink); +#endif + + dbg_status (ehci, "ehci_stop completed", readl (&ehci->regs->status)); +} + +static int ehci_get_frame (struct usb_hcd *hcd) +{ + struct ehci_hcd *ehci = hcd_to_ehci (hcd); + return (readl (&ehci->regs->frame_index) >> 3) % ehci->periodic_size; +} + +/*-------------------------------------------------------------------------*/ + +#ifdef CONFIG_PM + +/* suspend/resume, section 4.3 */ + +/* These routines rely on the bus (pci, platform, etc) + * to handle powerdown and wakeup, and currently also on + * 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); + + if (time_before (jiffies, ehci->next_statechange)) + msleep (100); + +#ifdef CONFIG_USB_SUSPEND + (void) usb_suspend_device (hcd->self.root_hub, state); +#else + usb_lock_device (hcd->self.root_hub); + (void) ehci_hub_suspend (hcd); + usb_unlock_device (hcd->self.root_hub); +#endif + + // save (PCI) FLADJ in case of Vaux power loss + // ... we'd only use it to handle clock skew + + return 0; +} + +static int ehci_resume (struct usb_hcd *hcd) +{ + struct ehci_hcd *ehci = hcd_to_ehci (hcd); + unsigned port; + struct usb_device *root = hcd->self.root_hub; + int retval = -EINVAL; + int powerup = 0; + + // maybe restore (PCI) FLADJ + + if (time_before (jiffies, ehci->next_statechange)) + msleep (100); + + /* If any port is suspended, we know we can/must resume the HC. */ + for (port = HCS_N_PORTS (ehci->hcs_params); port > 0; ) { + u32 status; + port--; + status = readl (&ehci->regs->port_status [port]); + if (status & PORT_SUSPEND) { + down (&hcd->self.root_hub->serialize); + retval = ehci_hub_resume (hcd); + up (&hcd->self.root_hub->serialize); + break; + } + if ((status & PORT_POWER) == 0) + powerup = 1; + if (!root->children [port]) + continue; + dbg_port (ehci, __FUNCTION__, port + 1, status); + usb_set_device_state (root->children[port], + USB_STATE_NOTATTACHED); + } + + /* Else reset, to cope with power loss or flush-to-storage + * style "resume" having activated BIOS during reboot. + */ + if (port == 0) { + (void) ehci_halt (ehci); + (void) ehci_reset (ehci); + (void) ehci_hc_reset (hcd); + + /* emptying the schedule aborts any urbs */ + spin_lock_irq (&ehci->lock); + if (ehci->reclaim) + ehci->reclaim_ready = 1; + ehci_work (ehci, NULL); + spin_unlock_irq (&ehci->lock); + + /* restart; khubd will disconnect devices */ + retval = ehci_start (hcd); + + /* here we "know" root ports should always stay powered; + * but some controllers may lost all power. + */ + if (powerup) { + ehci_dbg (ehci, "...powerup ports...\n"); + for (port = HCS_N_PORTS (ehci->hcs_params); port > 0; ) + (void) ehci_hub_control(hcd, + SetPortFeature, USB_PORT_FEAT_POWER, + port--, NULL, 0); + msleep(20); + } + } + + return retval; +} + +#endif + +/*-------------------------------------------------------------------------*/ + +/* + * ehci_work is called from some interrupts, timers, and so on. + * it calls driver completion functions, after dropping ehci->lock. + */ +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. + * such lossage has been observed on both VT6202 and VT8235. + */ + if (HC_IS_RUNNING (ehci_to_hcd(ehci)->state) && + (ehci->async->qh_next.ptr != NULL || + ehci->periodic_sched != 0)) + timer_action (ehci, TIMER_IO_WATCHDOG); +} + +/*-------------------------------------------------------------------------*/ + +static irqreturn_t ehci_irq (struct usb_hcd *hcd, struct pt_regs *regs) +{ + struct ehci_hcd *ehci = hcd_to_ehci (hcd); + u32 status; + int bh; + + spin_lock (&ehci->lock); + + status = readl (&ehci->regs->status); + + /* e.g. cardbus physical eject */ + if (status == ~(u32) 0) { + ehci_dbg (ehci, "device removed\n"); + goto dead; + } + + status &= INTR_MASK; + if (!status) { /* irq sharing? */ + spin_unlock(&ehci->lock); + return IRQ_NONE; + } + + /* clear (just) interrupts */ + writel (status, &ehci->regs->status); + readl (&ehci->regs->command); /* unblock posted write */ + bh = 0; + +#ifdef EHCI_VERBOSE_DEBUG + /* unrequested/ignored: Frame List Rollover */ + dbg_status (ehci, "irq", status); +#endif + + /* INT, ERR, and IAA interrupt rates can be throttled */ + + /* normal [4.15.1.2] or error [4.15.1.1] completion */ + if (likely ((status & (STS_INT|STS_ERR)) != 0)) { + if (likely ((status & STS_ERR) == 0)) + COUNT (ehci->stats.normal); + else + COUNT (ehci->stats.error); + bh = 1; + } + + /* complete the unlinking of some qh [4.15.2.3] */ + if (status & STS_IAA) { + COUNT (ehci->stats.reclaim); + ehci->reclaim_ready = 1; + bh = 1; + } + + /* remote wakeup [4.3.1] */ + if ((status & STS_PCD) && 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 (&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)) { + /* bogus "fatal" IRQs appear on some chips... why? */ + status = readl (&ehci->regs->status); + dbg_cmd (ehci, "fatal", readl (&ehci->regs->command)); + dbg_status (ehci, "fatal", status); + if (status & STS_HALT) { + ehci_err (ehci, "fatal error\n"); +dead: + ehci_reset (ehci); + writel (0, &ehci->regs->configured_flag); + /* generic layer kills/unlinks all urbs, then + * uses ehci_stop to clean up the rest + */ + bh = 1; + } + } + + if (bh) + ehci_work (ehci, regs); + spin_unlock (&ehci->lock); + return IRQ_HANDLED; +} + +/*-------------------------------------------------------------------------*/ + +/* + * non-error returns are a promise to giveback() the urb later + * we drop ownership so next owner (or urb unlink) can get it + * + * urb + dev is in hcd.self.controller.urb_list + * we're queueing TDs onto software and hardware lists + * + * hcd-specific init for hcpriv hasn't been done yet + * + * NOTE: control, bulk, and interrupt share the same code to append TDs + * to a (possibly active) QH, and the same QH scanning code. + */ +static int ehci_urb_enqueue ( + struct usb_hcd *hcd, + struct usb_host_endpoint *ep, + struct urb *urb, + int mem_flags +) { + struct ehci_hcd *ehci = hcd_to_ehci (hcd); + struct list_head qtd_list; + + INIT_LIST_HEAD (&qtd_list); + + switch (usb_pipetype (urb->pipe)) { + // case PIPE_CONTROL: + // case PIPE_BULK: + default: + if (!qh_urb_transaction (ehci, urb, &qtd_list, mem_flags)) + return -ENOMEM; + return submit_async (ehci, ep, urb, &qtd_list, mem_flags); + + case PIPE_INTERRUPT: + if (!qh_urb_transaction (ehci, urb, &qtd_list, mem_flags)) + return -ENOMEM; + return intr_submit (ehci, ep, urb, &qtd_list, mem_flags); + + case PIPE_ISOCHRONOUS: + if (urb->dev->speed == USB_SPEED_HIGH) + return itd_submit (ehci, urb, mem_flags); + else + return sitd_submit (ehci, urb, mem_flags); + } +} + +static void unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh) +{ + /* 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; + last->reclaim; + last = last->reclaim) + continue; + qh->qh_state = QH_STATE_UNLINK_WAIT; + last->reclaim = qh; + + /* bypass IAA if the hc can't care */ + } else if (!HC_IS_RUNNING (ehci_to_hcd(ehci)->state) && ehci->reclaim) + end_unlink_async (ehci, NULL); + + /* something else might have unlinked the qh by now */ + if (qh->qh_state == QH_STATE_LINKED) + start_unlink_async (ehci, qh); +} + +/* remove from hardware lists + * completions normally happen asynchronously + */ + +static int ehci_urb_dequeue (struct usb_hcd *hcd, struct urb *urb) +{ + struct ehci_hcd *ehci = hcd_to_ehci (hcd); + struct ehci_qh *qh; + unsigned long flags; + + spin_lock_irqsave (&ehci->lock, flags); + switch (usb_pipetype (urb->pipe)) { + // case PIPE_CONTROL: + // case PIPE_BULK: + default: + qh = (struct ehci_qh *) urb->hcpriv; + if (!qh) + break; + unlink_async (ehci, qh); + break; + + case PIPE_INTERRUPT: + qh = (struct ehci_qh *) urb->hcpriv; + if (!qh) + break; + switch (qh->qh_state) { + case QH_STATE_LINKED: + intr_deschedule (ehci, qh); + /* FALL THROUGH */ + case QH_STATE_IDLE: + qh_completions (ehci, qh, NULL); + break; + default: + ehci_dbg (ehci, "bogus qh %p state %d\n", + qh, qh->qh_state); + goto done; + } + + /* reschedule QH iff another request is queued */ + if (!list_empty (&qh->qtd_list) + && HC_IS_RUNNING (hcd->state)) { + int status; + + status = qh_schedule (ehci, qh); + spin_unlock_irqrestore (&ehci->lock, flags); + + if (status != 0) { + // shouldn't happen often, but ... + // FIXME kill those tds' urbs + err ("can't reschedule qh %p, err %d", + qh, status); + } + return status; + } + break; + + case PIPE_ISOCHRONOUS: + // itd or sitd ... + + // wait till next completion, do it then. + // completion irqs can wait up to 1024 msec, + break; + } +done: + spin_unlock_irqrestore (&ehci->lock, flags); + return 0; +} + +/*-------------------------------------------------------------------------*/ + +// bulk qh holds the data toggle + +static void +ehci_endpoint_disable (struct usb_hcd *hcd, struct usb_host_endpoint *ep) +{ + struct ehci_hcd *ehci = hcd_to_ehci (hcd); + unsigned long flags; + struct ehci_qh *qh, *tmp; + + /* ASSERT: any requests/urbs are being unlinked */ + /* ASSERT: nobody can be submitting urbs for this any more */ + +rescan: + spin_lock_irqsave (&ehci->lock, flags); + qh = ep->hcpriv; + if (!qh) + goto done; + + /* endpoints can be iso streams. for now, we don't + * accelerate iso completions ... so spin a while. + */ + if (qh->hw_info1 == 0) { + ehci_vdbg (ehci, "iso delay\n"); + goto idle_timeout; + } + + if (!HC_IS_RUNNING (hcd->state)) + qh->qh_state = QH_STATE_IDLE; + switch (qh->qh_state) { + case QH_STATE_LINKED: + for (tmp = ehci->async->qh_next.qh; + tmp && tmp != qh; + tmp = tmp->qh_next.qh) + continue; + /* periodic qh self-unlinks on empty */ + if (!tmp) + goto nogood; + unlink_async (ehci, qh); + /* FALL THROUGH */ + case QH_STATE_UNLINK: /* wait for hw to finish? */ +idle_timeout: + spin_unlock_irqrestore (&ehci->lock, flags); + set_current_state (TASK_UNINTERRUPTIBLE); + schedule_timeout (1); + goto rescan; + case QH_STATE_IDLE: /* fully unlinked */ + if (list_empty (&qh->qtd_list)) { + qh_put (qh); + break; + } + /* else FALL THROUGH */ + default: +nogood: + /* caller was supposed to have unlinked any requests; + * that's not our job. just leak this memory. + */ + ehci_err (ehci, "qh %p (#%02x) state %d%s\n", + qh, ep->desc.bEndpointAddress, qh->qh_state, + list_empty (&qh->qtd_list) ? "" : "(has tds)"); + break; + } + ep->hcpriv = NULL; +done: + spin_unlock_irqrestore (&ehci->lock, flags); + return; +} + +/*-------------------------------------------------------------------------*/ + +static const struct hc_driver ehci_driver = { + .description = hcd_name, + .product_desc = "EHCI Host Controller", + .hcd_priv_size = sizeof(struct ehci_hcd), + + /* + * generic hardware linkage + */ + .irq = ehci_irq, + .flags = HCD_MEMORY | HCD_USB2, + + /* + * basic lifecycle operations + */ + .reset = ehci_hc_reset, + .start = ehci_start, +#ifdef CONFIG_PM + .suspend = ehci_suspend, + .resume = ehci_resume, +#endif + .stop = ehci_stop, + + /* + * managing i/o requests and associated device resources + */ + .urb_enqueue = ehci_urb_enqueue, + .urb_dequeue = ehci_urb_dequeue, + .endpoint_disable = ehci_endpoint_disable, + + /* + * scheduling support + */ + .get_frame_number = ehci_get_frame, + + /* + * root hub support + */ + .hub_status_data = ehci_hub_status_data, + .hub_control = ehci_hub_control, + .hub_suspend = ehci_hub_suspend, + .hub_resume = ehci_hub_resume, +}; + +/*-------------------------------------------------------------------------*/ + +/* EHCI 1.0 doesn't require PCI */ + +#ifdef CONFIG_PCI + +/* PCI driver selection metadata; PCI hotplugging uses this */ +static const struct pci_device_id pci_ids [] = { { + /* handle any USB 2.0 EHCI controller */ + PCI_DEVICE_CLASS(((PCI_CLASS_SERIAL_USB << 8) | 0x20), ~0), + .driver_data = (unsigned long) &ehci_driver, + }, + { /* end: all zeroes */ } +}; +MODULE_DEVICE_TABLE (pci, pci_ids); + +/* pci driver glue; this is a "new style" PCI driver module */ +static struct pci_driver ehci_pci_driver = { + .name = (char *) hcd_name, + .id_table = pci_ids, + + .probe = usb_hcd_pci_probe, + .remove = usb_hcd_pci_remove, + +#ifdef CONFIG_PM + .suspend = usb_hcd_pci_suspend, + .resume = usb_hcd_pci_resume, +#endif +}; + +#endif /* PCI */ + + +#define DRIVER_INFO DRIVER_VERSION " " DRIVER_DESC + +MODULE_DESCRIPTION (DRIVER_INFO); +MODULE_AUTHOR (DRIVER_AUTHOR); +MODULE_LICENSE ("GPL"); + +static int __init init (void) +{ + if (usb_disabled()) + return -ENODEV; + + pr_debug ("%s: block sizes: qh %Zd qtd %Zd itd %Zd sitd %Zd\n", + hcd_name, + sizeof (struct ehci_qh), sizeof (struct ehci_qtd), + sizeof (struct ehci_itd), sizeof (struct ehci_sitd)); + + return pci_register_driver (&ehci_pci_driver); +} +module_init (init); + +static void __exit cleanup (void) +{ + pci_unregister_driver (&ehci_pci_driver); +} +module_exit (cleanup); |