Linux-2.6.12-rc2

Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.

Let it rip!
此提交包含在:
Linus Torvalds
2005-04-16 15:20:36 -07:00
當前提交 1da177e4c3
共有 17291 個檔案被更改,包括 6718755 行新增0 行删除

126
drivers/usb/host/Kconfig 一般檔案
查看文件

@@ -0,0 +1,126 @@
#
# USB Host Controller Drivers
#
comment "USB Host Controller Drivers"
depends on USB
config USB_EHCI_HCD
tristate "EHCI HCD (USB 2.0) support"
depends on USB && PCI
---help---
The Enhanced Host Controller Interface (EHCI) is standard for USB 2.0
"high speed" (480 Mbit/sec, 60 Mbyte/sec) host controller hardware.
If your USB host controller supports USB 2.0, you will likely want to
configure this Host Controller Driver. At this writing, the primary
implementation of EHCI is a chip from NEC, widely available in add-on
PCI cards, but implementations are in the works from other vendors
including Intel and Philips. Motherboard support is appearing.
EHCI controllers are packaged with "companion" host controllers (OHCI
or UHCI) to handle USB 1.1 devices connected to root hub ports. Ports
will connect to EHCI if it the device is high speed, otherwise they
connect to a companion controller. If you configure EHCI, you should
probably configure the OHCI (for NEC and some other vendors) USB Host
Controller Driver or UHCI (for Via motherboards) Host Controller
Driver too.
You may want to read <file:Documentation/usb/ehci.txt>.
To compile this driver as a module, choose M here: the
module will be called ehci-hcd.
config USB_EHCI_SPLIT_ISO
bool "Full speed ISO transactions (EXPERIMENTAL)"
depends on USB_EHCI_HCD && EXPERIMENTAL
default n
---help---
This code is new and hasn't been used with many different
EHCI or USB 2.0 transaction translator implementations.
It should work for ISO-OUT transfers, like audio.
config USB_EHCI_ROOT_HUB_TT
bool "Root Hub Transaction Translators (EXPERIMENTAL)"
depends on USB_EHCI_HCD && EXPERIMENTAL
---help---
Some EHCI chips have vendor-specific extensions to integrate
transaction translators, so that no OHCI or UHCI companion
controller is needed. It's safe to say "y" even if your
controller doesn't support this feature.
This supports the EHCI implementation from TransDimension Inc.
config USB_OHCI_HCD
tristate "OHCI HCD support"
depends on USB && USB_ARCH_HAS_OHCI
select ISP1301_OMAP if MACH_OMAP_H2 || MACH_OMAP_H3
---help---
The Open Host Controller Interface (OHCI) is a standard for accessing
USB 1.1 host controller hardware. It does more in hardware than Intel's
UHCI specification. If your USB host controller follows the OHCI spec,
say Y. On most non-x86 systems, and on x86 hardware that's not using a
USB controller from Intel or VIA, this is appropriate. If your host
controller doesn't use PCI, this is probably appropriate. For a PCI
based system where you're not sure, the "lspci -v" entry will list the
right "prog-if" for your USB controller(s): EHCI, OHCI, or UHCI.
To compile this driver as a module, choose M here: the
module will be called ohci-hcd.
config USB_OHCI_HCD_PPC_SOC
bool "OHCI support for on-chip PPC USB controller"
depends on USB_OHCI_HCD && (STB03xxx || PPC_MPC52xx)
default y
select USB_OHCI_BIG_ENDIAN
---help---
Enables support for the USB controller on the MPC52xx or
STB03xxx processor chip. If unsure, say Y.
config USB_OHCI_HCD_PCI
bool "OHCI support for PCI-bus USB controllers"
depends on USB_OHCI_HCD && PCI && (STB03xxx || PPC_MPC52xx)
default y
select USB_OHCI_LITTLE_ENDIAN
---help---
Enables support for PCI-bus plug-in USB controller cards.
If unsure, say Y.
config USB_OHCI_BIG_ENDIAN
bool
depends on USB_OHCI_HCD
default n
config USB_OHCI_LITTLE_ENDIAN
bool
depends on USB_OHCI_HCD
default n if STB03xxx || PPC_MPC52xx
default y
config USB_UHCI_HCD
tristate "UHCI HCD (most Intel and VIA) support"
depends on USB && PCI
---help---
The Universal Host Controller Interface is a standard by Intel for
accessing the USB hardware in the PC (which is also called the USB
host controller). If your USB host controller conforms to this
standard, you may want to say Y, but see below. All recent boards
with Intel PCI chipsets (like intel 430TX, 440FX, 440LX, 440BX,
i810, i820) conform to this standard. Also all VIA PCI chipsets
(like VIA VP2, VP3, MVP3, Apollo Pro, Apollo Pro II or Apollo Pro
133). If unsure, say Y.
To compile this driver as a module, choose M here: the
module will be called uhci-hcd.
config USB_SL811_HCD
tristate "SL811HS HCD support"
depends on USB
default N
help
The SL811HS is a single-port USB controller that supports either
host side or peripheral side roles. Enable this option if your
board has this chip, and you want to use it as a host controller.
If unsure, say N.
To compile this driver as a module, choose M here: the
module will be called sl811-hcd.

10
drivers/usb/host/Makefile 一般檔案
查看文件

@@ -0,0 +1,10 @@
#
# Makefile for USB Host Controller Driver
# framework and drivers
#
obj-$(CONFIG_USB_EHCI_HCD) += ehci-hcd.o
obj-$(CONFIG_USB_OHCI_HCD) += ohci-hcd.o
obj-$(CONFIG_USB_UHCI_HCD) += uhci-hcd.o
obj-$(CONFIG_USB_SL811_HCD) += sl811-hcd.o
obj-$(CONFIG_ETRAX_ARCH_V10) += hc_crisv10.o

755
drivers/usb/host/ehci-dbg.c 一般檔案
查看文件

@@ -0,0 +1,755 @@
/*
* 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 */
#define ehci_dbg(ehci, fmt, args...) \
dev_dbg (ehci_to_hcd(ehci)->self.controller , fmt , ## args )
#define ehci_err(ehci, fmt, args...) \
dev_err (ehci_to_hcd(ehci)->self.controller , fmt , ## args )
#define ehci_info(ehci, fmt, args...) \
dev_info (ehci_to_hcd(ehci)->self.controller , fmt , ## args )
#define ehci_warn(ehci, fmt, args...) \
dev_warn (ehci_to_hcd(ehci)->self.controller , fmt , ## 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++) {
// FIXME MIPS won't readb() ...
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_qtd (const char *label, struct ehci_hcd *ehci, struct ehci_qtd *qtd)
{
ehci_dbg (ehci, "%s td %p n%08x %08x t%08x p0=%08x\n", label, qtd,
le32_to_cpup (&qtd->hw_next),
le32_to_cpup (&qtd->hw_alt_next),
le32_to_cpup (&qtd->hw_token),
le32_to_cpup (&qtd->hw_buf [0]));
if (qtd->hw_buf [1])
ehci_dbg (ehci, " p1=%08x p2=%08x p3=%08x p4=%08x\n",
le32_to_cpup (&qtd->hw_buf [1]),
le32_to_cpup (&qtd->hw_buf [2]),
le32_to_cpup (&qtd->hw_buf [3]),
le32_to_cpup (&qtd->hw_buf [4]));
}
static void __attribute__((__unused__))
dbg_qh (const char *label, struct ehci_hcd *ehci, struct ehci_qh *qh)
{
ehci_dbg (ehci, "%s qh %p n%08x info %x %x qtd %x\n", label,
qh, qh->hw_next, qh->hw_info1, qh->hw_info2,
qh->hw_current);
dbg_qtd ("overlay", ehci, (struct ehci_qtd *) &qh->hw_qtd_next);
}
static void __attribute__((__unused__))
dbg_itd (const char *label, struct ehci_hcd *ehci, struct ehci_itd *itd)
{
ehci_dbg (ehci, "%s [%d] itd %p, next %08x, urb %p\n",
label, itd->frame, itd, le32_to_cpu(itd->hw_next), itd->urb);
ehci_dbg (ehci,
" trans: %08x %08x %08x %08x %08x %08x %08x %08x\n",
le32_to_cpu(itd->hw_transaction[0]),
le32_to_cpu(itd->hw_transaction[1]),
le32_to_cpu(itd->hw_transaction[2]),
le32_to_cpu(itd->hw_transaction[3]),
le32_to_cpu(itd->hw_transaction[4]),
le32_to_cpu(itd->hw_transaction[5]),
le32_to_cpu(itd->hw_transaction[6]),
le32_to_cpu(itd->hw_transaction[7]));
ehci_dbg (ehci,
" buf: %08x %08x %08x %08x %08x %08x %08x\n",
le32_to_cpu(itd->hw_bufp[0]),
le32_to_cpu(itd->hw_bufp[1]),
le32_to_cpu(itd->hw_bufp[2]),
le32_to_cpu(itd->hw_bufp[3]),
le32_to_cpu(itd->hw_bufp[4]),
le32_to_cpu(itd->hw_bufp[5]),
le32_to_cpu(itd->hw_bufp[6]));
ehci_dbg (ehci, " index: %d %d %d %d %d %d %d %d\n",
itd->index[0], itd->index[1], itd->index[2],
itd->index[3], itd->index[4], itd->index[5],
itd->index[6], itd->index[7]);
}
static void __attribute__((__unused__))
dbg_sitd (const char *label, struct ehci_hcd *ehci, struct ehci_sitd *sitd)
{
ehci_dbg (ehci, "%s [%d] sitd %p, next %08x, urb %p\n",
label, sitd->frame, sitd, le32_to_cpu(sitd->hw_next), sitd->urb);
ehci_dbg (ehci,
" addr %08x sched %04x result %08x buf %08x %08x\n",
le32_to_cpu(sitd->hw_fullspeed_ep),
le32_to_cpu(sitd->hw_uframe),
le32_to_cpu(sitd->hw_results),
le32_to_cpu(sitd->hw_buf [0]),
le32_to_cpu(sitd->hw_buf [1]));
}
static int __attribute__((__unused__))
dbg_status_buf (char *buf, unsigned len, const char *label, u32 status)
{
return scnprintf (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, const char *label, u32 enable)
{
return scnprintf (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, const char *label, u32 command)
{
return scnprintf (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, const 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 scnprintf (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, const char *label, u32 status)
{ return 0; }
static inline int __attribute__((__unused__))
dbg_command_buf (char *buf, unsigned len, const char *label, u32 command)
{ return 0; }
static inline int __attribute__((__unused__))
dbg_intr_buf (char *buf, unsigned len, const char *label, u32 enable)
{ return 0; }
static inline int __attribute__((__unused__))
dbg_port_buf (char *buf, unsigned len, const 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 (__le32 token)
{
__u32 v = le32_to_cpu (token);
if (v & QTD_STS_ACTIVE)
return '*';
if (v & QTD_STS_HALT)
return '-';
if (!IS_SHORT_READ (v))
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;
if (qh->hw_qtd_next == EHCI_LIST_END) /* NEC does this */
mark = '@';
else
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 == EHCI_LIST_END)
mark = '.'; /* use hw_qtd_next */
/* else alt_next points to some other qtd */
}
scratch = le32_to_cpup (&qh->hw_info1);
hw_curr = (mark == '*') ? le32_to_cpup (&qh->hw_current) : 0;
temp = scnprintf (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, le32_to_cpup (&qh->hw_info2),
le32_to_cpup (&qh->hw_token), mark,
(__constant_cpu_to_le32 (QTD_TOGGLE) & qh->hw_token)
? "data1" : "data0",
(le32_to_cpup (&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 = le32_to_cpup (&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 class_device *class_dev, char *buf)
{
struct usb_bus *bus;
struct usb_hcd *hcd;
struct ehci_hcd *ehci;
unsigned long flags;
unsigned temp, size;
char *next;
struct ehci_qh *qh;
*buf = 0;
bus = to_usb_bus(class_dev);
hcd = bus->hcpriv;
ehci = hcd_to_ehci (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 = scnprintf (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 strlen (buf);
}
static CLASS_DEVICE_ATTR (async, S_IRUGO, show_async, NULL);
#define DBG_SCHED_LIMIT 64
static ssize_t
show_periodic (struct class_device *class_dev, char *buf)
{
struct usb_bus *bus;
struct usb_hcd *hcd;
struct ehci_hcd *ehci;
unsigned long flags;
union ehci_shadow p, *seen;
unsigned temp, size, seen_count;
char *next;
unsigned i;
__le32 tag;
if (!(seen = kmalloc (DBG_SCHED_LIMIT * sizeof *seen, SLAB_ATOMIC)))
return 0;
seen_count = 0;
bus = to_usb_bus(class_dev);
hcd = bus->hcpriv;
ehci = hcd_to_ehci (hcd);
next = buf;
size = PAGE_SIZE;
temp = scnprintf (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 (likely (!p.ptr))
continue;
tag = Q_NEXT_TYPE (ehci->periodic [i]);
temp = scnprintf (next, size, "%4d: ", i);
size -= temp;
next += temp;
do {
switch (tag) {
case Q_TYPE_QH:
temp = scnprintf (next, size, " qh%d-%04x/%p",
p.qh->period,
le32_to_cpup (&p.qh->hw_info2)
/* uframe masks */
& 0xffff,
p.qh);
size -= temp;
next += temp;
/* don't repeat what follows this qh */
for (temp = 0; temp < seen_count; temp++) {
if (seen [temp].ptr != p.ptr)
continue;
if (p.qh->qh_next.ptr)
temp = scnprintf (next, size,
" ...");
p.ptr = NULL;
break;
}
/* show more info the first time around */
if (temp == seen_count && p.ptr) {
u32 scratch = le32_to_cpup (
&p.qh->hw_info1);
struct ehci_qtd *qtd;
char *type = "";
/* count tds, get ep direction */
temp = 0;
list_for_each_entry (qtd,
&p.qh->qtd_list,
qtd_list) {
temp++;
switch (0x03 & (le32_to_cpu (
qtd->hw_token) >> 8)) {
case 0: type = "out"; continue;
case 1: type = "in"; continue;
}
}
temp = scnprintf (next, size,
" (%c%d ep%d%s "
"[%d/%d] q%d p%d)",
speed_char (scratch),
scratch & 0x007f,
(scratch >> 8) & 0x000f, type,
p.qh->usecs, p.qh->c_usecs,
temp,
0x7ff & (scratch >> 16));
if (seen_count < DBG_SCHED_LIMIT)
seen [seen_count++].qh = p.qh;
} else
temp = 0;
if (p.qh) {
tag = Q_NEXT_TYPE (p.qh->hw_next);
p = p.qh->qh_next;
}
break;
case Q_TYPE_FSTN:
temp = scnprintf (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 = scnprintf (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 = scnprintf (next, size,
" sitd%d-%04x/%p",
p.sitd->stream->interval,
le32_to_cpup (&p.sitd->hw_uframe)
& 0x0000ffff,
p.sitd);
tag = Q_NEXT_TYPE (p.sitd->hw_next);
p = p.sitd->sitd_next;
break;
}
size -= temp;
next += temp;
} while (p.ptr);
temp = scnprintf (next, size, "\n");
size -= temp;
next += temp;
}
spin_unlock_irqrestore (&ehci->lock, flags);
kfree (seen);
return PAGE_SIZE - size;
}
static CLASS_DEVICE_ATTR (periodic, S_IRUGO, show_periodic, NULL);
#undef DBG_SCHED_LIMIT
static ssize_t
show_registers (struct class_device *class_dev, char *buf)
{
struct usb_bus *bus;
struct usb_hcd *hcd;
struct ehci_hcd *ehci;
unsigned long flags;
unsigned temp, size, i;
char *next, scratch [80];
static char fmt [] = "%*s\n";
static char label [] = "";
bus = to_usb_bus(class_dev);
hcd = bus->hcpriv;
ehci = hcd_to_ehci (hcd);
next = buf;
size = PAGE_SIZE;
spin_lock_irqsave (&ehci->lock, flags);
if (bus->controller->power.power_state) {
size = scnprintf (next, size,
"bus %s, device %s (driver " DRIVER_VERSION ")\n"
"SUSPENDED (no register access)\n",
hcd->self.controller->bus->name,
hcd->self.controller->bus_id);
goto done;
}
/* Capability Registers */
i = HC_VERSION(readl (&ehci->caps->hc_capbase));
temp = scnprintf (next, size,
"bus %s, device %s (driver " DRIVER_VERSION ")\n"
"EHCI %x.%02x, hcd state %d\n",
hcd->self.controller->bus->name,
hcd->self.controller->bus_id,
i >> 8, i & 0x0ff, hcd->state);
size -= temp;
next += temp;
// FIXME interpret both types of params
i = readl (&ehci->caps->hcs_params);
temp = scnprintf (next, size, "structural params 0x%08x\n", i);
size -= temp;
next += temp;
i = readl (&ehci->caps->hcc_params);
temp = scnprintf (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 = scnprintf (next, size, fmt, temp, scratch);
size -= temp;
next += temp;
temp = dbg_command_buf (scratch, sizeof scratch, label,
readl (&ehci->regs->command));
temp = scnprintf (next, size, fmt, temp, scratch);
size -= temp;
next += temp;
temp = dbg_intr_buf (scratch, sizeof scratch, label,
readl (&ehci->regs->intr_enable));
temp = scnprintf (next, size, fmt, temp, scratch);
size -= temp;
next += temp;
temp = scnprintf (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 + 1,
readl (&ehci->regs->port_status [i]));
temp = scnprintf (next, size, fmt, temp, scratch);
size -= temp;
next += temp;
}
if (ehci->reclaim) {
temp = scnprintf (next, size, "reclaim qh %p%s\n",
ehci->reclaim,
ehci->reclaim_ready ? " ready" : "");
size -= temp;
next += temp;
}
#ifdef EHCI_STATS
temp = scnprintf (next, size,
"irq normal %ld err %ld reclaim %ld (lost %ld)\n",
ehci->stats.normal, ehci->stats.error, ehci->stats.reclaim,
ehci->stats.lost_iaa);
size -= temp;
next += temp;
temp = scnprintf (next, size, "complete %ld unlink %ld\n",
ehci->stats.complete, ehci->stats.unlink);
size -= temp;
next += temp;
#endif
done:
spin_unlock_irqrestore (&ehci->lock, flags);
return PAGE_SIZE - size;
}
static CLASS_DEVICE_ATTR (registers, S_IRUGO, show_registers, NULL);
static inline void create_debug_files (struct ehci_hcd *ehci)
{
struct class_device *cldev = &ehci_to_hcd(ehci)->self.class_dev;
class_device_create_file(cldev, &class_device_attr_async);
class_device_create_file(cldev, &class_device_attr_periodic);
class_device_create_file(cldev, &class_device_attr_registers);
}
static inline void remove_debug_files (struct ehci_hcd *ehci)
{
struct class_device *cldev = &ehci_to_hcd(ehci)->self.class_dev;
class_device_remove_file(cldev, &class_device_attr_async);
class_device_remove_file(cldev, &class_device_attr_periodic);
class_device_remove_file(cldev, &class_device_attr_registers);
}
#endif /* STUB_DEBUG_FILES */

1261
drivers/usb/host/ehci-hcd.c 一般檔案

檔案差異因為檔案過大而無法顯示 載入差異

553
drivers/usb/host/ehci-hub.c 一般檔案
查看文件

@@ -0,0 +1,553 @@
/*
* 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
*/
/*-------------------------------------------------------------------------*/
#ifdef CONFIG_PM
static int ehci_hub_suspend (struct usb_hcd *hcd)
{
struct ehci_hcd *ehci = hcd_to_ehci (hcd);
int port;
if (time_before (jiffies, ehci->next_statechange))
msleep(5);
port = HCS_N_PORTS (ehci->hcs_params);
spin_lock_irq (&ehci->lock);
/* stop schedules, clean any completed work */
if (HC_IS_RUNNING(hcd->state)) {
ehci_quiesce (ehci);
hcd->state = HC_STATE_QUIESCING;
}
ehci->command = readl (&ehci->regs->command);
if (ehci->reclaim)
ehci->reclaim_ready = 1;
ehci_work(ehci, NULL);
/* suspend any active/unsuspended ports, maybe allow wakeup */
while (port--) {
u32 __iomem *reg = &ehci->regs->port_status [port];
u32 t1 = readl (reg);
u32 t2 = t1;
if ((t1 & PORT_PE) && !(t1 & PORT_OWNER))
t2 |= PORT_SUSPEND;
if (hcd->remote_wakeup)
t2 |= PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E;
else
t2 &= ~(PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E);
if (t1 != t2) {
ehci_vdbg (ehci, "port %d, %08x -> %08x\n",
port + 1, t1, t2);
writel (t2, reg);
}
}
/* turn off now-idle HC */
ehci_halt (ehci);
hcd->state = HC_STATE_SUSPENDED;
ehci->next_statechange = jiffies + msecs_to_jiffies(10);
spin_unlock_irq (&ehci->lock);
return 0;
}
/* caller has locked the root hub, and should reset/reinit on error */
static int ehci_hub_resume (struct usb_hcd *hcd)
{
struct ehci_hcd *ehci = hcd_to_ehci (hcd);
u32 temp;
int i;
int intr_enable;
if (time_before (jiffies, ehci->next_statechange))
msleep(5);
spin_lock_irq (&ehci->lock);
/* re-init operational registers in case we lost power */
if (readl (&ehci->regs->intr_enable) == 0) {
/* at least some APM implementations will try to deliver
* IRQs right away, so delay them until we're ready.
*/
intr_enable = 1;
writel (0, &ehci->regs->segment);
writel (ehci->periodic_dma, &ehci->regs->frame_list);
writel ((u32)ehci->async->qh_dma, &ehci->regs->async_next);
} else
intr_enable = 0;
ehci_dbg(ehci, "resume root hub%s\n",
intr_enable ? " after power loss" : "");
/* restore CMD_RUN, framelist size, and irq threshold */
writel (ehci->command, &ehci->regs->command);
/* take ports out of suspend */
i = HCS_N_PORTS (ehci->hcs_params);
while (i--) {
temp = readl (&ehci->regs->port_status [i]);
temp &= ~(PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E);
if (temp & PORT_SUSPEND) {
ehci->reset_done [i] = jiffies + msecs_to_jiffies (20);
temp |= PORT_RESUME;
}
writel (temp, &ehci->regs->port_status [i]);
}
i = HCS_N_PORTS (ehci->hcs_params);
mdelay (20);
while (i--) {
temp = readl (&ehci->regs->port_status [i]);
if ((temp & PORT_SUSPEND) == 0)
continue;
temp &= ~PORT_RESUME;
writel (temp, &ehci->regs->port_status [i]);
ehci_vdbg (ehci, "resumed port %d\n", i + 1);
}
(void) readl (&ehci->regs->command);
/* maybe re-activate the schedule(s) */
temp = 0;
if (ehci->async->qh_next.qh)
temp |= CMD_ASE;
if (ehci->periodic_sched)
temp |= CMD_PSE;
if (temp) {
ehci->command |= temp;
writel (ehci->command, &ehci->regs->command);
}
ehci->next_statechange = jiffies + msecs_to_jiffies(5);
hcd->state = HC_STATE_RUNNING;
/* Now we can safely re-enable irqs */
if (intr_enable)
writel (INTR_MASK, &ehci->regs->intr_enable);
spin_unlock_irq (&ehci->lock);
return 0;
}
#else
#define ehci_hub_suspend NULL
#define ehci_hub_resume NULL
#endif /* CONFIG_PM */
/*-------------------------------------------------------------------------*/
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)) {
/* with integrated TT, there's nobody to hand it to! */
if (ehci_is_TDI(ehci)) {
ehci_dbg (ehci,
"Failed to enable port %d on root hub TT\n",
index+1);
return port_status;
}
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;
/* if !USB_SUSPEND, root hub timers won't get shut down ... */
if (!HC_IS_RUNNING(hcd->state))
return 0;
/* init status to no-changes */
buf [0] = 0;
ports = HCS_N_PORTS (ehci->hcs_params);
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
// PORT_STAT_C_SUSPEND?
|| ((temp & PORT_RESUME) != 0
&& time_after (jiffies,
ehci->reset_done [i]))) {
if (i < 7)
buf [0] |= 1 << (i + 1);
else
buf [1] |= 1 << (i - 7);
status = STS_PCD;
}
}
/* FIXME autosuspend idle root hubs */
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 = 10; /* ehci 1.0, 2.3.9 says 20ms max */
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 0
// re-enable when we support USB_PORT_FEAT_INDICATOR below.
if (HCS_INDICATOR (ehci->hcs_params))
temp |= 0x0080; /* per-port indicators (LEDs) */
#endif
desc->wHubCharacteristics = (__force __u16)cpu_to_le16 (temp);
}
/*-------------------------------------------------------------------------*/
#define PORT_WAKE_BITS (PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E)
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:
if (temp & PORT_RESET)
goto error;
if (temp & PORT_SUSPEND) {
if ((temp & PORT_PE) == 0)
goto error;
/* resume signaling for 20 msec */
writel ((temp & ~PORT_WAKE_BITS) | PORT_RESUME,
&ehci->regs->port_status [wIndex]);
ehci->reset_done [wIndex] = jiffies
+ msecs_to_jiffies (20);
}
break;
case USB_PORT_FEAT_C_SUSPEND:
/* we auto-clear this feature */
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;
if (temp & PORT_OCC)
status |= 1 << USB_PORT_FEAT_C_OVER_CURRENT;
/* whoever resumes must GetPortStatus to complete it!! */
if ((temp & PORT_RESUME)
&& time_after (jiffies,
ehci->reset_done [wIndex])) {
status |= 1 << USB_PORT_FEAT_C_SUSPEND;
ehci->reset_done [wIndex] = 0;
/* stop resume signaling */
temp = readl (&ehci->regs->port_status [wIndex]);
writel (temp & ~PORT_RESUME,
&ehci->regs->port_status [wIndex]);
retval = handshake (
&ehci->regs->port_status [wIndex],
PORT_RESUME, 0, 2000 /* 2msec */);
if (retval != 0) {
ehci_err (ehci, "port %d resume error %d\n",
wIndex + 1, retval);
goto error;
}
temp &= ~(PORT_SUSPEND|PORT_RESUME|(3<<10));
}
/* 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;
ehci->reset_done [wIndex] = 0;
/* force reset to complete */
writel (temp & ~PORT_RESET,
&ehci->regs->port_status [wIndex]);
retval = handshake (
&ehci->regs->port_status [wIndex],
PORT_RESET, 0, 500);
if (retval != 0) {
ehci_err (ehci, "port %d reset error %d\n",
wIndex + 1, retval);
goto error;
}
/* see what we found out */
temp = check_reset_complete (ehci, wIndex,
readl (&ehci->regs->port_status [wIndex]));
}
// 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 may be from integrated TT
status |= ehci_port_speed(ehci, temp);
}
if (temp & PORT_PE)
status |= 1 << USB_PORT_FEAT_ENABLE;
if (temp & (PORT_SUSPEND|PORT_RESUME))
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()...
*((__le32 *) 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:
if ((temp & PORT_PE) == 0
|| (temp & PORT_RESET) != 0)
goto error;
if (hcd->remote_wakeup)
temp |= PORT_WAKE_BITS;
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:
if (temp & PORT_RESUME)
goto error;
/* line status bits may report this as low speed,
* which can be fine if this root hub has a
* transaction translator built in.
*/
if ((temp & (PORT_PE|PORT_CONNECT)) == PORT_CONNECT
&& !ehci_is_TDI(ehci)
&& 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
+ msecs_to_jiffies (50);
}
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;
}

237
drivers/usb/host/ehci-mem.c 一般檔案
查看文件

@@ -0,0 +1,237 @@
/*
* 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 dma_pool or dma_alloc_coherent
* - 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 pageable.
*/
/*-------------------------------------------------------------------------*/
/* 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_token = cpu_to_le32 (QTD_STS_HALT);
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 = dma_pool_alloc (ehci->qtd_pool, flags, &dma);
if (qtd != NULL) {
ehci_qtd_init (qtd, dma);
}
return qtd;
}
static inline void ehci_qtd_free (struct ehci_hcd *ehci, struct ehci_qtd *qtd)
{
dma_pool_free (ehci->qtd_pool, qtd, qtd->qtd_dma);
}
static void qh_destroy (struct kref *kref)
{
struct ehci_qh *qh = container_of(kref, struct ehci_qh, kref);
struct ehci_hcd *ehci = qh->ehci;
/* 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);
usb_put_dev (qh->dev);
dma_pool_free (ehci->qh_pool, qh, qh->qh_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 *)
dma_pool_alloc (ehci->qh_pool, flags, &dma);
if (!qh)
return qh;
memset (qh, 0, sizeof *qh);
kref_init(&qh->kref);
qh->ehci = ehci;
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 == NULL) {
ehci_dbg (ehci, "no dummy td\n");
dma_pool_free (ehci->qh_pool, qh, qh->qh_dma);
qh = NULL;
}
return qh;
}
/* to share a qh (cpu threads, or hc) */
static inline struct ehci_qh *qh_get (struct ehci_qh *qh)
{
kref_get(&qh->kref);
return qh;
}
static inline void qh_put (struct ehci_qh *qh)
{
kref_put(&qh->kref, qh_destroy);
}
/*-------------------------------------------------------------------------*/
/* 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->async);
ehci->async = NULL;
/* DMA consistent memory and pools */
if (ehci->qtd_pool)
dma_pool_destroy (ehci->qtd_pool);
ehci->qtd_pool = NULL;
if (ehci->qh_pool) {
dma_pool_destroy (ehci->qh_pool);
ehci->qh_pool = NULL;
}
if (ehci->itd_pool)
dma_pool_destroy (ehci->itd_pool);
ehci->itd_pool = NULL;
if (ehci->sitd_pool)
dma_pool_destroy (ehci->sitd_pool);
ehci->sitd_pool = NULL;
if (ehci->periodic)
dma_free_coherent (ehci_to_hcd(ehci)->self.controller,
ehci->periodic_size * sizeof (u32),
ehci->periodic, ehci->periodic_dma);
ehci->periodic = NULL;
/* shadow periodic table */
if (ehci->pshadow)
kfree (ehci->pshadow);
ehci->pshadow = NULL;
}
/* 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 = dma_pool_create ("ehci_qtd",
ehci_to_hcd(ehci)->self.controller,
sizeof (struct ehci_qtd),
32 /* byte alignment (for hw parts) */,
4096 /* can't cross 4K */);
if (!ehci->qtd_pool) {
goto fail;
}
/* QHs for control/bulk/intr transfers */
ehci->qh_pool = dma_pool_create ("ehci_qh",
ehci_to_hcd(ehci)->self.controller,
sizeof (struct ehci_qh),
32 /* byte alignment (for hw parts) */,
4096 /* can't cross 4K */);
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 = dma_pool_create ("ehci_itd",
ehci_to_hcd(ehci)->self.controller,
sizeof (struct ehci_itd),
32 /* byte alignment (for hw parts) */,
4096 /* can't cross 4K */);
if (!ehci->itd_pool) {
goto fail;
}
/* SITD for full/low speed split ISO transfers */
ehci->sitd_pool = dma_pool_create ("ehci_sitd",
ehci_to_hcd(ehci)->self.controller,
sizeof (struct ehci_sitd),
32 /* byte alignment (for hw parts) */,
4096 /* can't cross 4K */);
if (!ehci->sitd_pool) {
goto fail;
}
/* Hardware periodic table */
ehci->periodic = (__le32 *)
dma_alloc_coherent (ehci_to_hcd(ehci)->self.controller,
ehci->periodic_size * sizeof(__le32),
&ehci->periodic_dma, 0);
if (ehci->periodic == NULL) {
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 == NULL) {
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;
}

1090
drivers/usb/host/ehci-q.c 一般檔案

檔案差異因為檔案過大而無法顯示 載入差異

1999
drivers/usb/host/ehci-sched.c 一般檔案

檔案差異因為檔案過大而無法顯示 載入差異

637
drivers/usb/host/ehci.h 一般檔案
查看文件

@@ -0,0 +1,637 @@
/*
* 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;
unsigned long lost_iaa;
/* 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), ...
* usb_host_endpoint: hcpriv
* 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;
unsigned reclaim_ready : 1;
unsigned scanning : 1;
/* periodic schedule support */
#define DEFAULT_I_TDPS 1024 /* some HCs can do less */
unsigned periodic_size;
__le32 *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];
/* per-HC memory pools (could be per-bus, but ...) */
struct dma_pool *qh_pool; /* qh per active urb */
struct dma_pool *qtd_pool; /* one or more per qh */
struct dma_pool *itd_pool; /* itd per iso urb */
struct dma_pool *sitd_pool; /* sitd per split iso urb */
struct timer_list watchdog;
struct notifier_block reboot_notifier;
unsigned long actions;
unsigned stamp;
unsigned long next_statechange;
u32 command;
unsigned is_tdi_rh_tt:1; /* TDI roothub with TT */
/* glue to PCI and HCD framework */
struct ehci_caps __iomem *caps;
struct ehci_regs __iomem *regs;
__u32 hcs_params; /* cached register copy */
/* irq statistics */
#ifdef EHCI_STATS
struct ehci_stats stats;
# define COUNT(x) do { (x)++; } while (0)
#else
# define COUNT(x) do {} while (0)
#endif
};
/* convert between an HCD pointer and the corresponding EHCI_HCD */
static inline struct ehci_hcd *hcd_to_ehci (struct usb_hcd *hcd)
{
return (struct ehci_hcd *) (hcd->hcd_priv);
}
static inline struct usb_hcd *ehci_to_hcd (struct ehci_hcd *ehci)
{
return container_of ((void *) ehci, struct usb_hcd, hcd_priv);
}
enum ehci_timer_action {
TIMER_IO_WATCHDOG,
TIMER_IAA_WATCHDOG,
TIMER_ASYNC_SHRINK,
TIMER_ASYNC_OFF,
};
static inline void
timer_action_done (struct ehci_hcd *ehci, enum ehci_timer_action action)
{
clear_bit (action, &ehci->actions);
}
static inline void
timer_action (struct ehci_hcd *ehci, enum ehci_timer_action action)
{
if (!test_and_set_bit (action, &ehci->actions)) {
unsigned long t;
switch (action) {
case TIMER_IAA_WATCHDOG:
t = EHCI_IAA_JIFFIES;
break;
case TIMER_IO_WATCHDOG:
t = EHCI_IO_JIFFIES;
break;
case TIMER_ASYNC_OFF:
t = EHCI_ASYNC_JIFFIES;
break;
// case TIMER_ASYNC_SHRINK:
default:
t = EHCI_SHRINK_JIFFIES;
break;
}
t += jiffies;
// all timings except IAA watchdog can be overridden.
// async queue SHRINK often precedes IAA. while it's ready
// to go OFF neither can matter, and afterwards the IO
// watchdog stops unless there's still periodic traffic.
if (action != TIMER_IAA_WATCHDOG
&& t > ehci->watchdog.expires
&& timer_pending (&ehci->watchdog))
return;
mod_timer (&ehci->watchdog, t);
}
}
/*-------------------------------------------------------------------------*/
/* EHCI register interface, corresponds to EHCI Revision 0.95 specification */
/* Section 2.2 Host Controller Capability Registers */
struct ehci_caps {
/* these fields are specified as 8 and 16 bit registers,
* but some hosts can't perform 8 or 16 bit PCI accesses.
*/
u32 hc_capbase;
#define HC_LENGTH(p) (((p)>>00)&0x00ff) /* bits 7:0 */
#define HC_VERSION(p) (((p)>>16)&0xffff) /* bits 31:16 */
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 */
/* ASYNCLISTADDR: 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 */
#define PORT_LED_OFF (0<<14)
#define PORT_LED_AMBER (1<<14)
#define PORT_LED_GREEN (2<<14)
#define PORT_LED_MASK (3<<14)
#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));
/* Appendix C, Debug port ... intended for use with special "debug devices"
* that can help if there's no serial console. (nonstandard enumeration.)
*/
struct ehci_dbg_port {
u32 control;
#define DBGP_OWNER (1<<30)
#define DBGP_ENABLED (1<<28)
#define DBGP_DONE (1<<16)
#define DBGP_INUSE (1<<10)
#define DBGP_ERRCODE(x) (((x)>>7)&0x0f)
# define DBGP_ERR_BAD 1
# define DBGP_ERR_SIGNAL 2
#define DBGP_ERROR (1<<6)
#define DBGP_GO (1<<5)
#define DBGP_OUT (1<<4)
#define DBGP_LEN(x) (((x)>>0)&0x0f)
u32 pids;
#define DBGP_PID_GET(x) (((x)>>16)&0xff)
#define DBGP_PID_SET(data,tok) (((data)<<8)|(tok));
u32 data03;
u32 data47;
u32 address;
#define DBGP_EPADDR(dev,ep) (((dev)<<8)|(ep));
} __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 */
__le32 hw_next; /* see EHCI 3.5.1 */
__le32 hw_alt_next; /* see EHCI 3.5.2 */
__le32 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? */
__le32 hw_buf [5]; /* see EHCI 3.5.4 */
__le32 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)));
/* mask NakCnt+T in qh->hw_alt_next */
#define QTD_MASK __constant_cpu_to_le32 (~0x1f)
#define IS_SHORT_READ(token) (QTD_LENGTH (token) != 0 && QTD_PID (token) == 1)
/*-------------------------------------------------------------------------*/
/* 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 */
u32 *hw_next; /* (all types) */
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 */
__le32 hw_next; /* see EHCI 3.6.1 */
__le32 hw_info1; /* see EHCI 3.6.2 */
#define QH_HEAD 0x00008000
__le32 hw_info2; /* see EHCI 3.6.2 */
__le32 hw_current; /* qtd list - see EHCI 3.6.4 */
/* qtd overlay (hardware parts of a struct ehci_qtd) */
__le32 hw_qtd_next;
__le32 hw_alt_next;
__le32 hw_token;
__le32 hw_buf [5];
__le32 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 */
struct ehci_hcd *ehci;
struct kref kref;
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 */
struct usb_device *dev; /* access to TT */
} __attribute__ ((aligned (32)));
/*-------------------------------------------------------------------------*/
/* description of one iso transaction (up to 3 KB data if highspeed) */
struct ehci_iso_packet {
/* These will be copied to iTD when scheduling */
u64 bufp; /* itd->hw_bufp{,_hi}[pg] |= */
__le32 transaction; /* itd->hw_transaction[i] |= */
u8 cross; /* buf crosses pages */
/* for full speed OUT splits */
u32 buf1;
};
/* temporary schedule data for packets from iso urbs (both speeds)
* each packet is one logical usb transaction to the device (not TT),
* beginning at stream->next_uframe
*/
struct ehci_iso_sched {
struct list_head td_list;
unsigned span;
struct ehci_iso_packet packet [0];
};
/*
* ehci_iso_stream - groups all (s)itds for this endpoint.
* acts like a qh would, if EHCI had them for ISO.
*/
struct ehci_iso_stream {
/* first two fields match QH, but info1 == 0 */
__le32 hw_next;
__le32 hw_info1;
u32 refcount;
u8 bEndpointAddress;
u8 highspeed;
u16 depth; /* depth in uframes */
struct list_head td_list; /* queued itds/sitds */
struct list_head free_list; /* list of unused itds/sitds */
struct usb_device *udev;
struct usb_host_endpoint *ep;
/* output of (re)scheduling */
unsigned long start; /* jiffies */
unsigned long rescheduled;
int next_uframe;
__le32 splits;
/* the rest is derived from the endpoint descriptor,
* trusting urb->interval == f(epdesc->bInterval) and
* including the extra info for hw_bufp[0..2]
*/
u8 interval;
u8 usecs, c_usecs;
u16 maxp;
u16 raw_mask;
unsigned bandwidth;
/* This is used to initialize iTD's hw_bufp fields */
__le32 buf0;
__le32 buf1;
__le32 buf2;
/* this is used to initialize sITD's tt info */
__le32 address;
};
/*-------------------------------------------------------------------------*/
/*
* 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 */
__le32 hw_next; /* see EHCI 3.3.1 */
__le32 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) & 0x0fff)
#define EHCI_ITD_IOC (1 << 15) /* interrupt on complete */
#define ITD_ACTIVE __constant_cpu_to_le32(EHCI_ISOC_ACTIVE)
__le32 hw_bufp [7]; /* see EHCI 3.3.3 */
__le32 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 ehci_iso_stream *stream; /* endpoint's queue */
struct list_head itd_list; /* list of stream's itds */
/* any/all hw_transactions here may be used by that urb */
unsigned frame; /* where scheduled */
unsigned pg;
unsigned index[8]; /* in urb->iso_frame_desc */
u8 usecs[8];
} __attribute__ ((aligned (32)));
/*-------------------------------------------------------------------------*/
/*
* EHCI Specification 0.95 Section 3.4
* siTD, aka split-transaction isochronous Transfer Descriptor
* ... describe 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 */
__le32 hw_next;
/* uses bit field macros above - see EHCI 0.95 Table 3-8 */
__le32 hw_fullspeed_ep; /* EHCI table 3-9 */
__le32 hw_uframe; /* EHCI table 3-10 */
__le32 hw_results; /* EHCI table 3-11 */
#define SITD_IOC (1 << 31) /* interrupt on completion */
#define SITD_PAGE (1 << 30) /* buffer 0/1 */
#define SITD_LENGTH(x) (0x3ff & ((x)>>16))
#define SITD_STS_ACTIVE (1 << 7) /* HC may execute this */
#define SITD_STS_ERR (1 << 6) /* error from TT */
#define SITD_STS_DBE (1 << 5) /* data buffer error (in HC) */
#define SITD_STS_BABBLE (1 << 4) /* device was babbling */
#define SITD_STS_XACT (1 << 3) /* illegal IN response */
#define SITD_STS_MMF (1 << 2) /* incomplete split transaction */
#define SITD_STS_STS (1 << 1) /* split transaction state */
#define SITD_ACTIVE __constant_cpu_to_le32(SITD_STS_ACTIVE)
__le32 hw_buf [2]; /* EHCI table 3-12 */
__le32 hw_backpointer; /* EHCI table 3-13 */
__le32 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;
struct ehci_iso_stream *stream; /* endpoint's queue */
struct list_head sitd_list; /* list of stream's sitds */
unsigned frame;
unsigned index;
} __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 {
__le32 hw_next; /* any periodic q entry */
__le32 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)));
/*-------------------------------------------------------------------------*/
#ifdef CONFIG_USB_EHCI_ROOT_HUB_TT
/*
* Some EHCI controllers have a Transaction Translator built into the
* root hub. This is a non-standard feature. Each controller will need
* to add code to the following inline functions, and call them as
* needed (mostly in root hub code).
*/
#define ehci_is_TDI(e) ((e)->is_tdi_rh_tt)
/* Returns the speed of a device attached to a port on the root hub. */
static inline unsigned int
ehci_port_speed(struct ehci_hcd *ehci, unsigned int portsc)
{
if (ehci_is_TDI(ehci)) {
switch ((portsc>>26)&3) {
case 0:
return 0;
case 1:
return (1<<USB_PORT_FEAT_LOWSPEED);
case 2:
default:
return (1<<USB_PORT_FEAT_HIGHSPEED);
}
}
return (1<<USB_PORT_FEAT_HIGHSPEED);
}
#else
#define ehci_is_TDI(e) (0)
#define ehci_port_speed(ehci, portsc) (1<<USB_PORT_FEAT_HIGHSPEED)
#endif
/*-------------------------------------------------------------------------*/
#ifndef DEBUG
#define STUB_DEBUG_FILES
#endif /* DEBUG */
/*-------------------------------------------------------------------------*/
#endif /* __LINUX_EHCI_HCD_H */

