ChangeSet 1.1023, 2003/03/05 14:31:25-08:00, david-b@pacbell.net [PATCH] USB: rename drivers/usb/hcd --> host Could you "bk mv drivers/usb/hcd drivers/usb/host" and then apply this patch? That will let most 2.5 host controller driver patches apply directly to 2.4 ... helping both releases, since 2.5 has more fixes while 2.4 has more users. (And testers!) drivers/usb/hcd/Config.in | 7 drivers/usb/hcd/Makefile | 27 - drivers/usb/hcd/ehci-dbg.c | 650 ------------------------ drivers/usb/hcd/ehci-hcd.c | 1033 -------------------------------------- drivers/usb/hcd/ehci-hub.c | 344 ------------ drivers/usb/hcd/ehci-mem.c | 251 --------- drivers/usb/hcd/ehci-q.c | 1090 ---------------------------------------- drivers/usb/hcd/ehci-sched.c | 1123 ------------------------------------------ drivers/usb/hcd/ehci.h | 452 ---------------- drivers/usb/Config.in | 2 drivers/usb/Makefile | 6 drivers/usb/host/Config.in | 7 drivers/usb/host/Makefile | 27 + drivers/usb/host/ehci-dbg.c | 650 ++++++++++++++++++++++++ drivers/usb/host/ehci-hcd.c | 1033 ++++++++++++++++++++++++++++++++++++++ drivers/usb/host/ehci-hub.c | 344 ++++++++++++ drivers/usb/host/ehci-mem.c | 251 +++++++++ drivers/usb/host/ehci-q.c | 1090 ++++++++++++++++++++++++++++++++++++++++ drivers/usb/host/ehci-sched.c | 1123 ++++++++++++++++++++++++++++++++++++++++++ drivers/usb/host/ehci.h | 452 ++++++++++++++++ 20 files changed, 4981 insertions(+), 4981 deletions(-) diff -Nru a/drivers/usb/Config.in b/drivers/usb/Config.in --- a/drivers/usb/Config.in Thu Mar 6 14:23:09 2003 +++ b/drivers/usb/Config.in Thu Mar 6 14:23:09 2003 @@ -17,7 +17,7 @@ fi comment 'USB Host Controller Drivers' - source drivers/usb/hcd/Config.in + source drivers/usb/host/Config.in if [ "$CONFIG_USB_UHCI_ALT" != "y" ]; then dep_tristate ' UHCI (Intel PIIX4, VIA, ...) support' CONFIG_USB_UHCI $CONFIG_USB fi diff -Nru a/drivers/usb/Makefile b/drivers/usb/Makefile --- a/drivers/usb/Makefile Thu Mar 6 14:23:09 2003 +++ b/drivers/usb/Makefile Thu Mar 6 14:23:09 2003 @@ -53,7 +53,7 @@ # EHCI should initialize/link before the other HCDs ifeq ($(CONFIG_USB_EHCI_HCD),y) - obj-y += hcd/ehci-hcd.o + obj-y += host/ehci-hcd.o endif obj-$(CONFIG_USB_UHCI) += usb-uhci.o @@ -63,7 +63,7 @@ ifneq ($(CONFIG_USB_EHCI_HCD),n) usbcore-objs += hcd.o endif -subdir-$(CONFIG_USB_EHCI_HCD) += hcd +subdir-$(CONFIG_USB_EHCI_HCD) += host obj-$(CONFIG_USB_MOUSE) += usbmouse.o obj-$(CONFIG_USB_HID) += hid.o @@ -107,7 +107,7 @@ obj-$(CONFIG_USB_LCD) += usblcd.o # Object files in subdirectories -mod-subdirs := serial hcd +mod-subdirs := serial host subdir-$(CONFIG_USB_SERIAL) += serial subdir-$(CONFIG_USB_STORAGE) += storage diff -Nru a/drivers/usb/hcd/Config.in b/drivers/usb/hcd/Config.in --- a/drivers/usb/hcd/Config.in Thu Mar 6 14:23:09 2003 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,7 +0,0 @@ -# -# USB Host Controller Drivers -# -dep_tristate ' EHCI HCD (USB 2.0) support (EXPERIMENTAL)' CONFIG_USB_EHCI_HCD $CONFIG_USB $CONFIG_EXPERIMENTAL -# dep_tristate ' OHCI HCD support (EXPERIMENTAL)' CONFIG_USB_OHCI_HCD $CONFIG_USB $CONFIG_EXPERIMENTAL -# dep_tristate ' UHCI HCD (most Intel and VIA) support (EXPERIMENTAL)' CONFIG_USB_UHCI_HCD $CONFIG_USB $CONFIG_EXPERIMENTAL - diff -Nru a/drivers/usb/hcd/Makefile b/drivers/usb/hcd/Makefile --- a/drivers/usb/hcd/Makefile Thu Mar 6 14:23:09 2003 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,27 +0,0 @@ -# -# Makefile for USB Host Controller Driver -# framework and drivers -# - -O_TARGET := - -obj-$(CONFIG_USB_EHCI_HCD) += ehci-hcd.o -# obj-$(CONFIG_USB_OHCI_HCD) += ohci-hcd.o -# obj-$(CONFIG_USB_UHCI_HCD) += uhci-hcd.o - -# Extract lists of the multi-part drivers. -# The 'int-*' lists are the intermediate files used to build the multi's. -multi-y := $(filter $(list-multi), $(obj-y)) -multi-m := $(filter $(list-multi), $(obj-m)) -int-y := $(sort $(foreach m, $(multi-y), $($(basename $(m))-objs))) -int-m := $(sort $(foreach m, $(multi-m), $($(basename $(m))-objs))) - -# Take multi-part drivers out of obj-y and put components in. -obj-y := $(filter-out $(list-multi), $(obj-y)) $(int-y) - -# Translate to Rules.make lists. -OX_OBJS := $(obj-y) -MX_OBJS := $(obj-m) -MIX_OBJS := $(int-m) - -include $(TOPDIR)/Rules.make diff -Nru a/drivers/usb/hcd/ehci-dbg.c b/drivers/usb/hcd/ehci-dbg.c --- a/drivers/usb/hcd/ehci-dbg.c Thu Mar 6 14:23:09 2003 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,650 +0,0 @@ -/* - * Copyright (c) 2001-2002 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. - */ - -/* this file is part of ehci-hcd.c */ - -#ifdef DEBUG -#define ehci_dbg(ehci, fmt, args...) \ - printk(KERN_DEBUG "%s %s: " fmt , hcd_name , \ - (ehci)->hcd.pdev->slot_name , ## args ) -#else -#define ehci_dbg(ehci, fmt, args...) do { } while (0) -#endif - -#define ehci_err(ehci, fmt, args...) \ - printk(KERN_ERR "%s %s: " fmt , hcd_name , \ - (ehci)->hcd.pdev->slot_name , ## args ) -#define ehci_info(ehci, fmt, args...) \ - printk(KERN_INFO "%s %s: " fmt , hcd_name , \ - (ehci)->hcd.pdev->slot_name , ## args ) -#define ehci_warn(ehci, fmt, args...) \ - printk(KERN_WARNING "%s %s: " fmt , hcd_name , \ - (ehci)->hcd.pdev->slot_name , ## args ) - - -#ifdef EHCI_VERBOSE_DEBUG -# define vdbg dbg -# define ehci_vdbg ehci_dbg -#else -# define vdbg(fmt,args...) do { } while (0) -# define ehci_vdbg(ehci, fmt, args...) do { } while (0) -#endif - -#ifdef DEBUG - -/* check the values in the HCSPARAMS register - * (host controller _Structural_ parameters) - * see EHCI spec, Table 2-4 for each value - */ -static void dbg_hcs_params (struct ehci_hcd *ehci, char *label) -{ - u32 params = readl (&ehci->caps->hcs_params); - - ehci_dbg (ehci, - "%s hcs_params 0x%x dbg=%d%s cc=%d pcc=%d%s%s ports=%d\n", - label, params, - HCS_DEBUG_PORT (params), - HCS_INDICATOR (params) ? " ind" : "", - HCS_N_CC (params), - HCS_N_PCC (params), - HCS_PORTROUTED (params) ? "" : " ordered", - HCS_PPC (params) ? "" : " !ppc", - HCS_N_PORTS (params) - ); - /* Port routing, per EHCI 0.95 Spec, Section 2.2.5 */ - if (HCS_PORTROUTED (params)) { - int i; - char buf [46], tmp [7], byte; - - buf[0] = 0; - for (i = 0; i < HCS_N_PORTS (params); i++) { - byte = readb (&ehci->caps->portroute[(i>>1)]); - sprintf(tmp, "%d ", - ((i & 0x1) ? ((byte)&0xf) : ((byte>>4)&0xf))); - strcat(buf, tmp); - } - ehci_dbg (ehci, "%s portroute %s\n", - label, buf); - } -} -#else - -static inline void dbg_hcs_params (struct ehci_hcd *ehci, char *label) {} - -#endif - -#ifdef DEBUG - -/* check the values in the HCCPARAMS register - * (host controller _Capability_ parameters) - * see EHCI Spec, Table 2-5 for each value - * */ -static void dbg_hcc_params (struct ehci_hcd *ehci, char *label) -{ - u32 params = readl (&ehci->caps->hcc_params); - - if (HCC_ISOC_CACHE (params)) { - ehci_dbg (ehci, - "%s hcc_params %04x caching frame %s%s%s\n", - label, params, - HCC_PGM_FRAMELISTLEN (params) ? "256/512/1024" : "1024", - HCC_CANPARK (params) ? " park" : "", - HCC_64BIT_ADDR (params) ? " 64 bit addr" : ""); - } else { - ehci_dbg (ehci, - "%s hcc_params %04x thresh %d uframes %s%s%s\n", - label, - params, - HCC_ISOC_THRES (params), - HCC_PGM_FRAMELISTLEN (params) ? "256/512/1024" : "1024", - HCC_CANPARK (params) ? " park" : "", - HCC_64BIT_ADDR (params) ? " 64 bit addr" : ""); - } -} -#else - -static inline void dbg_hcc_params (struct ehci_hcd *ehci, char *label) {} - -#endif - -#ifdef DEBUG - -static void __attribute__((__unused__)) -dbg_qh (char *label, struct ehci_hcd *ehci, struct ehci_qh *qh) -{ - dbg ("%s %p info1 %x info2 %x hw_curr %x qtd_next %x", label, - qh, qh->hw_info1, qh->hw_info2, - qh->hw_current, qh->hw_qtd_next); - dbg (" alt+errs= %x, token= %x, page0= %x, page1= %x", - qh->hw_alt_next, qh->hw_token, - qh->hw_buf [0], qh->hw_buf [1]); - if (qh->hw_buf [2]) { - dbg (" page2= %x, page3= %x, page4= %x", - qh->hw_buf [2], qh->hw_buf [3], - qh->hw_buf [4]); - } -} - -static int __attribute__((__unused__)) -dbg_status_buf (char *buf, unsigned len, char *label, u32 status) -{ - return snprintf (buf, len, - "%s%sstatus %04x%s%s%s%s%s%s%s%s%s%s", - label, label [0] ? " " : "", status, - (status & STS_ASS) ? " Async" : "", - (status & STS_PSS) ? " Periodic" : "", - (status & STS_RECL) ? " Recl" : "", - (status & STS_HALT) ? " Halt" : "", - (status & STS_IAA) ? " IAA" : "", - (status & STS_FATAL) ? " FATAL" : "", - (status & STS_FLR) ? " FLR" : "", - (status & STS_PCD) ? " PCD" : "", - (status & STS_ERR) ? " ERR" : "", - (status & STS_INT) ? " INT" : "" - ); -} - -static int __attribute__((__unused__)) -dbg_intr_buf (char *buf, unsigned len, char *label, u32 enable) -{ - return snprintf (buf, len, - "%s%sintrenable %02x%s%s%s%s%s%s", - label, label [0] ? " " : "", enable, - (enable & STS_IAA) ? " IAA" : "", - (enable & STS_FATAL) ? " FATAL" : "", - (enable & STS_FLR) ? " FLR" : "", - (enable & STS_PCD) ? " PCD" : "", - (enable & STS_ERR) ? " ERR" : "", - (enable & STS_INT) ? " INT" : "" - ); -} - -static const char *const fls_strings [] = - { "1024", "512", "256", "??" }; - -static int dbg_command_buf (char *buf, unsigned len, char *label, u32 command) -{ - return snprintf (buf, len, - "%s%scommand %06x %s=%d ithresh=%d%s%s%s%s period=%s%s %s", - label, label [0] ? " " : "", command, - (command & CMD_PARK) ? "park" : "(park)", - CMD_PARK_CNT (command), - (command >> 16) & 0x3f, - (command & CMD_LRESET) ? " LReset" : "", - (command & CMD_IAAD) ? " IAAD" : "", - (command & CMD_ASE) ? " Async" : "", - (command & CMD_PSE) ? " Periodic" : "", - fls_strings [(command >> 2) & 0x3], - (command & CMD_RESET) ? " Reset" : "", - (command & CMD_RUN) ? "RUN" : "HALT" - ); -} - -static int -dbg_port_buf (char *buf, unsigned len, char *label, int port, u32 status) -{ - char *sig; - - /* signaling state */ - switch (status & (3 << 10)) { - case 0 << 10: sig = "se0"; break; - case 1 << 10: sig = "k"; break; /* low speed */ - case 2 << 10: sig = "j"; break; - default: sig = "?"; break; - } - - return snprintf (buf, len, - "%s%sport %d status %06x%s%s sig=%s %s%s%s%s%s%s%s%s%s", - label, label [0] ? " " : "", port, status, - (status & PORT_POWER) ? " POWER" : "", - (status & PORT_OWNER) ? " OWNER" : "", - sig, - (status & PORT_RESET) ? " RESET" : "", - (status & PORT_SUSPEND) ? " SUSPEND" : "", - (status & PORT_RESUME) ? " RESUME" : "", - (status & PORT_OCC) ? " OCC" : "", - (status & PORT_OC) ? " OC" : "", - (status & PORT_PEC) ? " PEC" : "", - (status & PORT_PE) ? " PE" : "", - (status & PORT_CSC) ? " CSC" : "", - (status & PORT_CONNECT) ? " CONNECT" : "" - ); -} - -#else -static inline void __attribute__((__unused__)) -dbg_qh (char *label, struct ehci_hcd *ehci, struct ehci_qh *qh) -{} - -static inline int __attribute__((__unused__)) -dbg_status_buf (char *buf, unsigned len, char *label, u32 status) -{ return 0; } - -static inline int __attribute__((__unused__)) -dbg_command_buf (char *buf, unsigned len, char *label, u32 command) -{ return 0; } - -static inline int __attribute__((__unused__)) -dbg_intr_buf (char *buf, unsigned len, char *label, u32 enable) -{ return 0; } - -static inline int __attribute__((__unused__)) -dbg_port_buf (char *buf, unsigned len, char *label, int port, u32 status) -{ return 0; } - -#endif /* DEBUG */ - -/* functions have the "wrong" filename when they're output... */ -#define dbg_status(ehci, label, status) { \ - char _buf [80]; \ - dbg_status_buf (_buf, sizeof _buf, label, status); \ - ehci_dbg (ehci, "%s\n", _buf); \ -} - -#define dbg_cmd(ehci, label, command) { \ - char _buf [80]; \ - dbg_command_buf (_buf, sizeof _buf, label, command); \ - ehci_dbg (ehci, "%s\n", _buf); \ -} - -#define dbg_port(ehci, label, port, status) { \ - char _buf [80]; \ - dbg_port_buf (_buf, sizeof _buf, label, port, status); \ - ehci_dbg (ehci, "%s\n", _buf); \ -} - -/*-------------------------------------------------------------------------*/ - -#ifdef STUB_DEBUG_FILES - -static inline void create_debug_files (struct ehci_hcd *bus) { } -static inline void remove_debug_files (struct ehci_hcd *bus) { } - -#else - -/* troubleshooting help: expose state in driverfs */ - -#define speed_char(info1) ({ char tmp; \ - switch (info1 & (3 << 12)) { \ - case 0 << 12: tmp = 'f'; break; \ - case 1 << 12: tmp = 'l'; break; \ - case 2 << 12: tmp = 'h'; break; \ - default: tmp = '?'; break; \ - }; tmp; }) - -static inline char token_mark (u32 token) -{ - token = le32_to_cpu (token); - if (token & QTD_STS_ACTIVE) - return '*'; - if (token & QTD_STS_HALT) - return '-'; - if (QTD_PID (token) != 1 /* not IN: OUT or SETUP */ - || QTD_LENGTH (token) == 0) - return ' '; - /* tries to advance through hw_alt_next */ - return '/'; -} - -static void qh_lines ( - struct ehci_hcd *ehci, - struct ehci_qh *qh, - char **nextp, - unsigned *sizep -) -{ - u32 scratch; - u32 hw_curr; - struct list_head *entry; - struct ehci_qtd *td; - unsigned temp; - unsigned size = *sizep; - char *next = *nextp; - char mark; - - mark = token_mark (qh->hw_token); - if (mark == '/') { /* qh_alt_next controls qh advance? */ - if ((qh->hw_alt_next & QTD_MASK) == ehci->async->hw_alt_next) - mark = '#'; /* blocked */ - else if (qh->hw_alt_next & cpu_to_le32 (0x01)) - mark = '.'; /* use hw_qtd_next */ - /* else alt_next points to some other qtd */ - } - scratch = cpu_to_le32p (&qh->hw_info1); - hw_curr = (mark == '*') ? cpu_to_le32p (&qh->hw_current) : 0; - temp = snprintf (next, size, - "qh/%p dev%d %cs ep%d %08x %08x (%08x%c %s nak%d)", - qh, scratch & 0x007f, - speed_char (scratch), - (scratch >> 8) & 0x000f, - scratch, cpu_to_le32p (&qh->hw_info2), - cpu_to_le32p (&qh->hw_token), mark, - (cpu_to_le32 (0x8000000) & qh->hw_token) - ? "data0" : "data1", - (cpu_to_le32p (&qh->hw_alt_next) >> 1) & 0x0f); - size -= temp; - next += temp; - - /* hc may be modifying the list as we read it ... */ - list_for_each (entry, &qh->qtd_list) { - td = list_entry (entry, struct ehci_qtd, qtd_list); - scratch = cpu_to_le32p (&td->hw_token); - mark = ' '; - if (hw_curr == td->qtd_dma) - mark = '*'; - else if (qh->hw_qtd_next == td->qtd_dma) - mark = '+'; - else if (QTD_LENGTH (scratch)) { - if (td->hw_alt_next == ehci->async->hw_alt_next) - mark = '#'; - else if (td->hw_alt_next != EHCI_LIST_END) - mark = '/'; - } - temp = snprintf (next, size, - "\n\t%p%c%s len=%d %08x urb %p", - td, mark, ({ char *tmp; - switch ((scratch>>8)&0x03) { - case 0: tmp = "out"; break; - case 1: tmp = "in"; break; - case 2: tmp = "setup"; break; - default: tmp = "?"; break; - } tmp;}), - (scratch >> 16) & 0x7fff, - scratch, - td->urb); - if (temp < 0) - temp = 0; - else if (size < temp) - temp = size; - size -= temp; - next += temp; - if (temp == size) - goto done; - } - - temp = snprintf (next, size, "\n"); - if (temp < 0) - temp = 0; - else if (size < temp) - temp = size; - size -= temp; - next += temp; - -done: - *sizep = size; - *nextp = next; -} - -static ssize_t -show_async (struct device *dev, char *buf) -{ - struct pci_dev *pdev; - struct ehci_hcd *ehci; - unsigned long flags; - unsigned temp, size; - char *next; - struct ehci_qh *qh; - - pdev = container_of (dev, struct pci_dev, dev); - ehci = container_of (pci_get_drvdata (pdev), struct ehci_hcd, hcd); - next = buf; - size = PAGE_SIZE; - - /* dumps a snapshot of the async schedule. - * usually empty except for long-term bulk reads, or head. - * one QH per line, and TDs we know about - */ - spin_lock_irqsave (&ehci->lock, flags); - for (qh = ehci->async->qh_next.qh; size > 0 && qh; qh = qh->qh_next.qh) - qh_lines (ehci, qh, &next, &size); - if (ehci->reclaim && size > 0) { - temp = snprintf (next, size, "\nreclaim =\n"); - size -= temp; - next += temp; - - for (qh = ehci->reclaim; size > 0 && qh; qh = qh->reclaim) - qh_lines (ehci, qh, &next, &size); - } - spin_unlock_irqrestore (&ehci->lock, flags); - - return PAGE_SIZE - size; -} -static DEVICE_ATTR (async, S_IRUGO, show_async, NULL); - -#define DBG_SCHED_LIMIT 64 - -static ssize_t -show_periodic (struct device *dev, char *buf) -{ - struct pci_dev *pdev; - struct ehci_hcd *ehci; - unsigned long flags; - union ehci_shadow p, *seen; - unsigned temp, size, seen_count; - char *next; - unsigned i, tag; - - if (!(seen = kmalloc (DBG_SCHED_LIMIT * sizeof *seen, SLAB_ATOMIC))) - return 0; - seen_count = 0; - - pdev = container_of (dev, struct pci_dev, dev); - ehci = container_of (pci_get_drvdata (pdev), struct ehci_hcd, hcd); - next = buf; - size = PAGE_SIZE; - - temp = snprintf (next, size, "size = %d\n", ehci->periodic_size); - size -= temp; - next += temp; - - /* dump a snapshot of the periodic schedule. - * iso changes, interrupt usually doesn't. - */ - spin_lock_irqsave (&ehci->lock, flags); - for (i = 0; i < ehci->periodic_size; i++) { - p = ehci->pshadow [i]; - if (!p.ptr) - continue; - tag = Q_NEXT_TYPE (ehci->periodic [i]); - - temp = snprintf (next, size, "%4d: ", i); - size -= temp; - next += temp; - - do { - switch (tag) { - case Q_TYPE_QH: - temp = snprintf (next, size, " qh%d/%p", - p.qh->period, p.qh); - size -= temp; - next += temp; - for (temp = 0; temp < seen_count; temp++) { - if (seen [temp].ptr == p.ptr) - break; - } - /* show more info the first time around */ - if (temp == seen_count) { - u32 scratch = cpu_to_le32p ( - &p.qh->hw_info1); - - temp = snprintf (next, size, - " (%cs dev%d ep%d [%d/%d] %d)", - speed_char (scratch), - scratch & 0x007f, - (scratch >> 8) & 0x000f, - p.qh->usecs, p.qh->c_usecs, - 0x7ff & (scratch >> 16)); - - /* FIXME TD info too */ - - if (seen_count < DBG_SCHED_LIMIT) - seen [seen_count++].qh = p.qh; - } else - temp = 0; - tag = Q_NEXT_TYPE (p.qh->hw_next); - p = p.qh->qh_next; - break; - case Q_TYPE_FSTN: - temp = snprintf (next, size, - " fstn-%8x/%p", p.fstn->hw_prev, - p.fstn); - tag = Q_NEXT_TYPE (p.fstn->hw_next); - p = p.fstn->fstn_next; - break; - case Q_TYPE_ITD: - temp = snprintf (next, size, - " itd/%p", p.itd); - tag = Q_NEXT_TYPE (p.itd->hw_next); - p = p.itd->itd_next; - break; - case Q_TYPE_SITD: - temp = snprintf (next, size, - " sitd/%p", p.sitd); - tag = Q_NEXT_TYPE (p.sitd->hw_next); - p = p.sitd->sitd_next; - break; - } - size -= temp; - next += temp; - } while (p.ptr); - - temp = snprintf (next, size, "\n"); - size -= temp; - next += temp; - } - spin_unlock_irqrestore (&ehci->lock, flags); - kfree (seen); - - return PAGE_SIZE - size; -} -static DEVICE_ATTR (periodic, S_IRUGO, show_periodic, NULL); - -#undef DBG_SCHED_LIMIT - -static ssize_t -show_registers (struct device *dev, char *buf) -{ - struct pci_dev *pdev; - struct ehci_hcd *ehci; - unsigned long flags; - unsigned temp, size, i; - char *next, scratch [80]; - static char fmt [] = "%*s\n"; - static char label [] = ""; - - pdev = container_of (dev, struct pci_dev, dev); - ehci = container_of (pci_get_drvdata (pdev), struct ehci_hcd, hcd); - - next = buf; - size = PAGE_SIZE; - - spin_lock_irqsave (&ehci->lock, flags); - - /* Capability Registers */ - i = readw (&ehci->caps->hci_version); - temp = snprintf (next, size, - "EHCI %x.%02x, hcd state %d (version " DRIVER_VERSION ")\n", - i >> 8, i & 0x0ff, ehci->hcd.state); - size -= temp; - next += temp; - - // FIXME interpret both types of params - i = readl (&ehci->caps->hcs_params); - temp = snprintf (next, size, "structural params 0x%08x\n", i); - size -= temp; - next += temp; - - i = readl (&ehci->caps->hcc_params); - temp = snprintf (next, size, "capability params 0x%08x\n", i); - size -= temp; - next += temp; - - /* Operational Registers */ - temp = dbg_status_buf (scratch, sizeof scratch, label, - readl (&ehci->regs->status)); - temp = snprintf (next, size, fmt, temp, scratch); - size -= temp; - next += temp; - - temp = dbg_command_buf (scratch, sizeof scratch, label, - readl (&ehci->regs->command)); - temp = snprintf (next, size, fmt, temp, scratch); - size -= temp; - next += temp; - - temp = dbg_intr_buf (scratch, sizeof scratch, label, - readl (&ehci->regs->intr_enable)); - temp = snprintf (next, size, fmt, temp, scratch); - size -= temp; - next += temp; - - temp = snprintf (next, size, "uframe %04x\n", - readl (&ehci->regs->frame_index)); - size -= temp; - next += temp; - - for (i = 0; i < HCS_N_PORTS (ehci->hcs_params); i++) { - temp = dbg_port_buf (scratch, sizeof scratch, label, i, - readl (&ehci->regs->port_status [i])); - temp = snprintf (next, size, fmt, temp, scratch); - size -= temp; - next += temp; - } - - if (ehci->reclaim) { - temp = snprintf (next, size, "reclaim qh %p%s\n", - ehci->reclaim, - ehci->reclaim_ready ? " ready" : ""); - size -= temp; - next += temp; - } - -#ifdef EHCI_STATS - temp = snprintf (next, size, "irq normal %ld err %ld reclaim %ld\n", - ehci->stats.normal, ehci->stats.error, ehci->stats.reclaim); - size -= temp; - next += temp; - - temp = snprintf (next, size, "complete %ld unlink %ld\n", - ehci->stats.complete, ehci->stats.unlink); - size -= temp; - next += temp; -#endif - - spin_unlock_irqrestore (&ehci->lock, flags); - - return PAGE_SIZE - size; -} -static DEVICE_ATTR (registers, S_IRUGO, show_registers, NULL); - -static inline void create_debug_files (struct ehci_hcd *bus) -{ - device_create_file (&bus->hcd.pdev->dev, &dev_attr_async); - device_create_file (&bus->hcd.pdev->dev, &dev_attr_periodic); - device_create_file (&bus->hcd.pdev->dev, &dev_attr_registers); -} - -static inline void remove_debug_files (struct ehci_hcd *bus) -{ - device_remove_file (&bus->hcd.pdev->dev, &dev_attr_async); - device_remove_file (&bus->hcd.pdev->dev, &dev_attr_periodic); - device_remove_file (&bus->hcd.pdev->dev, &dev_attr_registers); -} - -#endif /* STUB_DEBUG_FILES */ - diff -Nru a/drivers/usb/hcd/ehci-hcd.c b/drivers/usb/hcd/ehci-hcd.c --- a/drivers/usb/hcd/ehci-hcd.c Thu Mar 6 14:23:09 2003 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,1033 +0,0 @@ -/* - * Copyright (c) 2000-2002 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef CONFIG_USB_DEBUG - #define DEBUG -#else - #undef DEBUG -#endif - -#include - -#include -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,32) -#include "../hcd.h" -#else -#include "../core/hcd.h" -#endif - -#include -#include -#include -#include -#include - - -/*-------------------------------------------------------------------------*/ - -/* - * 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: - * - * 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 "2003-Jan-22" -#define DRIVER_AUTHOR "David Brownell" -#define DRIVER_DESC "USB 2.0 'Enhanced' Host Controller (EHCI) Driver" - -static const char hcd_name [] = "ehci-hcd"; - - -// #define EHCI_VERBOSE_DEBUG -// #define have_split_iso - -#ifdef DEBUG -#define EHCI_STATS -#endif - -#define INTR_AUTOMAGIC /* urb lifecycle mode, gone in 2.5 */ - -/* 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_WATCHDOG_JIFFIES (HZ/100) /* arbitrary; ~10 msec */ -#define EHCI_ASYNC_JIFFIES (HZ/20) /* async idle timeout */ - -/* Initial IRQ latency: lower than default */ -static int log2_irq_thresh = 0; // 0 to 6 -MODULE_PARM (log2_irq_thresh, "i"); -MODULE_PARM_DESC (log2_irq_thresh, "log2 IRQ latency, 1-64 microframes"); - -#define INTR_MASK (STS_IAA | STS_FATAL | 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 (u32 *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; -} - -/* - * hc states include: unknown, halted, ready, running - * transitional states are messy just now - * trying to avoid "running" unless urbs are active - * a "ready" hc can be finishing prefetched work - */ - -/* 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); -} - -/* reset a non-running (STS_HALT == 1) controller */ -static int ehci_reset (struct ehci_hcd *ehci) -{ - u32 command = readl (&ehci->regs->command); - - command |= CMD_RESET; - dbg_cmd (ehci, "reset", command); - writel (command, &ehci->regs->command); - ehci->hcd.state = USB_STATE_HALT; - return handshake (&ehci->regs->command, CMD_RESET, 0, 250 * 1000); -} - -/* idle the controller (from running) */ -static void ehci_ready (struct ehci_hcd *ehci) -{ - u32 temp; - -#ifdef DEBUG - if (!HCD_IS_RUNNING (ehci->hcd.state)) - BUG (); -#endif - - /* wait for any schedule enables/disables to take effect */ - temp = 0; - if (ehci->async->qh_next.qh) - temp = STS_ASS; - if (ehci->next_uframe != -1) - temp |= STS_PSS; - if (handshake (&ehci->regs->status, STS_ASS | STS_PSS, - temp, 16 * 125) != 0) { - ehci->hcd.state = USB_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->hcd.state = USB_STATE_HALT; - return; - } - ehci->hcd.state = USB_STATE_READY; -} - -/*-------------------------------------------------------------------------*/ - -#include "ehci-hub.c" -#include "ehci-mem.c" -#include "ehci-q.c" -#include "ehci-sched.c" - -/*-------------------------------------------------------------------------*/ - -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; - 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"); - writel (STS_IAA, &ehci->regs->status); - ehci->reclaim_ready = 1; - } - } - - ehci_work (ehci, NULL); - if (ehci->reclaim && !timer_pending (&ehci->watchdog)) - mod_timer (&ehci->watchdog, - jiffies + EHCI_WATCHDOG_JIFFIES); - - /* stop async processing after it's idled a while */ - else if (ehci->async_idle) { - start_unlink_async (ehci, ehci->async); - ehci->async_idle = 0; - } - spin_unlock_irqrestore (&ehci->lock, flags); -} - -/* 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 = 500; - - /* request handoff to OS */ - cap &= 1 << 24; - pci_write_config_dword (ehci->hcd.pdev, where, cap); - - /* and wait a while for it to happen */ - do { - wait_ms (10); - msec -= 10; - pci_read_config_dword (ehci->hcd.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; - } - ehci_dbg (ehci, "BIOS handoff succeeded\n"); - } - return 0; -} - -/* called by khubd or root hub init threads */ - -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 tempbyte; - - spin_lock_init (&ehci->lock); - - ehci->caps = (struct ehci_caps *) hcd->regs; - ehci->regs = (struct ehci_regs *) (hcd->regs + ehci->caps->length); - dbg_hcs_params (ehci, "ehci_start"); - dbg_hcc_params (ehci, "ehci_start"); - - hcc_params = readl (&ehci->caps->hcc_params); - - /* EHCI 0.96 and later may have "extended capabilities" */ - temp = HCC_EXT_CAPS (hcc_params); - while (temp) { - u32 cap; - - pci_read_config_dword (ehci->hcd.pdev, 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; - } - - /* cache this readonly data; minimize PCI reads */ - ehci->hcs_params = readl (&ehci->caps->hcs_params); - - /* force HC to halt state */ - if ((retval = ehci_halt (ehci)) != 0) - return retval; - - /* - * 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 ((retval = ehci_mem_init (ehci, SLAB_KERNEL)) < 0) - return retval; - - /* controllers may cache some of the periodic schedule ... */ - 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 = 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 (INTR_MASK, &ehci->regs->intr_enable); - writel (ehci->periodic_dma, &ehci->regs->frame_list); - - /* - * 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. - */ - ehci->async->qh_next.qh = 0; - 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 (!pci_set_dma_mask (ehci->hcd.pdev, 0xffffffffffffffffULL)) - ehci_info (ehci, "enabled 64bit PCI DMA\n"); - } - - /* help hc dma work well with cachelines */ - pci_set_mwi (ehci->hcd.pdev); - - /* clear interrupt enables, set irq latency */ - temp = readl (&ehci->regs->command) & 0xff; - if (log2_irq_thresh < 0 || log2_irq_thresh > 6) - log2_irq_thresh = 0; - temp |= 1 << (16 + log2_irq_thresh); - // if hc can park (ehci >= 0.96), default is 3 packets per async QH - 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 (); - } - } - temp &= ~(CMD_IAAD | CMD_ASE | CMD_PSE), - // 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 ... ? */ - - init_timer (&ehci->watchdog); - ehci->watchdog.function = ehci_watchdog; - ehci->watchdog.data = (unsigned long) ehci; - - /* wire up the root hub */ - bus = hcd_to_bus (hcd); - bus->root_hub = udev = usb_alloc_dev (NULL, bus); - if (!udev) { -done2: - ehci_mem_cleanup (ehci); - return -ENOMEM; - } - - /* - * 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. - */ - ehci->hcd.state = USB_STATE_READY; - writel (FLAG_CF, &ehci->regs->configured_flag); - readl (&ehci->regs->command); /* unblock posted write */ - - /* PCI Serial Bus Release Number is at 0x60 offset */ - pci_read_config_byte (hcd->pdev, 0x60, &tempbyte); - temp = readw (&ehci->caps->hci_version); - ehci_info (ehci, - "USB %x.%x enabled, EHCI %x.%02x, driver %s\n", - ((tempbyte & 0xf0)>>4), (tempbyte & 0x0f), - temp >> 8, temp & 0xff, DRIVER_VERSION); - - /* - * From here on, khubd concurrently accesses the root - * hub; drivers will be talking to enumerated devices. - * - * Before this point the HC was idle/ready. After, khubd - * and device drivers may start it running. - */ - usb_connect (udev); - udev->speed = USB_SPEED_HIGH; - if (hcd_register_root (hcd) != 0) { - if (hcd->state == USB_STATE_RUNNING) - ehci_ready (ehci); - ehci_reset (ehci); - bus->root_hub = 0; - retval = -ENODEV; - goto done2; - } - - 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); - - ehci_dbg (ehci, "stop\n"); - - /* no more interrupts ... */ - if (hcd->state == USB_STATE_RUNNING) - ehci_ready (ehci); - if (in_interrupt ()) { /* must not happen!! */ - ehci_err (ehci, "stopped in_interrupt!\n"); - return; - } - del_timer_sync (&ehci->watchdog); - ehci_reset (ehci); - - /* let companion controllers work when we aren't */ - writel (0, &ehci->regs->configured_flag); - - remove_debug_files (ehci); - - /* root hub is shut down separately (first, when possible) */ - spin_lock_irq (&ehci->lock); - 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\n", - ehci->stats.normal, ehci->stats.error, ehci->stats.reclaim); - 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 */ - -static int ehci_suspend (struct usb_hcd *hcd, u32 state) -{ - struct ehci_hcd *ehci = hcd_to_ehci (hcd); - int ports; - int i; - - dbg ("%s: suspend to %d", hcd_to_bus (hcd)->bus_name, state); - - ports = HCS_N_PORTS (ehci->hcs_params); - - // 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; -dbg ("%s: suspend port %d", hcd_to_bus (hcd)->bus_name, 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 - - /* who tells PCI to reduce power consumption? */ - - return 0; -} - -static int ehci_resume (struct usb_hcd *hcd) -{ - struct ehci_hcd *ehci = hcd_to_ehci (hcd); - int ports; - int i; - - dbg ("%s: resume", hcd_to_bus (hcd)->bus_name); - - 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_READY; - // 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; -dbg ("%s: resume port %d", hcd_to_bus (hcd)->bus_name, 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; -} - -#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) -{ - if (ehci->reclaim_ready) - end_unlink_async (ehci, regs); - scan_async (ehci, regs); - if (ehci->next_uframe != -1) - scan_periodic (ehci, regs); -} - -/*-------------------------------------------------------------------------*/ - -static void 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? */ - goto done; - - /* clear (just) interrupts */ - writel (status, &ehci->regs->status); - readl (&ehci->regs->command); /* unblock posted write */ - bh = 0; - -#ifdef EHCI_VERBOSE_DEBUG - /* unrequested/ignored: Port Change Detect, 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; - } - - /* PCI errors [4.15.2.4] */ - if (unlikely ((status & STS_FATAL) != 0)) { - ehci_err (ehci, "fatal error\n"); -dead: - ehci_reset (ehci); - /* generic layer kills/unlinks all urbs, then - * uses ehci_stop to clean up the rest - */ - bh = 1; - } - - if (bh) - ehci_work (ehci, regs); -done: - spin_unlock (&ehci->lock); -} - -/*-------------------------------------------------------------------------*/ - -/* - * 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_dev.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 urb *urb, - int mem_flags -) { - 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)) { - // case PIPE_CONTROL: - // case PIPE_BULK: - default: - if (!qh_urb_transaction (ehci, urb, &qtd_list, mem_flags)) - return -ENOMEM; - return submit_async (ehci, urb, &qtd_list, mem_flags); - - case PIPE_INTERRUPT: - if (!qh_urb_transaction (ehci, urb, &qtd_list, mem_flags)) - return -ENOMEM; - return intr_submit (ehci, urb, &qtd_list, mem_flags); - - case PIPE_ISOCHRONOUS: - if (urb->dev->speed == USB_SPEED_HIGH) - return itd_submit (ehci, urb, mem_flags); -#ifdef have_split_iso - else - return sitd_submit (ehci, urb, mem_flags); -#else - dbg ("no split iso support yet"); - return -ENOSYS; -#endif /* have_split_iso */ - } -} - -/* 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; - - /* if we need to use IAA and it's busy, defer */ - if (qh->qh_state == QH_STATE_LINKED - && ehci->reclaim - && HCD_IS_RUNNING (ehci->hcd.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 (!HCD_IS_RUNNING (ehci->hcd.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); - break; - - case PIPE_INTERRUPT: - qh = (struct ehci_qh *) urb->hcpriv; - if (!qh) - break; - if (qh->qh_state == QH_STATE_LINKED) { - /* messy, can spin or block a microframe ... */ - intr_deschedule (ehci, qh, 1); - /* qh_state == IDLE */ - } - qh_completions (ehci, qh, NULL); - - /* reschedule QH iff another request is queued */ - if (!list_empty (&qh->qtd_list) - && HCD_IS_RUNNING (ehci->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, - urb->transfer_flags |= EHCI_STATE_UNLINK; - break; - } - spin_unlock_irqrestore (&ehci->lock, flags); - return 0; -} - -/*-------------------------------------------------------------------------*/ - -// bulk qh holds the data toggle - -static void ehci_free_config (struct usb_hcd *hcd, struct usb_device *udev) -{ - struct hcd_dev *dev = (struct hcd_dev *)udev->hcpriv; - struct ehci_hcd *ehci = hcd_to_ehci (hcd); - int i; - unsigned long flags; - - /* ASSERT: no requests/urbs are still linked (so no TDs) */ - /* ASSERT: nobody can be submitting urbs for this any more */ - - dbg ("%s: free_config devnum %d", - hcd_to_bus (hcd)->bus_name, udev->devnum); - - spin_lock_irqsave (&ehci->lock, flags); - for (i = 0; i < 32; i++) { - if (dev->ep [i]) { - struct ehci_qh *qh; - char *why; - - /* dev->ep never has ITDs or SITDs */ - qh = (struct ehci_qh *) dev->ep [i]; - - /* detect/report non-recoverable errors */ - if (in_interrupt ()) - why = "disconnect() didn't"; - else if ((qh->hw_info2 & cpu_to_le32 (0xffff)) != 0 - && qh->qh_state != QH_STATE_IDLE) - why = "(active periodic)"; - else - why = 0; - if (why) { - err ("dev %s-%s ep %d-%s error: %s", - hcd_to_bus (hcd)->bus_name, - udev->devpath, - i & 0xf, (i & 0x10) ? "IN" : "OUT", - why); - BUG (); - } - - dev->ep [i] = 0; - if (qh->qh_state == QH_STATE_IDLE) - goto idle; - dbg ("free_config, async ep 0x%02x qh %p", i, qh); - - /* scan_async() empties the ring as it does its work, - * using IAA, but doesn't (yet?) turn it off. if it - * doesn't empty this qh, likely it's the last entry. - */ - while (qh->qh_state == QH_STATE_LINKED - && ehci->reclaim - && HCD_IS_RUNNING (ehci->hcd.state) - ) { - spin_unlock_irqrestore (&ehci->lock, flags); - /* wait_ms() won't spin, we're a thread; - * and we know IRQ/timer/... can progress - */ - wait_ms (1); - spin_lock_irqsave (&ehci->lock, flags); - } - if (qh->qh_state == QH_STATE_LINKED) - start_unlink_async (ehci, qh); - while (qh->qh_state != QH_STATE_IDLE - && ehci->hcd.state != USB_STATE_HALT) { - spin_unlock_irqrestore (&ehci->lock, flags); - wait_ms (1); - spin_lock_irqsave (&ehci->lock, flags); - } -idle: - qh_put (ehci, qh); - } - } - - spin_unlock_irqrestore (&ehci->lock, flags); -} - -/*-------------------------------------------------------------------------*/ - -static const struct hc_driver ehci_driver = { - .description = hcd_name, - - /* - * generic hardware linkage - */ - .irq = ehci_irq, - .flags = HCD_MEMORY | HCD_USB2, - - /* - * basic lifecycle operations - */ - .start = ehci_start, -#ifdef CONFIG_PM - .suspend = ehci_suspend, - .resume = ehci_resume, -#endif - .stop = ehci_stop, - - /* - * memory lifecycle (except per-request) - */ - .hcd_alloc = ehci_hcd_alloc, - .hcd_free = ehci_hcd_free, - - /* - * managing i/o requests and associated device resources - */ - .urb_enqueue = ehci_urb_enqueue, - .urb_dequeue = ehci_urb_dequeue, - .free_config = ehci_free_config, - - /* - * scheduling support - */ - .get_frame_number = ehci_get_frame, - - /* - * root hub support - */ - .hub_status_data = ehci_hub_status_data, - .hub_control = ehci_hub_control, -}; - -/*-------------------------------------------------------------------------*/ - -/* EHCI spec says PCI is required. */ - -/* PCI driver selection metadata; PCI hotplugging uses this */ -static const struct pci_device_id __devinitdata pci_ids [] = { { - - /* handle any USB 2.0 EHCI controller */ - - .class = ((PCI_CLASS_SERIAL_USB << 8) | 0x20), - .class_mask = ~0, - .driver_data = (unsigned long) &ehci_driver, - - /* no matter who makes it */ - .vendor = PCI_ANY_ID, - .device = PCI_ANY_ID, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - -}, { /* 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 -}; - -#define DRIVER_INFO DRIVER_VERSION " " DRIVER_DESC - -MODULE_DESCRIPTION (DRIVER_INFO); -MODULE_AUTHOR (DRIVER_AUTHOR); -MODULE_LICENSE ("GPL"); - -static int __init init (void) -{ - dbg (DRIVER_INFO); - dbg ("block sizes: qh %Zd qtd %Zd itd %Zd sitd %Zd", - sizeof (struct ehci_qh), sizeof (struct ehci_qtd), - sizeof (struct ehci_itd), sizeof (struct ehci_sitd)); - - return pci_module_init (&ehci_pci_driver); -} -module_init (init); - -static void __exit cleanup (void) -{ - pci_unregister_driver (&ehci_pci_driver); -} -module_exit (cleanup); diff -Nru a/drivers/usb/hcd/ehci-hub.c b/drivers/usb/hcd/ehci-hub.c --- a/drivers/usb/hcd/ehci-hub.c Thu Mar 6 14:23:09 2003 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,344 +0,0 @@ -/* - * Copyright (c) 2001-2002 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. - */ - -/* this file is part of ehci-hcd.c */ - -/*-------------------------------------------------------------------------*/ - -/* - * EHCI Root Hub ... the nonsharable stuff - * - * Registers don't need cpu_to_le32, that happens transparently - */ - -/*-------------------------------------------------------------------------*/ - -static int check_reset_complete ( - struct ehci_hcd *ehci, - int index, - int port_status -) { - if (!(port_status & PORT_CONNECT)) { - ehci->reset_done [index] = 0; - return port_status; - } - - /* if reset finished and it's still not enabled -- handoff */ - if (!(port_status & PORT_PE)) { - ehci_dbg (ehci, "port %d full speed --> companion\n", - index + 1); - - // what happens if HCS_N_CC(params) == 0 ? - port_status |= PORT_OWNER; - writel (port_status, &ehci->regs->port_status [index]); - - } else - ehci_dbg (ehci, "port %d high speed\n", index + 1); - - return port_status; -} - -/*-------------------------------------------------------------------------*/ - - -/* build "status change" packet (one or two bytes) from HC registers */ - -static int -ehci_hub_status_data (struct usb_hcd *hcd, char *buf) -{ - struct ehci_hcd *ehci = hcd_to_ehci (hcd); - u32 temp, status = 0; - int ports, i, retval = 1; - unsigned long flags; - - /* init status to no-changes */ - buf [0] = 0; - ports = HCS_N_PORTS (ehci->hcs_params); - if (ports > 7) { - buf [1] = 0; - retval++; - } - - /* no hub change reports (bit 0) for now (power, ...) */ - - /* port N changes (bit N)? */ - spin_lock_irqsave (&ehci->lock, flags); - for (i = 0; i < ports; i++) { - temp = readl (&ehci->regs->port_status [i]); - if (temp & PORT_OWNER) { - /* don't report this in GetPortStatus */ - if (temp & PORT_CSC) { - 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) { - if (i < 7) - buf [0] |= 1 << (i + 1); - else - buf [1] |= 1 << (i - 7); - status = STS_PCD; - } - } - spin_unlock_irqrestore (&ehci->lock, flags); - return status ? retval : 0; -} - -/*-------------------------------------------------------------------------*/ - -static void -ehci_hub_descriptor ( - struct ehci_hcd *ehci, - struct usb_hub_descriptor *desc -) { - int ports = HCS_N_PORTS (ehci->hcs_params); - u16 temp; - - desc->bDescriptorType = 0x29; - desc->bPwrOn2PwrGood = 0; /* FIXME: f(system power) */ - desc->bHubContrCurrent = 0; - - desc->bNbrPorts = ports; - temp = 1 + (ports / 8); - desc->bDescLength = 7 + 2 * temp; - - /* two bitmaps: ports removable, and usb 1.0 legacy PortPwrCtrlMask */ - memset (&desc->bitmap [0], 0, temp); - memset (&desc->bitmap [temp], 0xff, temp); - - temp = 0x0008; /* per-port overcurrent reporting */ - if (HCS_PPC (ehci->hcs_params)) - temp |= 0x0001; /* per-port power control */ - if (HCS_INDICATOR (ehci->hcs_params)) - temp |= 0x0080; /* per-port indicators (LEDs) */ - desc->wHubCharacteristics = cpu_to_le16 (temp); -} - -/*-------------------------------------------------------------------------*/ - -static int ehci_hub_control ( - struct usb_hcd *hcd, - u16 typeReq, - u16 wValue, - u16 wIndex, - char *buf, - u16 wLength -) { - struct ehci_hcd *ehci = hcd_to_ehci (hcd); - int ports = HCS_N_PORTS (ehci->hcs_params); - u32 temp, status; - unsigned long flags; - int retval = 0; - - /* - * FIXME: support SetPortFeatures USB_PORT_FEAT_INDICATOR. - * HCS_INDICATOR may say we can change LEDs to off/amber/green. - * (track current state ourselves) ... blink for diagnostics, - * power, "this is the one", etc. EHCI spec supports this. - */ - - spin_lock_irqsave (&ehci->lock, flags); - switch (typeReq) { - case ClearHubFeature: - switch (wValue) { - case C_HUB_LOCAL_POWER: - case C_HUB_OVER_CURRENT: - /* no hub-wide feature/status flags */ - break; - default: - goto error; - } - break; - case ClearPortFeature: - if (!wIndex || wIndex > ports) - goto error; - wIndex--; - temp = readl (&ehci->regs->port_status [wIndex]); - if (temp & PORT_OWNER) - break; - - switch (wValue) { - case USB_PORT_FEAT_ENABLE: - writel (temp & ~PORT_PE, - &ehci->regs->port_status [wIndex]); - break; - case USB_PORT_FEAT_C_ENABLE: - writel (temp | PORT_PEC, - &ehci->regs->port_status [wIndex]); - break; - case USB_PORT_FEAT_SUSPEND: - case USB_PORT_FEAT_C_SUSPEND: - /* ? */ - break; - case USB_PORT_FEAT_POWER: - if (HCS_PPC (ehci->hcs_params)) - writel (temp & ~PORT_POWER, - &ehci->regs->port_status [wIndex]); - break; - case USB_PORT_FEAT_C_CONNECTION: - writel (temp | PORT_CSC, - &ehci->regs->port_status [wIndex]); - break; - case USB_PORT_FEAT_C_OVER_CURRENT: - writel (temp | PORT_OCC, - &ehci->regs->port_status [wIndex]); - break; - case USB_PORT_FEAT_C_RESET: - /* GetPortStatus clears reset */ - break; - default: - goto error; - } - readl (&ehci->regs->command); /* unblock posted write */ - break; - case GetHubDescriptor: - ehci_hub_descriptor (ehci, (struct usb_hub_descriptor *) - buf); - break; - case GetHubStatus: - /* no hub-wide feature/status flags */ - memset (buf, 0, 4); - //cpu_to_le32s ((u32 *) buf); - break; - case GetPortStatus: - if (!wIndex || wIndex > ports) - goto error; - wIndex--; - status = 0; - temp = readl (&ehci->regs->port_status [wIndex]); - - // wPortChange bits - if (temp & PORT_CSC) - status |= 1 << USB_PORT_FEAT_C_CONNECTION; - if (temp & PORT_PEC) - status |= 1 << USB_PORT_FEAT_C_ENABLE; - // USB_PORT_FEAT_C_SUSPEND - if (temp & PORT_OCC) - status |= 1 << USB_PORT_FEAT_C_OVER_CURRENT; - - /* whoever resets must GetPortStatus to complete it!! */ - if ((temp & PORT_RESET) - && time_after (jiffies, - ehci->reset_done [wIndex])) { - status |= 1 << USB_PORT_FEAT_C_RESET; - - /* force reset to complete */ - writel (temp & ~PORT_RESET, - &ehci->regs->port_status [wIndex]); - do { - temp = readl ( - &ehci->regs->port_status [wIndex]); - udelay (10); - } while (temp & PORT_RESET); - - /* see what we found out */ - temp = check_reset_complete (ehci, wIndex, temp); - } - - // don't show wPortStatus if it's owned by a companion hc - if (!(temp & PORT_OWNER)) { - if (temp & PORT_CONNECT) { - status |= 1 << USB_PORT_FEAT_CONNECTION; - status |= 1 << USB_PORT_FEAT_HIGHSPEED; - } - if (temp & PORT_PE) - status |= 1 << USB_PORT_FEAT_ENABLE; - if (temp & PORT_SUSPEND) - status |= 1 << USB_PORT_FEAT_SUSPEND; - if (temp & PORT_OC) - status |= 1 << USB_PORT_FEAT_OVER_CURRENT; - if (temp & PORT_RESET) - status |= 1 << USB_PORT_FEAT_RESET; - if (temp & PORT_POWER) - status |= 1 << USB_PORT_FEAT_POWER; - } - -#ifndef EHCI_VERBOSE_DEBUG - if (status & ~0xffff) /* only if wPortChange is interesting */ -#endif - dbg_port (ehci, "GetStatus", wIndex + 1, temp); - // we "know" this alignment is good, caller used kmalloc()... - *((u32 *) buf) = cpu_to_le32 (status); - break; - case SetHubFeature: - switch (wValue) { - case C_HUB_LOCAL_POWER: - case C_HUB_OVER_CURRENT: - /* no hub-wide feature/status flags */ - break; - default: - goto error; - } - break; - case SetPortFeature: - if (!wIndex || wIndex > ports) - goto error; - wIndex--; - temp = readl (&ehci->regs->port_status [wIndex]); - if (temp & PORT_OWNER) - break; - - switch (wValue) { - case USB_PORT_FEAT_SUSPEND: - writel (temp | PORT_SUSPEND, - &ehci->regs->port_status [wIndex]); - break; - case USB_PORT_FEAT_POWER: - if (HCS_PPC (ehci->hcs_params)) - writel (temp | PORT_POWER, - &ehci->regs->port_status [wIndex]); - break; - case USB_PORT_FEAT_RESET: - /* line status bits may report this as low speed */ - if ((temp & (PORT_PE|PORT_CONNECT)) == PORT_CONNECT - && PORT_USB11 (temp)) { - ehci_dbg (ehci, - "port %d low speed --> companion\n", - wIndex + 1); - temp |= PORT_OWNER; - } else { - ehci_vdbg (ehci, "port %d reset\n", wIndex + 1); - temp |= PORT_RESET; - temp &= ~PORT_PE; - - /* - * caller must wait, then call GetPortStatus - * usb 2.0 spec says 50 ms resets on root - */ - ehci->reset_done [wIndex] = jiffies - + ((50 /* msec */ * HZ) / 1000); - } - writel (temp, &ehci->regs->port_status [wIndex]); - break; - default: - goto error; - } - readl (&ehci->regs->command); /* unblock posted writes */ - break; - - default: -error: - /* "stall" on error */ - retval = -EPIPE; - } - spin_unlock_irqrestore (&ehci->lock, flags); - return retval; -} diff -Nru a/drivers/usb/hcd/ehci-mem.c b/drivers/usb/hcd/ehci-mem.c --- a/drivers/usb/hcd/ehci-mem.c Thu Mar 6 14:23:09 2003 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,251 +0,0 @@ -/* - * Copyright (c) 2001 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. - */ - -/* this file is part of ehci-hcd.c */ - -/*-------------------------------------------------------------------------*/ - -/* - * There's basically three types of memory: - * - data used only by the HCD ... kmalloc is fine - * - async and periodic schedules, shared by HC and HCD ... these - * need to use pci_pool or pci_alloc_consistent - * - driver buffers, read/written by HC ... single shot DMA mapped - * - * There's also PCI "register" data, which is memory mapped. - * No memory seen by this driver is pagable. - */ - -/*-------------------------------------------------------------------------*/ -/* - * Allocator / cleanup for the per device structure - * Called by hcd init / removal code - */ -static struct usb_hcd *ehci_hcd_alloc (void) -{ - struct ehci_hcd *ehci; - - ehci = (struct ehci_hcd *) - kmalloc (sizeof (struct ehci_hcd), GFP_KERNEL); - if (ehci != 0) { - memset (ehci, 0, sizeof (struct ehci_hcd)); - return &ehci->hcd; - } - return 0; -} - -static void ehci_hcd_free (struct usb_hcd *hcd) -{ - kfree (hcd_to_ehci (hcd)); -} - -/*-------------------------------------------------------------------------*/ - -/* Allocate the key transfer structures from the previously allocated pool */ - -static inline void ehci_qtd_init (struct ehci_qtd *qtd, dma_addr_t dma) -{ - memset (qtd, 0, sizeof *qtd); - qtd->qtd_dma = dma; - qtd->hw_next = EHCI_LIST_END; - qtd->hw_alt_next = EHCI_LIST_END; - INIT_LIST_HEAD (&qtd->qtd_list); -} - -static struct ehci_qtd *ehci_qtd_alloc (struct ehci_hcd *ehci, int flags) -{ - struct ehci_qtd *qtd; - dma_addr_t dma; - - qtd = pci_pool_alloc (ehci->qtd_pool, flags, &dma); - if (qtd != 0) { - ehci_qtd_init (qtd, dma); - } - return qtd; -} - -static inline void ehci_qtd_free (struct ehci_hcd *ehci, struct ehci_qtd *qtd) -{ - pci_pool_free (ehci->qtd_pool, qtd, qtd->qtd_dma); -} - - -static struct ehci_qh *ehci_qh_alloc (struct ehci_hcd *ehci, int flags) -{ - struct ehci_qh *qh; - dma_addr_t dma; - - qh = (struct ehci_qh *) - pci_pool_alloc (ehci->qh_pool, flags, &dma); - if (!qh) - return qh; - - memset (qh, 0, sizeof *qh); - atomic_set (&qh->refcount, 1); - qh->qh_dma = dma; - // INIT_LIST_HEAD (&qh->qh_list); - INIT_LIST_HEAD (&qh->qtd_list); - - /* dummy td enables safe urb queuing */ - qh->dummy = ehci_qtd_alloc (ehci, flags); - if (qh->dummy == 0) { - ehci_dbg (ehci, "no dummy td\n"); - pci_pool_free (ehci->qh_pool, qh, qh->qh_dma); - qh = 0; - } - return qh; -} - -/* to share a qh (cpu threads, or hc) */ -static inline struct ehci_qh *qh_get (/* ehci, */ struct ehci_qh *qh) -{ - atomic_inc (&qh->refcount); - return qh; -} - -static void qh_put (struct ehci_hcd *ehci, struct ehci_qh *qh) -{ - if (!atomic_dec_and_test (&qh->refcount)) - return; - /* clean qtds first, and know this is not linked */ - if (!list_empty (&qh->qtd_list) || qh->qh_next.ptr) { - ehci_dbg (ehci, "unused qh not empty!\n"); - BUG (); - } - if (qh->dummy) - ehci_qtd_free (ehci, qh->dummy); - pci_pool_free (ehci->qh_pool, qh, qh->qh_dma); -} - -/*-------------------------------------------------------------------------*/ - -/* The queue heads and transfer descriptors are managed from pools tied - * to each of the "per device" structures. - * This is the initialisation and cleanup code. - */ - -static void ehci_mem_cleanup (struct ehci_hcd *ehci) -{ - if (ehci->async) - qh_put (ehci, ehci->async); - ehci->async = 0; - - /* PCI consistent memory and pools */ - if (ehci->qtd_pool) - pci_pool_destroy (ehci->qtd_pool); - ehci->qtd_pool = 0; - - if (ehci->qh_pool) { - pci_pool_destroy (ehci->qh_pool); - ehci->qh_pool = 0; - } - - if (ehci->itd_pool) - pci_pool_destroy (ehci->itd_pool); - ehci->itd_pool = 0; - - if (ehci->sitd_pool) - pci_pool_destroy (ehci->sitd_pool); - ehci->sitd_pool = 0; - - if (ehci->periodic) - pci_free_consistent (ehci->hcd.pdev, - ehci->periodic_size * sizeof (u32), - ehci->periodic, ehci->periodic_dma); - ehci->periodic = 0; - - /* shadow periodic table */ - if (ehci->pshadow) - kfree (ehci->pshadow); - ehci->pshadow = 0; -} - -/* remember to add cleanup code (above) if you add anything here */ -static int ehci_mem_init (struct ehci_hcd *ehci, int flags) -{ - int i; - - /* QTDs for control/bulk/intr transfers */ - ehci->qtd_pool = pci_pool_create ("ehci_qtd", ehci->hcd.pdev, - sizeof (struct ehci_qtd), - 32 /* byte alignment (for hw parts) */, - 4096 /* can't cross 4K */, - flags); - if (!ehci->qtd_pool) { - goto fail; - } - - /* QHs for control/bulk/intr transfers */ - ehci->qh_pool = pci_pool_create ("ehci_qh", ehci->hcd.pdev, - sizeof (struct ehci_qh), - 32 /* byte alignment (for hw parts) */, - 4096 /* can't cross 4K */, - flags); - if (!ehci->qh_pool) { - goto fail; - } - ehci->async = ehci_qh_alloc (ehci, flags); - if (!ehci->async) { - goto fail; - } - - /* ITD for high speed ISO transfers */ - ehci->itd_pool = pci_pool_create ("ehci_itd", ehci->hcd.pdev, - sizeof (struct ehci_itd), - 32 /* byte alignment (for hw parts) */, - 4096 /* can't cross 4K */, - flags); - if (!ehci->itd_pool) { - goto fail; - } - - /* SITD for full/low speed split ISO transfers */ - ehci->sitd_pool = pci_pool_create ("ehci_sitd", ehci->hcd.pdev, - sizeof (struct ehci_sitd), - 32 /* byte alignment (for hw parts) */, - 4096 /* can't cross 4K */, - flags); - if (!ehci->sitd_pool) { - goto fail; - } - - /* Hardware periodic table */ - ehci->periodic = (u32 *) - pci_alloc_consistent (ehci->hcd.pdev, - ehci->periodic_size * sizeof (u32), - &ehci->periodic_dma); - if (ehci->periodic == 0) { - goto fail; - } - for (i = 0; i < ehci->periodic_size; i++) - ehci->periodic [i] = EHCI_LIST_END; - - /* software shadow of hardware table */ - ehci->pshadow = kmalloc (ehci->periodic_size * sizeof (void *), flags); - if (ehci->pshadow == 0) { - goto fail; - } - memset (ehci->pshadow, 0, ehci->periodic_size * sizeof (void *)); - - return 0; - -fail: - ehci_dbg (ehci, "couldn't init memory\n"); - ehci_mem_cleanup (ehci); - return -ENOMEM; -} diff -Nru a/drivers/usb/hcd/ehci-q.c b/drivers/usb/hcd/ehci-q.c --- a/drivers/usb/hcd/ehci-q.c Thu Mar 6 14:23:09 2003 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,1090 +0,0 @@ -/* - * Copyright (c) 2001-2002 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. - */ - -/* this file is part of ehci-hcd.c */ - -/*-------------------------------------------------------------------------*/ - -/* - * EHCI hardware queue manipulation ... the core. QH/QTD manipulation. - * - * Control, bulk, and interrupt traffic all use "qh" lists. They list "qtd" - * entries describing USB transactions, max 16-20kB/entry (with 4kB-aligned - * buffers needed for the larger number). We use one QH per endpoint, queue - * multiple urbs (all three types) per endpoint. URBs may need several qtds. - * - * ISO traffic uses "ISO TD" (itd, and sitd) records, and (along with - * interrupts) needs careful scheduling. Performance improvements can be - * an ongoing challenge. That's in "ehci-sched.c". - * - * USB 1.1 devices are handled (a) by "companion" OHCI or UHCI root hubs, - * or otherwise through transaction translators (TTs) in USB 2.0 hubs using - * (b) special fields in qh entries or (c) split iso entries. TTs will - * buffer low/full speed data so the host collects it at high speed. - */ - -/*-------------------------------------------------------------------------*/ - -/* fill a qtd, returning how much of the buffer we were able to queue up */ - -static int -qtd_fill (struct ehci_qtd *qtd, dma_addr_t buf, size_t len, - int token, int maxpacket) -{ - int i, count; - u64 addr = buf; - - /* one buffer entry per 4K ... first might be short or unaligned */ - qtd->hw_buf [0] = cpu_to_le32 ((u32)addr); - qtd->hw_buf_hi [0] = cpu_to_le32 ((u32)(addr >> 32)); - count = 0x1000 - (buf & 0x0fff); /* rest of that page */ - if (likely (len < count)) /* ... iff needed */ - count = len; - else { - buf += 0x1000; - buf &= ~0x0fff; - - /* per-qtd limit: from 16K to 20K (best alignment) */ - for (i = 1; count < len && i < 5; i++) { - addr = buf; - qtd->hw_buf [i] = cpu_to_le32 ((u32)addr); - qtd->hw_buf_hi [i] = cpu_to_le32 ((u32)(addr >> 32)); - buf += 0x1000; - if ((count + 0x1000) < len) - count += 0x1000; - else - count = len; - } - - /* short packets may only terminate transfers */ - if (count != len) - count -= (count % maxpacket); - } - qtd->hw_token = cpu_to_le32 ((count << 16) | token); - qtd->length = count; - - return count; -} - -/*-------------------------------------------------------------------------*/ - -/* update halted (but potentially linked) qh */ - -static inline void -qh_update (struct ehci_hcd *ehci, struct ehci_qh *qh, struct ehci_qtd *qtd) -{ - qh->hw_current = 0; - qh->hw_qtd_next = QTD_NEXT (qtd->qtd_dma); - qh->hw_alt_next = EHCI_LIST_END; - - /* HC must see latest qtd and qh data before we clear ACTIVE+HALT */ - wmb (); - qh->hw_token &= __constant_cpu_to_le32 (QTD_TOGGLE | QTD_STS_PING); -} - -/*-------------------------------------------------------------------------*/ - -#define IS_SHORT_READ(token) (QTD_LENGTH (token) != 0 && QTD_PID (token) == 1) - -static void qtd_copy_status ( - struct ehci_hcd *ehci, - struct urb *urb, - size_t length, - u32 token -) -{ - /* count IN/OUT bytes, not SETUP (even short packets) */ - if (likely (QTD_PID (token) != 2)) - urb->actual_length += length - QTD_LENGTH (token); - - /* don't modify error codes */ - if (unlikely (urb->status != -EINPROGRESS)) - return; - - /* force cleanup after short read; not always an error */ - if (unlikely (IS_SHORT_READ (token))) - urb->status = -EREMOTEIO; - - /* serious "can't proceed" faults reported by the hardware */ - if (token & QTD_STS_HALT) { - if (token & QTD_STS_BABBLE) { - /* FIXME "must" disable babbling device's port too */ - urb->status = -EOVERFLOW; - } else if (token & QTD_STS_MMF) { - /* fs/ls interrupt xfer missed the complete-split */ - urb->status = -EPROTO; - } else if (token & QTD_STS_DBE) { - urb->status = (QTD_PID (token) == 1) /* IN ? */ - ? -ENOSR /* hc couldn't read data */ - : -ECOMM; /* hc couldn't write data */ - } else if (token & QTD_STS_XACT) { - /* timeout, bad crc, wrong PID, etc; retried */ - if (QTD_CERR (token)) - urb->status = -EPIPE; - else { - dbg ("3strikes"); - urb->status = -EPROTO; - } - /* CERR nonzero + no errors + halt --> stall */ - } else if (QTD_CERR (token)) - urb->status = -EPIPE; - else /* unknown */ - urb->status = -EPROTO; - - ehci_vdbg (ehci, - "dev%d ep%d%s qtd token %08x --> status %d\n", - usb_pipedevice (urb->pipe), - usb_pipeendpoint (urb->pipe), - usb_pipein (urb->pipe) ? "in" : "out", - token, urb->status); - - /* stall indicates some recovery action is needed */ - if (urb->status == -EPIPE) { - int pipe = urb->pipe; - - if (!usb_pipecontrol (pipe)) - usb_endpoint_halt (urb->dev, - usb_pipeendpoint (pipe), - usb_pipeout (pipe)); - if (urb->dev->tt && !usb_pipeint (pipe)) { -#ifdef DEBUG - struct usb_device *tt = urb->dev->tt->hub; - dbg ("clear tt %s-%s p%d buffer, a%d ep%d", - tt->bus->bus_name, tt->devpath, - urb->dev->ttport, urb->dev->devnum, - usb_pipeendpoint (pipe)); -#endif /* DEBUG */ - usb_hub_tt_clear_buffer (urb->dev, pipe); - } - } - } -} - -static void -ehci_urb_done (struct ehci_hcd *ehci, struct urb *urb, struct pt_regs *regs) -{ -#ifdef INTR_AUTOMAGIC - struct urb *resubmit = 0; - struct usb_device *dev = 0; - - static int ehci_urb_enqueue (struct usb_hcd *, struct urb *, int); -#endif - - if (likely (urb->hcpriv != 0)) { - struct ehci_qh *qh = (struct ehci_qh *) urb->hcpriv; - - /* S-mask in a QH means it's an interrupt urb */ - if ((qh->hw_info2 & cpu_to_le32 (0x00ff)) != 0) { - - /* ... update hc-wide periodic stats (for usbfs) */ - hcd_to_bus (&ehci->hcd)->bandwidth_int_reqs--; - -#ifdef INTR_AUTOMAGIC - if (!((urb->status == -ENOENT) - || (urb->status == -ECONNRESET))) { - resubmit = usb_get_urb (urb); - dev = urb->dev; - } -#endif - } - qh_put (ehci, qh); - } - - spin_lock (&urb->lock); - urb->hcpriv = 0; - switch (urb->status) { - case -EINPROGRESS: /* success */ - urb->status = 0; - default: /* fault */ - COUNT (ehci->stats.complete); - break; - case -EREMOTEIO: /* fault or normal */ - if (!(urb->transfer_flags & URB_SHORT_NOT_OK)) - urb->status = 0; - COUNT (ehci->stats.complete); - break; - case -ECONNRESET: /* canceled */ - case -ENOENT: - COUNT (ehci->stats.unlink); - break; - } - spin_unlock (&urb->lock); - - /* complete() can reenter this HCD */ - spin_unlock (&ehci->lock); - usb_hcd_giveback_urb (&ehci->hcd, urb, regs); - -#ifdef INTR_AUTOMAGIC - if (resubmit && ((urb->status == -ENOENT) - || (urb->status == -ECONNRESET))) { - usb_put_urb (resubmit); - resubmit = 0; - } - // device drivers will soon be doing something like this - if (resubmit) { - int status; - - resubmit->dev = dev; - status = SUBMIT_URB (resubmit, SLAB_ATOMIC); - if (status != 0) - err ("can't resubmit interrupt urb %p: status %d", - resubmit, status); - usb_put_urb (resubmit); - } -#endif - - spin_lock (&ehci->lock); -} - - -/* - * Process and free completed qtds for a qh, returning URBs to drivers. - * Chases up to qh->hw_current. Returns number of completions called, - * indicating how much "real" work we did. - */ -#define HALT_BIT cpu_to_le32(QTD_STS_HALT) -static unsigned -qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh, struct pt_regs *regs) -{ - struct ehci_qtd *last = 0, *end = qh->dummy; - struct list_head *entry, *tmp; - int stopped; - unsigned count = 0; - int do_status = 0; - u8 state; - - if (unlikely (list_empty (&qh->qtd_list))) - return count; - - /* completions (or tasks on other cpus) must never clobber HALT - * till we've gone through and cleaned everything up, even when - * they add urbs to this qh's queue or mark them for unlinking. - * - * NOTE: unlinking expects to be done in queue order. - */ - state = qh->qh_state; - qh->qh_state = QH_STATE_COMPLETING; - stopped = (state == QH_STATE_IDLE); - - /* remove de-activated QTDs from front of queue. - * after faults (including short reads), cleanup this urb - * then let the queue advance. - * if queue is stopped, handles unlinks. - */ - list_for_each_safe (entry, tmp, &qh->qtd_list) { - struct ehci_qtd *qtd; - struct urb *urb; - u32 token = 0; - - qtd = list_entry (entry, struct ehci_qtd, qtd_list); - urb = qtd->urb; - - /* clean up any state from previous QTD ...*/ - if (last) { - if (likely (last->urb != urb)) { - ehci_urb_done (ehci, last->urb, regs); - count++; - } - ehci_qtd_free (ehci, last); - last = 0; - } - - /* ignore urbs submitted during completions we reported */ - if (qtd == end) - break; - - /* hardware copies qtd out of qh overlay */ - rmb (); - token = le32_to_cpu (qtd->hw_token); - stopped = stopped - || (HALT_BIT & qh->hw_token) != 0 - || (ehci->hcd.state == USB_STATE_HALT); - - /* always clean up qtds the hc de-activated */ - if ((token & QTD_STS_ACTIVE) == 0) { - - /* magic dummy for short reads; won't advance */ - if (IS_SHORT_READ (token) - && !(token & QTD_STS_HALT) - && (qh->hw_alt_next & QTD_MASK) - == ehci->async->hw_alt_next) { - stopped = 1; - goto halt; - } - - /* stop scanning when we reach qtds the hc is using */ - } else if (likely (!stopped)) { - break; - - } else { - /* ignore active urbs unless some previous qtd - * for the urb faulted (including short read) or - * its urb was canceled. we may patch qh or qtds. - */ - if (likely (urb->status == -EINPROGRESS)) - continue; - - /* issue status after short control reads */ - if (unlikely (do_status != 0) - && QTD_PID (token) == 0 /* OUT */) { - do_status = 0; - continue; - } - - /* token in overlay may be most current */ - if (state == QH_STATE_IDLE - && cpu_to_le32 (qtd->qtd_dma) - == qh->hw_current) - token = le32_to_cpu (qh->hw_token); - - /* force halt for unlinked or blocked qh, so we'll - * patch the qh later and so that completions can't - * activate it while we "know" it's stopped. - */ - if ((HALT_BIT & qh->hw_token) == 0) { -halt: - qh->hw_token |= HALT_BIT; - wmb (); - } - } - - /* remove it from the queue */ - spin_lock (&urb->lock); - qtd_copy_status (ehci, urb, qtd->length, token); - do_status = (urb->status == -EREMOTEIO) - && usb_pipecontrol (urb->pipe); - spin_unlock (&urb->lock); - - if (stopped && qtd->qtd_list.prev != &qh->qtd_list) { - last = list_entry (qtd->qtd_list.prev, - struct ehci_qtd, qtd_list); - last->hw_next = qtd->hw_next; - } - list_del (&qtd->qtd_list); - last = qtd; - } - - /* last urb's completion might still need calling */ - if (likely (last != 0)) { - ehci_urb_done (ehci, last->urb, regs); - count++; - ehci_qtd_free (ehci, last); - } - - /* restore original state; caller must unlink or relink */ - qh->qh_state = state; - - /* update qh after fault cleanup */ - if (unlikely ((HALT_BIT & qh->hw_token) != 0)) { - qh_update (ehci, qh, - list_empty (&qh->qtd_list) - ? qh->dummy - : list_entry (qh->qtd_list.next, - struct ehci_qtd, qtd_list)); - } - - return count; -} -#undef HALT_BIT - -/*-------------------------------------------------------------------------*/ - -/* - * reverse of qh_urb_transaction: free a list of TDs. - * used for cleanup after errors, before HC sees an URB's TDs. - */ -static void qtd_list_free ( - struct ehci_hcd *ehci, - struct urb *urb, - struct list_head *qtd_list -) { - struct list_head *entry, *temp; - - list_for_each_safe (entry, temp, qtd_list) { - struct ehci_qtd *qtd; - - qtd = list_entry (entry, struct ehci_qtd, qtd_list); - list_del (&qtd->qtd_list); - ehci_qtd_free (ehci, qtd); - } -} - -/* - * create a list of filled qtds for this URB; won't link into qh. - */ -static struct list_head * -qh_urb_transaction ( - struct ehci_hcd *ehci, - struct urb *urb, - struct list_head *head, - int flags -) { - struct ehci_qtd *qtd, *qtd_prev; - dma_addr_t buf; - int len, maxpacket; - int is_input; - u32 token; - - /* - * URBs map to sequences of QTDs: one logical transaction - */ - qtd = ehci_qtd_alloc (ehci, flags); - if (unlikely (!qtd)) - return 0; - list_add_tail (&qtd->qtd_list, head); - qtd->urb = urb; - - token = QTD_STS_ACTIVE; - token |= (EHCI_TUNE_CERR << 10); - /* for split transactions, SplitXState initialized to zero */ - - len = urb->transfer_buffer_length; - is_input = usb_pipein (urb->pipe); - if (usb_pipecontrol (urb->pipe)) { - /* SETUP pid */ - qtd_fill (qtd, urb->setup_dma, sizeof (struct usb_ctrlrequest), - token | (2 /* "setup" */ << 8), 8); - - /* ... and always at least one more pid */ - token ^= QTD_TOGGLE; - qtd_prev = qtd; - qtd = ehci_qtd_alloc (ehci, flags); - if (unlikely (!qtd)) - goto cleanup; - qtd->urb = urb; - qtd_prev->hw_next = QTD_NEXT (qtd->qtd_dma); - list_add_tail (&qtd->qtd_list, head); - } - - /* - * data transfer stage: buffer setup - */ - if (likely (len > 0)) - buf = urb->transfer_dma; - else - buf = 0; - - // FIXME this 'buf' check break some zlps... - if (!buf || is_input) - token |= (1 /* "in" */ << 8); - /* else it's already initted to "out" pid (0 << 8) */ - - maxpacket = usb_maxpacket (urb->dev, urb->pipe, !is_input) & 0x03ff; - - /* - * buffer gets wrapped in one or more qtds; - * last one may be "short" (including zero len) - * and may serve as a control status ack - */ - for (;;) { - int this_qtd_len; - - this_qtd_len = qtd_fill (qtd, buf, len, token, maxpacket); - len -= this_qtd_len; - buf += this_qtd_len; - if (is_input) - qtd->hw_alt_next = ehci->async->hw_alt_next; - - /* qh makes control packets use qtd toggle; maybe switch it */ - if ((maxpacket & (this_qtd_len + (maxpacket - 1))) == 0) - token ^= QTD_TOGGLE; - - if (likely (len <= 0)) - break; - - qtd_prev = qtd; - qtd = ehci_qtd_alloc (ehci, flags); - if (unlikely (!qtd)) - goto cleanup; - qtd->urb = urb; - qtd_prev->hw_next = QTD_NEXT (qtd->qtd_dma); - list_add_tail (&qtd->qtd_list, head); - } - - /* unless the bulk/interrupt caller wants a chance to clean - * up after short reads, hc should advance qh past this urb - */ - if (likely ((urb->transfer_flags & URB_SHORT_NOT_OK) == 0 - || usb_pipecontrol (urb->pipe))) - qtd->hw_alt_next = EHCI_LIST_END; - - /* - * control requests may need a terminating data "status" ack; - * bulk ones may need a terminating short packet (zero length). - */ - if (likely (buf != 0)) { - int one_more = 0; - - if (usb_pipecontrol (urb->pipe)) { - one_more = 1; - token ^= 0x0100; /* "in" <--> "out" */ - token |= QTD_TOGGLE; /* force DATA1 */ - } else if (usb_pipebulk (urb->pipe) - && (urb->transfer_flags & URB_ZERO_PACKET) - && !(urb->transfer_buffer_length % maxpacket)) { - one_more = 1; - } - if (one_more) { - qtd_prev = qtd; - qtd = ehci_qtd_alloc (ehci, flags); - if (unlikely (!qtd)) - goto cleanup; - qtd->urb = urb; - qtd_prev->hw_next = QTD_NEXT (qtd->qtd_dma); - list_add_tail (&qtd->qtd_list, head); - - /* never any data in such packets */ - qtd_fill (qtd, 0, 0, token, 0); - } - } - - /* by default, enable interrupt on urb completion */ - if (likely (!(urb->transfer_flags & URB_NO_INTERRUPT))) - qtd->hw_token |= __constant_cpu_to_le32 (QTD_IOC); - return head; - -cleanup: - qtd_list_free (ehci, urb, head); - return 0; -} - -/*-------------------------------------------------------------------------*/ - -/* - * Hardware maintains data toggle (like OHCI) ... here we (re)initialize - * the hardware data toggle in the QH, and set the pseudo-toggle in udev - * so we can see if usb_clear_halt() was called. NOP for control, since - * we set up qh->hw_info1 to always use the QTD toggle bits. - */ -static inline void -clear_toggle (struct usb_device *udev, int ep, int is_out, struct ehci_qh *qh) -{ - vdbg ("clear toggle, dev %d ep 0x%x-%s", - udev->devnum, ep, is_out ? "out" : "in"); - qh->hw_token &= ~__constant_cpu_to_le32 (QTD_TOGGLE); - usb_settoggle (udev, ep, is_out, 1); -} - -// Would be best to create all qh's from config descriptors, -// when each interface/altsetting is established. Unlink -// any previous qh and cancel its urbs first; endpoints are -// implicitly reset then (data toggle too). -// That'd mean updating how usbcore talks to HCDs. (2.5?) - - -// high bandwidth multiplier, as encoded in highspeed endpoint descriptors -#define hb_mult(wMaxPacketSize) (1 + (((wMaxPacketSize) >> 11) & 0x03)) -// ... and packet size, for any kind of endpoint descriptor -#define max_packet(wMaxPacketSize) ((wMaxPacketSize) & 0x03ff) - -/* - * Each QH holds a qtd list; a QH is used for everything except iso. - * - * For interrupt urbs, the scheduler must set the microframe scheduling - * mask(s) each time the QH gets scheduled. For highspeed, that's - * just one microframe in the s-mask. For split interrupt transactions - * there are additional complications: c-mask, maybe FSTNs. - */ -static struct ehci_qh * -qh_make ( - struct ehci_hcd *ehci, - struct urb *urb, - int flags -) { - struct ehci_qh *qh = ehci_qh_alloc (ehci, flags); - u32 info1 = 0, info2 = 0; - int is_input, type; - int maxp = 0; - - if (!qh) - return qh; - - /* - * init endpoint/device data for this QH - */ - info1 |= usb_pipeendpoint (urb->pipe) << 8; - info1 |= usb_pipedevice (urb->pipe) << 0; - - is_input = usb_pipein (urb->pipe); - type = usb_pipetype (urb->pipe); - maxp = usb_maxpacket (urb->dev, urb->pipe, !is_input); - - /* Compute interrupt scheduling parameters just once, and save. - * - allowing for high bandwidth, how many nsec/uframe are used? - * - split transactions need a second CSPLIT uframe; same question - * - splits also need a schedule gap (for full/low speed I/O) - * - qh has a polling interval - * - * For control/bulk requests, the HC or TT handles these. - */ - if (type == PIPE_INTERRUPT) { - qh->usecs = usb_calc_bus_time (USB_SPEED_HIGH, is_input, 0, - hb_mult (maxp) * max_packet (maxp)); - qh->start = NO_FRAME; - - if (urb->dev->speed == USB_SPEED_HIGH) { - qh->c_usecs = 0; - qh->gap_uf = 0; - - /* FIXME handle HS periods of less than 1 frame. */ - qh->period = urb->interval >> 3; - if (qh->period < 1) { - dbg ("intr period %d uframes, NYET!", - urb->interval); - goto done; - } - } else { - /* gap is f(FS/LS transfer times) */ - qh->gap_uf = 1 + usb_calc_bus_time (urb->dev->speed, - is_input, 0, maxp) / (125 * 1000); - - /* FIXME this just approximates SPLIT/CSPLIT times */ - if (is_input) { // SPLIT, gap, CSPLIT+DATA - qh->c_usecs = qh->usecs + HS_USECS (0); - qh->usecs = HS_USECS (1); - } else { // SPLIT+DATA, gap, CSPLIT - qh->usecs += HS_USECS (1); - qh->c_usecs = HS_USECS (0); - } - - qh->period = urb->interval; - } - } - - /* using TT? */ - switch (urb->dev->speed) { - case USB_SPEED_LOW: - info1 |= (1 << 12); /* EPS "low" */ - /* FALL THROUGH */ - - case USB_SPEED_FULL: - /* EPS 0 means "full" */ - if (type != PIPE_INTERRUPT) - info1 |= (EHCI_TUNE_RL_TT << 28); - if (type == PIPE_CONTROL) { - info1 |= (1 << 27); /* for TT */ - info1 |= 1 << 14; /* toggle from qtd */ - } - info1 |= maxp << 16; - - info2 |= (EHCI_TUNE_MULT_TT << 30); - info2 |= urb->dev->ttport << 23; - info2 |= urb->dev->tt->hub->devnum << 16; - - /* NOTE: if (PIPE_INTERRUPT) { scheduler sets c-mask } */ - - break; - - case USB_SPEED_HIGH: /* no TT involved */ - info1 |= (2 << 12); /* EPS "high" */ - if (type == PIPE_CONTROL) { - info1 |= (EHCI_TUNE_RL_HS << 28); - info1 |= 64 << 16; /* usb2 fixed maxpacket */ - info1 |= 1 << 14; /* toggle from qtd */ - info2 |= (EHCI_TUNE_MULT_HS << 30); - } else if (type == PIPE_BULK) { - info1 |= (EHCI_TUNE_RL_HS << 28); - info1 |= 512 << 16; /* usb2 fixed maxpacket */ - info2 |= (EHCI_TUNE_MULT_HS << 30); - } else { /* PIPE_INTERRUPT */ - info1 |= max_packet (maxp) << 16; - info2 |= hb_mult (maxp) << 30; - } - break; - default: - dbg ("bogus dev %p speed %d", urb->dev, urb->dev->speed); -done: - qh_put (ehci, qh); - return 0; - } - - /* NOTE: if (PIPE_INTERRUPT) { scheduler sets s-mask } */ - - /* init as halted, toggle clear, advance to dummy */ - qh->qh_state = QH_STATE_IDLE; - qh->hw_info1 = cpu_to_le32 (info1); - qh->hw_info2 = cpu_to_le32 (info2); - qh_update (ehci, qh, qh->dummy); - qh->hw_token = cpu_to_le32 (QTD_STS_HALT); - usb_settoggle (urb->dev, usb_pipeendpoint (urb->pipe), !is_input, 1); - return qh; -} -#undef hb_mult -#undef hb_packet - -/*-------------------------------------------------------------------------*/ - -/* move qh (and its qtds) onto async queue; maybe enable queue. */ - -static void qh_link_async (struct ehci_hcd *ehci, struct ehci_qh *qh) -{ - u32 dma = QH_NEXT (qh->qh_dma); - struct ehci_qh *head; - - /* (re)start the async schedule? */ - head = ehci->async; - if (ehci->async_idle) - del_timer (&ehci->watchdog); - if (!head->qh_next.qh) { - u32 cmd = readl (&ehci->regs->command); - - if (!(cmd & CMD_ASE)) { - /* in case a clear of CMD_ASE didn't take yet */ - (void) handshake (&ehci->regs->status, STS_ASS, 0, 150); - cmd |= CMD_ASE | CMD_RUN; - writel (cmd, &ehci->regs->command); - ehci->hcd.state = USB_STATE_RUNNING; - /* posted write need not be known to HC yet ... */ - } - } - - qh->hw_token &= ~__constant_cpu_to_le32 (QTD_STS_HALT); - - /* splice right after start */ - qh->qh_next = head->qh_next; - qh->hw_next = head->hw_next; - wmb (); - - head->qh_next.qh = qh; - head->hw_next = dma; - - qh->qh_state = QH_STATE_LINKED; - /* qtd completions reported later by interrupt */ - - ehci->async_idle = 0; -} - -/*-------------------------------------------------------------------------*/ - -/* - * For control/bulk/interrupt, return QH with these TDs appended. - * Allocates and initializes the QH if necessary. - * Returns null if it can't allocate a QH it needs to. - * If the QH has TDs (urbs) already, that's great. - */ -static struct ehci_qh *qh_append_tds ( - struct ehci_hcd *ehci, - struct urb *urb, - struct list_head *qtd_list, - int epnum, - void **ptr -) -{ - struct ehci_qh *qh = 0; - - qh = (struct ehci_qh *) *ptr; - if (unlikely (qh == 0)) { - /* can't sleep here, we have ehci->lock... */ - qh = qh_make (ehci, urb, SLAB_ATOMIC); - *ptr = qh; - } - if (likely (qh != 0)) { - struct ehci_qtd *qtd; - - if (unlikely (list_empty (qtd_list))) - qtd = 0; - else - qtd = list_entry (qtd_list->next, struct ehci_qtd, - qtd_list); - - /* control qh may need patching after enumeration */ - if (unlikely (epnum == 0)) { - /* set_address changes the address */ - if (le32_to_cpu (qh->hw_info1 & 0x7f) == 0) - qh->hw_info1 |= cpu_to_le32 ( - usb_pipedevice (urb->pipe)); - - /* for full speed, ep0 maxpacket can grow */ - else if (!(qh->hw_info1 & cpu_to_le32 (0x3 << 12))) { - u32 info, max; - - info = le32_to_cpu (qh->hw_info1); - max = urb->dev->descriptor.bMaxPacketSize0; - if (max > (0x07ff & (info >> 16))) { - info &= ~(0x07ff << 16); - info |= max << 16; - qh->hw_info1 = cpu_to_le32 (info); - } - } - } - - /* FIXME: changing config or interface setting is not - * supported yet. preferred fix is for usbcore to tell - * us to clear out each endpoint's state, but... - */ - - /* usb_clear_halt() means qh data toggle gets reset */ - if (unlikely (!usb_gettoggle (urb->dev, - (epnum & 0x0f), !(epnum & 0x10))) - && !usb_pipecontrol (urb->pipe)) { - /* "never happens": drivers do stall cleanup right */ - if (qh->qh_state != QH_STATE_IDLE - && qh->qh_state != QH_STATE_COMPLETING) - ehci_warn (ehci, "clear toggle dev%d " - "ep%d%s: not idle\n", - usb_pipedevice (urb->pipe), - epnum & 0x0f, - usb_pipein (urb->pipe) - ? "in" : "out"); - /* else we know this overlay write is safe */ - clear_toggle (urb->dev, - epnum & 0x0f, !(epnum & 0x10), qh); - } - - /* just one way to queue requests: swap with the dummy qtd. - * only hc or qh_completions() usually modify the overlay. - */ - if (likely (qtd != 0)) { - struct ehci_qtd *dummy; - dma_addr_t dma; - u32 token; - - /* to avoid racing the HC, use the dummy td instead of - * the first td of our list (becomes new dummy). both - * tds stay deactivated until we're done, when the - * HC is allowed to fetch the old dummy (4.10.2). - */ - token = qtd->hw_token; - qtd->hw_token = 0; - wmb (); - dummy = qh->dummy; - - dma = dummy->qtd_dma; - *dummy = *qtd; - dummy->qtd_dma = dma; - - list_del (&qtd->qtd_list); - list_add (&dummy->qtd_list, qtd_list); - __list_splice (qtd_list, qh->qtd_list.prev); - - ehci_qtd_init (qtd, qtd->qtd_dma); - qh->dummy = qtd; - - /* hc must see the new dummy at list end */ - dma = qtd->qtd_dma; - qtd = list_entry (qh->qtd_list.prev, - struct ehci_qtd, qtd_list); - qtd->hw_next = QTD_NEXT (dma); - - /* let the hc process these next qtds */ - wmb (); - dummy->hw_token = token; - - urb->hcpriv = qh_get (qh); - } - } - return qh; -} - -/*-------------------------------------------------------------------------*/ - -static int -submit_async ( - struct ehci_hcd *ehci, - struct urb *urb, - struct list_head *qtd_list, - int mem_flags -) { - struct ehci_qtd *qtd; - struct hcd_dev *dev; - int epnum; - unsigned long flags; - struct ehci_qh *qh = 0; - - qtd = list_entry (qtd_list->next, struct ehci_qtd, qtd_list); - dev = (struct hcd_dev *)urb->dev->hcpriv; - epnum = usb_pipeendpoint (urb->pipe); - if (usb_pipein (urb->pipe) && !usb_pipecontrol (urb->pipe)) - epnum |= 0x10; - - vdbg ("%s: submit_async urb %p len %d ep %d-%s qtd %p [qh %p]", - hcd_to_bus (&ehci->hcd)->bus_name, - urb, urb->transfer_buffer_length, - epnum & 0x0f, (epnum & 0x10) ? "in" : "out", - qtd, dev ? dev->ep [epnum] : (void *)~0); - - spin_lock_irqsave (&ehci->lock, flags); - qh = qh_append_tds (ehci, urb, qtd_list, epnum, &dev->ep [epnum]); - - /* Control/bulk operations through TTs don't need scheduling, - * the HC and TT handle it when the TT has a buffer ready. - */ - if (likely (qh != 0)) { - if (likely (qh->qh_state == QH_STATE_IDLE)) - qh_link_async (ehci, qh_get (qh)); - } - spin_unlock_irqrestore (&ehci->lock, flags); - if (unlikely (qh == 0)) { - qtd_list_free (ehci, urb, qtd_list); - return -ENOMEM; - } - return 0; -} - -/*-------------------------------------------------------------------------*/ - -/* the async qh for the qtds being reclaimed are now unlinked from the HC */ - -static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh); - -static void end_unlink_async (struct ehci_hcd *ehci, struct pt_regs *regs) -{ - struct ehci_qh *qh = ehci->reclaim; - struct ehci_qh *next; - - del_timer (&ehci->watchdog); - - qh->hw_next = cpu_to_le32 (qh->qh_dma); - qh->qh_state = QH_STATE_IDLE; - qh->qh_next.qh = 0; - qh_put (ehci, qh); // refcount from reclaim - ehci->reclaim = 0; - ehci->reclaim_ready = 0; - - /* other unlink(s) may be pending (in QH_STATE_UNLINK_WAIT) */ - next = qh->reclaim; - qh->reclaim = 0; - - qh_completions (ehci, qh, regs); - - if (!list_empty (&qh->qtd_list) - && HCD_IS_RUNNING (ehci->hcd.state)) - qh_link_async (ehci, qh); - else { - qh_put (ehci, qh); // refcount from async list - - /* it's not free to turn the async schedule on/off; leave it - * active but idle for a while once it empties. - */ - if (HCD_IS_RUNNING (ehci->hcd.state) - && ehci->async->qh_next.qh == 0 - && !timer_pending (&ehci->watchdog)) { - ehci->async_idle = 1; - mod_timer (&ehci->watchdog, - jiffies + EHCI_ASYNC_JIFFIES); - } - } - - if (next) - start_unlink_async (ehci, next); -} - -/* makes sure the async qh will become idle */ -/* caller must own ehci->lock */ - -static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh) -{ - int cmd = readl (&ehci->regs->command); - struct ehci_qh *prev; - -#ifdef DEBUG - if (ehci->reclaim - || (qh->qh_state != QH_STATE_LINKED - && qh->qh_state != QH_STATE_UNLINK_WAIT) -#ifdef CONFIG_SMP -// this macro lies except on SMP compiles - || !spin_is_locked (&ehci->lock) -#endif - ) - BUG (); -#endif - - /* stop async schedule right now? */ - if (unlikely (qh == ehci->async)) { - /* can't get here without STS_ASS set */ - if (ehci->hcd.state != USB_STATE_HALT) { - writel (cmd & ~CMD_ASE, &ehci->regs->command); - wmb (); - // handshake later, if we need to - } - return; - } - - qh->qh_state = QH_STATE_UNLINK; - ehci->reclaim = qh = qh_get (qh); - - prev = ehci->async; - while (prev->qh_next.qh != qh) - prev = prev->qh_next.qh; - - prev->hw_next = qh->hw_next; - prev->qh_next = qh->qh_next; - wmb (); - - if (unlikely (ehci->hcd.state == USB_STATE_HALT)) { - /* if (unlikely (qh->reclaim != 0)) - * this will recurse, probably not much - */ - end_unlink_async (ehci, NULL); - return; - } - - ehci->reclaim_ready = 0; - cmd |= CMD_IAAD; - writel (cmd, &ehci->regs->command); - /* posted write need not be known to HC yet ... */ - - mod_timer (&ehci->watchdog, jiffies + EHCI_WATCHDOG_JIFFIES); -} - -/*-------------------------------------------------------------------------*/ - -static void -scan_async (struct ehci_hcd *ehci, struct pt_regs *regs) -{ - struct ehci_qh *qh; - - if (!++(ehci->stamp)) - ehci->stamp++; -rescan: - qh = ehci->async->qh_next.qh; - if (likely (qh != 0)) { - do { - /* clean any finished work for this qh */ - if (!list_empty (&qh->qtd_list) - && qh->stamp != ehci->stamp) { - int temp; - - /* unlinks could happen here; completion - * reporting drops the lock. rescan using - * the latest schedule, but don't rescan - * qhs we already finished (no looping). - */ - qh = qh_get (qh); - qh->stamp = ehci->stamp; - temp = qh_completions (ehci, qh, regs); - qh_put (ehci, qh); - if (temp != 0) { - goto rescan; - } - } - - /* unlink idle entries, reducing HC PCI usage as - * well as HCD schedule-scanning costs. - * - * FIXME don't unlink idle entries so quickly; it - * can penalize (common) half duplex protocols. - */ - if (list_empty (&qh->qtd_list) && !ehci->reclaim) { - start_unlink_async (ehci, qh); - } - - qh = qh->qh_next.qh; - } while (qh); - } -} diff -Nru a/drivers/usb/hcd/ehci-sched.c b/drivers/usb/hcd/ehci-sched.c --- a/drivers/usb/hcd/ehci-sched.c Thu Mar 6 14:23:09 2003 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,1123 +0,0 @@ -/* - * Copyright (c) 2001-2002 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. - */ - -/* this file is part of ehci-hcd.c */ - -/*-------------------------------------------------------------------------*/ - -/* - * EHCI scheduled transaction support: interrupt, iso, split iso - * These are called "periodic" transactions in the EHCI spec. - * - * Note that for interrupt transfers, the QH/QTD manipulation is shared - * with the "asynchronous" transaction support (control/bulk transfers). - * The only real difference is in how interrupt transfers are scheduled. - * We get some funky API restrictions from the current URB model, which - * works notably better for reading transfers than for writing. (And - * which accordingly needs to change before it'll work inside devices, - * or with "USB On The Go" additions to USB 2.0 ...) - */ - -static int ehci_get_frame (struct usb_hcd *hcd); - -/*-------------------------------------------------------------------------*/ - -/* - * periodic_next_shadow - return "next" pointer on shadow list - * @periodic: host pointer to qh/itd/sitd - * @tag: hardware tag for type of this record - */ -static union ehci_shadow * -periodic_next_shadow (union ehci_shadow *periodic, int tag) -{ - switch (tag) { - case Q_TYPE_QH: - return &periodic->qh->qh_next; - case Q_TYPE_FSTN: - return &periodic->fstn->fstn_next; - case Q_TYPE_ITD: - return &periodic->itd->itd_next; -#ifdef have_split_iso - case Q_TYPE_SITD: - return &periodic->sitd->sitd_next; -#endif /* have_split_iso */ - } - dbg ("BAD shadow %p tag %d", periodic->ptr, tag); - // BUG (); - return 0; -} - -/* returns true after successful unlink */ -/* caller must hold ehci->lock */ -static int periodic_unlink (struct ehci_hcd *ehci, unsigned frame, void *ptr) -{ - union ehci_shadow *prev_p = &ehci->pshadow [frame]; - u32 *hw_p = &ehci->periodic [frame]; - union ehci_shadow here = *prev_p; - union ehci_shadow *next_p; - - /* find predecessor of "ptr"; hw and shadow lists are in sync */ - while (here.ptr && here.ptr != ptr) { - prev_p = periodic_next_shadow (prev_p, Q_NEXT_TYPE (*hw_p)); - hw_p = &here.qh->hw_next; - here = *prev_p; - } - /* an interrupt entry (at list end) could have been shared */ - if (!here.ptr) { - dbg ("entry %p no longer on frame [%d]", ptr, frame); - return 0; - } - // vdbg ("periodic unlink %p from frame %d", ptr, frame); - - /* update hardware list ... HC may still know the old structure, so - * don't change hw_next until it'll have purged its cache - */ - next_p = periodic_next_shadow (&here, Q_NEXT_TYPE (*hw_p)); - *hw_p = here.qh->hw_next; - - /* unlink from shadow list; HCD won't see old structure again */ - *prev_p = *next_p; - next_p->ptr = 0; - - return 1; -} - -/* how many of the uframe's 125 usecs are allocated? */ -static unsigned short -periodic_usecs (struct ehci_hcd *ehci, unsigned frame, unsigned uframe) -{ - u32 *hw_p = &ehci->periodic [frame]; - union ehci_shadow *q = &ehci->pshadow [frame]; - unsigned usecs = 0; - - while (q->ptr) { - switch (Q_NEXT_TYPE (*hw_p)) { - case Q_TYPE_QH: - /* is it in the S-mask? */ - if (q->qh->hw_info2 & cpu_to_le32 (1 << uframe)) - usecs += q->qh->usecs; - /* ... or C-mask? */ - if (q->qh->hw_info2 & cpu_to_le32 (1 << (8 + uframe))) - usecs += q->qh->c_usecs; - q = &q->qh->qh_next; - break; - case Q_TYPE_FSTN: - /* for "save place" FSTNs, count the relevant INTR - * bandwidth from the previous frame - */ - if (q->fstn->hw_prev != EHCI_LIST_END) { - dbg ("not counting FSTN bandwidth yet ..."); - } - q = &q->fstn->fstn_next; - break; - case Q_TYPE_ITD: - /* NOTE the "one uframe per itd" policy */ - if (q->itd->hw_transaction [uframe] != 0) - usecs += q->itd->usecs; - q = &q->itd->itd_next; - break; -#ifdef have_split_iso - case Q_TYPE_SITD: - temp = q->sitd->hw_fullspeed_ep & - __constant_cpu_to_le32 (1 << 31); - - // FIXME: this doesn't count data bytes right... - - /* is it in the S-mask? (count SPLIT, DATA) */ - if (q->sitd->hw_uframe & cpu_to_le32 (1 << uframe)) { - if (temp) - usecs += HS_USECS (188); - else - usecs += HS_USECS (1); - } - - /* ... C-mask? (count CSPLIT, DATA) */ - if (q->sitd->hw_uframe & - cpu_to_le32 (1 << (8 + uframe))) { - if (temp) - usecs += HS_USECS (0); - else - usecs += HS_USECS (188); - } - q = &q->sitd->sitd_next; - break; -#endif /* have_split_iso */ - default: - BUG (); - } - } -#ifdef DEBUG - if (usecs > 100) - err ("overallocated uframe %d, periodic is %d usecs", - frame * 8 + uframe, usecs); -#endif - return usecs; -} - -/*-------------------------------------------------------------------------*/ - -static int enable_periodic (struct ehci_hcd *ehci) -{ - u32 cmd; - int status; - - /* did clearing PSE did take effect yet? - * takes effect only at frame boundaries... - */ - status = handshake (&ehci->regs->status, STS_PSS, 0, 9 * 125); - if (status != 0) { - ehci->hcd.state = USB_STATE_HALT; - return status; - } - - cmd = readl (&ehci->regs->command) | CMD_PSE; - writel (cmd, &ehci->regs->command); - /* posted write ... PSS happens later */ - ehci->hcd.state = USB_STATE_RUNNING; - - /* make sure ehci_work scans these */ - ehci->next_uframe = readl (&ehci->regs->frame_index) - % (ehci->periodic_size << 3); - return 0; -} - -static int disable_periodic (struct ehci_hcd *ehci) -{ - u32 cmd; - int status; - - /* did setting PSE not take effect yet? - * takes effect only at frame boundaries... - */ - status = handshake (&ehci->regs->status, STS_PSS, STS_PSS, 9 * 125); - if (status != 0) { - ehci->hcd.state = USB_STATE_HALT; - return status; - } - - cmd = readl (&ehci->regs->command) & ~CMD_PSE; - writel (cmd, &ehci->regs->command); - /* posted write ... */ - - ehci->next_uframe = -1; - return 0; -} - -/*-------------------------------------------------------------------------*/ - -// FIXME microframe periods not yet handled - -static void intr_deschedule ( - struct ehci_hcd *ehci, - struct ehci_qh *qh, - int wait -) { - int status; - unsigned frame = qh->start; - - do { - periodic_unlink (ehci, frame, qh); - qh_put (ehci, qh); - frame += qh->period; - } while (frame < ehci->periodic_size); - - qh->qh_state = QH_STATE_UNLINK; - qh->qh_next.ptr = 0; - ehci->periodic_sched--; - - /* maybe turn off periodic schedule */ - if (!ehci->periodic_sched) - status = disable_periodic (ehci); - else { - status = 0; - vdbg ("periodic schedule still enabled"); - } - - /* - * If the hc may be looking at this qh, then delay a uframe - * (yeech!) to be sure it's done. - * No other threads may be mucking with this qh. - */ - if (((ehci_get_frame (&ehci->hcd) - frame) % qh->period) == 0) { - if (wait) { - udelay (125); - qh->hw_next = EHCI_LIST_END; - } else { - /* we may not be IDLE yet, but if the qh is empty - * the race is very short. then if qh also isn't - * rescheduled soon, it won't matter. otherwise... - */ - vdbg ("intr_deschedule..."); - } - } else - qh->hw_next = EHCI_LIST_END; - - qh->qh_state = QH_STATE_IDLE; - - /* update per-qh bandwidth utilization (for usbfs) */ - hcd_to_bus (&ehci->hcd)->bandwidth_allocated -= - (qh->usecs + qh->c_usecs) / qh->period; - - dbg ("descheduled qh %p, period = %d frame = %d count = %d, urbs = %d", - qh, qh->period, frame, - atomic_read (&qh->refcount), ehci->periodic_sched); -} - -static int check_period ( - struct ehci_hcd *ehci, - unsigned frame, - unsigned uframe, - unsigned period, - unsigned usecs -) { - /* complete split running into next frame? - * given FSTN support, we could sometimes check... - */ - if (uframe >= 8) - return 0; - - /* - * 80% periodic == 100 usec/uframe available - * convert "usecs we need" to "max already claimed" - */ - usecs = 100 - usecs; - - do { - int claimed; - -// FIXME delete when intr_submit handles non-empty queues -// this gives us a one intr/frame limit (vs N/uframe) -// ... and also lets us avoid tracking split transactions -// that might collide at a given TT/hub. - if (ehci->pshadow [frame].ptr) - return 0; - - claimed = periodic_usecs (ehci, frame, uframe); - if (claimed > usecs) - return 0; - -// FIXME update to handle sub-frame periods - } while ((frame += period) < ehci->periodic_size); - - // success! - return 1; -} - -static int check_intr_schedule ( - struct ehci_hcd *ehci, - unsigned frame, - unsigned uframe, - const struct ehci_qh *qh, - u32 *c_maskp -) -{ - int retval = -ENOSPC; - - if (!check_period (ehci, frame, uframe, qh->period, qh->usecs)) - goto done; - if (!qh->c_usecs) { - retval = 0; - *c_maskp = cpu_to_le32 (0); - goto done; - } - - /* This is a split transaction; check the bandwidth available for - * the completion too. Check both worst and best case gaps: worst - * case is SPLIT near uframe end, and CSPLIT near start ... best is - * vice versa. Difference can be almost two uframe times, but we - * reserve unnecessary bandwidth (waste it) this way. (Actually - * even better cases exist, like immediate device NAK.) - * - * FIXME don't even bother unless we know this TT is idle in that - * range of uframes ... for now, check_period() allows only one - * interrupt transfer per frame, so needn't check "TT busy" status - * when scheduling a split (QH, SITD, or FSTN). - * - * FIXME ehci 0.96 and above can use FSTNs - */ - if (!check_period (ehci, frame, uframe + qh->gap_uf + 1, - qh->period, qh->c_usecs)) - goto done; - if (!check_period (ehci, frame, uframe + qh->gap_uf, - qh->period, qh->c_usecs)) - goto done; - - *c_maskp = cpu_to_le32 (0x03 << (8 + uframe + qh->gap_uf)); - retval = 0; -done: - return retval; -} - -static int qh_schedule (struct ehci_hcd *ehci, struct ehci_qh *qh) -{ - int status; - unsigned uframe; - u32 c_mask; - unsigned frame; /* 0..(qh->period - 1), or NO_FRAME */ - - qh->hw_next = EHCI_LIST_END; - frame = qh->start; - - /* reuse the previous schedule slots, if we can */ - if (frame < qh->period) { - uframe = ffs (le32_to_cpup (&qh->hw_info2) & 0x00ff); - status = check_intr_schedule (ehci, frame, --uframe, - qh, &c_mask); - } else { - uframe = 0; - c_mask = 0; - status = -ENOSPC; - } - - /* else scan the schedule to find a group of slots such that all - * uframes have enough periodic bandwidth available. - */ - if (status) { - frame = qh->period - 1; - do { - for (uframe = 0; uframe < 8; uframe++) { - status = check_intr_schedule (ehci, - frame, uframe, qh, - &c_mask); - if (status == 0) - break; - } - } while (status && --frame); - if (status) - goto done; - qh->start = frame; - - /* reset S-frame and (maybe) C-frame masks */ - qh->hw_info2 &= ~0xffff; - qh->hw_info2 |= cpu_to_le32 (1 << uframe) | c_mask; - } else - dbg ("reused previous qh %p schedule", qh); - - /* stuff into the periodic schedule */ - qh->qh_state = QH_STATE_LINKED; - dbg ("scheduled qh %p usecs %d/%d period %d.0 starting %d.%d (gap %d)", - qh, qh->usecs, qh->c_usecs, - qh->period, frame, uframe, qh->gap_uf); - do { - if (unlikely (ehci->pshadow [frame].ptr != 0)) { - -// FIXME -- just link toward the end, before any qh with a shorter period, -// AND accomodate it already having been linked here (after some other qh) -// AS WELL AS updating the schedule checking logic - - BUG (); - } else { - ehci->pshadow [frame].qh = qh_get (qh); - ehci->periodic [frame] = - QH_NEXT (qh->qh_dma); - } - wmb (); - frame += qh->period; - } while (frame < ehci->periodic_size); - - /* update per-qh bandwidth for usbfs */ - hcd_to_bus (&ehci->hcd)->bandwidth_allocated += - (qh->usecs + qh->c_usecs) / qh->period; - - /* maybe enable periodic schedule processing */ - if (!ehci->periodic_sched++) - status = enable_periodic (ehci); -done: - return status; -} - -static int intr_submit ( - struct ehci_hcd *ehci, - struct urb *urb, - struct list_head *qtd_list, - int mem_flags -) { - unsigned epnum; - unsigned long flags; - struct ehci_qh *qh; - struct hcd_dev *dev; - int is_input; - int status = 0; - struct list_head empty; - - /* get endpoint and transfer/schedule data */ - epnum = usb_pipeendpoint (urb->pipe); - is_input = usb_pipein (urb->pipe); - if (is_input) - epnum |= 0x10; - - spin_lock_irqsave (&ehci->lock, flags); - dev = (struct hcd_dev *)urb->dev->hcpriv; - - /* get qh and force any scheduling errors */ - INIT_LIST_HEAD (&empty); - qh = qh_append_tds (ehci, urb, &empty, epnum, &dev->ep [epnum]); - if (qh == 0) { - status = -ENOMEM; - goto done; - } - if (qh->qh_state == QH_STATE_IDLE) { - if ((status = qh_schedule (ehci, qh)) != 0) - goto done; - } - - /* then queue the urb's tds to the qh */ - qh = qh_append_tds (ehci, urb, qtd_list, epnum, &dev->ep [epnum]); - BUG_ON (qh == 0); - - /* ... update usbfs periodic stats */ - hcd_to_bus (&ehci->hcd)->bandwidth_int_reqs++; - -done: - spin_unlock_irqrestore (&ehci->lock, flags); - if (status) - qtd_list_free (ehci, urb, qtd_list); - - return status; -} - -static unsigned -intr_complete ( - struct ehci_hcd *ehci, - unsigned frame, - struct ehci_qh *qh, - struct pt_regs *regs -) { - unsigned count; - - /* nothing to report? */ - if (likely ((qh->hw_token & __constant_cpu_to_le32 (QTD_STS_ACTIVE)) - != 0)) - return 0; - if (unlikely (list_empty (&qh->qtd_list))) { - dbg ("intr qh %p no TDs?", qh); - return 0; - } - - /* handle any completions */ - count = qh_completions (ehci, qh, regs); - - if (unlikely (list_empty (&qh->qtd_list))) - intr_deschedule (ehci, qh, 0); - - return count; -} - -/*-------------------------------------------------------------------------*/ - -static void -itd_free_list (struct ehci_hcd *ehci, struct urb *urb) -{ - struct ehci_itd *first_itd = urb->hcpriv; - - while (!list_empty (&first_itd->itd_list)) { - struct ehci_itd *itd; - - itd = list_entry ( - first_itd->itd_list.next, - struct ehci_itd, itd_list); - list_del (&itd->itd_list); - pci_pool_free (ehci->itd_pool, itd, itd->itd_dma); - } - pci_pool_free (ehci->itd_pool, first_itd, first_itd->itd_dma); - urb->hcpriv = 0; -} - -static int -itd_fill ( - struct ehci_hcd *ehci, - struct ehci_itd *itd, - struct urb *urb, - unsigned index, // urb->iso_frame_desc [index] - dma_addr_t dma // mapped transfer buffer -) { - u64 temp; - u32 buf1; - unsigned i, epnum, maxp, multi; - unsigned length; - int is_input; - - itd->hw_next = EHCI_LIST_END; - itd->urb = urb; - itd->index = index; - - /* tell itd about its transfer buffer, max 2 pages */ - length = urb->iso_frame_desc [index].length; - dma += urb->iso_frame_desc [index].offset; - temp = dma & ~0x0fff; - for (i = 0; i < 2; i++) { - itd->hw_bufp [i] = cpu_to_le32 ((u32) temp); - itd->hw_bufp_hi [i] = cpu_to_le32 ((u32)(temp >> 32)); - temp += 0x1000; - } - itd->buf_dma = dma; - - /* - * this might be a "high bandwidth" highspeed endpoint, - * as encoded in the ep descriptor's maxpacket field - */ - epnum = usb_pipeendpoint (urb->pipe); - is_input = usb_pipein (urb->pipe); - if (is_input) { - maxp = urb->dev->epmaxpacketin [epnum]; - buf1 = (1 << 11); - } else { - maxp = urb->dev->epmaxpacketout [epnum]; - buf1 = 0; - } - buf1 |= (maxp & 0x03ff); - multi = 1; - multi += (maxp >> 11) & 0x03; - maxp &= 0x03ff; - maxp *= multi; - - /* transfer can't fit in any uframe? */ - if (length < 0 || maxp < length) { - dbg ("BAD iso packet: %d bytes, max %d, urb %p [%d] (of %d)", - length, maxp, urb, index, - urb->iso_frame_desc [index].length); - return -ENOSPC; - } - itd->usecs = usb_calc_bus_time (USB_SPEED_HIGH, is_input, 1, length); - - /* "plus" info in low order bits of buffer pointers */ - itd->hw_bufp [0] |= cpu_to_le32 ((epnum << 8) | urb->dev->devnum); - itd->hw_bufp [1] |= cpu_to_le32 (buf1); - itd->hw_bufp [2] |= cpu_to_le32 (multi); - - /* figure hw_transaction[] value (it's scheduled later) */ - itd->transaction = EHCI_ISOC_ACTIVE; - itd->transaction |= dma & 0x0fff; /* offset; buffer=0 */ - if ((index + 1) == urb->number_of_packets) - itd->transaction |= EHCI_ITD_IOC; /* end-of-urb irq */ - itd->transaction |= length << 16; - cpu_to_le32s (&itd->transaction); - - return 0; -} - -static int -itd_urb_transaction ( - struct ehci_hcd *ehci, - struct urb *urb, - int mem_flags -) { - int frame_index; - struct ehci_itd *first_itd, *itd; - int status; - dma_addr_t itd_dma; - - /* allocate/init ITDs */ - for (frame_index = 0, first_itd = 0; - frame_index < urb->number_of_packets; - frame_index++) { - itd = pci_pool_alloc (ehci->itd_pool, mem_flags, &itd_dma); - if (!itd) { - status = -ENOMEM; - goto fail; - } - memset (itd, 0, sizeof *itd); - itd->itd_dma = itd_dma; - - status = itd_fill (ehci, itd, urb, frame_index, - urb->transfer_dma); - if (status != 0) - goto fail; - - if (first_itd) - list_add_tail (&itd->itd_list, - &first_itd->itd_list); - else { - INIT_LIST_HEAD (&itd->itd_list); - urb->hcpriv = first_itd = itd; - } - } - urb->error_count = 0; - return 0; - -fail: - if (urb->hcpriv) - itd_free_list (ehci, urb); - return status; -} - -/*-------------------------------------------------------------------------*/ - -static inline void -itd_link (struct ehci_hcd *ehci, unsigned frame, struct ehci_itd *itd) -{ - /* always prepend ITD/SITD ... only QH tree is order-sensitive */ - itd->itd_next = ehci->pshadow [frame]; - itd->hw_next = ehci->periodic [frame]; - ehci->pshadow [frame].itd = itd; - ehci->periodic [frame] = cpu_to_le32 (itd->itd_dma) | Q_TYPE_ITD; -} - -/* - * return zero on success, else -errno - * - start holds first uframe to start scheduling into - * - max is the first uframe it's NOT (!) OK to start scheduling into - * math to be done modulo "mod" (ehci->periodic_size << 3) - */ -static int get_iso_range ( - struct ehci_hcd *ehci, - struct urb *urb, - unsigned *start, - unsigned *max, - unsigned mod -) { - struct list_head *lh; - struct hcd_dev *dev = urb->dev->hcpriv; - int last = -1; - unsigned now, span, end; - - span = urb->interval * urb->number_of_packets; - - /* first see if we know when the next transfer SHOULD happen */ - list_for_each (lh, &dev->urb_list) { - struct urb *u; - struct ehci_itd *itd; - unsigned s; - - u = list_entry (lh, struct urb, urb_list); - if (u == urb || u->pipe != urb->pipe) - continue; - if (u->interval != urb->interval) { /* must not change! */ - dbg ("urb %p interval %d ... != %p interval %d", - u, u->interval, urb, urb->interval); - return -EINVAL; - } - - /* URB for this endpoint... covers through when? */ - itd = urb->hcpriv; - s = itd->uframe + u->interval * u->number_of_packets; - if (last < 0) - last = s; - else { - /* - * So far we can only queue two ISO URBs... - * - * FIXME do interval math, figure out whether - * this URB is "before" or not ... also, handle - * the case where the URB might have completed, - * but hasn't yet been processed. - */ - dbg ("NYET: queue >2 URBs per ISO endpoint"); - return -EDOM; - } - } - - /* calculate the legal range [start,max) */ - now = readl (&ehci->regs->frame_index) + 1; /* next uframe */ - if (!ehci->periodic_sched) - now += 8; /* startup delay */ - now %= mod; - end = now + mod; - if (last < 0) { - *start = now + ehci->i_thresh + /* paranoia */ 1; - *max = end - span; - if (*max < *start + 1) - *max = *start + 1; - } else { - *start = last % mod; - *max = (last + 1) % mod; - } - - /* explicit start frame? */ - if (!(urb->transfer_flags & URB_ISO_ASAP)) { - unsigned temp; - - /* sanity check: must be in range */ - urb->start_frame %= ehci->periodic_size; - temp = urb->start_frame << 3; - if (temp < *start) - temp += mod; - if (temp > *max) - return -EDOM; - - /* use that explicit start frame */ - *start = urb->start_frame << 3; - temp += 8; - if (temp < *max) - *max = temp; - } - - // FIXME minimize wraparound to "now" ... insist max+span - // (and start+span) remains a few frames short of "end" - - *max %= ehci->periodic_size; - if ((*start + span) < end) - return 0; - return -EFBIG; -} - -static int -itd_schedule (struct ehci_hcd *ehci, struct urb *urb) -{ - unsigned start, max, i; - int status; - unsigned mod = ehci->periodic_size << 3; - - for (i = 0; i < urb->number_of_packets; i++) { - urb->iso_frame_desc [i].status = -EINPROGRESS; - urb->iso_frame_desc [i].actual_length = 0; - } - - if ((status = get_iso_range (ehci, urb, &start, &max, mod)) != 0) - return status; - - do { - unsigned uframe; - unsigned usecs; - struct ehci_itd *itd; - - /* check schedule: enough space? */ - itd = urb->hcpriv; - uframe = start; - for (i = 0, uframe = start; - i < urb->number_of_packets; - i++, uframe += urb->interval) { - uframe %= mod; - - /* can't commit more than 80% periodic == 100 usec */ - if (periodic_usecs (ehci, uframe >> 3, uframe & 0x7) - > (100 - itd->usecs)) { - itd = 0; - break; - } - itd = list_entry (itd->itd_list.next, - struct ehci_itd, itd_list); - } - if (!itd) - continue; - - /* that's where we'll schedule this! */ - itd = urb->hcpriv; - urb->start_frame = start >> 3; - vdbg ("ISO urb %p (%d packets period %d) starting %d.%d", - urb, urb->number_of_packets, urb->interval, - urb->start_frame, start & 0x7); - for (i = 0, uframe = start, usecs = 0; - i < urb->number_of_packets; - i++, uframe += urb->interval) { - uframe %= mod; - - itd->uframe = uframe; - itd->hw_transaction [uframe & 0x07] = itd->transaction; - itd_link (ehci, (uframe >> 3) % ehci->periodic_size, - itd); - wmb (); - usecs += itd->usecs; - - itd = list_entry (itd->itd_list.next, - struct ehci_itd, itd_list); - } - - /* update bandwidth utilization records (for usbfs) - * - * FIXME This claims each URB queued to an endpoint, as if - * transfers were concurrent, not sequential. So bandwidth - * typically gets double-billed ... comes from tying it to - * URBs rather than endpoints in the schedule. Luckily we - * don't use this usbfs data for serious decision making. - */ - usecs /= urb->number_of_packets; - usecs /= urb->interval; - usecs >>= 3; - if (usecs < 1) - usecs = 1; - usb_claim_bandwidth (urb->dev, urb, usecs, 1); - - /* maybe enable periodic schedule processing */ - if (!ehci->periodic_sched++) { - if ((status = enable_periodic (ehci)) != 0) { - // FIXME deschedule right away - err ("itd_schedule, enable = %d", status); - } - } - - return 0; - - } while ((start = ++start % mod) != max); - - /* no room in the schedule */ - dbg ("urb %p, CAN'T SCHEDULE", urb); - return -ENOSPC; -} - -/*-------------------------------------------------------------------------*/ - -#define ISO_ERRS (EHCI_ISOC_BUF_ERR | EHCI_ISOC_BABBLE | EHCI_ISOC_XACTERR) - -static unsigned -itd_complete ( - struct ehci_hcd *ehci, - struct ehci_itd *itd, - unsigned uframe, - struct pt_regs *regs -) { - struct urb *urb = itd->urb; - struct usb_iso_packet_descriptor *desc; - u32 t; - - /* update status for this uframe's transfers */ - desc = &urb->iso_frame_desc [itd->index]; - - t = itd->hw_transaction [uframe]; - itd->hw_transaction [uframe] = 0; - if (t & EHCI_ISOC_ACTIVE) - desc->status = -EXDEV; - else if (t & ISO_ERRS) { - urb->error_count++; - if (t & EHCI_ISOC_BUF_ERR) - desc->status = usb_pipein (urb->pipe) - ? -ENOSR /* couldn't read */ - : -ECOMM; /* couldn't write */ - else if (t & EHCI_ISOC_BABBLE) - desc->status = -EOVERFLOW; - else /* (t & EHCI_ISOC_XACTERR) */ - desc->status = -EPROTO; - - /* HC need not update length with this error */ - if (!(t & EHCI_ISOC_BABBLE)) - desc->actual_length += EHCI_ITD_LENGTH (t); - } else { - desc->status = 0; - desc->actual_length += EHCI_ITD_LENGTH (t); - } - - vdbg ("itd %p urb %p packet %d/%d trans %x status %d len %d", - itd, urb, itd->index + 1, urb->number_of_packets, - t, desc->status, desc->actual_length); - - /* handle completion now? */ - if ((itd->index + 1) != urb->number_of_packets) - return 0; - - /* - * Always give the urb back to the driver ... expect it to submit - * a new urb (or resubmit this), and to have another already queued - * when un-interrupted transfers are needed. - * - * NOTE that for now we don't accelerate ISO unlinks; they just - * happen according to the current schedule. Means a delay of - * up to about a second (max). - */ - itd_free_list (ehci, urb); - if (urb->status == -EINPROGRESS) - urb->status = 0; - - /* complete() can reenter this HCD */ - spin_unlock (&ehci->lock); - usb_hcd_giveback_urb (&ehci->hcd, urb, regs); - spin_lock (&ehci->lock); - - /* defer stopping schedule; completion can submit */ - ehci->periodic_sched--; - if (!ehci->periodic_sched) - (void) disable_periodic (ehci); - - return 1; -} - -/*-------------------------------------------------------------------------*/ - -static int itd_submit (struct ehci_hcd *ehci, struct urb *urb, int mem_flags) -{ - int status; - unsigned long flags; - - dbg ("itd_submit urb %p", urb); - - /* allocate ITDs w/o locking anything */ - status = itd_urb_transaction (ehci, urb, mem_flags); - if (status < 0) - return status; - - /* schedule ... need to lock */ - spin_lock_irqsave (&ehci->lock, flags); - status = itd_schedule (ehci, urb); - spin_unlock_irqrestore (&ehci->lock, flags); - if (status < 0) - itd_free_list (ehci, urb); - - return status; -} - -#ifdef have_split_iso - -/*-------------------------------------------------------------------------*/ - -/* - * "Split ISO TDs" ... used for USB 1.1 devices going through - * the TTs in USB 2.0 hubs. - * - * FIXME not yet implemented - */ - -#endif /* have_split_iso */ - -/*-------------------------------------------------------------------------*/ - -static void -scan_periodic (struct ehci_hcd *ehci, struct pt_regs *regs) -{ - unsigned frame, clock, now_uframe, mod; - unsigned count = 0; - - mod = ehci->periodic_size << 3; - - /* - * When running, scan from last scan point up to "now" - * else clean up by scanning everything that's left. - * Touches as few pages as possible: cache-friendly. - * Don't scan ISO entries more than once, though. - */ - frame = ehci->next_uframe >> 3; - if (HCD_IS_RUNNING (ehci->hcd.state)) - now_uframe = readl (&ehci->regs->frame_index); - else - now_uframe = (frame << 3) - 1; - now_uframe %= mod; - clock = now_uframe >> 3; - - for (;;) { - union ehci_shadow q, *q_p; - u32 type, *hw_p; - unsigned uframes; - -restart: - /* scan schedule to _before_ current frame index */ - if (frame == clock) - uframes = now_uframe & 0x07; - else - uframes = 8; - - q_p = &ehci->pshadow [frame]; - hw_p = &ehci->periodic [frame]; - q.ptr = q_p->ptr; - type = Q_NEXT_TYPE (*hw_p); - - /* scan each element in frame's queue for completions */ - while (q.ptr != 0) { - int last; - unsigned uf; - union ehci_shadow temp; - - switch (type) { - case Q_TYPE_QH: - last = (q.qh->hw_next == EHCI_LIST_END); - temp = q.qh->qh_next; - type = Q_NEXT_TYPE (q.qh->hw_next); - count += intr_complete (ehci, frame, - qh_get (q.qh), regs); - qh_put (ehci, q.qh); - q = temp; - break; - case Q_TYPE_FSTN: - last = (q.fstn->hw_next == EHCI_LIST_END); - /* for "save place" FSTNs, look at QH entries - * in the previous frame for completions. - */ - if (q.fstn->hw_prev != EHCI_LIST_END) { - dbg ("ignoring completions from FSTNs"); - } - type = Q_NEXT_TYPE (q.fstn->hw_next); - q = q.fstn->fstn_next; - break; - case Q_TYPE_ITD: - last = (q.itd->hw_next == EHCI_LIST_END); - - /* Unlink each (S)ITD we see, since the ISO - * URB model forces constant rescheduling. - * That complicates sharing uframes in ITDs, - * and means we need to skip uframes the HC - * hasn't yet processed. - */ - for (uf = 0; uf < uframes; uf++) { - if (q.itd->hw_transaction [uf] != 0) { - temp = q; - *q_p = q.itd->itd_next; - *hw_p = q.itd->hw_next; - type = Q_NEXT_TYPE (*hw_p); - - /* might free q.itd ... */ - count += itd_complete (ehci, - temp.itd, uf, regs); - break; - } - } - /* we might skip this ITD's uframe ... */ - if (uf == uframes) { - q_p = &q.itd->itd_next; - hw_p = &q.itd->hw_next; - type = Q_NEXT_TYPE (q.itd->hw_next); - } - - q = *q_p; - break; -#ifdef have_split_iso - case Q_TYPE_SITD: - last = (q.sitd->hw_next == EHCI_LIST_END); - sitd_complete (ehci, q.sitd); - type = Q_NEXT_TYPE (q.sitd->hw_next); - - // FIXME unlink SITD after split completes - q = q.sitd->sitd_next; - break; -#endif /* have_split_iso */ - default: - dbg ("corrupt type %d frame %d shadow %p", - type, frame, q.ptr); - // BUG (); - last = 1; - q.ptr = 0; - } - - /* did completion remove an interior q entry? */ - if (unlikely (q.ptr == 0 && !last)) - goto restart; - } - - /* stop when we catch up to the HC */ - - // FIXME: this assumes we won't get lapped when - // latencies climb; that should be rare, but... - // detect it, and just go all the way around. - // FLR might help detect this case, so long as latencies - // don't exceed periodic_size msec (default 1.024 sec). - - // FIXME: likewise assumes HC doesn't halt mid-scan - - if (frame == clock) { - unsigned now; - - if (!HCD_IS_RUNNING (ehci->hcd.state)) - break; - ehci->next_uframe = now_uframe; - now = readl (&ehci->regs->frame_index) % mod; - if (now_uframe == now) - break; - - /* rescan the rest of this frame, then ... */ - now_uframe = now; - clock = now_uframe >> 3; - } else - frame = (frame + 1) % ehci->periodic_size; - } -} diff -Nru a/drivers/usb/hcd/ehci.h b/drivers/usb/hcd/ehci.h --- a/drivers/usb/hcd/ehci.h Thu Mar 6 14:23:09 2003 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,452 +0,0 @@ -/* - * Copyright (c) 2001-2002 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. - */ - -#ifndef __LINUX_EHCI_HCD_H -#define __LINUX_EHCI_HCD_H - -/* definitions used for the EHCI driver */ - -/* statistics can be kept for for tuning/monitoring */ -struct ehci_stats { - /* irq usage */ - unsigned long normal; - unsigned long error; - unsigned long reclaim; - - /* termination of urbs from core */ - unsigned long complete; - unsigned long unlink; -}; - -/* ehci_hcd->lock guards shared data against other CPUs: - * ehci_hcd: async, reclaim, periodic (and shadow), ... - * hcd_dev: ep[] - * ehci_qh: qh_next, qtd_list - * ehci_qtd: qtd_list - * - * Also, hold this lock when talking to HC registers or - * when updating hw_* fields in shared qh/qtd/... structures. - */ - -#define EHCI_MAX_ROOT_PORTS 15 /* see HCS_N_PORTS */ - -struct ehci_hcd { /* one per controller */ - spinlock_t lock; - - /* async schedule support */ - struct ehci_qh *async; - struct ehci_qh *reclaim; - int reclaim_ready : 1, - async_idle : 1; - - /* periodic schedule support */ -#define DEFAULT_I_TDPS 1024 /* some HCs can do less */ - unsigned periodic_size; - u32 *periodic; /* hw periodic table */ - dma_addr_t periodic_dma; - unsigned i_thresh; /* uframes HC might cache */ - - union ehci_shadow *pshadow; /* mirror hw periodic table */ - int next_uframe; /* scan periodic, start here */ - unsigned periodic_sched; /* periodic activity count */ - - /* per root hub port */ - unsigned long reset_done [EHCI_MAX_ROOT_PORTS]; - - /* glue to PCI and HCD framework */ - struct usb_hcd hcd; - struct ehci_caps *caps; - struct ehci_regs *regs; - u32 hcs_params; /* cached register copy */ - - /* per-HC memory pools (could be per-PCI-bus, but ...) */ - struct pci_pool *qh_pool; /* qh per active urb */ - struct pci_pool *qtd_pool; /* one or more per qh */ - struct pci_pool *itd_pool; /* itd per iso urb */ - struct pci_pool *sitd_pool; /* sitd per split iso urb */ - - struct timer_list watchdog; - unsigned stamp; - -#ifdef EHCI_STATS - struct ehci_stats stats; -# define COUNT(x) do { (x)++; } while (0) -#else -# define COUNT(x) do {} while (0) -#endif -}; - -/* unwrap an HCD pointer to get an EHCI_HCD pointer */ -#define hcd_to_ehci(hcd_ptr) container_of(hcd_ptr, struct ehci_hcd, hcd) - -/* NOTE: urb->transfer_flags expected to not use this bit !!! */ -#define EHCI_STATE_UNLINK 0x8000 /* urb being unlinked */ - -/*-------------------------------------------------------------------------*/ - -/* EHCI register interface, corresponds to EHCI Revision 0.95 specification */ - -/* Section 2.2 Host Controller Capability Registers */ -struct ehci_caps { - u8 length; /* CAPLENGTH - size of this struct */ - u8 reserved; /* offset 0x1 */ - u16 hci_version; /* HCIVERSION - offset 0x2 */ - u32 hcs_params; /* HCSPARAMS - offset 0x4 */ -#define HCS_DEBUG_PORT(p) (((p)>>20)&0xf) /* bits 23:20, debug port? */ -#define HCS_INDICATOR(p) ((p)&(1 << 16)) /* true: has port indicators */ -#define HCS_N_CC(p) (((p)>>12)&0xf) /* bits 15:12, #companion HCs */ -#define HCS_N_PCC(p) (((p)>>8)&0xf) /* bits 11:8, ports per CC */ -#define HCS_PORTROUTED(p) ((p)&(1 << 7)) /* true: port routing */ -#define HCS_PPC(p) ((p)&(1 << 4)) /* true: port power control */ -#define HCS_N_PORTS(p) (((p)>>0)&0xf) /* bits 3:0, ports on HC */ - - u32 hcc_params; /* HCCPARAMS - offset 0x8 */ -#define HCC_EXT_CAPS(p) (((p)>>8)&0xff) /* for pci extended caps */ -#define HCC_ISOC_CACHE(p) ((p)&(1 << 7)) /* true: can cache isoc frame */ -#define HCC_ISOC_THRES(p) (((p)>>4)&0x7) /* bits 6:4, uframes cached */ -#define HCC_CANPARK(p) ((p)&(1 << 2)) /* true: can park on async qh */ -#define HCC_PGM_FRAMELISTLEN(p) ((p)&(1 << 1)) /* true: periodic_size changes*/ -#define HCC_64BIT_ADDR(p) ((p)&(1)) /* true: can use 64-bit addr */ - u8 portroute [8]; /* nibbles for routing - offset 0xC */ -} __attribute__ ((packed)); - - -/* Section 2.3 Host Controller Operational Registers */ -struct ehci_regs { - - /* USBCMD: offset 0x00 */ - u32 command; -/* 23:16 is r/w intr rate, in microframes; default "8" == 1/msec */ -#define CMD_PARK (1<<11) /* enable "park" on async qh */ -#define CMD_PARK_CNT(c) (((c)>>8)&3) /* how many transfers to park for */ -#define CMD_LRESET (1<<7) /* partial reset (no ports, etc) */ -#define CMD_IAAD (1<<6) /* "doorbell" interrupt async advance */ -#define CMD_ASE (1<<5) /* async schedule enable */ -#define CMD_PSE (1<<4) /* periodic schedule enable */ -/* 3:2 is periodic frame list size */ -#define CMD_RESET (1<<1) /* reset HC not bus */ -#define CMD_RUN (1<<0) /* start/stop HC */ - - /* USBSTS: offset 0x04 */ - u32 status; -#define STS_ASS (1<<15) /* Async Schedule Status */ -#define STS_PSS (1<<14) /* Periodic Schedule Status */ -#define STS_RECL (1<<13) /* Reclamation */ -#define STS_HALT (1<<12) /* Not running (any reason) */ -/* some bits reserved */ - /* these STS_* flags are also intr_enable bits (USBINTR) */ -#define STS_IAA (1<<5) /* Interrupted on async advance */ -#define STS_FATAL (1<<4) /* such as some PCI access errors */ -#define STS_FLR (1<<3) /* frame list rolled over */ -#define STS_PCD (1<<2) /* port change detect */ -#define STS_ERR (1<<1) /* "error" completion (overflow, ...) */ -#define STS_INT (1<<0) /* "normal" completion (short, ...) */ - - /* USBINTR: offset 0x08 */ - u32 intr_enable; - - /* FRINDEX: offset 0x0C */ - u32 frame_index; /* current microframe number */ - /* CTRLDSSEGMENT: offset 0x10 */ - u32 segment; /* address bits 63:32 if needed */ - /* PERIODICLISTBASE: offset 0x14 */ - u32 frame_list; /* points to periodic list */ - /* ASYNCICLISTADDR: offset 0x18 */ - u32 async_next; /* address of next async queue head */ - - u32 reserved [9]; - - /* CONFIGFLAG: offset 0x40 */ - u32 configured_flag; -#define FLAG_CF (1<<0) /* true: we'll support "high speed" */ - - /* PORTSC: offset 0x44 */ - u32 port_status [0]; /* up to N_PORTS */ -/* 31:23 reserved */ -#define PORT_WKOC_E (1<<22) /* wake on overcurrent (enable) */ -#define PORT_WKDISC_E (1<<21) /* wake on disconnect (enable) */ -#define PORT_WKCONN_E (1<<20) /* wake on connect (enable) */ -/* 19:16 for port testing */ -/* 15:14 for using port indicator leds (if HCS_INDICATOR allows) */ -#define PORT_OWNER (1<<13) /* true: companion hc owns this port */ -#define PORT_POWER (1<<12) /* true: has power (see PPC) */ -#define PORT_USB11(x) (((x)&(3<<10))==(1<<10)) /* USB 1.1 device */ -/* 11:10 for detecting lowspeed devices (reset vs release ownership) */ -/* 9 reserved */ -#define PORT_RESET (1<<8) /* reset port */ -#define PORT_SUSPEND (1<<7) /* suspend port */ -#define PORT_RESUME (1<<6) /* resume it */ -#define PORT_OCC (1<<5) /* over current change */ -#define PORT_OC (1<<4) /* over current active */ -#define PORT_PEC (1<<3) /* port enable change */ -#define PORT_PE (1<<2) /* port enable */ -#define PORT_CSC (1<<1) /* connect status change */ -#define PORT_CONNECT (1<<0) /* device connected */ -} __attribute__ ((packed)); - - -/*-------------------------------------------------------------------------*/ - -#define QTD_NEXT(dma) cpu_to_le32((u32)dma) - -/* - * EHCI Specification 0.95 Section 3.5 - * QTD: describe data transfer components (buffer, direction, ...) - * See Fig 3-6 "Queue Element Transfer Descriptor Block Diagram". - * - * These are associated only with "QH" (Queue Head) structures, - * used with control, bulk, and interrupt transfers. - */ -struct ehci_qtd { - /* first part defined by EHCI spec */ - u32 hw_next; /* see EHCI 3.5.1 */ - u32 hw_alt_next; /* see EHCI 3.5.2 */ - u32 hw_token; /* see EHCI 3.5.3 */ -#define QTD_TOGGLE (1 << 31) /* data toggle */ -#define QTD_LENGTH(tok) (((tok)>>16) & 0x7fff) -#define QTD_IOC (1 << 15) /* interrupt on complete */ -#define QTD_CERR(tok) (((tok)>>10) & 0x3) -#define QTD_PID(tok) (((tok)>>8) & 0x3) -#define QTD_STS_ACTIVE (1 << 7) /* HC may execute this */ -#define QTD_STS_HALT (1 << 6) /* halted on error */ -#define QTD_STS_DBE (1 << 5) /* data buffer error (in HC) */ -#define QTD_STS_BABBLE (1 << 4) /* device was babbling (qtd halted) */ -#define QTD_STS_XACT (1 << 3) /* device gave illegal response */ -#define QTD_STS_MMF (1 << 2) /* incomplete split transaction */ -#define QTD_STS_STS (1 << 1) /* split transaction state */ -#define QTD_STS_PING (1 << 0) /* issue PING? */ - u32 hw_buf [5]; /* see EHCI 3.5.4 */ - u32 hw_buf_hi [5]; /* Appendix B */ - - /* the rest is HCD-private */ - dma_addr_t qtd_dma; /* qtd address */ - struct list_head qtd_list; /* sw qtd list */ - struct urb *urb; /* qtd's urb */ - size_t length; /* length of buffer */ -} __attribute__ ((aligned (32))); - -#define QTD_MASK cpu_to_le32 (~0x1f) /* mask NakCnt+T in qh->hw_alt_next */ - -/*-------------------------------------------------------------------------*/ - -/* type tag from {qh,itd,sitd,fstn}->hw_next */ -#define Q_NEXT_TYPE(dma) ((dma) & __constant_cpu_to_le32 (3 << 1)) - -/* values for that type tag */ -#define Q_TYPE_ITD __constant_cpu_to_le32 (0 << 1) -#define Q_TYPE_QH __constant_cpu_to_le32 (1 << 1) -#define Q_TYPE_SITD __constant_cpu_to_le32 (2 << 1) -#define Q_TYPE_FSTN __constant_cpu_to_le32 (3 << 1) - -/* next async queue entry, or pointer to interrupt/periodic QH */ -#define QH_NEXT(dma) (cpu_to_le32(((u32)dma)&~0x01f)|Q_TYPE_QH) - -/* for periodic/async schedules and qtd lists, mark end of list */ -#define EHCI_LIST_END __constant_cpu_to_le32(1) /* "null pointer" to hw */ - -/* - * Entries in periodic shadow table are pointers to one of four kinds - * of data structure. That's dictated by the hardware; a type tag is - * encoded in the low bits of the hardware's periodic schedule. Use - * Q_NEXT_TYPE to get the tag. - * - * For entries in the async schedule, the type tag always says "qh". - */ -union ehci_shadow { - struct ehci_qh *qh; /* Q_TYPE_QH */ - struct ehci_itd *itd; /* Q_TYPE_ITD */ - struct ehci_sitd *sitd; /* Q_TYPE_SITD */ - struct ehci_fstn *fstn; /* Q_TYPE_FSTN */ - void *ptr; -}; - -/*-------------------------------------------------------------------------*/ - -/* - * EHCI Specification 0.95 Section 3.6 - * QH: describes control/bulk/interrupt endpoints - * See Fig 3-7 "Queue Head Structure Layout". - * - * These appear in both the async and (for interrupt) periodic schedules. - */ - -struct ehci_qh { - /* first part defined by EHCI spec */ - u32 hw_next; /* see EHCI 3.6.1 */ - u32 hw_info1; /* see EHCI 3.6.2 */ -#define QH_HEAD 0x00008000 - u32 hw_info2; /* see EHCI 3.6.2 */ - u32 hw_current; /* qtd list - see EHCI 3.6.4 */ - - /* qtd overlay (hardware parts of a struct ehci_qtd) */ - u32 hw_qtd_next; - u32 hw_alt_next; - u32 hw_token; - u32 hw_buf [5]; - u32 hw_buf_hi [5]; - - /* the rest is HCD-private */ - dma_addr_t qh_dma; /* address of qh */ - union ehci_shadow qh_next; /* ptr to qh; or periodic */ - struct list_head qtd_list; /* sw qtd list */ - struct ehci_qtd *dummy; - struct ehci_qh *reclaim; /* next to reclaim */ - - atomic_t refcount; - unsigned stamp; - - u8 qh_state; -#define QH_STATE_LINKED 1 /* HC sees this */ -#define QH_STATE_UNLINK 2 /* HC may still see this */ -#define QH_STATE_IDLE 3 /* HC doesn't see this */ -#define QH_STATE_UNLINK_WAIT 4 /* LINKED and on reclaim q */ -#define QH_STATE_COMPLETING 5 /* don't touch token.HALT */ - - /* periodic schedule info */ - u8 usecs; /* intr bandwidth */ - u8 gap_uf; /* uframes split/csplit gap */ - u8 c_usecs; /* ... split completion bw */ - unsigned short period; /* polling interval */ - unsigned short start; /* where polling starts */ -#define NO_FRAME ((unsigned short)~0) /* pick new start */ - -} __attribute__ ((aligned (32))); - -/*-------------------------------------------------------------------------*/ - -/* - * EHCI Specification 0.95 Section 3.3 - * Fig 3-4 "Isochronous Transaction Descriptor (iTD)" - * - * Schedule records for high speed iso xfers - */ -struct ehci_itd { - /* first part defined by EHCI spec */ - u32 hw_next; /* see EHCI 3.3.1 */ - u32 hw_transaction [8]; /* see EHCI 3.3.2 */ -#define EHCI_ISOC_ACTIVE (1<<31) /* activate transfer this slot */ -#define EHCI_ISOC_BUF_ERR (1<<30) /* Data buffer error */ -#define EHCI_ISOC_BABBLE (1<<29) /* babble detected */ -#define EHCI_ISOC_XACTERR (1<<28) /* XactErr - transaction error */ -#define EHCI_ITD_LENGTH(tok) (((tok)>>16) & 0x7fff) -#define EHCI_ITD_IOC (1 << 15) /* interrupt on complete */ - - u32 hw_bufp [7]; /* see EHCI 3.3.3 */ - u32 hw_bufp_hi [7]; /* Appendix B */ - - /* the rest is HCD-private */ - dma_addr_t itd_dma; /* for this itd */ - union ehci_shadow itd_next; /* ptr to periodic q entry */ - - struct urb *urb; - struct list_head itd_list; /* list of urb frames' itds */ - dma_addr_t buf_dma; /* frame's buffer address */ - - /* for now, only one hw_transaction per itd */ - u32 transaction; - u16 index; /* in urb->iso_frame_desc */ - u16 uframe; /* in periodic schedule */ - u16 usecs; -} __attribute__ ((aligned (32))); - -/*-------------------------------------------------------------------------*/ - -/* - * EHCI Specification 0.95 Section 3.4 - * siTD, aka split-transaction isochronous Transfer Descriptor - * ... describe low/full speed iso xfers through TT in hubs - * see Figure 3-5 "Split-transaction Isochronous Transaction Descriptor (siTD) - */ -struct ehci_sitd { - /* first part defined by EHCI spec */ - u32 hw_next; -/* uses bit field macros above - see EHCI 0.95 Table 3-8 */ - u32 hw_fullspeed_ep; /* see EHCI table 3-9 */ - u32 hw_uframe; /* see EHCI table 3-10 */ - u32 hw_tx_results1; /* see EHCI table 3-11 */ - u32 hw_tx_results2; /* see EHCI table 3-12 */ - u32 hw_tx_results3; /* see EHCI table 3-12 */ - u32 hw_backpointer; /* see EHCI table 3-13 */ - u32 hw_buf_hi [2]; /* Appendix B */ - - /* the rest is HCD-private */ - dma_addr_t sitd_dma; - union ehci_shadow sitd_next; /* ptr to periodic q entry */ - struct urb *urb; - dma_addr_t buf_dma; /* buffer address */ - - unsigned short usecs; /* start bandwidth */ - unsigned short c_usecs; /* completion bandwidth */ -} __attribute__ ((aligned (32))); - -/*-------------------------------------------------------------------------*/ - -/* - * EHCI Specification 0.96 Section 3.7 - * Periodic Frame Span Traversal Node (FSTN) - * - * Manages split interrupt transactions (using TT) that span frame boundaries - * into uframes 0/1; see 4.12.2.2. In those uframes, a "save place" FSTN - * makes the HC jump (back) to a QH to scan for fs/ls QH completions until - * it hits a "restore" FSTN; then it returns to finish other uframe 0/1 work. - */ -struct ehci_fstn { - u32 hw_next; /* any periodic q entry */ - u32 hw_prev; /* qh or EHCI_LIST_END */ - - /* the rest is HCD-private */ - dma_addr_t fstn_dma; - union ehci_shadow fstn_next; /* ptr to periodic q entry */ -} __attribute__ ((aligned (32))); - -/*-------------------------------------------------------------------------*/ - -#include -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,32) - -#define SUBMIT_URB(urb,mem_flags) usb_submit_urb(urb) -#define STUB_DEBUG_FILES - -static inline int hcd_register_root (struct usb_hcd *hcd) -{ - return usb_new_device (hcd_to_bus (hcd)->root_hub); -} - -#else /* LINUX_VERSION_CODE */ - -// hcd_to_bus() eventually moves to hcd.h on 2.5 too -static inline struct usb_bus *hcd_to_bus (struct usb_hcd *hcd) - { return &hcd->self; } -// ... as does hcd_register_root() -static inline int hcd_register_root (struct usb_hcd *hcd) -{ - return usb_register_root_hub ( - hcd_to_bus (hcd)->root_hub, &hcd->pdev->dev); -} - -#define SUBMIT_URB(urb,mem_flags) usb_submit_urb(urb,mem_flags) - -#ifndef DEBUG -#define STUB_DEBUG_FILES -#endif /* DEBUG */ - -#endif /* LINUX_VERSION_CODE */ - -/*-------------------------------------------------------------------------*/ - -#endif /* __LINUX_EHCI_HCD_H */ diff -Nru a/drivers/usb/host/Config.in b/drivers/usb/host/Config.in --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/usb/host/Config.in Thu Mar 6 14:23:09 2003 @@ -0,0 +1,7 @@ +# +# USB Host Controller Drivers +# +dep_tristate ' EHCI HCD (USB 2.0) support (EXPERIMENTAL)' CONFIG_USB_EHCI_HCD $CONFIG_USB $CONFIG_EXPERIMENTAL +# dep_tristate ' OHCI HCD support (EXPERIMENTAL)' CONFIG_USB_OHCI_HCD $CONFIG_USB $CONFIG_EXPERIMENTAL +# dep_tristate ' UHCI HCD (most Intel and VIA) support (EXPERIMENTAL)' CONFIG_USB_UHCI_HCD $CONFIG_USB $CONFIG_EXPERIMENTAL + diff -Nru a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/usb/host/Makefile Thu Mar 6 14:23:09 2003 @@ -0,0 +1,27 @@ +# +# Makefile for USB Host Controller Driver +# framework and drivers +# + +O_TARGET := + +obj-$(CONFIG_USB_EHCI_HCD) += ehci-hcd.o +# obj-$(CONFIG_USB_OHCI_HCD) += ohci-hcd.o +# obj-$(CONFIG_USB_UHCI_HCD) += uhci-hcd.o + +# Extract lists of the multi-part drivers. +# The 'int-*' lists are the intermediate files used to build the multi's. +multi-y := $(filter $(list-multi), $(obj-y)) +multi-m := $(filter $(list-multi), $(obj-m)) +int-y := $(sort $(foreach m, $(multi-y), $($(basename $(m))-objs))) +int-m := $(sort $(foreach m, $(multi-m), $($(basename $(m))-objs))) + +# Take multi-part drivers out of obj-y and put components in. +obj-y := $(filter-out $(list-multi), $(obj-y)) $(int-y) + +# Translate to Rules.make lists. +OX_OBJS := $(obj-y) +MX_OBJS := $(obj-m) +MIX_OBJS := $(int-m) + +include $(TOPDIR)/Rules.make diff -Nru a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/usb/host/ehci-dbg.c Thu Mar 6 14:23:09 2003 @@ -0,0 +1,650 @@ +/* + * Copyright (c) 2001-2002 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. + */ + +/* this file is part of ehci-hcd.c */ + +#ifdef DEBUG +#define ehci_dbg(ehci, fmt, args...) \ + printk(KERN_DEBUG "%s %s: " fmt , hcd_name , \ + (ehci)->hcd.pdev->slot_name , ## args ) +#else +#define ehci_dbg(ehci, fmt, args...) do { } while (0) +#endif + +#define ehci_err(ehci, fmt, args...) \ + printk(KERN_ERR "%s %s: " fmt , hcd_name , \ + (ehci)->hcd.pdev->slot_name , ## args ) +#define ehci_info(ehci, fmt, args...) \ + printk(KERN_INFO "%s %s: " fmt , hcd_name , \ + (ehci)->hcd.pdev->slot_name , ## args ) +#define ehci_warn(ehci, fmt, args...) \ + printk(KERN_WARNING "%s %s: " fmt , hcd_name , \ + (ehci)->hcd.pdev->slot_name , ## args ) + + +#ifdef EHCI_VERBOSE_DEBUG +# define vdbg dbg +# define ehci_vdbg ehci_dbg +#else +# define vdbg(fmt,args...) do { } while (0) +# define ehci_vdbg(ehci, fmt, args...) do { } while (0) +#endif + +#ifdef DEBUG + +/* check the values in the HCSPARAMS register + * (host controller _Structural_ parameters) + * see EHCI spec, Table 2-4 for each value + */ +static void dbg_hcs_params (struct ehci_hcd *ehci, char *label) +{ + u32 params = readl (&ehci->caps->hcs_params); + + ehci_dbg (ehci, + "%s hcs_params 0x%x dbg=%d%s cc=%d pcc=%d%s%s ports=%d\n", + label, params, + HCS_DEBUG_PORT (params), + HCS_INDICATOR (params) ? " ind" : "", + HCS_N_CC (params), + HCS_N_PCC (params), + HCS_PORTROUTED (params) ? "" : " ordered", + HCS_PPC (params) ? "" : " !ppc", + HCS_N_PORTS (params) + ); + /* Port routing, per EHCI 0.95 Spec, Section 2.2.5 */ + if (HCS_PORTROUTED (params)) { + int i; + char buf [46], tmp [7], byte; + + buf[0] = 0; + for (i = 0; i < HCS_N_PORTS (params); i++) { + byte = readb (&ehci->caps->portroute[(i>>1)]); + sprintf(tmp, "%d ", + ((i & 0x1) ? ((byte)&0xf) : ((byte>>4)&0xf))); + strcat(buf, tmp); + } + ehci_dbg (ehci, "%s portroute %s\n", + label, buf); + } +} +#else + +static inline void dbg_hcs_params (struct ehci_hcd *ehci, char *label) {} + +#endif + +#ifdef DEBUG + +/* check the values in the HCCPARAMS register + * (host controller _Capability_ parameters) + * see EHCI Spec, Table 2-5 for each value + * */ +static void dbg_hcc_params (struct ehci_hcd *ehci, char *label) +{ + u32 params = readl (&ehci->caps->hcc_params); + + if (HCC_ISOC_CACHE (params)) { + ehci_dbg (ehci, + "%s hcc_params %04x caching frame %s%s%s\n", + label, params, + HCC_PGM_FRAMELISTLEN (params) ? "256/512/1024" : "1024", + HCC_CANPARK (params) ? " park" : "", + HCC_64BIT_ADDR (params) ? " 64 bit addr" : ""); + } else { + ehci_dbg (ehci, + "%s hcc_params %04x thresh %d uframes %s%s%s\n", + label, + params, + HCC_ISOC_THRES (params), + HCC_PGM_FRAMELISTLEN (params) ? "256/512/1024" : "1024", + HCC_CANPARK (params) ? " park" : "", + HCC_64BIT_ADDR (params) ? " 64 bit addr" : ""); + } +} +#else + +static inline void dbg_hcc_params (struct ehci_hcd *ehci, char *label) {} + +#endif + +#ifdef DEBUG + +static void __attribute__((__unused__)) +dbg_qh (char *label, struct ehci_hcd *ehci, struct ehci_qh *qh) +{ + dbg ("%s %p info1 %x info2 %x hw_curr %x qtd_next %x", label, + qh, qh->hw_info1, qh->hw_info2, + qh->hw_current, qh->hw_qtd_next); + dbg (" alt+errs= %x, token= %x, page0= %x, page1= %x", + qh->hw_alt_next, qh->hw_token, + qh->hw_buf [0], qh->hw_buf [1]); + if (qh->hw_buf [2]) { + dbg (" page2= %x, page3= %x, page4= %x", + qh->hw_buf [2], qh->hw_buf [3], + qh->hw_buf [4]); + } +} + +static int __attribute__((__unused__)) +dbg_status_buf (char *buf, unsigned len, char *label, u32 status) +{ + return snprintf (buf, len, + "%s%sstatus %04x%s%s%s%s%s%s%s%s%s%s", + label, label [0] ? " " : "", status, + (status & STS_ASS) ? " Async" : "", + (status & STS_PSS) ? " Periodic" : "", + (status & STS_RECL) ? " Recl" : "", + (status & STS_HALT) ? " Halt" : "", + (status & STS_IAA) ? " IAA" : "", + (status & STS_FATAL) ? " FATAL" : "", + (status & STS_FLR) ? " FLR" : "", + (status & STS_PCD) ? " PCD" : "", + (status & STS_ERR) ? " ERR" : "", + (status & STS_INT) ? " INT" : "" + ); +} + +static int __attribute__((__unused__)) +dbg_intr_buf (char *buf, unsigned len, char *label, u32 enable) +{ + return snprintf (buf, len, + "%s%sintrenable %02x%s%s%s%s%s%s", + label, label [0] ? " " : "", enable, + (enable & STS_IAA) ? " IAA" : "", + (enable & STS_FATAL) ? " FATAL" : "", + (enable & STS_FLR) ? " FLR" : "", + (enable & STS_PCD) ? " PCD" : "", + (enable & STS_ERR) ? " ERR" : "", + (enable & STS_INT) ? " INT" : "" + ); +} + +static const char *const fls_strings [] = + { "1024", "512", "256", "??" }; + +static int dbg_command_buf (char *buf, unsigned len, char *label, u32 command) +{ + return snprintf (buf, len, + "%s%scommand %06x %s=%d ithresh=%d%s%s%s%s period=%s%s %s", + label, label [0] ? " " : "", command, + (command & CMD_PARK) ? "park" : "(park)", + CMD_PARK_CNT (command), + (command >> 16) & 0x3f, + (command & CMD_LRESET) ? " LReset" : "", + (command & CMD_IAAD) ? " IAAD" : "", + (command & CMD_ASE) ? " Async" : "", + (command & CMD_PSE) ? " Periodic" : "", + fls_strings [(command >> 2) & 0x3], + (command & CMD_RESET) ? " Reset" : "", + (command & CMD_RUN) ? "RUN" : "HALT" + ); +} + +static int +dbg_port_buf (char *buf, unsigned len, char *label, int port, u32 status) +{ + char *sig; + + /* signaling state */ + switch (status & (3 << 10)) { + case 0 << 10: sig = "se0"; break; + case 1 << 10: sig = "k"; break; /* low speed */ + case 2 << 10: sig = "j"; break; + default: sig = "?"; break; + } + + return snprintf (buf, len, + "%s%sport %d status %06x%s%s sig=%s %s%s%s%s%s%s%s%s%s", + label, label [0] ? " " : "", port, status, + (status & PORT_POWER) ? " POWER" : "", + (status & PORT_OWNER) ? " OWNER" : "", + sig, + (status & PORT_RESET) ? " RESET" : "", + (status & PORT_SUSPEND) ? " SUSPEND" : "", + (status & PORT_RESUME) ? " RESUME" : "", + (status & PORT_OCC) ? " OCC" : "", + (status & PORT_OC) ? " OC" : "", + (status & PORT_PEC) ? " PEC" : "", + (status & PORT_PE) ? " PE" : "", + (status & PORT_CSC) ? " CSC" : "", + (status & PORT_CONNECT) ? " CONNECT" : "" + ); +} + +#else +static inline void __attribute__((__unused__)) +dbg_qh (char *label, struct ehci_hcd *ehci, struct ehci_qh *qh) +{} + +static inline int __attribute__((__unused__)) +dbg_status_buf (char *buf, unsigned len, char *label, u32 status) +{ return 0; } + +static inline int __attribute__((__unused__)) +dbg_command_buf (char *buf, unsigned len, char *label, u32 command) +{ return 0; } + +static inline int __attribute__((__unused__)) +dbg_intr_buf (char *buf, unsigned len, char *label, u32 enable) +{ return 0; } + +static inline int __attribute__((__unused__)) +dbg_port_buf (char *buf, unsigned len, char *label, int port, u32 status) +{ return 0; } + +#endif /* DEBUG */ + +/* functions have the "wrong" filename when they're output... */ +#define dbg_status(ehci, label, status) { \ + char _buf [80]; \ + dbg_status_buf (_buf, sizeof _buf, label, status); \ + ehci_dbg (ehci, "%s\n", _buf); \ +} + +#define dbg_cmd(ehci, label, command) { \ + char _buf [80]; \ + dbg_command_buf (_buf, sizeof _buf, label, command); \ + ehci_dbg (ehci, "%s\n", _buf); \ +} + +#define dbg_port(ehci, label, port, status) { \ + char _buf [80]; \ + dbg_port_buf (_buf, sizeof _buf, label, port, status); \ + ehci_dbg (ehci, "%s\n", _buf); \ +} + +/*-------------------------------------------------------------------------*/ + +#ifdef STUB_DEBUG_FILES + +static inline void create_debug_files (struct ehci_hcd *bus) { } +static inline void remove_debug_files (struct ehci_hcd *bus) { } + +#else + +/* troubleshooting help: expose state in driverfs */ + +#define speed_char(info1) ({ char tmp; \ + switch (info1 & (3 << 12)) { \ + case 0 << 12: tmp = 'f'; break; \ + case 1 << 12: tmp = 'l'; break; \ + case 2 << 12: tmp = 'h'; break; \ + default: tmp = '?'; break; \ + }; tmp; }) + +static inline char token_mark (u32 token) +{ + token = le32_to_cpu (token); + if (token & QTD_STS_ACTIVE) + return '*'; + if (token & QTD_STS_HALT) + return '-'; + if (QTD_PID (token) != 1 /* not IN: OUT or SETUP */ + || QTD_LENGTH (token) == 0) + return ' '; + /* tries to advance through hw_alt_next */ + return '/'; +} + +static void qh_lines ( + struct ehci_hcd *ehci, + struct ehci_qh *qh, + char **nextp, + unsigned *sizep +) +{ + u32 scratch; + u32 hw_curr; + struct list_head *entry; + struct ehci_qtd *td; + unsigned temp; + unsigned size = *sizep; + char *next = *nextp; + char mark; + + mark = token_mark (qh->hw_token); + if (mark == '/') { /* qh_alt_next controls qh advance? */ + if ((qh->hw_alt_next & QTD_MASK) == ehci->async->hw_alt_next) + mark = '#'; /* blocked */ + else if (qh->hw_alt_next & cpu_to_le32 (0x01)) + mark = '.'; /* use hw_qtd_next */ + /* else alt_next points to some other qtd */ + } + scratch = cpu_to_le32p (&qh->hw_info1); + hw_curr = (mark == '*') ? cpu_to_le32p (&qh->hw_current) : 0; + temp = snprintf (next, size, + "qh/%p dev%d %cs ep%d %08x %08x (%08x%c %s nak%d)", + qh, scratch & 0x007f, + speed_char (scratch), + (scratch >> 8) & 0x000f, + scratch, cpu_to_le32p (&qh->hw_info2), + cpu_to_le32p (&qh->hw_token), mark, + (cpu_to_le32 (0x8000000) & qh->hw_token) + ? "data0" : "data1", + (cpu_to_le32p (&qh->hw_alt_next) >> 1) & 0x0f); + size -= temp; + next += temp; + + /* hc may be modifying the list as we read it ... */ + list_for_each (entry, &qh->qtd_list) { + td = list_entry (entry, struct ehci_qtd, qtd_list); + scratch = cpu_to_le32p (&td->hw_token); + mark = ' '; + if (hw_curr == td->qtd_dma) + mark = '*'; + else if (qh->hw_qtd_next == td->qtd_dma) + mark = '+'; + else if (QTD_LENGTH (scratch)) { + if (td->hw_alt_next == ehci->async->hw_alt_next) + mark = '#'; + else if (td->hw_alt_next != EHCI_LIST_END) + mark = '/'; + } + temp = snprintf (next, size, + "\n\t%p%c%s len=%d %08x urb %p", + td, mark, ({ char *tmp; + switch ((scratch>>8)&0x03) { + case 0: tmp = "out"; break; + case 1: tmp = "in"; break; + case 2: tmp = "setup"; break; + default: tmp = "?"; break; + } tmp;}), + (scratch >> 16) & 0x7fff, + scratch, + td->urb); + if (temp < 0) + temp = 0; + else if (size < temp) + temp = size; + size -= temp; + next += temp; + if (temp == size) + goto done; + } + + temp = snprintf (next, size, "\n"); + if (temp < 0) + temp = 0; + else if (size < temp) + temp = size; + size -= temp; + next += temp; + +done: + *sizep = size; + *nextp = next; +} + +static ssize_t +show_async (struct device *dev, char *buf) +{ + struct pci_dev *pdev; + struct ehci_hcd *ehci; + unsigned long flags; + unsigned temp, size; + char *next; + struct ehci_qh *qh; + + pdev = container_of (dev, struct pci_dev, dev); + ehci = container_of (pci_get_drvdata (pdev), struct ehci_hcd, hcd); + next = buf; + size = PAGE_SIZE; + + /* dumps a snapshot of the async schedule. + * usually empty except for long-term bulk reads, or head. + * one QH per line, and TDs we know about + */ + spin_lock_irqsave (&ehci->lock, flags); + for (qh = ehci->async->qh_next.qh; size > 0 && qh; qh = qh->qh_next.qh) + qh_lines (ehci, qh, &next, &size); + if (ehci->reclaim && size > 0) { + temp = snprintf (next, size, "\nreclaim =\n"); + size -= temp; + next += temp; + + for (qh = ehci->reclaim; size > 0 && qh; qh = qh->reclaim) + qh_lines (ehci, qh, &next, &size); + } + spin_unlock_irqrestore (&ehci->lock, flags); + + return PAGE_SIZE - size; +} +static DEVICE_ATTR (async, S_IRUGO, show_async, NULL); + +#define DBG_SCHED_LIMIT 64 + +static ssize_t +show_periodic (struct device *dev, char *buf) +{ + struct pci_dev *pdev; + struct ehci_hcd *ehci; + unsigned long flags; + union ehci_shadow p, *seen; + unsigned temp, size, seen_count; + char *next; + unsigned i, tag; + + if (!(seen = kmalloc (DBG_SCHED_LIMIT * sizeof *seen, SLAB_ATOMIC))) + return 0; + seen_count = 0; + + pdev = container_of (dev, struct pci_dev, dev); + ehci = container_of (pci_get_drvdata (pdev), struct ehci_hcd, hcd); + next = buf; + size = PAGE_SIZE; + + temp = snprintf (next, size, "size = %d\n", ehci->periodic_size); + size -= temp; + next += temp; + + /* dump a snapshot of the periodic schedule. + * iso changes, interrupt usually doesn't. + */ + spin_lock_irqsave (&ehci->lock, flags); + for (i = 0; i < ehci->periodic_size; i++) { + p = ehci->pshadow [i]; + if (!p.ptr) + continue; + tag = Q_NEXT_TYPE (ehci->periodic [i]); + + temp = snprintf (next, size, "%4d: ", i); + size -= temp; + next += temp; + + do { + switch (tag) { + case Q_TYPE_QH: + temp = snprintf (next, size, " qh%d/%p", + p.qh->period, p.qh); + size -= temp; + next += temp; + for (temp = 0; temp < seen_count; temp++) { + if (seen [temp].ptr == p.ptr) + break; + } + /* show more info the first time around */ + if (temp == seen_count) { + u32 scratch = cpu_to_le32p ( + &p.qh->hw_info1); + + temp = snprintf (next, size, + " (%cs dev%d ep%d [%d/%d] %d)", + speed_char (scratch), + scratch & 0x007f, + (scratch >> 8) & 0x000f, + p.qh->usecs, p.qh->c_usecs, + 0x7ff & (scratch >> 16)); + + /* FIXME TD info too */ + + if (seen_count < DBG_SCHED_LIMIT) + seen [seen_count++].qh = p.qh; + } else + temp = 0; + tag = Q_NEXT_TYPE (p.qh->hw_next); + p = p.qh->qh_next; + break; + case Q_TYPE_FSTN: + temp = snprintf (next, size, + " fstn-%8x/%p", p.fstn->hw_prev, + p.fstn); + tag = Q_NEXT_TYPE (p.fstn->hw_next); + p = p.fstn->fstn_next; + break; + case Q_TYPE_ITD: + temp = snprintf (next, size, + " itd/%p", p.itd); + tag = Q_NEXT_TYPE (p.itd->hw_next); + p = p.itd->itd_next; + break; + case Q_TYPE_SITD: + temp = snprintf (next, size, + " sitd/%p", p.sitd); + tag = Q_NEXT_TYPE (p.sitd->hw_next); + p = p.sitd->sitd_next; + break; + } + size -= temp; + next += temp; + } while (p.ptr); + + temp = snprintf (next, size, "\n"); + size -= temp; + next += temp; + } + spin_unlock_irqrestore (&ehci->lock, flags); + kfree (seen); + + return PAGE_SIZE - size; +} +static DEVICE_ATTR (periodic, S_IRUGO, show_periodic, NULL); + +#undef DBG_SCHED_LIMIT + +static ssize_t +show_registers (struct device *dev, char *buf) +{ + struct pci_dev *pdev; + struct ehci_hcd *ehci; + unsigned long flags; + unsigned temp, size, i; + char *next, scratch [80]; + static char fmt [] = "%*s\n"; + static char label [] = ""; + + pdev = container_of (dev, struct pci_dev, dev); + ehci = container_of (pci_get_drvdata (pdev), struct ehci_hcd, hcd); + + next = buf; + size = PAGE_SIZE; + + spin_lock_irqsave (&ehci->lock, flags); + + /* Capability Registers */ + i = readw (&ehci->caps->hci_version); + temp = snprintf (next, size, + "EHCI %x.%02x, hcd state %d (version " DRIVER_VERSION ")\n", + i >> 8, i & 0x0ff, ehci->hcd.state); + size -= temp; + next += temp; + + // FIXME interpret both types of params + i = readl (&ehci->caps->hcs_params); + temp = snprintf (next, size, "structural params 0x%08x\n", i); + size -= temp; + next += temp; + + i = readl (&ehci->caps->hcc_params); + temp = snprintf (next, size, "capability params 0x%08x\n", i); + size -= temp; + next += temp; + + /* Operational Registers */ + temp = dbg_status_buf (scratch, sizeof scratch, label, + readl (&ehci->regs->status)); + temp = snprintf (next, size, fmt, temp, scratch); + size -= temp; + next += temp; + + temp = dbg_command_buf (scratch, sizeof scratch, label, + readl (&ehci->regs->command)); + temp = snprintf (next, size, fmt, temp, scratch); + size -= temp; + next += temp; + + temp = dbg_intr_buf (scratch, sizeof scratch, label, + readl (&ehci->regs->intr_enable)); + temp = snprintf (next, size, fmt, temp, scratch); + size -= temp; + next += temp; + + temp = snprintf (next, size, "uframe %04x\n", + readl (&ehci->regs->frame_index)); + size -= temp; + next += temp; + + for (i = 0; i < HCS_N_PORTS (ehci->hcs_params); i++) { + temp = dbg_port_buf (scratch, sizeof scratch, label, i, + readl (&ehci->regs->port_status [i])); + temp = snprintf (next, size, fmt, temp, scratch); + size -= temp; + next += temp; + } + + if (ehci->reclaim) { + temp = snprintf (next, size, "reclaim qh %p%s\n", + ehci->reclaim, + ehci->reclaim_ready ? " ready" : ""); + size -= temp; + next += temp; + } + +#ifdef EHCI_STATS + temp = snprintf (next, size, "irq normal %ld err %ld reclaim %ld\n", + ehci->stats.normal, ehci->stats.error, ehci->stats.reclaim); + size -= temp; + next += temp; + + temp = snprintf (next, size, "complete %ld unlink %ld\n", + ehci->stats.complete, ehci->stats.unlink); + size -= temp; + next += temp; +#endif + + spin_unlock_irqrestore (&ehci->lock, flags); + + return PAGE_SIZE - size; +} +static DEVICE_ATTR (registers, S_IRUGO, show_registers, NULL); + +static inline void create_debug_files (struct ehci_hcd *bus) +{ + device_create_file (&bus->hcd.pdev->dev, &dev_attr_async); + device_create_file (&bus->hcd.pdev->dev, &dev_attr_periodic); + device_create_file (&bus->hcd.pdev->dev, &dev_attr_registers); +} + +static inline void remove_debug_files (struct ehci_hcd *bus) +{ + device_remove_file (&bus->hcd.pdev->dev, &dev_attr_async); + device_remove_file (&bus->hcd.pdev->dev, &dev_attr_periodic); + device_remove_file (&bus->hcd.pdev->dev, &dev_attr_registers); +} + +#endif /* STUB_DEBUG_FILES */ + diff -Nru a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/usb/host/ehci-hcd.c Thu Mar 6 14:23:09 2003 @@ -0,0 +1,1033 @@ +/* + * Copyright (c) 2000-2002 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_USB_DEBUG + #define DEBUG +#else + #undef DEBUG +#endif + +#include + +#include +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,32) +#include "../hcd.h" +#else +#include "../core/hcd.h" +#endif + +#include +#include +#include +#include +#include + + +/*-------------------------------------------------------------------------*/ + +/* + * 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: + * + * 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 "2003-Jan-22" +#define DRIVER_AUTHOR "David Brownell" +#define DRIVER_DESC "USB 2.0 'Enhanced' Host Controller (EHCI) Driver" + +static const char hcd_name [] = "ehci-hcd"; + + +// #define EHCI_VERBOSE_DEBUG +// #define have_split_iso + +#ifdef DEBUG +#define EHCI_STATS +#endif + +#define INTR_AUTOMAGIC /* urb lifecycle mode, gone in 2.5 */ + +/* 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_WATCHDOG_JIFFIES (HZ/100) /* arbitrary; ~10 msec */ +#define EHCI_ASYNC_JIFFIES (HZ/20) /* async idle timeout */ + +/* Initial IRQ latency: lower than default */ +static int log2_irq_thresh = 0; // 0 to 6 +MODULE_PARM (log2_irq_thresh, "i"); +MODULE_PARM_DESC (log2_irq_thresh, "log2 IRQ latency, 1-64 microframes"); + +#define INTR_MASK (STS_IAA | STS_FATAL | 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 (u32 *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; +} + +/* + * hc states include: unknown, halted, ready, running + * transitional states are messy just now + * trying to avoid "running" unless urbs are active + * a "ready" hc can be finishing prefetched work + */ + +/* 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); +} + +/* reset a non-running (STS_HALT == 1) controller */ +static int ehci_reset (struct ehci_hcd *ehci) +{ + u32 command = readl (&ehci->regs->command); + + command |= CMD_RESET; + dbg_cmd (ehci, "reset", command); + writel (command, &ehci->regs->command); + ehci->hcd.state = USB_STATE_HALT; + return handshake (&ehci->regs->command, CMD_RESET, 0, 250 * 1000); +} + +/* idle the controller (from running) */ +static void ehci_ready (struct ehci_hcd *ehci) +{ + u32 temp; + +#ifdef DEBUG + if (!HCD_IS_RUNNING (ehci->hcd.state)) + BUG (); +#endif + + /* wait for any schedule enables/disables to take effect */ + temp = 0; + if (ehci->async->qh_next.qh) + temp = STS_ASS; + if (ehci->next_uframe != -1) + temp |= STS_PSS; + if (handshake (&ehci->regs->status, STS_ASS | STS_PSS, + temp, 16 * 125) != 0) { + ehci->hcd.state = USB_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->hcd.state = USB_STATE_HALT; + return; + } + ehci->hcd.state = USB_STATE_READY; +} + +/*-------------------------------------------------------------------------*/ + +#include "ehci-hub.c" +#include "ehci-mem.c" +#include "ehci-q.c" +#include "ehci-sched.c" + +/*-------------------------------------------------------------------------*/ + +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; + 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"); + writel (STS_IAA, &ehci->regs->status); + ehci->reclaim_ready = 1; + } + } + + ehci_work (ehci, NULL); + if (ehci->reclaim && !timer_pending (&ehci->watchdog)) + mod_timer (&ehci->watchdog, + jiffies + EHCI_WATCHDOG_JIFFIES); + + /* stop async processing after it's idled a while */ + else if (ehci->async_idle) { + start_unlink_async (ehci, ehci->async); + ehci->async_idle = 0; + } + spin_unlock_irqrestore (&ehci->lock, flags); +} + +/* 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 = 500; + + /* request handoff to OS */ + cap &= 1 << 24; + pci_write_config_dword (ehci->hcd.pdev, where, cap); + + /* and wait a while for it to happen */ + do { + wait_ms (10); + msec -= 10; + pci_read_config_dword (ehci->hcd.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; + } + ehci_dbg (ehci, "BIOS handoff succeeded\n"); + } + return 0; +} + +/* called by khubd or root hub init threads */ + +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 tempbyte; + + spin_lock_init (&ehci->lock); + + ehci->caps = (struct ehci_caps *) hcd->regs; + ehci->regs = (struct ehci_regs *) (hcd->regs + ehci->caps->length); + dbg_hcs_params (ehci, "ehci_start"); + dbg_hcc_params (ehci, "ehci_start"); + + hcc_params = readl (&ehci->caps->hcc_params); + + /* EHCI 0.96 and later may have "extended capabilities" */ + temp = HCC_EXT_CAPS (hcc_params); + while (temp) { + u32 cap; + + pci_read_config_dword (ehci->hcd.pdev, 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; + } + + /* cache this readonly data; minimize PCI reads */ + ehci->hcs_params = readl (&ehci->caps->hcs_params); + + /* force HC to halt state */ + if ((retval = ehci_halt (ehci)) != 0) + return retval; + + /* + * 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 ((retval = ehci_mem_init (ehci, SLAB_KERNEL)) < 0) + return retval; + + /* controllers may cache some of the periodic schedule ... */ + 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 = 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 (INTR_MASK, &ehci->regs->intr_enable); + writel (ehci->periodic_dma, &ehci->regs->frame_list); + + /* + * 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. + */ + ehci->async->qh_next.qh = 0; + 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 (!pci_set_dma_mask (ehci->hcd.pdev, 0xffffffffffffffffULL)) + ehci_info (ehci, "enabled 64bit PCI DMA\n"); + } + + /* help hc dma work well with cachelines */ + pci_set_mwi (ehci->hcd.pdev); + + /* clear interrupt enables, set irq latency */ + temp = readl (&ehci->regs->command) & 0xff; + if (log2_irq_thresh < 0 || log2_irq_thresh > 6) + log2_irq_thresh = 0; + temp |= 1 << (16 + log2_irq_thresh); + // if hc can park (ehci >= 0.96), default is 3 packets per async QH + 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 (); + } + } + temp &= ~(CMD_IAAD | CMD_ASE | CMD_PSE), + // 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 ... ? */ + + init_timer (&ehci->watchdog); + ehci->watchdog.function = ehci_watchdog; + ehci->watchdog.data = (unsigned long) ehci; + + /* wire up the root hub */ + bus = hcd_to_bus (hcd); + bus->root_hub = udev = usb_alloc_dev (NULL, bus); + if (!udev) { +done2: + ehci_mem_cleanup (ehci); + return -ENOMEM; + } + + /* + * 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. + */ + ehci->hcd.state = USB_STATE_READY; + writel (FLAG_CF, &ehci->regs->configured_flag); + readl (&ehci->regs->command); /* unblock posted write */ + + /* PCI Serial Bus Release Number is at 0x60 offset */ + pci_read_config_byte (hcd->pdev, 0x60, &tempbyte); + temp = readw (&ehci->caps->hci_version); + ehci_info (ehci, + "USB %x.%x enabled, EHCI %x.%02x, driver %s\n", + ((tempbyte & 0xf0)>>4), (tempbyte & 0x0f), + temp >> 8, temp & 0xff, DRIVER_VERSION); + + /* + * From here on, khubd concurrently accesses the root + * hub; drivers will be talking to enumerated devices. + * + * Before this point the HC was idle/ready. After, khubd + * and device drivers may start it running. + */ + usb_connect (udev); + udev->speed = USB_SPEED_HIGH; + if (hcd_register_root (hcd) != 0) { + if (hcd->state == USB_STATE_RUNNING) + ehci_ready (ehci); + ehci_reset (ehci); + bus->root_hub = 0; + retval = -ENODEV; + goto done2; + } + + 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); + + ehci_dbg (ehci, "stop\n"); + + /* no more interrupts ... */ + if (hcd->state == USB_STATE_RUNNING) + ehci_ready (ehci); + if (in_interrupt ()) { /* must not happen!! */ + ehci_err (ehci, "stopped in_interrupt!\n"); + return; + } + del_timer_sync (&ehci->watchdog); + ehci_reset (ehci); + + /* let companion controllers work when we aren't */ + writel (0, &ehci->regs->configured_flag); + + remove_debug_files (ehci); + + /* root hub is shut down separately (first, when possible) */ + spin_lock_irq (&ehci->lock); + 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\n", + ehci->stats.normal, ehci->stats.error, ehci->stats.reclaim); + 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 */ + +static int ehci_suspend (struct usb_hcd *hcd, u32 state) +{ + struct ehci_hcd *ehci = hcd_to_ehci (hcd); + int ports; + int i; + + dbg ("%s: suspend to %d", hcd_to_bus (hcd)->bus_name, state); + + ports = HCS_N_PORTS (ehci->hcs_params); + + // 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; +dbg ("%s: suspend port %d", hcd_to_bus (hcd)->bus_name, 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 + + /* who tells PCI to reduce power consumption? */ + + return 0; +} + +static int ehci_resume (struct usb_hcd *hcd) +{ + struct ehci_hcd *ehci = hcd_to_ehci (hcd); + int ports; + int i; + + dbg ("%s: resume", hcd_to_bus (hcd)->bus_name); + + 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_READY; + // 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; +dbg ("%s: resume port %d", hcd_to_bus (hcd)->bus_name, 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; +} + +#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) +{ + if (ehci->reclaim_ready) + end_unlink_async (ehci, regs); + scan_async (ehci, regs); + if (ehci->next_uframe != -1) + scan_periodic (ehci, regs); +} + +/*-------------------------------------------------------------------------*/ + +static void 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? */ + goto done; + + /* clear (just) interrupts */ + writel (status, &ehci->regs->status); + readl (&ehci->regs->command); /* unblock posted write */ + bh = 0; + +#ifdef EHCI_VERBOSE_DEBUG + /* unrequested/ignored: Port Change Detect, 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; + } + + /* PCI errors [4.15.2.4] */ + if (unlikely ((status & STS_FATAL) != 0)) { + ehci_err (ehci, "fatal error\n"); +dead: + ehci_reset (ehci); + /* generic layer kills/unlinks all urbs, then + * uses ehci_stop to clean up the rest + */ + bh = 1; + } + + if (bh) + ehci_work (ehci, regs); +done: + spin_unlock (&ehci->lock); +} + +/*-------------------------------------------------------------------------*/ + +/* + * 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_dev.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 urb *urb, + int mem_flags +) { + 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)) { + // case PIPE_CONTROL: + // case PIPE_BULK: + default: + if (!qh_urb_transaction (ehci, urb, &qtd_list, mem_flags)) + return -ENOMEM; + return submit_async (ehci, urb, &qtd_list, mem_flags); + + case PIPE_INTERRUPT: + if (!qh_urb_transaction (ehci, urb, &qtd_list, mem_flags)) + return -ENOMEM; + return intr_submit (ehci, urb, &qtd_list, mem_flags); + + case PIPE_ISOCHRONOUS: + if (urb->dev->speed == USB_SPEED_HIGH) + return itd_submit (ehci, urb, mem_flags); +#ifdef have_split_iso + else + return sitd_submit (ehci, urb, mem_flags); +#else + dbg ("no split iso support yet"); + return -ENOSYS; +#endif /* have_split_iso */ + } +} + +/* 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; + + /* if we need to use IAA and it's busy, defer */ + if (qh->qh_state == QH_STATE_LINKED + && ehci->reclaim + && HCD_IS_RUNNING (ehci->hcd.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 (!HCD_IS_RUNNING (ehci->hcd.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); + break; + + case PIPE_INTERRUPT: + qh = (struct ehci_qh *) urb->hcpriv; + if (!qh) + break; + if (qh->qh_state == QH_STATE_LINKED) { + /* messy, can spin or block a microframe ... */ + intr_deschedule (ehci, qh, 1); + /* qh_state == IDLE */ + } + qh_completions (ehci, qh, NULL); + + /* reschedule QH iff another request is queued */ + if (!list_empty (&qh->qtd_list) + && HCD_IS_RUNNING (ehci->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, + urb->transfer_flags |= EHCI_STATE_UNLINK; + break; + } + spin_unlock_irqrestore (&ehci->lock, flags); + return 0; +} + +/*-------------------------------------------------------------------------*/ + +// bulk qh holds the data toggle + +static void ehci_free_config (struct usb_hcd *hcd, struct usb_device *udev) +{ + struct hcd_dev *dev = (struct hcd_dev *)udev->hcpriv; + struct ehci_hcd *ehci = hcd_to_ehci (hcd); + int i; + unsigned long flags; + + /* ASSERT: no requests/urbs are still linked (so no TDs) */ + /* ASSERT: nobody can be submitting urbs for this any more */ + + dbg ("%s: free_config devnum %d", + hcd_to_bus (hcd)->bus_name, udev->devnum); + + spin_lock_irqsave (&ehci->lock, flags); + for (i = 0; i < 32; i++) { + if (dev->ep [i]) { + struct ehci_qh *qh; + char *why; + + /* dev->ep never has ITDs or SITDs */ + qh = (struct ehci_qh *) dev->ep [i]; + + /* detect/report non-recoverable errors */ + if (in_interrupt ()) + why = "disconnect() didn't"; + else if ((qh->hw_info2 & cpu_to_le32 (0xffff)) != 0 + && qh->qh_state != QH_STATE_IDLE) + why = "(active periodic)"; + else + why = 0; + if (why) { + err ("dev %s-%s ep %d-%s error: %s", + hcd_to_bus (hcd)->bus_name, + udev->devpath, + i & 0xf, (i & 0x10) ? "IN" : "OUT", + why); + BUG (); + } + + dev->ep [i] = 0; + if (qh->qh_state == QH_STATE_IDLE) + goto idle; + dbg ("free_config, async ep 0x%02x qh %p", i, qh); + + /* scan_async() empties the ring as it does its work, + * using IAA, but doesn't (yet?) turn it off. if it + * doesn't empty this qh, likely it's the last entry. + */ + while (qh->qh_state == QH_STATE_LINKED + && ehci->reclaim + && HCD_IS_RUNNING (ehci->hcd.state) + ) { + spin_unlock_irqrestore (&ehci->lock, flags); + /* wait_ms() won't spin, we're a thread; + * and we know IRQ/timer/... can progress + */ + wait_ms (1); + spin_lock_irqsave (&ehci->lock, flags); + } + if (qh->qh_state == QH_STATE_LINKED) + start_unlink_async (ehci, qh); + while (qh->qh_state != QH_STATE_IDLE + && ehci->hcd.state != USB_STATE_HALT) { + spin_unlock_irqrestore (&ehci->lock, flags); + wait_ms (1); + spin_lock_irqsave (&ehci->lock, flags); + } +idle: + qh_put (ehci, qh); + } + } + + spin_unlock_irqrestore (&ehci->lock, flags); +} + +/*-------------------------------------------------------------------------*/ + +static const struct hc_driver ehci_driver = { + .description = hcd_name, + + /* + * generic hardware linkage + */ + .irq = ehci_irq, + .flags = HCD_MEMORY | HCD_USB2, + + /* + * basic lifecycle operations + */ + .start = ehci_start, +#ifdef CONFIG_PM + .suspend = ehci_suspend, + .resume = ehci_resume, +#endif + .stop = ehci_stop, + + /* + * memory lifecycle (except per-request) + */ + .hcd_alloc = ehci_hcd_alloc, + .hcd_free = ehci_hcd_free, + + /* + * managing i/o requests and associated device resources + */ + .urb_enqueue = ehci_urb_enqueue, + .urb_dequeue = ehci_urb_dequeue, + .free_config = ehci_free_config, + + /* + * scheduling support + */ + .get_frame_number = ehci_get_frame, + + /* + * root hub support + */ + .hub_status_data = ehci_hub_status_data, + .hub_control = ehci_hub_control, +}; + +/*-------------------------------------------------------------------------*/ + +/* EHCI spec says PCI is required. */ + +/* PCI driver selection metadata; PCI hotplugging uses this */ +static const struct pci_device_id __devinitdata pci_ids [] = { { + + /* handle any USB 2.0 EHCI controller */ + + .class = ((PCI_CLASS_SERIAL_USB << 8) | 0x20), + .class_mask = ~0, + .driver_data = (unsigned long) &ehci_driver, + + /* no matter who makes it */ + .vendor = PCI_ANY_ID, + .device = PCI_ANY_ID, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + +}, { /* 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 +}; + +#define DRIVER_INFO DRIVER_VERSION " " DRIVER_DESC + +MODULE_DESCRIPTION (DRIVER_INFO); +MODULE_AUTHOR (DRIVER_AUTHOR); +MODULE_LICENSE ("GPL"); + +static int __init init (void) +{ + dbg (DRIVER_INFO); + dbg ("block sizes: qh %Zd qtd %Zd itd %Zd sitd %Zd", + sizeof (struct ehci_qh), sizeof (struct ehci_qtd), + sizeof (struct ehci_itd), sizeof (struct ehci_sitd)); + + return pci_module_init (&ehci_pci_driver); +} +module_init (init); + +static void __exit cleanup (void) +{ + pci_unregister_driver (&ehci_pci_driver); +} +module_exit (cleanup); diff -Nru a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/usb/host/ehci-hub.c Thu Mar 6 14:23:09 2003 @@ -0,0 +1,344 @@ +/* + * Copyright (c) 2001-2002 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. + */ + +/* this file is part of ehci-hcd.c */ + +/*-------------------------------------------------------------------------*/ + +/* + * EHCI Root Hub ... the nonsharable stuff + * + * Registers don't need cpu_to_le32, that happens transparently + */ + +/*-------------------------------------------------------------------------*/ + +static int check_reset_complete ( + struct ehci_hcd *ehci, + int index, + int port_status +) { + if (!(port_status & PORT_CONNECT)) { + ehci->reset_done [index] = 0; + return port_status; + } + + /* if reset finished and it's still not enabled -- handoff */ + if (!(port_status & PORT_PE)) { + ehci_dbg (ehci, "port %d full speed --> companion\n", + index + 1); + + // what happens if HCS_N_CC(params) == 0 ? + port_status |= PORT_OWNER; + writel (port_status, &ehci->regs->port_status [index]); + + } else + ehci_dbg (ehci, "port %d high speed\n", index + 1); + + return port_status; +} + +/*-------------------------------------------------------------------------*/ + + +/* build "status change" packet (one or two bytes) from HC registers */ + +static int +ehci_hub_status_data (struct usb_hcd *hcd, char *buf) +{ + struct ehci_hcd *ehci = hcd_to_ehci (hcd); + u32 temp, status = 0; + int ports, i, retval = 1; + unsigned long flags; + + /* init status to no-changes */ + buf [0] = 0; + ports = HCS_N_PORTS (ehci->hcs_params); + if (ports > 7) { + buf [1] = 0; + retval++; + } + + /* no hub change reports (bit 0) for now (power, ...) */ + + /* port N changes (bit N)? */ + spin_lock_irqsave (&ehci->lock, flags); + for (i = 0; i < ports; i++) { + temp = readl (&ehci->regs->port_status [i]); + if (temp & PORT_OWNER) { + /* don't report this in GetPortStatus */ + if (temp & PORT_CSC) { + 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) { + if (i < 7) + buf [0] |= 1 << (i + 1); + else + buf [1] |= 1 << (i - 7); + status = STS_PCD; + } + } + spin_unlock_irqrestore (&ehci->lock, flags); + return status ? retval : 0; +} + +/*-------------------------------------------------------------------------*/ + +static void +ehci_hub_descriptor ( + struct ehci_hcd *ehci, + struct usb_hub_descriptor *desc +) { + int ports = HCS_N_PORTS (ehci->hcs_params); + u16 temp; + + desc->bDescriptorType = 0x29; + desc->bPwrOn2PwrGood = 0; /* FIXME: f(system power) */ + desc->bHubContrCurrent = 0; + + desc->bNbrPorts = ports; + temp = 1 + (ports / 8); + desc->bDescLength = 7 + 2 * temp; + + /* two bitmaps: ports removable, and usb 1.0 legacy PortPwrCtrlMask */ + memset (&desc->bitmap [0], 0, temp); + memset (&desc->bitmap [temp], 0xff, temp); + + temp = 0x0008; /* per-port overcurrent reporting */ + if (HCS_PPC (ehci->hcs_params)) + temp |= 0x0001; /* per-port power control */ + if (HCS_INDICATOR (ehci->hcs_params)) + temp |= 0x0080; /* per-port indicators (LEDs) */ + desc->wHubCharacteristics = cpu_to_le16 (temp); +} + +/*-------------------------------------------------------------------------*/ + +static int ehci_hub_control ( + struct usb_hcd *hcd, + u16 typeReq, + u16 wValue, + u16 wIndex, + char *buf, + u16 wLength +) { + struct ehci_hcd *ehci = hcd_to_ehci (hcd); + int ports = HCS_N_PORTS (ehci->hcs_params); + u32 temp, status; + unsigned long flags; + int retval = 0; + + /* + * FIXME: support SetPortFeatures USB_PORT_FEAT_INDICATOR. + * HCS_INDICATOR may say we can change LEDs to off/amber/green. + * (track current state ourselves) ... blink for diagnostics, + * power, "this is the one", etc. EHCI spec supports this. + */ + + spin_lock_irqsave (&ehci->lock, flags); + switch (typeReq) { + case ClearHubFeature: + switch (wValue) { + case C_HUB_LOCAL_POWER: + case C_HUB_OVER_CURRENT: + /* no hub-wide feature/status flags */ + break; + default: + goto error; + } + break; + case ClearPortFeature: + if (!wIndex || wIndex > ports) + goto error; + wIndex--; + temp = readl (&ehci->regs->port_status [wIndex]); + if (temp & PORT_OWNER) + break; + + switch (wValue) { + case USB_PORT_FEAT_ENABLE: + writel (temp & ~PORT_PE, + &ehci->regs->port_status [wIndex]); + break; + case USB_PORT_FEAT_C_ENABLE: + writel (temp | PORT_PEC, + &ehci->regs->port_status [wIndex]); + break; + case USB_PORT_FEAT_SUSPEND: + case USB_PORT_FEAT_C_SUSPEND: + /* ? */ + break; + case USB_PORT_FEAT_POWER: + if (HCS_PPC (ehci->hcs_params)) + writel (temp & ~PORT_POWER, + &ehci->regs->port_status [wIndex]); + break; + case USB_PORT_FEAT_C_CONNECTION: + writel (temp | PORT_CSC, + &ehci->regs->port_status [wIndex]); + break; + case USB_PORT_FEAT_C_OVER_CURRENT: + writel (temp | PORT_OCC, + &ehci->regs->port_status [wIndex]); + break; + case USB_PORT_FEAT_C_RESET: + /* GetPortStatus clears reset */ + break; + default: + goto error; + } + readl (&ehci->regs->command); /* unblock posted write */ + break; + case GetHubDescriptor: + ehci_hub_descriptor (ehci, (struct usb_hub_descriptor *) + buf); + break; + case GetHubStatus: + /* no hub-wide feature/status flags */ + memset (buf, 0, 4); + //cpu_to_le32s ((u32 *) buf); + break; + case GetPortStatus: + if (!wIndex || wIndex > ports) + goto error; + wIndex--; + status = 0; + temp = readl (&ehci->regs->port_status [wIndex]); + + // wPortChange bits + if (temp & PORT_CSC) + status |= 1 << USB_PORT_FEAT_C_CONNECTION; + if (temp & PORT_PEC) + status |= 1 << USB_PORT_FEAT_C_ENABLE; + // USB_PORT_FEAT_C_SUSPEND + if (temp & PORT_OCC) + status |= 1 << USB_PORT_FEAT_C_OVER_CURRENT; + + /* whoever resets must GetPortStatus to complete it!! */ + if ((temp & PORT_RESET) + && time_after (jiffies, + ehci->reset_done [wIndex])) { + status |= 1 << USB_PORT_FEAT_C_RESET; + + /* force reset to complete */ + writel (temp & ~PORT_RESET, + &ehci->regs->port_status [wIndex]); + do { + temp = readl ( + &ehci->regs->port_status [wIndex]); + udelay (10); + } while (temp & PORT_RESET); + + /* see what we found out */ + temp = check_reset_complete (ehci, wIndex, temp); + } + + // don't show wPortStatus if it's owned by a companion hc + if (!(temp & PORT_OWNER)) { + if (temp & PORT_CONNECT) { + status |= 1 << USB_PORT_FEAT_CONNECTION; + status |= 1 << USB_PORT_FEAT_HIGHSPEED; + } + if (temp & PORT_PE) + status |= 1 << USB_PORT_FEAT_ENABLE; + if (temp & PORT_SUSPEND) + status |= 1 << USB_PORT_FEAT_SUSPEND; + if (temp & PORT_OC) + status |= 1 << USB_PORT_FEAT_OVER_CURRENT; + if (temp & PORT_RESET) + status |= 1 << USB_PORT_FEAT_RESET; + if (temp & PORT_POWER) + status |= 1 << USB_PORT_FEAT_POWER; + } + +#ifndef EHCI_VERBOSE_DEBUG + if (status & ~0xffff) /* only if wPortChange is interesting */ +#endif + dbg_port (ehci, "GetStatus", wIndex + 1, temp); + // we "know" this alignment is good, caller used kmalloc()... + *((u32 *) buf) = cpu_to_le32 (status); + break; + case SetHubFeature: + switch (wValue) { + case C_HUB_LOCAL_POWER: + case C_HUB_OVER_CURRENT: + /* no hub-wide feature/status flags */ + break; + default: + goto error; + } + break; + case SetPortFeature: + if (!wIndex || wIndex > ports) + goto error; + wIndex--; + temp = readl (&ehci->regs->port_status [wIndex]); + if (temp & PORT_OWNER) + break; + + switch (wValue) { + case USB_PORT_FEAT_SUSPEND: + writel (temp | PORT_SUSPEND, + &ehci->regs->port_status [wIndex]); + break; + case USB_PORT_FEAT_POWER: + if (HCS_PPC (ehci->hcs_params)) + writel (temp | PORT_POWER, + &ehci->regs->port_status [wIndex]); + break; + case USB_PORT_FEAT_RESET: + /* line status bits may report this as low speed */ + if ((temp & (PORT_PE|PORT_CONNECT)) == PORT_CONNECT + && PORT_USB11 (temp)) { + ehci_dbg (ehci, + "port %d low speed --> companion\n", + wIndex + 1); + temp |= PORT_OWNER; + } else { + ehci_vdbg (ehci, "port %d reset\n", wIndex + 1); + temp |= PORT_RESET; + temp &= ~PORT_PE; + + /* + * caller must wait, then call GetPortStatus + * usb 2.0 spec says 50 ms resets on root + */ + ehci->reset_done [wIndex] = jiffies + + ((50 /* msec */ * HZ) / 1000); + } + writel (temp, &ehci->regs->port_status [wIndex]); + break; + default: + goto error; + } + readl (&ehci->regs->command); /* unblock posted writes */ + break; + + default: +error: + /* "stall" on error */ + retval = -EPIPE; + } + spin_unlock_irqrestore (&ehci->lock, flags); + return retval; +} diff -Nru a/drivers/usb/host/ehci-mem.c b/drivers/usb/host/ehci-mem.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/usb/host/ehci-mem.c Thu Mar 6 14:23:09 2003 @@ -0,0 +1,251 @@ +/* + * Copyright (c) 2001 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. + */ + +/* this file is part of ehci-hcd.c */ + +/*-------------------------------------------------------------------------*/ + +/* + * There's basically three types of memory: + * - data used only by the HCD ... kmalloc is fine + * - async and periodic schedules, shared by HC and HCD ... these + * need to use pci_pool or pci_alloc_consistent + * - driver buffers, read/written by HC ... single shot DMA mapped + * + * There's also PCI "register" data, which is memory mapped. + * No memory seen by this driver is pagable. + */ + +/*-------------------------------------------------------------------------*/ +/* + * Allocator / cleanup for the per device structure + * Called by hcd init / removal code + */ +static struct usb_hcd *ehci_hcd_alloc (void) +{ + struct ehci_hcd *ehci; + + ehci = (struct ehci_hcd *) + kmalloc (sizeof (struct ehci_hcd), GFP_KERNEL); + if (ehci != 0) { + memset (ehci, 0, sizeof (struct ehci_hcd)); + return &ehci->hcd; + } + return 0; +} + +static void ehci_hcd_free (struct usb_hcd *hcd) +{ + kfree (hcd_to_ehci (hcd)); +} + +/*-------------------------------------------------------------------------*/ + +/* Allocate the key transfer structures from the previously allocated pool */ + +static inline void ehci_qtd_init (struct ehci_qtd *qtd, dma_addr_t dma) +{ + memset (qtd, 0, sizeof *qtd); + qtd->qtd_dma = dma; + qtd->hw_next = EHCI_LIST_END; + qtd->hw_alt_next = EHCI_LIST_END; + INIT_LIST_HEAD (&qtd->qtd_list); +} + +static struct ehci_qtd *ehci_qtd_alloc (struct ehci_hcd *ehci, int flags) +{ + struct ehci_qtd *qtd; + dma_addr_t dma; + + qtd = pci_pool_alloc (ehci->qtd_pool, flags, &dma); + if (qtd != 0) { + ehci_qtd_init (qtd, dma); + } + return qtd; +} + +static inline void ehci_qtd_free (struct ehci_hcd *ehci, struct ehci_qtd *qtd) +{ + pci_pool_free (ehci->qtd_pool, qtd, qtd->qtd_dma); +} + + +static struct ehci_qh *ehci_qh_alloc (struct ehci_hcd *ehci, int flags) +{ + struct ehci_qh *qh; + dma_addr_t dma; + + qh = (struct ehci_qh *) + pci_pool_alloc (ehci->qh_pool, flags, &dma); + if (!qh) + return qh; + + memset (qh, 0, sizeof *qh); + atomic_set (&qh->refcount, 1); + qh->qh_dma = dma; + // INIT_LIST_HEAD (&qh->qh_list); + INIT_LIST_HEAD (&qh->qtd_list); + + /* dummy td enables safe urb queuing */ + qh->dummy = ehci_qtd_alloc (ehci, flags); + if (qh->dummy == 0) { + ehci_dbg (ehci, "no dummy td\n"); + pci_pool_free (ehci->qh_pool, qh, qh->qh_dma); + qh = 0; + } + return qh; +} + +/* to share a qh (cpu threads, or hc) */ +static inline struct ehci_qh *qh_get (/* ehci, */ struct ehci_qh *qh) +{ + atomic_inc (&qh->refcount); + return qh; +} + +static void qh_put (struct ehci_hcd *ehci, struct ehci_qh *qh) +{ + if (!atomic_dec_and_test (&qh->refcount)) + return; + /* clean qtds first, and know this is not linked */ + if (!list_empty (&qh->qtd_list) || qh->qh_next.ptr) { + ehci_dbg (ehci, "unused qh not empty!\n"); + BUG (); + } + if (qh->dummy) + ehci_qtd_free (ehci, qh->dummy); + pci_pool_free (ehci->qh_pool, qh, qh->qh_dma); +} + +/*-------------------------------------------------------------------------*/ + +/* The queue heads and transfer descriptors are managed from pools tied + * to each of the "per device" structures. + * This is the initialisation and cleanup code. + */ + +static void ehci_mem_cleanup (struct ehci_hcd *ehci) +{ + if (ehci->async) + qh_put (ehci, ehci->async); + ehci->async = 0; + + /* PCI consistent memory and pools */ + if (ehci->qtd_pool) + pci_pool_destroy (ehci->qtd_pool); + ehci->qtd_pool = 0; + + if (ehci->qh_pool) { + pci_pool_destroy (ehci->qh_pool); + ehci->qh_pool = 0; + } + + if (ehci->itd_pool) + pci_pool_destroy (ehci->itd_pool); + ehci->itd_pool = 0; + + if (ehci->sitd_pool) + pci_pool_destroy (ehci->sitd_pool); + ehci->sitd_pool = 0; + + if (ehci->periodic) + pci_free_consistent (ehci->hcd.pdev, + ehci->periodic_size * sizeof (u32), + ehci->periodic, ehci->periodic_dma); + ehci->periodic = 0; + + /* shadow periodic table */ + if (ehci->pshadow) + kfree (ehci->pshadow); + ehci->pshadow = 0; +} + +/* remember to add cleanup code (above) if you add anything here */ +static int ehci_mem_init (struct ehci_hcd *ehci, int flags) +{ + int i; + + /* QTDs for control/bulk/intr transfers */ + ehci->qtd_pool = pci_pool_create ("ehci_qtd", ehci->hcd.pdev, + sizeof (struct ehci_qtd), + 32 /* byte alignment (for hw parts) */, + 4096 /* can't cross 4K */, + flags); + if (!ehci->qtd_pool) { + goto fail; + } + + /* QHs for control/bulk/intr transfers */ + ehci->qh_pool = pci_pool_create ("ehci_qh", ehci->hcd.pdev, + sizeof (struct ehci_qh), + 32 /* byte alignment (for hw parts) */, + 4096 /* can't cross 4K */, + flags); + if (!ehci->qh_pool) { + goto fail; + } + ehci->async = ehci_qh_alloc (ehci, flags); + if (!ehci->async) { + goto fail; + } + + /* ITD for high speed ISO transfers */ + ehci->itd_pool = pci_pool_create ("ehci_itd", ehci->hcd.pdev, + sizeof (struct ehci_itd), + 32 /* byte alignment (for hw parts) */, + 4096 /* can't cross 4K */, + flags); + if (!ehci->itd_pool) { + goto fail; + } + + /* SITD for full/low speed split ISO transfers */ + ehci->sitd_pool = pci_pool_create ("ehci_sitd", ehci->hcd.pdev, + sizeof (struct ehci_sitd), + 32 /* byte alignment (for hw parts) */, + 4096 /* can't cross 4K */, + flags); + if (!ehci->sitd_pool) { + goto fail; + } + + /* Hardware periodic table */ + ehci->periodic = (u32 *) + pci_alloc_consistent (ehci->hcd.pdev, + ehci->periodic_size * sizeof (u32), + &ehci->periodic_dma); + if (ehci->periodic == 0) { + goto fail; + } + for (i = 0; i < ehci->periodic_size; i++) + ehci->periodic [i] = EHCI_LIST_END; + + /* software shadow of hardware table */ + ehci->pshadow = kmalloc (ehci->periodic_size * sizeof (void *), flags); + if (ehci->pshadow == 0) { + goto fail; + } + memset (ehci->pshadow, 0, ehci->periodic_size * sizeof (void *)); + + return 0; + +fail: + ehci_dbg (ehci, "couldn't init memory\n"); + ehci_mem_cleanup (ehci); + return -ENOMEM; +} diff -Nru a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/usb/host/ehci-q.c Thu Mar 6 14:23:09 2003 @@ -0,0 +1,1090 @@ +/* + * Copyright (c) 2001-2002 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. + */ + +/* this file is part of ehci-hcd.c */ + +/*-------------------------------------------------------------------------*/ + +/* + * EHCI hardware queue manipulation ... the core. QH/QTD manipulation. + * + * Control, bulk, and interrupt traffic all use "qh" lists. They list "qtd" + * entries describing USB transactions, max 16-20kB/entry (with 4kB-aligned + * buffers needed for the larger number). We use one QH per endpoint, queue + * multiple urbs (all three types) per endpoint. URBs may need several qtds. + * + * ISO traffic uses "ISO TD" (itd, and sitd) records, and (along with + * interrupts) needs careful scheduling. Performance improvements can be + * an ongoing challenge. That's in "ehci-sched.c". + * + * USB 1.1 devices are handled (a) by "companion" OHCI or UHCI root hubs, + * or otherwise through transaction translators (TTs) in USB 2.0 hubs using + * (b) special fields in qh entries or (c) split iso entries. TTs will + * buffer low/full speed data so the host collects it at high speed. + */ + +/*-------------------------------------------------------------------------*/ + +/* fill a qtd, returning how much of the buffer we were able to queue up */ + +static int +qtd_fill (struct ehci_qtd *qtd, dma_addr_t buf, size_t len, + int token, int maxpacket) +{ + int i, count; + u64 addr = buf; + + /* one buffer entry per 4K ... first might be short or unaligned */ + qtd->hw_buf [0] = cpu_to_le32 ((u32)addr); + qtd->hw_buf_hi [0] = cpu_to_le32 ((u32)(addr >> 32)); + count = 0x1000 - (buf & 0x0fff); /* rest of that page */ + if (likely (len < count)) /* ... iff needed */ + count = len; + else { + buf += 0x1000; + buf &= ~0x0fff; + + /* per-qtd limit: from 16K to 20K (best alignment) */ + for (i = 1; count < len && i < 5; i++) { + addr = buf; + qtd->hw_buf [i] = cpu_to_le32 ((u32)addr); + qtd->hw_buf_hi [i] = cpu_to_le32 ((u32)(addr >> 32)); + buf += 0x1000; + if ((count + 0x1000) < len) + count += 0x1000; + else + count = len; + } + + /* short packets may only terminate transfers */ + if (count != len) + count -= (count % maxpacket); + } + qtd->hw_token = cpu_to_le32 ((count << 16) | token); + qtd->length = count; + + return count; +} + +/*-------------------------------------------------------------------------*/ + +/* update halted (but potentially linked) qh */ + +static inline void +qh_update (struct ehci_hcd *ehci, struct ehci_qh *qh, struct ehci_qtd *qtd) +{ + qh->hw_current = 0; + qh->hw_qtd_next = QTD_NEXT (qtd->qtd_dma); + qh->hw_alt_next = EHCI_LIST_END; + + /* HC must see latest qtd and qh data before we clear ACTIVE+HALT */ + wmb (); + qh->hw_token &= __constant_cpu_to_le32 (QTD_TOGGLE | QTD_STS_PING); +} + +/*-------------------------------------------------------------------------*/ + +#define IS_SHORT_READ(token) (QTD_LENGTH (token) != 0 && QTD_PID (token) == 1) + +static void qtd_copy_status ( + struct ehci_hcd *ehci, + struct urb *urb, + size_t length, + u32 token +) +{ + /* count IN/OUT bytes, not SETUP (even short packets) */ + if (likely (QTD_PID (token) != 2)) + urb->actual_length += length - QTD_LENGTH (token); + + /* don't modify error codes */ + if (unlikely (urb->status != -EINPROGRESS)) + return; + + /* force cleanup after short read; not always an error */ + if (unlikely (IS_SHORT_READ (token))) + urb->status = -EREMOTEIO; + + /* serious "can't proceed" faults reported by the hardware */ + if (token & QTD_STS_HALT) { + if (token & QTD_STS_BABBLE) { + /* FIXME "must" disable babbling device's port too */ + urb->status = -EOVERFLOW; + } else if (token & QTD_STS_MMF) { + /* fs/ls interrupt xfer missed the complete-split */ + urb->status = -EPROTO; + } else if (token & QTD_STS_DBE) { + urb->status = (QTD_PID (token) == 1) /* IN ? */ + ? -ENOSR /* hc couldn't read data */ + : -ECOMM; /* hc couldn't write data */ + } else if (token & QTD_STS_XACT) { + /* timeout, bad crc, wrong PID, etc; retried */ + if (QTD_CERR (token)) + urb->status = -EPIPE; + else { + dbg ("3strikes"); + urb->status = -EPROTO; + } + /* CERR nonzero + no errors + halt --> stall */ + } else if (QTD_CERR (token)) + urb->status = -EPIPE; + else /* unknown */ + urb->status = -EPROTO; + + ehci_vdbg (ehci, + "dev%d ep%d%s qtd token %08x --> status %d\n", + usb_pipedevice (urb->pipe), + usb_pipeendpoint (urb->pipe), + usb_pipein (urb->pipe) ? "in" : "out", + token, urb->status); + + /* stall indicates some recovery action is needed */ + if (urb->status == -EPIPE) { + int pipe = urb->pipe; + + if (!usb_pipecontrol (pipe)) + usb_endpoint_halt (urb->dev, + usb_pipeendpoint (pipe), + usb_pipeout (pipe)); + if (urb->dev->tt && !usb_pipeint (pipe)) { +#ifdef DEBUG + struct usb_device *tt = urb->dev->tt->hub; + dbg ("clear tt %s-%s p%d buffer, a%d ep%d", + tt->bus->bus_name, tt->devpath, + urb->dev->ttport, urb->dev->devnum, + usb_pipeendpoint (pipe)); +#endif /* DEBUG */ + usb_hub_tt_clear_buffer (urb->dev, pipe); + } + } + } +} + +static void +ehci_urb_done (struct ehci_hcd *ehci, struct urb *urb, struct pt_regs *regs) +{ +#ifdef INTR_AUTOMAGIC + struct urb *resubmit = 0; + struct usb_device *dev = 0; + + static int ehci_urb_enqueue (struct usb_hcd *, struct urb *, int); +#endif + + if (likely (urb->hcpriv != 0)) { + struct ehci_qh *qh = (struct ehci_qh *) urb->hcpriv; + + /* S-mask in a QH means it's an interrupt urb */ + if ((qh->hw_info2 & cpu_to_le32 (0x00ff)) != 0) { + + /* ... update hc-wide periodic stats (for usbfs) */ + hcd_to_bus (&ehci->hcd)->bandwidth_int_reqs--; + +#ifdef INTR_AUTOMAGIC + if (!((urb->status == -ENOENT) + || (urb->status == -ECONNRESET))) { + resubmit = usb_get_urb (urb); + dev = urb->dev; + } +#endif + } + qh_put (ehci, qh); + } + + spin_lock (&urb->lock); + urb->hcpriv = 0; + switch (urb->status) { + case -EINPROGRESS: /* success */ + urb->status = 0; + default: /* fault */ + COUNT (ehci->stats.complete); + break; + case -EREMOTEIO: /* fault or normal */ + if (!(urb->transfer_flags & URB_SHORT_NOT_OK)) + urb->status = 0; + COUNT (ehci->stats.complete); + break; + case -ECONNRESET: /* canceled */ + case -ENOENT: + COUNT (ehci->stats.unlink); + break; + } + spin_unlock (&urb->lock); + + /* complete() can reenter this HCD */ + spin_unlock (&ehci->lock); + usb_hcd_giveback_urb (&ehci->hcd, urb, regs); + +#ifdef INTR_AUTOMAGIC + if (resubmit && ((urb->status == -ENOENT) + || (urb->status == -ECONNRESET))) { + usb_put_urb (resubmit); + resubmit = 0; + } + // device drivers will soon be doing something like this + if (resubmit) { + int status; + + resubmit->dev = dev; + status = SUBMIT_URB (resubmit, SLAB_ATOMIC); + if (status != 0) + err ("can't resubmit interrupt urb %p: status %d", + resubmit, status); + usb_put_urb (resubmit); + } +#endif + + spin_lock (&ehci->lock); +} + + +/* + * Process and free completed qtds for a qh, returning URBs to drivers. + * Chases up to qh->hw_current. Returns number of completions called, + * indicating how much "real" work we did. + */ +#define HALT_BIT cpu_to_le32(QTD_STS_HALT) +static unsigned +qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh, struct pt_regs *regs) +{ + struct ehci_qtd *last = 0, *end = qh->dummy; + struct list_head *entry, *tmp; + int stopped; + unsigned count = 0; + int do_status = 0; + u8 state; + + if (unlikely (list_empty (&qh->qtd_list))) + return count; + + /* completions (or tasks on other cpus) must never clobber HALT + * till we've gone through and cleaned everything up, even when + * they add urbs to this qh's queue or mark them for unlinking. + * + * NOTE: unlinking expects to be done in queue order. + */ + state = qh->qh_state; + qh->qh_state = QH_STATE_COMPLETING; + stopped = (state == QH_STATE_IDLE); + + /* remove de-activated QTDs from front of queue. + * after faults (including short reads), cleanup this urb + * then let the queue advance. + * if queue is stopped, handles unlinks. + */ + list_for_each_safe (entry, tmp, &qh->qtd_list) { + struct ehci_qtd *qtd; + struct urb *urb; + u32 token = 0; + + qtd = list_entry (entry, struct ehci_qtd, qtd_list); + urb = qtd->urb; + + /* clean up any state from previous QTD ...*/ + if (last) { + if (likely (last->urb != urb)) { + ehci_urb_done (ehci, last->urb, regs); + count++; + } + ehci_qtd_free (ehci, last); + last = 0; + } + + /* ignore urbs submitted during completions we reported */ + if (qtd == end) + break; + + /* hardware copies qtd out of qh overlay */ + rmb (); + token = le32_to_cpu (qtd->hw_token); + stopped = stopped + || (HALT_BIT & qh->hw_token) != 0 + || (ehci->hcd.state == USB_STATE_HALT); + + /* always clean up qtds the hc de-activated */ + if ((token & QTD_STS_ACTIVE) == 0) { + + /* magic dummy for short reads; won't advance */ + if (IS_SHORT_READ (token) + && !(token & QTD_STS_HALT) + && (qh->hw_alt_next & QTD_MASK) + == ehci->async->hw_alt_next) { + stopped = 1; + goto halt; + } + + /* stop scanning when we reach qtds the hc is using */ + } else if (likely (!stopped)) { + break; + + } else { + /* ignore active urbs unless some previous qtd + * for the urb faulted (including short read) or + * its urb was canceled. we may patch qh or qtds. + */ + if (likely (urb->status == -EINPROGRESS)) + continue; + + /* issue status after short control reads */ + if (unlikely (do_status != 0) + && QTD_PID (token) == 0 /* OUT */) { + do_status = 0; + continue; + } + + /* token in overlay may be most current */ + if (state == QH_STATE_IDLE + && cpu_to_le32 (qtd->qtd_dma) + == qh->hw_current) + token = le32_to_cpu (qh->hw_token); + + /* force halt for unlinked or blocked qh, so we'll + * patch the qh later and so that completions can't + * activate it while we "know" it's stopped. + */ + if ((HALT_BIT & qh->hw_token) == 0) { +halt: + qh->hw_token |= HALT_BIT; + wmb (); + } + } + + /* remove it from the queue */ + spin_lock (&urb->lock); + qtd_copy_status (ehci, urb, qtd->length, token); + do_status = (urb->status == -EREMOTEIO) + && usb_pipecontrol (urb->pipe); + spin_unlock (&urb->lock); + + if (stopped && qtd->qtd_list.prev != &qh->qtd_list) { + last = list_entry (qtd->qtd_list.prev, + struct ehci_qtd, qtd_list); + last->hw_next = qtd->hw_next; + } + list_del (&qtd->qtd_list); + last = qtd; + } + + /* last urb's completion might still need calling */ + if (likely (last != 0)) { + ehci_urb_done (ehci, last->urb, regs); + count++; + ehci_qtd_free (ehci, last); + } + + /* restore original state; caller must unlink or relink */ + qh->qh_state = state; + + /* update qh after fault cleanup */ + if (unlikely ((HALT_BIT & qh->hw_token) != 0)) { + qh_update (ehci, qh, + list_empty (&qh->qtd_list) + ? qh->dummy + : list_entry (qh->qtd_list.next, + struct ehci_qtd, qtd_list)); + } + + return count; +} +#undef HALT_BIT + +/*-------------------------------------------------------------------------*/ + +/* + * reverse of qh_urb_transaction: free a list of TDs. + * used for cleanup after errors, before HC sees an URB's TDs. + */ +static void qtd_list_free ( + struct ehci_hcd *ehci, + struct urb *urb, + struct list_head *qtd_list +) { + struct list_head *entry, *temp; + + list_for_each_safe (entry, temp, qtd_list) { + struct ehci_qtd *qtd; + + qtd = list_entry (entry, struct ehci_qtd, qtd_list); + list_del (&qtd->qtd_list); + ehci_qtd_free (ehci, qtd); + } +} + +/* + * create a list of filled qtds for this URB; won't link into qh. + */ +static struct list_head * +qh_urb_transaction ( + struct ehci_hcd *ehci, + struct urb *urb, + struct list_head *head, + int flags +) { + struct ehci_qtd *qtd, *qtd_prev; + dma_addr_t buf; + int len, maxpacket; + int is_input; + u32 token; + + /* + * URBs map to sequences of QTDs: one logical transaction + */ + qtd = ehci_qtd_alloc (ehci, flags); + if (unlikely (!qtd)) + return 0; + list_add_tail (&qtd->qtd_list, head); + qtd->urb = urb; + + token = QTD_STS_ACTIVE; + token |= (EHCI_TUNE_CERR << 10); + /* for split transactions, SplitXState initialized to zero */ + + len = urb->transfer_buffer_length; + is_input = usb_pipein (urb->pipe); + if (usb_pipecontrol (urb->pipe)) { + /* SETUP pid */ + qtd_fill (qtd, urb->setup_dma, sizeof (struct usb_ctrlrequest), + token | (2 /* "setup" */ << 8), 8); + + /* ... and always at least one more pid */ + token ^= QTD_TOGGLE; + qtd_prev = qtd; + qtd = ehci_qtd_alloc (ehci, flags); + if (unlikely (!qtd)) + goto cleanup; + qtd->urb = urb; + qtd_prev->hw_next = QTD_NEXT (qtd->qtd_dma); + list_add_tail (&qtd->qtd_list, head); + } + + /* + * data transfer stage: buffer setup + */ + if (likely (len > 0)) + buf = urb->transfer_dma; + else + buf = 0; + + // FIXME this 'buf' check break some zlps... + if (!buf || is_input) + token |= (1 /* "in" */ << 8); + /* else it's already initted to "out" pid (0 << 8) */ + + maxpacket = usb_maxpacket (urb->dev, urb->pipe, !is_input) & 0x03ff; + + /* + * buffer gets wrapped in one or more qtds; + * last one may be "short" (including zero len) + * and may serve as a control status ack + */ + for (;;) { + int this_qtd_len; + + this_qtd_len = qtd_fill (qtd, buf, len, token, maxpacket); + len -= this_qtd_len; + buf += this_qtd_len; + if (is_input) + qtd->hw_alt_next = ehci->async->hw_alt_next; + + /* qh makes control packets use qtd toggle; maybe switch it */ + if ((maxpacket & (this_qtd_len + (maxpacket - 1))) == 0) + token ^= QTD_TOGGLE; + + if (likely (len <= 0)) + break; + + qtd_prev = qtd; + qtd = ehci_qtd_alloc (ehci, flags); + if (unlikely (!qtd)) + goto cleanup; + qtd->urb = urb; + qtd_prev->hw_next = QTD_NEXT (qtd->qtd_dma); + list_add_tail (&qtd->qtd_list, head); + } + + /* unless the bulk/interrupt caller wants a chance to clean + * up after short reads, hc should advance qh past this urb + */ + if (likely ((urb->transfer_flags & URB_SHORT_NOT_OK) == 0 + || usb_pipecontrol (urb->pipe))) + qtd->hw_alt_next = EHCI_LIST_END; + + /* + * control requests may need a terminating data "status" ack; + * bulk ones may need a terminating short packet (zero length). + */ + if (likely (buf != 0)) { + int one_more = 0; + + if (usb_pipecontrol (urb->pipe)) { + one_more = 1; + token ^= 0x0100; /* "in" <--> "out" */ + token |= QTD_TOGGLE; /* force DATA1 */ + } else if (usb_pipebulk (urb->pipe) + && (urb->transfer_flags & URB_ZERO_PACKET) + && !(urb->transfer_buffer_length % maxpacket)) { + one_more = 1; + } + if (one_more) { + qtd_prev = qtd; + qtd = ehci_qtd_alloc (ehci, flags); + if (unlikely (!qtd)) + goto cleanup; + qtd->urb = urb; + qtd_prev->hw_next = QTD_NEXT (qtd->qtd_dma); + list_add_tail (&qtd->qtd_list, head); + + /* never any data in such packets */ + qtd_fill (qtd, 0, 0, token, 0); + } + } + + /* by default, enable interrupt on urb completion */ + if (likely (!(urb->transfer_flags & URB_NO_INTERRUPT))) + qtd->hw_token |= __constant_cpu_to_le32 (QTD_IOC); + return head; + +cleanup: + qtd_list_free (ehci, urb, head); + return 0; +} + +/*-------------------------------------------------------------------------*/ + +/* + * Hardware maintains data toggle (like OHCI) ... here we (re)initialize + * the hardware data toggle in the QH, and set the pseudo-toggle in udev + * so we can see if usb_clear_halt() was called. NOP for control, since + * we set up qh->hw_info1 to always use the QTD toggle bits. + */ +static inline void +clear_toggle (struct usb_device *udev, int ep, int is_out, struct ehci_qh *qh) +{ + vdbg ("clear toggle, dev %d ep 0x%x-%s", + udev->devnum, ep, is_out ? "out" : "in"); + qh->hw_token &= ~__constant_cpu_to_le32 (QTD_TOGGLE); + usb_settoggle (udev, ep, is_out, 1); +} + +// Would be best to create all qh's from config descriptors, +// when each interface/altsetting is established. Unlink +// any previous qh and cancel its urbs first; endpoints are +// implicitly reset then (data toggle too). +// That'd mean updating how usbcore talks to HCDs. (2.5?) + + +// high bandwidth multiplier, as encoded in highspeed endpoint descriptors +#define hb_mult(wMaxPacketSize) (1 + (((wMaxPacketSize) >> 11) & 0x03)) +// ... and packet size, for any kind of endpoint descriptor +#define max_packet(wMaxPacketSize) ((wMaxPacketSize) & 0x03ff) + +/* + * Each QH holds a qtd list; a QH is used for everything except iso. + * + * For interrupt urbs, the scheduler must set the microframe scheduling + * mask(s) each time the QH gets scheduled. For highspeed, that's + * just one microframe in the s-mask. For split interrupt transactions + * there are additional complications: c-mask, maybe FSTNs. + */ +static struct ehci_qh * +qh_make ( + struct ehci_hcd *ehci, + struct urb *urb, + int flags +) { + struct ehci_qh *qh = ehci_qh_alloc (ehci, flags); + u32 info1 = 0, info2 = 0; + int is_input, type; + int maxp = 0; + + if (!qh) + return qh; + + /* + * init endpoint/device data for this QH + */ + info1 |= usb_pipeendpoint (urb->pipe) << 8; + info1 |= usb_pipedevice (urb->pipe) << 0; + + is_input = usb_pipein (urb->pipe); + type = usb_pipetype (urb->pipe); + maxp = usb_maxpacket (urb->dev, urb->pipe, !is_input); + + /* Compute interrupt scheduling parameters just once, and save. + * - allowing for high bandwidth, how many nsec/uframe are used? + * - split transactions need a second CSPLIT uframe; same question + * - splits also need a schedule gap (for full/low speed I/O) + * - qh has a polling interval + * + * For control/bulk requests, the HC or TT handles these. + */ + if (type == PIPE_INTERRUPT) { + qh->usecs = usb_calc_bus_time (USB_SPEED_HIGH, is_input, 0, + hb_mult (maxp) * max_packet (maxp)); + qh->start = NO_FRAME; + + if (urb->dev->speed == USB_SPEED_HIGH) { + qh->c_usecs = 0; + qh->gap_uf = 0; + + /* FIXME handle HS periods of less than 1 frame. */ + qh->period = urb->interval >> 3; + if (qh->period < 1) { + dbg ("intr period %d uframes, NYET!", + urb->interval); + goto done; + } + } else { + /* gap is f(FS/LS transfer times) */ + qh->gap_uf = 1 + usb_calc_bus_time (urb->dev->speed, + is_input, 0, maxp) / (125 * 1000); + + /* FIXME this just approximates SPLIT/CSPLIT times */ + if (is_input) { // SPLIT, gap, CSPLIT+DATA + qh->c_usecs = qh->usecs + HS_USECS (0); + qh->usecs = HS_USECS (1); + } else { // SPLIT+DATA, gap, CSPLIT + qh->usecs += HS_USECS (1); + qh->c_usecs = HS_USECS (0); + } + + qh->period = urb->interval; + } + } + + /* using TT? */ + switch (urb->dev->speed) { + case USB_SPEED_LOW: + info1 |= (1 << 12); /* EPS "low" */ + /* FALL THROUGH */ + + case USB_SPEED_FULL: + /* EPS 0 means "full" */ + if (type != PIPE_INTERRUPT) + info1 |= (EHCI_TUNE_RL_TT << 28); + if (type == PIPE_CONTROL) { + info1 |= (1 << 27); /* for TT */ + info1 |= 1 << 14; /* toggle from qtd */ + } + info1 |= maxp << 16; + + info2 |= (EHCI_TUNE_MULT_TT << 30); + info2 |= urb->dev->ttport << 23; + info2 |= urb->dev->tt->hub->devnum << 16; + + /* NOTE: if (PIPE_INTERRUPT) { scheduler sets c-mask } */ + + break; + + case USB_SPEED_HIGH: /* no TT involved */ + info1 |= (2 << 12); /* EPS "high" */ + if (type == PIPE_CONTROL) { + info1 |= (EHCI_TUNE_RL_HS << 28); + info1 |= 64 << 16; /* usb2 fixed maxpacket */ + info1 |= 1 << 14; /* toggle from qtd */ + info2 |= (EHCI_TUNE_MULT_HS << 30); + } else if (type == PIPE_BULK) { + info1 |= (EHCI_TUNE_RL_HS << 28); + info1 |= 512 << 16; /* usb2 fixed maxpacket */ + info2 |= (EHCI_TUNE_MULT_HS << 30); + } else { /* PIPE_INTERRUPT */ + info1 |= max_packet (maxp) << 16; + info2 |= hb_mult (maxp) << 30; + } + break; + default: + dbg ("bogus dev %p speed %d", urb->dev, urb->dev->speed); +done: + qh_put (ehci, qh); + return 0; + } + + /* NOTE: if (PIPE_INTERRUPT) { scheduler sets s-mask } */ + + /* init as halted, toggle clear, advance to dummy */ + qh->qh_state = QH_STATE_IDLE; + qh->hw_info1 = cpu_to_le32 (info1); + qh->hw_info2 = cpu_to_le32 (info2); + qh_update (ehci, qh, qh->dummy); + qh->hw_token = cpu_to_le32 (QTD_STS_HALT); + usb_settoggle (urb->dev, usb_pipeendpoint (urb->pipe), !is_input, 1); + return qh; +} +#undef hb_mult +#undef hb_packet + +/*-------------------------------------------------------------------------*/ + +/* move qh (and its qtds) onto async queue; maybe enable queue. */ + +static void qh_link_async (struct ehci_hcd *ehci, struct ehci_qh *qh) +{ + u32 dma = QH_NEXT (qh->qh_dma); + struct ehci_qh *head; + + /* (re)start the async schedule? */ + head = ehci->async; + if (ehci->async_idle) + del_timer (&ehci->watchdog); + if (!head->qh_next.qh) { + u32 cmd = readl (&ehci->regs->command); + + if (!(cmd & CMD_ASE)) { + /* in case a clear of CMD_ASE didn't take yet */ + (void) handshake (&ehci->regs->status, STS_ASS, 0, 150); + cmd |= CMD_ASE | CMD_RUN; + writel (cmd, &ehci->regs->command); + ehci->hcd.state = USB_STATE_RUNNING; + /* posted write need not be known to HC yet ... */ + } + } + + qh->hw_token &= ~__constant_cpu_to_le32 (QTD_STS_HALT); + + /* splice right after start */ + qh->qh_next = head->qh_next; + qh->hw_next = head->hw_next; + wmb (); + + head->qh_next.qh = qh; + head->hw_next = dma; + + qh->qh_state = QH_STATE_LINKED; + /* qtd completions reported later by interrupt */ + + ehci->async_idle = 0; +} + +/*-------------------------------------------------------------------------*/ + +/* + * For control/bulk/interrupt, return QH with these TDs appended. + * Allocates and initializes the QH if necessary. + * Returns null if it can't allocate a QH it needs to. + * If the QH has TDs (urbs) already, that's great. + */ +static struct ehci_qh *qh_append_tds ( + struct ehci_hcd *ehci, + struct urb *urb, + struct list_head *qtd_list, + int epnum, + void **ptr +) +{ + struct ehci_qh *qh = 0; + + qh = (struct ehci_qh *) *ptr; + if (unlikely (qh == 0)) { + /* can't sleep here, we have ehci->lock... */ + qh = qh_make (ehci, urb, SLAB_ATOMIC); + *ptr = qh; + } + if (likely (qh != 0)) { + struct ehci_qtd *qtd; + + if (unlikely (list_empty (qtd_list))) + qtd = 0; + else + qtd = list_entry (qtd_list->next, struct ehci_qtd, + qtd_list); + + /* control qh may need patching after enumeration */ + if (unlikely (epnum == 0)) { + /* set_address changes the address */ + if (le32_to_cpu (qh->hw_info1 & 0x7f) == 0) + qh->hw_info1 |= cpu_to_le32 ( + usb_pipedevice (urb->pipe)); + + /* for full speed, ep0 maxpacket can grow */ + else if (!(qh->hw_info1 & cpu_to_le32 (0x3 << 12))) { + u32 info, max; + + info = le32_to_cpu (qh->hw_info1); + max = urb->dev->descriptor.bMaxPacketSize0; + if (max > (0x07ff & (info >> 16))) { + info &= ~(0x07ff << 16); + info |= max << 16; + qh->hw_info1 = cpu_to_le32 (info); + } + } + } + + /* FIXME: changing config or interface setting is not + * supported yet. preferred fix is for usbcore to tell + * us to clear out each endpoint's state, but... + */ + + /* usb_clear_halt() means qh data toggle gets reset */ + if (unlikely (!usb_gettoggle (urb->dev, + (epnum & 0x0f), !(epnum & 0x10))) + && !usb_pipecontrol (urb->pipe)) { + /* "never happens": drivers do stall cleanup right */ + if (qh->qh_state != QH_STATE_IDLE + && qh->qh_state != QH_STATE_COMPLETING) + ehci_warn (ehci, "clear toggle dev%d " + "ep%d%s: not idle\n", + usb_pipedevice (urb->pipe), + epnum & 0x0f, + usb_pipein (urb->pipe) + ? "in" : "out"); + /* else we know this overlay write is safe */ + clear_toggle (urb->dev, + epnum & 0x0f, !(epnum & 0x10), qh); + } + + /* just one way to queue requests: swap with the dummy qtd. + * only hc or qh_completions() usually modify the overlay. + */ + if (likely (qtd != 0)) { + struct ehci_qtd *dummy; + dma_addr_t dma; + u32 token; + + /* to avoid racing the HC, use the dummy td instead of + * the first td of our list (becomes new dummy). both + * tds stay deactivated until we're done, when the + * HC is allowed to fetch the old dummy (4.10.2). + */ + token = qtd->hw_token; + qtd->hw_token = 0; + wmb (); + dummy = qh->dummy; + + dma = dummy->qtd_dma; + *dummy = *qtd; + dummy->qtd_dma = dma; + + list_del (&qtd->qtd_list); + list_add (&dummy->qtd_list, qtd_list); + __list_splice (qtd_list, qh->qtd_list.prev); + + ehci_qtd_init (qtd, qtd->qtd_dma); + qh->dummy = qtd; + + /* hc must see the new dummy at list end */ + dma = qtd->qtd_dma; + qtd = list_entry (qh->qtd_list.prev, + struct ehci_qtd, qtd_list); + qtd->hw_next = QTD_NEXT (dma); + + /* let the hc process these next qtds */ + wmb (); + dummy->hw_token = token; + + urb->hcpriv = qh_get (qh); + } + } + return qh; +} + +/*-------------------------------------------------------------------------*/ + +static int +submit_async ( + struct ehci_hcd *ehci, + struct urb *urb, + struct list_head *qtd_list, + int mem_flags +) { + struct ehci_qtd *qtd; + struct hcd_dev *dev; + int epnum; + unsigned long flags; + struct ehci_qh *qh = 0; + + qtd = list_entry (qtd_list->next, struct ehci_qtd, qtd_list); + dev = (struct hcd_dev *)urb->dev->hcpriv; + epnum = usb_pipeendpoint (urb->pipe); + if (usb_pipein (urb->pipe) && !usb_pipecontrol (urb->pipe)) + epnum |= 0x10; + + vdbg ("%s: submit_async urb %p len %d ep %d-%s qtd %p [qh %p]", + hcd_to_bus (&ehci->hcd)->bus_name, + urb, urb->transfer_buffer_length, + epnum & 0x0f, (epnum & 0x10) ? "in" : "out", + qtd, dev ? dev->ep [epnum] : (void *)~0); + + spin_lock_irqsave (&ehci->lock, flags); + qh = qh_append_tds (ehci, urb, qtd_list, epnum, &dev->ep [epnum]); + + /* Control/bulk operations through TTs don't need scheduling, + * the HC and TT handle it when the TT has a buffer ready. + */ + if (likely (qh != 0)) { + if (likely (qh->qh_state == QH_STATE_IDLE)) + qh_link_async (ehci, qh_get (qh)); + } + spin_unlock_irqrestore (&ehci->lock, flags); + if (unlikely (qh == 0)) { + qtd_list_free (ehci, urb, qtd_list); + return -ENOMEM; + } + return 0; +} + +/*-------------------------------------------------------------------------*/ + +/* the async qh for the qtds being reclaimed are now unlinked from the HC */ + +static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh); + +static void end_unlink_async (struct ehci_hcd *ehci, struct pt_regs *regs) +{ + struct ehci_qh *qh = ehci->reclaim; + struct ehci_qh *next; + + del_timer (&ehci->watchdog); + + qh->hw_next = cpu_to_le32 (qh->qh_dma); + qh->qh_state = QH_STATE_IDLE; + qh->qh_next.qh = 0; + qh_put (ehci, qh); // refcount from reclaim + ehci->reclaim = 0; + ehci->reclaim_ready = 0; + + /* other unlink(s) may be pending (in QH_STATE_UNLINK_WAIT) */ + next = qh->reclaim; + qh->reclaim = 0; + + qh_completions (ehci, qh, regs); + + if (!list_empty (&qh->qtd_list) + && HCD_IS_RUNNING (ehci->hcd.state)) + qh_link_async (ehci, qh); + else { + qh_put (ehci, qh); // refcount from async list + + /* it's not free to turn the async schedule on/off; leave it + * active but idle for a while once it empties. + */ + if (HCD_IS_RUNNING (ehci->hcd.state) + && ehci->async->qh_next.qh == 0 + && !timer_pending (&ehci->watchdog)) { + ehci->async_idle = 1; + mod_timer (&ehci->watchdog, + jiffies + EHCI_ASYNC_JIFFIES); + } + } + + if (next) + start_unlink_async (ehci, next); +} + +/* makes sure the async qh will become idle */ +/* caller must own ehci->lock */ + +static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh) +{ + int cmd = readl (&ehci->regs->command); + struct ehci_qh *prev; + +#ifdef DEBUG + if (ehci->reclaim + || (qh->qh_state != QH_STATE_LINKED + && qh->qh_state != QH_STATE_UNLINK_WAIT) +#ifdef CONFIG_SMP +// this macro lies except on SMP compiles + || !spin_is_locked (&ehci->lock) +#endif + ) + BUG (); +#endif + + /* stop async schedule right now? */ + if (unlikely (qh == ehci->async)) { + /* can't get here without STS_ASS set */ + if (ehci->hcd.state != USB_STATE_HALT) { + writel (cmd & ~CMD_ASE, &ehci->regs->command); + wmb (); + // handshake later, if we need to + } + return; + } + + qh->qh_state = QH_STATE_UNLINK; + ehci->reclaim = qh = qh_get (qh); + + prev = ehci->async; + while (prev->qh_next.qh != qh) + prev = prev->qh_next.qh; + + prev->hw_next = qh->hw_next; + prev->qh_next = qh->qh_next; + wmb (); + + if (unlikely (ehci->hcd.state == USB_STATE_HALT)) { + /* if (unlikely (qh->reclaim != 0)) + * this will recurse, probably not much + */ + end_unlink_async (ehci, NULL); + return; + } + + ehci->reclaim_ready = 0; + cmd |= CMD_IAAD; + writel (cmd, &ehci->regs->command); + /* posted write need not be known to HC yet ... */ + + mod_timer (&ehci->watchdog, jiffies + EHCI_WATCHDOG_JIFFIES); +} + +/*-------------------------------------------------------------------------*/ + +static void +scan_async (struct ehci_hcd *ehci, struct pt_regs *regs) +{ + struct ehci_qh *qh; + + if (!++(ehci->stamp)) + ehci->stamp++; +rescan: + qh = ehci->async->qh_next.qh; + if (likely (qh != 0)) { + do { + /* clean any finished work for this qh */ + if (!list_empty (&qh->qtd_list) + && qh->stamp != ehci->stamp) { + int temp; + + /* unlinks could happen here; completion + * reporting drops the lock. rescan using + * the latest schedule, but don't rescan + * qhs we already finished (no looping). + */ + qh = qh_get (qh); + qh->stamp = ehci->stamp; + temp = qh_completions (ehci, qh, regs); + qh_put (ehci, qh); + if (temp != 0) { + goto rescan; + } + } + + /* unlink idle entries, reducing HC PCI usage as + * well as HCD schedule-scanning costs. + * + * FIXME don't unlink idle entries so quickly; it + * can penalize (common) half duplex protocols. + */ + if (list_empty (&qh->qtd_list) && !ehci->reclaim) { + start_unlink_async (ehci, qh); + } + + qh = qh->qh_next.qh; + } while (qh); + } +} diff -Nru a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/usb/host/ehci-sched.c Thu Mar 6 14:23:09 2003 @@ -0,0 +1,1123 @@ +/* + * Copyright (c) 2001-2002 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. + */ + +/* this file is part of ehci-hcd.c */ + +/*-------------------------------------------------------------------------*/ + +/* + * EHCI scheduled transaction support: interrupt, iso, split iso + * These are called "periodic" transactions in the EHCI spec. + * + * Note that for interrupt transfers, the QH/QTD manipulation is shared + * with the "asynchronous" transaction support (control/bulk transfers). + * The only real difference is in how interrupt transfers are scheduled. + * We get some funky API restrictions from the current URB model, which + * works notably better for reading transfers than for writing. (And + * which accordingly needs to change before it'll work inside devices, + * or with "USB On The Go" additions to USB 2.0 ...) + */ + +static int ehci_get_frame (struct usb_hcd *hcd); + +/*-------------------------------------------------------------------------*/ + +/* + * periodic_next_shadow - return "next" pointer on shadow list + * @periodic: host pointer to qh/itd/sitd + * @tag: hardware tag for type of this record + */ +static union ehci_shadow * +periodic_next_shadow (union ehci_shadow *periodic, int tag) +{ + switch (tag) { + case Q_TYPE_QH: + return &periodic->qh->qh_next; + case Q_TYPE_FSTN: + return &periodic->fstn->fstn_next; + case Q_TYPE_ITD: + return &periodic->itd->itd_next; +#ifdef have_split_iso + case Q_TYPE_SITD: + return &periodic->sitd->sitd_next; +#endif /* have_split_iso */ + } + dbg ("BAD shadow %p tag %d", periodic->ptr, tag); + // BUG (); + return 0; +} + +/* returns true after successful unlink */ +/* caller must hold ehci->lock */ +static int periodic_unlink (struct ehci_hcd *ehci, unsigned frame, void *ptr) +{ + union ehci_shadow *prev_p = &ehci->pshadow [frame]; + u32 *hw_p = &ehci->periodic [frame]; + union ehci_shadow here = *prev_p; + union ehci_shadow *next_p; + + /* find predecessor of "ptr"; hw and shadow lists are in sync */ + while (here.ptr && here.ptr != ptr) { + prev_p = periodic_next_shadow (prev_p, Q_NEXT_TYPE (*hw_p)); + hw_p = &here.qh->hw_next; + here = *prev_p; + } + /* an interrupt entry (at list end) could have been shared */ + if (!here.ptr) { + dbg ("entry %p no longer on frame [%d]", ptr, frame); + return 0; + } + // vdbg ("periodic unlink %p from frame %d", ptr, frame); + + /* update hardware list ... HC may still know the old structure, so + * don't change hw_next until it'll have purged its cache + */ + next_p = periodic_next_shadow (&here, Q_NEXT_TYPE (*hw_p)); + *hw_p = here.qh->hw_next; + + /* unlink from shadow list; HCD won't see old structure again */ + *prev_p = *next_p; + next_p->ptr = 0; + + return 1; +} + +/* how many of the uframe's 125 usecs are allocated? */ +static unsigned short +periodic_usecs (struct ehci_hcd *ehci, unsigned frame, unsigned uframe) +{ + u32 *hw_p = &ehci->periodic [frame]; + union ehci_shadow *q = &ehci->pshadow [frame]; + unsigned usecs = 0; + + while (q->ptr) { + switch (Q_NEXT_TYPE (*hw_p)) { + case Q_TYPE_QH: + /* is it in the S-mask? */ + if (q->qh->hw_info2 & cpu_to_le32 (1 << uframe)) + usecs += q->qh->usecs; + /* ... or C-mask? */ + if (q->qh->hw_info2 & cpu_to_le32 (1 << (8 + uframe))) + usecs += q->qh->c_usecs; + q = &q->qh->qh_next; + break; + case Q_TYPE_FSTN: + /* for "save place" FSTNs, count the relevant INTR + * bandwidth from the previous frame + */ + if (q->fstn->hw_prev != EHCI_LIST_END) { + dbg ("not counting FSTN bandwidth yet ..."); + } + q = &q->fstn->fstn_next; + break; + case Q_TYPE_ITD: + /* NOTE the "one uframe per itd" policy */ + if (q->itd->hw_transaction [uframe] != 0) + usecs += q->itd->usecs; + q = &q->itd->itd_next; + break; +#ifdef have_split_iso + case Q_TYPE_SITD: + temp = q->sitd->hw_fullspeed_ep & + __constant_cpu_to_le32 (1 << 31); + + // FIXME: this doesn't count data bytes right... + + /* is it in the S-mask? (count SPLIT, DATA) */ + if (q->sitd->hw_uframe & cpu_to_le32 (1 << uframe)) { + if (temp) + usecs += HS_USECS (188); + else + usecs += HS_USECS (1); + } + + /* ... C-mask? (count CSPLIT, DATA) */ + if (q->sitd->hw_uframe & + cpu_to_le32 (1 << (8 + uframe))) { + if (temp) + usecs += HS_USECS (0); + else + usecs += HS_USECS (188); + } + q = &q->sitd->sitd_next; + break; +#endif /* have_split_iso */ + default: + BUG (); + } + } +#ifdef DEBUG + if (usecs > 100) + err ("overallocated uframe %d, periodic is %d usecs", + frame * 8 + uframe, usecs); +#endif + return usecs; +} + +/*-------------------------------------------------------------------------*/ + +static int enable_periodic (struct ehci_hcd *ehci) +{ + u32 cmd; + int status; + + /* did clearing PSE did take effect yet? + * takes effect only at frame boundaries... + */ + status = handshake (&ehci->regs->status, STS_PSS, 0, 9 * 125); + if (status != 0) { + ehci->hcd.state = USB_STATE_HALT; + return status; + } + + cmd = readl (&ehci->regs->command) | CMD_PSE; + writel (cmd, &ehci->regs->command); + /* posted write ... PSS happens later */ + ehci->hcd.state = USB_STATE_RUNNING; + + /* make sure ehci_work scans these */ + ehci->next_uframe = readl (&ehci->regs->frame_index) + % (ehci->periodic_size << 3); + return 0; +} + +static int disable_periodic (struct ehci_hcd *ehci) +{ + u32 cmd; + int status; + + /* did setting PSE not take effect yet? + * takes effect only at frame boundaries... + */ + status = handshake (&ehci->regs->status, STS_PSS, STS_PSS, 9 * 125); + if (status != 0) { + ehci->hcd.state = USB_STATE_HALT; + return status; + } + + cmd = readl (&ehci->regs->command) & ~CMD_PSE; + writel (cmd, &ehci->regs->command); + /* posted write ... */ + + ehci->next_uframe = -1; + return 0; +} + +/*-------------------------------------------------------------------------*/ + +// FIXME microframe periods not yet handled + +static void intr_deschedule ( + struct ehci_hcd *ehci, + struct ehci_qh *qh, + int wait +) { + int status; + unsigned frame = qh->start; + + do { + periodic_unlink (ehci, frame, qh); + qh_put (ehci, qh); + frame += qh->period; + } while (frame < ehci->periodic_size); + + qh->qh_state = QH_STATE_UNLINK; + qh->qh_next.ptr = 0; + ehci->periodic_sched--; + + /* maybe turn off periodic schedule */ + if (!ehci->periodic_sched) + status = disable_periodic (ehci); + else { + status = 0; + vdbg ("periodic schedule still enabled"); + } + + /* + * If the hc may be looking at this qh, then delay a uframe + * (yeech!) to be sure it's done. + * No other threads may be mucking with this qh. + */ + if (((ehci_get_frame (&ehci->hcd) - frame) % qh->period) == 0) { + if (wait) { + udelay (125); + qh->hw_next = EHCI_LIST_END; + } else { + /* we may not be IDLE yet, but if the qh is empty + * the race is very short. then if qh also isn't + * rescheduled soon, it won't matter. otherwise... + */ + vdbg ("intr_deschedule..."); + } + } else + qh->hw_next = EHCI_LIST_END; + + qh->qh_state = QH_STATE_IDLE; + + /* update per-qh bandwidth utilization (for usbfs) */ + hcd_to_bus (&ehci->hcd)->bandwidth_allocated -= + (qh->usecs + qh->c_usecs) / qh->period; + + dbg ("descheduled qh %p, period = %d frame = %d count = %d, urbs = %d", + qh, qh->period, frame, + atomic_read (&qh->refcount), ehci->periodic_sched); +} + +static int check_period ( + struct ehci_hcd *ehci, + unsigned frame, + unsigned uframe, + unsigned period, + unsigned usecs +) { + /* complete split running into next frame? + * given FSTN support, we could sometimes check... + */ + if (uframe >= 8) + return 0; + + /* + * 80% periodic == 100 usec/uframe available + * convert "usecs we need" to "max already claimed" + */ + usecs = 100 - usecs; + + do { + int claimed; + +// FIXME delete when intr_submit handles non-empty queues +// this gives us a one intr/frame limit (vs N/uframe) +// ... and also lets us avoid tracking split transactions +// that might collide at a given TT/hub. + if (ehci->pshadow [frame].ptr) + return 0; + + claimed = periodic_usecs (ehci, frame, uframe); + if (claimed > usecs) + return 0; + +// FIXME update to handle sub-frame periods + } while ((frame += period) < ehci->periodic_size); + + // success! + return 1; +} + +static int check_intr_schedule ( + struct ehci_hcd *ehci, + unsigned frame, + unsigned uframe, + const struct ehci_qh *qh, + u32 *c_maskp +) +{ + int retval = -ENOSPC; + + if (!check_period (ehci, frame, uframe, qh->period, qh->usecs)) + goto done; + if (!qh->c_usecs) { + retval = 0; + *c_maskp = cpu_to_le32 (0); + goto done; + } + + /* This is a split transaction; check the bandwidth available for + * the completion too. Check both worst and best case gaps: worst + * case is SPLIT near uframe end, and CSPLIT near start ... best is + * vice versa. Difference can be almost two uframe times, but we + * reserve unnecessary bandwidth (waste it) this way. (Actually + * even better cases exist, like immediate device NAK.) + * + * FIXME don't even bother unless we know this TT is idle in that + * range of uframes ... for now, check_period() allows only one + * interrupt transfer per frame, so needn't check "TT busy" status + * when scheduling a split (QH, SITD, or FSTN). + * + * FIXME ehci 0.96 and above can use FSTNs + */ + if (!check_period (ehci, frame, uframe + qh->gap_uf + 1, + qh->period, qh->c_usecs)) + goto done; + if (!check_period (ehci, frame, uframe + qh->gap_uf, + qh->period, qh->c_usecs)) + goto done; + + *c_maskp = cpu_to_le32 (0x03 << (8 + uframe + qh->gap_uf)); + retval = 0; +done: + return retval; +} + +static int qh_schedule (struct ehci_hcd *ehci, struct ehci_qh *qh) +{ + int status; + unsigned uframe; + u32 c_mask; + unsigned frame; /* 0..(qh->period - 1), or NO_FRAME */ + + qh->hw_next = EHCI_LIST_END; + frame = qh->start; + + /* reuse the previous schedule slots, if we can */ + if (frame < qh->period) { + uframe = ffs (le32_to_cpup (&qh->hw_info2) & 0x00ff); + status = check_intr_schedule (ehci, frame, --uframe, + qh, &c_mask); + } else { + uframe = 0; + c_mask = 0; + status = -ENOSPC; + } + + /* else scan the schedule to find a group of slots such that all + * uframes have enough periodic bandwidth available. + */ + if (status) { + frame = qh->period - 1; + do { + for (uframe = 0; uframe < 8; uframe++) { + status = check_intr_schedule (ehci, + frame, uframe, qh, + &c_mask); + if (status == 0) + break; + } + } while (status && --frame); + if (status) + goto done; + qh->start = frame; + + /* reset S-frame and (maybe) C-frame masks */ + qh->hw_info2 &= ~0xffff; + qh->hw_info2 |= cpu_to_le32 (1 << uframe) | c_mask; + } else + dbg ("reused previous qh %p schedule", qh); + + /* stuff into the periodic schedule */ + qh->qh_state = QH_STATE_LINKED; + dbg ("scheduled qh %p usecs %d/%d period %d.0 starting %d.%d (gap %d)", + qh, qh->usecs, qh->c_usecs, + qh->period, frame, uframe, qh->gap_uf); + do { + if (unlikely (ehci->pshadow [frame].ptr != 0)) { + +// FIXME -- just link toward the end, before any qh with a shorter period, +// AND accomodate it already having been linked here (after some other qh) +// AS WELL AS updating the schedule checking logic + + BUG (); + } else { + ehci->pshadow [frame].qh = qh_get (qh); + ehci->periodic [frame] = + QH_NEXT (qh->qh_dma); + } + wmb (); + frame += qh->period; + } while (frame < ehci->periodic_size); + + /* update per-qh bandwidth for usbfs */ + hcd_to_bus (&ehci->hcd)->bandwidth_allocated += + (qh->usecs + qh->c_usecs) / qh->period; + + /* maybe enable periodic schedule processing */ + if (!ehci->periodic_sched++) + status = enable_periodic (ehci); +done: + return status; +} + +static int intr_submit ( + struct ehci_hcd *ehci, + struct urb *urb, + struct list_head *qtd_list, + int mem_flags +) { + unsigned epnum; + unsigned long flags; + struct ehci_qh *qh; + struct hcd_dev *dev; + int is_input; + int status = 0; + struct list_head empty; + + /* get endpoint and transfer/schedule data */ + epnum = usb_pipeendpoint (urb->pipe); + is_input = usb_pipein (urb->pipe); + if (is_input) + epnum |= 0x10; + + spin_lock_irqsave (&ehci->lock, flags); + dev = (struct hcd_dev *)urb->dev->hcpriv; + + /* get qh and force any scheduling errors */ + INIT_LIST_HEAD (&empty); + qh = qh_append_tds (ehci, urb, &empty, epnum, &dev->ep [epnum]); + if (qh == 0) { + status = -ENOMEM; + goto done; + } + if (qh->qh_state == QH_STATE_IDLE) { + if ((status = qh_schedule (ehci, qh)) != 0) + goto done; + } + + /* then queue the urb's tds to the qh */ + qh = qh_append_tds (ehci, urb, qtd_list, epnum, &dev->ep [epnum]); + BUG_ON (qh == 0); + + /* ... update usbfs periodic stats */ + hcd_to_bus (&ehci->hcd)->bandwidth_int_reqs++; + +done: + spin_unlock_irqrestore (&ehci->lock, flags); + if (status) + qtd_list_free (ehci, urb, qtd_list); + + return status; +} + +static unsigned +intr_complete ( + struct ehci_hcd *ehci, + unsigned frame, + struct ehci_qh *qh, + struct pt_regs *regs +) { + unsigned count; + + /* nothing to report? */ + if (likely ((qh->hw_token & __constant_cpu_to_le32 (QTD_STS_ACTIVE)) + != 0)) + return 0; + if (unlikely (list_empty (&qh->qtd_list))) { + dbg ("intr qh %p no TDs?", qh); + return 0; + } + + /* handle any completions */ + count = qh_completions (ehci, qh, regs); + + if (unlikely (list_empty (&qh->qtd_list))) + intr_deschedule (ehci, qh, 0); + + return count; +} + +/*-------------------------------------------------------------------------*/ + +static void +itd_free_list (struct ehci_hcd *ehci, struct urb *urb) +{ + struct ehci_itd *first_itd = urb->hcpriv; + + while (!list_empty (&first_itd->itd_list)) { + struct ehci_itd *itd; + + itd = list_entry ( + first_itd->itd_list.next, + struct ehci_itd, itd_list); + list_del (&itd->itd_list); + pci_pool_free (ehci->itd_pool, itd, itd->itd_dma); + } + pci_pool_free (ehci->itd_pool, first_itd, first_itd->itd_dma); + urb->hcpriv = 0; +} + +static int +itd_fill ( + struct ehci_hcd *ehci, + struct ehci_itd *itd, + struct urb *urb, + unsigned index, // urb->iso_frame_desc [index] + dma_addr_t dma // mapped transfer buffer +) { + u64 temp; + u32 buf1; + unsigned i, epnum, maxp, multi; + unsigned length; + int is_input; + + itd->hw_next = EHCI_LIST_END; + itd->urb = urb; + itd->index = index; + + /* tell itd about its transfer buffer, max 2 pages */ + length = urb->iso_frame_desc [index].length; + dma += urb->iso_frame_desc [index].offset; + temp = dma & ~0x0fff; + for (i = 0; i < 2; i++) { + itd->hw_bufp [i] = cpu_to_le32 ((u32) temp); + itd->hw_bufp_hi [i] = cpu_to_le32 ((u32)(temp >> 32)); + temp += 0x1000; + } + itd->buf_dma = dma; + + /* + * this might be a "high bandwidth" highspeed endpoint, + * as encoded in the ep descriptor's maxpacket field + */ + epnum = usb_pipeendpoint (urb->pipe); + is_input = usb_pipein (urb->pipe); + if (is_input) { + maxp = urb->dev->epmaxpacketin [epnum]; + buf1 = (1 << 11); + } else { + maxp = urb->dev->epmaxpacketout [epnum]; + buf1 = 0; + } + buf1 |= (maxp & 0x03ff); + multi = 1; + multi += (maxp >> 11) & 0x03; + maxp &= 0x03ff; + maxp *= multi; + + /* transfer can't fit in any uframe? */ + if (length < 0 || maxp < length) { + dbg ("BAD iso packet: %d bytes, max %d, urb %p [%d] (of %d)", + length, maxp, urb, index, + urb->iso_frame_desc [index].length); + return -ENOSPC; + } + itd->usecs = usb_calc_bus_time (USB_SPEED_HIGH, is_input, 1, length); + + /* "plus" info in low order bits of buffer pointers */ + itd->hw_bufp [0] |= cpu_to_le32 ((epnum << 8) | urb->dev->devnum); + itd->hw_bufp [1] |= cpu_to_le32 (buf1); + itd->hw_bufp [2] |= cpu_to_le32 (multi); + + /* figure hw_transaction[] value (it's scheduled later) */ + itd->transaction = EHCI_ISOC_ACTIVE; + itd->transaction |= dma & 0x0fff; /* offset; buffer=0 */ + if ((index + 1) == urb->number_of_packets) + itd->transaction |= EHCI_ITD_IOC; /* end-of-urb irq */ + itd->transaction |= length << 16; + cpu_to_le32s (&itd->transaction); + + return 0; +} + +static int +itd_urb_transaction ( + struct ehci_hcd *ehci, + struct urb *urb, + int mem_flags +) { + int frame_index; + struct ehci_itd *first_itd, *itd; + int status; + dma_addr_t itd_dma; + + /* allocate/init ITDs */ + for (frame_index = 0, first_itd = 0; + frame_index < urb->number_of_packets; + frame_index++) { + itd = pci_pool_alloc (ehci->itd_pool, mem_flags, &itd_dma); + if (!itd) { + status = -ENOMEM; + goto fail; + } + memset (itd, 0, sizeof *itd); + itd->itd_dma = itd_dma; + + status = itd_fill (ehci, itd, urb, frame_index, + urb->transfer_dma); + if (status != 0) + goto fail; + + if (first_itd) + list_add_tail (&itd->itd_list, + &first_itd->itd_list); + else { + INIT_LIST_HEAD (&itd->itd_list); + urb->hcpriv = first_itd = itd; + } + } + urb->error_count = 0; + return 0; + +fail: + if (urb->hcpriv) + itd_free_list (ehci, urb); + return status; +} + +/*-------------------------------------------------------------------------*/ + +static inline void +itd_link (struct ehci_hcd *ehci, unsigned frame, struct ehci_itd *itd) +{ + /* always prepend ITD/SITD ... only QH tree is order-sensitive */ + itd->itd_next = ehci->pshadow [frame]; + itd->hw_next = ehci->periodic [frame]; + ehci->pshadow [frame].itd = itd; + ehci->periodic [frame] = cpu_to_le32 (itd->itd_dma) | Q_TYPE_ITD; +} + +/* + * return zero on success, else -errno + * - start holds first uframe to start scheduling into + * - max is the first uframe it's NOT (!) OK to start scheduling into + * math to be done modulo "mod" (ehci->periodic_size << 3) + */ +static int get_iso_range ( + struct ehci_hcd *ehci, + struct urb *urb, + unsigned *start, + unsigned *max, + unsigned mod +) { + struct list_head *lh; + struct hcd_dev *dev = urb->dev->hcpriv; + int last = -1; + unsigned now, span, end; + + span = urb->interval * urb->number_of_packets; + + /* first see if we know when the next transfer SHOULD happen */ + list_for_each (lh, &dev->urb_list) { + struct urb *u; + struct ehci_itd *itd; + unsigned s; + + u = list_entry (lh, struct urb, urb_list); + if (u == urb || u->pipe != urb->pipe) + continue; + if (u->interval != urb->interval) { /* must not change! */ + dbg ("urb %p interval %d ... != %p interval %d", + u, u->interval, urb, urb->interval); + return -EINVAL; + } + + /* URB for this endpoint... covers through when? */ + itd = urb->hcpriv; + s = itd->uframe + u->interval * u->number_of_packets; + if (last < 0) + last = s; + else { + /* + * So far we can only queue two ISO URBs... + * + * FIXME do interval math, figure out whether + * this URB is "before" or not ... also, handle + * the case where the URB might have completed, + * but hasn't yet been processed. + */ + dbg ("NYET: queue >2 URBs per ISO endpoint"); + return -EDOM; + } + } + + /* calculate the legal range [start,max) */ + now = readl (&ehci->regs->frame_index) + 1; /* next uframe */ + if (!ehci->periodic_sched) + now += 8; /* startup delay */ + now %= mod; + end = now + mod; + if (last < 0) { + *start = now + ehci->i_thresh + /* paranoia */ 1; + *max = end - span; + if (*max < *start + 1) + *max = *start + 1; + } else { + *start = last % mod; + *max = (last + 1) % mod; + } + + /* explicit start frame? */ + if (!(urb->transfer_flags & URB_ISO_ASAP)) { + unsigned temp; + + /* sanity check: must be in range */ + urb->start_frame %= ehci->periodic_size; + temp = urb->start_frame << 3; + if (temp < *start) + temp += mod; + if (temp > *max) + return -EDOM; + + /* use that explicit start frame */ + *start = urb->start_frame << 3; + temp += 8; + if (temp < *max) + *max = temp; + } + + // FIXME minimize wraparound to "now" ... insist max+span + // (and start+span) remains a few frames short of "end" + + *max %= ehci->periodic_size; + if ((*start + span) < end) + return 0; + return -EFBIG; +} + +static int +itd_schedule (struct ehci_hcd *ehci, struct urb *urb) +{ + unsigned start, max, i; + int status; + unsigned mod = ehci->periodic_size << 3; + + for (i = 0; i < urb->number_of_packets; i++) { + urb->iso_frame_desc [i].status = -EINPROGRESS; + urb->iso_frame_desc [i].actual_length = 0; + } + + if ((status = get_iso_range (ehci, urb, &start, &max, mod)) != 0) + return status; + + do { + unsigned uframe; + unsigned usecs; + struct ehci_itd *itd; + + /* check schedule: enough space? */ + itd = urb->hcpriv; + uframe = start; + for (i = 0, uframe = start; + i < urb->number_of_packets; + i++, uframe += urb->interval) { + uframe %= mod; + + /* can't commit more than 80% periodic == 100 usec */ + if (periodic_usecs (ehci, uframe >> 3, uframe & 0x7) + > (100 - itd->usecs)) { + itd = 0; + break; + } + itd = list_entry (itd->itd_list.next, + struct ehci_itd, itd_list); + } + if (!itd) + continue; + + /* that's where we'll schedule this! */ + itd = urb->hcpriv; + urb->start_frame = start >> 3; + vdbg ("ISO urb %p (%d packets period %d) starting %d.%d", + urb, urb->number_of_packets, urb->interval, + urb->start_frame, start & 0x7); + for (i = 0, uframe = start, usecs = 0; + i < urb->number_of_packets; + i++, uframe += urb->interval) { + uframe %= mod; + + itd->uframe = uframe; + itd->hw_transaction [uframe & 0x07] = itd->transaction; + itd_link (ehci, (uframe >> 3) % ehci->periodic_size, + itd); + wmb (); + usecs += itd->usecs; + + itd = list_entry (itd->itd_list.next, + struct ehci_itd, itd_list); + } + + /* update bandwidth utilization records (for usbfs) + * + * FIXME This claims each URB queued to an endpoint, as if + * transfers were concurrent, not sequential. So bandwidth + * typically gets double-billed ... comes from tying it to + * URBs rather than endpoints in the schedule. Luckily we + * don't use this usbfs data for serious decision making. + */ + usecs /= urb->number_of_packets; + usecs /= urb->interval; + usecs >>= 3; + if (usecs < 1) + usecs = 1; + usb_claim_bandwidth (urb->dev, urb, usecs, 1); + + /* maybe enable periodic schedule processing */ + if (!ehci->periodic_sched++) { + if ((status = enable_periodic (ehci)) != 0) { + // FIXME deschedule right away + err ("itd_schedule, enable = %d", status); + } + } + + return 0; + + } while ((start = ++start % mod) != max); + + /* no room in the schedule */ + dbg ("urb %p, CAN'T SCHEDULE", urb); + return -ENOSPC; +} + +/*-------------------------------------------------------------------------*/ + +#define ISO_ERRS (EHCI_ISOC_BUF_ERR | EHCI_ISOC_BABBLE | EHCI_ISOC_XACTERR) + +static unsigned +itd_complete ( + struct ehci_hcd *ehci, + struct ehci_itd *itd, + unsigned uframe, + struct pt_regs *regs +) { + struct urb *urb = itd->urb; + struct usb_iso_packet_descriptor *desc; + u32 t; + + /* update status for this uframe's transfers */ + desc = &urb->iso_frame_desc [itd->index]; + + t = itd->hw_transaction [uframe]; + itd->hw_transaction [uframe] = 0; + if (t & EHCI_ISOC_ACTIVE) + desc->status = -EXDEV; + else if (t & ISO_ERRS) { + urb->error_count++; + if (t & EHCI_ISOC_BUF_ERR) + desc->status = usb_pipein (urb->pipe) + ? -ENOSR /* couldn't read */ + : -ECOMM; /* couldn't write */ + else if (t & EHCI_ISOC_BABBLE) + desc->status = -EOVERFLOW; + else /* (t & EHCI_ISOC_XACTERR) */ + desc->status = -EPROTO; + + /* HC need not update length with this error */ + if (!(t & EHCI_ISOC_BABBLE)) + desc->actual_length += EHCI_ITD_LENGTH (t); + } else { + desc->status = 0; + desc->actual_length += EHCI_ITD_LENGTH (t); + } + + vdbg ("itd %p urb %p packet %d/%d trans %x status %d len %d", + itd, urb, itd->index + 1, urb->number_of_packets, + t, desc->status, desc->actual_length); + + /* handle completion now? */ + if ((itd->index + 1) != urb->number_of_packets) + return 0; + + /* + * Always give the urb back to the driver ... expect it to submit + * a new urb (or resubmit this), and to have another already queued + * when un-interrupted transfers are needed. + * + * NOTE that for now we don't accelerate ISO unlinks; they just + * happen according to the current schedule. Means a delay of + * up to about a second (max). + */ + itd_free_list (ehci, urb); + if (urb->status == -EINPROGRESS) + urb->status = 0; + + /* complete() can reenter this HCD */ + spin_unlock (&ehci->lock); + usb_hcd_giveback_urb (&ehci->hcd, urb, regs); + spin_lock (&ehci->lock); + + /* defer stopping schedule; completion can submit */ + ehci->periodic_sched--; + if (!ehci->periodic_sched) + (void) disable_periodic (ehci); + + return 1; +} + +/*-------------------------------------------------------------------------*/ + +static int itd_submit (struct ehci_hcd *ehci, struct urb *urb, int mem_flags) +{ + int status; + unsigned long flags; + + dbg ("itd_submit urb %p", urb); + + /* allocate ITDs w/o locking anything */ + status = itd_urb_transaction (ehci, urb, mem_flags); + if (status < 0) + return status; + + /* schedule ... need to lock */ + spin_lock_irqsave (&ehci->lock, flags); + status = itd_schedule (ehci, urb); + spin_unlock_irqrestore (&ehci->lock, flags); + if (status < 0) + itd_free_list (ehci, urb); + + return status; +} + +#ifdef have_split_iso + +/*-------------------------------------------------------------------------*/ + +/* + * "Split ISO TDs" ... used for USB 1.1 devices going through + * the TTs in USB 2.0 hubs. + * + * FIXME not yet implemented + */ + +#endif /* have_split_iso */ + +/*-------------------------------------------------------------------------*/ + +static void +scan_periodic (struct ehci_hcd *ehci, struct pt_regs *regs) +{ + unsigned frame, clock, now_uframe, mod; + unsigned count = 0; + + mod = ehci->periodic_size << 3; + + /* + * When running, scan from last scan point up to "now" + * else clean up by scanning everything that's left. + * Touches as few pages as possible: cache-friendly. + * Don't scan ISO entries more than once, though. + */ + frame = ehci->next_uframe >> 3; + if (HCD_IS_RUNNING (ehci->hcd.state)) + now_uframe = readl (&ehci->regs->frame_index); + else + now_uframe = (frame << 3) - 1; + now_uframe %= mod; + clock = now_uframe >> 3; + + for (;;) { + union ehci_shadow q, *q_p; + u32 type, *hw_p; + unsigned uframes; + +restart: + /* scan schedule to _before_ current frame index */ + if (frame == clock) + uframes = now_uframe & 0x07; + else + uframes = 8; + + q_p = &ehci->pshadow [frame]; + hw_p = &ehci->periodic [frame]; + q.ptr = q_p->ptr; + type = Q_NEXT_TYPE (*hw_p); + + /* scan each element in frame's queue for completions */ + while (q.ptr != 0) { + int last; + unsigned uf; + union ehci_shadow temp; + + switch (type) { + case Q_TYPE_QH: + last = (q.qh->hw_next == EHCI_LIST_END); + temp = q.qh->qh_next; + type = Q_NEXT_TYPE (q.qh->hw_next); + count += intr_complete (ehci, frame, + qh_get (q.qh), regs); + qh_put (ehci, q.qh); + q = temp; + break; + case Q_TYPE_FSTN: + last = (q.fstn->hw_next == EHCI_LIST_END); + /* for "save place" FSTNs, look at QH entries + * in the previous frame for completions. + */ + if (q.fstn->hw_prev != EHCI_LIST_END) { + dbg ("ignoring completions from FSTNs"); + } + type = Q_NEXT_TYPE (q.fstn->hw_next); + q = q.fstn->fstn_next; + break; + case Q_TYPE_ITD: + last = (q.itd->hw_next == EHCI_LIST_END); + + /* Unlink each (S)ITD we see, since the ISO + * URB model forces constant rescheduling. + * That complicates sharing uframes in ITDs, + * and means we need to skip uframes the HC + * hasn't yet processed. + */ + for (uf = 0; uf < uframes; uf++) { + if (q.itd->hw_transaction [uf] != 0) { + temp = q; + *q_p = q.itd->itd_next; + *hw_p = q.itd->hw_next; + type = Q_NEXT_TYPE (*hw_p); + + /* might free q.itd ... */ + count += itd_complete (ehci, + temp.itd, uf, regs); + break; + } + } + /* we might skip this ITD's uframe ... */ + if (uf == uframes) { + q_p = &q.itd->itd_next; + hw_p = &q.itd->hw_next; + type = Q_NEXT_TYPE (q.itd->hw_next); + } + + q = *q_p; + break; +#ifdef have_split_iso + case Q_TYPE_SITD: + last = (q.sitd->hw_next == EHCI_LIST_END); + sitd_complete (ehci, q.sitd); + type = Q_NEXT_TYPE (q.sitd->hw_next); + + // FIXME unlink SITD after split completes + q = q.sitd->sitd_next; + break; +#endif /* have_split_iso */ + default: + dbg ("corrupt type %d frame %d shadow %p", + type, frame, q.ptr); + // BUG (); + last = 1; + q.ptr = 0; + } + + /* did completion remove an interior q entry? */ + if (unlikely (q.ptr == 0 && !last)) + goto restart; + } + + /* stop when we catch up to the HC */ + + // FIXME: this assumes we won't get lapped when + // latencies climb; that should be rare, but... + // detect it, and just go all the way around. + // FLR might help detect this case, so long as latencies + // don't exceed periodic_size msec (default 1.024 sec). + + // FIXME: likewise assumes HC doesn't halt mid-scan + + if (frame == clock) { + unsigned now; + + if (!HCD_IS_RUNNING (ehci->hcd.state)) + break; + ehci->next_uframe = now_uframe; + now = readl (&ehci->regs->frame_index) % mod; + if (now_uframe == now) + break; + + /* rescan the rest of this frame, then ... */ + now_uframe = now; + clock = now_uframe >> 3; + } else + frame = (frame + 1) % ehci->periodic_size; + } +} diff -Nru a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/usb/host/ehci.h Thu Mar 6 14:23:09 2003 @@ -0,0 +1,452 @@ +/* + * Copyright (c) 2001-2002 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. + */ + +#ifndef __LINUX_EHCI_HCD_H +#define __LINUX_EHCI_HCD_H + +/* definitions used for the EHCI driver */ + +/* statistics can be kept for for tuning/monitoring */ +struct ehci_stats { + /* irq usage */ + unsigned long normal; + unsigned long error; + unsigned long reclaim; + + /* termination of urbs from core */ + unsigned long complete; + unsigned long unlink; +}; + +/* ehci_hcd->lock guards shared data against other CPUs: + * ehci_hcd: async, reclaim, periodic (and shadow), ... + * hcd_dev: ep[] + * ehci_qh: qh_next, qtd_list + * ehci_qtd: qtd_list + * + * Also, hold this lock when talking to HC registers or + * when updating hw_* fields in shared qh/qtd/... structures. + */ + +#define EHCI_MAX_ROOT_PORTS 15 /* see HCS_N_PORTS */ + +struct ehci_hcd { /* one per controller */ + spinlock_t lock; + + /* async schedule support */ + struct ehci_qh *async; + struct ehci_qh *reclaim; + int reclaim_ready : 1, + async_idle : 1; + + /* periodic schedule support */ +#define DEFAULT_I_TDPS 1024 /* some HCs can do less */ + unsigned periodic_size; + u32 *periodic; /* hw periodic table */ + dma_addr_t periodic_dma; + unsigned i_thresh; /* uframes HC might cache */ + + union ehci_shadow *pshadow; /* mirror hw periodic table */ + int next_uframe; /* scan periodic, start here */ + unsigned periodic_sched; /* periodic activity count */ + + /* per root hub port */ + unsigned long reset_done [EHCI_MAX_ROOT_PORTS]; + + /* glue to PCI and HCD framework */ + struct usb_hcd hcd; + struct ehci_caps *caps; + struct ehci_regs *regs; + u32 hcs_params; /* cached register copy */ + + /* per-HC memory pools (could be per-PCI-bus, but ...) */ + struct pci_pool *qh_pool; /* qh per active urb */ + struct pci_pool *qtd_pool; /* one or more per qh */ + struct pci_pool *itd_pool; /* itd per iso urb */ + struct pci_pool *sitd_pool; /* sitd per split iso urb */ + + struct timer_list watchdog; + unsigned stamp; + +#ifdef EHCI_STATS + struct ehci_stats stats; +# define COUNT(x) do { (x)++; } while (0) +#else +# define COUNT(x) do {} while (0) +#endif +}; + +/* unwrap an HCD pointer to get an EHCI_HCD pointer */ +#define hcd_to_ehci(hcd_ptr) container_of(hcd_ptr, struct ehci_hcd, hcd) + +/* NOTE: urb->transfer_flags expected to not use this bit !!! */ +#define EHCI_STATE_UNLINK 0x8000 /* urb being unlinked */ + +/*-------------------------------------------------------------------------*/ + +/* EHCI register interface, corresponds to EHCI Revision 0.95 specification */ + +/* Section 2.2 Host Controller Capability Registers */ +struct ehci_caps { + u8 length; /* CAPLENGTH - size of this struct */ + u8 reserved; /* offset 0x1 */ + u16 hci_version; /* HCIVERSION - offset 0x2 */ + u32 hcs_params; /* HCSPARAMS - offset 0x4 */ +#define HCS_DEBUG_PORT(p) (((p)>>20)&0xf) /* bits 23:20, debug port? */ +#define HCS_INDICATOR(p) ((p)&(1 << 16)) /* true: has port indicators */ +#define HCS_N_CC(p) (((p)>>12)&0xf) /* bits 15:12, #companion HCs */ +#define HCS_N_PCC(p) (((p)>>8)&0xf) /* bits 11:8, ports per CC */ +#define HCS_PORTROUTED(p) ((p)&(1 << 7)) /* true: port routing */ +#define HCS_PPC(p) ((p)&(1 << 4)) /* true: port power control */ +#define HCS_N_PORTS(p) (((p)>>0)&0xf) /* bits 3:0, ports on HC */ + + u32 hcc_params; /* HCCPARAMS - offset 0x8 */ +#define HCC_EXT_CAPS(p) (((p)>>8)&0xff) /* for pci extended caps */ +#define HCC_ISOC_CACHE(p) ((p)&(1 << 7)) /* true: can cache isoc frame */ +#define HCC_ISOC_THRES(p) (((p)>>4)&0x7) /* bits 6:4, uframes cached */ +#define HCC_CANPARK(p) ((p)&(1 << 2)) /* true: can park on async qh */ +#define HCC_PGM_FRAMELISTLEN(p) ((p)&(1 << 1)) /* true: periodic_size changes*/ +#define HCC_64BIT_ADDR(p) ((p)&(1)) /* true: can use 64-bit addr */ + u8 portroute [8]; /* nibbles for routing - offset 0xC */ +} __attribute__ ((packed)); + + +/* Section 2.3 Host Controller Operational Registers */ +struct ehci_regs { + + /* USBCMD: offset 0x00 */ + u32 command; +/* 23:16 is r/w intr rate, in microframes; default "8" == 1/msec */ +#define CMD_PARK (1<<11) /* enable "park" on async qh */ +#define CMD_PARK_CNT(c) (((c)>>8)&3) /* how many transfers to park for */ +#define CMD_LRESET (1<<7) /* partial reset (no ports, etc) */ +#define CMD_IAAD (1<<6) /* "doorbell" interrupt async advance */ +#define CMD_ASE (1<<5) /* async schedule enable */ +#define CMD_PSE (1<<4) /* periodic schedule enable */ +/* 3:2 is periodic frame list size */ +#define CMD_RESET (1<<1) /* reset HC not bus */ +#define CMD_RUN (1<<0) /* start/stop HC */ + + /* USBSTS: offset 0x04 */ + u32 status; +#define STS_ASS (1<<15) /* Async Schedule Status */ +#define STS_PSS (1<<14) /* Periodic Schedule Status */ +#define STS_RECL (1<<13) /* Reclamation */ +#define STS_HALT (1<<12) /* Not running (any reason) */ +/* some bits reserved */ + /* these STS_* flags are also intr_enable bits (USBINTR) */ +#define STS_IAA (1<<5) /* Interrupted on async advance */ +#define STS_FATAL (1<<4) /* such as some PCI access errors */ +#define STS_FLR (1<<3) /* frame list rolled over */ +#define STS_PCD (1<<2) /* port change detect */ +#define STS_ERR (1<<1) /* "error" completion (overflow, ...) */ +#define STS_INT (1<<0) /* "normal" completion (short, ...) */ + + /* USBINTR: offset 0x08 */ + u32 intr_enable; + + /* FRINDEX: offset 0x0C */ + u32 frame_index; /* current microframe number */ + /* CTRLDSSEGMENT: offset 0x10 */ + u32 segment; /* address bits 63:32 if needed */ + /* PERIODICLISTBASE: offset 0x14 */ + u32 frame_list; /* points to periodic list */ + /* ASYNCICLISTADDR: offset 0x18 */ + u32 async_next; /* address of next async queue head */ + + u32 reserved [9]; + + /* CONFIGFLAG: offset 0x40 */ + u32 configured_flag; +#define FLAG_CF (1<<0) /* true: we'll support "high speed" */ + + /* PORTSC: offset 0x44 */ + u32 port_status [0]; /* up to N_PORTS */ +/* 31:23 reserved */ +#define PORT_WKOC_E (1<<22) /* wake on overcurrent (enable) */ +#define PORT_WKDISC_E (1<<21) /* wake on disconnect (enable) */ +#define PORT_WKCONN_E (1<<20) /* wake on connect (enable) */ +/* 19:16 for port testing */ +/* 15:14 for using port indicator leds (if HCS_INDICATOR allows) */ +#define PORT_OWNER (1<<13) /* true: companion hc owns this port */ +#define PORT_POWER (1<<12) /* true: has power (see PPC) */ +#define PORT_USB11(x) (((x)&(3<<10))==(1<<10)) /* USB 1.1 device */ +/* 11:10 for detecting lowspeed devices (reset vs release ownership) */ +/* 9 reserved */ +#define PORT_RESET (1<<8) /* reset port */ +#define PORT_SUSPEND (1<<7) /* suspend port */ +#define PORT_RESUME (1<<6) /* resume it */ +#define PORT_OCC (1<<5) /* over current change */ +#define PORT_OC (1<<4) /* over current active */ +#define PORT_PEC (1<<3) /* port enable change */ +#define PORT_PE (1<<2) /* port enable */ +#define PORT_CSC (1<<1) /* connect status change */ +#define PORT_CONNECT (1<<0) /* device connected */ +} __attribute__ ((packed)); + + +/*-------------------------------------------------------------------------*/ + +#define QTD_NEXT(dma) cpu_to_le32((u32)dma) + +/* + * EHCI Specification 0.95 Section 3.5 + * QTD: describe data transfer components (buffer, direction, ...) + * See Fig 3-6 "Queue Element Transfer Descriptor Block Diagram". + * + * These are associated only with "QH" (Queue Head) structures, + * used with control, bulk, and interrupt transfers. + */ +struct ehci_qtd { + /* first part defined by EHCI spec */ + u32 hw_next; /* see EHCI 3.5.1 */ + u32 hw_alt_next; /* see EHCI 3.5.2 */ + u32 hw_token; /* see EHCI 3.5.3 */ +#define QTD_TOGGLE (1 << 31) /* data toggle */ +#define QTD_LENGTH(tok) (((tok)>>16) & 0x7fff) +#define QTD_IOC (1 << 15) /* interrupt on complete */ +#define QTD_CERR(tok) (((tok)>>10) & 0x3) +#define QTD_PID(tok) (((tok)>>8) & 0x3) +#define QTD_STS_ACTIVE (1 << 7) /* HC may execute this */ +#define QTD_STS_HALT (1 << 6) /* halted on error */ +#define QTD_STS_DBE (1 << 5) /* data buffer error (in HC) */ +#define QTD_STS_BABBLE (1 << 4) /* device was babbling (qtd halted) */ +#define QTD_STS_XACT (1 << 3) /* device gave illegal response */ +#define QTD_STS_MMF (1 << 2) /* incomplete split transaction */ +#define QTD_STS_STS (1 << 1) /* split transaction state */ +#define QTD_STS_PING (1 << 0) /* issue PING? */ + u32 hw_buf [5]; /* see EHCI 3.5.4 */ + u32 hw_buf_hi [5]; /* Appendix B */ + + /* the rest is HCD-private */ + dma_addr_t qtd_dma; /* qtd address */ + struct list_head qtd_list; /* sw qtd list */ + struct urb *urb; /* qtd's urb */ + size_t length; /* length of buffer */ +} __attribute__ ((aligned (32))); + +#define QTD_MASK cpu_to_le32 (~0x1f) /* mask NakCnt+T in qh->hw_alt_next */ + +/*-------------------------------------------------------------------------*/ + +/* type tag from {qh,itd,sitd,fstn}->hw_next */ +#define Q_NEXT_TYPE(dma) ((dma) & __constant_cpu_to_le32 (3 << 1)) + +/* values for that type tag */ +#define Q_TYPE_ITD __constant_cpu_to_le32 (0 << 1) +#define Q_TYPE_QH __constant_cpu_to_le32 (1 << 1) +#define Q_TYPE_SITD __constant_cpu_to_le32 (2 << 1) +#define Q_TYPE_FSTN __constant_cpu_to_le32 (3 << 1) + +/* next async queue entry, or pointer to interrupt/periodic QH */ +#define QH_NEXT(dma) (cpu_to_le32(((u32)dma)&~0x01f)|Q_TYPE_QH) + +/* for periodic/async schedules and qtd lists, mark end of list */ +#define EHCI_LIST_END __constant_cpu_to_le32(1) /* "null pointer" to hw */ + +/* + * Entries in periodic shadow table are pointers to one of four kinds + * of data structure. That's dictated by the hardware; a type tag is + * encoded in the low bits of the hardware's periodic schedule. Use + * Q_NEXT_TYPE to get the tag. + * + * For entries in the async schedule, the type tag always says "qh". + */ +union ehci_shadow { + struct ehci_qh *qh; /* Q_TYPE_QH */ + struct ehci_itd *itd; /* Q_TYPE_ITD */ + struct ehci_sitd *sitd; /* Q_TYPE_SITD */ + struct ehci_fstn *fstn; /* Q_TYPE_FSTN */ + void *ptr; +}; + +/*-------------------------------------------------------------------------*/ + +/* + * EHCI Specification 0.95 Section 3.6 + * QH: describes control/bulk/interrupt endpoints + * See Fig 3-7 "Queue Head Structure Layout". + * + * These appear in both the async and (for interrupt) periodic schedules. + */ + +struct ehci_qh { + /* first part defined by EHCI spec */ + u32 hw_next; /* see EHCI 3.6.1 */ + u32 hw_info1; /* see EHCI 3.6.2 */ +#define QH_HEAD 0x00008000 + u32 hw_info2; /* see EHCI 3.6.2 */ + u32 hw_current; /* qtd list - see EHCI 3.6.4 */ + + /* qtd overlay (hardware parts of a struct ehci_qtd) */ + u32 hw_qtd_next; + u32 hw_alt_next; + u32 hw_token; + u32 hw_buf [5]; + u32 hw_buf_hi [5]; + + /* the rest is HCD-private */ + dma_addr_t qh_dma; /* address of qh */ + union ehci_shadow qh_next; /* ptr to qh; or periodic */ + struct list_head qtd_list; /* sw qtd list */ + struct ehci_qtd *dummy; + struct ehci_qh *reclaim; /* next to reclaim */ + + atomic_t refcount; + unsigned stamp; + + u8 qh_state; +#define QH_STATE_LINKED 1 /* HC sees this */ +#define QH_STATE_UNLINK 2 /* HC may still see this */ +#define QH_STATE_IDLE 3 /* HC doesn't see this */ +#define QH_STATE_UNLINK_WAIT 4 /* LINKED and on reclaim q */ +#define QH_STATE_COMPLETING 5 /* don't touch token.HALT */ + + /* periodic schedule info */ + u8 usecs; /* intr bandwidth */ + u8 gap_uf; /* uframes split/csplit gap */ + u8 c_usecs; /* ... split completion bw */ + unsigned short period; /* polling interval */ + unsigned short start; /* where polling starts */ +#define NO_FRAME ((unsigned short)~0) /* pick new start */ + +} __attribute__ ((aligned (32))); + +/*-------------------------------------------------------------------------*/ + +/* + * EHCI Specification 0.95 Section 3.3 + * Fig 3-4 "Isochronous Transaction Descriptor (iTD)" + * + * Schedule records for high speed iso xfers + */ +struct ehci_itd { + /* first part defined by EHCI spec */ + u32 hw_next; /* see EHCI 3.3.1 */ + u32 hw_transaction [8]; /* see EHCI 3.3.2 */ +#define EHCI_ISOC_ACTIVE (1<<31) /* activate transfer this slot */ +#define EHCI_ISOC_BUF_ERR (1<<30) /* Data buffer error */ +#define EHCI_ISOC_BABBLE (1<<29) /* babble detected */ +#define EHCI_ISOC_XACTERR (1<<28) /* XactErr - transaction error */ +#define EHCI_ITD_LENGTH(tok) (((tok)>>16) & 0x7fff) +#define EHCI_ITD_IOC (1 << 15) /* interrupt on complete */ + + u32 hw_bufp [7]; /* see EHCI 3.3.3 */ + u32 hw_bufp_hi [7]; /* Appendix B */ + + /* the rest is HCD-private */ + dma_addr_t itd_dma; /* for this itd */ + union ehci_shadow itd_next; /* ptr to periodic q entry */ + + struct urb *urb; + struct list_head itd_list; /* list of urb frames' itds */ + dma_addr_t buf_dma; /* frame's buffer address */ + + /* for now, only one hw_transaction per itd */ + u32 transaction; + u16 index; /* in urb->iso_frame_desc */ + u16 uframe; /* in periodic schedule */ + u16 usecs; +} __attribute__ ((aligned (32))); + +/*-------------------------------------------------------------------------*/ + +/* + * EHCI Specification 0.95 Section 3.4 + * siTD, aka split-transaction isochronous Transfer Descriptor + * ... describe low/full speed iso xfers through TT in hubs + * see Figure 3-5 "Split-transaction Isochronous Transaction Descriptor (siTD) + */ +struct ehci_sitd { + /* first part defined by EHCI spec */ + u32 hw_next; +/* uses bit field macros above - see EHCI 0.95 Table 3-8 */ + u32 hw_fullspeed_ep; /* see EHCI table 3-9 */ + u32 hw_uframe; /* see EHCI table 3-10 */ + u32 hw_tx_results1; /* see EHCI table 3-11 */ + u32 hw_tx_results2; /* see EHCI table 3-12 */ + u32 hw_tx_results3; /* see EHCI table 3-12 */ + u32 hw_backpointer; /* see EHCI table 3-13 */ + u32 hw_buf_hi [2]; /* Appendix B */ + + /* the rest is HCD-private */ + dma_addr_t sitd_dma; + union ehci_shadow sitd_next; /* ptr to periodic q entry */ + struct urb *urb; + dma_addr_t buf_dma; /* buffer address */ + + unsigned short usecs; /* start bandwidth */ + unsigned short c_usecs; /* completion bandwidth */ +} __attribute__ ((aligned (32))); + +/*-------------------------------------------------------------------------*/ + +/* + * EHCI Specification 0.96 Section 3.7 + * Periodic Frame Span Traversal Node (FSTN) + * + * Manages split interrupt transactions (using TT) that span frame boundaries + * into uframes 0/1; see 4.12.2.2. In those uframes, a "save place" FSTN + * makes the HC jump (back) to a QH to scan for fs/ls QH completions until + * it hits a "restore" FSTN; then it returns to finish other uframe 0/1 work. + */ +struct ehci_fstn { + u32 hw_next; /* any periodic q entry */ + u32 hw_prev; /* qh or EHCI_LIST_END */ + + /* the rest is HCD-private */ + dma_addr_t fstn_dma; + union ehci_shadow fstn_next; /* ptr to periodic q entry */ +} __attribute__ ((aligned (32))); + +/*-------------------------------------------------------------------------*/ + +#include +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,32) + +#define SUBMIT_URB(urb,mem_flags) usb_submit_urb(urb) +#define STUB_DEBUG_FILES + +static inline int hcd_register_root (struct usb_hcd *hcd) +{ + return usb_new_device (hcd_to_bus (hcd)->root_hub); +} + +#else /* LINUX_VERSION_CODE */ + +// hcd_to_bus() eventually moves to hcd.h on 2.5 too +static inline struct usb_bus *hcd_to_bus (struct usb_hcd *hcd) + { return &hcd->self; } +// ... as does hcd_register_root() +static inline int hcd_register_root (struct usb_hcd *hcd) +{ + return usb_register_root_hub ( + hcd_to_bus (hcd)->root_hub, &hcd->pdev->dev); +} + +#define SUBMIT_URB(urb,mem_flags) usb_submit_urb(urb,mem_flags) + +#ifndef DEBUG +#define STUB_DEBUG_FILES +#endif /* DEBUG */ + +#endif /* LINUX_VERSION_CODE */ + +/*-------------------------------------------------------------------------*/ + +#endif /* __LINUX_EHCI_HCD_H */