4556
drivers/usb/host/hc_crisv10.c 一般檔案

檔案差異因為檔案過大而無法顯示 載入差異

查看文件

@@ -0,0 +1,289 @@
#ifndef __LINUX_ETRAX_USB_H
#define __LINUX_ETRAX_USB_H
#include <linux/types.h>
#include <linux/list.h>
typedef struct USB_IN_Desc {
volatile __u16 sw_len;
volatile __u16 command;
volatile unsigned long next;
volatile unsigned long buf;
volatile __u16 hw_len;
volatile __u16 status;
} USB_IN_Desc_t;
typedef struct USB_SB_Desc {
volatile __u16 sw_len;
volatile __u16 command;
volatile unsigned long next;
volatile unsigned long buf;
__u32 dummy;
} USB_SB_Desc_t;
typedef struct USB_EP_Desc {
volatile __u16 hw_len;
volatile __u16 command;
volatile unsigned long sub;
volatile unsigned long next;
__u32 dummy;
} USB_EP_Desc_t;
struct virt_root_hub {
int devnum;
void *urb;
void *int_addr;
int send;
int interval;
int numports;
struct timer_list rh_int_timer;
volatile __u16 wPortChange_1;
volatile __u16 wPortChange_2;
volatile __u16 prev_wPortStatus_1;
volatile __u16 prev_wPortStatus_2;
};
struct etrax_usb_intr_traffic {
int sleeping;
int error;
struct wait_queue *wq;
};
typedef struct etrax_usb_hc {
struct usb_bus *bus;
struct virt_root_hub rh;
struct etrax_usb_intr_traffic intr;
} etrax_hc_t;
typedef enum {
STARTED,
NOT_STARTED,
UNLINK,
TRANSFER_DONE,
WAITING_FOR_DESCR_INTR
} etrax_usb_urb_state_t;
typedef struct etrax_usb_urb_priv {
/* The first_sb field is used for freeing all SB descriptors belonging
to an urb. The corresponding ep descriptor's sub pointer cannot be
used for this since the DMA advances the sub pointer as it processes
the sb list. */
USB_SB_Desc_t *first_sb;
/* The last_sb field referes to the last SB descriptor that belongs to
this urb. This is important to know so we can free the SB descriptors
that ranges between first_sb and last_sb. */
USB_SB_Desc_t *last_sb;
/* The rx_offset field is used in ctrl and bulk traffic to keep track
of the offset in the urb's transfer_buffer where incoming data should be
copied to. */
__u32 rx_offset;
/* Counter used in isochronous transfers to keep track of the
number of packets received/transmitted. */
__u32 isoc_packet_counter;
/* This field is used to pass information about the urb's current state between
the various interrupt handlers (thus marked volatile). */
volatile etrax_usb_urb_state_t urb_state;
/* Connection between the submitted urb and ETRAX epid number */
__u8 epid;
/* The rx_data_list field is used for periodic traffic, to hold
received data for later processing in the the complete_urb functions,
where the data us copied to the urb's transfer_buffer. Basically, we
use this intermediate storage because we don't know when it's safe to
reuse the transfer_buffer (FIXME?). */
struct list_head rx_data_list;
} etrax_urb_priv_t;
/* This struct is for passing data from the top half to the bottom half. */
typedef struct usb_interrupt_registers
{
etrax_hc_t *hc;
__u32 r_usb_epid_attn;
__u8 r_usb_status;
__u16 r_usb_rh_port_status_1;
__u16 r_usb_rh_port_status_2;
__u32 r_usb_irq_mask_read;
__u32 r_usb_fm_number;
struct work_struct usb_bh;
} usb_interrupt_registers_t;
/* This struct is for passing data from the isoc top half to the isoc bottom half. */
typedef struct usb_isoc_complete_data
{
struct urb *urb;
struct work_struct usb_bh;
} usb_isoc_complete_data_t;
/* This struct holds data we get from the rx descriptors for DMA channel 9
for periodic traffic (intr and isoc). */
typedef struct rx_data
{
void *data;
int length;
struct list_head list;
} rx_data_t;
typedef struct urb_entry
{
struct urb *urb;
struct list_head list;
} urb_entry_t;
/* ---------------------------------------------------------------------------
Virtual Root HUB
------------------------------------------------------------------------- */
/* destination of request */
#define RH_INTERFACE 0x01
#define RH_ENDPOINT 0x02
#define RH_OTHER 0x03
#define RH_CLASS 0x20
#define RH_VENDOR 0x40
/* Requests: bRequest << 8 | bmRequestType */
#define RH_GET_STATUS 0x0080
#define RH_CLEAR_FEATURE 0x0100
#define RH_SET_FEATURE 0x0300
#define RH_SET_ADDRESS 0x0500
#define RH_GET_DESCRIPTOR 0x0680
#define RH_SET_DESCRIPTOR 0x0700
#define RH_GET_CONFIGURATION 0x0880
#define RH_SET_CONFIGURATION 0x0900
#define RH_GET_STATE 0x0280
#define RH_GET_INTERFACE 0x0A80
#define RH_SET_INTERFACE 0x0B00
#define RH_SYNC_FRAME 0x0C80
/* Our Vendor Specific Request */
#define RH_SET_EP 0x2000
/* Hub port features */
#define RH_PORT_CONNECTION 0x00
#define RH_PORT_ENABLE 0x01
#define RH_PORT_SUSPEND 0x02
#define RH_PORT_OVER_CURRENT 0x03
#define RH_PORT_RESET 0x04
#define RH_PORT_POWER 0x08
#define RH_PORT_LOW_SPEED 0x09
#define RH_C_PORT_CONNECTION 0x10
#define RH_C_PORT_ENABLE 0x11
#define RH_C_PORT_SUSPEND 0x12
#define RH_C_PORT_OVER_CURRENT 0x13
#define RH_C_PORT_RESET 0x14
/* Hub features */
#define RH_C_HUB_LOCAL_POWER 0x00
#define RH_C_HUB_OVER_CURRENT 0x01
#define RH_DEVICE_REMOTE_WAKEUP 0x00
#define RH_ENDPOINT_STALL 0x01
/* Our Vendor Specific feature */
#define RH_REMOVE_EP 0x00
#define RH_ACK 0x01
#define RH_REQ_ERR -1
#define RH_NACK 0x00
/* Field definitions for */
#define USB_IN_command__eol__BITNR 0 /* command macros */
#define USB_IN_command__eol__WIDTH 1
#define USB_IN_command__eol__no 0
#define USB_IN_command__eol__yes 1
#define USB_IN_command__intr__BITNR 3
#define USB_IN_command__intr__WIDTH 1
#define USB_IN_command__intr__no 0
#define USB_IN_command__intr__yes 1
#define USB_IN_status__eop__BITNR 1 /* status macros. */
#define USB_IN_status__eop__WIDTH 1
#define USB_IN_status__eop__no 0
#define USB_IN_status__eop__yes 1
#define USB_IN_status__eot__BITNR 5
#define USB_IN_status__eot__WIDTH 1
#define USB_IN_status__eot__no 0
#define USB_IN_status__eot__yes 1
#define USB_IN_status__error__BITNR 6
#define USB_IN_status__error__WIDTH 1
#define USB_IN_status__error__no 0
#define USB_IN_status__error__yes 1
#define USB_IN_status__nodata__BITNR 7
#define USB_IN_status__nodata__WIDTH 1
#define USB_IN_status__nodata__no 0
#define USB_IN_status__nodata__yes 1
#define USB_IN_status__epid__BITNR 8
#define USB_IN_status__epid__WIDTH 5
#define USB_EP_command__eol__BITNR 0
#define USB_EP_command__eol__WIDTH 1
#define USB_EP_command__eol__no 0
#define USB_EP_command__eol__yes 1
#define USB_EP_command__eof__BITNR 1
#define USB_EP_command__eof__WIDTH 1
#define USB_EP_command__eof__no 0
#define USB_EP_command__eof__yes 1
#define USB_EP_command__intr__BITNR 3
#define USB_EP_command__intr__WIDTH 1
#define USB_EP_command__intr__no 0
#define USB_EP_command__intr__yes 1
#define USB_EP_command__enable__BITNR 4
#define USB_EP_command__enable__WIDTH 1
#define USB_EP_command__enable__no 0
#define USB_EP_command__enable__yes 1
#define USB_EP_command__hw_valid__BITNR 5
#define USB_EP_command__hw_valid__WIDTH 1
#define USB_EP_command__hw_valid__no 0
#define USB_EP_command__hw_valid__yes 1
#define USB_EP_command__epid__BITNR 8
#define USB_EP_command__epid__WIDTH 5
#define USB_SB_command__eol__BITNR 0 /* command macros. */
#define USB_SB_command__eol__WIDTH 1
#define USB_SB_command__eol__no 0
#define USB_SB_command__eol__yes 1
#define USB_SB_command__eot__BITNR 1
#define USB_SB_command__eot__WIDTH 1
#define USB_SB_command__eot__no 0
#define USB_SB_command__eot__yes 1
#define USB_SB_command__intr__BITNR 3
#define USB_SB_command__intr__WIDTH 1
#define USB_SB_command__intr__no 0
#define USB_SB_command__intr__yes 1
#define USB_SB_command__tt__BITNR 4
#define USB_SB_command__tt__WIDTH 2
#define USB_SB_command__tt__zout 0
#define USB_SB_command__tt__in 1
#define USB_SB_command__tt__out 2
#define USB_SB_command__tt__setup 3
#define USB_SB_command__rem__BITNR 8
#define USB_SB_command__rem__WIDTH 6
#define USB_SB_command__full__BITNR 6
#define USB_SB_command__full__WIDTH 1
#define USB_SB_command__full__no 0
#define USB_SB_command__full__yes 1
#endif

查看文件

@@ -0,0 +1,284 @@
/*
* OHCI HCD (Host Controller Driver) for USB.
*
* (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
* (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
* (C) Copyright 2002 Hewlett-Packard Company
*
* Bus Glue for AMD Alchemy Au1xxx
*
* Written by Christopher Hoover <ch@hpl.hp.com>
* Based on fragments of previous driver by Rusell King et al.
*
* Modified for LH7A404 from ohci-sa1111.c
* by Durgesh Pattamatta <pattamattad@sharpsec.com>
* Modified for AMD Alchemy Au1xxx
* by Matt Porter <mporter@kernel.crashing.org>
*
* This file is licenced under the GPL.
*/
#include <asm/mach-au1x00/au1000.h>
#define USBH_ENABLE_BE (1<<0)
#define USBH_ENABLE_C (1<<1)
#define USBH_ENABLE_E (1<<2)
#define USBH_ENABLE_CE (1<<3)
#define USBH_ENABLE_RD (1<<4)
#ifdef __LITTLE_ENDIAN
#define USBH_ENABLE_INIT (USBH_ENABLE_CE | USBH_ENABLE_E | USBH_ENABLE_C)
#elif __BIG_ENDIAN
#define USBH_ENABLE_INIT (USBH_ENABLE_CE | USBH_ENABLE_E | USBH_ENABLE_C | USBH_ENABLE_BE)
#else
#error not byte order defined
#endif
extern int usb_disabled(void);
/*-------------------------------------------------------------------------*/
static void au1xxx_start_hc(struct platform_device *dev)
{
printk(KERN_DEBUG __FILE__
": starting Au1xxx OHCI USB Controller\n");
/* enable host controller */
au_writel(USBH_ENABLE_CE, USB_HOST_CONFIG);
udelay(1000);
au_writel(USBH_ENABLE_INIT, USB_HOST_CONFIG);
udelay(1000);
/* wait for reset complete (read register twice; see au1500 errata) */
while (au_readl(USB_HOST_CONFIG),
!(au_readl(USB_HOST_CONFIG) & USBH_ENABLE_RD))
udelay(1000);
printk(KERN_DEBUG __FILE__
": Clock to USB host has been enabled \n");
}
static void au1xxx_stop_hc(struct platform_device *dev)
{
printk(KERN_DEBUG __FILE__
": stopping Au1xxx OHCI USB Controller\n");
/* Disable clock */
au_writel(readl((void *)USB_HOST_CONFIG) & ~USBH_ENABLE_CE, USB_HOST_CONFIG);
}
/*-------------------------------------------------------------------------*/
/* configure so an HC device and id are always provided */
/* always called with process context; sleeping is OK */
/**
* usb_hcd_au1xxx_probe - initialize Au1xxx-based HCDs
* Context: !in_interrupt()
*
* Allocates basic resources for this USB host controller, and
* then invokes the start() method for the HCD associated with it
* through the hotplug entry's driver_data.
*
*/
int usb_hcd_au1xxx_probe (const struct hc_driver *driver,
struct platform_device *dev)
{
int retval;
struct usb_hcd *hcd;
if(dev->resource[1].flags != IORESOURCE_IRQ) {
pr_debug ("resource[1] is not IORESOURCE_IRQ");
return -ENOMEM;
}
hcd = usb_create_hcd(driver, &dev->dev, "au1xxx");
if (!hcd)
return -ENOMEM;
hcd->rsrc_start = dev->resource[0].start;
hcd->rsrc_len = dev->resource[0].end - dev->resource[0].start + 1;
if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
pr_debug("request_mem_region failed");
retval = -EBUSY;
goto err1;
}
hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
if (!hcd->regs) {
pr_debug("ioremap failed");
retval = -ENOMEM;
goto err2;
}
au1xxx_start_hc(dev);
ohci_hcd_init(hcd_to_ohci(hcd));
retval = usb_add_hcd(hcd, dev->resource[1].start, SA_INTERRUPT);
if (retval == 0)
return retval;
au1xxx_stop_hc(dev);
iounmap(hcd->regs);
err2:
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
err1:
usb_put_hcd(hcd);
return retval;
}
/* may be called without controller electrically present */
/* may be called with controller, bus, and devices active */
/**
* usb_hcd_au1xxx_remove - shutdown processing for Au1xxx-based HCDs
* @dev: USB Host Controller being removed
* Context: !in_interrupt()
*
* Reverses the effect of usb_hcd_au1xxx_probe(), first invoking
* the HCD's stop() method. It is always called from a thread
* context, normally "rmmod", "apmd", or something similar.
*
*/
void usb_hcd_au1xxx_remove (struct usb_hcd *hcd, struct platform_device *dev)
{
usb_remove_hcd(hcd);
au1xxx_stop_hc(dev);
iounmap(hcd->regs);
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
usb_put_hcd(hcd);
}
/*-------------------------------------------------------------------------*/
static int __devinit
ohci_au1xxx_start (struct usb_hcd *hcd)
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
int ret;
ohci_dbg (ohci, "ohci_au1xxx_start, ohci:%p", ohci);
if ((ret = ohci_init (ohci)) < 0)
return ret;
if ((ret = ohci_run (ohci)) < 0) {
err ("can't start %s", hcd->self.bus_name);
ohci_stop (hcd);
return ret;
}
return 0;
}
/*-------------------------------------------------------------------------*/
static const struct hc_driver ohci_au1xxx_hc_driver = {
.description = hcd_name,
.product_desc = "Au1xxx OHCI",
.hcd_priv_size = sizeof(struct ohci_hcd),
/*
* generic hardware linkage
*/
.irq = ohci_irq,
.flags = HCD_USB11 | HCD_MEMORY,
/*
* basic lifecycle operations
*/
.start = ohci_au1xxx_start,
#ifdef CONFIG_PM
/* suspend: ohci_au1xxx_suspend, -- tbd */
/* resume: ohci_au1xxx_resume, -- tbd */
#endif /*CONFIG_PM*/
.stop = ohci_stop,
/*
* managing i/o requests and associated device resources
*/
.urb_enqueue = ohci_urb_enqueue,
.urb_dequeue = ohci_urb_dequeue,
.endpoint_disable = ohci_endpoint_disable,
/*
* scheduling support
*/
.get_frame_number = ohci_get_frame,
/*
* root hub support
*/
.hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control,
};
/*-------------------------------------------------------------------------*/
static int ohci_hcd_au1xxx_drv_probe(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
int ret;
pr_debug ("In ohci_hcd_au1xxx_drv_probe");
if (usb_disabled())
return -ENODEV;
ret = usb_hcd_au1xxx_probe(&ohci_au1xxx_hc_driver, pdev);
return ret;
}
static int ohci_hcd_au1xxx_drv_remove(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct usb_hcd *hcd = dev_get_drvdata(dev);
usb_hcd_au1xxx_remove(hcd, pdev);
return 0;
}
/*TBD*/
/*static int ohci_hcd_au1xxx_drv_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct usb_hcd *hcd = dev_get_drvdata(dev);
return 0;
}
static int ohci_hcd_au1xxx_drv_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct usb_hcd *hcd = dev_get_drvdata(dev);
return 0;
}
*/
static struct device_driver ohci_hcd_au1xxx_driver = {
.name = "au1xxx-ohci",
.bus = &platform_bus_type,
.probe = ohci_hcd_au1xxx_drv_probe,
.remove = ohci_hcd_au1xxx_drv_remove,
/*.suspend = ohci_hcd_au1xxx_drv_suspend, */
/*.resume = ohci_hcd_au1xxx_drv_resume, */
};
static int __init ohci_hcd_au1xxx_init (void)
{
pr_debug (DRIVER_INFO " (Au1xxx)");
pr_debug ("block sizes: ed %d td %d\n",
sizeof (struct ed), sizeof (struct td));
return driver_register(&ohci_hcd_au1xxx_driver);
}
static void __exit ohci_hcd_au1xxx_cleanup (void)
{
driver_unregister(&ohci_hcd_au1xxx_driver);
}
module_init (ohci_hcd_au1xxx_init);
module_exit (ohci_hcd_au1xxx_cleanup);

707
drivers/usb/host/ohci-dbg.c 一般檔案
查看文件

@@ -0,0 +1,707 @@
/*
* OHCI HCD (Host Controller Driver) for USB.
*
* (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
* (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
*
* This file is licenced under the GPL.
*/
/*-------------------------------------------------------------------------*/
#ifdef DEBUG
#define edstring(ed_type) ({ char *temp; \
switch (ed_type) { \
case PIPE_CONTROL: temp = "ctrl"; break; \
case PIPE_BULK: temp = "bulk"; break; \
case PIPE_INTERRUPT: temp = "intr"; break; \
default: temp = "isoc"; break; \
}; temp;})
#define pipestring(pipe) edstring(usb_pipetype(pipe))
/* debug| print the main components of an URB
* small: 0) header + data packets 1) just header
*/
static void __attribute__((unused))
urb_print (struct urb * urb, char * str, int small)
{
unsigned int pipe= urb->pipe;
if (!urb->dev || !urb->dev->bus) {
dbg("%s URB: no dev", str);
return;
}
#ifndef OHCI_VERBOSE_DEBUG
if (urb->status != 0)
#endif
dbg("%s %p dev=%d ep=%d%s-%s flags=%x len=%d/%d stat=%d",
str,
urb,
usb_pipedevice (pipe),
usb_pipeendpoint (pipe),
usb_pipeout (pipe)? "out" : "in",
pipestring (pipe),
urb->transfer_flags,
urb->actual_length,
urb->transfer_buffer_length,
urb->status);
#ifdef OHCI_VERBOSE_DEBUG
if (!small) {
int i, len;
if (usb_pipecontrol (pipe)) {
printk (KERN_DEBUG __FILE__ ": setup(8):");
for (i = 0; i < 8 ; i++)
printk (" %02x", ((__u8 *) urb->setup_packet) [i]);
printk ("\n");
}
if (urb->transfer_buffer_length > 0 && urb->transfer_buffer) {
printk (KERN_DEBUG __FILE__ ": data(%d/%d):",
urb->actual_length,
urb->transfer_buffer_length);
len = usb_pipeout (pipe)?
urb->transfer_buffer_length: urb->actual_length;
for (i = 0; i < 16 && i < len; i++)
printk (" %02x", ((__u8 *) urb->transfer_buffer) [i]);
printk ("%s stat:%d\n", i < len? "...": "", urb->status);
}
}
#endif
}
#define ohci_dbg_sw(ohci, next, size, format, arg...) \
do { \
if (next) { \
unsigned s_len; \
s_len = scnprintf (*next, *size, format, ## arg ); \
*size -= s_len; *next += s_len; \
} else \
ohci_dbg(ohci,format, ## arg ); \
} while (0);
static void ohci_dump_intr_mask (
struct ohci_hcd *ohci,
char *label,
u32 mask,
char **next,
unsigned *size)
{
ohci_dbg_sw (ohci, next, size, "%s 0x%08x%s%s%s%s%s%s%s%s%s\n",
label,
mask,
(mask & OHCI_INTR_MIE) ? " MIE" : "",
(mask & OHCI_INTR_OC) ? " OC" : "",
(mask & OHCI_INTR_RHSC) ? " RHSC" : "",
(mask & OHCI_INTR_FNO) ? " FNO" : "",
(mask & OHCI_INTR_UE) ? " UE" : "",
(mask & OHCI_INTR_RD) ? " RD" : "",
(mask & OHCI_INTR_SF) ? " SF" : "",
(mask & OHCI_INTR_WDH) ? " WDH" : "",
(mask & OHCI_INTR_SO) ? " SO" : ""
);
}
static void maybe_print_eds (
struct ohci_hcd *ohci,
char *label,
u32 value,
char **next,
unsigned *size)
{
if (value)
ohci_dbg_sw (ohci, next, size, "%s %08x\n", label, value);
}
static char *hcfs2string (int state)
{
switch (state) {
case OHCI_USB_RESET: return "reset";
case OHCI_USB_RESUME: return "resume";
case OHCI_USB_OPER: return "operational";
case OHCI_USB_SUSPEND: return "suspend";
}
return "?";
}
// dump control and status registers
static void
ohci_dump_status (struct ohci_hcd *controller, char **next, unsigned *size)
{
struct ohci_regs __iomem *regs = controller->regs;
u32 temp;
temp = ohci_readl (controller, &regs->revision) & 0xff;
ohci_dbg_sw (controller, next, size,
"OHCI %d.%d, %s legacy support registers\n",
0x03 & (temp >> 4), (temp & 0x0f),
(temp & 0x0100) ? "with" : "NO");
temp = ohci_readl (controller, &regs->control);
ohci_dbg_sw (controller, next, size,
"control 0x%03x%s%s%s HCFS=%s%s%s%s%s CBSR=%d\n",
temp,
(temp & OHCI_CTRL_RWE) ? " RWE" : "",
(temp & OHCI_CTRL_RWC) ? " RWC" : "",
(temp & OHCI_CTRL_IR) ? " IR" : "",
hcfs2string (temp & OHCI_CTRL_HCFS),
(temp & OHCI_CTRL_BLE) ? " BLE" : "",
(temp & OHCI_CTRL_CLE) ? " CLE" : "",
(temp & OHCI_CTRL_IE) ? " IE" : "",
(temp & OHCI_CTRL_PLE) ? " PLE" : "",
temp & OHCI_CTRL_CBSR
);
temp = ohci_readl (controller, &regs->cmdstatus);
ohci_dbg_sw (controller, next, size,
"cmdstatus 0x%05x SOC=%d%s%s%s%s\n", temp,
(temp & OHCI_SOC) >> 16,
(temp & OHCI_OCR) ? " OCR" : "",
(temp & OHCI_BLF) ? " BLF" : "",
(temp & OHCI_CLF) ? " CLF" : "",
(temp & OHCI_HCR) ? " HCR" : ""
);
ohci_dump_intr_mask (controller, "intrstatus",
ohci_readl (controller, &regs->intrstatus),
next, size);
ohci_dump_intr_mask (controller, "intrenable",
ohci_readl (controller, &regs->intrenable),
next, size);
// intrdisable always same as intrenable
maybe_print_eds (controller, "ed_periodcurrent",
ohci_readl (controller, &regs->ed_periodcurrent),
next, size);
maybe_print_eds (controller, "ed_controlhead",
ohci_readl (controller, &regs->ed_controlhead),
next, size);
maybe_print_eds (controller, "ed_controlcurrent",
ohci_readl (controller, &regs->ed_controlcurrent),
next, size);
maybe_print_eds (controller, "ed_bulkhead",
ohci_readl (controller, &regs->ed_bulkhead),
next, size);
maybe_print_eds (controller, "ed_bulkcurrent",
ohci_readl (controller, &regs->ed_bulkcurrent),
next, size);
maybe_print_eds (controller, "donehead",
ohci_readl (controller, &regs->donehead), next, size);
/* broken fminterval means traffic won't flow! */
ohci_dbg (controller, "fminterval %08x\n",
ohci_readl (controller, &regs->fminterval));
}
#define dbg_port_sw(hc,num,value,next,size) \
ohci_dbg_sw (hc, next, size, \
"roothub.portstatus [%d] " \
"0x%08x%s%s%s%s%s%s%s%s%s%s%s%s\n", \
num, temp, \
(temp & RH_PS_PRSC) ? " PRSC" : "", \
(temp & RH_PS_OCIC) ? " OCIC" : "", \
(temp & RH_PS_PSSC) ? " PSSC" : "", \
(temp & RH_PS_PESC) ? " PESC" : "", \
(temp & RH_PS_CSC) ? " CSC" : "", \
\
(temp & RH_PS_LSDA) ? " LSDA" : "", \
(temp & RH_PS_PPS) ? " PPS" : "", \
(temp & RH_PS_PRS) ? " PRS" : "", \
(temp & RH_PS_POCI) ? " POCI" : "", \
(temp & RH_PS_PSS) ? " PSS" : "", \
\
(temp & RH_PS_PES) ? " PES" : "", \
(temp & RH_PS_CCS) ? " CCS" : "" \
);
static void
ohci_dump_roothub (
struct ohci_hcd *controller,
int verbose,
char **next,
unsigned *size)
{
u32 temp, ndp, i;
temp = roothub_a (controller);
if (temp == ~(u32)0)
return;
ndp = (temp & RH_A_NDP);
if (verbose) {
ohci_dbg_sw (controller, next, size,
"roothub.a %08x POTPGT=%d%s%s%s%s%s NDP=%d\n", temp,
((temp & RH_A_POTPGT) >> 24) & 0xff,
(temp & RH_A_NOCP) ? " NOCP" : "",
(temp & RH_A_OCPM) ? " OCPM" : "",
(temp & RH_A_DT) ? " DT" : "",
(temp & RH_A_NPS) ? " NPS" : "",
(temp & RH_A_PSM) ? " PSM" : "",
ndp
);
temp = roothub_b (controller);
ohci_dbg_sw (controller, next, size,
"roothub.b %08x PPCM=%04x DR=%04x\n",
temp,
(temp & RH_B_PPCM) >> 16,
(temp & RH_B_DR)
);
temp = roothub_status (controller);
ohci_dbg_sw (controller, next, size,
"roothub.status %08x%s%s%s%s%s%s\n",
temp,
(temp & RH_HS_CRWE) ? " CRWE" : "",
(temp & RH_HS_OCIC) ? " OCIC" : "",
(temp & RH_HS_LPSC) ? " LPSC" : "",
(temp & RH_HS_DRWE) ? " DRWE" : "",
(temp & RH_HS_OCI) ? " OCI" : "",
(temp & RH_HS_LPS) ? " LPS" : ""
);
}
for (i = 0; i < ndp; i++) {
temp = roothub_portstatus (controller, i);
dbg_port_sw (controller, i, temp, next, size);
}
}
static void ohci_dump (struct ohci_hcd *controller, int verbose)
{
ohci_dbg (controller, "OHCI controller state\n");
// dumps some of the state we know about
ohci_dump_status (controller, NULL, NULL);
if (controller->hcca)
ohci_dbg (controller,
"hcca frame #%04x\n", ohci_frame_no(controller));
ohci_dump_roothub (controller, 1, NULL, NULL);
}
static const char data0 [] = "DATA0";
static const char data1 [] = "DATA1";
static void ohci_dump_td (const struct ohci_hcd *ohci, const char *label,
const struct td *td)
{
u32 tmp = hc32_to_cpup (ohci, &td->hwINFO);
ohci_dbg (ohci, "%s td %p%s; urb %p index %d; hw next td %08x\n",
label, td,
(tmp & TD_DONE) ? " (DONE)" : "",
td->urb, td->index,
hc32_to_cpup (ohci, &td->hwNextTD));
if ((tmp & TD_ISO) == 0) {
const char *toggle, *pid;
u32 cbp, be;
switch (tmp & TD_T) {
case TD_T_DATA0: toggle = data0; break;
case TD_T_DATA1: toggle = data1; break;
case TD_T_TOGGLE: toggle = "(CARRY)"; break;
default: toggle = "(?)"; break;
}
switch (tmp & TD_DP) {
case TD_DP_SETUP: pid = "SETUP"; break;
case TD_DP_IN: pid = "IN"; break;
case TD_DP_OUT: pid = "OUT"; break;
default: pid = "(bad pid)"; break;
}
ohci_dbg (ohci, " info %08x CC=%x %s DI=%d %s %s\n", tmp,
TD_CC_GET(tmp), /* EC, */ toggle,
(tmp & TD_DI) >> 21, pid,
(tmp & TD_R) ? "R" : "");
cbp = hc32_to_cpup (ohci, &td->hwCBP);
be = hc32_to_cpup (ohci, &td->hwBE);
ohci_dbg (ohci, " cbp %08x be %08x (len %d)\n", cbp, be,
cbp ? (be + 1 - cbp) : 0);
} else {
unsigned i;
ohci_dbg (ohci, " info %08x CC=%x FC=%d DI=%d SF=%04x\n", tmp,
TD_CC_GET(tmp),
(tmp >> 24) & 0x07,
(tmp & TD_DI) >> 21,
tmp & 0x0000ffff);
ohci_dbg (ohci, " bp0 %08x be %08x\n",
hc32_to_cpup (ohci, &td->hwCBP) & ~0x0fff,
hc32_to_cpup (ohci, &td->hwBE));
for (i = 0; i < MAXPSW; i++) {
u16 psw = ohci_hwPSW (ohci, td, i);
int cc = (psw >> 12) & 0x0f;
ohci_dbg (ohci, " psw [%d] = %2x, CC=%x %s=%d\n", i,
psw, cc,
(cc >= 0x0e) ? "OFFSET" : "SIZE",
psw & 0x0fff);
}
}
}
/* caller MUST own hcd spinlock if verbose is set! */
static void __attribute__((unused))
ohci_dump_ed (const struct ohci_hcd *ohci, const char *label,
const struct ed *ed, int verbose)
{
u32 tmp = hc32_to_cpu (ohci, ed->hwINFO);
char *type = "";
ohci_dbg (ohci, "%s, ed %p state 0x%x type %s; next ed %08x\n",
label,
ed, ed->state, edstring (ed->type),
hc32_to_cpup (ohci, &ed->hwNextED));
switch (tmp & (ED_IN|ED_OUT)) {
case ED_OUT: type = "-OUT"; break;
case ED_IN: type = "-IN"; break;
/* else from TDs ... control */
}
ohci_dbg (ohci,
" info %08x MAX=%d%s%s%s%s EP=%d%s DEV=%d\n", tmp,
0x03ff & (tmp >> 16),
(tmp & ED_DEQUEUE) ? " DQ" : "",
(tmp & ED_ISO) ? " ISO" : "",
(tmp & ED_SKIP) ? " SKIP" : "",
(tmp & ED_LOWSPEED) ? " LOW" : "",
0x000f & (tmp >> 7),
type,
0x007f & tmp);
tmp = hc32_to_cpup (ohci, &ed->hwHeadP);
ohci_dbg (ohci, " tds: head %08x %s%s tail %08x%s\n",
tmp,
(tmp & ED_C) ? data1 : data0,
(tmp & ED_H) ? " HALT" : "",
hc32_to_cpup (ohci, &ed->hwTailP),
verbose ? "" : " (not listing)");
if (verbose) {
struct list_head *tmp;
/* use ed->td_list because HC concurrently modifies
* hwNextTD as it accumulates ed_donelist.
*/
list_for_each (tmp, &ed->td_list) {
struct td *td;
td = list_entry (tmp, struct td, td_list);
ohci_dump_td (ohci, " ->", td);
}
}
}
#else
static inline void ohci_dump (struct ohci_hcd *controller, int verbose) {}
#undef OHCI_VERBOSE_DEBUG
#endif /* DEBUG */
/*-------------------------------------------------------------------------*/
#ifdef STUB_DEBUG_FILES
static inline void create_debug_files (struct ohci_hcd *bus) { }
static inline void remove_debug_files (struct ohci_hcd *bus) { }
#else
static ssize_t
show_list (struct ohci_hcd *ohci, char *buf, size_t count, struct ed *ed)
{
unsigned temp, size = count;
if (!ed)
return 0;
/* print first --> last */
while (ed->ed_prev)
ed = ed->ed_prev;
/* dump a snapshot of the bulk or control schedule */
while (ed) {
u32 info = hc32_to_cpu (ohci, ed->hwINFO);
u32 headp = hc32_to_cpu (ohci, ed->hwHeadP);
struct list_head *entry;
struct td *td;
temp = scnprintf (buf, size,
"ed/%p %cs dev%d ep%d%s max %d %08x%s%s %s",
ed,
(info & ED_LOWSPEED) ? 'l' : 'f',
info & 0x7f,
(info >> 7) & 0xf,
(info & ED_IN) ? "in" : "out",
0x03ff & (info >> 16),
info,
(info & ED_SKIP) ? " s" : "",
(headp & ED_H) ? " H" : "",
(headp & ED_C) ? data1 : data0);
size -= temp;
buf += temp;
list_for_each (entry, &ed->td_list) {
u32 cbp, be;
td = list_entry (entry, struct td, td_list);
info = hc32_to_cpup (ohci, &td->hwINFO);
cbp = hc32_to_cpup (ohci, &td->hwCBP);
be = hc32_to_cpup (ohci, &td->hwBE);
temp = scnprintf (buf, size,
"\n\ttd %p %s %d cc=%x urb %p (%08x)",
td,
({ char *pid;
switch (info & TD_DP) {
case TD_DP_SETUP: pid = "setup"; break;
case TD_DP_IN: pid = "in"; break;
case TD_DP_OUT: pid = "out"; break;
default: pid = "(?)"; break;
} pid;}),
cbp ? (be + 1 - cbp) : 0,
TD_CC_GET (info), td->urb, info);
size -= temp;
buf += temp;
}
temp = scnprintf (buf, size, "\n");
size -= temp;
buf += temp;
ed = ed->ed_next;
}
return count - size;
}
static ssize_t
show_async (struct class_device *class_dev, char *buf)
{
struct usb_bus *bus;
struct usb_hcd *hcd;
struct ohci_hcd *ohci;
size_t temp;
unsigned long flags;
bus = to_usb_bus(class_dev);
hcd = bus->hcpriv;
ohci = hcd_to_ohci(hcd);
/* display control and bulk lists together, for simplicity */
spin_lock_irqsave (&ohci->lock, flags);
temp = show_list (ohci, buf, PAGE_SIZE, ohci->ed_controltail);
temp += show_list (ohci, buf + temp, PAGE_SIZE - temp, ohci->ed_bulktail);
spin_unlock_irqrestore (&ohci->lock, flags);
return temp;
}
static CLASS_DEVICE_ATTR (async, S_IRUGO, show_async, NULL);
#define DBG_SCHED_LIMIT 64
static ssize_t
show_periodic (struct class_device *class_dev, char *buf)
{
struct usb_bus *bus;
struct usb_hcd *hcd;
struct ohci_hcd *ohci;
struct ed **seen, *ed;
unsigned long flags;
unsigned temp, size, seen_count;
char *next;
unsigned i;
if (!(seen = kmalloc (DBG_SCHED_LIMIT * sizeof *seen, SLAB_ATOMIC)))
return 0;
seen_count = 0;
bus = to_usb_bus(class_dev);
hcd = bus->hcpriv;
ohci = hcd_to_ohci(hcd);
next = buf;
size = PAGE_SIZE;
temp = scnprintf (next, size, "size = %d\n", NUM_INTS);
size -= temp;
next += temp;
/* dump a snapshot of the periodic schedule (and load) */
spin_lock_irqsave (&ohci->lock, flags);
for (i = 0; i < NUM_INTS; i++) {
if (!(ed = ohci->periodic [i]))
continue;
temp = scnprintf (next, size, "%2d [%3d]:", i, ohci->load [i]);
size -= temp;
next += temp;
do {
temp = scnprintf (next, size, " ed%d/%p",
ed->interval, ed);
size -= temp;
next += temp;
for (temp = 0; temp < seen_count; temp++) {
if (seen [temp] == ed)
break;
}
/* show more info the first time around */
if (temp == seen_count) {
u32 info = hc32_to_cpu (ohci, ed->hwINFO);
struct list_head *entry;
unsigned qlen = 0;
/* qlen measured here in TDs, not urbs */
list_for_each (entry, &ed->td_list)
qlen++;
temp = scnprintf (next, size,
" (%cs dev%d ep%d%s-%s qlen %u"
" max %d %08x%s%s)",
(info & ED_LOWSPEED) ? 'l' : 'f',
info & 0x7f,
(info >> 7) & 0xf,
(info & ED_IN) ? "in" : "out",
(info & ED_ISO) ? "iso" : "int",
qlen,
0x03ff & (info >> 16),
info,
(info & ED_SKIP) ? " K" : "",
(ed->hwHeadP &
cpu_to_hc32(ohci, ED_H)) ?
" H" : "");
size -= temp;
next += temp;
if (seen_count < DBG_SCHED_LIMIT)
seen [seen_count++] = ed;
ed = ed->ed_next;
} else {
/* we've seen it and what's after */
temp = 0;
ed = NULL;
}
} while (ed);
temp = scnprintf (next, size, "\n");
size -= temp;
next += temp;
}
spin_unlock_irqrestore (&ohci->lock, flags);
kfree (seen);
return PAGE_SIZE - size;
}
static CLASS_DEVICE_ATTR (periodic, S_IRUGO, show_periodic, NULL);
#undef DBG_SCHED_LIMIT
static ssize_t
show_registers (struct class_device *class_dev, char *buf)
{
struct usb_bus *bus;
struct usb_hcd *hcd;
struct ohci_hcd *ohci;
struct ohci_regs __iomem *regs;
unsigned long flags;
unsigned temp, size;
char *next;
u32 rdata;
bus = to_usb_bus(class_dev);
hcd = bus->hcpriv;
ohci = hcd_to_ohci(hcd);
regs = ohci->regs;
next = buf;
size = PAGE_SIZE;
spin_lock_irqsave (&ohci->lock, flags);
/* dump driver info, then registers in spec order */
ohci_dbg_sw (ohci, &next, &size,
"bus %s, device %s\n"
"%s\n"
"%s version " DRIVER_VERSION "\n",
hcd->self.controller->bus->name,
hcd->self.controller->bus_id,
hcd->product_desc,
hcd_name);
if (bus->controller->power.power_state) {
size -= scnprintf (next, size,
"SUSPENDED (no register access)\n");
goto done;
}
ohci_dump_status(ohci, &next, &size);
/* hcca */
if (ohci->hcca)
ohci_dbg_sw (ohci, &next, &size,
"hcca frame 0x%04x\n", ohci_frame_no(ohci));
/* other registers mostly affect frame timings */
rdata = ohci_readl (ohci, &regs->fminterval);
temp = scnprintf (next, size,
"fmintvl 0x%08x %sFSMPS=0x%04x FI=0x%04x\n",
rdata, (rdata >> 31) ? "FIT " : "",
(rdata >> 16) & 0xefff, rdata & 0xffff);
size -= temp;
next += temp;
rdata = ohci_readl (ohci, &regs->fmremaining);
temp = scnprintf (next, size, "fmremaining 0x%08x %sFR=0x%04x\n",
rdata, (rdata >> 31) ? "FRT " : "",
rdata & 0x3fff);
size -= temp;
next += temp;
rdata = ohci_readl (ohci, &regs->periodicstart);
temp = scnprintf (next, size, "periodicstart 0x%04x\n",
rdata & 0x3fff);
size -= temp;
next += temp;
rdata = ohci_readl (ohci, &regs->lsthresh);
temp = scnprintf (next, size, "lsthresh 0x%04x\n",
rdata & 0x3fff);
size -= temp;
next += temp;
/* roothub */
ohci_dump_roothub (ohci, 1, &next, &size);
done:
spin_unlock_irqrestore (&ohci->lock, flags);
return PAGE_SIZE - size;
}
static CLASS_DEVICE_ATTR (registers, S_IRUGO, show_registers, NULL);
static inline void create_debug_files (struct ohci_hcd *ohci)
{
struct class_device *cldev = &ohci_to_hcd(ohci)->self.class_dev;
class_device_create_file(cldev, &class_device_attr_async);
class_device_create_file(cldev, &class_device_attr_periodic);
class_device_create_file(cldev, &class_device_attr_registers);
ohci_dbg (ohci, "created debug files\n");
}
static inline void remove_debug_files (struct ohci_hcd *ohci)
{
struct class_device *cldev = &ohci_to_hcd(ohci)->self.class_dev;
class_device_remove_file(cldev, &class_device_attr_async);
class_device_remove_file(cldev, &class_device_attr_periodic);
class_device_remove_file(cldev, &class_device_attr_registers);
}
#endif
/*-------------------------------------------------------------------------*/

925
drivers/usb/host/ohci-hcd.c 一般檔案
查看文件

@@ -0,0 +1,925 @@
/*
* OHCI HCD (Host Controller Driver) for USB.
*
* (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
* (C) Copyright 2000-2004 David Brownell <dbrownell@users.sourceforge.net>
*
* [ Initialisation is based on Linus' ]
* [ uhci code and gregs ohci fragments ]
* [ (C) Copyright 1999 Linus Torvalds ]
* [ (C) Copyright 1999 Gregory P. Smith]
*
*
* OHCI is the main "non-Intel/VIA" standard for USB 1.1 host controller
* interfaces (though some non-x86 Intel chips use it). It supports
* smarter hardware than UHCI. A download link for the spec available
* through the http://www.usb.org website.
*
* History:
*
* 2004/03/24 LH7A404 support (Durgesh Pattamatta & Marc Singer)
* 2004/02/04 use generic dma_* functions instead of pci_* (dsaxena@plexity.net)
* 2003/02/24 show registers in sysfs (Kevin Brosius)
*
* 2002/09/03 get rid of ed hashtables, rework periodic scheduling and
* bandwidth accounting; if debugging, show schedules in driverfs
* 2002/07/19 fixes to management of ED and schedule state.
* 2002/06/09 SA-1111 support (Christopher Hoover)
* 2002/06/01 remember frame when HC won't see EDs any more; use that info
* to fix urb unlink races caused by interrupt latency assumptions;
* minor ED field and function naming updates
* 2002/01/18 package as a patch for 2.5.3; this should match the
* 2.4.17 kernel modulo some bugs being fixed.
*
* 2001/10/18 merge pmac cleanup (Benjamin Herrenschmidt) and bugfixes
* from post-2.4.5 patches.
* 2001/09/20 URB_ZERO_PACKET support; hcca_dma portability, OPTi warning
* 2001/09/07 match PCI PM changes, errnos from Linus' tree
* 2001/05/05 fork 2.4.5 version into "hcd" framework, cleanup, simplify;
* pbook pci quirks gone (please fix pbook pci sw!) (db)
*
* 2001/04/08 Identify version on module load (gb)
* 2001/03/24 td/ed hashing to remove bus_to_virt (Steve Longerbeam);
pci_map_single (db)
* 2001/03/21 td and dev/ed allocation uses new pci_pool API (db)
* 2001/03/07 hcca allocation uses pci_alloc_consistent (Steve Longerbeam)
*
* 2000/09/26 fixed races in removing the private portion of the urb
* 2000/09/07 disable bulk and control lists when unlinking the last
* endpoint descriptor in order to avoid unrecoverable errors on
* the Lucent chips. (rwc@sgi)
* 2000/08/29 use bandwidth claiming hooks (thanks Randy!), fix some
* urb unlink probs, indentation fixes
* 2000/08/11 various oops fixes mostly affecting iso and cleanup from
* device unplugs.
* 2000/06/28 use PCI hotplug framework, for better power management
* and for Cardbus support (David Brownell)
* 2000/earlier: fixes for NEC/Lucent chips; suspend/resume handling
* when the controller loses power; handle UE; cleanup; ...
*
* v5.2 1999/12/07 URB 3rd preview,
* v5.1 1999/11/30 URB 2nd preview, cpia, (usb-scsi)
* v5.0 1999/11/22 URB Technical preview, Paul Mackerras powerbook susp/resume
* i386: HUB, Keyboard, Mouse, Printer
*
* v4.3 1999/10/27 multiple HCs, bulk_request
* v4.2 1999/09/05 ISO API alpha, new dev alloc, neg Error-codes
* v4.1 1999/08/27 Randy Dunlap's - ISO API first impl.
* v4.0 1999/08/18
* v3.0 1999/06/25
* v2.1 1999/05/09 code clean up
* v2.0 1999/05/04
* v1.0 1999/04/27 initial release
*
* This file is licenced under the GPL.
*/
#include <linux/config.h>
#ifdef CONFIG_USB_DEBUG
# define DEBUG
#else
# undef DEBUG
#endif
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/pci.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ioport.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/smp_lock.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/timer.h>
#include <linux/list.h>
#include <linux/interrupt.h> /* for in_interrupt () */
#include <linux/usb.h>
#include <linux/usb_otg.h>
#include "../core/hcd.h"
#include <linux/dma-mapping.h>
#include <linux/dmapool.h> /* needed by ohci-mem.c when no PCI */
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/system.h>
#include <asm/unaligned.h>
#include <asm/byteorder.h>
#define DRIVER_VERSION "2004 Nov 08"
#define DRIVER_AUTHOR "Roman Weissgaerber, David Brownell"
#define DRIVER_DESC "USB 1.1 'Open' Host Controller (OHCI) Driver"
/*-------------------------------------------------------------------------*/
// #define OHCI_VERBOSE_DEBUG /* not always helpful */
/* For initializing controller (mask in an HCFS mode too) */
#define OHCI_CONTROL_INIT OHCI_CTRL_CBSR
#define OHCI_INTR_INIT \
(OHCI_INTR_MIE | OHCI_INTR_UE | OHCI_INTR_RD | OHCI_INTR_WDH)
#ifdef __hppa__
/* On PA-RISC, PDC can leave IR set incorrectly; ignore it there. */
#define IR_DISABLE
#endif
#ifdef CONFIG_ARCH_OMAP
/* OMAP doesn't support IR (no SMM; not needed) */
#define IR_DISABLE
#endif
/*-------------------------------------------------------------------------*/
static const char hcd_name [] = "ohci_hcd";
#include "ohci.h"
static void ohci_dump (struct ohci_hcd *ohci, int verbose);
static int ohci_init (struct ohci_hcd *ohci);
static void ohci_stop (struct usb_hcd *hcd);
#include "ohci-hub.c"
#include "ohci-dbg.c"
#include "ohci-mem.c"
#include "ohci-q.c"
/*
* On architectures with edge-triggered interrupts we must never return
* IRQ_NONE.
*/
#if defined(CONFIG_SA1111) /* ... or other edge-triggered systems */
#define IRQ_NOTMINE IRQ_HANDLED
#else
#define IRQ_NOTMINE IRQ_NONE
#endif
/* Some boards misreport power switching/overcurrent */
static int distrust_firmware = 1;
module_param (distrust_firmware, bool, 0);
MODULE_PARM_DESC (distrust_firmware,
"true to distrust firmware power/overcurrent setup");
/* Some boards leave IR set wrongly, since they fail BIOS/SMM handshakes */
static int no_handshake = 0;
module_param (no_handshake, bool, 0);
MODULE_PARM_DESC (no_handshake, "true (not default) disables BIOS handshake");
/*-------------------------------------------------------------------------*/
/*
* queue up an urb for anything except the root hub
*/
static int ohci_urb_enqueue (
struct usb_hcd *hcd,
struct usb_host_endpoint *ep,
struct urb *urb,
int mem_flags
) {
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
struct ed *ed;
urb_priv_t *urb_priv;
unsigned int pipe = urb->pipe;
int i, size = 0;
unsigned long flags;
int retval = 0;
#ifdef OHCI_VERBOSE_DEBUG
urb_print (urb, "SUB", usb_pipein (pipe));
#endif
/* every endpoint has a ed, locate and maybe (re)initialize it */
if (! (ed = ed_get (ohci, ep, urb->dev, pipe, urb->interval)))
return -ENOMEM;
/* for the private part of the URB we need the number of TDs (size) */
switch (ed->type) {
case PIPE_CONTROL:
/* td_submit_urb() doesn't yet handle these */
if (urb->transfer_buffer_length > 4096)
return -EMSGSIZE;
/* 1 TD for setup, 1 for ACK, plus ... */
size = 2;
/* FALLTHROUGH */
// case PIPE_INTERRUPT:
// case PIPE_BULK:
default:
/* one TD for every 4096 Bytes (can be upto 8K) */
size += urb->transfer_buffer_length / 4096;
/* ... and for any remaining bytes ... */
if ((urb->transfer_buffer_length % 4096) != 0)
size++;
/* ... and maybe a zero length packet to wrap it up */
if (size == 0)
size++;
else if ((urb->transfer_flags & URB_ZERO_PACKET) != 0
&& (urb->transfer_buffer_length
% usb_maxpacket (urb->dev, pipe,
usb_pipeout (pipe))) == 0)
size++;
break;
case PIPE_ISOCHRONOUS: /* number of packets from URB */
size = urb->number_of_packets;
break;
}
/* allocate the private part of the URB */
urb_priv = kmalloc (sizeof (urb_priv_t) + size * sizeof (struct td *),
mem_flags);
if (!urb_priv)
return -ENOMEM;
memset (urb_priv, 0, sizeof (urb_priv_t) + size * sizeof (struct td *));
INIT_LIST_HEAD (&urb_priv->pending);
urb_priv->length = size;
urb_priv->ed = ed;
/* allocate the TDs (deferring hash chain updates) */
for (i = 0; i < size; i++) {
urb_priv->td [i] = td_alloc (ohci, mem_flags);
if (!urb_priv->td [i]) {
urb_priv->length = i;
urb_free_priv (ohci, urb_priv);
return -ENOMEM;
}
}
spin_lock_irqsave (&ohci->lock, flags);
/* don't submit to a dead HC */
if (!HC_IS_RUNNING(hcd->state)) {
retval = -ENODEV;
goto fail;
}
/* in case of unlink-during-submit */
spin_lock (&urb->lock);
if (urb->status != -EINPROGRESS) {
spin_unlock (&urb->lock);
urb->hcpriv = urb_priv;
finish_urb (ohci, urb, NULL);
retval = 0;
goto fail;
}
/* schedule the ed if needed */
if (ed->state == ED_IDLE) {
retval = ed_schedule (ohci, ed);
if (retval < 0)
goto fail0;
if (ed->type == PIPE_ISOCHRONOUS) {
u16 frame = ohci_frame_no(ohci);
/* delay a few frames before the first TD */
frame += max_t (u16, 8, ed->interval);
frame &= ~(ed->interval - 1);
frame |= ed->branch;
urb->start_frame = frame;
/* yes, only URB_ISO_ASAP is supported, and
* urb->start_frame is never used as input.
*/
}
} else if (ed->type == PIPE_ISOCHRONOUS)
urb->start_frame = ed->last_iso + ed->interval;
/* fill the TDs and link them to the ed; and
* enable that part of the schedule, if needed
* and update count of queued periodic urbs
*/
urb->hcpriv = urb_priv;
td_submit_urb (ohci, urb);
fail0:
spin_unlock (&urb->lock);
fail:
if (retval)
urb_free_priv (ohci, urb_priv);
spin_unlock_irqrestore (&ohci->lock, flags);
return retval;
}
/*
* decouple the URB from the HC queues (TDs, urb_priv); it's
* already marked using urb->status. reporting is always done
* asynchronously, and we might be dealing with an urb that's
* partially transferred, or an ED with other urbs being unlinked.
*/
static int ohci_urb_dequeue (struct usb_hcd *hcd, struct urb *urb)
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
unsigned long flags;
#ifdef OHCI_VERBOSE_DEBUG
urb_print (urb, "UNLINK", 1);
#endif
spin_lock_irqsave (&ohci->lock, flags);
if (HC_IS_RUNNING(hcd->state)) {
urb_priv_t *urb_priv;
/* Unless an IRQ completed the unlink while it was being
* handed to us, flag it for unlink and giveback, and force
* some upcoming INTR_SF to call finish_unlinks()
*/
urb_priv = urb->hcpriv;
if (urb_priv) {
if (urb_priv->ed->state == ED_OPER)
start_ed_unlink (ohci, urb_priv->ed);
}
} else {
/*
* with HC dead, we won't respect hc queue pointers
* any more ... just clean up every urb's memory.
*/
if (urb->hcpriv)
finish_urb (ohci, urb, NULL);
}
spin_unlock_irqrestore (&ohci->lock, flags);
return 0;
}
/*-------------------------------------------------------------------------*/
/* frees config/altsetting state for endpoints,
* including ED memory, dummy TD, and bulk/intr data toggle
*/
static void
ohci_endpoint_disable (struct usb_hcd *hcd, struct usb_host_endpoint *ep)
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
unsigned long flags;
struct ed *ed = ep->hcpriv;
unsigned limit = 1000;
/* ASSERT: any requests/urbs are being unlinked */
/* ASSERT: nobody can be submitting urbs for this any more */
if (!ed)
return;
rescan:
spin_lock_irqsave (&ohci->lock, flags);
if (!HC_IS_RUNNING (hcd->state)) {
sanitize:
ed->state = ED_IDLE;
finish_unlinks (ohci, 0, NULL);
}
switch (ed->state) {
case ED_UNLINK: /* wait for hw to finish? */
/* major IRQ delivery trouble loses INTR_SF too... */
if (limit-- == 0) {
ohci_warn (ohci, "IRQ INTR_SF lossage\n");
goto sanitize;
}
spin_unlock_irqrestore (&ohci->lock, flags);
set_current_state (TASK_UNINTERRUPTIBLE);
schedule_timeout (1);
goto rescan;
case ED_IDLE: /* fully unlinked */
if (list_empty (&ed->td_list)) {
td_free (ohci, ed->dummy);
ed_free (ohci, ed);
break;
}
/* else FALL THROUGH */
default:
/* caller was supposed to have unlinked any requests;
* that's not our job. can't recover; must leak ed.
*/
ohci_err (ohci, "leak ed %p (#%02x) state %d%s\n",
ed, ep->desc.bEndpointAddress, ed->state,
list_empty (&ed->td_list) ? "" : " (has tds)");
td_free (ohci, ed->dummy);
break;
}
ep->hcpriv = NULL;
spin_unlock_irqrestore (&ohci->lock, flags);
return;
}
static int ohci_get_frame (struct usb_hcd *hcd)
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
return ohci_frame_no(ohci);
}
static void ohci_usb_reset (struct ohci_hcd *ohci)
{
ohci->hc_control = ohci_readl (ohci, &ohci->regs->control);
ohci->hc_control &= OHCI_CTRL_RWC;
ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
}
/*-------------------------------------------------------------------------*
* HC functions
*-------------------------------------------------------------------------*/
/* init memory, and kick BIOS/SMM off */
static int ohci_init (struct ohci_hcd *ohci)
{
int ret;
disable (ohci);
ohci->regs = ohci_to_hcd(ohci)->regs;
ohci->next_statechange = jiffies;
#ifndef IR_DISABLE
/* SMM owns the HC? not for long! */
if (!no_handshake && ohci_readl (ohci,
&ohci->regs->control) & OHCI_CTRL_IR) {
u32 temp;
ohci_dbg (ohci, "USB HC TakeOver from BIOS/SMM\n");
/* this timeout is arbitrary. we make it long, so systems
* depending on usb keyboards may be usable even if the
* BIOS/SMM code seems pretty broken.
*/
temp = 500; /* arbitrary: five seconds */
ohci_writel (ohci, OHCI_INTR_OC, &ohci->regs->intrenable);
ohci_writel (ohci, OHCI_OCR, &ohci->regs->cmdstatus);
while (ohci_readl (ohci, &ohci->regs->control) & OHCI_CTRL_IR) {
msleep (10);
if (--temp == 0) {
ohci_err (ohci, "USB HC takeover failed!"
" (BIOS/SMM bug)\n");
return -EBUSY;
}
}
ohci_usb_reset (ohci);
}
#endif
/* Disable HC interrupts */
ohci_writel (ohci, OHCI_INTR_MIE, &ohci->regs->intrdisable);
// flush the writes
(void) ohci_readl (ohci, &ohci->regs->control);
if (ohci->hcca)
return 0;
ohci->hcca = dma_alloc_coherent (ohci_to_hcd(ohci)->self.controller,
sizeof *ohci->hcca, &ohci->hcca_dma, 0);
if (!ohci->hcca)
return -ENOMEM;
if ((ret = ohci_mem_init (ohci)) < 0)
ohci_stop (ohci_to_hcd(ohci));
return ret;
}
/*-------------------------------------------------------------------------*/
/* Start an OHCI controller, set the BUS operational
* resets USB and controller
* enable interrupts
* connect the virtual root hub
*/
static int ohci_run (struct ohci_hcd *ohci)
{
u32 mask, temp;
struct usb_device *udev;
struct usb_bus *bus;
int first = ohci->fminterval == 0;
disable (ohci);
/* boot firmware should have set this up (5.1.1.3.1) */
if (first) {
temp = ohci_readl (ohci, &ohci->regs->fminterval);
ohci->fminterval = temp & 0x3fff;
if (ohci->fminterval != FI)
ohci_dbg (ohci, "fminterval delta %d\n",
ohci->fminterval - FI);
ohci->fminterval |= FSMP (ohci->fminterval) << 16;
/* also: power/overcurrent flags in roothub.a */
}
/* Reset USB nearly "by the book". RemoteWakeupConnected
* saved if boot firmware (BIOS/SMM/...) told us it's connected
* (for OHCI integrated on mainboard, it normally is)
*/
ohci->hc_control = ohci_readl (ohci, &ohci->regs->control);
ohci_dbg (ohci, "resetting from state '%s', control = 0x%x\n",
hcfs2string (ohci->hc_control & OHCI_CTRL_HCFS),
ohci->hc_control);
if (ohci->hc_control & OHCI_CTRL_RWC
&& !(ohci->flags & OHCI_QUIRK_AMD756))
ohci_to_hcd(ohci)->can_wakeup = 1;
switch (ohci->hc_control & OHCI_CTRL_HCFS) {
case OHCI_USB_OPER:
temp = 0;
break;
case OHCI_USB_SUSPEND:
case OHCI_USB_RESUME:
ohci->hc_control &= OHCI_CTRL_RWC;
ohci->hc_control |= OHCI_USB_RESUME;
temp = 10 /* msec wait */;
break;
// case OHCI_USB_RESET:
default:
ohci->hc_control &= OHCI_CTRL_RWC;
ohci->hc_control |= OHCI_USB_RESET;
temp = 50 /* msec wait */;
break;
}
ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
// flush the writes
(void) ohci_readl (ohci, &ohci->regs->control);
msleep(temp);
temp = roothub_a (ohci);
if (!(temp & RH_A_NPS)) {
unsigned ports = temp & RH_A_NDP;
/* power down each port */
for (temp = 0; temp < ports; temp++)
ohci_writel (ohci, RH_PS_LSDA,
&ohci->regs->roothub.portstatus [temp]);
}
// flush those writes
(void) ohci_readl (ohci, &ohci->regs->control);
memset (ohci->hcca, 0, sizeof (struct ohci_hcca));
/* 2msec timelimit here means no irqs/preempt */
spin_lock_irq (&ohci->lock);
retry:
/* HC Reset requires max 10 us delay */
ohci_writel (ohci, OHCI_HCR, &ohci->regs->cmdstatus);
temp = 30; /* ... allow extra time */
while ((ohci_readl (ohci, &ohci->regs->cmdstatus) & OHCI_HCR) != 0) {
if (--temp == 0) {
spin_unlock_irq (&ohci->lock);
ohci_err (ohci, "USB HC reset timed out!\n");
return -1;
}
udelay (1);
}
/* now we're in the SUSPEND state ... must go OPERATIONAL
* within 2msec else HC enters RESUME
*
* ... but some hardware won't init fmInterval "by the book"
* (SiS, OPTi ...), so reset again instead. SiS doesn't need
* this if we write fmInterval after we're OPERATIONAL.
* Unclear about ALi, ServerWorks, and others ... this could
* easily be a longstanding bug in chip init on Linux.
*/
if (ohci->flags & OHCI_QUIRK_INITRESET) {
ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
// flush those writes
(void) ohci_readl (ohci, &ohci->regs->control);
}
/* Tell the controller where the control and bulk lists are
* The lists are empty now. */
ohci_writel (ohci, 0, &ohci->regs->ed_controlhead);
ohci_writel (ohci, 0, &ohci->regs->ed_bulkhead);
/* a reset clears this */
ohci_writel (ohci, (u32) ohci->hcca_dma, &ohci->regs->hcca);
periodic_reinit (ohci);
/* some OHCI implementations are finicky about how they init.
* bogus values here mean not even enumeration could work.
*/
if ((ohci_readl (ohci, &ohci->regs->fminterval) & 0x3fff0000) == 0
|| !ohci_readl (ohci, &ohci->regs->periodicstart)) {
if (!(ohci->flags & OHCI_QUIRK_INITRESET)) {
ohci->flags |= OHCI_QUIRK_INITRESET;
ohci_dbg (ohci, "enabling initreset quirk\n");
goto retry;
}
spin_unlock_irq (&ohci->lock);
ohci_err (ohci, "init err (%08x %04x)\n",
ohci_readl (ohci, &ohci->regs->fminterval),
ohci_readl (ohci, &ohci->regs->periodicstart));
return -EOVERFLOW;
}
/* start controller operations */
ohci->hc_control &= OHCI_CTRL_RWC;
ohci->hc_control |= OHCI_CONTROL_INIT | OHCI_USB_OPER;
ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
ohci_to_hcd(ohci)->state = HC_STATE_RUNNING;
/* wake on ConnectStatusChange, matching external hubs */
ohci_writel (ohci, RH_HS_DRWE, &ohci->regs->roothub.status);
/* Choose the interrupts we care about now, others later on demand */
mask = OHCI_INTR_INIT;
ohci_writel (ohci, mask, &ohci->regs->intrstatus);
ohci_writel (ohci, mask, &ohci->regs->intrenable);
/* handle root hub init quirks ... */
temp = roothub_a (ohci);
temp &= ~(RH_A_PSM | RH_A_OCPM);
if (ohci->flags & OHCI_QUIRK_SUPERIO) {
/* NSC 87560 and maybe others */
temp |= RH_A_NOCP;
temp &= ~(RH_A_POTPGT | RH_A_NPS);
ohci_writel (ohci, temp, &ohci->regs->roothub.a);
} else if ((ohci->flags & OHCI_QUIRK_AMD756) || distrust_firmware) {
/* hub power always on; required for AMD-756 and some
* Mac platforms. ganged overcurrent reporting, if any.
*/
temp |= RH_A_NPS;
ohci_writel (ohci, temp, &ohci->regs->roothub.a);
}
ohci_writel (ohci, RH_HS_LPSC, &ohci->regs->roothub.status);
ohci_writel (ohci, (temp & RH_A_NPS) ? 0 : RH_B_PPCM,
&ohci->regs->roothub.b);
// flush those writes
(void) ohci_readl (ohci, &ohci->regs->control);
spin_unlock_irq (&ohci->lock);
// POTPGT delay is bits 24-31, in 2 ms units.
mdelay ((temp >> 23) & 0x1fe);
bus = &ohci_to_hcd(ohci)->self;
ohci_to_hcd(ohci)->state = HC_STATE_RUNNING;
ohci_dump (ohci, 1);
udev = bus->root_hub;
if (udev) {
return 0;
}
/* connect the virtual root hub */
udev = usb_alloc_dev (NULL, bus, 0);
if (!udev) {
disable (ohci);
ohci->hc_control &= ~OHCI_CTRL_HCFS;
ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
return -ENOMEM;
}
udev->speed = USB_SPEED_FULL;
if (usb_hcd_register_root_hub (udev, ohci_to_hcd(ohci)) != 0) {
usb_put_dev (udev);
disable (ohci);
ohci->hc_control &= ~OHCI_CTRL_HCFS;
ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
return -ENODEV;
}
if (ohci->power_budget)
hub_set_power_budget(udev, ohci->power_budget);
create_debug_files (ohci);
return 0;
}
/*-------------------------------------------------------------------------*/
/* an interrupt happens */
static irqreturn_t ohci_irq (struct usb_hcd *hcd, struct pt_regs *ptregs)
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
struct ohci_regs __iomem *regs = ohci->regs;
int ints;
/* we can eliminate a (slow) ohci_readl()
if _only_ WDH caused this irq */
if ((ohci->hcca->done_head != 0)
&& ! (hc32_to_cpup (ohci, &ohci->hcca->done_head)
& 0x01)) {
ints = OHCI_INTR_WDH;
/* cardbus/... hardware gone before remove() */
} else if ((ints = ohci_readl (ohci, &regs->intrstatus)) == ~(u32)0) {
disable (ohci);
ohci_dbg (ohci, "device removed!\n");
return IRQ_HANDLED;
/* interrupt for some other device? */
} else if ((ints &= ohci_readl (ohci, &regs->intrenable)) == 0) {
return IRQ_NOTMINE;
}
if (ints & OHCI_INTR_UE) {
disable (ohci);
ohci_err (ohci, "OHCI Unrecoverable Error, disabled\n");
// e.g. due to PCI Master/Target Abort
ohci_dump (ohci, 1);
ohci_usb_reset (ohci);
}
if (ints & OHCI_INTR_RD) {
ohci_vdbg (ohci, "resume detect\n");
if (hcd->state != HC_STATE_QUIESCING)
schedule_work(&ohci->rh_resume);
}
if (ints & OHCI_INTR_WDH) {
if (HC_IS_RUNNING(hcd->state))
ohci_writel (ohci, OHCI_INTR_WDH, &regs->intrdisable);
spin_lock (&ohci->lock);
dl_done_list (ohci, ptregs);
spin_unlock (&ohci->lock);
if (HC_IS_RUNNING(hcd->state))
ohci_writel (ohci, OHCI_INTR_WDH, &regs->intrenable);
}
/* could track INTR_SO to reduce available PCI/... bandwidth */
/* handle any pending URB/ED unlinks, leaving INTR_SF enabled
* when there's still unlinking to be done (next frame).
*/
spin_lock (&ohci->lock);
if (ohci->ed_rm_list)
finish_unlinks (ohci, ohci_frame_no(ohci), ptregs);
if ((ints & OHCI_INTR_SF) != 0 && !ohci->ed_rm_list
&& HC_IS_RUNNING(hcd->state))
ohci_writel (ohci, OHCI_INTR_SF, &regs->intrdisable);
spin_unlock (&ohci->lock);
if (HC_IS_RUNNING(hcd->state)) {
ohci_writel (ohci, ints, &regs->intrstatus);
ohci_writel (ohci, OHCI_INTR_MIE, &regs->intrenable);
// flush those writes
(void) ohci_readl (ohci, &ohci->regs->control);
}
return IRQ_HANDLED;
}
/*-------------------------------------------------------------------------*/
static void ohci_stop (struct usb_hcd *hcd)
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
ohci_dbg (ohci, "stop %s controller (state 0x%02x)\n",
hcfs2string (ohci->hc_control & OHCI_CTRL_HCFS),
hcd->state);
ohci_dump (ohci, 1);
flush_scheduled_work();
ohci_usb_reset (ohci);
ohci_writel (ohci, OHCI_INTR_MIE, &ohci->regs->intrdisable);
remove_debug_files (ohci);
ohci_mem_cleanup (ohci);
if (ohci->hcca) {
dma_free_coherent (hcd->self.controller,
sizeof *ohci->hcca,
ohci->hcca, ohci->hcca_dma);
ohci->hcca = NULL;
ohci->hcca_dma = 0;
}
}
/*-------------------------------------------------------------------------*/
/* must not be called from interrupt context */
#if defined(CONFIG_USB_SUSPEND) || defined(CONFIG_PM)
static int ohci_restart (struct ohci_hcd *ohci)
{
int temp;
int i;
struct urb_priv *priv;
struct usb_device *root = ohci_to_hcd(ohci)->self.root_hub;
/* mark any devices gone, so they do nothing till khubd disconnects.
* recycle any "live" eds/tds (and urbs) right away.
* later, khubd disconnect processing will recycle the other state,
* (either as disconnect/reconnect, or maybe someday as a reset).
*/
spin_lock_irq(&ohci->lock);
disable (ohci);
for (i = 0; i < root->maxchild; i++) {
if (root->children [i])
usb_set_device_state (root->children[i],
USB_STATE_NOTATTACHED);
}
if (!list_empty (&ohci->pending))
ohci_dbg(ohci, "abort schedule...\n");
list_for_each_entry (priv, &ohci->pending, pending) {
struct urb *urb = priv->td[0]->urb;
struct ed *ed = priv->ed;
switch (ed->state) {
case ED_OPER:
ed->state = ED_UNLINK;
ed->hwINFO |= cpu_to_hc32(ohci, ED_DEQUEUE);
ed_deschedule (ohci, ed);
ed->ed_next = ohci->ed_rm_list;
ed->ed_prev = NULL;
ohci->ed_rm_list = ed;
/* FALLTHROUGH */
case ED_UNLINK:
break;
default:
ohci_dbg(ohci, "bogus ed %p state %d\n",
ed, ed->state);
}
spin_lock (&urb->lock);
urb->status = -ESHUTDOWN;
spin_unlock (&urb->lock);
}
finish_unlinks (ohci, 0, NULL);
spin_unlock_irq(&ohci->lock);
/* paranoia, in case that didn't work: */
/* empty the interrupt branches */
for (i = 0; i < NUM_INTS; i++) ohci->load [i] = 0;
for (i = 0; i < NUM_INTS; i++) ohci->hcca->int_table [i] = 0;
/* no EDs to remove */
ohci->ed_rm_list = NULL;
/* empty control and bulk lists */
ohci->ed_controltail = NULL;
ohci->ed_bulktail = NULL;
if ((temp = ohci_run (ohci)) < 0) {
ohci_err (ohci, "can't restart, %d\n", temp);
return temp;
} else {
/* here we "know" root ports should always stay powered,
* and that if we try to turn them back on the root hub
* will respond to CSC processing.
*/
i = roothub_a (ohci) & RH_A_NDP;
while (i--)
ohci_writel (ohci, RH_PS_PSS,
&ohci->regs->roothub.portstatus [temp]);
ohci_dbg (ohci, "restart complete\n");
}
return 0;
}
#endif
/*-------------------------------------------------------------------------*/
#define DRIVER_INFO DRIVER_VERSION " " DRIVER_DESC
MODULE_AUTHOR (DRIVER_AUTHOR);
MODULE_DESCRIPTION (DRIVER_INFO);
MODULE_LICENSE ("GPL");
#ifdef CONFIG_PCI
#include "ohci-pci.c"
#endif
#ifdef CONFIG_SA1111
#include "ohci-sa1111.c"
#endif
#ifdef CONFIG_ARCH_OMAP
#include "ohci-omap.c"
#endif
#ifdef CONFIG_ARCH_LH7A404
#include "ohci-lh7a404.c"
#endif
#ifdef CONFIG_PXA27x
#include "ohci-pxa27x.c"
#endif
#ifdef CONFIG_SOC_AU1X00
#include "ohci-au1xxx.c"
#endif
#ifdef CONFIG_USB_OHCI_HCD_PPC_SOC
#include "ohci-ppc-soc.c"
#endif
#if !(defined(CONFIG_PCI) \
|| defined(CONFIG_SA1111) \
|| defined(CONFIG_ARCH_OMAP) \
|| defined (CONFIG_ARCH_LH7A404) \
|| defined (CONFIG_PXA27x) \
|| defined (CONFIG_SOC_AU1X00) \
|| defined (CONFIG_USB_OHCI_HCD_PPC_SOC) \
)
#error "missing bus glue for ohci-hcd"
#endif

643
drivers/usb/host/ohci-hub.c 一般檔案
查看文件

@@ -0,0 +1,643 @@
/*
* OHCI HCD (Host Controller Driver) for USB.
*
* (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
* (C) Copyright 2000-2004 David Brownell <dbrownell@users.sourceforge.net>
*
* This file is licenced under GPL
*/
/*-------------------------------------------------------------------------*/
/*
* OHCI Root Hub ... the nonsharable stuff
*/
#define dbg_port(hc,label,num,value) \
ohci_dbg (hc, \
"%s roothub.portstatus [%d] " \
"= 0x%08x%s%s%s%s%s%s%s%s%s%s%s%s\n", \
label, num, temp, \
(temp & RH_PS_PRSC) ? " PRSC" : "", \
(temp & RH_PS_OCIC) ? " OCIC" : "", \
(temp & RH_PS_PSSC) ? " PSSC" : "", \
(temp & RH_PS_PESC) ? " PESC" : "", \
(temp & RH_PS_CSC) ? " CSC" : "", \
\
(temp & RH_PS_LSDA) ? " LSDA" : "", \
(temp & RH_PS_PPS) ? " PPS" : "", \
(temp & RH_PS_PRS) ? " PRS" : "", \
(temp & RH_PS_POCI) ? " POCI" : "", \
(temp & RH_PS_PSS) ? " PSS" : "", \
\
(temp & RH_PS_PES) ? " PES" : "", \
(temp & RH_PS_CCS) ? " CCS" : "" \
);
/*-------------------------------------------------------------------------*/
#if defined(CONFIG_USB_SUSPEND) || defined(CONFIG_PM)
#define OHCI_SCHED_ENABLES \
(OHCI_CTRL_CLE|OHCI_CTRL_BLE|OHCI_CTRL_PLE|OHCI_CTRL_IE)
static void dl_done_list (struct ohci_hcd *, struct pt_regs *);
static void finish_unlinks (struct ohci_hcd *, u16 , struct pt_regs *);
static int ohci_restart (struct ohci_hcd *ohci);
static int ohci_hub_suspend (struct usb_hcd *hcd)
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
int status = 0;
unsigned long flags;
spin_lock_irqsave (&ohci->lock, flags);
ohci->hc_control = ohci_readl (ohci, &ohci->regs->control);
switch (ohci->hc_control & OHCI_CTRL_HCFS) {
case OHCI_USB_RESUME:
ohci_dbg (ohci, "resume/suspend?\n");
ohci->hc_control &= ~OHCI_CTRL_HCFS;
ohci->hc_control |= OHCI_USB_RESET;
ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
(void) ohci_readl (ohci, &ohci->regs->control);
/* FALL THROUGH */
case OHCI_USB_RESET:
status = -EBUSY;
ohci_dbg (ohci, "needs reinit!\n");
goto done;
case OHCI_USB_SUSPEND:
ohci_dbg (ohci, "already suspended\n");
goto done;
}
ohci_dbg (ohci, "suspend root hub\n");
/* First stop any processing */
hcd->state = HC_STATE_QUIESCING;
if (ohci->hc_control & OHCI_SCHED_ENABLES) {
int limit;
ohci->hc_control &= ~OHCI_SCHED_ENABLES;
ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
ohci->hc_control = ohci_readl (ohci, &ohci->regs->control);
ohci_writel (ohci, OHCI_INTR_SF, &ohci->regs->intrstatus);
/* sched disables take effect on the next frame,
* then the last WDH could take 6+ msec
*/
ohci_dbg (ohci, "stopping schedules ...\n");
limit = 2000;
while (limit > 0) {
udelay (250);
limit =- 250;
if (ohci_readl (ohci, &ohci->regs->intrstatus)
& OHCI_INTR_SF)
break;
}
dl_done_list (ohci, NULL);
mdelay (7);
}
dl_done_list (ohci, NULL);
finish_unlinks (ohci, ohci_frame_no(ohci), NULL);
ohci_writel (ohci, ohci_readl (ohci, &ohci->regs->intrstatus),
&ohci->regs->intrstatus);
/* maybe resume can wake root hub */
if (hcd->remote_wakeup)
ohci->hc_control |= OHCI_CTRL_RWE;
else
ohci->hc_control &= ~OHCI_CTRL_RWE;
/* Suspend hub */
ohci->hc_control &= ~OHCI_CTRL_HCFS;
ohci->hc_control |= OHCI_USB_SUSPEND;
ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
(void) ohci_readl (ohci, &ohci->regs->control);
/* no resumes until devices finish suspending */
ohci->next_statechange = jiffies + msecs_to_jiffies (5);
done:
if (status == 0)
hcd->state = HC_STATE_SUSPENDED;
spin_unlock_irqrestore (&ohci->lock, flags);
return status;
}
static inline struct ed *find_head (struct ed *ed)
{
/* for bulk and control lists */
while (ed->ed_prev)
ed = ed->ed_prev;
return ed;
}
/* caller has locked the root hub */
static int ohci_hub_resume (struct usb_hcd *hcd)
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
u32 temp, enables;
int status = -EINPROGRESS;
if (time_before (jiffies, ohci->next_statechange))
msleep(5);
spin_lock_irq (&ohci->lock);
ohci->hc_control = ohci_readl (ohci, &ohci->regs->control);
if (ohci->hc_control & (OHCI_CTRL_IR | OHCI_SCHED_ENABLES)) {
/* this can happen after suspend-to-disk */
if (hcd->state == HC_STATE_RESUMING) {
ohci_dbg (ohci, "BIOS/SMM active, control %03x\n",
ohci->hc_control);
status = -EBUSY;
/* this happens when pmcore resumes HC then root */
} else {
ohci_dbg (ohci, "duplicate resume\n");
status = 0;
}
} else switch (ohci->hc_control & OHCI_CTRL_HCFS) {
case OHCI_USB_SUSPEND:
ohci->hc_control &= ~(OHCI_CTRL_HCFS|OHCI_SCHED_ENABLES);
ohci->hc_control |= OHCI_USB_RESUME;
ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
(void) ohci_readl (ohci, &ohci->regs->control);
ohci_dbg (ohci, "resume root hub\n");
break;
case OHCI_USB_RESUME:
/* HCFS changes sometime after INTR_RD */
ohci_info (ohci, "wakeup\n");
break;
case OHCI_USB_OPER:
ohci_dbg (ohci, "already resumed\n");
status = 0;
break;
default: /* RESET, we lost power */
ohci_dbg (ohci, "root hub hardware reset\n");
status = -EBUSY;
}
spin_unlock_irq (&ohci->lock);
if (status == -EBUSY) {
(void) ohci_init (ohci);
return ohci_restart (ohci);
}
if (status != -EINPROGRESS)
return status;
temp = roothub_a (ohci) & RH_A_NDP;
enables = 0;
while (temp--) {
u32 stat = ohci_readl (ohci,
&ohci->regs->roothub.portstatus [temp]);
/* force global, not selective, resume */
if (!(stat & RH_PS_PSS))
continue;
ohci_writel (ohci, RH_PS_POCI,
&ohci->regs->roothub.portstatus [temp]);
}
/* Some controllers (lucent erratum) need extra-long delays */
hcd->state = HC_STATE_RESUMING;
mdelay (20 /* usb 11.5.1.10 */ + 15);
temp = ohci_readl (ohci, &ohci->regs->control);
temp &= OHCI_CTRL_HCFS;
if (temp != OHCI_USB_RESUME) {
ohci_err (ohci, "controller won't resume\n");
return -EBUSY;
}
/* disable old schedule state, reinit from scratch */
ohci_writel (ohci, 0, &ohci->regs->ed_controlhead);
ohci_writel (ohci, 0, &ohci->regs->ed_controlcurrent);
ohci_writel (ohci, 0, &ohci->regs->ed_bulkhead);
ohci_writel (ohci, 0, &ohci->regs->ed_bulkcurrent);
ohci_writel (ohci, 0, &ohci->regs->ed_periodcurrent);
ohci_writel (ohci, (u32) ohci->hcca_dma, &ohci->regs->hcca);
/* Sometimes PCI D3 suspend trashes frame timings ... */
periodic_reinit (ohci);
/* interrupts might have been disabled */
ohci_writel (ohci, OHCI_INTR_INIT, &ohci->regs->intrenable);
if (ohci->ed_rm_list)
ohci_writel (ohci, OHCI_INTR_SF, &ohci->regs->intrenable);
ohci_writel (ohci, ohci_readl (ohci, &ohci->regs->intrstatus),
&ohci->regs->intrstatus);
/* Then re-enable operations */
ohci_writel (ohci, OHCI_USB_OPER, &ohci->regs->control);
(void) ohci_readl (ohci, &ohci->regs->control);
msleep (3);
temp = OHCI_CONTROL_INIT | OHCI_USB_OPER;
if (hcd->can_wakeup)
temp |= OHCI_CTRL_RWC;
ohci->hc_control = temp;
ohci_writel (ohci, temp, &ohci->regs->control);
(void) ohci_readl (ohci, &ohci->regs->control);
/* TRSMRCY */
msleep (10);
/* keep it alive for ~5x suspend + resume costs */
ohci->next_statechange = jiffies + msecs_to_jiffies (250);
/* maybe turn schedules back on */
enables = 0;
temp = 0;
if (!ohci->ed_rm_list) {
if (ohci->ed_controltail) {
ohci_writel (ohci,
find_head (ohci->ed_controltail)->dma,
&ohci->regs->ed_controlhead);
enables |= OHCI_CTRL_CLE;
temp |= OHCI_CLF;
}
if (ohci->ed_bulktail) {
ohci_writel (ohci, find_head (ohci->ed_bulktail)->dma,
&ohci->regs->ed_bulkhead);
enables |= OHCI_CTRL_BLE;
temp |= OHCI_BLF;
}
}
if (hcd->self.bandwidth_isoc_reqs || hcd->self.bandwidth_int_reqs)
enables |= OHCI_CTRL_PLE|OHCI_CTRL_IE;
if (enables) {
ohci_dbg (ohci, "restarting schedules ... %08x\n", enables);
ohci->hc_control |= enables;
ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
if (temp)
ohci_writel (ohci, temp, &ohci->regs->cmdstatus);
(void) ohci_readl (ohci, &ohci->regs->control);
}
hcd->state = HC_STATE_RUNNING;
return 0;
}
static void ohci_rh_resume (void *_hcd)
{
struct usb_hcd *hcd = _hcd;
usb_lock_device (hcd->self.root_hub);
(void) ohci_hub_resume (hcd);
usb_unlock_device (hcd->self.root_hub);
}
#else
static void ohci_rh_resume (void *_hcd)
{
struct ohci_hcd *ohci = hcd_to_ohci (_hcd);
ohci_dbg(ohci, "rh_resume ??\n");
}
#endif /* CONFIG_USB_SUSPEND || CONFIG_PM */
/*-------------------------------------------------------------------------*/
/* build "status change" packet (one or two bytes) from HC registers */
static int
ohci_hub_status_data (struct usb_hcd *hcd, char *buf)
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
int ports, i, changed = 0, length = 1;
int can_suspend = hcd->can_wakeup;
unsigned long flags;
spin_lock_irqsave (&ohci->lock, flags);
/* handle autosuspended root: finish resuming before
* letting khubd or root hub timer see state changes.
*/
if ((ohci->hc_control & OHCI_CTRL_HCFS) != OHCI_USB_OPER
|| !HC_IS_RUNNING(hcd->state)) {
can_suspend = 0;
goto done;
}
ports = roothub_a (ohci) & RH_A_NDP;
if (ports > MAX_ROOT_PORTS) {
ohci_err (ohci, "bogus NDP=%d, rereads as NDP=%d\n", ports,
ohci_readl (ohci, &ohci->regs->roothub.a) & RH_A_NDP);
/* retry later; "should not happen" */
goto done;
}
/* init status */
if (roothub_status (ohci) & (RH_HS_LPSC | RH_HS_OCIC))
buf [0] = changed = 1;
else
buf [0] = 0;
if (ports > 7) {
buf [1] = 0;
length++;
}
/* look at each port */
for (i = 0; i < ports; i++) {
u32 status = roothub_portstatus (ohci, i);
if (status & (RH_PS_CSC | RH_PS_PESC | RH_PS_PSSC
| RH_PS_OCIC | RH_PS_PRSC)) {
changed = 1;
if (i < 7)
buf [0] |= 1 << (i + 1);
else
buf [1] |= 1 << (i - 7);
continue;
}
/* can suspend if no ports are enabled; or if all all
* enabled ports are suspended AND remote wakeup is on.
*/
if (!(status & RH_PS_CCS))
continue;
if ((status & RH_PS_PSS) && hcd->remote_wakeup)
continue;
can_suspend = 0;
}
done:
spin_unlock_irqrestore (&ohci->lock, flags);
#ifdef CONFIG_PM
/* save power by suspending idle root hubs;
* INTR_RD wakes us when there's work
* NOTE: if we can do this, we don't need a root hub timer!
*/
if (can_suspend
&& !changed
&& !ohci->ed_rm_list
&& ((OHCI_CTRL_HCFS | OHCI_SCHED_ENABLES)
& ohci->hc_control)
== OHCI_USB_OPER
&& time_after (jiffies, ohci->next_statechange)
&& usb_trylock_device (hcd->self.root_hub)
) {
ohci_vdbg (ohci, "autosuspend\n");
(void) ohci_hub_suspend (hcd);
hcd->state = HC_STATE_RUNNING;
usb_unlock_device (hcd->self.root_hub);
}
#endif
return changed ? length : 0;
}
/*-------------------------------------------------------------------------*/
static void
ohci_hub_descriptor (
struct ohci_hcd *ohci,
struct usb_hub_descriptor *desc
) {
u32 rh = roothub_a (ohci);
int ports = rh & RH_A_NDP;
u16 temp;
desc->bDescriptorType = 0x29;
desc->bPwrOn2PwrGood = (rh & RH_A_POTPGT) >> 24;
desc->bHubContrCurrent = 0;
desc->bNbrPorts = ports;
temp = 1 + (ports / 8);
desc->bDescLength = 7 + 2 * temp;
temp = 0;
if (rh & RH_A_NPS) /* no power switching? */
temp |= 0x0002;
if (rh & RH_A_PSM) /* per-port power switching? */
temp |= 0x0001;
if (rh & RH_A_NOCP) /* no overcurrent reporting? */
temp |= 0x0010;
else if (rh & RH_A_OCPM) /* per-port overcurrent reporting? */
temp |= 0x0008;
desc->wHubCharacteristics = (__force __u16)cpu_to_hc16(ohci, temp);
/* two bitmaps: ports removable, and usb 1.0 legacy PortPwrCtrlMask */
rh = roothub_b (ohci);
desc->bitmap [0] = rh & RH_B_DR;
if (ports > 7) {
desc->bitmap [1] = (rh & RH_B_DR) >> 8;
desc->bitmap [2] = desc->bitmap [3] = 0xff;
} else
desc->bitmap [1] = 0xff;
}
/*-------------------------------------------------------------------------*/
#ifdef CONFIG_USB_OTG
static int ohci_start_port_reset (struct usb_hcd *hcd, unsigned port)
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
u32 status;
if (!port)
return -EINVAL;
port--;
/* start port reset before HNP protocol times out */
status = ohci_readl(ohci, &ohci->regs->roothub.portstatus [port]);
if (!(status & RH_PS_CCS))
return -ENODEV;
/* khubd will finish the reset later */
ohci_writel(ohci, RH_PS_PRS, &ohci->regs->roothub.portstatus [port]);
return 0;
}
static void start_hnp(struct ohci_hcd *ohci);
#else
#define ohci_start_port_reset NULL
#endif
/*-------------------------------------------------------------------------*/
/* See usb 7.1.7.5: root hubs must issue at least 50 msec reset signaling,
* not necessarily continuous ... to guard against resume signaling.
* The short timeout is safe for non-root hubs, and is backward-compatible
* with earlier Linux hosts.
*/
#ifdef CONFIG_USB_SUSPEND
#define PORT_RESET_MSEC 50
#else
#define PORT_RESET_MSEC 10
#endif
/* this timer value might be vendor-specific ... */
#define PORT_RESET_HW_MSEC 10
/* wrap-aware logic morphed from <linux/jiffies.h> */
#define tick_before(t1,t2) ((s16)(((s16)(t1))-((s16)(t2))) < 0)
/* called from some task, normally khubd */
static inline void root_port_reset (struct ohci_hcd *ohci, unsigned port)
{
__hc32 __iomem *portstat = &ohci->regs->roothub.portstatus [port];
u32 temp;
u16 now = ohci_readl(ohci, &ohci->regs->fmnumber);
u16 reset_done = now + PORT_RESET_MSEC;
/* build a "continuous enough" reset signal, with up to
* 3msec gap between pulses. scheduler HZ==100 must work;
* this might need to be deadline-scheduled.
*/
do {
/* spin until any current reset finishes */
for (;;) {
temp = ohci_readl (ohci, portstat);
if (!(temp & RH_PS_PRS))
break;
udelay (500);
}
if (!(temp & RH_PS_CCS))
break;
if (temp & RH_PS_PRSC)
ohci_writel (ohci, RH_PS_PRSC, portstat);
/* start the next reset, sleep till it's probably done */
ohci_writel (ohci, RH_PS_PRS, portstat);
msleep(PORT_RESET_HW_MSEC);
now = ohci_readl(ohci, &ohci->regs->fmnumber);
} while (tick_before(now, reset_done));
/* caller synchronizes using PRSC */
}
static int ohci_hub_control (
struct usb_hcd *hcd,
u16 typeReq,
u16 wValue,
u16 wIndex,
char *buf,
u16 wLength
) {
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
int ports = hcd_to_bus (hcd)->root_hub->maxchild;
u32 temp;
int retval = 0;
switch (typeReq) {
case ClearHubFeature:
switch (wValue) {
case C_HUB_OVER_CURRENT:
ohci_writel (ohci, RH_HS_OCIC,
&ohci->regs->roothub.status);
case C_HUB_LOCAL_POWER:
break;
default:
goto error;
}
break;
case ClearPortFeature:
if (!wIndex || wIndex > ports)
goto error;
wIndex--;
switch (wValue) {
case USB_PORT_FEAT_ENABLE:
temp = RH_PS_CCS;
break;
case USB_PORT_FEAT_C_ENABLE:
temp = RH_PS_PESC;
break;
case USB_PORT_FEAT_SUSPEND:
temp = RH_PS_POCI;
if ((ohci->hc_control & OHCI_CTRL_HCFS)
!= OHCI_USB_OPER)
schedule_work (&ohci->rh_resume);
break;
case USB_PORT_FEAT_C_SUSPEND:
temp = RH_PS_PSSC;
break;
case USB_PORT_FEAT_POWER:
temp = RH_PS_LSDA;
break;
case USB_PORT_FEAT_C_CONNECTION:
temp = RH_PS_CSC;
break;
case USB_PORT_FEAT_C_OVER_CURRENT:
temp = RH_PS_OCIC;
break;
case USB_PORT_FEAT_C_RESET:
temp = RH_PS_PRSC;
break;
default:
goto error;
}
ohci_writel (ohci, temp,
&ohci->regs->roothub.portstatus [wIndex]);
// ohci_readl (ohci, &ohci->regs->roothub.portstatus [wIndex]);
break;
case GetHubDescriptor:
ohci_hub_descriptor (ohci, (struct usb_hub_descriptor *) buf);
break;
case GetHubStatus:
temp = roothub_status (ohci) & ~(RH_HS_CRWE | RH_HS_DRWE);
*(__le32 *) buf = cpu_to_le32 (temp);
break;
case GetPortStatus:
if (!wIndex || wIndex > ports)
goto error;
wIndex--;
temp = roothub_portstatus (ohci, wIndex);
*(__le32 *) buf = cpu_to_le32 (temp);
#ifndef OHCI_VERBOSE_DEBUG
if (*(u16*)(buf+2)) /* only if wPortChange is interesting */
#endif
dbg_port (ohci, "GetStatus", wIndex, temp);
break;
case SetHubFeature:
switch (wValue) {
case C_HUB_OVER_CURRENT:
// FIXME: this can be cleared, yes?
case C_HUB_LOCAL_POWER:
break;
default:
goto error;
}
break;
case SetPortFeature:
if (!wIndex || wIndex > ports)
goto error;
wIndex--;
switch (wValue) {
case USB_PORT_FEAT_SUSPEND:
#ifdef CONFIG_USB_OTG
if (hcd->self.otg_port == (wIndex + 1)
&& hcd->self.b_hnp_enable)
start_hnp(ohci);
else
#endif
ohci_writel (ohci, RH_PS_PSS,
&ohci->regs->roothub.portstatus [wIndex]);
break;
case USB_PORT_FEAT_POWER:
ohci_writel (ohci, RH_PS_PPS,
&ohci->regs->roothub.portstatus [wIndex]);
break;
case USB_PORT_FEAT_RESET:
root_port_reset (ohci, wIndex);
break;
default:
goto error;
}
break;
default:
error:
/* "protocol stall" on error */
retval = -EPIPE;
}
return retval;
}

查看文件

@@ -0,0 +1,266 @@
/*
* OHCI HCD (Host Controller Driver) for USB.
*
* (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
* (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
* (C) Copyright 2002 Hewlett-Packard Company
*
* Bus Glue for Sharp LH7A404
*
* Written by Christopher Hoover <ch@hpl.hp.com>
* Based on fragments of previous driver by Rusell King et al.
*
* Modified for LH7A404 from ohci-sa1111.c
* by Durgesh Pattamatta <pattamattad@sharpsec.com>
*
* This file is licenced under the GPL.
*/
#include <asm/hardware.h>
#include <asm/mach-types.h>
#include <asm/arch/hardware.h>
extern int usb_disabled(void);
/*-------------------------------------------------------------------------*/
static void lh7a404_start_hc(struct platform_device *dev)
{
printk(KERN_DEBUG __FILE__
": starting LH7A404 OHCI USB Controller\n");
/*
* Now, carefully enable the USB clock, and take
* the USB host controller out of reset.
*/
CSC_PWRCNT |= CSC_PWRCNT_USBH_EN; /* Enable clock */
udelay(1000);
USBH_CMDSTATUS = OHCI_HCR;
printk(KERN_DEBUG __FILE__
": Clock to USB host has been enabled \n");
}
static void lh7a404_stop_hc(struct platform_device *dev)
{
printk(KERN_DEBUG __FILE__
": stopping LH7A404 OHCI USB Controller\n");
CSC_PWRCNT &= ~CSC_PWRCNT_USBH_EN; /* Disable clock */
}
/*-------------------------------------------------------------------------*/
/* configure so an HC device and id are always provided */
/* always called with process context; sleeping is OK */
/**
* usb_hcd_lh7a404_probe - initialize LH7A404-based HCDs
* Context: !in_interrupt()
*
* Allocates basic resources for this USB host controller, and
* then invokes the start() method for the HCD associated with it
* through the hotplug entry's driver_data.
*
*/
int usb_hcd_lh7a404_probe (const struct hc_driver *driver,
struct platform_device *dev)
{
int retval;
struct usb_hcd *hcd;
if (dev->resource[1].flags != IORESOURCE_IRQ) {
pr_debug("resource[1] is not IORESOURCE_IRQ");
return -ENOMEM;
}
hcd = usb_create_hcd(driver, &dev->dev, "lh7a404");
if (!hcd)
return -ENOMEM;
hcd->rsrc_start = dev->resource[0].start;
hcd->rsrc_len = dev->resource[0].end - dev->resource[0].start + 1;
if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
pr_debug("request_mem_region failed");
retval = -EBUSY;
goto err1;
}
hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
if (!hcd->regs) {
pr_debug("ioremap failed");
retval = -ENOMEM;
goto err2;
}
lh7a404_start_hc(dev);
ohci_hcd_init(hcd_to_ohci(hcd));
retval = usb_add_hcd(hcd, dev->resource[1].start, SA_INTERRUPT);
if (retval == 0)
return retval;
lh7a404_stop_hc(dev);
iounmap(hcd->regs);
err2:
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
err1:
usb_put_hcd(hcd);
return retval;
}
/* may be called without controller electrically present */
/* may be called with controller, bus, and devices active */
/**
* usb_hcd_lh7a404_remove - shutdown processing for LH7A404-based HCDs
* @dev: USB Host Controller being removed
* Context: !in_interrupt()
*
* Reverses the effect of usb_hcd_lh7a404_probe(), first invoking
* the HCD's stop() method. It is always called from a thread
* context, normally "rmmod", "apmd", or something similar.
*
*/
void usb_hcd_lh7a404_remove (struct usb_hcd *hcd, struct platform_device *dev)
{
usb_remove_hcd(hcd);
lh7a404_stop_hc(dev);
iounmap(hcd->regs);
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
usb_put_hcd(hcd);
}
/*-------------------------------------------------------------------------*/
static int __devinit
ohci_lh7a404_start (struct usb_hcd *hcd)
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
int ret;
ohci_dbg (ohci, "ohci_lh7a404_start, ohci:%p", ohci);
if ((ret = ohci_init(ohci)) < 0)
return ret;
if ((ret = ohci_run (ohci)) < 0) {
err ("can't start %s", hcd->self.bus_name);
ohci_stop (hcd);
return ret;
}
return 0;
}
/*-------------------------------------------------------------------------*/
static const struct hc_driver ohci_lh7a404_hc_driver = {
.description = hcd_name,
.product_desc = "LH7A404 OHCI",
.hcd_priv_size = sizeof(struct ohci_hcd),
/*
* generic hardware linkage
*/
.irq = ohci_irq,
.flags = HCD_USB11 | HCD_MEMORY,
/*
* basic lifecycle operations
*/
.start = ohci_lh7a404_start,
#ifdef CONFIG_PM
/* suspend: ohci_lh7a404_suspend, -- tbd */
/* resume: ohci_lh7a404_resume, -- tbd */
#endif /*CONFIG_PM*/
.stop = ohci_stop,
/*
* managing i/o requests and associated device resources
*/
.urb_enqueue = ohci_urb_enqueue,
.urb_dequeue = ohci_urb_dequeue,
.endpoint_disable = ohci_endpoint_disable,
/*
* scheduling support
*/
.get_frame_number = ohci_get_frame,
/*
* root hub support
*/
.hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control,
};
/*-------------------------------------------------------------------------*/
static int ohci_hcd_lh7a404_drv_probe(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
int ret;
pr_debug ("In ohci_hcd_lh7a404_drv_probe");
if (usb_disabled())
return -ENODEV;
ret = usb_hcd_lh7a404_probe(&ohci_lh7a404_hc_driver, pdev);
return ret;
}
static int ohci_hcd_lh7a404_drv_remove(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct usb_hcd *hcd = dev_get_drvdata(dev);
usb_hcd_lh7a404_remove(hcd, pdev);
return 0;
}
/*TBD*/
/*static int ohci_hcd_lh7a404_drv_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct usb_hcd *hcd = dev_get_drvdata(dev);
return 0;
}
static int ohci_hcd_lh7a404_drv_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct usb_hcd *hcd = dev_get_drvdata(dev);
return 0;
}
*/
static struct device_driver ohci_hcd_lh7a404_driver = {
.name = "lh7a404-ohci",
.bus = &platform_bus_type,
.probe = ohci_hcd_lh7a404_drv_probe,
.remove = ohci_hcd_lh7a404_drv_remove,
/*.suspend = ohci_hcd_lh7a404_drv_suspend, */
/*.resume = ohci_hcd_lh7a404_drv_resume, */
};
static int __init ohci_hcd_lh7a404_init (void)
{
pr_debug (DRIVER_INFO " (LH7A404)");
pr_debug ("block sizes: ed %d td %d\n",
sizeof (struct ed), sizeof (struct td));
return driver_register(&ohci_hcd_lh7a404_driver);
}
static void __exit ohci_hcd_lh7a404_cleanup (void)
{
driver_unregister(&ohci_hcd_lh7a404_driver);
}
module_init (ohci_hcd_lh7a404_init);
module_exit (ohci_hcd_lh7a404_cleanup);

139
drivers/usb/host/ohci-mem.c 一般檔案
查看文件

@@ -0,0 +1,139 @@
/*
* OHCI HCD (Host Controller Driver) for USB.
*
* (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
* (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
*
* This file is licenced under the GPL.
*/
/*-------------------------------------------------------------------------*/
/*
* 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 dma_pool or dma_alloc_coherent
* - driver buffers, read/written by HC ... the hcd glue or the
* device driver provides us with dma addresses
*
* There's also PCI "register" data, which is memory mapped.
* No memory seen by this driver is pagable.
*/
/*-------------------------------------------------------------------------*/
static void ohci_hcd_init (struct ohci_hcd *ohci)
{
ohci->next_statechange = jiffies;
spin_lock_init (&ohci->lock);
INIT_LIST_HEAD (&ohci->pending);
INIT_WORK (&ohci->rh_resume, ohci_rh_resume, ohci_to_hcd(ohci));
}
/*-------------------------------------------------------------------------*/
static int ohci_mem_init (struct ohci_hcd *ohci)
{
ohci->td_cache = dma_pool_create ("ohci_td",
ohci_to_hcd(ohci)->self.controller,
sizeof (struct td),
32 /* byte alignment */,
0 /* no page-crossing issues */);
if (!ohci->td_cache)
return -ENOMEM;
ohci->ed_cache = dma_pool_create ("ohci_ed",
ohci_to_hcd(ohci)->self.controller,
sizeof (struct ed),
16 /* byte alignment */,
0 /* no page-crossing issues */);
if (!ohci->ed_cache) {
dma_pool_destroy (ohci->td_cache);
return -ENOMEM;
}
return 0;
}
static void ohci_mem_cleanup (struct ohci_hcd *ohci)
{
if (ohci->td_cache) {
dma_pool_destroy (ohci->td_cache);
ohci->td_cache = NULL;
}
if (ohci->ed_cache) {
dma_pool_destroy (ohci->ed_cache);
ohci->ed_cache = NULL;
}
}
/*-------------------------------------------------------------------------*/
/* ohci "done list" processing needs this mapping */
static inline struct td *
dma_to_td (struct ohci_hcd *hc, dma_addr_t td_dma)
{
struct td *td;
td_dma &= TD_MASK;
td = hc->td_hash [TD_HASH_FUNC(td_dma)];
while (td && td->td_dma != td_dma)
td = td->td_hash;
return td;
}
/* TDs ... */
static struct td *
td_alloc (struct ohci_hcd *hc, int mem_flags)
{
dma_addr_t dma;
struct td *td;
td = dma_pool_alloc (hc->td_cache, mem_flags, &dma);
if (td) {
/* in case hc fetches it, make it look dead */
memset (td, 0, sizeof *td);
td->hwNextTD = cpu_to_hc32 (hc, dma);
td->td_dma = dma;
/* hashed in td_fill */
}
return td;
}
static void
td_free (struct ohci_hcd *hc, struct td *td)
{
struct td **prev = &hc->td_hash [TD_HASH_FUNC (td->td_dma)];
while (*prev && *prev != td)
prev = &(*prev)->td_hash;
if (*prev)
*prev = td->td_hash;
else if ((td->hwINFO & cpu_to_hc32(hc, TD_DONE)) != 0)
ohci_dbg (hc, "no hash for td %p\n", td);
dma_pool_free (hc->td_cache, td, td->td_dma);
}
/*-------------------------------------------------------------------------*/
/* EDs ... */
static struct ed *
ed_alloc (struct ohci_hcd *hc, int mem_flags)
{
dma_addr_t dma;
struct ed *ed;
ed = dma_pool_alloc (hc->ed_cache, mem_flags, &dma);
if (ed) {
memset (ed, 0, sizeof (*ed));
INIT_LIST_HEAD (&ed->td_list);
ed->dma = dma;
}
return ed;
}
static void
ed_free (struct ohci_hcd *hc, struct ed *ed)
{
dma_pool_free (hc->ed_cache, ed, ed->dma);
}

560
drivers/usb/host/ohci-omap.c 一般檔案
查看文件

@@ -0,0 +1,560 @@
/*
* OHCI HCD (Host Controller Driver) for USB.
*
* (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
* (C) Copyright 2000-2005 David Brownell
* (C) Copyright 2002 Hewlett-Packard Company
*
* OMAP Bus Glue
*
* Modified for OMAP by Tony Lindgren <tony@atomide.com>
* Based on the 2.4 OMAP OHCI driver originally done by MontaVista Software Inc.
* and on ohci-sa1111.c by Christopher Hoover <ch@hpl.hp.com>
*
* This file is licenced under the GPL.
*/
#include <asm/hardware.h>
#include <asm/io.h>
#include <asm/mach-types.h>
#include <asm/arch/hardware.h>
#include <asm/arch/mux.h>
#include <asm/arch/irqs.h>
#include <asm/arch/gpio.h>
#include <asm/arch/fpga.h>
#include <asm/arch/usb.h>
#include <asm/hardware/clock.h>
/* OMAP-1510 OHCI has its own MMU for DMA */
#define OMAP1510_LB_MEMSIZE 32 /* Should be same as SDRAM size */
#define OMAP1510_LB_CLOCK_DIV 0xfffec10c
#define OMAP1510_LB_MMU_CTL 0xfffec208
#define OMAP1510_LB_MMU_LCK 0xfffec224
#define OMAP1510_LB_MMU_LD_TLB 0xfffec228
#define OMAP1510_LB_MMU_CAM_H 0xfffec22c
#define OMAP1510_LB_MMU_CAM_L 0xfffec230
#define OMAP1510_LB_MMU_RAM_H 0xfffec234
#define OMAP1510_LB_MMU_RAM_L 0xfffec238
#ifndef CONFIG_ARCH_OMAP
#error "This file is OMAP bus glue. CONFIG_OMAP must be defined."
#endif
#ifdef CONFIG_TPS65010
#include <asm/arch/tps65010.h>
#else
#define LOW 0
#define HIGH 1
#define GPIO1 1
static inline int tps65010_set_gpio_out_value(unsigned gpio, unsigned value)
{
return 0;
}
#endif
extern int usb_disabled(void);
extern int ocpi_enable(void);
static struct clk *usb_host_ck;
static void omap_ohci_clock_power(int on)
{
if (on) {
clk_enable(usb_host_ck);
/* guesstimate for T5 == 1x 32K clock + APLL lock time */
udelay(100);
} else {
clk_disable(usb_host_ck);
}
}
/*
* Board specific gang-switched transceiver power on/off.
* NOTE: OSK supplies power from DC, not battery.
*/
static int omap_ohci_transceiver_power(int on)
{
if (on) {
if (machine_is_omap_innovator() && cpu_is_omap1510())
fpga_write(fpga_read(INNOVATOR_FPGA_CAM_USB_CONTROL)
| ((1 << 5/*usb1*/) | (1 << 3/*usb2*/)),
INNOVATOR_FPGA_CAM_USB_CONTROL);
else if (machine_is_omap_osk())
tps65010_set_gpio_out_value(GPIO1, LOW);
} else {
if (machine_is_omap_innovator() && cpu_is_omap1510())
fpga_write(fpga_read(INNOVATOR_FPGA_CAM_USB_CONTROL)
& ~((1 << 5/*usb1*/) | (1 << 3/*usb2*/)),
INNOVATOR_FPGA_CAM_USB_CONTROL);
else if (machine_is_omap_osk())
tps65010_set_gpio_out_value(GPIO1, HIGH);
}
return 0;
}
/*
* OMAP-1510 specific Local Bus clock on/off
*/
static int omap_1510_local_bus_power(int on)
{
if (on) {
omap_writel((1 << 1) | (1 << 0), OMAP1510_LB_MMU_CTL);
udelay(200);
} else {
omap_writel(0, OMAP1510_LB_MMU_CTL);
}
return 0;
}
/*
* OMAP-1510 specific Local Bus initialization
* NOTE: This assumes 32MB memory size in OMAP1510LB_MEMSIZE.
* See also arch/mach-omap/memory.h for __virt_to_dma() and
* __dma_to_virt() which need to match with the physical
* Local Bus address below.
*/
static int omap_1510_local_bus_init(void)
{
unsigned int tlb;
unsigned long lbaddr, physaddr;
omap_writel((omap_readl(OMAP1510_LB_CLOCK_DIV) & 0xfffffff8) | 0x4,
OMAP1510_LB_CLOCK_DIV);
/* Configure the Local Bus MMU table */
for (tlb = 0; tlb < OMAP1510_LB_MEMSIZE; tlb++) {
lbaddr = tlb * 0x00100000 + OMAP1510_LB_OFFSET;
physaddr = tlb * 0x00100000 + PHYS_OFFSET;
omap_writel((lbaddr & 0x0fffffff) >> 22, OMAP1510_LB_MMU_CAM_H);
omap_writel(((lbaddr & 0x003ffc00) >> 6) | 0xc,
OMAP1510_LB_MMU_CAM_L);
omap_writel(physaddr >> 16, OMAP1510_LB_MMU_RAM_H);
omap_writel((physaddr & 0x0000fc00) | 0x300, OMAP1510_LB_MMU_RAM_L);
omap_writel(tlb << 4, OMAP1510_LB_MMU_LCK);
omap_writel(0x1, OMAP1510_LB_MMU_LD_TLB);
}
/* Enable the walking table */
omap_writel(omap_readl(OMAP1510_LB_MMU_CTL) | (1 << 3), OMAP1510_LB_MMU_CTL);
udelay(200);
return 0;
}
#ifdef CONFIG_USB_OTG
static void start_hnp(struct ohci_hcd *ohci)
{
const unsigned port = ohci_to_hcd(ohci)->self.otg_port - 1;
unsigned long flags;
otg_start_hnp(ohci->transceiver);
local_irq_save(flags);
ohci->transceiver->state = OTG_STATE_A_SUSPEND;
writel (RH_PS_PSS, &ohci->regs->roothub.portstatus [port]);
OTG_CTRL_REG &= ~OTG_A_BUSREQ;
local_irq_restore(flags);
}
#endif
/*-------------------------------------------------------------------------*/
static int omap_start_hc(struct ohci_hcd *ohci, struct platform_device *pdev)
{
struct omap_usb_config *config = pdev->dev.platform_data;
int need_transceiver = (config->otg != 0);
int ret;
dev_dbg(&pdev->dev, "starting USB Controller\n");
if (config->otg) {
ohci_to_hcd(ohci)->self.otg_port = config->otg;
/* default/minimum OTG power budget: 8 mA */
ohci->power_budget = 8;
}
/* boards can use OTG transceivers in non-OTG modes */
need_transceiver = need_transceiver
|| machine_is_omap_h2() || machine_is_omap_h3();
if (cpu_is_omap16xx())
ocpi_enable();
#ifdef CONFIG_ARCH_OMAP_OTG
if (need_transceiver) {
ohci->transceiver = otg_get_transceiver();
if (ohci->transceiver) {
int status = otg_set_host(ohci->transceiver,
&ohci_to_hcd(ohci)->self);
dev_dbg(&pdev->dev, "init %s transceiver, status %d\n",
ohci->transceiver->label, status);
if (status) {
if (ohci->transceiver)
put_device(ohci->transceiver->dev);
return status;
}
} else {
dev_err(&pdev->dev, "can't find transceiver\n");
return -ENODEV;
}
}
#endif
omap_ohci_clock_power(1);
if (cpu_is_omap1510()) {
omap_1510_local_bus_power(1);
omap_1510_local_bus_init();
}
if ((ret = ohci_init(ohci)) < 0)
return ret;
/* board-specific power switching and overcurrent support */
if (machine_is_omap_osk() || machine_is_omap_innovator()) {
u32 rh = roothub_a (ohci);
/* power switching (ganged by default) */
rh &= ~RH_A_NPS;
/* TPS2045 switch for internal transceiver (port 1) */
if (machine_is_omap_osk()) {
ohci->power_budget = 250;
rh &= ~RH_A_NOCP;
/* gpio9 for overcurrent detction */
omap_cfg_reg(W8_1610_GPIO9);
omap_request_gpio(9);
omap_set_gpio_direction(9, 1 /* IN */);
/* for paranoia's sake: disable USB.PUEN */
omap_cfg_reg(W4_USB_HIGHZ);
}
ohci_writel(ohci, rh, &ohci->regs->roothub.a);
distrust_firmware = 0;
}
/* FIXME khubd hub requests should manage power switching */
omap_ohci_transceiver_power(1);
/* board init will have already handled HMC and mux setup.
* any external transceiver should already be initialized
* too, so all configured ports use the right signaling now.
*/
return 0;
}
static void omap_stop_hc(struct platform_device *pdev)
{
dev_dbg(&pdev->dev, "stopping USB Controller\n");
omap_ohci_clock_power(0);
}
/*-------------------------------------------------------------------------*/
void usb_hcd_omap_remove (struct usb_hcd *, struct platform_device *);
/* configure so an HC device and id are always provided */
/* always called with process context; sleeping is OK */
/**
* usb_hcd_omap_probe - initialize OMAP-based HCDs
* Context: !in_interrupt()
*
* Allocates basic resources for this USB host controller, and
* then invokes the start() method for the HCD associated with it
* through the hotplug entry's driver_data.
*/
int usb_hcd_omap_probe (const struct hc_driver *driver,
struct platform_device *pdev)
{
int retval;
struct usb_hcd *hcd = 0;
struct ohci_hcd *ohci;
if (pdev->num_resources != 2) {
printk(KERN_ERR "hcd probe: invalid num_resources: %i\n",
pdev->num_resources);
return -ENODEV;
}
if (pdev->resource[0].flags != IORESOURCE_MEM
|| pdev->resource[1].flags != IORESOURCE_IRQ) {
printk(KERN_ERR "hcd probe: invalid resource type\n");
return -ENODEV;
}
usb_host_ck = clk_get(0, "usb_hhc_ck");
if (IS_ERR(usb_host_ck))
return PTR_ERR(usb_host_ck);
hcd = usb_create_hcd (driver, &pdev->dev, pdev->dev.bus_id);
if (!hcd) {
retval = -ENOMEM;
goto err0;
}
hcd->rsrc_start = pdev->resource[0].start;
hcd->rsrc_len = pdev->resource[0].end - pdev->resource[0].start + 1;
if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
dev_dbg(&pdev->dev, "request_mem_region failed\n");
retval = -EBUSY;
goto err1;
}
hcd->regs = (void __iomem *) (int) IO_ADDRESS(hcd->rsrc_start);
ohci = hcd_to_ohci(hcd);
ohci_hcd_init(ohci);
retval = omap_start_hc(ohci, pdev);
if (retval < 0)
goto err2;
retval = usb_add_hcd(hcd, platform_get_irq(pdev, 0), SA_INTERRUPT);
if (retval == 0)
return retval;
omap_stop_hc(pdev);
err2:
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
err1:
usb_put_hcd(hcd);
err0:
clk_put(usb_host_ck);
return retval;
}
/* may be called with controller, bus, and devices active */
/**
* usb_hcd_omap_remove - shutdown processing for OMAP-based HCDs
* @dev: USB Host Controller being removed
* Context: !in_interrupt()
*
* Reverses the effect of usb_hcd_omap_probe(), first invoking
* the HCD's stop() method. It is always called from a thread
* context, normally "rmmod", "apmd", or something similar.
*
*/
void usb_hcd_omap_remove (struct usb_hcd *hcd, struct platform_device *pdev)
{
usb_remove_hcd(hcd);
if (machine_is_omap_osk())
omap_free_gpio(9);
omap_stop_hc(pdev);
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
usb_put_hcd(hcd);
clk_put(usb_host_ck);
}
/*-------------------------------------------------------------------------*/
static int __devinit
ohci_omap_start (struct usb_hcd *hcd)
{
struct omap_usb_config *config;
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
int ret;
config = hcd->self.controller->platform_data;
if (config->otg || config->rwc)
writel(OHCI_CTRL_RWC, &ohci->regs->control);
if ((ret = ohci_run (ohci)) < 0) {
dev_err(hcd->self.controller, "can't start\n");
ohci_stop (hcd);
return ret;
}
return 0;
}
/*-------------------------------------------------------------------------*/
static const struct hc_driver ohci_omap_hc_driver = {
.description = hcd_name,
.product_desc = "OMAP OHCI",
.hcd_priv_size = sizeof(struct ohci_hcd),
/*
* generic hardware linkage
*/
.irq = ohci_irq,
.flags = HCD_USB11 | HCD_MEMORY,
/*
* basic lifecycle operations
*/
.start = ohci_omap_start,
.stop = ohci_stop,
/*
* managing i/o requests and associated device resources
*/
.urb_enqueue = ohci_urb_enqueue,
.urb_dequeue = ohci_urb_dequeue,
.endpoint_disable = ohci_endpoint_disable,
/*
* scheduling support
*/
.get_frame_number = ohci_get_frame,
/*
* root hub support
*/
.hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control,
#ifdef CONFIG_USB_SUSPEND
.hub_suspend = ohci_hub_suspend,
.hub_resume = ohci_hub_resume,
#endif
.start_port_reset = ohci_start_port_reset,
};
/*-------------------------------------------------------------------------*/
static int ohci_hcd_omap_drv_probe(struct device *dev)
{
return usb_hcd_omap_probe(&ohci_omap_hc_driver,
to_platform_device(dev));
}
static int ohci_hcd_omap_drv_remove(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct usb_hcd *hcd = dev_get_drvdata(dev);
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
usb_hcd_omap_remove(hcd, pdev);
if (ohci->transceiver) {
(void) otg_set_host(ohci->transceiver, 0);
put_device(ohci->transceiver->dev);
}
dev_set_drvdata(dev, NULL);
return 0;
}
/*-------------------------------------------------------------------------*/
#ifdef CONFIG_PM
/* states match PCI usage, always suspending the root hub except that
* 4 ~= D3cold (ACPI D3) with clock off (resume sees reset).
*/
static int ohci_omap_suspend(struct device *dev, u32 state, u32 level)
{
struct ohci_hcd *ohci = hcd_to_ohci(dev_get_drvdata(dev));
int status = -EINVAL;
if (level != SUSPEND_POWER_DOWN)
return 0;
if (state <= dev->power.power_state)
return 0;
dev_dbg(dev, "suspend to %d\n", state);
down(&ohci_to_hcd(ohci)->self.root_hub->serialize);
status = ohci_hub_suspend(ohci_to_hcd(ohci));
if (status == 0) {
if (state >= 4) {
omap_ohci_clock_power(0);
ohci_to_hcd(ohci)->self.root_hub->state =
USB_STATE_SUSPENDED;
state = 4;
}
ohci_to_hcd(ohci)->state = HC_STATE_SUSPENDED;
dev->power.power_state = state;
}
up(&ohci_to_hcd(ohci)->self.root_hub->serialize);
return status;
}
static int ohci_omap_resume(struct device *dev, u32 level)
{
struct ohci_hcd *ohci = hcd_to_ohci(dev_get_drvdata(dev));
int status = 0;
if (level != RESUME_POWER_ON)
return 0;
switch (dev->power.power_state) {
case 0:
break;
case 4:
if (time_before(jiffies, ohci->next_statechange))
msleep(5);
ohci->next_statechange = jiffies;
omap_ohci_clock_power(1);
/* FALLTHROUGH */
default:
dev_dbg(dev, "resume from %d\n", dev->power.power_state);
#ifdef CONFIG_USB_SUSPEND
/* get extra cleanup even if remote wakeup isn't in use */
status = usb_resume_device(ohci_to_hcd(ohci)->self.root_hub);
#else
down(&ohci_to_hcd(ohci)->self.root_hub->serialize);
status = ohci_hub_resume(ohci_to_hcd(ohci));
up(&ohci_to_hcd(ohci)->self.root_hub->serialize);
#endif
if (status == 0)
dev->power.power_state = 0;
break;
}
return status;
}
#endif
/*-------------------------------------------------------------------------*/
/*
* Driver definition to register with the OMAP bus
*/
static struct device_driver ohci_hcd_omap_driver = {
.name = "ohci",
.bus = &platform_bus_type,
.probe = ohci_hcd_omap_drv_probe,
.remove = ohci_hcd_omap_drv_remove,
#ifdef CONFIG_PM
.suspend = ohci_omap_suspend,
.resume = ohci_omap_resume,
#endif
};
static int __init ohci_hcd_omap_init (void)
{
printk (KERN_DEBUG "%s: " DRIVER_INFO " (OMAP)\n", hcd_name);
if (usb_disabled())
return -ENODEV;
pr_debug("%s: block sizes: ed %Zd td %Zd\n", hcd_name,
sizeof (struct ed), sizeof (struct td));
return driver_register(&ohci_hcd_omap_driver);
}
static void __exit ohci_hcd_omap_cleanup (void)
{
driver_unregister(&ohci_hcd_omap_driver);
}
module_init (ohci_hcd_omap_init);
module_exit (ohci_hcd_omap_cleanup);

264
drivers/usb/host/ohci-pci.c 一般檔案
查看文件

@@ -0,0 +1,264 @@
/*
* OHCI HCD (Host Controller Driver) for USB.
*
* (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
* (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
*
* [ Initialisation is based on Linus' ]
* [ uhci code and gregs ohci fragments ]
* [ (C) Copyright 1999 Linus Torvalds ]
* [ (C) Copyright 1999 Gregory P. Smith]
*
* PCI Bus Glue
*
* This file is licenced under the GPL.
*/
#ifdef CONFIG_PMAC_PBOOK
#include <asm/machdep.h>
#include <asm/pmac_feature.h>
#include <asm/pci-bridge.h>
#include <asm/prom.h>
#ifndef CONFIG_PM
# define CONFIG_PM
#endif
#endif
#ifndef CONFIG_PCI
#error "This file is PCI bus glue. CONFIG_PCI must be defined."
#endif
/*-------------------------------------------------------------------------*/
static int
ohci_pci_reset (struct usb_hcd *hcd)
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
ohci_hcd_init (ohci);
return ohci_init (ohci);
}
static int __devinit
ohci_pci_start (struct usb_hcd *hcd)
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
int ret;
if(hcd->self.controller && hcd->self.controller->bus == &pci_bus_type) {
struct pci_dev *pdev = to_pci_dev(hcd->self.controller);
/* AMD 756, for most chips (early revs), corrupts register
* values on read ... so enable the vendor workaround.
*/
if (pdev->vendor == PCI_VENDOR_ID_AMD
&& pdev->device == 0x740c) {
ohci->flags = OHCI_QUIRK_AMD756;
ohci_info (ohci, "AMD756 erratum 4 workaround\n");
// also somewhat erratum 10 (suspend/resume issues)
}
/* FIXME for some of the early AMD 760 southbridges, OHCI
* won't work at all. blacklist them.
*/
/* Apple's OHCI driver has a lot of bizarre workarounds
* for this chip. Evidently control and bulk lists
* can get confused. (B&W G3 models, and ...)
*/
else if (pdev->vendor == PCI_VENDOR_ID_OPTI
&& pdev->device == 0xc861) {
ohci_info (ohci,
"WARNING: OPTi workarounds unavailable\n");
}
/* Check for NSC87560. We have to look at the bridge (fn1) to
* identify the USB (fn2). This quirk might apply to more or
* even all NSC stuff.
*/
else if (pdev->vendor == PCI_VENDOR_ID_NS) {
struct pci_dev *b;
b = pci_find_slot (pdev->bus->number,
PCI_DEVFN (PCI_SLOT (pdev->devfn), 1));
if (b && b->device == PCI_DEVICE_ID_NS_87560_LIO
&& b->vendor == PCI_VENDOR_ID_NS) {
ohci->flags |= OHCI_QUIRK_SUPERIO;
ohci_info (ohci, "Using NSC SuperIO setup\n");
}
}
}
/* NOTE: there may have already been a first reset, to
* keep bios/smm irqs from making trouble
*/
if ((ret = ohci_run (ohci)) < 0) {
ohci_err (ohci, "can't start\n");
ohci_stop (hcd);
return ret;
}
return 0;
}
#ifdef CONFIG_PM
static int ohci_pci_suspend (struct usb_hcd *hcd, u32 state)
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
/* suspend root hub, hoping it keeps power during suspend */
if (time_before (jiffies, ohci->next_statechange))
msleep (100);
#ifdef CONFIG_USB_SUSPEND
(void) usb_suspend_device (hcd->self.root_hub, state);
#else
usb_lock_device (hcd->self.root_hub);
(void) ohci_hub_suspend (hcd);
usb_unlock_device (hcd->self.root_hub);
#endif
/* let things settle down a bit */
msleep (100);
#ifdef CONFIG_PMAC_PBOOK
if (_machine == _MACH_Pmac) {
struct device_node *of_node;
/* Disable USB PAD & cell clock */
of_node = pci_device_to_OF_node (to_pci_dev(hcd->self.controller));
if (of_node)
pmac_call_feature(PMAC_FTR_USB_ENABLE, of_node, 0, 0);
}
#endif /* CONFIG_PMAC_PBOOK */
return 0;
}
static int ohci_pci_resume (struct usb_hcd *hcd)
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
int retval = 0;
#ifdef CONFIG_PMAC_PBOOK
if (_machine == _MACH_Pmac) {
struct device_node *of_node;
/* Re-enable USB PAD & cell clock */
of_node = pci_device_to_OF_node (to_pci_dev(hcd->self.controller));
if (of_node)
pmac_call_feature (PMAC_FTR_USB_ENABLE, of_node, 0, 1);
}
#endif /* CONFIG_PMAC_PBOOK */
/* resume root hub */
if (time_before (jiffies, ohci->next_statechange))
msleep (100);
#ifdef CONFIG_USB_SUSPEND
/* get extra cleanup even if remote wakeup isn't in use */
retval = usb_resume_device (hcd->self.root_hub);
#else
usb_lock_device (hcd->self.root_hub);
retval = ohci_hub_resume (hcd);
usb_unlock_device (hcd->self.root_hub);
#endif
return retval;
}
#endif /* CONFIG_PM */
/*-------------------------------------------------------------------------*/
static const struct hc_driver ohci_pci_hc_driver = {
.description = hcd_name,
.product_desc = "OHCI Host Controller",
.hcd_priv_size = sizeof(struct ohci_hcd),
/*
* generic hardware linkage
*/
.irq = ohci_irq,
.flags = HCD_MEMORY | HCD_USB11,
/*
* basic lifecycle operations
*/
.reset = ohci_pci_reset,
.start = ohci_pci_start,
#ifdef CONFIG_PM
.suspend = ohci_pci_suspend,
.resume = ohci_pci_resume,
#endif
.stop = ohci_stop,
/*
* managing i/o requests and associated device resources
*/
.urb_enqueue = ohci_urb_enqueue,
.urb_dequeue = ohci_urb_dequeue,
.endpoint_disable = ohci_endpoint_disable,
/*
* scheduling support
*/
.get_frame_number = ohci_get_frame,
/*
* root hub support
*/
.hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control,
#ifdef CONFIG_USB_SUSPEND
.hub_suspend = ohci_hub_suspend,
.hub_resume = ohci_hub_resume,
#endif
.start_port_reset = ohci_start_port_reset,
};
/*-------------------------------------------------------------------------*/
static const struct pci_device_id pci_ids [] = { {
/* handle any USB OHCI controller */
PCI_DEVICE_CLASS((PCI_CLASS_SERIAL_USB << 8) | 0x10, ~0),
.driver_data = (unsigned long) &ohci_pci_hc_driver,
}, { /* end: all zeroes */ }
};
MODULE_DEVICE_TABLE (pci, pci_ids);
/* pci driver glue; this is a "new style" PCI driver module */
static struct pci_driver ohci_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
};
static int __init ohci_hcd_pci_init (void)
{
printk (KERN_DEBUG "%s: " DRIVER_INFO " (PCI)\n", hcd_name);
if (usb_disabled())
return -ENODEV;
pr_debug ("%s: block sizes: ed %Zd td %Zd\n", hcd_name,
sizeof (struct ed), sizeof (struct td));
return pci_register_driver (&ohci_pci_driver);
}
module_init (ohci_hcd_pci_init);
/*-------------------------------------------------------------------------*/
static void __exit ohci_hcd_pci_cleanup (void)
{
pci_unregister_driver (&ohci_pci_driver);
}
module_exit (ohci_hcd_pci_cleanup);

查看文件

@@ -0,0 +1,234 @@
/*
* OHCI HCD (Host Controller Driver) for USB.
*
* (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
* (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
* (C) Copyright 2002 Hewlett-Packard Company
* (C) Copyright 2003-2005 MontaVista Software Inc.
*
* Bus Glue for PPC On-Chip OHCI driver
* Tested on Freescale MPC5200 and IBM STB04xxx
*
* Modified by Dale Farnsworth <dale@farnsworth.org> from ohci-sa1111.c
*
* This file is licenced under the GPL.
*/
#include <asm/usb.h>
/* configure so an HC device and id are always provided */
/* always called with process context; sleeping is OK */
/**
* usb_hcd_ppc_soc_probe - initialize On-Chip HCDs
* Context: !in_interrupt()
*
* Allocates basic resources for this USB host controller, and
* then invokes the start() method for the HCD associated with it
* through the hotplug entry's driver_data.
*
* Store this function in the HCD's struct pci_driver as probe().
*/
static int usb_hcd_ppc_soc_probe(const struct hc_driver *driver,
struct platform_device *pdev)
{
int retval;
struct usb_hcd *hcd;
struct ohci_hcd *ohci;
struct resource *res;
int irq;
struct usb_hcd_platform_data *pd = pdev->dev.platform_data;
pr_debug("initializing PPC-SOC USB Controller\n");
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (!res) {
pr_debug(__FILE__ ": no irq\n");
return -ENODEV;
}
irq = res->start;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
pr_debug(__FILE__ ": no reg addr\n");
return -ENODEV;
}
hcd = usb_create_hcd(driver, &pdev->dev, "PPC-SOC USB");
if (!hcd)
return -ENOMEM;
hcd->rsrc_start = res->start;
hcd->rsrc_len = res->end - res->start + 1;
if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
pr_debug(__FILE__ ": request_mem_region failed\n");
retval = -EBUSY;
goto err1;
}
hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
if (!hcd->regs) {
pr_debug(__FILE__ ": ioremap failed\n");
retval = -ENOMEM;
goto err2;
}
if (pd->start && (retval = pd->start(pdev)))
goto err3;
ohci = hcd_to_ohci(hcd);
ohci->flags |= OHCI_BIG_ENDIAN;
ohci_hcd_init(ohci);
retval = usb_add_hcd(hcd, irq, SA_INTERRUPT);
if (retval == 0)
return retval;
pr_debug("Removing PPC-SOC USB Controller\n");
if (pd && pd->stop)
pd->stop(pdev);
err3:
iounmap(hcd->regs);
err2:
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
err1:
usb_put_hcd(hcd);
return retval;
}
/* may be called without controller electrically present */
/* may be called with controller, bus, and devices active */
/**
* usb_hcd_ppc_soc_remove - shutdown processing for On-Chip HCDs
* @pdev: USB Host Controller being removed
* Context: !in_interrupt()
*
* Reverses the effect of usb_hcd_ppc_soc_probe(), first invoking
* the HCD's stop() method. It is always called from a thread
* context, normally "rmmod", "apmd", or something similar.
*
*/
static void usb_hcd_ppc_soc_remove(struct usb_hcd *hcd,
struct platform_device *pdev)
{
struct usb_hcd_platform_data *pd = pdev->dev.platform_data;
usb_remove_hcd(hcd);
pr_debug("stopping PPC-SOC USB Controller\n");
if (pd && pd->stop)
pd->stop(pdev);
iounmap(hcd->regs);
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
usb_hcd_put(hcd);
}
static int __devinit
ohci_ppc_soc_start(struct usb_hcd *hcd)
{
struct ohci_hcd *ohci = hcd_to_ohci(hcd);
int ret;
if ((ret = ohci_init(ohci)) < 0)
return ret;
if ((ret = ohci_run(ohci)) < 0) {
err("can't start %s", ohci_to_hcd(ohci)->self.bus_name);
ohci_stop(hcd);
return ret;
}
return 0;
}
static const struct hc_driver ohci_ppc_soc_hc_driver = {
.description = hcd_name,
.hcd_priv_size = sizeof(struct ohci_hcd),
/*
* generic hardware linkage
*/
.irq = ohci_irq,
.flags = HCD_USB11 | HCD_MEMORY,
/*
* basic lifecycle operations
*/
.start = ohci_ppc_soc_start,
.stop = ohci_stop,
/*
* managing i/o requests and associated device resources
*/
.urb_enqueue = ohci_urb_enqueue,
.urb_dequeue = ohci_urb_dequeue,
.endpoint_disable = ohci_endpoint_disable,
/*
* scheduling support
*/
.get_frame_number = ohci_get_frame,
/*
* root hub support
*/
.hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control,
#ifdef CONFIG_USB_SUSPEND
.hub_suspend = ohci_hub_suspend,
.hub_resume = ohci_hub_resume,
#endif
.start_port_reset = ohci_start_port_reset,
};
static int ohci_hcd_ppc_soc_drv_probe(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
int ret;
if (usb_disabled())
return -ENODEV;
ret = usb_hcd_ppc_soc_probe(&ohci_ppc_soc_hc_driver, pdev);
return ret;
}
static int ohci_hcd_ppc_soc_drv_remove(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct usb_hcd *hcd = dev_get_drvdata(dev);
usb_hcd_ppc_soc_remove(hcd, pdev);
return 0;
}
static struct device_driver ohci_hcd_ppc_soc_driver = {
.name = "ppc-soc-ohci",
.bus = &platform_bus_type,
.probe = ohci_hcd_ppc_soc_drv_probe,
.remove = ohci_hcd_ppc_soc_drv_remove,
#if defined(CONFIG_USB_SUSPEND) || defined(CONFIG_PM)
/*.suspend = ohci_hcd_ppc_soc_drv_suspend,*/
/*.resume = ohci_hcd_ppc_soc_drv_resume,*/
#endif
};
static int __init ohci_hcd_ppc_soc_init(void)
{
pr_debug(DRIVER_INFO " (PPC SOC)\n");
pr_debug("block sizes: ed %d td %d\n", sizeof(struct ed),
sizeof(struct td));
return driver_register(&ohci_hcd_ppc_soc_driver);
}
static void __exit ohci_hcd_ppc_soc_cleanup(void)
{
driver_unregister(&ohci_hcd_ppc_soc_driver);
}
module_init(ohci_hcd_ppc_soc_init);
module_exit(ohci_hcd_ppc_soc_cleanup);

查看文件

@@ -0,0 +1,383 @@
/*
* OHCI HCD (Host Controller Driver) for USB.
*
* (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
* (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
* (C) Copyright 2002 Hewlett-Packard Company
*
* Bus Glue for pxa27x
*
* Written by Christopher Hoover <ch@hpl.hp.com>
* Based on fragments of previous driver by Russell King et al.
*
* Modified for LH7A404 from ohci-sa1111.c
* by Durgesh Pattamatta <pattamattad@sharpsec.com>
*
* Modified for pxa27x from ohci-lh7a404.c
* by Nick Bane <nick@cecomputing.co.uk> 26-8-2004
*
* This file is licenced under the GPL.
*/
#include <linux/device.h>
#include <asm/mach-types.h>
#include <asm/hardware.h>
#include <asm/arch/pxa-regs.h>
#define PMM_NPS_MODE 1
#define PMM_GLOBAL_MODE 2
#define PMM_PERPORT_MODE 3
#define PXA_UHC_MAX_PORTNUM 3
#define UHCRHPS(x) __REG2( 0x4C000050, (x)<<2 )
static int pxa27x_ohci_pmm_state;
/*
PMM_NPS_MODE -- PMM Non-power switching mode
Ports are powered continuously.
PMM_GLOBAL_MODE -- PMM global switching mode
All ports are powered at the same time.
PMM_PERPORT_MODE -- PMM per port switching mode
Ports are powered individually.
*/
static int pxa27x_ohci_select_pmm( int mode )
{
pxa27x_ohci_pmm_state = mode;
switch ( mode ) {
case PMM_NPS_MODE:
UHCRHDA |= RH_A_NPS;
break;
case PMM_GLOBAL_MODE:
UHCRHDA &= ~(RH_A_NPS & RH_A_PSM);
break;
case PMM_PERPORT_MODE:
UHCRHDA &= ~(RH_A_NPS);
UHCRHDA |= RH_A_PSM;
/* Set port power control mask bits, only 3 ports. */
UHCRHDB |= (0x7<<17);
break;
default:
printk( KERN_ERR
"Invalid mode %d, set to non-power switch mode.\n",
mode );
pxa27x_ohci_pmm_state = PMM_NPS_MODE;
UHCRHDA |= RH_A_NPS;
}
return 0;
}
/*
If you select PMM_PERPORT_MODE, you should set the port power
*/
static int pxa27x_ohci_set_port_power( int port )
{
if ( (pxa27x_ohci_pmm_state==PMM_PERPORT_MODE)
&& (port>0) && (port<PXA_UHC_MAX_PORTNUM) ) {
UHCRHPS(port) |= 0x100;
return 0;
}
return -1;
}
/*
If you select PMM_PERPORT_MODE, you should set the port power
*/
static int pxa27x_ohci_clear_port_power( int port )
{
if ( (pxa27x_ohci_pmm_state==PMM_PERPORT_MODE)
&& (port>0) && (port<PXA_UHC_MAX_PORTNUM) ) {
UHCRHPS(port) |= 0x200;
return 0;
}
return -1;
}
extern int usb_disabled(void);
/*-------------------------------------------------------------------------*/
static void pxa27x_start_hc(struct platform_device *dev)
{
pxa_set_cken(CKEN10_USBHOST, 1);
UHCHR |= UHCHR_FHR;
udelay(11);
UHCHR &= ~UHCHR_FHR;
UHCHR |= UHCHR_FSBIR;
while (UHCHR & UHCHR_FSBIR)
cpu_relax();
/* This could be properly abstracted away through the
device data the day more machines are supported and
their differences can be figured out correctly. */
if (machine_is_mainstone()) {
/* setup Port1 GPIO pin. */
pxa_gpio_mode( 88 | GPIO_ALT_FN_1_IN); /* USBHPWR1 */
pxa_gpio_mode( 89 | GPIO_ALT_FN_2_OUT); /* USBHPEN1 */
/* Set the Power Control Polarity Low and Power Sense
Polarity Low to active low. Supply power to USB ports. */
UHCHR = (UHCHR | UHCHR_PCPL | UHCHR_PSPL) &
~(UHCHR_SSEP1 | UHCHR_SSEP2 | UHCHR_SSEP3 | UHCHR_SSE);
}
UHCHR &= ~UHCHR_SSE;
UHCHIE = (UHCHIE_UPRIE | UHCHIE_RWIE);
}
static void pxa27x_stop_hc(struct platform_device *dev)
{
UHCHR |= UHCHR_FHR;
udelay(11);
UHCHR &= ~UHCHR_FHR;
UHCCOMS |= 1;
udelay(10);
pxa_set_cken(CKEN10_USBHOST, 0);
}
/*-------------------------------------------------------------------------*/
/* configure so an HC device and id are always provided */
/* always called with process context; sleeping is OK */
/**
* usb_hcd_pxa27x_probe - initialize pxa27x-based HCDs
* Context: !in_interrupt()
*
* Allocates basic resources for this USB host controller, and
* then invokes the start() method for the HCD associated with it
* through the hotplug entry's driver_data.
*
*/
int usb_hcd_pxa27x_probe (const struct hc_driver *driver,
struct platform_device *dev)
{
int retval;
struct usb_hcd *hcd;
if (dev->resource[1].flags != IORESOURCE_IRQ) {
pr_debug ("resource[1] is not IORESOURCE_IRQ");
return -ENOMEM;
}
hcd = usb_create_hcd (driver, &dev->dev, "pxa27x");
if (!hcd)
return -ENOMEM;
hcd->rsrc_start = dev->resource[0].start;
hcd->rsrc_len = dev->resource[0].end - dev->resource[0].start + 1;
if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
pr_debug("request_mem_region failed");
retval = -EBUSY;
goto err1;
}
hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
if (!hcd->regs) {
pr_debug("ioremap failed");
retval = -ENOMEM;
goto err2;
}
pxa27x_start_hc(dev);
/* Select Power Management Mode */
pxa27x_ohci_select_pmm( PMM_PERPORT_MODE );
/* If choosing PMM_PERPORT_MODE, we should set the port power before we use it. */
if (pxa27x_ohci_set_port_power(1) < 0)
printk(KERN_ERR "Setting port 1 power failed.\n");
if (pxa27x_ohci_clear_port_power(2) < 0)
printk(KERN_ERR "Setting port 2 power failed.\n");
if (pxa27x_ohci_clear_port_power(3) < 0)
printk(KERN_ERR "Setting port 3 power failed.\n");
ohci_hcd_init(hcd_to_ohci(hcd));
retval = usb_add_hcd(hcd, dev->resource[1].start, SA_INTERRUPT);
if (retval == 0)
return retval;
pxa27x_stop_hc(dev);
iounmap(hcd->regs);
err2:
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
err1:
usb_put_hcd(hcd);
return retval;
}
/* may be called without controller electrically present */
/* may be called with controller, bus, and devices active */
/**
* usb_hcd_pxa27x_remove - shutdown processing for pxa27x-based HCDs
* @dev: USB Host Controller being removed
* Context: !in_interrupt()
*
* Reverses the effect of usb_hcd_pxa27x_probe(), first invoking
* the HCD's stop() method. It is always called from a thread
* context, normally "rmmod", "apmd", or something similar.
*
*/
void usb_hcd_pxa27x_remove (struct usb_hcd *hcd, struct platform_device *dev)
{
usb_remove_hcd(hcd);
pxa27x_stop_hc(dev);
iounmap(hcd->regs);
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
usb_put_hcd(hcd);
}
/*-------------------------------------------------------------------------*/
static int __devinit
ohci_pxa27x_start (struct usb_hcd *hcd)
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
int ret;
ohci_dbg (ohci, "ohci_pxa27x_start, ohci:%p", ohci);
if ((ret = ohci_init(ohci)) < 0)
return ret;
if ((ret = ohci_run (ohci)) < 0) {
err ("can't start %s", hcd->self.bus_name);
ohci_stop (hcd);
return ret;
}
return 0;
}
/*-------------------------------------------------------------------------*/
static const struct hc_driver ohci_pxa27x_hc_driver = {
.description = hcd_name,
.product_desc = "PXA27x OHCI",
.hcd_priv_size = sizeof(struct ohci_hcd),
/*
* generic hardware linkage
*/
.irq = ohci_irq,
.flags = HCD_USB11 | HCD_MEMORY,
/*
* basic lifecycle operations
*/
.start = ohci_pxa27x_start,
.stop = ohci_stop,
/*
* managing i/o requests and associated device resources
*/
.urb_enqueue = ohci_urb_enqueue,
.urb_dequeue = ohci_urb_dequeue,
.endpoint_disable = ohci_endpoint_disable,
/*
* scheduling support
*/
.get_frame_number = ohci_get_frame,
/*
* root hub support
*/
.hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control,
#ifdef CONFIG_USB_SUSPEND
.hub_suspend = ohci_hub_suspend,
.hub_resume = ohci_hub_resume,
#endif
};
/*-------------------------------------------------------------------------*/
static int ohci_hcd_pxa27x_drv_probe(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
int ret;
pr_debug ("In ohci_hcd_pxa27x_drv_probe");
if (usb_disabled())
return -ENODEV;
ret = usb_hcd_pxa27x_probe(&ohci_pxa27x_hc_driver, pdev);
return ret;
}
static int ohci_hcd_pxa27x_drv_remove(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct usb_hcd *hcd = dev_get_drvdata(dev);
usb_hcd_pxa27x_remove(hcd, pdev);
return 0;
}
static int ohci_hcd_pxa27x_drv_suspend(struct device *dev, u32 state, u32 level)
{
// struct platform_device *pdev = to_platform_device(dev);
// struct usb_hcd *hcd = dev_get_drvdata(dev);
printk("%s: not implemented yet\n", __FUNCTION__);
return 0;
}
static int ohci_hcd_pxa27x_drv_resume(struct device *dev, u32 state)
{
// struct platform_device *pdev = to_platform_device(dev);
// struct usb_hcd *hcd = dev_get_drvdata(dev);
printk("%s: not implemented yet\n", __FUNCTION__);
return 0;
}
static struct device_driver ohci_hcd_pxa27x_driver = {
.name = "pxa27x-ohci",
.bus = &platform_bus_type,
.probe = ohci_hcd_pxa27x_drv_probe,
.remove = ohci_hcd_pxa27x_drv_remove,
.suspend = ohci_hcd_pxa27x_drv_suspend,
.resume = ohci_hcd_pxa27x_drv_resume,
};
static int __init ohci_hcd_pxa27x_init (void)
{
pr_debug (DRIVER_INFO " (pxa27x)");
pr_debug ("block sizes: ed %d td %d\n",
sizeof (struct ed), sizeof (struct td));
return driver_register(&ohci_hcd_pxa27x_driver);
}
static void __exit ohci_hcd_pxa27x_cleanup (void)
{
driver_unregister(&ohci_hcd_pxa27x_driver);
}
module_init (ohci_hcd_pxa27x_init);
module_exit (ohci_hcd_pxa27x_cleanup);

1107
drivers/usb/host/ohci-q.c 一般檔案

檔案差異因為檔案過大而無法顯示 載入差異

查看文件

@@ -0,0 +1,289 @@
/*
* OHCI HCD (Host Controller Driver) for USB.
*
* (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
* (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
* (C) Copyright 2002 Hewlett-Packard Company
*
* SA1111 Bus Glue
*
* Written by Christopher Hoover <ch@hpl.hp.com>
* Based on fragments of previous driver by Rusell King et al.
*
* This file is licenced under the GPL.
*/
#include <asm/hardware.h>
#include <asm/mach-types.h>
#include <asm/arch/assabet.h>
#include <asm/arch/badge4.h>
#include <asm/hardware/sa1111.h>
#ifndef CONFIG_SA1111
#error "This file is SA-1111 bus glue. CONFIG_SA1111 must be defined."
#endif
extern int usb_disabled(void);
/*-------------------------------------------------------------------------*/
static void sa1111_start_hc(struct sa1111_dev *dev)
{
unsigned int usb_rst = 0;
printk(KERN_DEBUG __FILE__
": starting SA-1111 OHCI USB Controller\n");
#ifdef CONFIG_SA1100_BADGE4
if (machine_is_badge4()) {
badge4_set_5V(BADGE4_5V_USB, 1);
}
#endif
if (machine_is_xp860() ||
machine_has_neponset() ||
machine_is_pfs168() ||
machine_is_badge4())
usb_rst = USB_RESET_PWRSENSELOW | USB_RESET_PWRCTRLLOW;
/*
* Configure the power sense and control lines. Place the USB
* host controller in reset.
*/
sa1111_writel(usb_rst | USB_RESET_FORCEIFRESET | USB_RESET_FORCEHCRESET,
dev->mapbase + SA1111_USB_RESET);
/*
* Now, carefully enable the USB clock, and take
* the USB host controller out of reset.
*/
sa1111_enable_device(dev);
udelay(11);
sa1111_writel(usb_rst, dev->mapbase + SA1111_USB_RESET);
}
static void sa1111_stop_hc(struct sa1111_dev *dev)
{
unsigned int usb_rst;
printk(KERN_DEBUG __FILE__
": stopping SA-1111 OHCI USB Controller\n");
/*
* Put the USB host controller into reset.
*/
usb_rst = sa1111_readl(dev->mapbase + SA1111_USB_RESET);
sa1111_writel(usb_rst | USB_RESET_FORCEIFRESET | USB_RESET_FORCEHCRESET,
dev->mapbase + SA1111_USB_RESET);
/*
* Stop the USB clock.
*/
sa1111_disable_device(dev);
#ifdef CONFIG_SA1100_BADGE4
if (machine_is_badge4()) {
/* Disable power to the USB bus */
badge4_set_5V(BADGE4_5V_USB, 0);
}
#endif
}
/*-------------------------------------------------------------------------*/
#if 0
static void dump_hci_status(struct usb_hcd *hcd, const char *label)
{
unsigned long status = sa1111_readl(hcd->regs + SA1111_USB_STATUS);
dbg ("%s USB_STATUS = { %s%s%s%s%s}", label,
((status & USB_STATUS_IRQHCIRMTWKUP) ? "IRQHCIRMTWKUP " : ""),
((status & USB_STATUS_IRQHCIBUFFACC) ? "IRQHCIBUFFACC " : ""),
((status & USB_STATUS_NIRQHCIM) ? "" : "IRQHCIM "),
((status & USB_STATUS_NHCIMFCLR) ? "" : "HCIMFCLR "),
((status & USB_STATUS_USBPWRSENSE) ? "USBPWRSENSE " : ""));
}
#endif
/*-------------------------------------------------------------------------*/
/* configure so an HC device and id are always provided */
/* always called with process context; sleeping is OK */
/**
* usb_hcd_sa1111_probe - initialize SA-1111-based HCDs
* Context: !in_interrupt()
*
* Allocates basic resources for this USB host controller, and
* then invokes the start() method for the HCD associated with it
* through the hotplug entry's driver_data.
*
* Store this function in the HCD's struct pci_driver as probe().
*/
int usb_hcd_sa1111_probe (const struct hc_driver *driver,
struct sa1111_dev *dev)
{
struct usb_hcd *hcd;
int retval;
hcd = usb_create_hcd (driver, &dev->dev, "sa1111");
if (!hcd)
return -ENOMEM;
hcd->rsrc_start = dev->res.start;
hcd->rsrc_len = dev->res.end - dev->res.start + 1;
if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
dbg("request_mem_region failed");
retval = -EBUSY;
goto err1;
}
hcd->regs = dev->mapbase;
sa1111_start_hc(dev);
ohci_hcd_init(hcd_to_ohci(hcd));
retval = usb_add_hcd(hcd, dev->irq[1], SA_INTERRUPT);
if (retval == 0)
return retval;
sa1111_stop_hc(dev);
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
err1:
usb_put_hcd(hcd);
return retval;
}
/* may be called without controller electrically present */
/* may be called with controller, bus, and devices active */
/**
* usb_hcd_sa1111_remove - shutdown processing for SA-1111-based HCDs
* @dev: USB Host Controller being removed
* Context: !in_interrupt()
*
* Reverses the effect of usb_hcd_sa1111_probe(), first invoking
* the HCD's stop() method. It is always called from a thread
* context, normally "rmmod", "apmd", or something similar.
*
*/
void usb_hcd_sa1111_remove (struct usb_hcd *hcd, struct sa1111_dev *dev)
{
usb_remove_hcd(hcd);
sa1111_stop_hc(dev);
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
usb_put_hcd(hcd);
}
/*-------------------------------------------------------------------------*/
static int __devinit
ohci_sa1111_start (struct usb_hcd *hcd)
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
int ret;
if ((ret = ohci_init(ohci)) < 0)
return ret;
if ((ret = ohci_run (ohci)) < 0) {
err ("can't start %s", hcd->self.bus_name);
ohci_stop (hcd);
return ret;
}
return 0;
}
/*-------------------------------------------------------------------------*/
static const struct hc_driver ohci_sa1111_hc_driver = {
.description = hcd_name,
.product_desc = "SA-1111 OHCI",
.hcd_priv_size = sizeof(struct ohci_hcd),
/*
* generic hardware linkage
*/
.irq = ohci_irq,
.flags = HCD_USB11 | HCD_MEMORY,
/*
* basic lifecycle operations
*/
.start = ohci_sa1111_start,
#ifdef CONFIG_PM
/* suspend: ohci_sa1111_suspend, -- tbd */
/* resume: ohci_sa1111_resume, -- tbd */
#endif
.stop = ohci_stop,
/*
* managing i/o requests and associated device resources
*/
.urb_enqueue = ohci_urb_enqueue,
.urb_dequeue = ohci_urb_dequeue,
.endpoint_disable = ohci_endpoint_disable,
/*
* scheduling support
*/
.get_frame_number = ohci_get_frame,
/*
* root hub support
*/
.hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control,
#ifdef CONFIG_USB_SUSPEND
.hub_suspend = ohci_hub_suspend,
.hub_resume = ohci_hub_resume,
#endif
};
/*-------------------------------------------------------------------------*/
static int ohci_hcd_sa1111_drv_probe(struct sa1111_dev *dev)
{
int ret;
if (usb_disabled())
return -ENODEV;
ret = usb_hcd_sa1111_probe(&ohci_sa1111_hc_driver, dev);
return ret;
}
static int ohci_hcd_sa1111_drv_remove(struct sa1111_dev *dev)
{
struct usb_hcd *hcd = sa1111_get_drvdata(dev);
usb_hcd_sa1111_remove(hcd, dev);
return 0;
}
static struct sa1111_driver ohci_hcd_sa1111_driver = {
.drv = {
.name = "sa1111-ohci",
},
.devid = SA1111_DEVID_USB,
.probe = ohci_hcd_sa1111_drv_probe,
.remove = ohci_hcd_sa1111_drv_remove,
};
static int __init ohci_hcd_sa1111_init (void)
{
dbg (DRIVER_INFO " (SA-1111)");
dbg ("block sizes: ed %d td %d",
sizeof (struct ed), sizeof (struct td));
return sa1111_driver_register(&ohci_hcd_sa1111_driver);
}
static void __exit ohci_hcd_sa1111_cleanup (void)
{
sa1111_driver_unregister(&ohci_hcd_sa1111_driver);
}
module_init (ohci_hcd_sa1111_init);
module_exit (ohci_hcd_sa1111_cleanup);

636
drivers/usb/host/ohci.h 一般檔案
查看文件

@@ -0,0 +1,636 @@
/*
* OHCI HCD (Host Controller Driver) for USB.
*
* (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
* (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
*
* This file is licenced under the GPL.
*/
/*
* __hc32 and __hc16 are "Host Controller" types, they may be equivalent to
* __leXX (normally) or __beXX (given OHCI_BIG_ENDIAN), depending on the
* host controller implementation.
*/
typedef __u32 __bitwise __hc32;
typedef __u16 __bitwise __hc16;
/*
* OHCI Endpoint Descriptor (ED) ... holds TD queue
* See OHCI spec, section 4.2
*
* This is a "Queue Head" for those transfers, which is why
* both EHCI and UHCI call similar structures a "QH".
*/
struct ed {
/* first fields are hardware-specified */
__hc32 hwINFO; /* endpoint config bitmap */
/* info bits defined by hcd */
#define ED_DEQUEUE (1 << 27)
/* info bits defined by the hardware */
#define ED_ISO (1 << 15)
#define ED_SKIP (1 << 14)
#define ED_LOWSPEED (1 << 13)
#define ED_OUT (0x01 << 11)
#define ED_IN (0x02 << 11)
__hc32 hwTailP; /* tail of TD list */
__hc32 hwHeadP; /* head of TD list (hc r/w) */
#define ED_C (0x02) /* toggle carry */
#define ED_H (0x01) /* halted */
__hc32 hwNextED; /* next ED in list */
/* rest are purely for the driver's use */
dma_addr_t dma; /* addr of ED */
struct td *dummy; /* next TD to activate */
/* host's view of schedule */
struct ed *ed_next; /* on schedule or rm_list */
struct ed *ed_prev; /* for non-interrupt EDs */
struct list_head td_list; /* "shadow list" of our TDs */
/* create --> IDLE --> OPER --> ... --> IDLE --> destroy
* usually: OPER --> UNLINK --> (IDLE | OPER) --> ...
*/
u8 state; /* ED_{IDLE,UNLINK,OPER} */
#define ED_IDLE 0x00 /* NOT linked to HC */
#define ED_UNLINK 0x01 /* being unlinked from hc */
#define ED_OPER 0x02 /* IS linked to hc */
u8 type; /* PIPE_{BULK,...} */
/* periodic scheduling params (for intr and iso) */
u8 branch;
u16 interval;
u16 load;
u16 last_iso; /* iso only */
/* HC may see EDs on rm_list until next frame (frame_no == tick) */
u16 tick;
} __attribute__ ((aligned(16)));
#define ED_MASK ((u32)~0x0f) /* strip hw status in low addr bits */
/*
* OHCI Transfer Descriptor (TD) ... one per transfer segment
* See OHCI spec, sections 4.3.1 (general = control/bulk/interrupt)
* and 4.3.2 (iso)
*/
struct td {
/* first fields are hardware-specified */
__hc32 hwINFO; /* transfer info bitmask */
/* hwINFO bits for both general and iso tds: */
#define TD_CC 0xf0000000 /* condition code */
#define TD_CC_GET(td_p) ((td_p >>28) & 0x0f)
//#define TD_CC_SET(td_p, cc) (td_p) = ((td_p) & 0x0fffffff) | (((cc) & 0x0f) << 28)
#define TD_DI 0x00E00000 /* frames before interrupt */
#define TD_DI_SET(X) (((X) & 0x07)<< 21)
/* these two bits are available for definition/use by HCDs in both
* general and iso tds ... others are available for only one type
*/
#define TD_DONE 0x00020000 /* retired to donelist */
#define TD_ISO 0x00010000 /* copy of ED_ISO */
/* hwINFO bits for general tds: */
#define TD_EC 0x0C000000 /* error count */
#define TD_T 0x03000000 /* data toggle state */
#define TD_T_DATA0 0x02000000 /* DATA0 */
#define TD_T_DATA1 0x03000000 /* DATA1 */
#define TD_T_TOGGLE 0x00000000 /* uses ED_C */
#define TD_DP 0x00180000 /* direction/pid */
#define TD_DP_SETUP 0x00000000 /* SETUP pid */
#define TD_DP_IN 0x00100000 /* IN pid */
#define TD_DP_OUT 0x00080000 /* OUT pid */
/* 0x00180000 rsvd */
#define TD_R 0x00040000 /* round: short packets OK? */
/* (no hwINFO #defines yet for iso tds) */
__hc32 hwCBP; /* Current Buffer Pointer (or 0) */
__hc32 hwNextTD; /* Next TD Pointer */
__hc32 hwBE; /* Memory Buffer End Pointer */
/* PSW is only for ISO. Only 1 PSW entry is used, but on
* big-endian PPC hardware that's the second entry.
*/
#define MAXPSW 2
__hc16 hwPSW [MAXPSW];
/* rest are purely for the driver's use */
__u8 index;
struct ed *ed;
struct td *td_hash; /* dma-->td hashtable */
struct td *next_dl_td;
struct urb *urb;
dma_addr_t td_dma; /* addr of this TD */
dma_addr_t data_dma; /* addr of data it points to */
struct list_head td_list; /* "shadow list", TDs on same ED */
} __attribute__ ((aligned(32))); /* c/b/i need 16; only iso needs 32 */
#define TD_MASK ((u32)~0x1f) /* strip hw status in low addr bits */
/*
* Hardware transfer status codes -- CC from td->hwINFO or td->hwPSW
*/
#define TD_CC_NOERROR 0x00
#define TD_CC_CRC 0x01
#define TD_CC_BITSTUFFING 0x02
#define TD_CC_DATATOGGLEM 0x03
#define TD_CC_STALL 0x04
#define TD_DEVNOTRESP 0x05
#define TD_PIDCHECKFAIL 0x06
#define TD_UNEXPECTEDPID 0x07
#define TD_DATAOVERRUN 0x08
#define TD_DATAUNDERRUN 0x09
/* 0x0A, 0x0B reserved for hardware */
#define TD_BUFFEROVERRUN 0x0C
#define TD_BUFFERUNDERRUN 0x0D
/* 0x0E, 0x0F reserved for HCD */
#define TD_NOTACCESSED 0x0F
/* map OHCI TD status codes (CC) to errno values */
static const int cc_to_error [16] = {
/* No Error */ 0,
/* CRC Error */ -EILSEQ,
/* Bit Stuff */ -EPROTO,
/* Data Togg */ -EILSEQ,
/* Stall */ -EPIPE,
/* DevNotResp */ -ETIMEDOUT,
/* PIDCheck */ -EPROTO,
/* UnExpPID */ -EPROTO,
/* DataOver */ -EOVERFLOW,
/* DataUnder */ -EREMOTEIO,
/* (for hw) */ -EIO,
/* (for hw) */ -EIO,
/* BufferOver */ -ECOMM,
/* BuffUnder */ -ENOSR,
/* (for HCD) */ -EALREADY,
/* (for HCD) */ -EALREADY
};
/*
* The HCCA (Host Controller Communications Area) is a 256 byte
* structure defined section 4.4.1 of the OHCI spec. The HC is
* told the base address of it. It must be 256-byte aligned.
*/
struct ohci_hcca {
#define NUM_INTS 32
__hc32 int_table [NUM_INTS]; /* periodic schedule */
/*
* OHCI defines u16 frame_no, followed by u16 zero pad.
* Since some processors can't do 16 bit bus accesses,
* portable access must be a 32 bits wide.
*/
__hc32 frame_no; /* current frame number */
__hc32 done_head; /* info returned for an interrupt */
u8 reserved_for_hc [116];
u8 what [4]; /* spec only identifies 252 bytes :) */
} __attribute__ ((aligned(256)));
/*
* This is the structure of the OHCI controller's memory mapped I/O region.
* You must use readl() and writel() (in <asm/io.h>) to access these fields!!
* Layout is in section 7 (and appendix B) of the spec.
*/
struct ohci_regs {
/* control and status registers (section 7.1) */
__hc32 revision;
__hc32 control;
__hc32 cmdstatus;
__hc32 intrstatus;
__hc32 intrenable;
__hc32 intrdisable;
/* memory pointers (section 7.2) */
__hc32 hcca;
__hc32 ed_periodcurrent;
__hc32 ed_controlhead;
__hc32 ed_controlcurrent;
__hc32 ed_bulkhead;
__hc32 ed_bulkcurrent;
__hc32 donehead;
/* frame counters (section 7.3) */
__hc32 fminterval;
__hc32 fmremaining;
__hc32 fmnumber;
__hc32 periodicstart;
__hc32 lsthresh;
/* Root hub ports (section 7.4) */
struct ohci_roothub_regs {
__hc32 a;
__hc32 b;
__hc32 status;
#define MAX_ROOT_PORTS 15 /* maximum OHCI root hub ports (RH_A_NDP) */
__hc32 portstatus [MAX_ROOT_PORTS];
} roothub;
/* and optional "legacy support" registers (appendix B) at 0x0100 */
} __attribute__ ((aligned(32)));
/* OHCI CONTROL AND STATUS REGISTER MASKS */
/*
* HcControl (control) register masks
*/
#define OHCI_CTRL_CBSR (3 << 0) /* control/bulk service ratio */
#define OHCI_CTRL_PLE (1 << 2) /* periodic list enable */
#define OHCI_CTRL_IE (1 << 3) /* isochronous enable */
#define OHCI_CTRL_CLE (1 << 4) /* control list enable */
#define OHCI_CTRL_BLE (1 << 5) /* bulk list enable */
#define OHCI_CTRL_HCFS (3 << 6) /* host controller functional state */
#define OHCI_CTRL_IR (1 << 8) /* interrupt routing */
#define OHCI_CTRL_RWC (1 << 9) /* remote wakeup connected */
#define OHCI_CTRL_RWE (1 << 10) /* remote wakeup enable */
/* pre-shifted values for HCFS */
# define OHCI_USB_RESET (0 << 6)
# define OHCI_USB_RESUME (1 << 6)
# define OHCI_USB_OPER (2 << 6)
# define OHCI_USB_SUSPEND (3 << 6)
/*
* HcCommandStatus (cmdstatus) register masks
*/
#define OHCI_HCR (1 << 0) /* host controller reset */
#define OHCI_CLF (1 << 1) /* control list filled */
#define OHCI_BLF (1 << 2) /* bulk list filled */
#define OHCI_OCR (1 << 3) /* ownership change request */
#define OHCI_SOC (3 << 16) /* scheduling overrun count */
/*
* masks used with interrupt registers:
* HcInterruptStatus (intrstatus)
* HcInterruptEnable (intrenable)
* HcInterruptDisable (intrdisable)
*/
#define OHCI_INTR_SO (1 << 0) /* scheduling overrun */
#define OHCI_INTR_WDH (1 << 1) /* writeback of done_head */
#define OHCI_INTR_SF (1 << 2) /* start frame */
#define OHCI_INTR_RD (1 << 3) /* resume detect */
#define OHCI_INTR_UE (1 << 4) /* unrecoverable error */
#define OHCI_INTR_FNO (1 << 5) /* frame number overflow */
#define OHCI_INTR_RHSC (1 << 6) /* root hub status change */
#define OHCI_INTR_OC (1 << 30) /* ownership change */
#define OHCI_INTR_MIE (1 << 31) /* master interrupt enable */
/* OHCI ROOT HUB REGISTER MASKS */
/* roothub.portstatus [i] bits */
#define RH_PS_CCS 0x00000001 /* current connect status */
#define RH_PS_PES 0x00000002 /* port enable status*/
#define RH_PS_PSS 0x00000004 /* port suspend status */
#define RH_PS_POCI 0x00000008 /* port over current indicator */
#define RH_PS_PRS 0x00000010 /* port reset status */
#define RH_PS_PPS 0x00000100 /* port power status */
#define RH_PS_LSDA 0x00000200 /* low speed device attached */
#define RH_PS_CSC 0x00010000 /* connect status change */
#define RH_PS_PESC 0x00020000 /* port enable status change */
#define RH_PS_PSSC 0x00040000 /* port suspend status change */
#define RH_PS_OCIC 0x00080000 /* over current indicator change */
#define RH_PS_PRSC 0x00100000 /* port reset status change */
/* roothub.status bits */
#define RH_HS_LPS 0x00000001 /* local power status */
#define RH_HS_OCI 0x00000002 /* over current indicator */
#define RH_HS_DRWE 0x00008000 /* device remote wakeup enable */
#define RH_HS_LPSC 0x00010000 /* local power status change */
#define RH_HS_OCIC 0x00020000 /* over current indicator change */
#define RH_HS_CRWE 0x80000000 /* clear remote wakeup enable */
/* roothub.b masks */
#define RH_B_DR 0x0000ffff /* device removable flags */
#define RH_B_PPCM 0xffff0000 /* port power control mask */
/* roothub.a masks */
#define RH_A_NDP (0xff << 0) /* number of downstream ports */
#define RH_A_PSM (1 << 8) /* power switching mode */
#define RH_A_NPS (1 << 9) /* no power switching */
#define RH_A_DT (1 << 10) /* device type (mbz) */
#define RH_A_OCPM (1 << 11) /* over current protection mode */
#define RH_A_NOCP (1 << 12) /* no over current protection */
#define RH_A_POTPGT (0xff << 24) /* power on to power good time */
/* hcd-private per-urb state */
typedef struct urb_priv {
struct ed *ed;
u16 length; // # tds in this request
u16 td_cnt; // tds already serviced
struct list_head pending;
struct td *td [0]; // all TDs in this request
} urb_priv_t;
#define TD_HASH_SIZE 64 /* power'o'two */
// sizeof (struct td) ~= 64 == 2^6 ...
#define TD_HASH_FUNC(td_dma) ((td_dma ^ (td_dma >> 6)) % TD_HASH_SIZE)
/*
* This is the full ohci controller description
*
* Note how the "proper" USB information is just
* a subset of what the full implementation needs. (Linus)
*/
struct ohci_hcd {
spinlock_t lock;
/*
* I/O memory used to communicate with the HC (dma-consistent)
*/
struct ohci_regs __iomem *regs;
/*
* main memory used to communicate with the HC (dma-consistent).
* hcd adds to schedule for a live hc any time, but removals finish
* only at the start of the next frame.
*/
struct ohci_hcca *hcca;
dma_addr_t hcca_dma;
struct ed *ed_rm_list; /* to be removed */
struct ed *ed_bulktail; /* last in bulk list */
struct ed *ed_controltail; /* last in ctrl list */
struct ed *periodic [NUM_INTS]; /* shadow int_table */
/*
* OTG controllers and transceivers need software interaction;
* other external transceivers should be software-transparent
*/
struct otg_transceiver *transceiver;
unsigned power_budget;
/*
* memory management for queue data structures
*/
struct dma_pool *td_cache;
struct dma_pool *ed_cache;
struct td *td_hash [TD_HASH_SIZE];
struct list_head pending;
/*
* driver state
*/
int load [NUM_INTS];
u32 hc_control; /* copy of hc control reg */
unsigned long next_statechange; /* suspend/resume */
u32 fminterval; /* saved register */
struct work_struct rh_resume;
unsigned long flags; /* for HC bugs */
#define OHCI_QUIRK_AMD756 0x01 /* erratum #4 */
#define OHCI_QUIRK_SUPERIO 0x02 /* natsemi */
#define OHCI_QUIRK_INITRESET 0x04 /* SiS, OPTi, ... */
#define OHCI_BIG_ENDIAN 0x08 /* big endian HC */
// there are also chip quirks/bugs in init logic
};
/* convert between an hcd pointer and the corresponding ohci_hcd */
static inline struct ohci_hcd *hcd_to_ohci (struct usb_hcd *hcd)
{
return (struct ohci_hcd *) (hcd->hcd_priv);
}
static inline struct usb_hcd *ohci_to_hcd (const struct ohci_hcd *ohci)
{
return container_of ((void *) ohci, struct usb_hcd, hcd_priv);
}
/*-------------------------------------------------------------------------*/
#ifndef DEBUG
#define STUB_DEBUG_FILES
#endif /* DEBUG */
#define ohci_dbg(ohci, fmt, args...) \
dev_dbg (ohci_to_hcd(ohci)->self.controller , fmt , ## args )
#define ohci_err(ohci, fmt, args...) \
dev_err (ohci_to_hcd(ohci)->self.controller , fmt , ## args )
#define ohci_info(ohci, fmt, args...) \
dev_info (ohci_to_hcd(ohci)->self.controller , fmt , ## args )
#define ohci_warn(ohci, fmt, args...) \
dev_warn (ohci_to_hcd(ohci)->self.controller , fmt , ## args )
#ifdef OHCI_VERBOSE_DEBUG
# define ohci_vdbg ohci_dbg
#else
# define ohci_vdbg(ohci, fmt, args...) do { } while (0)
#endif
/*-------------------------------------------------------------------------*/
/*
* While most USB host controllers implement their registers and
* in-memory communication descriptors in little-endian format,
* a minority (notably the IBM STB04XXX and the Motorola MPC5200
* processors) implement them in big endian format.
*
* This attempts to support either format at compile time without a
* runtime penalty, or both formats with the additional overhead
* of checking a flag bit.
*/
#ifdef CONFIG_USB_OHCI_BIG_ENDIAN
#ifdef CONFIG_USB_OHCI_LITTLE_ENDIAN
#define big_endian(ohci) (ohci->flags & OHCI_BIG_ENDIAN) /* either */
#else
#define big_endian(ohci) 1 /* only big endian */
#endif
/*
* Big-endian read/write functions are arch-specific.
* Other arches can be added if/when they're needed.
*/
#if defined(CONFIG_PPC)
#define readl_be(addr) in_be32((__force unsigned *)addr)
#define writel_be(val, addr) out_be32((__force unsigned *)addr, val)
#endif
static inline unsigned int ohci_readl (const struct ohci_hcd *ohci,
__hc32 __iomem * regs)
{
return big_endian(ohci) ? readl_be (regs) : readl ((__force u32 *)regs);
}
static inline void ohci_writel (const struct ohci_hcd *ohci,
const unsigned int val, __hc32 __iomem *regs)
{
big_endian(ohci) ? writel_be (val, regs) :
writel (val, (__force u32 *)regs);
}
#else /* !CONFIG_USB_OHCI_BIG_ENDIAN */
#define big_endian(ohci) 0 /* only little endian */
#ifdef CONFIG_ARCH_LH7A404
/* Marc Singer: at the time this code was written, the LH7A404
* had a problem reading the USB host registers. This
* implementation of the ohci_readl function performs the read
* twice as a work-around.
*/
static inline unsigned int
ohci_readl (const struct ohci_hcd *ohci, const __hc32 *regs)
{
*(volatile __force unsigned int*) regs;
return *(volatile __force unsigned int*) regs;
}
#else
/* Standard version of ohci_readl uses standard, platform
* specific implementation. */
static inline unsigned int
ohci_readl (const struct ohci_hcd *ohci, __hc32 __iomem * regs)
{
return readl(regs);
}
#endif
static inline void ohci_writel (const struct ohci_hcd *ohci,
const unsigned int val, __hc32 __iomem *regs)
{
writel (val, regs);
}
#endif /* !CONFIG_USB_OHCI_BIG_ENDIAN */
/*-------------------------------------------------------------------------*/
/* cpu to ohci */
static inline __hc16 cpu_to_hc16 (const struct ohci_hcd *ohci, const u16 x)
{
return big_endian(ohci) ? (__force __hc16)cpu_to_be16(x) : (__force __hc16)cpu_to_le16(x);
}
static inline __hc16 cpu_to_hc16p (const struct ohci_hcd *ohci, const u16 *x)
{
return big_endian(ohci) ? cpu_to_be16p(x) : cpu_to_le16p(x);
}
static inline __hc32 cpu_to_hc32 (const struct ohci_hcd *ohci, const u32 x)
{
return big_endian(ohci) ? (__force __hc32)cpu_to_be32(x) : (__force __hc32)cpu_to_le32(x);
}
static inline __hc32 cpu_to_hc32p (const struct ohci_hcd *ohci, const u32 *x)
{
return big_endian(ohci) ? cpu_to_be32p(x) : cpu_to_le32p(x);
}
/* ohci to cpu */
static inline u16 hc16_to_cpu (const struct ohci_hcd *ohci, const __hc16 x)
{
return big_endian(ohci) ? be16_to_cpu((__force __be16)x) : le16_to_cpu((__force __le16)x);
}
static inline u16 hc16_to_cpup (const struct ohci_hcd *ohci, const __hc16 *x)
{
return big_endian(ohci) ? be16_to_cpup((__force __be16 *)x) : le16_to_cpup((__force __le16 *)x);
}
static inline u32 hc32_to_cpu (const struct ohci_hcd *ohci, const __hc32 x)
{
return big_endian(ohci) ? be32_to_cpu((__force __be32)x) : le32_to_cpu((__force __le32)x);
}
static inline u32 hc32_to_cpup (const struct ohci_hcd *ohci, const __hc32 *x)
{
return big_endian(ohci) ? be32_to_cpup((__force __be32 *)x) : le32_to_cpup((__force __le32 *)x);
}
/*-------------------------------------------------------------------------*/
/* HCCA frame number is 16 bits, but is accessed as 32 bits since not all
* hardware handles 16 bit reads. That creates a different confusion on
* some big-endian SOC implementations. Same thing happens with PSW access.
*/
#ifdef CONFIG_STB03xxx
#define OHCI_BE_FRAME_NO_SHIFT 16
#else
#define OHCI_BE_FRAME_NO_SHIFT 0
#endif
static inline u16 ohci_frame_no(const struct ohci_hcd *ohci)
{
u32 tmp;
if (big_endian(ohci)) {
tmp = be32_to_cpup((__force __be32 *)&ohci->hcca->frame_no);
tmp >>= OHCI_BE_FRAME_NO_SHIFT;
} else
tmp = le32_to_cpup((__force __le32 *)&ohci->hcca->frame_no);
return (u16)tmp;
}
static inline __hc16 *ohci_hwPSWp(const struct ohci_hcd *ohci,
const struct td *td, int index)
{
return (__hc16 *)(big_endian(ohci) ?
&td->hwPSW[index ^ 1] : &td->hwPSW[index]);
}
static inline u16 ohci_hwPSW(const struct ohci_hcd *ohci,
const struct td *td, int index)
{
return hc16_to_cpup(ohci, ohci_hwPSWp(ohci, td, index));
}
/*-------------------------------------------------------------------------*/
static inline void disable (struct ohci_hcd *ohci)
{
ohci_to_hcd(ohci)->state = HC_STATE_HALT;
}
#define FI 0x2edf /* 12000 bits per frame (-1) */
#define FSMP(fi) (0x7fff & ((6 * ((fi) - 210)) / 7))
#define FIT (1 << 31)
#define LSTHRESH 0x628 /* lowspeed bit threshold */
static void periodic_reinit (struct ohci_hcd *ohci)
{
u32 fi = ohci->fminterval & 0x03fff;
u32 fit = ohci_readl(ohci, &ohci->regs->fminterval) & FIT;
ohci_writel (ohci, (fit ^ FIT) | ohci->fminterval,
&ohci->regs->fminterval);
ohci_writel (ohci, ((9 * fi) / 10) & 0x3fff,
&ohci->regs->periodicstart);
}
/* AMD-756 (D2 rev) reports corrupt register contents in some cases.
* The erratum (#4) description is incorrect. AMD's workaround waits
* till some bits (mostly reserved) are clear; ok for all revs.
*/
#define read_roothub(hc, register, mask) ({ \
u32 temp = ohci_readl (hc, &hc->regs->roothub.register); \
if (temp == -1) \
disable (hc); \
else if (hc->flags & OHCI_QUIRK_AMD756) \
while (temp & mask) \
temp = ohci_readl (hc, &hc->regs->roothub.register); \
temp; })
static u32 roothub_a (struct ohci_hcd *hc)
{ return read_roothub (hc, a, 0xfc0fe000); }
static inline u32 roothub_b (struct ohci_hcd *hc)
{ return ohci_readl (hc, &hc->regs->roothub.b); }
static inline u32 roothub_status (struct ohci_hcd *hc)
{ return ohci_readl (hc, &hc->regs->roothub.status); }
static u32 roothub_portstatus (struct ohci_hcd *hc, int i)
{ return read_roothub (hc, portstatus [i], 0xffe0fce0); }

1851
drivers/usb/host/sl811-hcd.c 一般檔案

檔案差異因為檔案過大而無法顯示 載入差異

266
drivers/usb/host/sl811.h 一般檔案
查看文件

@@ -0,0 +1,266 @@
/*
* SL811HS register declarations and HCD data structures
*
* Copyright (C) 2004 Psion Teklogix
* Copyright (C) 2004 David Brownell
* Copyright (C) 2001 Cypress Semiconductor Inc.
*/
/*
* SL811HS has transfer registers, and control registers. In host/master
* mode one set of registers is used; in peripheral/slave mode, another.
* - SL11H only has some "A" transfer registers from 0x00-0x04
* - SL811HS also has "B" registers from 0x08-0x0c
* - SL811S (or HS in slave mode) has four A+B sets, at 00, 10, 20, 30
*/
#define SL811_EP_A(base) ((base) + 0)
#define SL811_EP_B(base) ((base) + 8)
#define SL811_HOST_BUF 0x00
#define SL811_PERIPH_EP0 0x00
#define SL811_PERIPH_EP1 0x10
#define SL811_PERIPH_EP2 0x20
#define SL811_PERIPH_EP3 0x30
/* TRANSFER REGISTERS: host and peripheral sides are similar
* except for the control models (master vs slave).
*/
#define SL11H_HOSTCTLREG 0
# define SL11H_HCTLMASK_ARM 0x01
# define SL11H_HCTLMASK_ENABLE 0x02
# define SL11H_HCTLMASK_IN 0x00
# define SL11H_HCTLMASK_OUT 0x04
# define SL11H_HCTLMASK_ISOCH 0x10
# define SL11H_HCTLMASK_AFTERSOF 0x20
# define SL11H_HCTLMASK_TOGGLE 0x40
# define SL11H_HCTLMASK_PREAMBLE 0x80
#define SL11H_BUFADDRREG 1
#define SL11H_BUFLNTHREG 2
#define SL11H_PKTSTATREG 3 /* read */
# define SL11H_STATMASK_ACK 0x01
# define SL11H_STATMASK_ERROR 0x02
# define SL11H_STATMASK_TMOUT 0x04
# define SL11H_STATMASK_SEQ 0x08
# define SL11H_STATMASK_SETUP 0x10
# define SL11H_STATMASK_OVF 0x20
# define SL11H_STATMASK_NAK 0x40
# define SL11H_STATMASK_STALL 0x80
#define SL11H_PIDEPREG 3 /* write */
# define SL_SETUP 0xd0
# define SL_IN 0x90
# define SL_OUT 0x10
# define SL_SOF 0x50
# define SL_PREAMBLE 0xc0
# define SL_NAK 0xa0
# define SL_STALL 0xe0
# define SL_DATA0 0x30
# define SL_DATA1 0xb0
#define SL11H_XFERCNTREG 4 /* read */
#define SL11H_DEVADDRREG 4 /* write */
/* CONTROL REGISTERS: host and peripheral are very different.
*/
#define SL11H_CTLREG1 5
# define SL11H_CTL1MASK_SOF_ENA 0x01
# define SL11H_CTL1MASK_FORCE 0x18
# define SL11H_CTL1MASK_NORMAL 0x00
# define SL11H_CTL1MASK_SE0 0x08 /* reset */
# define SL11H_CTL1MASK_J 0x10
# define SL11H_CTL1MASK_K 0x18 /* resume */
# define SL11H_CTL1MASK_LSPD 0x20
# define SL11H_CTL1MASK_SUSPEND 0x40
#define SL11H_IRQ_ENABLE 6
# define SL11H_INTMASK_DONE_A 0x01
# define SL11H_INTMASK_DONE_B 0x02
# define SL11H_INTMASK_SOFINTR 0x10
# define SL11H_INTMASK_INSRMV 0x20 /* to/from SE0 */
# define SL11H_INTMASK_RD 0x40
# define SL11H_INTMASK_DP 0x80 /* only in INTSTATREG */
#define SL11S_ADDRESS 7
/* 0x08-0x0c are for the B buffer (not in SL11) */
#define SL11H_IRQ_STATUS 0x0D /* write to ack */
#define SL11H_HWREVREG 0x0E /* read */
# define SL11H_HWRMASK_HWREV 0xF0
#define SL11H_SOFLOWREG 0x0E /* write */
#define SL11H_SOFTMRREG 0x0F /* read */
/* a write to this register enables SL811HS features.
* HOST flag presumably overrides the chip input signal?
*/
#define SL811HS_CTLREG2 0x0F
# define SL811HS_CTL2MASK_SOF_MASK 0x3F
# define SL811HS_CTL2MASK_DSWAP 0x40
# define SL811HS_CTL2MASK_HOST 0x80
#define SL811HS_CTL2_INIT (SL811HS_CTL2MASK_HOST | 0x2e)
/* DATA BUFFERS: registers from 0x10..0xff are for data buffers;
* that's 240 bytes, which we'll split evenly between A and B sides.
* Only ISO can use more than 64 bytes per packet.
* (The SL11S has 0x40..0xff for buffers.)
*/
#define H_MAXPACKET 120 /* bytes in A or B fifos */
#define SL11H_DATA_START 0x10
#define SL811HS_PACKET_BUF(is_a) ((is_a) \
? SL11H_DATA_START \
: (SL11H_DATA_START + H_MAXPACKET))
/*-------------------------------------------------------------------------*/
#define LOG2_PERIODIC_SIZE 5 /* arbitrary; this matches OHCI */
#define PERIODIC_SIZE (1 << LOG2_PERIODIC_SIZE)
struct sl811 {
spinlock_t lock;
void __iomem *addr_reg;
void __iomem *data_reg;
struct sl811_platform_data *board;
struct proc_dir_entry *pde;
unsigned long stat_insrmv;
unsigned long stat_wake;
unsigned long stat_sof;
unsigned long stat_a;
unsigned long stat_b;
unsigned long stat_lost;
unsigned long stat_overrun;
/* sw model */
struct timer_list timer;
struct sl811h_ep *next_periodic;
struct sl811h_ep *next_async;
struct sl811h_ep *active_a;
unsigned long jiffies_a;
struct sl811h_ep *active_b;
unsigned long jiffies_b;
u32 port1;
u8 ctrl1, ctrl2, irq_enable;
u16 frame;
/* async schedule: control, bulk */
struct list_head async;
/* periodic schedule: interrupt, iso */
u16 load[PERIODIC_SIZE];
struct sl811h_ep *periodic[PERIODIC_SIZE];
unsigned periodic_count;
};
static inline struct sl811 *hcd_to_sl811(struct usb_hcd *hcd)
{
return (struct sl811 *) (hcd->hcd_priv);
}
static inline struct usb_hcd *sl811_to_hcd(struct sl811 *sl811)
{
return container_of((void *) sl811, struct usb_hcd, hcd_priv);
}
struct sl811h_ep {
struct usb_host_endpoint *hep;
struct usb_device *udev;
u8 defctrl;
u8 maxpacket;
u8 epnum;
u8 nextpid;
u16 error_count;
u16 nak_count;
u16 length; /* of current packet */
/* periodic schedule */
u16 period;
u16 branch;
u16 load;
struct sl811h_ep *next;
/* async schedule */
struct list_head schedule;
};
/*-------------------------------------------------------------------------*/
/* These register utilities should work for the SL811S register API too
* NOTE: caller must hold sl811->lock.
*/
static inline u8 sl811_read(struct sl811 *sl811, int reg)
{
writeb(reg, sl811->addr_reg);
return readb(sl811->data_reg);
}
static inline void sl811_write(struct sl811 *sl811, int reg, u8 val)
{
writeb(reg, sl811->addr_reg);
writeb(val, sl811->data_reg);
}
static inline void
sl811_write_buf(struct sl811 *sl811, int addr, const void *buf, size_t count)
{
const u8 *data;
void __iomem *data_reg;
if (!count)
return;
writeb(addr, sl811->addr_reg);
data = buf;
data_reg = sl811->data_reg;
do {
writeb(*data++, data_reg);
} while (--count);
}
static inline void
sl811_read_buf(struct sl811 *sl811, int addr, void *buf, size_t count)
{
u8 *data;
void __iomem *data_reg;
if (!count)
return;
writeb(addr, sl811->addr_reg);
data = buf;
data_reg = sl811->data_reg;
do {
*data++ = readb(data_reg);
} while (--count);
}
/*-------------------------------------------------------------------------*/
#ifdef DEBUG
#define DBG(stuff...) printk(KERN_DEBUG "sl811: " stuff)
#else
#define DBG(stuff...) do{}while(0)
#endif
#ifdef VERBOSE
# define VDBG DBG
#else
# define VDBG(stuff...) do{}while(0)
#endif
#ifdef PACKET_TRACE
# define PACKET VDBG
#else
# define PACKET(stuff...) do{}while(0)
#endif
#define ERR(stuff...) printk(KERN_ERR "sl811: " stuff)
#define WARN(stuff...) printk(KERN_WARNING "sl811: " stuff)
#define INFO(stuff...) printk(KERN_INFO "sl811: " stuff)

查看文件

@@ -0,0 +1,587 @@
/*
* UHCI-specific debugging code. Invaluable when something
* goes wrong, but don't get in my face.
*
* Kernel visible pointers are surrounded in []'s and bus
* visible pointers are surrounded in ()'s
*
* (C) Copyright 1999 Linus Torvalds
* (C) Copyright 1999-2001 Johannes Erdfelt
*/
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/debugfs.h>
#include <linux/smp_lock.h>
#include <asm/io.h>
#include "uhci-hcd.h"
static struct dentry *uhci_debugfs_root = NULL;
/* Handle REALLY large printk's so we don't overflow buffers */
static inline void lprintk(char *buf)
{
char *p;
/* Just write one line at a time */
while (buf) {
p = strchr(buf, '\n');
if (p)
*p = 0;
printk(KERN_DEBUG "%s\n", buf);
buf = p;
if (buf)
buf++;
}
}
static int uhci_show_td(struct uhci_td *td, char *buf, int len, int space)
{
char *out = buf;
char *spid;
u32 status, token;
/* Try to make sure there's enough memory */
if (len < 160)
return 0;
status = td_status(td);
out += sprintf(out, "%*s[%p] link (%08x) ", space, "", td, le32_to_cpu(td->link));
out += sprintf(out, "e%d %s%s%s%s%s%s%s%s%s%sLength=%x ",
((status >> 27) & 3),
(status & TD_CTRL_SPD) ? "SPD " : "",
(status & TD_CTRL_LS) ? "LS " : "",
(status & TD_CTRL_IOC) ? "IOC " : "",
(status & TD_CTRL_ACTIVE) ? "Active " : "",
(status & TD_CTRL_STALLED) ? "Stalled " : "",
(status & TD_CTRL_DBUFERR) ? "DataBufErr " : "",
(status & TD_CTRL_BABBLE) ? "Babble " : "",
(status & TD_CTRL_NAK) ? "NAK " : "",
(status & TD_CTRL_CRCTIMEO) ? "CRC/Timeo " : "",
(status & TD_CTRL_BITSTUFF) ? "BitStuff " : "",
status & 0x7ff);
token = td_token(td);
switch (uhci_packetid(token)) {
case USB_PID_SETUP:
spid = "SETUP";
break;
case USB_PID_OUT:
spid = "OUT";
break;
case USB_PID_IN:
spid = "IN";
break;
default:
spid = "?";
break;
}
out += sprintf(out, "MaxLen=%x DT%d EndPt=%x Dev=%x, PID=%x(%s) ",
token >> 21,
((token >> 19) & 1),
(token >> 15) & 15,
(token >> 8) & 127,
(token & 0xff),
spid);
out += sprintf(out, "(buf=%08x)\n", le32_to_cpu(td->buffer));
return out - buf;
}
static int uhci_show_qh(struct uhci_qh *qh, char *buf, int len, int space)
{
char *out = buf;
struct urb_priv *urbp;
struct list_head *head, *tmp;
struct uhci_td *td;
int i = 0, checked = 0, prevactive = 0;
__le32 element = qh_element(qh);
/* Try to make sure there's enough memory */
if (len < 80 * 6)
return 0;
out += sprintf(out, "%*s[%p] link (%08x) element (%08x)\n", space, "",
qh, le32_to_cpu(qh->link), le32_to_cpu(element));
if (element & UHCI_PTR_QH)
out += sprintf(out, "%*s Element points to QH (bug?)\n", space, "");
if (element & UHCI_PTR_DEPTH)
out += sprintf(out, "%*s Depth traverse\n", space, "");
if (element & cpu_to_le32(8))
out += sprintf(out, "%*s Bit 3 set (bug?)\n", space, "");
if (!(element & ~(UHCI_PTR_QH | UHCI_PTR_DEPTH)))
out += sprintf(out, "%*s Element is NULL (bug?)\n", space, "");
if (!qh->urbp) {
out += sprintf(out, "%*s urbp == NULL\n", space, "");
goto out;
}
urbp = qh->urbp;
head = &urbp->td_list;
tmp = head->next;
td = list_entry(tmp, struct uhci_td, list);
if (cpu_to_le32(td->dma_handle) != (element & ~UHCI_PTR_BITS))
out += sprintf(out, "%*s Element != First TD\n", space, "");
while (tmp != head) {
struct uhci_td *td = list_entry(tmp, struct uhci_td, list);
tmp = tmp->next;
out += sprintf(out, "%*s%d: ", space + 2, "", i++);
out += uhci_show_td(td, out, len - (out - buf), 0);
if (i > 10 && !checked && prevactive && tmp != head &&
debug <= 2) {
struct list_head *ntmp = tmp;
struct uhci_td *ntd = td;
int active = 1, ni = i;
checked = 1;
while (ntmp != head && ntmp->next != head && active) {
ntd = list_entry(ntmp, struct uhci_td, list);
ntmp = ntmp->next;
active = td_status(ntd) & TD_CTRL_ACTIVE;
ni++;
}
if (active && ni > i) {
out += sprintf(out, "%*s[skipped %d active TD's]\n", space, "", ni - i);
tmp = ntmp;
td = ntd;
i = ni;
}
}
prevactive = td_status(td) & TD_CTRL_ACTIVE;
}
if (list_empty(&urbp->queue_list) || urbp->queued)
goto out;
out += sprintf(out, "%*sQueued QH's:\n", -space, "--");
head = &urbp->queue_list;
tmp = head->next;
while (tmp != head) {
struct urb_priv *nurbp = list_entry(tmp, struct urb_priv,
queue_list);
tmp = tmp->next;
out += uhci_show_qh(nurbp->qh, out, len - (out - buf), space);
}
out:
return out - buf;
}
#define show_frame_num() \
if (!shown) { \
shown = 1; \
out += sprintf(out, "- Frame %d\n", i); \
}
#ifdef CONFIG_PROC_FS
static const char *qh_names[] = {
"skel_int128_qh", "skel_int64_qh",
"skel_int32_qh", "skel_int16_qh",
"skel_int8_qh", "skel_int4_qh",
"skel_int2_qh", "skel_int1_qh",
"skel_ls_control_qh", "skel_fs_control_qh",
"skel_bulk_qh", "skel_term_qh"
};
#define show_qh_name() \
if (!shown) { \
shown = 1; \
out += sprintf(out, "- %s\n", qh_names[i]); \
}
static int uhci_show_sc(int port, unsigned short status, char *buf, int len)
{
char *out = buf;
/* Try to make sure there's enough memory */
if (len < 160)
return 0;
out += sprintf(out, " stat%d = %04x %s%s%s%s%s%s%s%s%s%s\n",
port,
status,
(status & USBPORTSC_SUSP) ? " Suspend" : "",
(status & USBPORTSC_OCC) ? " OverCurrentChange" : "",
(status & USBPORTSC_OC) ? " OverCurrent" : "",
(status & USBPORTSC_PR) ? " Reset" : "",
(status & USBPORTSC_LSDA) ? " LowSpeed" : "",
(status & USBPORTSC_RD) ? " ResumeDetect" : "",
(status & USBPORTSC_PEC) ? " EnableChange" : "",
(status & USBPORTSC_PE) ? " Enabled" : "",
(status & USBPORTSC_CSC) ? " ConnectChange" : "",
(status & USBPORTSC_CCS) ? " Connected" : "");
return out - buf;
}
static int uhci_show_status(struct uhci_hcd *uhci, char *buf, int len)
{
char *out = buf;
unsigned long io_addr = uhci->io_addr;
unsigned short usbcmd, usbstat, usbint, usbfrnum;
unsigned int flbaseadd;
unsigned char sof;
unsigned short portsc1, portsc2;
/* Try to make sure there's enough memory */
if (len < 80 * 6)
return 0;
usbcmd = inw(io_addr + 0);
usbstat = inw(io_addr + 2);
usbint = inw(io_addr + 4);
usbfrnum = inw(io_addr + 6);
flbaseadd = inl(io_addr + 8);
sof = inb(io_addr + 12);
portsc1 = inw(io_addr + 16);
portsc2 = inw(io_addr + 18);
out += sprintf(out, " usbcmd = %04x %s%s%s%s%s%s%s%s\n",
usbcmd,
(usbcmd & USBCMD_MAXP) ? "Maxp64 " : "Maxp32 ",
(usbcmd & USBCMD_CF) ? "CF " : "",
(usbcmd & USBCMD_SWDBG) ? "SWDBG " : "",
(usbcmd & USBCMD_FGR) ? "FGR " : "",
(usbcmd & USBCMD_EGSM) ? "EGSM " : "",
(usbcmd & USBCMD_GRESET) ? "GRESET " : "",
(usbcmd & USBCMD_HCRESET) ? "HCRESET " : "",
(usbcmd & USBCMD_RS) ? "RS " : "");
out += sprintf(out, " usbstat = %04x %s%s%s%s%s%s\n",
usbstat,
(usbstat & USBSTS_HCH) ? "HCHalted " : "",
(usbstat & USBSTS_HCPE) ? "HostControllerProcessError " : "",
(usbstat & USBSTS_HSE) ? "HostSystemError " : "",
(usbstat & USBSTS_RD) ? "ResumeDetect " : "",
(usbstat & USBSTS_ERROR) ? "USBError " : "",
(usbstat & USBSTS_USBINT) ? "USBINT " : "");
out += sprintf(out, " usbint = %04x\n", usbint);
out += sprintf(out, " usbfrnum = (%d)%03x\n", (usbfrnum >> 10) & 1,
0xfff & (4*(unsigned int)usbfrnum));
out += sprintf(out, " flbaseadd = %08x\n", flbaseadd);
out += sprintf(out, " sof = %02x\n", sof);
out += uhci_show_sc(1, portsc1, out, len - (out - buf));
out += uhci_show_sc(2, portsc2, out, len - (out - buf));
return out - buf;
}
static int uhci_show_urbp(struct uhci_hcd *uhci, struct urb_priv *urbp, char *buf, int len)
{
struct list_head *tmp;
char *out = buf;
int count = 0;
if (len < 200)
return 0;
out += sprintf(out, "urb_priv [%p] ", urbp);
out += sprintf(out, "urb [%p] ", urbp->urb);
out += sprintf(out, "qh [%p] ", urbp->qh);
out += sprintf(out, "Dev=%d ", usb_pipedevice(urbp->urb->pipe));
out += sprintf(out, "EP=%x(%s) ", usb_pipeendpoint(urbp->urb->pipe), (usb_pipein(urbp->urb->pipe) ? "IN" : "OUT"));
switch (usb_pipetype(urbp->urb->pipe)) {
case PIPE_ISOCHRONOUS: out += sprintf(out, "ISO "); break;
case PIPE_INTERRUPT: out += sprintf(out, "INT "); break;
case PIPE_BULK: out += sprintf(out, "BLK "); break;
case PIPE_CONTROL: out += sprintf(out, "CTL "); break;
}
out += sprintf(out, "%s", (urbp->fsbr ? "FSBR " : ""));
out += sprintf(out, "%s", (urbp->fsbr_timeout ? "FSBR_TO " : ""));
if (urbp->urb->status != -EINPROGRESS)
out += sprintf(out, "Status=%d ", urbp->urb->status);
//out += sprintf(out, "Inserttime=%lx ",urbp->inserttime);
//out += sprintf(out, "FSBRtime=%lx ",urbp->fsbrtime);
count = 0;
list_for_each(tmp, &urbp->td_list)
count++;
out += sprintf(out, "TDs=%d ",count);
if (urbp->queued)
out += sprintf(out, "queued\n");
else {
count = 0;
list_for_each(tmp, &urbp->queue_list)
count++;
out += sprintf(out, "queued URBs=%d\n", count);
}
return out - buf;
}
static int uhci_show_lists(struct uhci_hcd *uhci, char *buf, int len)
{
char *out = buf;
struct list_head *head, *tmp;
int count;
out += sprintf(out, "Main list URBs:");
if (list_empty(&uhci->urb_list))
out += sprintf(out, " Empty\n");
else {
out += sprintf(out, "\n");
count = 0;
head = &uhci->urb_list;
tmp = head->next;
while (tmp != head) {
struct urb_priv *urbp = list_entry(tmp, struct urb_priv, urb_list);
out += sprintf(out, " %d: ", ++count);
out += uhci_show_urbp(uhci, urbp, out, len - (out - buf));
tmp = tmp->next;
}
}
out += sprintf(out, "Remove list URBs:");
if (list_empty(&uhci->urb_remove_list))
out += sprintf(out, " Empty\n");
else {
out += sprintf(out, "\n");
count = 0;
head = &uhci->urb_remove_list;
tmp = head->next;
while (tmp != head) {
struct urb_priv *urbp = list_entry(tmp, struct urb_priv, urb_list);
out += sprintf(out, " %d: ", ++count);
out += uhci_show_urbp(uhci, urbp, out, len - (out - buf));
tmp = tmp->next;
}
}
out += sprintf(out, "Complete list URBs:");
if (list_empty(&uhci->complete_list))
out += sprintf(out, " Empty\n");
else {
out += sprintf(out, "\n");
count = 0;
head = &uhci->complete_list;
tmp = head->next;
while (tmp != head) {
struct urb_priv *urbp = list_entry(tmp, struct urb_priv, urb_list);
out += sprintf(out, " %d: ", ++count);
out += uhci_show_urbp(uhci, urbp, out, len - (out - buf));
tmp = tmp->next;
}
}
return out - buf;
}
static int uhci_sprint_schedule(struct uhci_hcd *uhci, char *buf, int len)
{
unsigned long flags;
char *out = buf;
int i, j;
struct uhci_qh *qh;
struct uhci_td *td;
struct list_head *tmp, *head;
spin_lock_irqsave(&uhci->lock, flags);
out += sprintf(out, "HC status\n");
out += uhci_show_status(uhci, out, len - (out - buf));
out += sprintf(out, "Frame List\n");
for (i = 0; i < UHCI_NUMFRAMES; ++i) {
int shown = 0;
td = uhci->fl->frame_cpu[i];
if (!td)
continue;
if (td->dma_handle != (dma_addr_t)uhci->fl->frame[i]) {
show_frame_num();
out += sprintf(out, " frame list does not match td->dma_handle!\n");
}
show_frame_num();
head = &td->fl_list;
tmp = head;
do {
td = list_entry(tmp, struct uhci_td, fl_list);
tmp = tmp->next;
out += uhci_show_td(td, out, len - (out - buf), 4);
} while (tmp != head);
}
out += sprintf(out, "Skeleton QH's\n");
for (i = 0; i < UHCI_NUM_SKELQH; ++i) {
int shown = 0;
qh = uhci->skelqh[i];
if (debug > 1) {
show_qh_name();
out += uhci_show_qh(qh, out, len - (out - buf), 4);
}
/* Last QH is the Terminating QH, it's different */
if (i == UHCI_NUM_SKELQH - 1) {
if (qh->link != UHCI_PTR_TERM)
out += sprintf(out, " bandwidth reclamation on!\n");
if (qh_element(qh) != cpu_to_le32(uhci->term_td->dma_handle))
out += sprintf(out, " skel_term_qh element is not set to term_td!\n");
continue;
}
j = (i < 7) ? 7 : i+1; /* Next skeleton */
if (list_empty(&qh->list)) {
if (i < UHCI_NUM_SKELQH - 1) {
if (qh->link !=
(cpu_to_le32(uhci->skelqh[j]->dma_handle) | UHCI_PTR_QH)) {
show_qh_name();
out += sprintf(out, " skeleton QH not linked to next skeleton QH!\n");
}
}
continue;
}
show_qh_name();
head = &qh->list;
tmp = head->next;
while (tmp != head) {
qh = list_entry(tmp, struct uhci_qh, list);
tmp = tmp->next;
out += uhci_show_qh(qh, out, len - (out - buf), 4);
}
if (i < UHCI_NUM_SKELQH - 1) {
if (qh->link !=
(cpu_to_le32(uhci->skelqh[j]->dma_handle) | UHCI_PTR_QH))
out += sprintf(out, " last QH not linked to next skeleton!\n");
}
}
if (debug > 2)
out += uhci_show_lists(uhci, out, len - (out - buf));
spin_unlock_irqrestore(&uhci->lock, flags);
return out - buf;
}
#define MAX_OUTPUT (64 * 1024)
struct uhci_debug {
int size;
char *data;
struct uhci_hcd *uhci;
};
static int uhci_debug_open(struct inode *inode, struct file *file)
{
struct uhci_hcd *uhci = inode->u.generic_ip;
struct uhci_debug *up;
int ret = -ENOMEM;
lock_kernel();
up = kmalloc(sizeof(*up), GFP_KERNEL);
if (!up)
goto out;
up->data = kmalloc(MAX_OUTPUT, GFP_KERNEL);
if (!up->data) {
kfree(up);
goto out;
}
up->size = uhci_sprint_schedule(uhci, up->data, MAX_OUTPUT);
file->private_data = up;
ret = 0;
out:
unlock_kernel();
return ret;
}
static loff_t uhci_debug_lseek(struct file *file, loff_t off, int whence)
{
struct uhci_debug *up;
loff_t new = -1;
lock_kernel();
up = file->private_data;
switch (whence) {
case 0:
new = off;
break;
case 1:
new = file->f_pos + off;
break;
}
if (new < 0 || new > up->size) {
unlock_kernel();
return -EINVAL;
}
unlock_kernel();
return (file->f_pos = new);
}
static ssize_t uhci_debug_read(struct file *file, char __user *buf,
size_t nbytes, loff_t *ppos)
{
struct uhci_debug *up = file->private_data;
return simple_read_from_buffer(buf, nbytes, ppos, up->data, up->size);
}
static int uhci_debug_release(struct inode *inode, struct file *file)
{
struct uhci_debug *up = file->private_data;
kfree(up->data);
kfree(up);
return 0;
}
static struct file_operations uhci_debug_operations = {
.open = uhci_debug_open,
.llseek = uhci_debug_lseek,
.read = uhci_debug_read,
.release = uhci_debug_release,
};
#else /* CONFIG_DEBUG_FS */
#define uhci_debug_operations (* (struct file_operations *) NULL)
#endif

919
drivers/usb/host/uhci-hcd.c 一般檔案
查看文件

@@ -0,0 +1,919 @@
/*
* Universal Host Controller Interface driver for USB.
*
* Maintainer: Alan Stern <stern@rowland.harvard.edu>
*
* (C) Copyright 1999 Linus Torvalds
* (C) Copyright 1999-2002 Johannes Erdfelt, johannes@erdfelt.com
* (C) Copyright 1999 Randy Dunlap
* (C) Copyright 1999 Georg Acher, acher@in.tum.de
* (C) Copyright 1999 Deti Fliegl, deti@fliegl.de
* (C) Copyright 1999 Thomas Sailer, sailer@ife.ee.ethz.ch
* (C) Copyright 1999 Roman Weissgaerber, weissg@vienna.at
* (C) Copyright 2000 Yggdrasil Computing, Inc. (port of new PCI interface
* support from usb-ohci.c by Adam Richter, adam@yggdrasil.com).
* (C) Copyright 1999 Gregory P. Smith (from usb-ohci.c)
* (C) Copyright 2004 Alan Stern, stern@rowland.harvard.edu
*
* Intel documents this fairly well, and as far as I know there
* are no royalties or anything like that, but even so there are
* people who decided that they want to do the same thing in a
* completely different way.
*
* WARNING! The USB documentation is downright evil. Most of it
* is just crap, written by a committee. You're better off ignoring
* most of it, the important stuff is:
* - the low-level protocol (fairly simple but lots of small details)
* - working around the horridness of the rest
*/
#include <linux/config.h>
#ifdef CONFIG_USB_DEBUG
#define DEBUG
#else
#undef DEBUG
#endif
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/ioport.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/smp_lock.h>
#include <linux/errno.h>
#include <linux/unistd.h>
#include <linux/interrupt.h>
#include <linux/spinlock.h>
#include <linux/debugfs.h>
#include <linux/pm.h>
#include <linux/dmapool.h>
#include <linux/dma-mapping.h>
#include <linux/usb.h>
#include <linux/bitops.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/system.h>
#include "../core/hcd.h"
#include "uhci-hcd.h"
/*
* Version Information
*/
#define DRIVER_VERSION "v2.2"
#define DRIVER_AUTHOR "Linus 'Frodo Rabbit' Torvalds, Johannes Erdfelt, \
Randy Dunlap, Georg Acher, Deti Fliegl, Thomas Sailer, Roman Weissgaerber, \
Alan Stern"
#define DRIVER_DESC "USB Universal Host Controller Interface driver"
/*
* debug = 0, no debugging messages
* debug = 1, dump failed URB's except for stalls
* debug = 2, dump all failed URB's (including stalls)
* show all queues in /debug/uhci/[pci_addr]
* debug = 3, show all TD's in URB's when dumping
*/
#ifdef DEBUG
static int debug = 1;
#else
static int debug = 0;
#endif
module_param(debug, int, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(debug, "Debug level");
static char *errbuf;
#define ERRBUF_LEN (32 * 1024)
static kmem_cache_t *uhci_up_cachep; /* urb_priv */
static void uhci_get_current_frame_number(struct uhci_hcd *uhci);
static void hc_state_transitions(struct uhci_hcd *uhci);
/* If a transfer is still active after this much time, turn off FSBR */
#define IDLE_TIMEOUT msecs_to_jiffies(50)
#define FSBR_DELAY msecs_to_jiffies(50)
/* When we timeout an idle transfer for FSBR, we'll switch it over to */
/* depth first traversal. We'll do it in groups of this number of TD's */
/* to make sure it doesn't hog all of the bandwidth */
#define DEPTH_INTERVAL 5
#include "uhci-hub.c"
#include "uhci-debug.c"
#include "uhci-q.c"
static int init_stall_timer(struct usb_hcd *hcd);
static void stall_callback(unsigned long ptr)
{
struct usb_hcd *hcd = (struct usb_hcd *)ptr;
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
struct urb_priv *up;
unsigned long flags;
spin_lock_irqsave(&uhci->lock, flags);
uhci_scan_schedule(uhci, NULL);
list_for_each_entry(up, &uhci->urb_list, urb_list) {
struct urb *u = up->urb;
spin_lock(&u->lock);
/* Check if the FSBR timed out */
if (up->fsbr && !up->fsbr_timeout && time_after_eq(jiffies, up->fsbrtime + IDLE_TIMEOUT))
uhci_fsbr_timeout(uhci, u);
spin_unlock(&u->lock);
}
/* Really disable FSBR */
if (!uhci->fsbr && uhci->fsbrtimeout && time_after_eq(jiffies, uhci->fsbrtimeout)) {
uhci->fsbrtimeout = 0;
uhci->skel_term_qh->link = UHCI_PTR_TERM;
}
/* Poll for and perform state transitions */
hc_state_transitions(uhci);
if (unlikely(uhci->suspended_ports && uhci->state != UHCI_SUSPENDED))
uhci_check_ports(uhci);
init_stall_timer(hcd);
spin_unlock_irqrestore(&uhci->lock, flags);
}
static int init_stall_timer(struct usb_hcd *hcd)
{
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
init_timer(&uhci->stall_timer);
uhci->stall_timer.function = stall_callback;
uhci->stall_timer.data = (unsigned long)hcd;
uhci->stall_timer.expires = jiffies + msecs_to_jiffies(100);
add_timer(&uhci->stall_timer);
return 0;
}
static irqreturn_t uhci_irq(struct usb_hcd *hcd, struct pt_regs *regs)
{
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
unsigned long io_addr = uhci->io_addr;
unsigned short status;
/*
* Read the interrupt status, and write it back to clear the
* interrupt cause. Contrary to the UHCI specification, the
* "HC Halted" status bit is persistent: it is RO, not R/WC.
*/
status = inw(io_addr + USBSTS);
if (!(status & ~USBSTS_HCH)) /* shared interrupt, not mine */
return IRQ_NONE;
outw(status, io_addr + USBSTS); /* Clear it */
if (status & ~(USBSTS_USBINT | USBSTS_ERROR | USBSTS_RD)) {
if (status & USBSTS_HSE)
dev_err(uhci_dev(uhci), "host system error, "
"PCI problems?\n");
if (status & USBSTS_HCPE)
dev_err(uhci_dev(uhci), "host controller process "
"error, something bad happened!\n");
if ((status & USBSTS_HCH) && uhci->state > 0) {
dev_err(uhci_dev(uhci), "host controller halted, "
"very bad!\n");
/* FIXME: Reset the controller, fix the offending TD */
}
}
if (status & USBSTS_RD)
uhci->resume_detect = 1;
spin_lock(&uhci->lock);
uhci_scan_schedule(uhci, regs);
spin_unlock(&uhci->lock);
return IRQ_HANDLED;
}
static void reset_hc(struct uhci_hcd *uhci)
{
unsigned long io_addr = uhci->io_addr;
/* Turn off PIRQ, SMI, and all interrupts. This also turns off
* the BIOS's USB Legacy Support.
*/
pci_write_config_word(to_pci_dev(uhci_dev(uhci)), USBLEGSUP, 0);
outw(0, uhci->io_addr + USBINTR);
/* Global reset for 50ms */
uhci->state = UHCI_RESET;
outw(USBCMD_GRESET, io_addr + USBCMD);
msleep(50);
outw(0, io_addr + USBCMD);
/* Another 10ms delay */
msleep(10);
uhci->resume_detect = 0;
uhci->is_stopped = UHCI_IS_STOPPED;
}
static void suspend_hc(struct uhci_hcd *uhci)
{
unsigned long io_addr = uhci->io_addr;
dev_dbg(uhci_dev(uhci), "%s\n", __FUNCTION__);
uhci->state = UHCI_SUSPENDED;
uhci->resume_detect = 0;
outw(USBCMD_EGSM, io_addr + USBCMD);
/* FIXME: Wait for the controller to actually stop */
uhci_get_current_frame_number(uhci);
uhci->is_stopped = UHCI_IS_STOPPED;
uhci_scan_schedule(uhci, NULL);
}
static void wakeup_hc(struct uhci_hcd *uhci)
{
unsigned long io_addr = uhci->io_addr;
switch (uhci->state) {
case UHCI_SUSPENDED: /* Start the resume */
dev_dbg(uhci_dev(uhci), "%s\n", __FUNCTION__);
/* Global resume for >= 20ms */
outw(USBCMD_FGR | USBCMD_EGSM, io_addr + USBCMD);
uhci->state = UHCI_RESUMING_1;
uhci->state_end = jiffies + msecs_to_jiffies(20);
uhci->is_stopped = 0;
break;
case UHCI_RESUMING_1: /* End global resume */
uhci->state = UHCI_RESUMING_2;
outw(0, io_addr + USBCMD);
/* Falls through */
case UHCI_RESUMING_2: /* Wait for EOP to be sent */
if (inw(io_addr + USBCMD) & USBCMD_FGR)
break;
/* Run for at least 1 second, and
* mark it configured with a 64-byte max packet */
uhci->state = UHCI_RUNNING_GRACE;
uhci->state_end = jiffies + HZ;
outw(USBCMD_RS | USBCMD_CF | USBCMD_MAXP,
io_addr + USBCMD);
break;
case UHCI_RUNNING_GRACE: /* Now allowed to suspend */
uhci->state = UHCI_RUNNING;
break;
default:
break;
}
}
static int ports_active(struct uhci_hcd *uhci)
{
unsigned long io_addr = uhci->io_addr;
int connection = 0;
int i;
for (i = 0; i < uhci->rh_numports; i++)
connection |= (inw(io_addr + USBPORTSC1 + i * 2) & USBPORTSC_CCS);
return connection;
}
static int suspend_allowed(struct uhci_hcd *uhci)
{
unsigned long io_addr = uhci->io_addr;
int i;
if (to_pci_dev(uhci_dev(uhci))->vendor != PCI_VENDOR_ID_INTEL)
return 1;
/* Some of Intel's USB controllers have a bug that causes false
* resume indications if any port has an over current condition.
* To prevent problems, we will not allow a global suspend if
* any ports are OC.
*
* Some motherboards using Intel's chipsets (but not using all
* the USB ports) appear to hardwire the over current inputs active
* to disable the USB ports.
*/
/* check for over current condition on any port */
for (i = 0; i < uhci->rh_numports; i++) {
if (inw(io_addr + USBPORTSC1 + i * 2) & USBPORTSC_OC)
return 0;
}
return 1;
}
static void hc_state_transitions(struct uhci_hcd *uhci)
{
switch (uhci->state) {
case UHCI_RUNNING:
/* global suspend if nothing connected for 1 second */
if (!ports_active(uhci) && suspend_allowed(uhci)) {
uhci->state = UHCI_SUSPENDING_GRACE;
uhci->state_end = jiffies + HZ;
}
break;
case UHCI_SUSPENDING_GRACE:
if (ports_active(uhci))
uhci->state = UHCI_RUNNING;
else if (time_after_eq(jiffies, uhci->state_end))
suspend_hc(uhci);
break;
case UHCI_SUSPENDED:
/* wakeup if requested by a device */
if (uhci->resume_detect)
wakeup_hc(uhci);
break;
case UHCI_RESUMING_1:
case UHCI_RESUMING_2:
case UHCI_RUNNING_GRACE:
if (time_after_eq(jiffies, uhci->state_end))
wakeup_hc(uhci);
break;
default:
break;
}
}
/*
* Store the current frame number in uhci->frame_number if the controller
* is runnning
*/
static void uhci_get_current_frame_number(struct uhci_hcd *uhci)
{
if (!uhci->is_stopped)
uhci->frame_number = inw(uhci->io_addr + USBFRNUM);
}
static int start_hc(struct uhci_hcd *uhci)
{
unsigned long io_addr = uhci->io_addr;
int timeout = 10;
/*
* Reset the HC - this will force us to get a
* new notification of any already connected
* ports due to the virtual disconnect that it
* implies.
*/
outw(USBCMD_HCRESET, io_addr + USBCMD);
while (inw(io_addr + USBCMD) & USBCMD_HCRESET) {
if (--timeout < 0) {
dev_err(uhci_dev(uhci), "USBCMD_HCRESET timed out!\n");
return -ETIMEDOUT;
}
msleep(1);
}
/* Mark controller as running before we enable interrupts */
uhci_to_hcd(uhci)->state = HC_STATE_RUNNING;
/* Turn on PIRQ and all interrupts */
pci_write_config_word(to_pci_dev(uhci_dev(uhci)), USBLEGSUP,
USBLEGSUP_DEFAULT);
outw(USBINTR_TIMEOUT | USBINTR_RESUME | USBINTR_IOC | USBINTR_SP,
io_addr + USBINTR);
/* Start at frame 0 */
outw(0, io_addr + USBFRNUM);
outl(uhci->fl->dma_handle, io_addr + USBFLBASEADD);
/* Run and mark it configured with a 64-byte max packet */
uhci->state = UHCI_RUNNING_GRACE;
uhci->state_end = jiffies + HZ;
outw(USBCMD_RS | USBCMD_CF | USBCMD_MAXP, io_addr + USBCMD);
uhci->is_stopped = 0;
return 0;
}
/*
* De-allocate all resources
*/
static void release_uhci(struct uhci_hcd *uhci)
{
int i;
for (i = 0; i < UHCI_NUM_SKELQH; i++)
if (uhci->skelqh[i]) {
uhci_free_qh(uhci, uhci->skelqh[i]);
uhci->skelqh[i] = NULL;
}
if (uhci->term_td) {
uhci_free_td(uhci, uhci->term_td);
uhci->term_td = NULL;
}
if (uhci->qh_pool) {
dma_pool_destroy(uhci->qh_pool);
uhci->qh_pool = NULL;
}
if (uhci->td_pool) {
dma_pool_destroy(uhci->td_pool);
uhci->td_pool = NULL;
}
if (uhci->fl) {
dma_free_coherent(uhci_dev(uhci), sizeof(*uhci->fl),
uhci->fl, uhci->fl->dma_handle);
uhci->fl = NULL;
}
if (uhci->dentry) {
debugfs_remove(uhci->dentry);
uhci->dentry = NULL;
}
}
static int uhci_reset(struct usb_hcd *hcd)
{
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
uhci->io_addr = (unsigned long) hcd->rsrc_start;
/* Kick BIOS off this hardware and reset, so we won't get
* interrupts from any previous setup.
*/
reset_hc(uhci);
return 0;
}
/*
* Allocate a frame list, and then setup the skeleton
*
* The hardware doesn't really know any difference
* in the queues, but the order does matter for the
* protocols higher up. The order is:
*
* - any isochronous events handled before any
* of the queues. We don't do that here, because
* we'll create the actual TD entries on demand.
* - The first queue is the interrupt queue.
* - The second queue is the control queue, split into low- and full-speed
* - The third queue is bulk queue.
* - The fourth queue is the bandwidth reclamation queue, which loops back
* to the full-speed control queue.
*/
static int uhci_start(struct usb_hcd *hcd)
{
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
int retval = -EBUSY;
int i, port;
unsigned io_size;
dma_addr_t dma_handle;
struct usb_device *udev;
struct dentry *dentry;
io_size = (unsigned) hcd->rsrc_len;
dentry = debugfs_create_file(hcd->self.bus_name, S_IFREG|S_IRUGO|S_IWUSR, uhci_debugfs_root, uhci, &uhci_debug_operations);
if (!dentry) {
dev_err(uhci_dev(uhci), "couldn't create uhci debugfs entry\n");
retval = -ENOMEM;
goto err_create_debug_entry;
}
uhci->dentry = dentry;
uhci->fsbr = 0;
uhci->fsbrtimeout = 0;
spin_lock_init(&uhci->lock);
INIT_LIST_HEAD(&uhci->qh_remove_list);
INIT_LIST_HEAD(&uhci->td_remove_list);
INIT_LIST_HEAD(&uhci->urb_remove_list);
INIT_LIST_HEAD(&uhci->urb_list);
INIT_LIST_HEAD(&uhci->complete_list);
init_waitqueue_head(&uhci->waitqh);
uhci->fl = dma_alloc_coherent(uhci_dev(uhci), sizeof(*uhci->fl),
&dma_handle, 0);
if (!uhci->fl) {
dev_err(uhci_dev(uhci), "unable to allocate "
"consistent memory for frame list\n");
goto err_alloc_fl;
}
memset((void *)uhci->fl, 0, sizeof(*uhci->fl));
uhci->fl->dma_handle = dma_handle;
uhci->td_pool = dma_pool_create("uhci_td", uhci_dev(uhci),
sizeof(struct uhci_td), 16, 0);
if (!uhci->td_pool) {
dev_err(uhci_dev(uhci), "unable to create td dma_pool\n");
goto err_create_td_pool;
}
uhci->qh_pool = dma_pool_create("uhci_qh", uhci_dev(uhci),
sizeof(struct uhci_qh), 16, 0);
if (!uhci->qh_pool) {
dev_err(uhci_dev(uhci), "unable to create qh dma_pool\n");
goto err_create_qh_pool;
}
/* Initialize the root hub */
/* UHCI specs says devices must have 2 ports, but goes on to say */
/* they may have more but give no way to determine how many they */
/* have. However, according to the UHCI spec, Bit 7 is always set */
/* to 1. So we try to use this to our advantage */
for (port = 0; port < (io_size - 0x10) / 2; port++) {
unsigned int portstatus;
portstatus = inw(uhci->io_addr + 0x10 + (port * 2));
if (!(portstatus & 0x0080))
break;
}
if (debug)
dev_info(uhci_dev(uhci), "detected %d ports\n", port);
/* This is experimental so anything less than 2 or greater than 8 is */
/* something weird and we'll ignore it */
if (port < 2 || port > UHCI_RH_MAXCHILD) {
dev_info(uhci_dev(uhci), "port count misdetected? "
"forcing to 2 ports\n");
port = 2;
}
uhci->rh_numports = port;
udev = usb_alloc_dev(NULL, &hcd->self, 0);
if (!udev) {
dev_err(uhci_dev(uhci), "unable to allocate root hub\n");
goto err_alloc_root_hub;
}
uhci->term_td = uhci_alloc_td(uhci, udev);
if (!uhci->term_td) {
dev_err(uhci_dev(uhci), "unable to allocate terminating TD\n");
goto err_alloc_term_td;
}
for (i = 0; i < UHCI_NUM_SKELQH; i++) {
uhci->skelqh[i] = uhci_alloc_qh(uhci, udev);
if (!uhci->skelqh[i]) {
dev_err(uhci_dev(uhci), "unable to allocate QH\n");
goto err_alloc_skelqh;
}
}
/*
* 8 Interrupt queues; link all higher int queues to int1,
* then link int1 to control and control to bulk
*/
uhci->skel_int128_qh->link =
uhci->skel_int64_qh->link =
uhci->skel_int32_qh->link =
uhci->skel_int16_qh->link =
uhci->skel_int8_qh->link =
uhci->skel_int4_qh->link =
uhci->skel_int2_qh->link =
cpu_to_le32(uhci->skel_int1_qh->dma_handle) | UHCI_PTR_QH;
uhci->skel_int1_qh->link = cpu_to_le32(uhci->skel_ls_control_qh->dma_handle) | UHCI_PTR_QH;
uhci->skel_ls_control_qh->link = cpu_to_le32(uhci->skel_fs_control_qh->dma_handle) | UHCI_PTR_QH;
uhci->skel_fs_control_qh->link = cpu_to_le32(uhci->skel_bulk_qh->dma_handle) | UHCI_PTR_QH;
uhci->skel_bulk_qh->link = cpu_to_le32(uhci->skel_term_qh->dma_handle) | UHCI_PTR_QH;
/* This dummy TD is to work around a bug in Intel PIIX controllers */
uhci_fill_td(uhci->term_td, 0, (UHCI_NULL_DATA_SIZE << 21) |
(0x7f << TD_TOKEN_DEVADDR_SHIFT) | USB_PID_IN, 0);
uhci->term_td->link = cpu_to_le32(uhci->term_td->dma_handle);
uhci->skel_term_qh->link = UHCI_PTR_TERM;
uhci->skel_term_qh->element = cpu_to_le32(uhci->term_td->dma_handle);
/*
* Fill the frame list: make all entries point to the proper
* interrupt queue.
*
* The interrupt queues will be interleaved as evenly as possible.
* There's not much to be done about period-1 interrupts; they have
* to occur in every frame. But we can schedule period-2 interrupts
* in odd-numbered frames, period-4 interrupts in frames congruent
* to 2 (mod 4), and so on. This way each frame only has two
* interrupt QHs, which will help spread out bandwidth utilization.
*/
for (i = 0; i < UHCI_NUMFRAMES; i++) {
int irq;
/*
* ffs (Find First bit Set) does exactly what we need:
* 1,3,5,... => ffs = 0 => use skel_int2_qh = skelqh[6],
* 2,6,10,... => ffs = 1 => use skel_int4_qh = skelqh[5], etc.
* ffs > 6 => not on any high-period queue, so use
* skel_int1_qh = skelqh[7].
* Add UHCI_NUMFRAMES to insure at least one bit is set.
*/
irq = 6 - (int) __ffs(i + UHCI_NUMFRAMES);
if (irq < 0)
irq = 7;
/* Only place we don't use the frame list routines */
uhci->fl->frame[i] = UHCI_PTR_QH |
cpu_to_le32(uhci->skelqh[irq]->dma_handle);
}
/*
* Some architectures require a full mb() to enforce completion of
* the memory writes above before the I/O transfers in start_hc().
*/
mb();
if ((retval = start_hc(uhci)) != 0)
goto err_alloc_skelqh;
init_stall_timer(hcd);
udev->speed = USB_SPEED_FULL;
if (usb_hcd_register_root_hub(udev, hcd) != 0) {
dev_err(uhci_dev(uhci), "unable to start root hub\n");
retval = -ENOMEM;
goto err_start_root_hub;
}
return 0;
/*
* error exits:
*/
err_start_root_hub:
reset_hc(uhci);
del_timer_sync(&uhci->stall_timer);
err_alloc_skelqh:
for (i = 0; i < UHCI_NUM_SKELQH; i++)
if (uhci->skelqh[i]) {
uhci_free_qh(uhci, uhci->skelqh[i]);
uhci->skelqh[i] = NULL;
}
uhci_free_td(uhci, uhci->term_td);
uhci->term_td = NULL;
err_alloc_term_td:
usb_put_dev(udev);
err_alloc_root_hub:
dma_pool_destroy(uhci->qh_pool);
uhci->qh_pool = NULL;
err_create_qh_pool:
dma_pool_destroy(uhci->td_pool);
uhci->td_pool = NULL;
err_create_td_pool:
dma_free_coherent(uhci_dev(uhci), sizeof(*uhci->fl),
uhci->fl, uhci->fl->dma_handle);
uhci->fl = NULL;
err_alloc_fl:
debugfs_remove(uhci->dentry);
uhci->dentry = NULL;
err_create_debug_entry:
return retval;
}
static void uhci_stop(struct usb_hcd *hcd)
{
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
del_timer_sync(&uhci->stall_timer);
reset_hc(uhci);
spin_lock_irq(&uhci->lock);
uhci_scan_schedule(uhci, NULL);
spin_unlock_irq(&uhci->lock);
release_uhci(uhci);
}
#ifdef CONFIG_PM
static int uhci_suspend(struct usb_hcd *hcd, u32 state)
{
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
spin_lock_irq(&uhci->lock);
/* Don't try to suspend broken motherboards, reset instead */
if (suspend_allowed(uhci))
suspend_hc(uhci);
else {
spin_unlock_irq(&uhci->lock);
reset_hc(uhci);
spin_lock_irq(&uhci->lock);
uhci_scan_schedule(uhci, NULL);
}
spin_unlock_irq(&uhci->lock);
return 0;
}
static int uhci_resume(struct usb_hcd *hcd)
{
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
int rc;
pci_set_master(to_pci_dev(uhci_dev(uhci)));
spin_lock_irq(&uhci->lock);
if (uhci->state == UHCI_SUSPENDED) {
/*
* Some systems don't maintain the UHCI register values
* during a PM suspend/resume cycle, so reinitialize
* the Frame Number, Framelist Base Address, Interrupt
* Enable, and Legacy Support registers.
*/
pci_write_config_word(to_pci_dev(uhci_dev(uhci)), USBLEGSUP,
0);
outw(uhci->frame_number, uhci->io_addr + USBFRNUM);
outl(uhci->fl->dma_handle, uhci->io_addr + USBFLBASEADD);
outw(USBINTR_TIMEOUT | USBINTR_RESUME | USBINTR_IOC |
USBINTR_SP, uhci->io_addr + USBINTR);
uhci->resume_detect = 1;
pci_write_config_word(to_pci_dev(uhci_dev(uhci)), USBLEGSUP,
USBLEGSUP_DEFAULT);
} else {
spin_unlock_irq(&uhci->lock);
reset_hc(uhci);
if ((rc = start_hc(uhci)) != 0)
return rc;
spin_lock_irq(&uhci->lock);
}
hcd->state = HC_STATE_RUNNING;
spin_unlock_irq(&uhci->lock);
return 0;
}
#endif
/* Wait until all the URBs for a particular device/endpoint are gone */
static void uhci_hcd_endpoint_disable(struct usb_hcd *hcd,
struct usb_host_endpoint *ep)
{
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
wait_event_interruptible(uhci->waitqh, list_empty(&ep->urb_list));
}
static int uhci_hcd_get_frame_number(struct usb_hcd *hcd)
{
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
int frame_number;
unsigned long flags;
/* Minimize latency by avoiding the spinlock */
local_irq_save(flags);
rmb();
frame_number = (uhci->is_stopped ? uhci->frame_number :
inw(uhci->io_addr + USBFRNUM));
local_irq_restore(flags);
return frame_number;
}
static const char hcd_name[] = "uhci_hcd";
static const struct hc_driver uhci_driver = {
.description = hcd_name,
.product_desc = "UHCI Host Controller",
.hcd_priv_size = sizeof(struct uhci_hcd),
/* Generic hardware linkage */
.irq = uhci_irq,
.flags = HCD_USB11,
/* Basic lifecycle operations */
.reset = uhci_reset,
.start = uhci_start,
#ifdef CONFIG_PM
.suspend = uhci_suspend,
.resume = uhci_resume,
#endif
.stop = uhci_stop,
.urb_enqueue = uhci_urb_enqueue,
.urb_dequeue = uhci_urb_dequeue,
.endpoint_disable = uhci_hcd_endpoint_disable,
.get_frame_number = uhci_hcd_get_frame_number,
.hub_status_data = uhci_hub_status_data,
.hub_control = uhci_hub_control,
};
static const struct pci_device_id uhci_pci_ids[] = { {
/* handle any USB UHCI controller */
PCI_DEVICE_CLASS(((PCI_CLASS_SERIAL_USB << 8) | 0x00), ~0),
.driver_data = (unsigned long) &uhci_driver,
}, { /* end: all zeroes */ }
};
MODULE_DEVICE_TABLE(pci, uhci_pci_ids);
static struct pci_driver uhci_pci_driver = {
.name = (char *)hcd_name,
.id_table = uhci_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 /* PM */
};
static int __init uhci_hcd_init(void)
{
int retval = -ENOMEM;
printk(KERN_INFO DRIVER_DESC " " DRIVER_VERSION "\n");
if (usb_disabled())
return -ENODEV;
if (debug) {
errbuf = kmalloc(ERRBUF_LEN, GFP_KERNEL);
if (!errbuf)
goto errbuf_failed;
}
uhci_debugfs_root = debugfs_create_dir("uhci", NULL);
if (!uhci_debugfs_root)
goto debug_failed;
uhci_up_cachep = kmem_cache_create("uhci_urb_priv",
sizeof(struct urb_priv), 0, 0, NULL, NULL);
if (!uhci_up_cachep)
goto up_failed;
retval = pci_register_driver(&uhci_pci_driver);
if (retval)
goto init_failed;
return 0;
init_failed:
if (kmem_cache_destroy(uhci_up_cachep))
warn("not all urb_priv's were freed!");
up_failed:
debugfs_remove(uhci_debugfs_root);
debug_failed:
if (errbuf)
kfree(errbuf);
errbuf_failed:
return retval;
}
static void __exit uhci_hcd_cleanup(void)
{
pci_unregister_driver(&uhci_pci_driver);
if (kmem_cache_destroy(uhci_up_cachep))
warn("not all urb_priv's were freed!");
debugfs_remove(uhci_debugfs_root);
if (errbuf)
kfree(errbuf);
}
module_init(uhci_hcd_init);
module_exit(uhci_hcd_cleanup);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");

454
drivers/usb/host/uhci-hcd.h 一般檔案
查看文件

@@ -0,0 +1,454 @@
#ifndef __LINUX_UHCI_HCD_H
#define __LINUX_UHCI_HCD_H
#include <linux/list.h>
#include <linux/usb.h>
#define usb_packetid(pipe) (usb_pipein(pipe) ? USB_PID_IN : USB_PID_OUT)
#define PIPE_DEVEP_MASK 0x0007ff00
/*
* Universal Host Controller Interface data structures and defines
*/
/* Command register */
#define USBCMD 0
#define USBCMD_RS 0x0001 /* Run/Stop */
#define USBCMD_HCRESET 0x0002 /* Host reset */
#define USBCMD_GRESET 0x0004 /* Global reset */
#define USBCMD_EGSM 0x0008 /* Global Suspend Mode */
#define USBCMD_FGR 0x0010 /* Force Global Resume */
#define USBCMD_SWDBG 0x0020 /* SW Debug mode */
#define USBCMD_CF 0x0040 /* Config Flag (sw only) */
#define USBCMD_MAXP 0x0080 /* Max Packet (0 = 32, 1 = 64) */
/* Status register */
#define USBSTS 2
#define USBSTS_USBINT 0x0001 /* Interrupt due to IOC */
#define USBSTS_ERROR 0x0002 /* Interrupt due to error */
#define USBSTS_RD 0x0004 /* Resume Detect */
#define USBSTS_HSE 0x0008 /* Host System Error - basically PCI problems */
#define USBSTS_HCPE 0x0010 /* Host Controller Process Error - the scripts were buggy */
#define USBSTS_HCH 0x0020 /* HC Halted */
/* Interrupt enable register */
#define USBINTR 4
#define USBINTR_TIMEOUT 0x0001 /* Timeout/CRC error enable */
#define USBINTR_RESUME 0x0002 /* Resume interrupt enable */
#define USBINTR_IOC 0x0004 /* Interrupt On Complete enable */
#define USBINTR_SP 0x0008 /* Short packet interrupt enable */
#define USBFRNUM 6
#define USBFLBASEADD 8
#define USBSOF 12
/* USB port status and control registers */
#define USBPORTSC1 16
#define USBPORTSC2 18
#define USBPORTSC_CCS 0x0001 /* Current Connect Status ("device present") */
#define USBPORTSC_CSC 0x0002 /* Connect Status Change */
#define USBPORTSC_PE 0x0004 /* Port Enable */
#define USBPORTSC_PEC 0x0008 /* Port Enable Change */
#define USBPORTSC_DPLUS 0x0010 /* D+ high (line status) */
#define USBPORTSC_DMINUS 0x0020 /* D- high (line status) */
#define USBPORTSC_RD 0x0040 /* Resume Detect */
#define USBPORTSC_RES1 0x0080 /* reserved, always 1 */
#define USBPORTSC_LSDA 0x0100 /* Low Speed Device Attached */
#define USBPORTSC_PR 0x0200 /* Port Reset */
/* OC and OCC from Intel 430TX and later (not UHCI 1.1d spec) */
#define USBPORTSC_OC 0x0400 /* Over Current condition */
#define USBPORTSC_OCC 0x0800 /* Over Current Change R/WC */
#define USBPORTSC_SUSP 0x1000 /* Suspend */
#define USBPORTSC_RES2 0x2000 /* reserved, write zeroes */
#define USBPORTSC_RES3 0x4000 /* reserved, write zeroes */
#define USBPORTSC_RES4 0x8000 /* reserved, write zeroes */
/* Legacy support register */
#define USBLEGSUP 0xc0
#define USBLEGSUP_DEFAULT 0x2000 /* only PIRQ enable set */
#define UHCI_NULL_DATA_SIZE 0x7FF /* for UHCI controller TD */
#define UHCI_PTR_BITS cpu_to_le32(0x000F)
#define UHCI_PTR_TERM cpu_to_le32(0x0001)
#define UHCI_PTR_QH cpu_to_le32(0x0002)
#define UHCI_PTR_DEPTH cpu_to_le32(0x0004)
#define UHCI_PTR_BREADTH cpu_to_le32(0x0000)
#define UHCI_NUMFRAMES 1024 /* in the frame list [array] */
#define UHCI_MAX_SOF_NUMBER 2047 /* in an SOF packet */
#define CAN_SCHEDULE_FRAMES 1000 /* how far future frames can be scheduled */
struct uhci_frame_list {
__le32 frame[UHCI_NUMFRAMES];
void *frame_cpu[UHCI_NUMFRAMES];
dma_addr_t dma_handle;
};
struct urb_priv;
/*
* One role of a QH is to hold a queue of TDs for some endpoint. Each QH is
* used with one URB, and qh->element (updated by the HC) is either:
* - the next unprocessed TD for the URB, or
* - UHCI_PTR_TERM (when there's no more traffic for this endpoint), or
* - the QH for the next URB queued to the same endpoint.
*
* The other role of a QH is to serve as a "skeleton" framelist entry, so we
* can easily splice a QH for some endpoint into the schedule at the right
* place. Then qh->element is UHCI_PTR_TERM.
*
* In the frame list, qh->link maintains a list of QHs seen by the HC:
* skel1 --> ep1-qh --> ep2-qh --> ... --> skel2 --> ...
*/
struct uhci_qh {
/* Hardware fields */
__le32 link; /* Next queue */
__le32 element; /* Queue element pointer */
/* Software fields */
dma_addr_t dma_handle;
struct usb_device *dev;
struct urb_priv *urbp;
struct list_head list; /* P: uhci->frame_list_lock */
struct list_head remove_list; /* P: uhci->remove_list_lock */
} __attribute__((aligned(16)));
/*
* We need a special accessor for the element pointer because it is
* subject to asynchronous updates by the controller
*/
static __le32 inline qh_element(struct uhci_qh *qh) {
__le32 element = qh->element;
barrier();
return element;
}
/*
* for TD <status>:
*/
#define TD_CTRL_SPD (1 << 29) /* Short Packet Detect */
#define TD_CTRL_C_ERR_MASK (3 << 27) /* Error Counter bits */
#define TD_CTRL_C_ERR_SHIFT 27
#define TD_CTRL_LS (1 << 26) /* Low Speed Device */
#define TD_CTRL_IOS (1 << 25) /* Isochronous Select */
#define TD_CTRL_IOC (1 << 24) /* Interrupt on Complete */
#define TD_CTRL_ACTIVE (1 << 23) /* TD Active */
#define TD_CTRL_STALLED (1 << 22) /* TD Stalled */
#define TD_CTRL_DBUFERR (1 << 21) /* Data Buffer Error */
#define TD_CTRL_BABBLE (1 << 20) /* Babble Detected */
#define TD_CTRL_NAK (1 << 19) /* NAK Received */
#define TD_CTRL_CRCTIMEO (1 << 18) /* CRC/Time Out Error */
#define TD_CTRL_BITSTUFF (1 << 17) /* Bit Stuff Error */
#define TD_CTRL_ACTLEN_MASK 0x7FF /* actual length, encoded as n - 1 */
#define TD_CTRL_ANY_ERROR (TD_CTRL_STALLED | TD_CTRL_DBUFERR | \
TD_CTRL_BABBLE | TD_CTRL_CRCTIME | TD_CTRL_BITSTUFF)
#define uhci_maxerr(err) ((err) << TD_CTRL_C_ERR_SHIFT)
#define uhci_status_bits(ctrl_sts) ((ctrl_sts) & 0xF60000)
#define uhci_actual_length(ctrl_sts) (((ctrl_sts) + 1) & TD_CTRL_ACTLEN_MASK) /* 1-based */
/*
* for TD <info>: (a.k.a. Token)
*/
#define td_token(td) le32_to_cpu((td)->token)
#define TD_TOKEN_DEVADDR_SHIFT 8
#define TD_TOKEN_TOGGLE_SHIFT 19
#define TD_TOKEN_TOGGLE (1 << 19)
#define TD_TOKEN_EXPLEN_SHIFT 21
#define TD_TOKEN_EXPLEN_MASK 0x7FF /* expected length, encoded as n - 1 */
#define TD_TOKEN_PID_MASK 0xFF
#define uhci_explen(len) ((len) << TD_TOKEN_EXPLEN_SHIFT)
#define uhci_expected_length(token) ((((token) >> 21) + 1) & TD_TOKEN_EXPLEN_MASK)
#define uhci_toggle(token) (((token) >> TD_TOKEN_TOGGLE_SHIFT) & 1)
#define uhci_endpoint(token) (((token) >> 15) & 0xf)
#define uhci_devaddr(token) (((token) >> TD_TOKEN_DEVADDR_SHIFT) & 0x7f)
#define uhci_devep(token) (((token) >> TD_TOKEN_DEVADDR_SHIFT) & 0x7ff)
#define uhci_packetid(token) ((token) & TD_TOKEN_PID_MASK)
#define uhci_packetout(token) (uhci_packetid(token) != USB_PID_IN)
#define uhci_packetin(token) (uhci_packetid(token) == USB_PID_IN)
/*
* The documentation says "4 words for hardware, 4 words for software".
*
* That's silly, the hardware doesn't care. The hardware only cares that
* the hardware words are 16-byte aligned, and we can have any amount of
* sw space after the TD entry as far as I can tell.
*
* But let's just go with the documentation, at least for 32-bit machines.
* On 64-bit machines we probably want to take advantage of the fact that
* hw doesn't really care about the size of the sw-only area.
*
* Alas, not anymore, we have more than 4 words for software, woops.
* Everything still works tho, surprise! -jerdfelt
*
* td->link points to either another TD (not necessarily for the same urb or
* even the same endpoint), or nothing (PTR_TERM), or a QH (for queued urbs)
*/
struct uhci_td {
/* Hardware fields */
__le32 link;
__le32 status;
__le32 token;
__le32 buffer;
/* Software fields */
dma_addr_t dma_handle;
struct usb_device *dev;
struct urb *urb;
struct list_head list; /* P: urb->lock */
struct list_head remove_list; /* P: uhci->td_remove_list_lock */
int frame; /* for iso: what frame? */
struct list_head fl_list; /* P: uhci->frame_list_lock */
} __attribute__((aligned(16)));
/*
* We need a special accessor for the control/status word because it is
* subject to asynchronous updates by the controller
*/
static u32 inline td_status(struct uhci_td *td) {
__le32 status = td->status;
barrier();
return le32_to_cpu(status);
}
/*
* The UHCI driver places Interrupt, Control and Bulk into QH's both
* to group together TD's for one transfer, and also to faciliate queuing
* of URB's. To make it easy to insert entries into the schedule, we have
* a skeleton of QH's for each predefined Interrupt latency, low-speed
* control, full-speed control and terminating QH (see explanation for
* the terminating QH below).
*
* When we want to add a new QH, we add it to the end of the list for the
* skeleton QH.
*
* For instance, the queue can look like this:
*
* skel int128 QH
* dev 1 interrupt QH
* dev 5 interrupt QH
* skel int64 QH
* skel int32 QH
* ...
* skel int1 QH
* skel low-speed control QH
* dev 5 control QH
* skel full-speed control QH
* skel bulk QH
* dev 1 bulk QH
* dev 2 bulk QH
* skel terminating QH
*
* The terminating QH is used for 2 reasons:
* - To place a terminating TD which is used to workaround a PIIX bug
* (see Intel errata for explanation)
* - To loop back to the full-speed control queue for full-speed bandwidth
* reclamation
*
* Isochronous transfers are stored before the start of the skeleton
* schedule and don't use QH's. While the UHCI spec doesn't forbid the
* use of QH's for Isochronous, it doesn't use them either. Since we don't
* need to use them either, we follow the spec diagrams in hope that it'll
* be more compatible with future UHCI implementations.
*/
#define UHCI_NUM_SKELQH 12
#define skel_int128_qh skelqh[0]
#define skel_int64_qh skelqh[1]
#define skel_int32_qh skelqh[2]
#define skel_int16_qh skelqh[3]
#define skel_int8_qh skelqh[4]
#define skel_int4_qh skelqh[5]
#define skel_int2_qh skelqh[6]
#define skel_int1_qh skelqh[7]
#define skel_ls_control_qh skelqh[8]
#define skel_fs_control_qh skelqh[9]
#define skel_bulk_qh skelqh[10]
#define skel_term_qh skelqh[11]
/*
* Search tree for determining where <interval> fits in the skelqh[]
* skeleton.
*
* An interrupt request should be placed into the slowest skelqh[]
* which meets the interval/period/frequency requirement.
* An interrupt request is allowed to be faster than <interval> but not slower.
*
* For a given <interval>, this function returns the appropriate/matching
* skelqh[] index value.
*/
static inline int __interval_to_skel(int interval)
{
if (interval < 16) {
if (interval < 4) {
if (interval < 2)
return 7; /* int1 for 0-1 ms */
return 6; /* int2 for 2-3 ms */
}
if (interval < 8)
return 5; /* int4 for 4-7 ms */
return 4; /* int8 for 8-15 ms */
}
if (interval < 64) {
if (interval < 32)
return 3; /* int16 for 16-31 ms */
return 2; /* int32 for 32-63 ms */
}
if (interval < 128)
return 1; /* int64 for 64-127 ms */
return 0; /* int128 for 128-255 ms (Max.) */
}
/*
* Device states for the host controller.
*
* To prevent "bouncing" in the presence of electrical noise,
* we insist on a 1-second "grace" period, before switching to
* the RUNNING or SUSPENDED states, during which the state is
* not allowed to change.
*
* The resume process is divided into substates in order to avoid
* potentially length delays during the timer handler.
*
* States in which the host controller is halted must have values <= 0.
*/
enum uhci_state {
UHCI_RESET,
UHCI_RUNNING_GRACE, /* Before RUNNING */
UHCI_RUNNING, /* The normal state */
UHCI_SUSPENDING_GRACE, /* Before SUSPENDED */
UHCI_SUSPENDED = -10, /* When no devices are attached */
UHCI_RESUMING_1,
UHCI_RESUMING_2
};
/*
* This describes the full uhci information.
*
* Note how the "proper" USB information is just
* a subset of what the full implementation needs.
*/
struct uhci_hcd {
/* debugfs */
struct dentry *dentry;
/* Grabbed from PCI */
unsigned long io_addr;
struct dma_pool *qh_pool;
struct dma_pool *td_pool;
struct usb_bus *bus;
struct uhci_td *term_td; /* Terminating TD, see UHCI bug */
struct uhci_qh *skelqh[UHCI_NUM_SKELQH]; /* Skeleton QH's */
spinlock_t lock;
struct uhci_frame_list *fl; /* P: uhci->lock */
int fsbr; /* Full-speed bandwidth reclamation */
unsigned long fsbrtimeout; /* FSBR delay */
enum uhci_state state; /* FIXME: needs a spinlock */
unsigned long state_end; /* Time of next transition */
unsigned int frame_number; /* As of last check */
unsigned int is_stopped;
#define UHCI_IS_STOPPED 9999 /* Larger than a frame # */
unsigned int scan_in_progress:1; /* Schedule scan is running */
unsigned int need_rescan:1; /* Redo the schedule scan */
unsigned int resume_detect:1; /* Need a Global Resume */
/* Support for port suspend/resume/reset */
unsigned long port_c_suspend; /* Bit-arrays of ports */
unsigned long suspended_ports;
unsigned long resuming_ports;
unsigned long ports_timeout; /* Time to stop signalling */
/* Main list of URB's currently controlled by this HC */
struct list_head urb_list; /* P: uhci->lock */
/* List of QH's that are done, but waiting to be unlinked (race) */
struct list_head qh_remove_list; /* P: uhci->lock */
unsigned int qh_remove_age; /* Age in frames */
/* List of TD's that are done, but waiting to be freed (race) */
struct list_head td_remove_list; /* P: uhci->lock */
unsigned int td_remove_age; /* Age in frames */
/* List of asynchronously unlinked URB's */
struct list_head urb_remove_list; /* P: uhci->lock */
unsigned int urb_remove_age; /* Age in frames */
/* List of URB's awaiting completion callback */
struct list_head complete_list; /* P: uhci->lock */
int rh_numports;
struct timer_list stall_timer;
wait_queue_head_t waitqh; /* endpoint_disable waiters */
};
/* Convert between a usb_hcd pointer and the corresponding uhci_hcd */
static inline struct uhci_hcd *hcd_to_uhci(struct usb_hcd *hcd)
{
return (struct uhci_hcd *) (hcd->hcd_priv);
}
static inline struct usb_hcd *uhci_to_hcd(struct uhci_hcd *uhci)
{
return container_of((void *) uhci, struct usb_hcd, hcd_priv);
}
#define uhci_dev(u) (uhci_to_hcd(u)->self.controller)
struct urb_priv {
struct list_head urb_list;
struct urb *urb;
struct uhci_qh *qh; /* QH for this URB */
struct list_head td_list; /* P: urb->lock */
unsigned fsbr : 1; /* URB turned on FSBR */
unsigned fsbr_timeout : 1; /* URB timed out on FSBR */
unsigned queued : 1; /* QH was queued (not linked in) */
unsigned short_control_packet : 1; /* If we get a short packet during */
/* a control transfer, retrigger */
/* the status phase */
unsigned long inserttime; /* In jiffies */
unsigned long fsbrtime; /* In jiffies */
struct list_head queue_list; /* P: uhci->frame_list_lock */
};
/*
* Locking in uhci.c
*
* Almost everything relating to the hardware schedule and processing
* of URBs is protected by uhci->lock. urb->status is protected by
* urb->lock; that's the one exception.
*
* To prevent deadlocks, never lock uhci->lock while holding urb->lock.
* The safe order of locking is:
*
* #1 uhci->lock
* #2 urb->lock
*/
#endif

299
drivers/usb/host/uhci-hub.c 一般檔案
查看文件

@@ -0,0 +1,299 @@
/*
* Universal Host Controller Interface driver for USB.
*
* Maintainer: Alan Stern <stern@rowland.harvard.edu>
*
* (C) Copyright 1999 Linus Torvalds
* (C) Copyright 1999-2002 Johannes Erdfelt, johannes@erdfelt.com
* (C) Copyright 1999 Randy Dunlap
* (C) Copyright 1999 Georg Acher, acher@in.tum.de
* (C) Copyright 1999 Deti Fliegl, deti@fliegl.de
* (C) Copyright 1999 Thomas Sailer, sailer@ife.ee.ethz.ch
* (C) Copyright 2004 Alan Stern, stern@rowland.harvard.edu
*/
static __u8 root_hub_hub_des[] =
{
0x09, /* __u8 bLength; */
0x29, /* __u8 bDescriptorType; Hub-descriptor */
0x02, /* __u8 bNbrPorts; */
0x0a, /* __u16 wHubCharacteristics; */
0x00, /* (per-port OC, no power switching) */
0x01, /* __u8 bPwrOn2pwrGood; 2ms */
0x00, /* __u8 bHubContrCurrent; 0 mA */
0x00, /* __u8 DeviceRemovable; *** 7 Ports max *** */
0xff /* __u8 PortPwrCtrlMask; *** 7 ports max *** */
};
#define UHCI_RH_MAXCHILD 7
/* must write as zeroes */
#define WZ_BITS (USBPORTSC_RES2 | USBPORTSC_RES3 | USBPORTSC_RES4)
/* status change bits: nonzero writes will clear */
#define RWC_BITS (USBPORTSC_OCC | USBPORTSC_PEC | USBPORTSC_CSC)
static int uhci_hub_status_data(struct usb_hcd *hcd, char *buf)
{
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
int port;
*buf = 0;
for (port = 0; port < uhci->rh_numports; ++port) {
if ((inw(uhci->io_addr + USBPORTSC1 + port * 2) & RWC_BITS) ||
test_bit(port, &uhci->port_c_suspend))
*buf |= (1 << (port + 1));
}
if (*buf && uhci->state == UHCI_SUSPENDED)
uhci->resume_detect = 1;
return !!*buf;
}
#define OK(x) len = (x); break
#define CLR_RH_PORTSTAT(x) \
status = inw(port_addr); \
status &= ~(RWC_BITS|WZ_BITS); \
status &= ~(x); \
status |= RWC_BITS & (x); \
outw(status, port_addr)
#define SET_RH_PORTSTAT(x) \
status = inw(port_addr); \
status |= (x); \
status &= ~(RWC_BITS|WZ_BITS); \
outw(status, port_addr)
/* UHCI controllers don't automatically stop resume signalling after 20 msec,
* so we have to poll and check timeouts in order to take care of it.
*/
static void uhci_finish_suspend(struct uhci_hcd *uhci, int port,
unsigned long port_addr)
{
int status;
if (test_bit(port, &uhci->suspended_ports)) {
CLR_RH_PORTSTAT(USBPORTSC_SUSP | USBPORTSC_RD);
clear_bit(port, &uhci->suspended_ports);
clear_bit(port, &uhci->resuming_ports);
set_bit(port, &uhci->port_c_suspend);
/* The controller won't actually turn off the RD bit until
* it has had a chance to send a low-speed EOP sequence,
* which takes 3 bit times (= 2 microseconds). We'll delay
* slightly longer for good luck. */
udelay(4);
}
}
static void uhci_check_ports(struct uhci_hcd *uhci)
{
unsigned int port;
unsigned long port_addr;
int status;
for (port = 0; port < uhci->rh_numports; ++port) {
port_addr = uhci->io_addr + USBPORTSC1 + 2 * port;
status = inw(port_addr);
if (unlikely(status & USBPORTSC_PR)) {
if (time_after_eq(jiffies, uhci->ports_timeout)) {
CLR_RH_PORTSTAT(USBPORTSC_PR);
udelay(10);
/* If the port was enabled before, turning
* reset on caused a port enable change.
* Turning reset off causes a port connect
* status change. Clear these changes. */
CLR_RH_PORTSTAT(USBPORTSC_CSC | USBPORTSC_PEC);
SET_RH_PORTSTAT(USBPORTSC_PE);
}
}
if (unlikely(status & USBPORTSC_RD)) {
if (!test_bit(port, &uhci->resuming_ports)) {
/* Port received a wakeup request */
set_bit(port, &uhci->resuming_ports);
uhci->ports_timeout = jiffies +
msecs_to_jiffies(20);
} else if (time_after_eq(jiffies,
uhci->ports_timeout)) {
uhci_finish_suspend(uhci, port, port_addr);
}
}
}
}
/* size of returned buffer is part of USB spec */
static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
u16 wIndex, char *buf, u16 wLength)
{
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
int status, lstatus, retval = 0, len = 0;
unsigned int port = wIndex - 1;
unsigned long port_addr = uhci->io_addr + USBPORTSC1 + 2 * port;
u16 wPortChange, wPortStatus;
unsigned long flags;
spin_lock_irqsave(&uhci->lock, flags);
switch (typeReq) {
case GetHubStatus:
*(__le32 *)buf = cpu_to_le32(0);
OK(4); /* hub power */
case GetPortStatus:
if (port >= uhci->rh_numports)
goto err;
uhci_check_ports(uhci);
status = inw(port_addr);
/* Intel controllers report the OverCurrent bit active on.
* VIA controllers report it active off, so we'll adjust the
* bit value. (It's not standardized in the UHCI spec.)
*/
if (to_pci_dev(hcd->self.controller)->vendor ==
PCI_VENDOR_ID_VIA)
status ^= USBPORTSC_OC;
/* UHCI doesn't support C_RESET (always false) */
wPortChange = lstatus = 0;
if (status & USBPORTSC_CSC)
wPortChange |= USB_PORT_STAT_C_CONNECTION;
if (status & USBPORTSC_PEC)
wPortChange |= USB_PORT_STAT_C_ENABLE;
if (status & USBPORTSC_OCC)
wPortChange |= USB_PORT_STAT_C_OVERCURRENT;
if (test_bit(port, &uhci->port_c_suspend)) {
wPortChange |= USB_PORT_STAT_C_SUSPEND;
lstatus |= 1;
}
if (test_bit(port, &uhci->suspended_ports))
lstatus |= 2;
if (test_bit(port, &uhci->resuming_ports))
lstatus |= 4;
/* UHCI has no power switching (always on) */
wPortStatus = USB_PORT_STAT_POWER;
if (status & USBPORTSC_CCS)
wPortStatus |= USB_PORT_STAT_CONNECTION;
if (status & USBPORTSC_PE) {
wPortStatus |= USB_PORT_STAT_ENABLE;
if (status & (USBPORTSC_SUSP | USBPORTSC_RD))
wPortStatus |= USB_PORT_STAT_SUSPEND;
}
if (status & USBPORTSC_OC)
wPortStatus |= USB_PORT_STAT_OVERCURRENT;
if (status & USBPORTSC_PR)
wPortStatus |= USB_PORT_STAT_RESET;
if (status & USBPORTSC_LSDA)
wPortStatus |= USB_PORT_STAT_LOW_SPEED;
if (wPortChange)
dev_dbg(uhci_dev(uhci), "port %d portsc %04x,%02x\n",
wIndex, status, lstatus);
*(__le16 *)buf = cpu_to_le16(wPortStatus);
*(__le16 *)(buf + 2) = cpu_to_le16(wPortChange);
OK(4);
case SetHubFeature: /* We don't implement these */
case ClearHubFeature:
switch (wValue) {
case C_HUB_OVER_CURRENT:
case C_HUB_LOCAL_POWER:
OK(0);
default:
goto err;
}
break;
case SetPortFeature:
if (port >= uhci->rh_numports)
goto err;
switch (wValue) {
case USB_PORT_FEAT_SUSPEND:
set_bit(port, &uhci->suspended_ports);
SET_RH_PORTSTAT(USBPORTSC_SUSP);
OK(0);
case USB_PORT_FEAT_RESET:
SET_RH_PORTSTAT(USBPORTSC_PR);
/* Reset terminates Resume signalling */
uhci_finish_suspend(uhci, port, port_addr);
/* USB v2.0 7.1.7.5 */
uhci->ports_timeout = jiffies + msecs_to_jiffies(50);
OK(0);
case USB_PORT_FEAT_POWER:
/* UHCI has no power switching */
OK(0);
default:
goto err;
}
break;
case ClearPortFeature:
if (port >= uhci->rh_numports)
goto err;
switch (wValue) {
case USB_PORT_FEAT_ENABLE:
CLR_RH_PORTSTAT(USBPORTSC_PE);
/* Disable terminates Resume signalling */
uhci_finish_suspend(uhci, port, port_addr);
OK(0);
case USB_PORT_FEAT_C_ENABLE:
CLR_RH_PORTSTAT(USBPORTSC_PEC);
OK(0);
case USB_PORT_FEAT_SUSPEND:
if (test_bit(port, &uhci->suspended_ports) &&
!test_and_set_bit(port,
&uhci->resuming_ports)) {
SET_RH_PORTSTAT(USBPORTSC_RD);
/* The controller won't allow RD to be set
* if the port is disabled. When this happens
* just skip the Resume signalling.
*/
if (!(inw(port_addr) & USBPORTSC_RD))
uhci_finish_suspend(uhci, port,
port_addr);
else
/* USB v2.0 7.1.7.7 */
uhci->ports_timeout = jiffies +
msecs_to_jiffies(20);
}
OK(0);
case USB_PORT_FEAT_C_SUSPEND:
clear_bit(port, &uhci->port_c_suspend);
OK(0);
case USB_PORT_FEAT_POWER:
/* UHCI has no power switching */
goto err;
case USB_PORT_FEAT_C_CONNECTION:
CLR_RH_PORTSTAT(USBPORTSC_CSC);
OK(0);
case USB_PORT_FEAT_C_OVER_CURRENT:
CLR_RH_PORTSTAT(USBPORTSC_OCC);
OK(0);
case USB_PORT_FEAT_C_RESET:
/* this driver won't report these */
OK(0);
default:
goto err;
}
break;
case GetHubDescriptor:
len = min_t(unsigned int, sizeof(root_hub_hub_des), wLength);
memcpy(buf, root_hub_hub_des, len);
if (len > 2)
buf[2] = uhci->rh_numports;
OK(len);
default:
err:
retval = -EPIPE;
}
spin_unlock_irqrestore(&uhci->lock, flags);
return retval;
}

1539
drivers/usb/host/uhci-q.c 一般檔案

檔案差異因為檔案過大而無法顯示 載入差異