summaryrefslogtreecommitdiffstats
path: root/drivers/usb/host
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/host')
-rw-r--r--drivers/usb/host/Kconfig28
-rw-r--r--drivers/usb/host/Makefile2
-rw-r--r--drivers/usb/host/ehci-dbg.c183
-rw-r--r--drivers/usb/host/ehci-fsl.c10
-rw-r--r--drivers/usb/host/ehci-hcd.c118
-rw-r--r--drivers/usb/host/ehci-hub.c122
-rw-r--r--drivers/usb/host/ehci-mem.c27
-rw-r--r--drivers/usb/host/ehci-pci.c10
-rw-r--r--drivers/usb/host/ehci-ppc-soc.c182
-rw-r--r--drivers/usb/host/ehci-ps3.c86
-rw-r--r--drivers/usb/host/ehci-q.c96
-rw-r--r--drivers/usb/host/ehci-sched.c339
-rw-r--r--drivers/usb/host/ehci.h234
-rw-r--r--drivers/usb/host/ohci-dbg.c4
-rw-r--r--drivers/usb/host/ohci-hcd.c92
-rw-r--r--drivers/usb/host/ohci-hub.c5
-rw-r--r--drivers/usb/host/ohci-mem.c1
-rw-r--r--drivers/usb/host/ohci-pci.c57
-rw-r--r--drivers/usb/host/ohci-pnx4008.c2
-rw-r--r--drivers/usb/host/ohci-ps3.c87
-rw-r--r--drivers/usb/host/ohci.h2
-rw-r--r--drivers/usb/host/r8a66597-hcd.c2244
-rw-r--r--drivers/usb/host/r8a66597.h634
-rw-r--r--drivers/usb/host/uhci-hcd.c5
24 files changed, 4158 insertions, 412 deletions
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index 62711870f8e..2f529828c74 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -69,8 +69,20 @@ config USB_EHCI_TT_NEWSCHED
config USB_EHCI_BIG_ENDIAN_MMIO
bool
- depends on USB_EHCI_HCD
- default n
+ depends on USB_EHCI_HCD && (PPC_CELLEB || PPC_PS3 || 440EPX)
+ default y
+
+config USB_EHCI_BIG_ENDIAN_DESC
+ bool
+ depends on USB_EHCI_HCD && 440EPX
+ default y
+
+config USB_EHCI_FSL
+ bool
+ select USB_EHCI_ROOT_HUB_TT
+ default y if MPC834x || PPC_MPC831x
+ ---help---
+ Variation of ARC USB block used in some Freescale chips.
config USB_ISP116X_HCD
tristate "ISP116X HCD support"
@@ -224,3 +236,15 @@ config USB_SL811_CS
To compile this driver as a module, choose M here: the
module will be called "sl811_cs".
+config USB_R8A66597_HCD
+ tristate "R8A66597 HCD suppoort"
+ depends on USB
+ help
+ The R8A66597 is a USB 2.0 host and peripheral controller.
+
+ 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 r8a66597-hcd.
+
diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile
index 2ff396bd180..bb8e9d44f37 100644
--- a/drivers/usb/host/Makefile
+++ b/drivers/usb/host/Makefile
@@ -15,3 +15,5 @@ obj-$(CONFIG_USB_UHCI_HCD) += uhci-hcd.o
obj-$(CONFIG_USB_SL811_HCD) += sl811-hcd.o
obj-$(CONFIG_USB_SL811_CS) += sl811_cs.o
obj-$(CONFIG_USB_U132_HCD) += u132-hcd.o
+obj-$(CONFIG_USB_R8A66597_HCD) += r8a66597-hcd.o
+
diff --git a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c
index 43eddaecc3d..c9cc4413198 100644
--- a/drivers/usb/host/ehci-dbg.c
+++ b/drivers/usb/host/ehci-dbg.c
@@ -52,7 +52,7 @@ static void dbg_hcs_params (struct ehci_hcd *ehci, char *label)
HCS_INDICATOR (params) ? " ind" : "",
HCS_N_CC (params),
HCS_N_PCC (params),
- HCS_PORTROUTED (params) ? "" : " ordered",
+ HCS_PORTROUTED (params) ? "" : " ordered",
HCS_PPC (params) ? "" : " !ppc",
HCS_N_PORTS (params)
);
@@ -91,20 +91,20 @@ static void dbg_hcc_params (struct ehci_hcd *ehci, char *label)
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" : "");
+ "%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" : "");
+ "%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
@@ -115,23 +115,23 @@ static inline void dbg_hcc_params (struct ehci_hcd *ehci, char *label) {}
#ifdef DEBUG
-static void __attribute__((__unused__))
+static void __maybe_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]));
+ ehci_dbg(ehci, "%s td %p n%08x %08x t%08x p0=%08x\n", label, qtd,
+ hc32_to_cpup(ehci, &qtd->hw_next),
+ hc32_to_cpup(ehci, &qtd->hw_alt_next),
+ hc32_to_cpup(ehci, &qtd->hw_token),
+ hc32_to_cpup(ehci, &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]));
+ ehci_dbg(ehci, " p1=%08x p2=%08x p3=%08x p4=%08x\n",
+ hc32_to_cpup(ehci, &qtd->hw_buf[1]),
+ hc32_to_cpup(ehci, &qtd->hw_buf[2]),
+ hc32_to_cpup(ehci, &qtd->hw_buf[3]),
+ hc32_to_cpup(ehci, &qtd->hw_buf[4]));
}
-static void __attribute__((__unused__))
+static void __maybe_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,
@@ -140,51 +140,53 @@ dbg_qh (const char *label, struct ehci_hcd *ehci, struct ehci_qh *qh)
dbg_qtd ("overlay", ehci, (struct ehci_qtd *) &qh->hw_qtd_next);
}
-static void __attribute__((__unused__))
+static void __maybe_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);
+ label, itd->frame, itd, hc32_to_cpu(ehci, 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]));
+ hc32_to_cpu(ehci, itd->hw_transaction[0]),
+ hc32_to_cpu(ehci, itd->hw_transaction[1]),
+ hc32_to_cpu(ehci, itd->hw_transaction[2]),
+ hc32_to_cpu(ehci, itd->hw_transaction[3]),
+ hc32_to_cpu(ehci, itd->hw_transaction[4]),
+ hc32_to_cpu(ehci, itd->hw_transaction[5]),
+ hc32_to_cpu(ehci, itd->hw_transaction[6]),
+ hc32_to_cpu(ehci, 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]));
+ hc32_to_cpu(ehci, itd->hw_bufp[0]),
+ hc32_to_cpu(ehci, itd->hw_bufp[1]),
+ hc32_to_cpu(ehci, itd->hw_bufp[2]),
+ hc32_to_cpu(ehci, itd->hw_bufp[3]),
+ hc32_to_cpu(ehci, itd->hw_bufp[4]),
+ hc32_to_cpu(ehci, itd->hw_bufp[5]),
+ hc32_to_cpu(ehci, 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__))
+static void __maybe_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);
+ label, sitd->frame, sitd, hc32_to_cpu(ehci, 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]));
+ hc32_to_cpu(ehci, sitd->hw_fullspeed_ep),
+ hc32_to_cpu(ehci, sitd->hw_uframe),
+ hc32_to_cpu(ehci, sitd->hw_results),
+ hc32_to_cpu(ehci, sitd->hw_buf[0]),
+ hc32_to_cpu(ehci, sitd->hw_buf[1]));
}
-static int __attribute__((__unused__))
+static int __maybe_unused
dbg_status_buf (char *buf, unsigned len, const char *label, u32 status)
{
return scnprintf (buf, len,
@@ -203,7 +205,7 @@ dbg_status_buf (char *buf, unsigned len, const char *label, u32 status)
);
}
-static int __attribute__((__unused__))
+static int __maybe_unused
dbg_intr_buf (char *buf, unsigned len, const char *label, u32 enable)
{
return scnprintf (buf, len,
@@ -267,28 +269,27 @@ dbg_port_buf (char *buf, unsigned len, const char *label, int port, u32 status)
(status & PORT_PEC) ? " PEC" : "",
(status & PORT_PE) ? " PE" : "",
(status & PORT_CSC) ? " CSC" : "",
- (status & PORT_CONNECT) ? " CONNECT" : ""
- );
+ (status & PORT_CONNECT) ? " CONNECT" : "");
}
#else
-static inline void __attribute__((__unused__))
+static inline void __maybe_unused
dbg_qh (char *label, struct ehci_hcd *ehci, struct ehci_qh *qh)
{}
-static inline int __attribute__((__unused__))
+static inline int __maybe_unused
dbg_status_buf (char *buf, unsigned len, const char *label, u32 status)
{ return 0; }
-static inline int __attribute__((__unused__))
+static inline int __maybe_unused
dbg_command_buf (char *buf, unsigned len, const char *label, u32 command)
{ return 0; }
-static inline int __attribute__((__unused__))
+static inline int __maybe_unused
dbg_intr_buf (char *buf, unsigned len, const char *label, u32 enable)
{ return 0; }
-static inline int __attribute__((__unused__))
+static inline int __maybe_unused
dbg_port_buf (char *buf, unsigned len, const char *label, int port, u32 status)
{ return 0; }
@@ -332,9 +333,10 @@ static inline void remove_debug_files (struct ehci_hcd *bus) { }
default: tmp = '?'; break; \
}; tmp; })
-static inline char token_mark (__le32 token)
+static inline char token_mark(struct ehci_hcd *ehci, __hc32 token)
{
- __u32 v = le32_to_cpu (token);
+ __u32 v = hc32_to_cpu(ehci, token);
+
if (v & QTD_STS_ACTIVE)
return '*';
if (v & QTD_STS_HALT)
@@ -360,46 +362,48 @@ static void qh_lines (
unsigned size = *sizep;
char *next = *nextp;
char mark;
+ u32 list_end = EHCI_LIST_END(ehci);
- if (qh->hw_qtd_next == EHCI_LIST_END) /* NEC does this */
+ if (qh->hw_qtd_next == list_end) /* NEC does this */
mark = '@';
else
- mark = token_mark (qh->hw_token);
+ mark = token_mark(ehci, qh->hw_token);
if (mark == '/') { /* qh_alt_next controls qh advance? */
- if ((qh->hw_alt_next & QTD_MASK) == ehci->async->hw_alt_next)
+ if ((qh->hw_alt_next & QTD_MASK(ehci))
+ == ehci->async->hw_alt_next)
mark = '#'; /* blocked */
- else if (qh->hw_alt_next == EHCI_LIST_END)
+ else if (qh->hw_alt_next == 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;
+ scratch = hc32_to_cpup(ehci, &qh->hw_info1);
+ hw_curr = (mark == '*') ? hc32_to_cpup(ehci, &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)
+ scratch, hc32_to_cpup(ehci, &qh->hw_info2),
+ hc32_to_cpup(ehci, &qh->hw_token), mark,
+ (cpu_to_hc32(ehci, QTD_TOGGLE) & qh->hw_token)
? "data1" : "data0",
- (le32_to_cpup (&qh->hw_alt_next) >> 1) & 0x0f);
+ (hc32_to_cpup(ehci, &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);
+ scratch = hc32_to_cpup(ehci, &td->hw_token);
mark = ' ';
if (hw_curr == td->qtd_dma)
mark = '*';
- else if (qh->hw_qtd_next == cpu_to_le32(td->qtd_dma))
+ else if (qh->hw_qtd_next == cpu_to_hc32(ehci, 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)
+ else if (td->hw_alt_next != list_end)
mark = '/';
}
temp = snprintf (next, size,
@@ -490,7 +494,7 @@ show_periodic (struct class_device *class_dev, char *buf)
unsigned temp, size, seen_count;
char *next;
unsigned i;
- __le32 tag;
+ __hc32 tag;
if (!(seen = kmalloc (DBG_SCHED_LIMIT * sizeof *seen, GFP_ATOMIC)))
return 0;
@@ -514,18 +518,19 @@ show_periodic (struct class_device *class_dev, char *buf)
p = ehci->pshadow [i];
if (likely (!p.ptr))
continue;
- tag = Q_NEXT_TYPE (ehci->periodic [i]);
+ tag = Q_NEXT_TYPE(ehci, ehci->periodic [i]);
temp = scnprintf (next, size, "%4d: ", i);
size -= temp;
next += temp;
do {
- switch (tag) {
+ switch (hc32_to_cpu(ehci, tag)) {
case Q_TYPE_QH:
temp = scnprintf (next, size, " qh%d-%04x/%p",
p.qh->period,
- le32_to_cpup (&p.qh->hw_info2)
+ hc32_to_cpup(ehci,
+ &p.qh->hw_info2)
/* uframe masks */
& (QH_CMASK | QH_SMASK),
p.qh);
@@ -543,7 +548,7 @@ show_periodic (struct class_device *class_dev, char *buf)
}
/* show more info the first time around */
if (temp == seen_count && p.ptr) {
- u32 scratch = le32_to_cpup (
+ u32 scratch = hc32_to_cpup(ehci,
&p.qh->hw_info1);
struct ehci_qtd *qtd;
char *type = "";
@@ -554,7 +559,8 @@ show_periodic (struct class_device *class_dev, char *buf)
&p.qh->qtd_list,
qtd_list) {
temp++;
- switch (0x03 & (le32_to_cpu (
+ switch (0x03 & (hc32_to_cpu(
+ ehci,
qtd->hw_token) >> 8)) {
case 0: type = "out"; continue;
case 1: type = "in"; continue;
@@ -576,7 +582,7 @@ show_periodic (struct class_device *class_dev, char *buf)
} else
temp = 0;
if (p.qh) {
- tag = Q_NEXT_TYPE (p.qh->hw_next);
+ tag = Q_NEXT_TYPE(ehci, p.qh->hw_next);
p = p.qh->qh_next;
}
break;
@@ -584,23 +590,23 @@ show_periodic (struct class_device *class_dev, char *buf)
temp = scnprintf (next, size,
" fstn-%8x/%p", p.fstn->hw_prev,
p.fstn);
- tag = Q_NEXT_TYPE (p.fstn->hw_next);
+ tag = Q_NEXT_TYPE(ehci, 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);
+ tag = Q_NEXT_TYPE(ehci, 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)
+ hc32_to_cpup(ehci, &p.sitd->hw_uframe)
& 0x0000ffff,
p.sitd);
- tag = Q_NEXT_TYPE (p.sitd->hw_next);
+ tag = Q_NEXT_TYPE(ehci, p.sitd->hw_next);
p = p.sitd->sitd_next;
break;
}
@@ -673,7 +679,8 @@ show_registers (struct class_device *class_dev, char *buf)
unsigned count = 256/4;
pdev = to_pci_dev(ehci_to_hcd(ehci)->self.controller);
- offset = HCC_EXT_CAPS (ehci_readl(ehci, &ehci->caps->hcc_params));
+ offset = HCC_EXT_CAPS(ehci_readl(ehci,
+ &ehci->caps->hcc_params));
while (offset && count--) {
pci_read_config_dword (pdev, offset, &cap);
switch (cap & 0xff) {
@@ -740,14 +747,16 @@ show_registers (struct class_device *class_dev, char *buf)
for (i = 1; i <= HCS_N_PORTS (ehci->hcs_params); i++) {
temp = dbg_port_buf (scratch, sizeof scratch, label, i,
- ehci_readl(ehci, &ehci->regs->port_status [i - 1]));
+ ehci_readl(ehci,
+ &ehci->regs->port_status[i - 1]));
temp = scnprintf (next, size, fmt, temp, scratch);
size -= temp;
next += temp;
if (i == HCS_DEBUG_PORT(ehci->hcs_params) && ehci->debug) {
temp = scnprintf (next, size,
" debug control %08x\n",
- ehci_readl(ehci, &ehci->debug->control));
+ ehci_readl(ehci,
+ &ehci->debug->control));
size -= temp;
next += temp;
}
diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c
index c7a7c590426..b7b7bfbce52 100644
--- a/drivers/usb/host/ehci-fsl.c
+++ b/drivers/usb/host/ehci-fsl.c
@@ -67,7 +67,8 @@ int usb_hcd_fsl_probe(const struct hc_driver *driver,
* in host mode.
*/
if (!((pdata->operating_mode == FSL_USB2_DR_HOST) ||
- (pdata->operating_mode == FSL_USB2_MPH_HOST))) {
+ (pdata->operating_mode == FSL_USB2_MPH_HOST) ||
+ (pdata->operating_mode == FSL_USB2_DR_OTG))) {
dev_err(&pdev->dev,
"Non Host Mode configured for %s. Wrong driver linked.\n",
pdev->dev.bus_id);
@@ -185,12 +186,14 @@ static void mpc83xx_usb_setup(struct usb_hcd *hcd)
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
struct fsl_usb2_platform_data *pdata;
void __iomem *non_ehci = hcd->regs;
+ u32 temp;
pdata =
(struct fsl_usb2_platform_data *)hcd->self.controller->
platform_data;
/* Enable PHY interface in the control reg. */
- out_be32(non_ehci + FSL_SOC_USB_CTRL, 0x00000004);
+ temp = in_be32(non_ehci + FSL_SOC_USB_CTRL);
+ out_be32(non_ehci + FSL_SOC_USB_CTRL, temp | 0x00000004);
out_be32(non_ehci + FSL_SOC_USB_SNOOP1, 0x0000001b);
#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE)
@@ -206,7 +209,8 @@ static void mpc83xx_usb_setup(struct usb_hcd *hcd)
out_be32(non_ehci + FSL_SOC_USB_SNOOP2, 0x80000000 | SNOOP_SIZE_2GB);
#endif
- if (pdata->operating_mode == FSL_USB2_DR_HOST)
+ if ((pdata->operating_mode == FSL_USB2_DR_HOST) ||
+ (pdata->operating_mode == FSL_USB2_DR_OTG))
mpc83xx_setup_phy(ehci, pdata->phy_mode, 0);
if (pdata->operating_mode == FSL_USB2_MPH_HOST) {
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index 099aff64f53..c4e15ed1405 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -41,10 +41,6 @@
#include <asm/irq.h>
#include <asm/system.h>
#include <asm/unaligned.h>
-#ifdef CONFIG_PPC_PS3
-#include <asm/firmware.h>
-#endif
-
/*-------------------------------------------------------------------------*/
@@ -201,9 +197,15 @@ static void tdi_reset (struct ehci_hcd *ehci)
u32 __iomem *reg_ptr;
u32 tmp;
- reg_ptr = (u32 __iomem *)(((u8 __iomem *)ehci->regs) + 0x68);
+ reg_ptr = (u32 __iomem *)(((u8 __iomem *)ehci->regs) + USBMODE);
tmp = ehci_readl(ehci, reg_ptr);
- tmp |= 0x3;
+ tmp |= USBMODE_CM_HC;
+ /* The default byte access to MMR space is LE after
+ * controller reset. Set the required endian mode
+ * for transfer buffers to match the host microprocessor
+ */
+ if (ehci_big_endian_mmio(ehci))
+ tmp |= USBMODE_BE;
ehci_writel(ehci, tmp, reg_ptr);
}
@@ -273,6 +275,58 @@ static void ehci_work(struct ehci_hcd *ehci);
/*-------------------------------------------------------------------------*/
+#ifdef CONFIG_CPU_FREQ
+
+#include <linux/cpufreq.h>
+
+static void ehci_cpufreq_pause (struct ehci_hcd *ehci)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ehci->lock, flags);
+ if (!ehci->cpufreq_changing++)
+ qh_inactivate_split_intr_qhs(ehci);
+ spin_unlock_irqrestore(&ehci->lock, flags);
+}
+
+static void ehci_cpufreq_unpause (struct ehci_hcd *ehci)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ehci->lock, flags);
+ if (!--ehci->cpufreq_changing)
+ qh_reactivate_split_intr_qhs(ehci);
+ spin_unlock_irqrestore(&ehci->lock, flags);
+}
+
+/*
+ * ehci_cpufreq_notifier is needed to avoid MMF errors that occur when
+ * EHCI controllers that don't cache many uframes get delayed trying to
+ * read main memory during CPU frequency transitions. This can cause
+ * split interrupt transactions to not be completed in the required uframe.
+ * This has been observed on the Broadcom/ServerWorks HT1000 controller.
+ */
+static int ehci_cpufreq_notifier(struct notifier_block *nb, unsigned long val,
+ void *data)
+{
+ struct ehci_hcd *ehci = container_of(nb, struct ehci_hcd,
+ cpufreq_transition);
+
+ switch (val) {
+ case CPUFREQ_PRECHANGE:
+ ehci_cpufreq_pause(ehci);
+ break;
+ case CPUFREQ_POSTCHANGE:
+ ehci_cpufreq_unpause(ehci);
+ break;
+ }
+ return 0;
+}
+
+#endif
+
+/*-------------------------------------------------------------------------*/
+
static void ehci_watchdog (unsigned long param)
{
struct ehci_hcd *ehci = (struct ehci_hcd *) param;
@@ -347,6 +401,8 @@ static void ehci_port_power (struct ehci_hcd *ehci, int is_on)
is_on ? SetPortFeature : ClearPortFeature,
USB_PORT_FEAT_POWER,
port--, NULL, 0);
+ /* Flush those writes */
+ ehci_readl(ehci, &ehci->regs->command);
msleep(20);
}
@@ -404,6 +460,10 @@ static void ehci_stop (struct usb_hcd *hcd)
ehci_writel(ehci, 0, &ehci->regs->intr_enable);
spin_unlock_irq(&ehci->lock);
+#ifdef CONFIG_CPU_FREQ
+ cpufreq_unregister_notifier(&ehci->cpufreq_transition,
+ CPUFREQ_TRANSITION_NOTIFIER);
+#endif
/* let companion controllers work when we aren't */
ehci_writel(ehci, 0, &ehci->regs->configured_flag);
@@ -470,12 +530,12 @@ static int ehci_init(struct usb_hcd *hcd)
* from automatically advancing to the next td after short reads.
*/
ehci->async->qh_next.qh = NULL;
- ehci->async->hw_next = QH_NEXT(ehci->async->qh_dma);
- ehci->async->hw_info1 = cpu_to_le32(QH_HEAD);
- ehci->async->hw_token = cpu_to_le32(QTD_STS_HALT);
- ehci->async->hw_qtd_next = EHCI_LIST_END;
+ ehci->async->hw_next = QH_NEXT(ehci, ehci->async->qh_dma);
+ ehci->async->hw_info1 = cpu_to_hc32(ehci, QH_HEAD);
+ ehci->async->hw_token = cpu_to_hc32(ehci, QTD_STS_HALT);
+ ehci->async->hw_qtd_next = EHCI_LIST_END(ehci);
ehci->async->qh_state = QH_STATE_LINKED;
- ehci->async->hw_alt_next = QTD_NEXT(ehci->async->dummy->qtd_dma);
+ ehci->async->hw_alt_next = QTD_NEXT(ehci, ehci->async->dummy->qtd_dma);
/* clear interrupt enables, set irq latency */
if (log2_irq_thresh < 0 || log2_irq_thresh > 6)
@@ -509,6 +569,17 @@ static int ehci_init(struct usb_hcd *hcd)
}
ehci->command = temp;
+#ifdef CONFIG_CPU_FREQ
+ INIT_LIST_HEAD(&ehci->split_intr_qhs);
+ /*
+ * If the EHCI controller caches enough uframes, this probably
+ * isn't needed unless there are so many low/full speed devices
+ * that the controller's can't cache it all.
+ */
+ ehci->cpufreq_transition.notifier_call = ehci_cpufreq_notifier;
+ cpufreq_register_notifier(&ehci->cpufreq_transition,
+ CPUFREQ_TRANSITION_NOTIFIER);
+#endif
return 0;
}
@@ -925,7 +996,7 @@ MODULE_LICENSE ("GPL");
#define PCI_DRIVER ehci_pci_driver
#endif
-#ifdef CONFIG_MPC834x
+#ifdef CONFIG_USB_EHCI_FSL
#include "ehci-fsl.c"
#define PLATFORM_DRIVER ehci_fsl_driver
#endif
@@ -937,7 +1008,12 @@ MODULE_LICENSE ("GPL");
#ifdef CONFIG_PPC_PS3
#include "ehci-ps3.c"
-#define PS3_SYSTEM_BUS_DRIVER ps3_ehci_sb_driver
+#define PS3_SYSTEM_BUS_DRIVER ps3_ehci_driver
+#endif
+
+#ifdef CONFIG_440EPX
+#include "ehci-ppc-soc.c"
+#define PLATFORM_DRIVER ehci_ppc_soc_driver
#endif
#if !defined(PCI_DRIVER) && !defined(PLATFORM_DRIVER) && \
@@ -971,18 +1047,15 @@ static int __init ehci_hcd_init(void)
#endif
#ifdef PS3_SYSTEM_BUS_DRIVER
- if (firmware_has_feature(FW_FEATURE_PS3_LV1)) {
- retval = ps3_system_bus_driver_register(
- &PS3_SYSTEM_BUS_DRIVER);
- if (retval < 0) {
+ retval = ps3_ehci_driver_register(&PS3_SYSTEM_BUS_DRIVER);
+ if (retval < 0) {
#ifdef PLATFORM_DRIVER
- platform_driver_unregister(&PLATFORM_DRIVER);
+ platform_driver_unregister(&PLATFORM_DRIVER);
#endif
#ifdef PCI_DRIVER
- pci_unregister_driver(&PCI_DRIVER);
+ pci_unregister_driver(&PCI_DRIVER);
#endif
- return retval;
- }
+ return retval;
}
#endif
@@ -999,8 +1072,7 @@ static void __exit ehci_hcd_cleanup(void)
pci_unregister_driver(&PCI_DRIVER);
#endif
#ifdef PS3_SYSTEM_BUS_DRIVER
- if (firmware_has_feature(FW_FEATURE_PS3_LV1))
- ps3_system_bus_driver_unregister(&PS3_SYSTEM_BUS_DRIVER);
+ ps3_ehci_driver_unregister(&PS3_SYSTEM_BUS_DRIVER);
#endif
}
module_exit(ehci_hcd_cleanup);
diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c
index f4d301bc83b..0dcb4164dc8 100644
--- a/drivers/usb/host/ehci-hub.c
+++ b/drivers/usb/host/ehci-hub.c
@@ -28,6 +28,89 @@
/*-------------------------------------------------------------------------*/
+#ifdef CONFIG_USB_PERSIST
+
+static int ehci_hub_control(
+ struct usb_hcd *hcd,
+ u16 typeReq,
+ u16 wValue,
+ u16 wIndex,
+ char *buf,
+ u16 wLength
+);
+
+/* After a power loss, ports that were owned by the companion must be
+ * reset so that the companion can still own them.
+ */
+static void ehci_handover_companion_ports(struct ehci_hcd *ehci)
+{
+ u32 __iomem *reg;
+ u32 status;
+ int port;
+ __le32 buf;
+ struct usb_hcd *hcd = ehci_to_hcd(ehci);
+
+ if (!ehci->owned_ports)
+ return;
+
+ /* Give the connections some time to appear */
+ msleep(20);
+
+ port = HCS_N_PORTS(ehci->hcs_params);
+ while (port--) {
+ if (test_bit(port, &ehci->owned_ports)) {
+ reg = &ehci->regs->port_status[port];
+ status = ehci_readl(ehci, reg) & ~PORT_RWC_BITS;
+
+ /* Port already owned by companion? */
+ if (status & PORT_OWNER)
+ clear_bit(port, &ehci->owned_ports);
+ else if (test_bit(port, &ehci->companion_ports))
+ ehci_writel(ehci, status & ~PORT_PE, reg);
+ else
+ ehci_hub_control(hcd, SetPortFeature,
+ USB_PORT_FEAT_RESET, port + 1,
+ NULL, 0);
+ }
+ }
+
+ if (!ehci->owned_ports)
+ return;
+ msleep(90); /* Wait for resets to complete */
+
+ port = HCS_N_PORTS(ehci->hcs_params);
+ while (port--) {
+ if (test_bit(port, &ehci->owned_ports)) {
+ ehci_hub_control(hcd, GetPortStatus,
+ 0, port + 1,
+ (char *) &buf, sizeof(buf));
+
+ /* The companion should now own the port,
+ * but if something went wrong the port must not
+ * remain enabled.
+ */
+ reg = &ehci->regs->port_status[port];
+ status = ehci_readl(ehci, reg) & ~PORT_RWC_BITS;
+ if (status & PORT_OWNER)
+ ehci_writel(ehci, status | PORT_CSC, reg);
+ else {
+ ehci_dbg(ehci, "failed handover port %d: %x\n",
+ port + 1, status);
+ ehci_writel(ehci, status & ~PORT_PE, reg);
+ }
+ }
+ }
+
+ ehci->owned_ports = 0;
+}
+
+#else /* CONFIG_USB_PERSIST */
+
+static inline void ehci_handover_companion_ports(struct ehci_hcd *ehci)
+{ }
+
+#endif
+
#ifdef CONFIG_PM
static int ehci_bus_suspend (struct usb_hcd *hcd)
@@ -60,14 +143,16 @@ static int ehci_bus_suspend (struct usb_hcd *hcd)
* then manually resume them in the bus_resume() routine.
*/
ehci->bus_suspended = 0;
+ ehci->owned_ports = 0;
while (port--) {
u32 __iomem *reg = &ehci->regs->port_status [port];
u32 t1 = ehci_readl(ehci, reg) & ~PORT_RWC_BITS;
u32 t2 = t1;
/* keep track of which ports we suspend */
- if ((t1 & PORT_PE) && !(t1 & PORT_OWNER) &&
- !(t1 & PORT_SUSPEND)) {
+ if (t1 & PORT_OWNER)
+ set_bit(port, &ehci->owned_ports);
+ else if ((t1 & PORT_PE) && !(t1 & PORT_SUSPEND)) {
t2 |= PORT_SUSPEND;
set_bit(port, &ehci->bus_suspended);
}
@@ -108,11 +193,16 @@ static int ehci_bus_resume (struct usb_hcd *hcd)
{
struct ehci_hcd *ehci = hcd_to_ehci (hcd);
u32 temp;
+ u32 power_okay;
int i;
if (time_before (jiffies, ehci->next_statechange))
msleep(5);
spin_lock_irq (&ehci->lock);
+ if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) {
+ spin_unlock_irq(&ehci->lock);
+ return -ESHUTDOWN;
+ }
/* Ideally and we've got a real resume here, and no port's power
* was lost. (For PCI, that means Vaux was maintained.) But we
@@ -120,8 +210,9 @@ static int ehci_bus_resume (struct usb_hcd *hcd)
* the last user of the controller, not reset/pm hardware keeping
* state we gave to it.
*/
- temp = ehci_readl(ehci, &ehci->regs->intr_enable);
- ehci_dbg(ehci, "resume root hub%s\n", temp ? "" : " after power loss");
+ power_okay = ehci_readl(ehci, &ehci->regs->intr_enable);
+ ehci_dbg(ehci, "resume root hub%s\n",
+ power_okay ? "" : " after power loss");
/* at least some APM implementations will try to deliver
* IRQs right away, so delay them until we're ready.
@@ -184,6 +275,9 @@ static int ehci_bus_resume (struct usb_hcd *hcd)
ehci_writel(ehci, INTR_MASK, &ehci->regs->intr_enable);
spin_unlock_irq (&ehci->lock);
+
+ if (!power_okay)
+ ehci_handover_companion_ports(ehci);
return 0;
}
@@ -448,7 +542,8 @@ static int ehci_hub_control (
) {
struct ehci_hcd *ehci = hcd_to_ehci (hcd);
int ports = HCS_N_PORTS (ehci->hcs_params);
- u32 __iomem *status_reg = &ehci->regs->port_status[wIndex - 1];
+ u32 __iomem *status_reg = &ehci->regs->port_status[
+ (wIndex & 0xff) - 1];
u32 temp, status;
unsigned long flags;
int retval = 0;
@@ -556,9 +651,24 @@ static int ehci_hub_control (
status |= 1 << USB_PORT_FEAT_C_CONNECTION;
if (temp & PORT_PEC)
status |= 1 << USB_PORT_FEAT_C_ENABLE;
- if ((temp & PORT_OCC) && !ignore_oc)
+
+ if ((temp & PORT_OCC) && !ignore_oc){
status |= 1 << USB_PORT_FEAT_C_OVER_CURRENT;
+ /*
+ * Hubs should disable port power on over-current.
+ * However, not all EHCI implementations do this
+ * automatically, even if they _do_ support per-port
+ * power switching; they're allowed to just limit the
+ * current. khubd will turn the power back on.
+ */
+ if (HCS_PPC (ehci->hcs_params)){
+ ehci_writel(ehci,
+ temp & ~(PORT_RWC_BITS | PORT_POWER),
+ status_reg);
+ }
+ }
+
/* whoever resumes must GetPortStatus to complete it!! */
if (temp & PORT_RESUME) {
diff --git a/drivers/usb/host/ehci-mem.c b/drivers/usb/host/ehci-mem.c
index a8ba2e1497a..8816d09903d 100644
--- a/drivers/usb/host/ehci-mem.c
+++ b/drivers/usb/host/ehci-mem.c
@@ -27,7 +27,7 @@
* 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.
+ * There's also "register" data (e.g. PCI or SOC), which is memory mapped.
* No memory seen by this driver is pageable.
*/
@@ -35,13 +35,14 @@
/* Allocate the key transfer structures from the previously allocated pool */
-static inline void ehci_qtd_init (struct ehci_qtd *qtd, dma_addr_t dma)
+static inline void ehci_qtd_init(struct ehci_hcd *ehci, 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;
+ qtd->hw_next = EHCI_LIST_END(ehci);
+ qtd->hw_alt_next = EHCI_LIST_END(ehci);
INIT_LIST_HEAD (&qtd->qtd_list);
}
@@ -52,7 +53,7 @@ static struct ehci_qtd *ehci_qtd_alloc (struct ehci_hcd *ehci, gfp_t flags)
qtd = dma_pool_alloc (ehci->qtd_pool, flags, &dma);
if (qtd != NULL) {
- ehci_qtd_init (qtd, dma);
+ ehci_qtd_init(ehci, qtd, dma);
}
return qtd;
}
@@ -63,9 +64,8 @@ static inline void ehci_qtd_free (struct ehci_hcd *ehci, struct ehci_qtd *qtd)
}
-static void qh_destroy (struct kref *kref)
+static void qh_destroy(struct ehci_qh *qh)
{
- 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 */
@@ -89,11 +89,14 @@ static struct ehci_qh *ehci_qh_alloc (struct ehci_hcd *ehci, gfp_t flags)
return qh;
memset (qh, 0, sizeof *qh);
- kref_init(&qh->kref);
+ qh->refcount = 1;
qh->ehci = ehci;
qh->qh_dma = dma;
// INIT_LIST_HEAD (&qh->qh_list);
INIT_LIST_HEAD (&qh->qtd_list);
+#ifdef CONFIG_CPU_FREQ
+ INIT_LIST_HEAD (&qh->split_intr_qhs);
+#endif
/* dummy td enables safe urb queuing */
qh->dummy = ehci_qtd_alloc (ehci, flags);
@@ -108,13 +111,15 @@ static struct ehci_qh *ehci_qh_alloc (struct ehci_hcd *ehci, gfp_t flags)
/* to share a qh (cpu threads, or hc) */
static inline struct ehci_qh *qh_get (struct ehci_qh *qh)
{
- kref_get(&qh->kref);
+ WARN_ON(!qh->refcount);
+ qh->refcount++;
return qh;
}
static inline void qh_put (struct ehci_qh *qh)
{
- kref_put(&qh->kref, qh_destroy);
+ if (!--qh->refcount)
+ qh_destroy(qh);
}
/*-------------------------------------------------------------------------*/
@@ -217,7 +222,7 @@ static int ehci_mem_init (struct ehci_hcd *ehci, gfp_t flags)
goto fail;
}
for (i = 0; i < ehci->periodic_size; i++)
- ehci->periodic [i] = EHCI_LIST_END;
+ ehci->periodic [i] = EHCI_LIST_END(ehci);
/* software shadow of hardware table */
ehci->pshadow = kcalloc(ehci->periodic_size, sizeof(void *), flags);
diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c
index 12edc723ec7..a7816e392a8 100644
--- a/drivers/usb/host/ehci-pci.c
+++ b/drivers/usb/host/ehci-pci.c
@@ -149,8 +149,7 @@ static int ehci_pci_setup(struct usb_hcd *hcd)
* fixed in newer silicon.
*/
case 0x0068:
- pci_read_config_dword(pdev, PCI_REVISION_ID, &temp);
- if ((temp & 0xff) < 0xa4)
+ if (pdev->revision < 0xa4)
ehci->no_selective_suspend = 1;
break;
}
@@ -313,13 +312,14 @@ static int ehci_pci_resume(struct usb_hcd *hcd)
ehci_work(ehci);
spin_unlock_irq(&ehci->lock);
- /* here we "know" root ports should always stay powered */
- ehci_port_power(ehci, 1);
-
ehci_writel(ehci, ehci->command, &ehci->regs->command);
ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag);
ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */
+ /* here we "know" root ports should always stay powered */
+ ehci_port_power(ehci, 1);
+ ehci_handover_companion_ports(ehci);
+
hcd->state = HC_STATE_SUSPENDED;
return 0;
}
diff --git a/drivers/usb/host/ehci-ppc-soc.c b/drivers/usb/host/ehci-ppc-soc.c
new file mode 100644
index 00000000000..c2cedb09ed8
--- /dev/null
+++ b/drivers/usb/host/ehci-ppc-soc.c
@@ -0,0 +1,182 @@
+/*
+ * EHCI HCD (Host Controller Driver) for USB.
+ *
+ * (C) Copyright 2006-2007 Stefan Roese <sr@denx.de>, DENX Software Engineering
+ *
+ * Bus Glue for PPC On-Chip EHCI driver
+ * Tested on AMCC 440EPx
+ *
+ * Based on "ehci-au12xx.c" by David Brownell <dbrownell@users.sourceforge.net>
+ *
+ * This file is licenced under the GPL.
+ */
+
+#include <linux/platform_device.h>
+
+extern int usb_disabled(void);
+
+/**
+ * usb_ehci_ppc_soc_probe - initialize PPC-SoC-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_ehci_ppc_soc_probe(const struct hc_driver *driver,
+ struct usb_hcd **hcd_out,
+ struct platform_device *dev)
+{
+ int retval;
+ struct usb_hcd *hcd;
+ struct ehci_hcd *ehci;
+
+ if (dev->resource[1].flags != IORESOURCE_IRQ) {
+ pr_debug("resource[1] is not IORESOURCE_IRQ");
+ retval = -ENOMEM;
+ }
+ hcd = usb_create_hcd(driver, &dev->dev, "PPC-SOC EHCI");
+ 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;
+ }
+
+ ehci = hcd_to_ehci(hcd);
+ ehci->big_endian_mmio = 1;
+ ehci->big_endian_desc = 1;
+ ehci->caps = hcd->regs;
+ ehci->regs = hcd->regs + HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase));
+
+ /* cache this readonly data; minimize chip reads */
+ ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
+
+#if defined(CONFIG_440EPX)
+ /*
+ * 440EPx Errata USBH_3
+ * Fix: Enable Break Memory Transfer (BMT) in INSNREG3
+ */
+ out_be32((void *)((ulong)(&ehci->regs->command) + 0x8c), (1 << 0));
+ ehci_dbg(ehci, "Break Memory Transfer (BMT) has beed enabled!\n");
+#endif
+
+ retval = usb_add_hcd(hcd, dev->resource[1].start, IRQF_DISABLED);
+ if (retval == 0)
+ return retval;
+
+ 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_ehci_hcd_ppc_soc_remove - shutdown processing for PPC-SoC-based HCDs
+ * @dev: USB Host Controller being removed
+ * Context: !in_interrupt()
+ *
+ * Reverses the effect of usb_ehci_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.
+ *
+ */
+void usb_ehci_ppc_soc_remove(struct usb_hcd *hcd, struct platform_device *dev)
+{
+ usb_remove_hcd(hcd);
+ iounmap(hcd->regs);
+ release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+ usb_put_hcd(hcd);
+}
+
+static const struct hc_driver ehci_ppc_soc_hc_driver = {
+ .description = hcd_name,
+ .product_desc = "PPC-SOC EHCI",
+ .hcd_priv_size = sizeof(struct ehci_hcd),
+
+ /*
+ * generic hardware linkage
+ */
+ .irq = ehci_irq,
+ .flags = HCD_MEMORY | HCD_USB2,
+
+ /*
+ * basic lifecycle operations
+ */
+ .reset = ehci_init,
+ .start = ehci_run,
+ .stop = ehci_stop,
+ .shutdown = ehci_shutdown,
+
+ /*
+ * managing i/o requests and associated device resources
+ */
+ .urb_enqueue = ehci_urb_enqueue,
+ .urb_dequeue = ehci_urb_dequeue,
+ .endpoint_disable = ehci_endpoint_disable,
+
+ /*
+ * scheduling support
+ */
+ .get_frame_number = ehci_get_frame,
+
+ /*
+ * root hub support
+ */
+ .hub_status_data = ehci_hub_status_data,
+ .hub_control = ehci_hub_control,
+#ifdef CONFIG_PM
+ .hub_suspend = ehci_hub_suspend,
+ .hub_resume = ehci_hub_resume,
+#endif
+};
+
+static int ehci_hcd_ppc_soc_drv_probe(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd = NULL;
+ int ret;
+
+ pr_debug("In ehci_hcd_ppc_soc_drv_probe\n");
+
+ if (usb_disabled())
+ return -ENODEV;
+
+ ret = usb_ehci_ppc_soc_probe(&ehci_ppc_soc_hc_driver, &hcd, pdev);
+ return ret;
+}
+
+static int ehci_hcd_ppc_soc_drv_remove(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
+
+ usb_ehci_ppc_soc_remove(hcd, pdev);
+ return 0;
+}
+
+MODULE_ALIAS("ppc-soc-ehci");
+static struct platform_driver ehci_ppc_soc_driver = {
+ .probe = ehci_hcd_ppc_soc_drv_probe,
+ .remove = ehci_hcd_ppc_soc_drv_remove,
+ .shutdown = usb_hcd_platform_shutdown,
+ .driver = {
+ .name = "ppc-soc-ehci",
+ .bus = &platform_bus_type
+ }
+};
diff --git a/drivers/usb/host/ehci-ps3.c b/drivers/usb/host/ehci-ps3.c
index 37b83ba0996..829fe649a98 100644
--- a/drivers/usb/host/ehci-ps3.c
+++ b/drivers/usb/host/ehci-ps3.c
@@ -18,6 +18,7 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#include <asm/firmware.h>
#include <asm/ps3.h>
static int ps3_ehci_hc_reset(struct usb_hcd *hcd)
@@ -73,7 +74,7 @@ static const struct hc_driver ps3_ehci_hc_driver = {
#endif
};
-static int ps3_ehci_sb_probe(struct ps3_system_bus_device *dev)
+static int ps3_ehci_probe(struct ps3_system_bus_device *dev)
{
int result;
struct usb_hcd *hcd;
@@ -85,13 +86,30 @@ static int ps3_ehci_sb_probe(struct ps3_system_bus_device *dev)
goto fail_start;
}
+ result = ps3_open_hv_device(dev);
+
+ if (result) {
+ dev_dbg(&dev->core, "%s:%d: ps3_open_hv_device failed\n",
+ __func__, __LINE__);
+ goto fail_open;
+ }
+
+ result = ps3_dma_region_create(dev->d_region);
+
+ if (result) {
+ dev_dbg(&dev->core, "%s:%d: ps3_dma_region_create failed: "
+ "(%d)\n", __func__, __LINE__, result);
+ BUG_ON("check region type");
+ goto fail_dma_region;
+ }
+
result = ps3_mmio_region_create(dev->m_region);
if (result) {
dev_dbg(&dev->core, "%s:%d: ps3_map_mmio_region failed\n",
__func__, __LINE__);
result = -EPERM;
- goto fail_mmio;
+ goto fail_mmio_region;
}
dev_dbg(&dev->core, "%s:%d: mmio mapped_addr %lxh\n", __func__,
@@ -120,6 +138,11 @@ static int ps3_ehci_sb_probe(struct ps3_system_bus_device *dev)
hcd->rsrc_start = dev->m_region->lpar_addr;
hcd->rsrc_len = dev->m_region->len;
+
+ if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name))
+ dev_dbg(&dev->core, "%s:%d: request_mem_region failed\n",
+ __func__, __LINE__);
+
hcd->regs = ioremap(dev->m_region->lpar_addr, dev->m_region->len);
if (!hcd->regs) {
@@ -153,34 +176,73 @@ static int ps3_ehci_sb_probe(struct ps3_system_bus_device *dev)
fail_add_hcd:
iounmap(hcd->regs);
fail_ioremap:
+ release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
usb_put_hcd(hcd);
fail_create_hcd:
ps3_io_irq_destroy(virq);
fail_irq:
ps3_free_mmio_region(dev->m_region);
-fail_mmio:
+fail_mmio_region:
+ ps3_dma_region_free(dev->d_region);
+fail_dma_region:
+ ps3_close_hv_device(dev);
+fail_open:
fail_start:
return result;
}
-static int ps3_ehci_sb_remove(struct ps3_system_bus_device *dev)
+static int ps3_ehci_remove(struct ps3_system_bus_device *dev)
{
+ unsigned int tmp;
struct usb_hcd *hcd =
(struct usb_hcd *)ps3_system_bus_get_driver_data(dev);
- usb_put_hcd(hcd);
+ BUG_ON(!hcd);
+
+ dev_dbg(&dev->core, "%s:%d: regs %p\n", __func__, __LINE__, hcd->regs);
+ dev_dbg(&dev->core, "%s:%d: irq %u\n", __func__, __LINE__, hcd->irq);
+
+ tmp = hcd->irq;
+
+ usb_remove_hcd(hcd);
+
ps3_system_bus_set_driver_data(dev, NULL);
+ BUG_ON(!hcd->regs);
+ iounmap(hcd->regs);
+
+ release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+ usb_put_hcd(hcd);
+
+ ps3_io_irq_destroy(tmp);
+ ps3_free_mmio_region(dev->m_region);
+
+ ps3_dma_region_free(dev->d_region);
+ ps3_close_hv_device(dev);
+
return 0;
}
-MODULE_ALIAS("ps3-ehci");
+static int ps3_ehci_driver_register(struct ps3_system_bus_driver *drv)
+{
+ return firmware_has_feature(FW_FEATURE_PS3_LV1)
+ ? ps3_system_bus_driver_register(drv)
+ : 0;
+}
+
+static void ps3_ehci_driver_unregister(struct ps3_system_bus_driver *drv)
+{
+ if (firmware_has_feature(FW_FEATURE_PS3_LV1))
+ ps3_system_bus_driver_unregister(drv);
+}
+
+MODULE_ALIAS(PS3_MODULE_ALIAS_EHCI);
-static struct ps3_system_bus_driver ps3_ehci_sb_driver = {
+static struct ps3_system_bus_driver ps3_ehci_driver = {
+ .core.name = "ps3-ehci-driver",
+ .core.owner = THIS_MODULE,
.match_id = PS3_MATCH_ID_EHCI,
- .core = {
- .name = "ps3-ehci-driver",
- },
- .probe = ps3_ehci_sb_probe,
- .remove = ps3_ehci_sb_remove,
+ .probe = ps3_ehci_probe,
+ .remove = ps3_ehci_remove,
+ .shutdown = ps3_ehci_remove,
};
diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c
index e7fbbd00e7c..2284028f8aa 100644
--- a/drivers/usb/host/ehci-q.c
+++ b/drivers/usb/host/ehci-q.c
@@ -43,15 +43,15 @@
/* fill a qtd, returning how much of the buffer we were able to queue up */
static int
-qtd_fill (struct ehci_qtd *qtd, dma_addr_t buf, size_t len,
- int token, int maxpacket)
+qtd_fill(struct ehci_hcd *ehci, struct ehci_qtd *qtd, dma_addr_t buf,
+ size_t len, int token, int maxpacket)
{
int i, count;
u64 addr = buf;
/* one buffer entry per 4K ... first might be short or unaligned */
- qtd->hw_buf [0] = cpu_to_le32 ((u32)addr);
- qtd->hw_buf_hi [0] = cpu_to_le32 ((u32)(addr >> 32));
+ qtd->hw_buf[0] = cpu_to_hc32(ehci, (u32)addr);
+ qtd->hw_buf_hi[0] = cpu_to_hc32(ehci, (u32)(addr >> 32));
count = 0x1000 - (buf & 0x0fff); /* rest of that page */
if (likely (len < count)) /* ... iff needed */
count = len;
@@ -62,8 +62,9 @@ qtd_fill (struct ehci_qtd *qtd, dma_addr_t buf, size_t len,
/* per-qtd limit: from 16K to 20K (best alignment) */
for (i = 1; count < len && i < 5; i++) {
addr = buf;
- qtd->hw_buf [i] = cpu_to_le32 ((u32)addr);
- qtd->hw_buf_hi [i] = cpu_to_le32 ((u32)(addr >> 32));
+ qtd->hw_buf[i] = cpu_to_hc32(ehci, (u32)addr);
+ qtd->hw_buf_hi[i] = cpu_to_hc32(ehci,
+ (u32)(addr >> 32));
buf += 0x1000;
if ((count + 0x1000) < len)
count += 0x1000;
@@ -75,7 +76,7 @@ qtd_fill (struct ehci_qtd *qtd, dma_addr_t buf, size_t len,
if (count != len)
count -= (count % maxpacket);
}
- qtd->hw_token = cpu_to_le32 ((count << 16) | token);
+ qtd->hw_token = cpu_to_hc32(ehci, (count << 16) | token);
qtd->length = count;
return count;
@@ -89,28 +90,28 @@ qh_update (struct ehci_hcd *ehci, struct ehci_qh *qh, struct ehci_qtd *qtd)
/* writes to an active overlay are unsafe */
BUG_ON(qh->qh_state != QH_STATE_IDLE);
- qh->hw_qtd_next = QTD_NEXT (qtd->qtd_dma);
- qh->hw_alt_next = EHCI_LIST_END;
+ qh->hw_qtd_next = QTD_NEXT(ehci, qtd->qtd_dma);
+ qh->hw_alt_next = EHCI_LIST_END(ehci);
/* Except for control endpoints, we make hardware maintain data
* toggle (like OHCI) ... here (re)initialize the toggle in the QH,
* and set the pseudo-toggle in udev. Only usb_clear_halt() will
* ever clear it.
*/
- if (!(qh->hw_info1 & cpu_to_le32(1 << 14))) {
+ if (!(qh->hw_info1 & cpu_to_hc32(ehci, 1 << 14))) {
unsigned is_out, epnum;
- is_out = !(qtd->hw_token & cpu_to_le32(1 << 8));
- epnum = (le32_to_cpup(&qh->hw_info1) >> 8) & 0x0f;
+ is_out = !(qtd->hw_token & cpu_to_hc32(ehci, 1 << 8));
+ epnum = (hc32_to_cpup(ehci, &qh->hw_info1) >> 8) & 0x0f;
if (unlikely (!usb_gettoggle (qh->dev, epnum, is_out))) {
- qh->hw_token &= ~__constant_cpu_to_le32 (QTD_TOGGLE);
+ qh->hw_token &= ~cpu_to_hc32(ehci, QTD_TOGGLE);
usb_settoggle (qh->dev, epnum, is_out, 1);
}
}
/* HC must see latest qtd and qh data before we clear ACTIVE+HALT */
wmb ();
- qh->hw_token &= __constant_cpu_to_le32 (QTD_TOGGLE | QTD_STS_PING);
+ qh->hw_token &= cpu_to_hc32(ehci, QTD_TOGGLE | QTD_STS_PING);
}
/* if it weren't for a common silicon quirk (writing the dummy into the qh
@@ -128,7 +129,7 @@ qh_refresh (struct ehci_hcd *ehci, struct ehci_qh *qh)
qtd = list_entry (qh->qtd_list.next,
struct ehci_qtd, qtd_list);
/* first qtd may already be partially processed */
- if (cpu_to_le32 (qtd->qtd_dma) == qh->hw_current)
+ if (cpu_to_hc32(ehci, qtd->qtd_dma) == qh->hw_current)
qtd = NULL;
}
@@ -222,7 +223,7 @@ __acquires(ehci->lock)
struct ehci_qh *qh = (struct ehci_qh *) urb->hcpriv;
/* S-mask in a QH means it's an interrupt urb */
- if ((qh->hw_info2 & __constant_cpu_to_le32 (QH_SMASK)) != 0) {
+ if ((qh->hw_info2 & cpu_to_hc32(ehci, QH_SMASK)) != 0) {
/* ... update hc-wide periodic stats (for usbfs) */
ehci_to_hcd(ehci)->self.bandwidth_int_reqs--;
@@ -277,7 +278,6 @@ static int qh_schedule (struct ehci_hcd *ehci, struct ehci_qh *qh);
* Chases up to qh->hw_current. Returns number of completions called,
* indicating how much "real" work we did.
*/
-#define HALT_BIT __constant_cpu_to_le32(QTD_STS_HALT)
static unsigned
qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh)
{
@@ -287,6 +287,7 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh)
unsigned count = 0;
int do_status = 0;
u8 state;
+ u32 halt = HALT_BIT(ehci);
if (unlikely (list_empty (&qh->qtd_list)))
return count;
@@ -311,6 +312,10 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh)
struct urb *urb;
u32 token = 0;
+ /* ignore QHs that are currently inactive */
+ if (qh->hw_info1 & __constant_cpu_to_le32(QH_INACTIVATE))
+ break;
+
qtd = list_entry (entry, struct ehci_qtd, qtd_list);
urb = qtd->urb;
@@ -330,7 +335,7 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh)
/* hardware copies qtd out of qh overlay */
rmb ();
- token = le32_to_cpu (qtd->hw_token);
+ token = hc32_to_cpu(ehci, qtd->hw_token);
/* always clean up qtds the hc de-activated */
if ((token & QTD_STS_ACTIVE) == 0) {
@@ -342,7 +347,8 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh)
* that silicon quirk can kick in with this dummy too.
*/
} else if (IS_SHORT_READ (token)
- && !(qtd->hw_alt_next & EHCI_LIST_END)) {
+ && !(qtd->hw_alt_next
+ & EHCI_LIST_END(ehci))) {
stopped = 1;
goto halt;
}
@@ -374,17 +380,17 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh)
/* token in overlay may be most current */
if (state == QH_STATE_IDLE
- && cpu_to_le32 (qtd->qtd_dma)
+ && cpu_to_hc32(ehci, qtd->qtd_dma)
== qh->hw_current)
- token = le32_to_cpu (qh->hw_token);
+ token = hc32_to_cpu(ehci, qh->hw_token);
/* force halt for unlinked or blocked qh, so we'll
* patch the qh later and so that completions can't
* activate it while we "know" it's stopped.
*/
- if ((HALT_BIT & qh->hw_token) == 0) {
+ if ((halt & qh->hw_token) == 0) {
halt:
- qh->hw_token |= HALT_BIT;
+ qh->hw_token |= halt;
wmb ();
}
}
@@ -419,7 +425,7 @@ halt:
* it after fault cleanup, or recovering from silicon wrongly
* overlaying the dummy qtd (which reduces DMA chatter).
*/
- if (stopped != 0 || qh->hw_qtd_next == EHCI_LIST_END) {
+ if (stopped != 0 || qh->hw_qtd_next == EHCI_LIST_END(ehci)) {
switch (state) {
case QH_STATE_IDLE:
qh_refresh(ehci, qh);
@@ -428,7 +434,7 @@ halt:
/* should be rare for periodic transfers,
* except maybe high bandwidth ...
*/
- if ((__constant_cpu_to_le32 (QH_SMASK)
+ if ((cpu_to_hc32(ehci, QH_SMASK)
& qh->hw_info2) != 0) {
intr_deschedule (ehci, qh);
(void) qh_schedule (ehci, qh);
@@ -502,8 +508,9 @@ qh_urb_transaction (
is_input = usb_pipein (urb->pipe);
if (usb_pipecontrol (urb->pipe)) {
/* SETUP pid */
- qtd_fill (qtd, urb->setup_dma, sizeof (struct usb_ctrlrequest),
- token | (2 /* "setup" */ << 8), 8);
+ qtd_fill(ehci, qtd, urb->setup_dma,
+ sizeof (struct usb_ctrlrequest),
+ token | (2 /* "setup" */ << 8), 8);
/* ... and always at least one more pid */
token ^= QTD_TOGGLE;
@@ -512,7 +519,7 @@ qh_urb_transaction (
if (unlikely (!qtd))
goto cleanup;
qtd->urb = urb;
- qtd_prev->hw_next = QTD_NEXT (qtd->qtd_dma);
+ qtd_prev->hw_next = QTD_NEXT(ehci, qtd->qtd_dma);
list_add_tail (&qtd->qtd_list, head);
/* for zero length DATA stages, STATUS is always IN */
@@ -539,7 +546,7 @@ qh_urb_transaction (
for (;;) {
int this_qtd_len;
- this_qtd_len = qtd_fill (qtd, buf, len, token, maxpacket);
+ this_qtd_len = qtd_fill(ehci, qtd, buf, len, token, maxpacket);
len -= this_qtd_len;
buf += this_qtd_len;
if (is_input)
@@ -557,7 +564,7 @@ qh_urb_transaction (
if (unlikely (!qtd))
goto cleanup;
qtd->urb = urb;
- qtd_prev->hw_next = QTD_NEXT (qtd->qtd_dma);
+ qtd_prev->hw_next = QTD_NEXT(ehci, qtd->qtd_dma);
list_add_tail (&qtd->qtd_list, head);
}
@@ -566,7 +573,7 @@ qh_urb_transaction (
*/
if (likely ((urb->transfer_flags & URB_SHORT_NOT_OK) == 0
|| usb_pipecontrol (urb->pipe)))
- qtd->hw_alt_next = EHCI_LIST_END;
+ qtd->hw_alt_next = EHCI_LIST_END(ehci);
/*
* control requests may need a terminating data "status" ack;
@@ -590,17 +597,17 @@ qh_urb_transaction (
if (unlikely (!qtd))
goto cleanup;
qtd->urb = urb;
- qtd_prev->hw_next = QTD_NEXT (qtd->qtd_dma);
+ qtd_prev->hw_next = QTD_NEXT(ehci, qtd->qtd_dma);
list_add_tail (&qtd->qtd_list, head);
/* never any data in such packets */
- qtd_fill (qtd, 0, 0, token, 0);
+ qtd_fill(ehci, qtd, 0, 0, token, 0);
}
}
/* by default, enable interrupt on urb completion */
if (likely (!(urb->transfer_flags & URB_NO_INTERRUPT)))
- qtd->hw_token |= __constant_cpu_to_le32 (QTD_IOC);
+ qtd->hw_token |= cpu_to_hc32(ehci, QTD_IOC);
return head;
cleanup:
@@ -769,8 +776,8 @@ done:
/* init as live, toggle clear, advance to dummy */
qh->qh_state = QH_STATE_IDLE;
- qh->hw_info1 = cpu_to_le32 (info1);
- qh->hw_info2 = cpu_to_le32 (info2);
+ qh->hw_info1 = cpu_to_hc32(ehci, info1);
+ qh->hw_info2 = cpu_to_hc32(ehci, info2);
usb_settoggle (urb->dev, usb_pipeendpoint (urb->pipe), !is_input, 1);
qh_refresh (ehci, qh);
return qh;
@@ -782,7 +789,7 @@ done:
static void qh_link_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
{
- __le32 dma = QH_NEXT (qh->qh_dma);
+ __hc32 dma = QH_NEXT(ehci, qh->qh_dma);
struct ehci_qh *head;
/* (re)start the async schedule? */
@@ -820,8 +827,6 @@ static void qh_link_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
/*-------------------------------------------------------------------------*/
-#define QH_ADDR_MASK __constant_cpu_to_le32(0x7f)
-
/*
* For control/bulk/interrupt, return QH with these TDs appended.
* Allocates and initializes the QH if necessary.
@@ -837,6 +842,7 @@ static struct ehci_qh *qh_append_tds (
)
{
struct ehci_qh *qh = NULL;
+ u32 qh_addr_mask = cpu_to_hc32(ehci, 0x7f);
qh = (struct ehci_qh *) *ptr;
if (unlikely (qh == NULL)) {
@@ -858,7 +864,7 @@ static struct ehci_qh *qh_append_tds (
/* usb_reset_device() briefly reverts to address 0 */
if (usb_pipedevice (urb->pipe) == 0)
- qh->hw_info1 &= ~QH_ADDR_MASK;
+ qh->hw_info1 &= ~qh_addr_mask;
}
/* just one way to queue requests: swap with the dummy qtd.
@@ -867,7 +873,7 @@ static struct ehci_qh *qh_append_tds (
if (likely (qtd != NULL)) {
struct ehci_qtd *dummy;
dma_addr_t dma;
- __le32 token;
+ __hc32 token;
/* to avoid racing the HC, use the dummy td instead of
* the first td of our list (becomes new dummy). both
@@ -875,7 +881,7 @@ static struct ehci_qh *qh_append_tds (
* HC is allowed to fetch the old dummy (4.10.2).
*/
token = qtd->hw_token;
- qtd->hw_token = HALT_BIT;
+ qtd->hw_token = HALT_BIT(ehci);
wmb ();
dummy = qh->dummy;
@@ -887,14 +893,14 @@ static struct ehci_qh *qh_append_tds (
list_add (&dummy->qtd_list, qtd_list);
__list_splice (qtd_list, qh->qtd_list.prev);
- ehci_qtd_init (qtd, qtd->qtd_dma);
+ ehci_qtd_init(ehci, qtd, qtd->qtd_dma);
qh->dummy = qtd;
/* hc must see the new dummy at list end */
dma = qtd->qtd_dma;
qtd = list_entry (qh->qtd_list.prev,
struct ehci_qtd, qtd_list);
- qtd->hw_next = QTD_NEXT (dma);
+ qtd->hw_next = QTD_NEXT(ehci, dma);
/* let the hc process these next qtds */
wmb ();
@@ -970,7 +976,7 @@ static void end_unlink_async (struct ehci_hcd *ehci)
timer_action_done (ehci, TIMER_IAA_WATCHDOG);
- // qh->hw_next = cpu_to_le32 (qh->qh_dma);
+ // qh->hw_next = cpu_to_hc32(qh->qh_dma);
qh->qh_state = QH_STATE_IDLE;
qh->qh_next.qh = NULL;
qh_put (qh); // refcount from reclaim
diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c
index 7b5ae7111f2..d4a8ace4967 100644
--- a/drivers/usb/host/ehci-sched.c
+++ b/drivers/usb/host/ehci-sched.c
@@ -44,9 +44,10 @@ static int ehci_get_frame (struct usb_hcd *hcd);
* @tag: hardware tag for type of this record
*/
static union ehci_shadow *
-periodic_next_shadow (union ehci_shadow *periodic, __le32 tag)
+periodic_next_shadow(struct ehci_hcd *ehci, union ehci_shadow *periodic,
+ __hc32 tag)
{
- switch (tag) {
+ switch (hc32_to_cpu(ehci, tag)) {
case Q_TYPE_QH:
return &periodic->qh->qh_next;
case Q_TYPE_FSTN:
@@ -62,13 +63,14 @@ periodic_next_shadow (union ehci_shadow *periodic, __le32 tag)
/* caller must hold ehci->lock */
static void periodic_unlink (struct ehci_hcd *ehci, unsigned frame, void *ptr)
{
- union ehci_shadow *prev_p = &ehci->pshadow [frame];
- __le32 *hw_p = &ehci->periodic [frame];
+ union ehci_shadow *prev_p = &ehci->pshadow[frame];
+ __hc32 *hw_p = &ehci->periodic[frame];
union ehci_shadow here = *prev_p;
/* find predecessor of "ptr"; hw and shadow lists are in sync */
while (here.ptr && here.ptr != ptr) {
- prev_p = periodic_next_shadow (prev_p, Q_NEXT_TYPE (*hw_p));
+ prev_p = periodic_next_shadow(ehci, prev_p,
+ Q_NEXT_TYPE(ehci, *hw_p));
hw_p = here.hw_next;
here = *prev_p;
}
@@ -79,7 +81,8 @@ static void periodic_unlink (struct ehci_hcd *ehci, unsigned frame, void *ptr)
/* update shadow and hardware lists ... the old "next" pointers
* from ptr may still be in use, the caller updates them.
*/
- *prev_p = *periodic_next_shadow (&here, Q_NEXT_TYPE (*hw_p));
+ *prev_p = *periodic_next_shadow(ehci, &here,
+ Q_NEXT_TYPE(ehci, *hw_p));
*hw_p = *here.hw_next;
}
@@ -87,18 +90,19 @@ static void periodic_unlink (struct ehci_hcd *ehci, unsigned frame, void *ptr)
static unsigned short
periodic_usecs (struct ehci_hcd *ehci, unsigned frame, unsigned uframe)
{
- __le32 *hw_p = &ehci->periodic [frame];
+ __hc32 *hw_p = &ehci->periodic [frame];
union ehci_shadow *q = &ehci->pshadow [frame];
unsigned usecs = 0;
while (q->ptr) {
- switch (Q_NEXT_TYPE (*hw_p)) {
+ switch (hc32_to_cpu(ehci, Q_NEXT_TYPE(ehci, *hw_p))) {
case Q_TYPE_QH:
/* is it in the S-mask? */
- if (q->qh->hw_info2 & cpu_to_le32 (1 << uframe))
+ if (q->qh->hw_info2 & cpu_to_hc32(ehci, 1 << uframe))
usecs += q->qh->usecs;
/* ... or C-mask? */
- if (q->qh->hw_info2 & cpu_to_le32 (1 << (8 + uframe)))
+ if (q->qh->hw_info2 & cpu_to_hc32(ehci,
+ 1 << (8 + uframe)))
usecs += q->qh->c_usecs;
hw_p = &q->qh->hw_next;
q = &q->qh->qh_next;
@@ -108,7 +112,7 @@ periodic_usecs (struct ehci_hcd *ehci, unsigned frame, unsigned uframe)
/* for "save place" FSTNs, count the relevant INTR
* bandwidth from the previous frame
*/
- if (q->fstn->hw_prev != EHCI_LIST_END) {
+ if (q->fstn->hw_prev != EHCI_LIST_END(ehci)) {
ehci_dbg (ehci, "ignoring FSTN cost ...\n");
}
hw_p = &q->fstn->hw_next;
@@ -121,9 +125,10 @@ periodic_usecs (struct ehci_hcd *ehci, unsigned frame, unsigned uframe)
break;
case Q_TYPE_SITD:
/* is it in the S-mask? (count SPLIT, DATA) */
- if (q->sitd->hw_uframe & cpu_to_le32 (1 << uframe)) {
+ if (q->sitd->hw_uframe & cpu_to_hc32(ehci,
+ 1 << uframe)) {
if (q->sitd->hw_fullspeed_ep &
- __constant_cpu_to_le32 (1<<31))
+ cpu_to_hc32(ehci, 1<<31))
usecs += q->sitd->stream->usecs;
else /* worst case for OUT start-split */
usecs += HS_USECS_ISO (188);
@@ -131,7 +136,7 @@ periodic_usecs (struct ehci_hcd *ehci, unsigned frame, unsigned uframe)
/* ... C-mask? (count CSPLIT, DATA) */
if (q->sitd->hw_uframe &
- cpu_to_le32 (1 << (8 + uframe))) {
+ cpu_to_hc32(ehci, 1 << (8 + uframe))) {
/* worst case for IN complete-split */
usecs += q->sitd->stream->c_usecs;
}
@@ -173,9 +178,9 @@ static int same_tt (struct usb_device *dev1, struct usb_device *dev2)
* will cause a transfer in "B-frame" uframe 0. "B-frames" lag
* "H-frames" by 1 uframe. See the EHCI spec sec 4.5 and figure 4.7.
*/
-static inline unsigned char tt_start_uframe(struct ehci_hcd *ehci, __le32 mask)
+static inline unsigned char tt_start_uframe(struct ehci_hcd *ehci, __hc32 mask)
{
- unsigned char smask = QH_SMASK & le32_to_cpu(mask);
+ unsigned char smask = QH_SMASK & hc32_to_cpu(ehci, mask);
if (!smask) {
ehci_err(ehci, "invalid empty smask!\n");
/* uframe 7 can't have bw so this will indicate failure */
@@ -217,14 +222,14 @@ periodic_tt_usecs (
unsigned short tt_usecs[8]
)
{
- __le32 *hw_p = &ehci->periodic [frame];
+ __hc32 *hw_p = &ehci->periodic [frame];
union ehci_shadow *q = &ehci->pshadow [frame];
unsigned char uf;
memset(tt_usecs, 0, 16);
while (q->ptr) {
- switch (Q_NEXT_TYPE(*hw_p)) {
+ switch (hc32_to_cpu(ehci, Q_NEXT_TYPE(ehci, *hw_p))) {
case Q_TYPE_ITD:
hw_p = &q->itd->hw_next;
q = &q->itd->itd_next;
@@ -247,8 +252,8 @@ periodic_tt_usecs (
continue;
// case Q_TYPE_FSTN:
default:
- ehci_dbg(ehci,
- "ignoring periodic frame %d FSTN\n", frame);
+ ehci_dbg(ehci, "ignoring periodic frame %d FSTN\n",
+ frame);
hw_p = &q->fstn->hw_next;
q = &q->fstn->fstn_next;
}
@@ -368,41 +373,42 @@ static int tt_no_collision (
*/
for (; frame < ehci->periodic_size; frame += period) {
union ehci_shadow here;
- __le32 type;
+ __hc32 type;
here = ehci->pshadow [frame];
- type = Q_NEXT_TYPE (ehci->periodic [frame]);
+ type = Q_NEXT_TYPE(ehci, ehci->periodic [frame]);
while (here.ptr) {
- switch (type) {
+ switch (hc32_to_cpu(ehci, type)) {
case Q_TYPE_ITD:
- type = Q_NEXT_TYPE (here.itd->hw_next);
+ type = Q_NEXT_TYPE(ehci, here.itd->hw_next);
here = here.itd->itd_next;
continue;
case Q_TYPE_QH:
if (same_tt (dev, here.qh->dev)) {
u32 mask;
- mask = le32_to_cpu (here.qh->hw_info2);
+ mask = hc32_to_cpu(ehci,
+ here.qh->hw_info2);
/* "knows" no gap is needed */
mask |= mask >> 8;
if (mask & uf_mask)
break;
}
- type = Q_NEXT_TYPE (here.qh->hw_next);
+ type = Q_NEXT_TYPE(ehci, here.qh->hw_next);
here = here.qh->qh_next;
continue;
case Q_TYPE_SITD:
if (same_tt (dev, here.sitd->urb->dev)) {
u16 mask;
- mask = le32_to_cpu (here.sitd
+ mask = hc32_to_cpu(ehci, here.sitd
->hw_uframe);
/* FIXME assumes no gap for IN! */
mask |= mask >> 8;
if (mask & uf_mask)
break;
}
- type = Q_NEXT_TYPE (here.sitd->hw_next);
+ type = Q_NEXT_TYPE(ehci, here.sitd->hw_next);
here = here.sitd->sitd_next;
continue;
// case Q_TYPE_FSTN:
@@ -473,6 +479,109 @@ static int disable_periodic (struct ehci_hcd *ehci)
}
/*-------------------------------------------------------------------------*/
+#ifdef CONFIG_CPU_FREQ
+
+static int safe_to_modify_i (struct ehci_hcd *ehci, struct ehci_qh *qh)
+{
+ int now; /* current (frame * 8) + uframe */
+ int prev_start, next_start; /* uframes from/to split start */
+ int start_uframe = ffs(le32_to_cpup (&qh->hw_info2) & QH_SMASK);
+ int end_uframe = fls((le32_to_cpup (&qh->hw_info2) & QH_CMASK) >> 8);
+ int split_duration = end_uframe - start_uframe;
+
+ now = readl(&ehci->regs->frame_index) % (ehci->periodic_size << 3);
+
+ next_start = ((1024 << 3) + (qh->start << 3) + start_uframe - now)
+ % (qh->period << 3);
+ prev_start = (qh->period << 3) - next_start;
+
+ /*
+ * Make sure there will be at least one uframe when qh is safe.
+ */
+ if ((qh->period << 3) <= (ehci->i_thresh + 2 + split_duration))
+ /* never safe */
+ return -EINVAL;
+
+ /*
+ * Wait 1 uframe after transaction should have started, to make
+ * sure controller has time to write back overlay, so we can
+ * check QTD_STS_STS to see if transaction is in progress.
+ */
+ if ((next_start > ehci->i_thresh) && (prev_start > 1))
+ /* safe to set "i" bit if split isn't in progress */
+ return (qh->hw_token & STATUS_BIT(ehci)) ? 0 : 1;
+ else
+ return 0;
+}
+
+/* Set inactivate bit for all the split interrupt QHs. */
+static void qh_inactivate_split_intr_qhs (struct ehci_hcd *ehci)
+{
+ struct ehci_qh *qh;
+ int not_done, safe;
+ u32 inactivate = INACTIVATE_BIT(ehci);
+ u32 active = ACTIVE_BIT(ehci);
+
+ do {
+ not_done = 0;
+ list_for_each_entry(qh, &ehci->split_intr_qhs,
+ split_intr_qhs) {
+ if (qh->hw_info1 & inactivate)
+ /* already off */
+ continue;
+ /*
+ * To avoid setting "I" after the start split happens,
+ * don't set it if the QH might be cached in the
+ * controller. Some HCs (Broadcom/ServerWorks HT1000)
+ * will stop in the middle of a split transaction when
+ * the "I" bit is set.
+ */
+ safe = safe_to_modify_i(ehci, qh);
+ if (safe == 0) {
+ not_done = 1;
+ } else if (safe > 0) {
+ qh->was_active = qh->hw_token & active;
+ qh->hw_info1 |= inactivate;
+ }
+ }
+ } while (not_done);
+ wmb();
+}
+
+static void qh_reactivate_split_intr_qhs (struct ehci_hcd *ehci)
+{
+ struct ehci_qh *qh;
+ u32 token;
+ int not_done, safe;
+ u32 inactivate = INACTIVATE_BIT(ehci);
+ u32 active = ACTIVE_BIT(ehci);
+ u32 halt = HALT_BIT(ehci);
+
+ do {
+ not_done = 0;
+ list_for_each_entry(qh, &ehci->split_intr_qhs, split_intr_qhs) {
+ if (!(qh->hw_info1 & inactivate)) /* already on */
+ continue;
+ /*
+ * Don't reactivate if cached, or controller might
+ * overwrite overlay after we modify it!
+ */
+ safe = safe_to_modify_i(ehci, qh);
+ if (safe == 0) {
+ not_done = 1;
+ } else if (safe > 0) {
+ /* See EHCI 1.0 section 4.15.2.4. */
+ token = qh->hw_token;
+ qh->hw_token = (token | halt) & ~active;
+ wmb();
+ qh->hw_info1 &= ~inactivate;
+ wmb();
+ qh->hw_token = (token & ~halt) | qh->was_active;
+ }
+ }
+ } while (not_done);
+}
+#endif
/* periodic schedule slots have iso tds (normal or split) first, then a
* sparse tree for active interrupt transfers.
@@ -487,25 +596,36 @@ static int qh_link_periodic (struct ehci_hcd *ehci, struct ehci_qh *qh)
dev_dbg (&qh->dev->dev,
"link qh%d-%04x/%p start %d [%d/%d us]\n",
- period, le32_to_cpup (&qh->hw_info2) & (QH_CMASK | QH_SMASK),
+ period, hc32_to_cpup(ehci, &qh->hw_info2) & (QH_CMASK | QH_SMASK),
qh, qh->start, qh->usecs, qh->c_usecs);
+#ifdef CONFIG_CPU_FREQ
+ /*
+ * If low/full speed interrupt QHs are inactive (because of
+ * cpufreq changing processor speeds), start QH with I flag set--
+ * it will automatically be cleared when cpufreq is done.
+ */
+ if (ehci->cpufreq_changing)
+ if (!(qh->hw_info1 & (cpu_to_le32(1 << 13))))
+ qh->hw_info1 |= INACTIVATE_BIT(ehci);
+#endif
+
/* high bandwidth, or otherwise every microframe */
if (period == 0)
period = 1;
for (i = qh->start; i < ehci->periodic_size; i += period) {
- union ehci_shadow *prev = &ehci->pshadow [i];
- __le32 *hw_p = &ehci->periodic [i];
+ union ehci_shadow *prev = &ehci->pshadow[i];
+ __hc32 *hw_p = &ehci->periodic[i];
union ehci_shadow here = *prev;
- __le32 type = 0;
+ __hc32 type = 0;
/* skip the iso nodes at list head */
while (here.ptr) {
- type = Q_NEXT_TYPE (*hw_p);
- if (type == Q_TYPE_QH)
+ type = Q_NEXT_TYPE(ehci, *hw_p);
+ if (type == cpu_to_hc32(ehci, Q_TYPE_QH))
break;
- prev = periodic_next_shadow (prev, type);
+ prev = periodic_next_shadow(ehci, prev, type);
hw_p = &here.qh->hw_next;
here = *prev;
}
@@ -527,7 +647,7 @@ static int qh_link_periodic (struct ehci_hcd *ehci, struct ehci_qh *qh)
qh->hw_next = *hw_p;
wmb ();
prev->qh = qh;
- *hw_p = QH_NEXT (qh->qh_dma);
+ *hw_p = QH_NEXT (ehci, qh->qh_dma);
}
}
qh->qh_state = QH_STATE_LINKED;
@@ -538,6 +658,12 @@ static int qh_link_periodic (struct ehci_hcd *ehci, struct ehci_qh *qh)
? ((qh->usecs + qh->c_usecs) / qh->period)
: (qh->usecs * 8);
+#ifdef CONFIG_CPU_FREQ
+ /* add qh to list of low/full speed interrupt QHs, if applicable */
+ if (!(qh->hw_info1 & (cpu_to_le32(1 << 13)))) {
+ list_add(&qh->split_intr_qhs, &ehci->split_intr_qhs);
+ }
+#endif
/* maybe enable periodic schedule processing */
if (!ehci->periodic_sched++)
return enable_periodic (ehci);
@@ -555,7 +681,14 @@ static void qh_unlink_periodic (struct ehci_hcd *ehci, struct ehci_qh *qh)
// and this qh is active in the current uframe
// (and overlay token SplitXstate is false?)
// THEN
- // qh->hw_info1 |= __constant_cpu_to_le32 (1 << 7 /* "ignore" */);
+ // qh->hw_info1 |= __constant_cpu_to_hc32(1 << 7 /* "ignore" */);
+
+#ifdef CONFIG_CPU_FREQ
+ /* remove qh from list of low/full speed interrupt QHs */
+ if (!(qh->hw_info1 & (cpu_to_le32(1 << 13)))) {
+ list_del_init(&qh->split_intr_qhs);
+ }
+#endif
/* high bandwidth, or otherwise part of every microframe */
if ((period = qh->period) == 0)
@@ -572,7 +705,7 @@ static void qh_unlink_periodic (struct ehci_hcd *ehci, struct ehci_qh *qh)
dev_dbg (&qh->dev->dev,
"unlink qh%d-%04x/%p start %d [%d/%d us]\n",
qh->period,
- le32_to_cpup (&qh->hw_info2) & (QH_CMASK | QH_SMASK),
+ hc32_to_cpup(ehci, &qh->hw_info2) & (QH_CMASK | QH_SMASK),
qh, qh->start, qh->usecs, qh->c_usecs);
/* qh->qh_next still "live" to HC */
@@ -598,7 +731,7 @@ static void intr_deschedule (struct ehci_hcd *ehci, struct ehci_qh *qh)
* active high speed queues may need bigger delays...
*/
if (list_empty (&qh->qtd_list)
- || (__constant_cpu_to_le32 (QH_CMASK)
+ || (cpu_to_hc32(ehci, QH_CMASK)
& qh->hw_info2) != 0)
wait = 2;
else
@@ -606,7 +739,7 @@ static void intr_deschedule (struct ehci_hcd *ehci, struct ehci_qh *qh)
udelay (wait);
qh->qh_state = QH_STATE_IDLE;
- qh->hw_next = EHCI_LIST_END;
+ qh->hw_next = EHCI_LIST_END(ehci);
wmb ();
}
@@ -663,7 +796,7 @@ static int check_intr_schedule (
unsigned frame,
unsigned uframe,
const struct ehci_qh *qh,
- __le32 *c_maskp
+ __hc32 *c_maskp
)
{
int retval = -ENOSPC;
@@ -695,7 +828,7 @@ static int check_intr_schedule (
retval = 0;
- *c_maskp = cpu_to_le32 (mask << 8);
+ *c_maskp = cpu_to_hc32(ehci, mask << 8);
}
#else
/* Make sure this tt's buffer is also available for CSPLITs.
@@ -706,7 +839,7 @@ static int check_intr_schedule (
* one smart pass...
*/
mask = 0x03 << (uframe + qh->gap_uf);
- *c_maskp = cpu_to_le32 (mask << 8);
+ *c_maskp = cpu_to_hc32(ehci, mask << 8);
mask |= 1 << uframe;
if (tt_no_collision (ehci, qh->period, qh->dev, frame, mask)) {
@@ -726,20 +859,20 @@ done:
/* "first fit" scheduling policy used the first time through,
* or when the previous schedule slot can't be re-used.
*/
-static int qh_schedule (struct ehci_hcd *ehci, struct ehci_qh *qh)
+static int qh_schedule(struct ehci_hcd *ehci, struct ehci_qh *qh)
{
int status;
unsigned uframe;
- __le32 c_mask;
+ __hc32 c_mask;
unsigned frame; /* 0..(qh->period - 1), or NO_FRAME */
qh_refresh(ehci, qh);
- qh->hw_next = EHCI_LIST_END;
+ qh->hw_next = EHCI_LIST_END(ehci);
frame = qh->start;
/* reuse the previous schedule slots, if we can */
if (frame < qh->period) {
- uframe = ffs (le32_to_cpup (&qh->hw_info2) & QH_SMASK);
+ uframe = ffs(hc32_to_cpup(ehci, &qh->hw_info2) & QH_SMASK);
status = check_intr_schedule (ehci, frame, --uframe,
qh, &c_mask);
} else {
@@ -775,10 +908,10 @@ static int qh_schedule (struct ehci_hcd *ehci, struct ehci_qh *qh)
qh->start = frame;
/* reset S-frame and (maybe) C-frame masks */
- qh->hw_info2 &= __constant_cpu_to_le32(~(QH_CMASK | QH_SMASK));
+ qh->hw_info2 &= cpu_to_hc32(ehci, ~(QH_CMASK | QH_SMASK));
qh->hw_info2 |= qh->period
- ? cpu_to_le32 (1 << uframe)
- : __constant_cpu_to_le32 (QH_SMASK);
+ ? cpu_to_hc32(ehci, 1 << uframe)
+ : cpu_to_hc32(ehci, QH_SMASK);
qh->hw_info2 |= c_mask;
} else
ehci_dbg (ehci, "reused qh %p schedule\n", qh);
@@ -808,7 +941,7 @@ static int intr_submit (
spin_lock_irqsave (&ehci->lock, flags);
if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE,
- &ehci_to_hcd(ehci)->flags))) {
+ &ehci_to_hcd(ehci)->flags))) {
status = -ESHUTDOWN;
goto done;
}
@@ -898,9 +1031,9 @@ iso_stream_init (
buf1 |= maxp;
maxp *= multi;
- stream->buf0 = cpu_to_le32 ((epnum << 8) | dev->devnum);
- stream->buf1 = cpu_to_le32 (buf1);
- stream->buf2 = cpu_to_le32 (multi);
+ stream->buf0 = cpu_to_hc32(ehci, (epnum << 8) | dev->devnum);
+ stream->buf1 = cpu_to_hc32(ehci, buf1);
+ stream->buf2 = cpu_to_hc32(ehci, multi);
/* usbfs wants to report the average usecs per frame tied up
* when transfers on this endpoint are scheduled ...
@@ -943,7 +1076,7 @@ iso_stream_init (
bandwidth /= 1 << (interval + 2);
/* stream->splits gets created from raw_mask later */
- stream->address = cpu_to_le32 (addr);
+ stream->address = cpu_to_hc32(ehci, addr);
}
stream->bandwidth = bandwidth;
@@ -1077,7 +1210,8 @@ iso_sched_alloc (unsigned packets, gfp_t mem_flags)
}
static inline void
-itd_sched_init (
+itd_sched_init(
+ struct ehci_hcd *ehci,
struct ehci_iso_sched *iso_sched,
struct ehci_iso_stream *stream,
struct urb *urb
@@ -1107,7 +1241,7 @@ itd_sched_init (
&& !(urb->transfer_flags & URB_NO_INTERRUPT))
trans |= EHCI_ITD_IOC;
trans |= length << 16;
- uframe->transaction = cpu_to_le32 (trans);
+ uframe->transaction = cpu_to_hc32(ehci, trans);
/* might need to cross a buffer page within a uframe */
uframe->bufp = (buf & ~(u64)0x0fff);
@@ -1149,7 +1283,7 @@ itd_urb_transaction (
if (unlikely (sched == NULL))
return -ENOMEM;
- itd_sched_init (sched, stream, urb);
+ itd_sched_init(ehci, sched, stream, urb);
if (urb->interval < 8)
num_itds = 1 + (sched->span + 7) / 8;
@@ -1167,7 +1301,7 @@ itd_urb_transaction (
/* prefer previously-allocated itds */
if (likely (!list_empty(&stream->free_list))) {
itd = list_entry (stream->free_list.prev,
- struct ehci_itd, itd_list);
+ struct ehci_itd, itd_list);
list_del (&itd->itd_list);
itd_dma = itd->itd_dma;
} else
@@ -1294,7 +1428,7 @@ sitd_slot_ok (
uframe += period_uframes;
} while (uframe < mod);
- stream->splits = cpu_to_le32(stream->raw_mask << (uframe & 7));
+ stream->splits = cpu_to_hc32(ehci, stream->raw_mask << (uframe & 7));
return 1;
}
@@ -1415,12 +1549,13 @@ ready:
/*-------------------------------------------------------------------------*/
static inline void
-itd_init (struct ehci_iso_stream *stream, struct ehci_itd *itd)
+itd_init(struct ehci_hcd *ehci, struct ehci_iso_stream *stream,
+ struct ehci_itd *itd)
{
int i;
/* it's been recently zeroed */
- itd->hw_next = EHCI_LIST_END;
+ itd->hw_next = EHCI_LIST_END(ehci);
itd->hw_bufp [0] = stream->buf0;
itd->hw_bufp [1] = stream->buf1;
itd->hw_bufp [2] = stream->buf2;
@@ -1432,7 +1567,8 @@ itd_init (struct ehci_iso_stream *stream, struct ehci_itd *itd)
}
static inline void
-itd_patch (
+itd_patch(
+ struct ehci_hcd *ehci,
struct ehci_itd *itd,
struct ehci_iso_sched *iso_sched,
unsigned index,
@@ -1447,17 +1583,18 @@ itd_patch (
uframe &= 0x07;
itd->index [uframe] = index;
- itd->hw_transaction [uframe] = uf->transaction;
- itd->hw_transaction [uframe] |= cpu_to_le32 (pg << 12);
- itd->hw_bufp [pg] |= cpu_to_le32 (uf->bufp & ~(u32)0);
- itd->hw_bufp_hi [pg] |= cpu_to_le32 ((u32)(uf->bufp >> 32));
+ itd->hw_transaction[uframe] = uf->transaction;
+ itd->hw_transaction[uframe] |= cpu_to_hc32(ehci, pg << 12);
+ itd->hw_bufp[pg] |= cpu_to_hc32(ehci, uf->bufp & ~(u32)0);
+ itd->hw_bufp_hi[pg] |= cpu_to_hc32(ehci, (u32)(uf->bufp >> 32));
/* iso_frame_desc[].offset must be strictly increasing */
if (unlikely (uf->cross)) {
u64 bufp = uf->bufp + 4096;
+
itd->pg = ++pg;
- itd->hw_bufp [pg] |= cpu_to_le32 (bufp & ~(u32)0);
- itd->hw_bufp_hi [pg] |= cpu_to_le32 ((u32)(bufp >> 32));
+ itd->hw_bufp[pg] |= cpu_to_hc32(ehci, bufp & ~(u32)0);
+ itd->hw_bufp_hi[pg] |= cpu_to_hc32(ehci, (u32)(bufp >> 32));
}
}
@@ -1470,7 +1607,7 @@ itd_link (struct ehci_hcd *ehci, unsigned frame, struct ehci_itd *itd)
ehci->pshadow [frame].itd = itd;
itd->frame = frame;
wmb ();
- ehci->periodic [frame] = cpu_to_le32 (itd->itd_dma) | Q_TYPE_ITD;
+ ehci->periodic[frame] = cpu_to_hc32(ehci, itd->itd_dma | Q_TYPE_ITD);
}
/* fit urb's itds into the selected schedule slot; activate as needed */
@@ -1515,14 +1652,14 @@ itd_link_urb (
list_move_tail (&itd->itd_list, &stream->td_list);
itd->stream = iso_stream_get (stream);
itd->urb = usb_get_urb (urb);
- itd_init (stream, itd);
+ itd_init (ehci, stream, itd);
}
uframe = next_uframe & 0x07;
frame = next_uframe >> 3;
itd->usecs [uframe] = stream->usecs;
- itd_patch (itd, iso_sched, packet, uframe);
+ itd_patch(ehci, itd, iso_sched, packet, uframe);
next_uframe += stream->interval;
stream->depth += stream->interval;
@@ -1570,7 +1707,7 @@ itd_complete (
urb_index = itd->index[uframe];
desc = &urb->iso_frame_desc [urb_index];
- t = le32_to_cpup (&itd->hw_transaction [uframe]);
+ t = hc32_to_cpup(ehci, &itd->hw_transaction [uframe]);
itd->hw_transaction [uframe] = 0;
stream->depth -= stream->interval;
@@ -1700,7 +1837,8 @@ done:
*/
static inline void
-sitd_sched_init (
+sitd_sched_init(
+ struct ehci_hcd *ehci,
struct ehci_iso_sched *iso_sched,
struct ehci_iso_stream *stream,
struct urb *urb
@@ -1729,7 +1867,7 @@ sitd_sched_init (
&& !(urb->transfer_flags & URB_NO_INTERRUPT))
trans |= SITD_IOC;
trans |= length << 16;
- packet->transaction = cpu_to_le32 (trans);
+ packet->transaction = cpu_to_hc32(ehci, trans);
/* might need to cross a buffer page within a td */
packet->bufp = buf;
@@ -1765,7 +1903,7 @@ sitd_urb_transaction (
if (iso_sched == NULL)
return -ENOMEM;
- sitd_sched_init (iso_sched, stream, urb);
+ sitd_sched_init(ehci, iso_sched, stream, urb);
/* allocate/init sITDs */
spin_lock_irqsave (&ehci->lock, flags);
@@ -1817,7 +1955,8 @@ sitd_urb_transaction (
/*-------------------------------------------------------------------------*/
static inline void
-sitd_patch (
+sitd_patch(
+ struct ehci_hcd *ehci,
struct ehci_iso_stream *stream,
struct ehci_sitd *sitd,
struct ehci_iso_sched *iso_sched,
@@ -1827,20 +1966,20 @@ sitd_patch (
struct ehci_iso_packet *uf = &iso_sched->packet [index];
u64 bufp = uf->bufp;
- sitd->hw_next = EHCI_LIST_END;
+ sitd->hw_next = EHCI_LIST_END(ehci);
sitd->hw_fullspeed_ep = stream->address;
sitd->hw_uframe = stream->splits;
sitd->hw_results = uf->transaction;
- sitd->hw_backpointer = EHCI_LIST_END;
+ sitd->hw_backpointer = EHCI_LIST_END(ehci);
bufp = uf->bufp;
- sitd->hw_buf [0] = cpu_to_le32 (bufp);
- sitd->hw_buf_hi [0] = cpu_to_le32 (bufp >> 32);
+ sitd->hw_buf[0] = cpu_to_hc32(ehci, bufp);
+ sitd->hw_buf_hi[0] = cpu_to_hc32(ehci, bufp >> 32);
- sitd->hw_buf [1] = cpu_to_le32 (uf->buf1);
+ sitd->hw_buf[1] = cpu_to_hc32(ehci, uf->buf1);
if (uf->cross)
bufp += 4096;
- sitd->hw_buf_hi [1] = cpu_to_le32 (bufp >> 32);
+ sitd->hw_buf_hi[1] = cpu_to_hc32(ehci, bufp >> 32);
sitd->index = index;
}
@@ -1853,7 +1992,7 @@ sitd_link (struct ehci_hcd *ehci, unsigned frame, struct ehci_sitd *sitd)
ehci->pshadow [frame].sitd = sitd;
sitd->frame = frame;
wmb ();
- ehci->periodic [frame] = cpu_to_le32 (sitd->sitd_dma) | Q_TYPE_SITD;
+ ehci->periodic[frame] = cpu_to_hc32(ehci, sitd->sitd_dma | Q_TYPE_SITD);
}
/* fit urb's sitds into the selected schedule slot; activate as needed */
@@ -1881,7 +2020,7 @@ sitd_link_urb (
urb->dev->devpath, stream->bEndpointAddress & 0x0f,
(stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out",
(next_uframe >> 3) % ehci->periodic_size,
- stream->interval, le32_to_cpu (stream->splits));
+ stream->interval, hc32_to_cpu(ehci, stream->splits));
stream->start = jiffies;
}
ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs++;
@@ -1902,7 +2041,7 @@ sitd_link_urb (
sitd->stream = iso_stream_get (stream);
sitd->urb = usb_get_urb (urb);
- sitd_patch (stream, sitd, sched, packet);
+ sitd_patch(ehci, stream, sitd, sched, packet);
sitd_link (ehci, (next_uframe >> 3) % ehci->periodic_size,
sitd);
@@ -1940,7 +2079,7 @@ sitd_complete (
urb_index = sitd->index;
desc = &urb->iso_frame_desc [urb_index];
- t = le32_to_cpup (&sitd->hw_results);
+ t = hc32_to_cpup(ehci, &sitd->hw_results);
/* report transfer status */
if (t & SITD_ERRS) {
@@ -2095,7 +2234,7 @@ scan_periodic (struct ehci_hcd *ehci)
for (;;) {
union ehci_shadow q, *q_p;
- __le32 type, *hw_p;
+ __hc32 type, *hw_p;
unsigned uframes;
/* don't scan past the live uframe */
@@ -2113,7 +2252,7 @@ restart:
q_p = &ehci->pshadow [frame];
hw_p = &ehci->periodic [frame];
q.ptr = q_p->ptr;
- type = Q_NEXT_TYPE (*hw_p);
+ type = Q_NEXT_TYPE(ehci, *hw_p);
modified = 0;
while (q.ptr != NULL) {
@@ -2122,11 +2261,11 @@ restart:
int live;
live = HC_IS_RUNNING (ehci_to_hcd(ehci)->state);
- switch (type) {
+ switch (hc32_to_cpu(ehci, type)) {
case Q_TYPE_QH:
/* handle any completions */
temp.qh = qh_get (q.qh);
- type = Q_NEXT_TYPE (q.qh->hw_next);
+ type = Q_NEXT_TYPE(ehci, q.qh->hw_next);
q = q.qh->qh_next;
modified = qh_completions (ehci, temp.qh);
if (unlikely (list_empty (&temp.qh->qtd_list)))
@@ -2137,10 +2276,10 @@ restart:
/* for "save place" FSTNs, look at QH entries
* in the previous frame for completions.
*/
- if (q.fstn->hw_prev != EHCI_LIST_END) {
+ if (q.fstn->hw_prev != EHCI_LIST_END(ehci)) {
dbg ("ignoring completions from FSTNs");
}
- type = Q_NEXT_TYPE (q.fstn->hw_next);
+ type = Q_NEXT_TYPE(ehci, q.fstn->hw_next);
q = q.fstn->fstn_next;
break;
case Q_TYPE_ITD:
@@ -2148,11 +2287,12 @@ restart:
rmb ();
for (uf = live ? uframes : 8; uf < 8; uf++) {
if (0 == (q.itd->hw_transaction [uf]
- & ITD_ACTIVE))
+ & ITD_ACTIVE(ehci)))
continue;
q_p = &q.itd->itd_next;
hw_p = &q.itd->hw_next;
- type = Q_NEXT_TYPE (q.itd->hw_next);
+ type = Q_NEXT_TYPE(ehci,
+ q.itd->hw_next);
q = *q_p;
break;
}
@@ -2164,23 +2304,24 @@ restart:
*/
*q_p = q.itd->itd_next;
*hw_p = q.itd->hw_next;
- type = Q_NEXT_TYPE (q.itd->hw_next);
+ type = Q_NEXT_TYPE(ehci, q.itd->hw_next);
wmb();
modified = itd_complete (ehci, q.itd);
q = *q_p;
break;
case Q_TYPE_SITD:
- if ((q.sitd->hw_results & SITD_ACTIVE)
+ if ((q.sitd->hw_results & SITD_ACTIVE(ehci))
&& live) {
q_p = &q.sitd->sitd_next;
hw_p = &q.sitd->hw_next;
- type = Q_NEXT_TYPE (q.sitd->hw_next);
+ type = Q_NEXT_TYPE(ehci,
+ q.sitd->hw_next);
q = *q_p;
break;
}
*q_p = q.sitd->sitd_next;
*hw_p = q.sitd->hw_next;
- type = Q_NEXT_TYPE (q.sitd->hw_next);
+ type = Q_NEXT_TYPE(ehci, q.sitd->hw_next);
wmb();
modified = sitd_complete (ehci, q.sitd);
q = *q_p;
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
index 46fa57a520d..2c68a04230c 100644
--- a/drivers/usb/host/ehci.h
+++ b/drivers/usb/host/ehci.h
@@ -21,6 +21,22 @@
/* definitions used for the EHCI driver */
+/*
+ * __hc32 and __hc16 are "Host Controller" types, they may be equivalent to
+ * __leXX (normally) or __beXX (given EHCI_BIG_ENDIAN_DESC), depending on
+ * the host controller implementation.
+ *
+ * To facilitate the strongest possible byte-order checking from "sparse"
+ * and so on, we use __leXX unless that's not practical.
+ */
+#ifdef CONFIG_USB_EHCI_BIG_ENDIAN_DESC
+typedef __u32 __bitwise __hc32;
+typedef __u16 __bitwise __hc16;
+#else
+#define __hc32 __le32
+#define __hc16 __le16
+#endif
+
/* statistics can be kept for for tuning/monitoring */
struct ehci_stats {
/* irq usage */
@@ -55,6 +71,12 @@ struct ehci_hcd { /* one per controller */
__u32 hcs_params; /* cached register copy */
spinlock_t lock;
+#ifdef CONFIG_CPU_FREQ
+ struct notifier_block cpufreq_transition;
+ int cpufreq_changing;
+ struct list_head split_intr_qhs;
+#endif
+
/* async schedule support */
struct ehci_qh *async;
struct ehci_qh *reclaim;
@@ -64,7 +86,7 @@ struct ehci_hcd { /* one per controller */
/* periodic schedule support */
#define DEFAULT_I_TDPS 1024 /* some HCs can do less */
unsigned periodic_size;
- __le32 *periodic; /* hw periodic table */
+ __hc32 *periodic; /* hw periodic table */
dma_addr_t periodic_dma;
unsigned i_thresh; /* uframes HC might cache */
@@ -74,11 +96,14 @@ struct ehci_hcd { /* one per controller */
/* per root hub port */
unsigned long reset_done [EHCI_MAX_ROOT_PORTS];
+
/* bit vectors (one bit per port) */
unsigned long bus_suspended; /* which ports were
already suspended at the start of a bus suspend */
unsigned long companion_ports; /* which ports are
dedicated to the companion controller */
+ unsigned long owned_ports; /* which ports are
+ owned by the companion during a bus suspend */
/* per-HC memory pools (could be per-bus, but ...) */
struct dma_pool *qh_pool; /* qh per active urb */
@@ -97,6 +122,7 @@ struct ehci_hcd { /* one per controller */
unsigned no_selective_suspend:1;
unsigned has_fsl_port_bug:1; /* FreeScale */
unsigned big_endian_mmio:1;
+ unsigned big_endian_desc:1;
u8 sbrn; /* packed release number */
@@ -276,6 +302,12 @@ struct ehci_regs {
#define PORT_RWC_BITS (PORT_CSC | PORT_PEC | PORT_OCC)
} __attribute__ ((packed));
+#define USBMODE 0x68 /* USB Device mode */
+#define USBMODE_SDIS (1<<3) /* Stream disable */
+#define USBMODE_BE (1<<2) /* BE/LE endianness select */
+#define USBMODE_CM_HC (3<<0) /* host controller mode */
+#define USBMODE_CM_IDLE (0<<0) /* idle state */
+
/* Appendix C, Debug port ... intended for use with special "debug devices"
* that can help if there's no serial console. (nonstandard enumeration.)
*/
@@ -303,7 +335,7 @@ struct ehci_dbg_port {
/*-------------------------------------------------------------------------*/
-#define QTD_NEXT(dma) cpu_to_le32((u32)dma)
+#define QTD_NEXT(ehci, dma) cpu_to_hc32(ehci, (u32)dma)
/*
* EHCI Specification 0.95 Section 3.5
@@ -315,9 +347,9 @@ struct ehci_dbg_port {
*/
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 */
+ __hc32 hw_next; /* see EHCI 3.5.1 */
+ __hc32 hw_alt_next; /* see EHCI 3.5.2 */
+ __hc32 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 */
@@ -331,8 +363,13 @@ struct ehci_qtd {
#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 */
+
+#define ACTIVE_BIT(ehci) cpu_to_hc32(ehci, QTD_STS_ACTIVE)
+#define HALT_BIT(ehci) cpu_to_hc32(ehci, QTD_STS_HALT)
+#define STATUS_BIT(ehci) cpu_to_hc32(ehci, QTD_STS_STS)
+
+ __hc32 hw_buf [5]; /* see EHCI 3.5.4 */
+ __hc32 hw_buf_hi [5]; /* Appendix B */
/* the rest is HCD-private */
dma_addr_t qtd_dma; /* qtd address */
@@ -342,26 +379,33 @@ struct ehci_qtd {
} __attribute__ ((aligned (32)));
/* mask NakCnt+T in qh->hw_alt_next */
-#define QTD_MASK __constant_cpu_to_le32 (~0x1f)
+#define QTD_MASK(ehci) cpu_to_hc32 (ehci, ~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))
+#define Q_NEXT_TYPE(ehci,dma) ((dma) & cpu_to_hc32(ehci, 3 << 1))
+/*
+ * Now the following defines are not converted using the
+ * __constant_cpu_to_le32() macro anymore, since we have to support
+ * "dynamic" switching between be and le support, so that the driver
+ * can be used on one system with SoC EHCI controller using big-endian
+ * descriptors as well as a normal little-endian PCI EHCI controller.
+ */
/* 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)
+#define Q_TYPE_ITD (0 << 1)
+#define Q_TYPE_QH (1 << 1)
+#define Q_TYPE_SITD (2 << 1)
+#define Q_TYPE_FSTN (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)
+#define QH_NEXT(ehci,dma) (cpu_to_hc32(ehci, (((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 */
+#define EHCI_LIST_END(ehci) cpu_to_hc32(ehci, 1) /* "null pointer" to hw */
/*
* Entries in periodic shadow table are pointers to one of four kinds
@@ -376,7 +420,7 @@ union ehci_shadow {
struct ehci_itd *itd; /* Q_TYPE_ITD */
struct ehci_sitd *sitd; /* Q_TYPE_SITD */
struct ehci_fstn *fstn; /* Q_TYPE_FSTN */
- __le32 *hw_next; /* (all types) */
+ __hc32 *hw_next; /* (all types) */
void *ptr;
};
@@ -392,23 +436,27 @@ union ehci_shadow {
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 */
+ __hc32 hw_next; /* see EHCI 3.6.1 */
+ __hc32 hw_info1; /* see EHCI 3.6.2 */
#define QH_HEAD 0x00008000
- __le32 hw_info2; /* see EHCI 3.6.2 */
+#define QH_INACTIVATE 0x00000080
+
+#define INACTIVATE_BIT(ehci) cpu_to_hc32(ehci, QH_INACTIVATE)
+
+ __hc32 hw_info2; /* see EHCI 3.6.2 */
#define QH_SMASK 0x000000ff
#define QH_CMASK 0x0000ff00
#define QH_HUBADDR 0x007f0000
#define QH_HUBPORT 0x3f800000
#define QH_MULT 0xc0000000
- __le32 hw_current; /* qtd list - see EHCI 3.6.4 */
+ __hc32 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];
+ __hc32 hw_qtd_next;
+ __hc32 hw_alt_next;
+ __hc32 hw_token;
+ __hc32 hw_buf [5];
+ __hc32 hw_buf_hi [5];
/* the rest is HCD-private */
dma_addr_t qh_dma; /* address of qh */
@@ -418,7 +466,14 @@ struct ehci_qh {
struct ehci_qh *reclaim; /* next to reclaim */
struct ehci_hcd *ehci;
- struct kref kref;
+
+ /*
+ * Do NOT use atomic operations for QH refcounting. On some CPUs
+ * (PPC7448 for example), atomic operations cannot be performed on
+ * memory that is cache-inhibited (i.e. being used for DMA).
+ * Spinlocks are used to protect all QH fields.
+ */
+ u32 refcount;
unsigned stamp;
u8 qh_state;
@@ -437,6 +492,10 @@ struct ehci_qh {
unsigned short start; /* where polling starts */
#define NO_FRAME ((unsigned short)~0) /* pick new start */
struct usb_device *dev; /* access to TT */
+#ifdef CONFIG_CPU_FREQ
+ struct list_head split_intr_qhs; /* list of split qhs */
+ __le32 was_active; /* active bit before "i" set */
+#endif
} __attribute__ ((aligned (32)));
/*-------------------------------------------------------------------------*/
@@ -445,7 +504,7 @@ struct ehci_qh {
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] |= */
+ __hc32 transaction; /* itd->hw_transaction[i] |= */
u8 cross; /* buf crosses pages */
/* for full speed OUT splits */
u32 buf1;
@@ -467,8 +526,8 @@ struct ehci_iso_sched {
*/
struct ehci_iso_stream {
/* first two fields match QH, but info1 == 0 */
- __le32 hw_next;
- __le32 hw_info1;
+ __hc32 hw_next;
+ __hc32 hw_info1;
u32 refcount;
u8 bEndpointAddress;
@@ -483,7 +542,7 @@ struct ehci_iso_stream {
unsigned long start; /* jiffies */
unsigned long rescheduled;
int next_uframe;
- __le32 splits;
+ __hc32 splits;
/* the rest is derived from the endpoint descriptor,
* trusting urb->interval == f(epdesc->bInterval) and
@@ -497,12 +556,12 @@ struct ehci_iso_stream {
unsigned bandwidth;
/* This is used to initialize iTD's hw_bufp fields */
- __le32 buf0;
- __le32 buf1;
- __le32 buf2;
+ __hc32 buf0;
+ __hc32 buf1;
+ __hc32 buf2;
/* this is used to initialize sITD's tt info */
- __le32 address;
+ __hc32 address;
};
/*-------------------------------------------------------------------------*/
@@ -515,8 +574,8 @@ struct ehci_iso_stream {
*/
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 */
+ __hc32 hw_next; /* see EHCI 3.3.1 */
+ __hc32 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 */
@@ -524,10 +583,10 @@ struct ehci_itd {
#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)
+#define ITD_ACTIVE(ehci) cpu_to_hc32(ehci, EHCI_ISOC_ACTIVE)
- __le32 hw_bufp [7]; /* see EHCI 3.3.3 */
- __le32 hw_bufp_hi [7]; /* Appendix B */
+ __hc32 hw_bufp [7]; /* see EHCI 3.3.3 */
+ __hc32 hw_bufp_hi [7]; /* Appendix B */
/* the rest is HCD-private */
dma_addr_t itd_dma; /* for this itd */
@@ -554,11 +613,11 @@ struct ehci_itd {
*/
struct ehci_sitd {
/* first part defined by EHCI spec */
- __le32 hw_next;
+ __hc32 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 */
+ __hc32 hw_fullspeed_ep; /* EHCI table 3-9 */
+ __hc32 hw_uframe; /* EHCI table 3-10 */
+ __hc32 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))
@@ -570,11 +629,11 @@ struct ehci_sitd {
#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)
+#define SITD_ACTIVE(ehci) cpu_to_hc32(ehci, 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 */
+ __hc32 hw_buf [2]; /* EHCI table 3-12 */
+ __hc32 hw_backpointer; /* EHCI table 3-13 */
+ __hc32 hw_buf_hi [2]; /* Appendix B */
/* the rest is HCD-private */
dma_addr_t sitd_dma;
@@ -599,8 +658,8 @@ struct ehci_sitd {
* 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 */
+ __hc32 hw_next; /* any periodic q entry */
+ __hc32 hw_prev; /* qh or EHCI_LIST_END */
/* the rest is HCD-private */
dma_addr_t fstn_dma;
@@ -672,8 +731,21 @@ ehci_port_speed(struct ehci_hcd *ehci, unsigned int portsc)
#define ehci_big_endian_mmio(e) 0
#endif
-static inline unsigned int ehci_readl (const struct ehci_hcd *ehci,
- __u32 __iomem * regs)
+/*
+ * Big-endian read/write functions are arch-specific.
+ * Other arches can be added if/when they're needed.
+ *
+ * REVISIT: arch/powerpc now has readl/writel_be, so the
+ * definition below can die once the 4xx support is
+ * finally ported over.
+ */
+#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 ehci_readl(const struct ehci_hcd *ehci,
+ __u32 __iomem * regs)
{
#ifdef CONFIG_USB_EHCI_BIG_ENDIAN_MMIO
return ehci_big_endian_mmio(ehci) ?
@@ -684,8 +756,8 @@ static inline unsigned int ehci_readl (const struct ehci_hcd *ehci,
#endif
}
-static inline void ehci_writel (const struct ehci_hcd *ehci,
- const unsigned int val, __u32 __iomem *regs)
+static inline void ehci_writel(const struct ehci_hcd *ehci,
+ const unsigned int val, __u32 __iomem *regs)
{
#ifdef CONFIG_USB_EHCI_BIG_ENDIAN_MMIO
ehci_big_endian_mmio(ehci) ?
@@ -698,6 +770,62 @@ static inline void ehci_writel (const struct ehci_hcd *ehci,
/*-------------------------------------------------------------------------*/
+/*
+ * The AMCC 440EPx not only implements its EHCI registers in big-endian
+ * format, but also its DMA data structures (descriptors).
+ *
+ * EHCI controllers accessed through PCI work normally (little-endian
+ * everywhere), so we won't bother supporting a BE-only mode for now.
+ */
+#ifdef CONFIG_USB_EHCI_BIG_ENDIAN_DESC
+#define ehci_big_endian_desc(e) ((e)->big_endian_desc)
+
+/* cpu to ehci */
+static inline __hc32 cpu_to_hc32 (const struct ehci_hcd *ehci, const u32 x)
+{
+ return ehci_big_endian_desc(ehci)
+ ? (__force __hc32)cpu_to_be32(x)
+ : (__force __hc32)cpu_to_le32(x);
+}
+
+/* ehci to cpu */
+static inline u32 hc32_to_cpu (const struct ehci_hcd *ehci, const __hc32 x)
+{
+ return ehci_big_endian_desc(ehci)
+ ? be32_to_cpu((__force __be32)x)
+ : le32_to_cpu((__force __le32)x);
+}
+
+static inline u32 hc32_to_cpup (const struct ehci_hcd *ehci, const __hc32 *x)
+{
+ return ehci_big_endian_desc(ehci)
+ ? be32_to_cpup((__force __be32 *)x)
+ : le32_to_cpup((__force __le32 *)x);
+}
+
+#else
+
+/* cpu to ehci */
+static inline __hc32 cpu_to_hc32 (const struct ehci_hcd *ehci, const u32 x)
+{
+ return cpu_to_le32(x);
+}
+
+/* ehci to cpu */
+static inline u32 hc32_to_cpu (const struct ehci_hcd *ehci, const __hc32 x)
+{
+ return le32_to_cpu(x);
+}
+
+static inline u32 hc32_to_cpup (const struct ehci_hcd *ehci, const __hc32 *x)
+{
+ return le32_to_cpup(x);
+}
+
+#endif
+
+/*-------------------------------------------------------------------------*/
+
#ifndef DEBUG
#define STUB_DEBUG_FILES
#endif /* DEBUG */
diff --git a/drivers/usb/host/ohci-dbg.c b/drivers/usb/host/ohci-dbg.c
index 273d5ddb72b..6f9e43e9a6c 100644
--- a/drivers/usb/host/ohci-dbg.c
+++ b/drivers/usb/host/ohci-dbg.c
@@ -23,7 +23,7 @@
/* debug| print the main components of an URB
* small: 0) header + data packets 1) just header
*/
-static void __attribute__((unused))
+static void __maybe_unused
urb_print (struct urb * urb, char * str, int small)
{
unsigned int pipe= urb->pipe;
@@ -338,7 +338,7 @@ static void ohci_dump_td (const struct ohci_hcd *ohci, const char *label,
}
/* caller MUST own hcd spinlock if verbose is set! */
-static void __attribute__((unused))
+static void __maybe_unused
ohci_dump_ed (const struct ohci_hcd *ohci, const char *label,
const struct ed *ed, int verbose)
{
diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c
index a66637e725f..2038125b7f8 100644
--- a/drivers/usb/host/ohci-hcd.c
+++ b/drivers/usb/host/ohci-hcd.c
@@ -35,15 +35,13 @@
#include <linux/dma-mapping.h>
#include <linux/dmapool.h>
#include <linux/reboot.h>
+#include <linux/workqueue.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/system.h>
#include <asm/unaligned.h>
#include <asm/byteorder.h>
-#ifdef CONFIG_PPC_PS3
-#include <asm/firmware.h>
-#endif
#include "../core/hcd.h"
@@ -82,6 +80,8 @@ static const char hcd_name [] = "ohci_hcd";
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);
+static int ohci_restart (struct ohci_hcd *ohci);
+static void ohci_quirk_nec_worker (struct work_struct *work);
#include "ohci-hub.c"
#include "ohci-dbg.c"
@@ -510,15 +510,7 @@ static int ohci_run (struct ohci_hcd *ohci)
// flush the writes
(void) ohci_readl (ohci, &ohci->regs->control);
msleep(temp);
- temp = roothub_a (ohci);
- if (!(temp & RH_A_NPS)) {
- /* power down each port */
- for (temp = 0; temp < ohci->num_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 */
@@ -659,9 +651,20 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd)
}
if (ints & OHCI_INTR_UE) {
- disable (ohci);
- ohci_err (ohci, "OHCI Unrecoverable Error, disabled\n");
// e.g. due to PCI Master/Target Abort
+ if (ohci->flags & OHCI_QUIRK_NEC) {
+ /* Workaround for a silicon bug in some NEC chips used
+ * in Apple's PowerBooks. Adapted from Darwin code.
+ */
+ ohci_err (ohci, "OHCI Unrecoverable Error, scheduling NEC chip restart\n");
+
+ ohci_writel (ohci, OHCI_INTR_UE, &regs->intrdisable);
+
+ schedule_work (&ohci->nec_work);
+ } else {
+ disable (ohci);
+ ohci_err (ohci, "OHCI Unrecoverable Error, disabled\n");
+ }
ohci_dump (ohci, 1);
ohci_usb_reset (ohci);
@@ -763,23 +766,16 @@ static void ohci_stop (struct usb_hcd *hcd)
/*-------------------------------------------------------------------------*/
/* must not be called from interrupt context */
-
-#ifdef CONFIG_PM
-
static int ohci_restart (struct ohci_hcd *ohci)
{
int temp;
int i;
struct urb_priv *priv;
- /* 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);
- usb_root_hub_lost_power(ohci_to_hcd(ohci)->self.root_hub);
+
+ /* Recycle any "live" eds/tds (and urbs). */
if (!list_empty (&ohci->pending))
ohci_dbg(ohci, "abort schedule...\n");
list_for_each_entry (priv, &ohci->pending, pending) {
@@ -826,20 +822,31 @@ static int ohci_restart (struct ohci_hcd *ohci)
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 = ohci->num_ports;
- while (i--)
- ohci_writel (ohci, RH_PS_PSS,
- &ohci->regs->roothub.portstatus [i]);
- ohci_dbg (ohci, "restart complete\n");
}
+ ohci_dbg(ohci, "restart complete\n");
return 0;
}
-#endif
+
+/*-------------------------------------------------------------------------*/
+
+/* NEC workaround */
+static void ohci_quirk_nec_worker(struct work_struct *work)
+{
+ struct ohci_hcd *ohci = container_of(work, struct ohci_hcd, nec_work);
+ int status;
+
+ status = ohci_init(ohci);
+ if (status != 0) {
+ ohci_err(ohci, "Restarting NEC controller failed "
+ "in ohci_init, %d\n", status);
+ return;
+ }
+
+ status = ohci_restart(ohci);
+ if (status != 0)
+ ohci_err(ohci, "Restarting NEC controller failed "
+ "in ohci_restart, %d\n", status);
+}
/*-------------------------------------------------------------------------*/
@@ -917,7 +924,7 @@ MODULE_LICENSE ("GPL");
#ifdef CONFIG_PPC_PS3
#include "ohci-ps3.c"
-#define PS3_SYSTEM_BUS_DRIVER ps3_ohci_sb_driver
+#define PS3_SYSTEM_BUS_DRIVER ps3_ohci_driver
#endif
#if !defined(PCI_DRIVER) && \
@@ -940,12 +947,9 @@ static int __init ohci_hcd_mod_init(void)
sizeof (struct ed), sizeof (struct td));
#ifdef PS3_SYSTEM_BUS_DRIVER
- if (firmware_has_feature(FW_FEATURE_PS3_LV1)) {
- retval = ps3_system_bus_driver_register(
- &PS3_SYSTEM_BUS_DRIVER);
- if (retval < 0)
- goto error_ps3;
- }
+ retval = ps3_ohci_driver_register(&PS3_SYSTEM_BUS_DRIVER);
+ if (retval < 0)
+ goto error_ps3;
#endif
#ifdef PLATFORM_DRIVER
@@ -991,8 +995,7 @@ static int __init ohci_hcd_mod_init(void)
error_platform:
#endif
#ifdef PS3_SYSTEM_BUS_DRIVER
- if (firmware_has_feature(FW_FEATURE_PS3_LV1))
- ps3_system_bus_driver_unregister(&PS3_SYSTEM_BUS_DRIVER);
+ ps3_ohci_driver_unregister(&PS3_SYSTEM_BUS_DRIVER);
error_ps3:
#endif
return retval;
@@ -1014,8 +1017,7 @@ static void __exit ohci_hcd_mod_exit(void)
platform_driver_unregister(&PLATFORM_DRIVER);
#endif
#ifdef PS3_SYSTEM_BUS_DRIVER
- if (firmware_has_feature(FW_FEATURE_PS3_LV1))
- ps3_system_bus_driver_unregister(&PS3_SYSTEM_BUS_DRIVER);
+ ps3_ohci_driver_unregister(&PS3_SYSTEM_BUS_DRIVER);
#endif
}
module_exit(ohci_hcd_mod_exit);
diff --git a/drivers/usb/host/ohci-hub.c b/drivers/usb/host/ohci-hub.c
index bb9cc595219..48e4b11f4d3 100644
--- a/drivers/usb/host/ohci-hub.c
+++ b/drivers/usb/host/ohci-hub.c
@@ -55,8 +55,6 @@ static void dl_done_list (struct ohci_hcd *);
static void finish_unlinks (struct ohci_hcd *, u16);
#ifdef CONFIG_PM
-static int ohci_restart(struct ohci_hcd *ohci);
-
static int ohci_rh_suspend (struct ohci_hcd *ohci, int autostop)
__releases(ohci->lock)
__acquires(ohci->lock)
@@ -191,6 +189,9 @@ __acquires(ohci->lock)
spin_unlock_irq (&ohci->lock);
(void) ohci_init (ohci);
status = ohci_restart (ohci);
+
+ usb_root_hub_lost_power(hcd->self.root_hub);
+
spin_lock_irq (&ohci->lock);
}
return status;
diff --git a/drivers/usb/host/ohci-mem.c b/drivers/usb/host/ohci-mem.c
index 2f20d3dc895..450c7b460c5 100644
--- a/drivers/usb/host/ohci-mem.c
+++ b/drivers/usb/host/ohci-mem.c
@@ -28,6 +28,7 @@ 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->nec_work, ohci_quirk_nec_worker);
}
/*-------------------------------------------------------------------------*/
diff --git a/drivers/usb/host/ohci-pci.c b/drivers/usb/host/ohci-pci.c
index ca62cb58322..a5e2eb85d07 100644
--- a/drivers/usb/host/ohci-pci.c
+++ b/drivers/usb/host/ohci-pci.c
@@ -111,6 +111,18 @@ static int ohci_quirk_toshiba_scc(struct usb_hcd *hcd)
#endif
}
+/* Check for NEC chip and apply quirk for allegedly lost interrupts.
+ */
+static int ohci_quirk_nec(struct usb_hcd *hcd)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci (hcd);
+
+ ohci->flags |= OHCI_QUIRK_NEC;
+ ohci_dbg (ohci, "enabled NEC chipset lost interrupt quirk\n");
+
+ return 0;
+}
+
/* List of quirks for OHCI */
static const struct pci_device_id ohci_pci_quirks[] = {
{
@@ -134,6 +146,10 @@ static const struct pci_device_id ohci_pci_quirks[] = {
.driver_data = (unsigned long)ohci_quirk_toshiba_scc,
},
{
+ PCI_DEVICE(PCI_VENDOR_ID_NEC, PCI_DEVICE_ID_NEC_USB),
+ .driver_data = (unsigned long)ohci_quirk_nec,
+ },
+ {
/* Toshiba portege 4000 */
.vendor = PCI_VENDOR_ID_AL,
.device = 0x5237,
@@ -202,6 +218,42 @@ static int __devinit ohci_pci_start (struct usb_hcd *hcd)
return ret;
}
+#if defined(CONFIG_USB_PERSIST) && (defined(CONFIG_USB_EHCI_HCD) || \
+ defined(CONFIG_USB_EHCI_HCD_MODULE))
+
+/* Following a power loss, we must prepare to regain control of the ports
+ * we used to own. This means turning on the port power before ehci-hcd
+ * tries to switch ownership.
+ *
+ * This isn't a 100% perfect solution. On most systems the OHCI controllers
+ * lie at lower PCI addresses than the EHCI controller, so they will be
+ * discovered (and hence resumed) first. But there is no guarantee things
+ * will always work this way. If the EHCI controller is resumed first and
+ * the OHCI ports are unpowered, then the handover will fail.
+ */
+static void prepare_for_handover(struct usb_hcd *hcd)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci(hcd);
+ int port;
+
+ /* Here we "know" root ports should always stay powered */
+ ohci_dbg(ohci, "powerup ports\n");
+ for (port = 0; port < ohci->num_ports; port++)
+ ohci_writel(ohci, RH_PS_PPS,
+ &ohci->regs->roothub.portstatus[port]);
+
+ /* Flush those writes */
+ ohci_readl(ohci, &ohci->regs->control);
+ msleep(20);
+}
+
+#else
+
+static inline void prepare_for_handover(struct usb_hcd *hcd)
+{ }
+
+#endif /* CONFIG_USB_PERSIST etc. */
+
#ifdef CONFIG_PM
static int ohci_pci_suspend (struct usb_hcd *hcd, pm_message_t message)
@@ -241,7 +293,10 @@ static int ohci_pci_suspend (struct usb_hcd *hcd, pm_message_t message)
static int ohci_pci_resume (struct usb_hcd *hcd)
{
set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
- usb_hcd_resume_root_hub(hcd);
+
+ /* FIXME: we should try to detect loss of VBUS power here */
+ prepare_for_handover(hcd);
+
return 0;
}
diff --git a/drivers/usb/host/ohci-pnx4008.c b/drivers/usb/host/ohci-pnx4008.c
index d601bbb9387..ca2a6abbc11 100644
--- a/drivers/usb/host/ohci-pnx4008.c
+++ b/drivers/usb/host/ohci-pnx4008.c
@@ -134,7 +134,7 @@ static int isp1301_attach(struct i2c_adapter *adap, int addr, int kind)
{
struct i2c_client *c;
- c = (struct i2c_client *)kzalloc(sizeof(*c), GFP_KERNEL);
+ c = kzalloc(sizeof(*c), GFP_KERNEL);
if (!c)
return -ENOMEM;
diff --git a/drivers/usb/host/ohci-ps3.c b/drivers/usb/host/ohci-ps3.c
index d7cf07288b0..01a0caeaa6b 100644
--- a/drivers/usb/host/ohci-ps3.c
+++ b/drivers/usb/host/ohci-ps3.c
@@ -18,6 +18,7 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#include <asm/firmware.h>
#include <asm/ps3.h>
static int ps3_ohci_hc_reset(struct usb_hcd *hcd)
@@ -75,7 +76,7 @@ static const struct hc_driver ps3_ohci_hc_driver = {
#endif
};
-static int ps3_ohci_sb_probe(struct ps3_system_bus_device *dev)
+static int ps3_ohci_probe(struct ps3_system_bus_device *dev)
{
int result;
struct usb_hcd *hcd;
@@ -87,13 +88,31 @@ static int ps3_ohci_sb_probe(struct ps3_system_bus_device *dev)
goto fail_start;
}
+ result = ps3_open_hv_device(dev);
+
+ if (result) {
+ dev_dbg(&dev->core, "%s:%d: ps3_open_hv_device failed: %s\n",
+ __func__, __LINE__, ps3_result(result));
+ result = -EPERM;
+ goto fail_open;
+ }
+
+ result = ps3_dma_region_create(dev->d_region);
+
+ if (result) {
+ dev_dbg(&dev->core, "%s:%d: ps3_dma_region_create failed: "
+ "(%d)\n", __func__, __LINE__, result);
+ BUG_ON("check region type");
+ goto fail_dma_region;
+ }
+
result = ps3_mmio_region_create(dev->m_region);
if (result) {
dev_dbg(&dev->core, "%s:%d: ps3_map_mmio_region failed\n",
__func__, __LINE__);
result = -EPERM;
- goto fail_mmio;
+ goto fail_mmio_region;
}
dev_dbg(&dev->core, "%s:%d: mmio mapped_addr %lxh\n", __func__,
@@ -122,6 +141,11 @@ static int ps3_ohci_sb_probe(struct ps3_system_bus_device *dev)
hcd->rsrc_start = dev->m_region->lpar_addr;
hcd->rsrc_len = dev->m_region->len;
+
+ if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name))
+ dev_dbg(&dev->core, "%s:%d: request_mem_region failed\n",
+ __func__, __LINE__);
+
hcd->regs = ioremap(dev->m_region->lpar_addr, dev->m_region->len);
if (!hcd->regs) {
@@ -155,34 +179,73 @@ static int ps3_ohci_sb_probe(struct ps3_system_bus_device *dev)
fail_add_hcd:
iounmap(hcd->regs);
fail_ioremap:
+ release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
usb_put_hcd(hcd);
fail_create_hcd:
ps3_io_irq_destroy(virq);
fail_irq:
ps3_free_mmio_region(dev->m_region);
-fail_mmio:
+fail_mmio_region:
+ ps3_dma_region_free(dev->d_region);
+fail_dma_region:
+ ps3_close_hv_device(dev);
+fail_open:
fail_start:
return result;
}
-static int ps3_ohci_sb_remove (struct ps3_system_bus_device *dev)
+static int ps3_ohci_remove (struct ps3_system_bus_device *dev)
{
+ unsigned int tmp;
struct usb_hcd *hcd =
(struct usb_hcd *)ps3_system_bus_get_driver_data(dev);
- usb_put_hcd(hcd);
+ BUG_ON(!hcd);
+
+ dev_dbg(&dev->core, "%s:%d: regs %p\n", __func__, __LINE__, hcd->regs);
+ dev_dbg(&dev->core, "%s:%d: irq %u\n", __func__, __LINE__, hcd->irq);
+
+ tmp = hcd->irq;
+
+ usb_remove_hcd(hcd);
+
ps3_system_bus_set_driver_data(dev, NULL);
+ BUG_ON(!hcd->regs);
+ iounmap(hcd->regs);
+
+ release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+ usb_put_hcd(hcd);
+
+ ps3_io_irq_destroy(tmp);
+ ps3_free_mmio_region(dev->m_region);
+
+ ps3_dma_region_free(dev->d_region);
+ ps3_close_hv_device(dev);
+
return 0;
}
-MODULE_ALIAS("ps3-ohci");
+static int ps3_ohci_driver_register(struct ps3_system_bus_driver *drv)
+{
+ return firmware_has_feature(FW_FEATURE_PS3_LV1)
+ ? ps3_system_bus_driver_register(drv)
+ : 0;
+}
+
+static void ps3_ohci_driver_unregister(struct ps3_system_bus_driver *drv)
+{
+ if (firmware_has_feature(FW_FEATURE_PS3_LV1))
+ ps3_system_bus_driver_unregister(drv);
+}
+
+MODULE_ALIAS(PS3_MODULE_ALIAS_OHCI);
-static struct ps3_system_bus_driver ps3_ohci_sb_driver = {
+static struct ps3_system_bus_driver ps3_ohci_driver = {
+ .core.name = "ps3-ohci-driver",
+ .core.owner = THIS_MODULE,
.match_id = PS3_MATCH_ID_OHCI,
- .core = {
- .name = "ps3-ohci-driver",
- },
- .probe = ps3_ohci_sb_probe,
- .remove = ps3_ohci_sb_remove,
+ .probe = ps3_ohci_probe,
+ .remove = ps3_ohci_remove,
+ .shutdown = ps3_ohci_remove,
};
diff --git a/drivers/usb/host/ohci.h b/drivers/usb/host/ohci.h
index c2b5ecfe5e9..4ada43cf138 100644
--- a/drivers/usb/host/ohci.h
+++ b/drivers/usb/host/ohci.h
@@ -397,8 +397,10 @@ struct ohci_hcd {
#define OHCI_QUIRK_BE_DESC 0x08 /* BE descriptors */
#define OHCI_QUIRK_BE_MMIO 0x10 /* BE registers */
#define OHCI_QUIRK_ZFMICRO 0x20 /* Compaq ZFMicro chipset*/
+#define OHCI_QUIRK_NEC 0x40 /* lost interrupts */
// there are also chip quirks/bugs in init logic
+ struct work_struct nec_work; /* Worker for NEC quirk */
};
/* convert between an hcd pointer and the corresponding ohci_hcd */
diff --git a/drivers/usb/host/r8a66597-hcd.c b/drivers/usb/host/r8a66597-hcd.c
new file mode 100644
index 00000000000..a7a7070c6e2
--- /dev/null
+++ b/drivers/usb/host/r8a66597-hcd.c
@@ -0,0 +1,2244 @@
+/*
+ * R8A66597 HCD (Host Controller Driver)
+ *
+ * Copyright (C) 2006-2007 Renesas Solutions Corp.
+ * Portions Copyright (C) 2004 Psion Teklogix (for NetBook PRO)
+ * Portions Copyright (C) 2004-2005 David Brownell
+ * Portions Copyright (C) 1999 Roman Weissgaerber
+ *
+ * Author : Yoshihiro Shimoda <shimoda.yoshihiro@renesas.com>
+ *
+ * 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; version 2 of the License.
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/smp_lock.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/delay.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/usb.h>
+#include <linux/platform_device.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+
+#include "../core/hcd.h"
+#include "r8a66597.h"
+
+MODULE_DESCRIPTION("R8A66597 USB Host Controller Driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Yoshihiro Shimoda");
+
+#define DRIVER_VERSION "29 May 2007"
+
+static const char hcd_name[] = "r8a66597_hcd";
+
+/* module parameters */
+static unsigned short clock = XTAL12;
+module_param(clock, ushort, 0644);
+MODULE_PARM_DESC(clock, "input clock: 48MHz=32768, 24MHz=16384, 12MHz=0(default=0)");
+static unsigned short vif = LDRV;
+module_param(vif, ushort, 0644);
+MODULE_PARM_DESC(vif, "input VIF: 3.3V=32768, 1.5V=0(default=32768)");
+static unsigned short endian = 0;
+module_param(endian, ushort, 0644);
+MODULE_PARM_DESC(endian, "data endian: big=256, little=0(default=0)");
+static unsigned short irq_sense = INTL;
+module_param(irq_sense, ushort, 0644);
+MODULE_PARM_DESC(irq_sense, "IRQ sense: low level=32, falling edge=0(default=32)");
+
+static void packet_write(struct r8a66597 *r8a66597, u16 pipenum);
+static int r8a66597_get_frame(struct usb_hcd *hcd);
+
+/* this function must be called with interrupt disabled */
+static void enable_pipe_irq(struct r8a66597 *r8a66597, u16 pipenum,
+ unsigned long reg)
+{
+ u16 tmp;
+
+ tmp = r8a66597_read(r8a66597, INTENB0);
+ r8a66597_bclr(r8a66597, BEMPE | NRDYE | BRDYE, INTENB0);
+ r8a66597_bset(r8a66597, 1 << pipenum, reg);
+ r8a66597_write(r8a66597, tmp, INTENB0);
+}
+
+/* this function must be called with interrupt disabled */
+static void disable_pipe_irq(struct r8a66597 *r8a66597, u16 pipenum,
+ unsigned long reg)
+{
+ u16 tmp;
+
+ tmp = r8a66597_read(r8a66597, INTENB0);
+ r8a66597_bclr(r8a66597, BEMPE | NRDYE | BRDYE, INTENB0);
+ r8a66597_bclr(r8a66597, 1 << pipenum, reg);
+ r8a66597_write(r8a66597, tmp, INTENB0);
+}
+
+static void set_devadd_reg(struct r8a66597 *r8a66597, u8 r8a66597_address,
+ u16 usbspd, u8 upphub, u8 hubport, int port)
+{
+ u16 val;
+ unsigned long devadd_reg = get_devadd_addr(r8a66597_address);
+
+ val = (upphub << 11) | (hubport << 8) | (usbspd << 6) | (port & 0x0001);
+ r8a66597_write(r8a66597, val, devadd_reg);
+}
+
+static int enable_controller(struct r8a66597 *r8a66597)
+{
+ u16 tmp;
+ int i = 0;
+
+ do {
+ r8a66597_write(r8a66597, USBE, SYSCFG0);
+ tmp = r8a66597_read(r8a66597, SYSCFG0);
+ if (i++ > 1000) {
+ err("register access fail.");
+ return -ENXIO;
+ }
+ } while ((tmp & USBE) != USBE);
+ r8a66597_bclr(r8a66597, USBE, SYSCFG0);
+ r8a66597_mdfy(r8a66597, clock, XTAL, SYSCFG0);
+
+ i = 0;
+ r8a66597_bset(r8a66597, XCKE, SYSCFG0);
+ do {
+ msleep(1);
+ tmp = r8a66597_read(r8a66597, SYSCFG0);
+ if (i++ > 500) {
+ err("register access fail.");
+ return -ENXIO;
+ }
+ } while ((tmp & SCKE) != SCKE);
+
+ r8a66597_bset(r8a66597, DCFM | DRPD, SYSCFG0);
+ r8a66597_bset(r8a66597, DRPD, SYSCFG1);
+
+ r8a66597_bset(r8a66597, vif & LDRV, PINCFG);
+ r8a66597_bset(r8a66597, HSE, SYSCFG0);
+ r8a66597_bset(r8a66597, HSE, SYSCFG1);
+ r8a66597_bset(r8a66597, USBE, SYSCFG0);
+
+ r8a66597_bset(r8a66597, BEMPE | NRDYE | BRDYE, INTENB0);
+ r8a66597_bset(r8a66597, irq_sense & INTL, SOFCFG);
+ r8a66597_bset(r8a66597, BRDY0, BRDYENB);
+ r8a66597_bset(r8a66597, BEMP0, BEMPENB);
+
+ r8a66597_write(r8a66597, BURST | CPU_ADR_RD_WR, DMA0CFG);
+ r8a66597_write(r8a66597, BURST | CPU_ADR_RD_WR, DMA1CFG);
+
+ r8a66597_bset(r8a66597, endian & BIGEND, CFIFOSEL);
+ r8a66597_bset(r8a66597, endian & BIGEND, D0FIFOSEL);
+ r8a66597_bset(r8a66597, endian & BIGEND, D1FIFOSEL);
+
+ r8a66597_bset(r8a66597, TRNENSEL, SOFCFG);
+
+ r8a66597_bset(r8a66597, SIGNE | SACKE, INTENB1);
+ r8a66597_bclr(r8a66597, DTCHE, INTENB1);
+ r8a66597_bset(r8a66597, ATTCHE, INTENB1);
+ r8a66597_bclr(r8a66597, DTCHE, INTENB2);
+ r8a66597_bset(r8a66597, ATTCHE, INTENB2);
+
+ return 0;
+}
+
+static void disable_controller(struct r8a66597 *r8a66597)
+{
+ u16 tmp;
+
+ r8a66597_write(r8a66597, 0, INTENB0);
+ r8a66597_write(r8a66597, 0, INTENB1);
+ r8a66597_write(r8a66597, 0, INTENB2);
+ r8a66597_write(r8a66597, 0, INTSTS0);
+ r8a66597_write(r8a66597, 0, INTSTS1);
+ r8a66597_write(r8a66597, 0, INTSTS2);
+
+ r8a66597_port_power(r8a66597, 0, 0);
+ r8a66597_port_power(r8a66597, 1, 0);
+
+ do {
+ tmp = r8a66597_read(r8a66597, SOFCFG) & EDGESTS;
+ udelay(640);
+ } while (tmp == EDGESTS);
+
+ r8a66597_bclr(r8a66597, DCFM | DRPD, SYSCFG0);
+ r8a66597_bclr(r8a66597, DRPD, SYSCFG1);
+ r8a66597_bclr(r8a66597, HSE, SYSCFG0);
+ r8a66597_bclr(r8a66597, HSE, SYSCFG1);
+
+ r8a66597_bclr(r8a66597, SCKE, SYSCFG0);
+ udelay(1);
+ r8a66597_bclr(r8a66597, PLLC, SYSCFG0);
+ r8a66597_bclr(r8a66597, XCKE, SYSCFG0);
+ r8a66597_bclr(r8a66597, USBE, SYSCFG0);
+}
+
+static int get_parent_r8a66597_address(struct r8a66597 *r8a66597,
+ struct usb_device *udev)
+{
+ struct r8a66597_device *dev;
+
+ if (udev->parent && udev->parent->devnum != 1)
+ udev = udev->parent;
+
+ dev = dev_get_drvdata(&udev->dev);
+ if (dev)
+ return dev->address;
+ else
+ return 0;
+}
+
+static int is_child_device(char *devpath)
+{
+ return (devpath[2] ? 1 : 0);
+}
+
+static int is_hub_limit(char *devpath)
+{
+ return ((strlen(devpath) >= 4) ? 1 : 0);
+}
+
+static void get_port_number(char *devpath, u16 *root_port, u16 *hub_port)
+{
+ if (root_port) {
+ *root_port = (devpath[0] & 0x0F) - 1;
+ if (*root_port >= R8A66597_MAX_ROOT_HUB)
+ err("illegal root port number");
+ }
+ if (hub_port)
+ *hub_port = devpath[2] & 0x0F;
+}
+
+static u16 get_r8a66597_usb_speed(enum usb_device_speed speed)
+{
+ u16 usbspd = 0;
+
+ switch (speed) {
+ case USB_SPEED_LOW:
+ usbspd = LSMODE;
+ break;
+ case USB_SPEED_FULL:
+ usbspd = FSMODE;
+ break;
+ case USB_SPEED_HIGH:
+ usbspd = HSMODE;
+ break;
+ default:
+ err("unknown speed");
+ break;
+ }
+
+ return usbspd;
+}
+
+static void set_child_connect_map(struct r8a66597 *r8a66597, int address)
+{
+ int idx;
+
+ idx = address / 32;
+ r8a66597->child_connect_map[idx] |= 1 << (address % 32);
+}
+
+static void put_child_connect_map(struct r8a66597 *r8a66597, int address)
+{
+ int idx;
+
+ idx = address / 32;
+ r8a66597->child_connect_map[idx] &= ~(1 << (address % 32));
+}
+
+static void set_pipe_reg_addr(struct r8a66597_pipe *pipe, u8 dma_ch)
+{
+ u16 pipenum = pipe->info.pipenum;
+ unsigned long fifoaddr[] = {D0FIFO, D1FIFO, CFIFO};
+ unsigned long fifosel[] = {D0FIFOSEL, D1FIFOSEL, CFIFOSEL};
+ unsigned long fifoctr[] = {D0FIFOCTR, D1FIFOCTR, CFIFOCTR};
+
+ if (dma_ch > R8A66597_PIPE_NO_DMA) /* dma fifo not use? */
+ dma_ch = R8A66597_PIPE_NO_DMA;
+
+ pipe->fifoaddr = fifoaddr[dma_ch];
+ pipe->fifosel = fifosel[dma_ch];
+ pipe->fifoctr = fifoctr[dma_ch];
+
+ if (pipenum == 0)
+ pipe->pipectr = DCPCTR;
+ else
+ pipe->pipectr = get_pipectr_addr(pipenum);
+
+ if (check_bulk_or_isoc(pipenum)) {
+ pipe->pipetre = get_pipetre_addr(pipenum);
+ pipe->pipetrn = get_pipetrn_addr(pipenum);
+ } else {
+ pipe->pipetre = 0;
+ pipe->pipetrn = 0;
+ }
+}
+
+static struct r8a66597_device *
+get_urb_to_r8a66597_dev(struct r8a66597 *r8a66597, struct urb *urb)
+{
+ if (usb_pipedevice(urb->pipe) == 0)
+ return &r8a66597->device0;
+
+ return dev_get_drvdata(&urb->dev->dev);
+}
+
+static int make_r8a66597_device(struct r8a66597 *r8a66597,
+ struct urb *urb, u8 addr)
+{
+ struct r8a66597_device *dev;
+ int usb_address = urb->setup_packet[2]; /* urb->pipe is address 0 */
+
+ dev = kzalloc(sizeof(struct r8a66597_device), GFP_KERNEL);
+ if (dev == NULL)
+ return -ENOMEM;
+
+ dev_set_drvdata(&urb->dev->dev, dev);
+ dev->udev = urb->dev;
+ dev->address = addr;
+ dev->usb_address = usb_address;
+ dev->state = USB_STATE_ADDRESS;
+ dev->ep_in_toggle = 0;
+ dev->ep_out_toggle = 0;
+ INIT_LIST_HEAD(&dev->device_list);
+ list_add_tail(&dev->device_list, &r8a66597->child_device);
+
+ get_port_number(urb->dev->devpath, &dev->root_port, &dev->hub_port);
+ if (!is_child_device(urb->dev->devpath))
+ r8a66597->root_hub[dev->root_port].dev = dev;
+
+ set_devadd_reg(r8a66597, dev->address,
+ get_r8a66597_usb_speed(urb->dev->speed),
+ get_parent_r8a66597_address(r8a66597, urb->dev),
+ dev->hub_port, dev->root_port);
+
+ return 0;
+}
+
+/* this function must be called with interrupt disabled */
+static u8 alloc_usb_address(struct r8a66597 *r8a66597, struct urb *urb)
+{
+ u8 addr; /* R8A66597's address */
+ struct r8a66597_device *dev;
+
+ if (is_hub_limit(urb->dev->devpath)) {
+ err("Externel hub limit reached.");
+ return 0;
+ }
+
+ dev = get_urb_to_r8a66597_dev(r8a66597, urb);
+ if (dev && dev->state >= USB_STATE_ADDRESS)
+ return dev->address;
+
+ for (addr = 1; addr <= R8A66597_MAX_DEVICE; addr++) {
+ if (r8a66597->address_map & (1 << addr))
+ continue;
+
+ dbg("alloc_address: r8a66597_addr=%d", addr);
+ r8a66597->address_map |= 1 << addr;
+
+ if (make_r8a66597_device(r8a66597, urb, addr) < 0)
+ return 0;
+
+ return addr;
+ }
+
+ err("cannot communicate with a USB device more than 10.(%x)",
+ r8a66597->address_map);
+
+ return 0;
+}
+
+/* this function must be called with interrupt disabled */
+static void free_usb_address(struct r8a66597 *r8a66597,
+ struct r8a66597_device *dev)
+{
+ int port;
+
+ if (!dev)
+ return;
+
+ dbg("free_addr: addr=%d", dev->address);
+
+ dev->state = USB_STATE_DEFAULT;
+ r8a66597->address_map &= ~(1 << dev->address);
+ dev->address = 0;
+ dev_set_drvdata(&dev->udev->dev, NULL);
+ list_del(&dev->device_list);
+ kfree(dev);
+
+ for (port = 0; port < R8A66597_MAX_ROOT_HUB; port++) {
+ if (r8a66597->root_hub[port].dev == dev) {
+ r8a66597->root_hub[port].dev = NULL;
+ break;
+ }
+ }
+}
+
+static void r8a66597_reg_wait(struct r8a66597 *r8a66597, unsigned long reg,
+ u16 mask, u16 loop)
+{
+ u16 tmp;
+ int i = 0;
+
+ do {
+ tmp = r8a66597_read(r8a66597, reg);
+ if (i++ > 1000000) {
+ err("register%lx, loop %x is timeout", reg, loop);
+ break;
+ }
+ ndelay(1);
+ } while ((tmp & mask) != loop);
+}
+
+/* this function must be called with interrupt disabled */
+static void pipe_start(struct r8a66597 *r8a66597, struct r8a66597_pipe *pipe)
+{
+ u16 tmp;
+
+ tmp = r8a66597_read(r8a66597, pipe->pipectr) & PID;
+ if ((pipe->info.pipenum != 0) & ((tmp & PID_STALL) != 0)) /* stall? */
+ r8a66597_mdfy(r8a66597, PID_NAK, PID, pipe->pipectr);
+ r8a66597_mdfy(r8a66597, PID_BUF, PID, pipe->pipectr);
+}
+
+/* this function must be called with interrupt disabled */
+static void pipe_stop(struct r8a66597 *r8a66597, struct r8a66597_pipe *pipe)
+{
+ u16 tmp;
+
+ tmp = r8a66597_read(r8a66597, pipe->pipectr) & PID;
+ if ((tmp & PID_STALL11) != PID_STALL11) /* force stall? */
+ r8a66597_mdfy(r8a66597, PID_STALL, PID, pipe->pipectr);
+ r8a66597_mdfy(r8a66597, PID_NAK, PID, pipe->pipectr);
+ r8a66597_reg_wait(r8a66597, pipe->pipectr, PBUSY, 0);
+}
+
+/* this function must be called with interrupt disabled */
+static void clear_all_buffer(struct r8a66597 *r8a66597,
+ struct r8a66597_pipe *pipe)
+{
+ u16 tmp;
+
+ if (!pipe || pipe->info.pipenum == 0)
+ return;
+
+ pipe_stop(r8a66597, pipe);
+ r8a66597_bset(r8a66597, ACLRM, pipe->pipectr);
+ tmp = r8a66597_read(r8a66597, pipe->pipectr);
+ tmp = r8a66597_read(r8a66597, pipe->pipectr);
+ tmp = r8a66597_read(r8a66597, pipe->pipectr);
+ r8a66597_bclr(r8a66597, ACLRM, pipe->pipectr);
+}
+
+/* this function must be called with interrupt disabled */
+static void r8a66597_pipe_toggle(struct r8a66597 *r8a66597,
+ struct r8a66597_pipe *pipe, int toggle)
+{
+ if (toggle)
+ r8a66597_bset(r8a66597, SQSET, pipe->pipectr);
+ else
+ r8a66597_bset(r8a66597, SQCLR, pipe->pipectr);
+}
+
+/* this function must be called with interrupt disabled */
+static inline void cfifo_change(struct r8a66597 *r8a66597, u16 pipenum)
+{
+ r8a66597_mdfy(r8a66597, MBW | pipenum, MBW | CURPIPE, CFIFOSEL);
+ r8a66597_reg_wait(r8a66597, CFIFOSEL, CURPIPE, pipenum);
+}
+
+/* this function must be called with interrupt disabled */
+static inline void fifo_change_from_pipe(struct r8a66597 *r8a66597,
+ struct r8a66597_pipe *pipe)
+{
+ cfifo_change(r8a66597, 0);
+ r8a66597_mdfy(r8a66597, MBW | 0, MBW | CURPIPE, D0FIFOSEL);
+ r8a66597_mdfy(r8a66597, MBW | 0, MBW | CURPIPE, D1FIFOSEL);
+
+ r8a66597_mdfy(r8a66597, MBW | pipe->info.pipenum, MBW | CURPIPE,
+ pipe->fifosel);
+ r8a66597_reg_wait(r8a66597, pipe->fifosel, CURPIPE, pipe->info.pipenum);
+}
+
+static u16 r8a66597_get_pipenum(struct urb *urb, struct usb_host_endpoint *hep)
+{
+ struct r8a66597_pipe *pipe = hep->hcpriv;
+
+ if (usb_pipeendpoint(urb->pipe) == 0)
+ return 0;
+ else
+ return pipe->info.pipenum;
+}
+
+static u16 get_urb_to_r8a66597_addr(struct r8a66597 *r8a66597, struct urb *urb)
+{
+ struct r8a66597_device *dev = get_urb_to_r8a66597_dev(r8a66597, urb);
+
+ return (usb_pipedevice(urb->pipe) == 0) ? 0 : dev->address;
+}
+
+static unsigned short *get_toggle_pointer(struct r8a66597_device *dev,
+ int urb_pipe)
+{
+ if (!dev)
+ return NULL;
+
+ return usb_pipein(urb_pipe) ? &dev->ep_in_toggle : &dev->ep_out_toggle;
+}
+
+/* this function must be called with interrupt disabled */
+static void pipe_toggle_set(struct r8a66597 *r8a66597,
+ struct r8a66597_pipe *pipe,
+ struct urb *urb, int set)
+{
+ struct r8a66597_device *dev = get_urb_to_r8a66597_dev(r8a66597, urb);
+ unsigned char endpoint = usb_pipeendpoint(urb->pipe);
+ unsigned short *toggle = get_toggle_pointer(dev, urb->pipe);
+
+ if (!toggle)
+ return;
+
+ if (set)
+ *toggle |= 1 << endpoint;
+ else
+ *toggle &= ~(1 << endpoint);
+}
+
+/* this function must be called with interrupt disabled */
+static void pipe_toggle_save(struct r8a66597 *r8a66597,
+ struct r8a66597_pipe *pipe,
+ struct urb *urb)
+{
+ if (r8a66597_read(r8a66597, pipe->pipectr) & SQMON)
+ pipe_toggle_set(r8a66597, pipe, urb, 1);
+ else
+ pipe_toggle_set(r8a66597, pipe, urb, 0);
+}
+
+/* this function must be called with interrupt disabled */
+static void pipe_toggle_restore(struct r8a66597 *r8a66597,
+ struct r8a66597_pipe *pipe,
+ struct urb *urb)
+{
+ struct r8a66597_device *dev = get_urb_to_r8a66597_dev(r8a66597, urb);
+ unsigned char endpoint = usb_pipeendpoint(urb->pipe);
+ unsigned short *toggle = get_toggle_pointer(dev, urb->pipe);
+
+ if (!toggle)
+ return;
+
+ r8a66597_pipe_toggle(r8a66597, pipe, *toggle & (1 << endpoint));
+}
+
+/* this function must be called with interrupt disabled */
+static void pipe_buffer_setting(struct r8a66597 *r8a66597,
+ struct r8a66597_pipe_info *info)
+{
+ u16 val = 0;
+
+ if (info->pipenum == 0)
+ return;
+
+ r8a66597_bset(r8a66597, ACLRM, get_pipectr_addr(info->pipenum));
+ r8a66597_bclr(r8a66597, ACLRM, get_pipectr_addr(info->pipenum));
+ r8a66597_write(r8a66597, info->pipenum, PIPESEL);
+ if (!info->dir_in)
+ val |= R8A66597_DIR;
+ if (info->type == R8A66597_BULK && info->dir_in)
+ val |= R8A66597_DBLB | R8A66597_SHTNAK;
+ val |= info->type | info->epnum;
+ r8a66597_write(r8a66597, val, PIPECFG);
+
+ r8a66597_write(r8a66597, (info->buf_bsize << 10) | (info->bufnum),
+ PIPEBUF);
+ r8a66597_write(r8a66597, make_devsel(info->address) | info->maxpacket,
+ PIPEMAXP);
+ if (info->interval)
+ info->interval--;
+ r8a66597_write(r8a66597, info->interval, PIPEPERI);
+}
+
+
+
+/* this function must be called with interrupt disabled */
+static void pipe_setting(struct r8a66597 *r8a66597, struct r8a66597_td *td)
+{
+ struct r8a66597_pipe_info *info;
+ struct urb *urb = td->urb;
+
+ if (td->pipenum > 0) {
+ info = &td->pipe->info;
+ cfifo_change(r8a66597, 0);
+ pipe_buffer_setting(r8a66597, info);
+
+ if (!usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe),
+ usb_pipeout(urb->pipe)) &&
+ !usb_pipecontrol(urb->pipe)) {
+ r8a66597_pipe_toggle(r8a66597, td->pipe, 0);
+ pipe_toggle_set(r8a66597, td->pipe, urb, 0);
+ clear_all_buffer(r8a66597, td->pipe);
+ usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe),
+ usb_pipeout(urb->pipe), 1);
+ }
+ pipe_toggle_restore(r8a66597, td->pipe, urb);
+ }
+}
+
+/* this function must be called with interrupt disabled */
+static u16 get_empty_pipenum(struct r8a66597 *r8a66597,
+ struct usb_endpoint_descriptor *ep)
+{
+ u16 array[R8A66597_MAX_NUM_PIPE], i = 0, min;
+
+ memset(array, 0, sizeof(array));
+ switch(ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {
+ case USB_ENDPOINT_XFER_BULK:
+ if (ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
+ array[i++] = 4;
+ else {
+ array[i++] = 3;
+ array[i++] = 5;
+ }
+ break;
+ case USB_ENDPOINT_XFER_INT:
+ if (ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) {
+ array[i++] = 6;
+ array[i++] = 7;
+ array[i++] = 8;
+ } else
+ array[i++] = 9;
+ break;
+ case USB_ENDPOINT_XFER_ISOC:
+ if (ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
+ array[i++] = 2;
+ else
+ array[i++] = 1;
+ break;
+ default:
+ err("Illegal type");
+ return 0;
+ }
+
+ i = 1;
+ min = array[0];
+ while (array[i] != 0) {
+ if (r8a66597->pipe_cnt[min] > r8a66597->pipe_cnt[array[i]])
+ min = array[i];
+ i++;
+ }
+
+ return min;
+}
+
+static u16 get_r8a66597_type(__u8 type)
+{
+ u16 r8a66597_type;
+
+ switch(type) {
+ case USB_ENDPOINT_XFER_BULK:
+ r8a66597_type = R8A66597_BULK;
+ break;
+ case USB_ENDPOINT_XFER_INT:
+ r8a66597_type = R8A66597_INT;
+ break;
+ case USB_ENDPOINT_XFER_ISOC:
+ r8a66597_type = R8A66597_ISO;
+ break;
+ default:
+ err("Illegal type");
+ r8a66597_type = 0x0000;
+ break;
+ }
+
+ return r8a66597_type;
+}
+
+static u16 get_bufnum(u16 pipenum)
+{
+ u16 bufnum = 0;
+
+ if (pipenum == 0)
+ bufnum = 0;
+ else if (check_bulk_or_isoc(pipenum))
+ bufnum = 8 + (pipenum - 1) * R8A66597_BUF_BSIZE*2;
+ else if (check_interrupt(pipenum))
+ bufnum = 4 + (pipenum - 6);
+ else
+ err("Illegal pipenum (%d)", pipenum);
+
+ return bufnum;
+}
+
+static u16 get_buf_bsize(u16 pipenum)
+{
+ u16 buf_bsize = 0;
+
+ if (pipenum == 0)
+ buf_bsize = 3;
+ else if (check_bulk_or_isoc(pipenum))
+ buf_bsize = R8A66597_BUF_BSIZE - 1;
+ else if (check_interrupt(pipenum))
+ buf_bsize = 0;
+ else
+ err("Illegal pipenum (%d)", pipenum);
+
+ return buf_bsize;
+}
+
+/* this function must be called with interrupt disabled */
+static void enable_r8a66597_pipe_dma(struct r8a66597 *r8a66597,
+ struct r8a66597_device *dev,
+ struct r8a66597_pipe *pipe,
+ struct urb *urb)
+{
+ int i;
+ struct r8a66597_pipe_info *info = &pipe->info;
+
+ if ((pipe->info.pipenum != 0) && (info->type != R8A66597_INT)) {
+ for (i = 0; i < R8A66597_MAX_DMA_CHANNEL; i++) {
+ if ((r8a66597->dma_map & (1 << i)) != 0)
+ continue;
+
+ info("address %d, EndpointAddress 0x%02x use DMA FIFO",
+ usb_pipedevice(urb->pipe),
+ info->dir_in ? USB_ENDPOINT_DIR_MASK + info->epnum
+ : info->epnum);
+
+ r8a66597->dma_map |= 1 << i;
+ dev->dma_map |= 1 << i;
+ set_pipe_reg_addr(pipe, i);
+
+ cfifo_change(r8a66597, 0);
+ r8a66597_mdfy(r8a66597, MBW | pipe->info.pipenum,
+ MBW | CURPIPE, pipe->fifosel);
+
+ r8a66597_reg_wait(r8a66597, pipe->fifosel, CURPIPE,
+ pipe->info.pipenum);
+ r8a66597_bset(r8a66597, BCLR, pipe->fifoctr);
+ break;
+ }
+ }
+}
+
+/* this function must be called with interrupt disabled */
+static void enable_r8a66597_pipe(struct r8a66597 *r8a66597, struct urb *urb,
+ struct usb_host_endpoint *hep,
+ struct r8a66597_pipe_info *info)
+{
+ struct r8a66597_device *dev = get_urb_to_r8a66597_dev(r8a66597, urb);
+ struct r8a66597_pipe *pipe = hep->hcpriv;
+
+ dbg("enable_pipe:");
+
+ pipe->info = *info;
+ set_pipe_reg_addr(pipe, R8A66597_PIPE_NO_DMA);
+ r8a66597->pipe_cnt[pipe->info.pipenum]++;
+ dev->pipe_cnt[pipe->info.pipenum]++;
+
+ enable_r8a66597_pipe_dma(r8a66597, dev, pipe, urb);
+}
+
+/* this function must be called with interrupt disabled */
+static void force_dequeue(struct r8a66597 *r8a66597, u16 pipenum, u16 address)
+{
+ struct r8a66597_td *td, *next;
+ struct urb *urb;
+ struct list_head *list = &r8a66597->pipe_queue[pipenum];
+
+ if (list_empty(list))
+ return;
+
+ list_for_each_entry_safe(td, next, list, queue) {
+ if (!td)
+ continue;
+ if (td->address != address)
+ continue;
+
+ urb = td->urb;
+ list_del(&td->queue);
+ kfree(td);
+
+ if (urb) {
+ urb->status = -ENODEV;
+ urb->hcpriv = NULL;
+ spin_unlock(&r8a66597->lock);
+ usb_hcd_giveback_urb(r8a66597_to_hcd(r8a66597), urb);
+ spin_lock(&r8a66597->lock);
+ }
+ break;
+ }
+}
+
+/* this function must be called with interrupt disabled */
+static void disable_r8a66597_pipe_all(struct r8a66597 *r8a66597,
+ struct r8a66597_device *dev)
+{
+ int check_ep0 = 0;
+ u16 pipenum;
+
+ if (!dev)
+ return;
+
+ for (pipenum = 1; pipenum < R8A66597_MAX_NUM_PIPE; pipenum++) {
+ if (!dev->pipe_cnt[pipenum])
+ continue;
+
+ if (!check_ep0) {
+ check_ep0 = 1;
+ force_dequeue(r8a66597, 0, dev->address);
+ }
+
+ r8a66597->pipe_cnt[pipenum] -= dev->pipe_cnt[pipenum];
+ dev->pipe_cnt[pipenum] = 0;
+ force_dequeue(r8a66597, pipenum, dev->address);
+ }
+
+ dbg("disable_pipe");
+
+ r8a66597->dma_map &= ~(dev->dma_map);
+ dev->dma_map = 0;
+}
+
+/* this function must be called with interrupt disabled */
+static void init_pipe_info(struct r8a66597 *r8a66597, struct urb *urb,
+ struct usb_host_endpoint *hep,
+ struct usb_endpoint_descriptor *ep)
+{
+ struct r8a66597_pipe_info info;
+
+ info.pipenum = get_empty_pipenum(r8a66597, ep);
+ info.address = get_urb_to_r8a66597_addr(r8a66597, urb);
+ info.epnum = ep->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
+ info.maxpacket = ep->wMaxPacketSize;
+ info.type = get_r8a66597_type(ep->bmAttributes
+ & USB_ENDPOINT_XFERTYPE_MASK);
+ info.bufnum = get_bufnum(info.pipenum);
+ info.buf_bsize = get_buf_bsize(info.pipenum);
+ info.interval = ep->bInterval;
+ if (ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
+ info.dir_in = 1;
+ else
+ info.dir_in = 0;
+
+ enable_r8a66597_pipe(r8a66597, urb, hep, &info);
+}
+
+static void init_pipe_config(struct r8a66597 *r8a66597, struct urb *urb)
+{
+ struct r8a66597_device *dev;
+
+ dev = get_urb_to_r8a66597_dev(r8a66597, urb);
+ dev->state = USB_STATE_CONFIGURED;
+}
+
+static void pipe_irq_enable(struct r8a66597 *r8a66597, struct urb *urb,
+ u16 pipenum)
+{
+ if (pipenum == 0 && usb_pipeout(urb->pipe))
+ enable_irq_empty(r8a66597, pipenum);
+ else
+ enable_irq_ready(r8a66597, pipenum);
+
+ if (!usb_pipeisoc(urb->pipe))
+ enable_irq_nrdy(r8a66597, pipenum);
+}
+
+static void pipe_irq_disable(struct r8a66597 *r8a66597, u16 pipenum)
+{
+ disable_irq_ready(r8a66597, pipenum);
+ disable_irq_nrdy(r8a66597, pipenum);
+}
+
+/* this function must be called with interrupt disabled */
+static void r8a66597_usb_preconnect(struct r8a66597 *r8a66597, int port)
+{
+ r8a66597->root_hub[port].port |= (1 << USB_PORT_FEAT_CONNECTION)
+ | (1 << USB_PORT_FEAT_C_CONNECTION);
+ r8a66597_write(r8a66597, (u16)~DTCH, get_intsts_reg(port));
+ r8a66597_bset(r8a66597, DTCHE, get_intenb_reg(port));
+}
+
+/* this function must be called with interrupt disabled */
+static void r8a66597_usb_connect(struct r8a66597 *r8a66597, int port)
+{
+ u16 speed = get_rh_usb_speed(r8a66597, port);
+ struct r8a66597_root_hub *rh = &r8a66597->root_hub[port];
+
+ if (speed == HSMODE)
+ rh->port |= (1 << USB_PORT_FEAT_HIGHSPEED);
+ else if (speed == LSMODE)
+ rh->port |= (1 << USB_PORT_FEAT_LOWSPEED);
+
+ rh->port &= ~(1 << USB_PORT_FEAT_RESET);
+ rh->port |= 1 << USB_PORT_FEAT_ENABLE;
+}
+
+/* this function must be called with interrupt disabled */
+static void r8a66597_usb_disconnect(struct r8a66597 *r8a66597, int port)
+{
+ struct r8a66597_device *dev = r8a66597->root_hub[port].dev;
+
+ r8a66597->root_hub[port].port &= ~(1 << USB_PORT_FEAT_CONNECTION);
+ r8a66597->root_hub[port].port |= (1 << USB_PORT_FEAT_C_CONNECTION);
+
+ disable_r8a66597_pipe_all(r8a66597, dev);
+ free_usb_address(r8a66597, dev);
+
+ r8a66597_bset(r8a66597, ATTCHE, get_intenb_reg(port));
+}
+
+/* this function must be called with interrupt disabled */
+static void prepare_setup_packet(struct r8a66597 *r8a66597,
+ struct r8a66597_td *td)
+{
+ int i;
+ u16 *p = (u16 *)td->urb->setup_packet;
+ unsigned long setup_addr = USBREQ;
+
+ r8a66597_write(r8a66597, make_devsel(td->address) | td->maxpacket,
+ DCPMAXP);
+ r8a66597_write(r8a66597, (u16)~(SIGN | SACK), INTSTS1);
+
+ for (i = 0; i < 4; i++) {
+ r8a66597_write(r8a66597, p[i], setup_addr);
+ setup_addr += 2;
+ }
+ r8a66597_write(r8a66597, SUREQ, DCPCTR);
+}
+
+/* this function must be called with interrupt disabled */
+static void prepare_packet_read(struct r8a66597 *r8a66597,
+ struct r8a66597_td *td)
+{
+ struct urb *urb = td->urb;
+
+ if (usb_pipecontrol(urb->pipe)) {
+ r8a66597_bclr(r8a66597, R8A66597_DIR, DCPCFG);
+ r8a66597_mdfy(r8a66597, 0, ISEL | CURPIPE, CFIFOSEL);
+ r8a66597_reg_wait(r8a66597, CFIFOSEL, CURPIPE, 0);
+ if (urb->actual_length == 0) {
+ r8a66597_pipe_toggle(r8a66597, td->pipe, 1);
+ r8a66597_write(r8a66597, BCLR, CFIFOCTR);
+ }
+ pipe_irq_disable(r8a66597, td->pipenum);
+ pipe_start(r8a66597, td->pipe);
+ pipe_irq_enable(r8a66597, urb, td->pipenum);
+ } else {
+ if (urb->actual_length == 0) {
+ pipe_irq_disable(r8a66597, td->pipenum);
+ pipe_setting(r8a66597, td);
+ pipe_stop(r8a66597, td->pipe);
+ r8a66597_write(r8a66597, (u16)~(1 << td->pipenum),
+ BRDYSTS);
+
+ if (td->pipe->pipetre) {
+ r8a66597_write(r8a66597, TRCLR,
+ td->pipe->pipetre);
+ r8a66597_write(r8a66597,
+ (urb->transfer_buffer_length
+ + td->maxpacket - 1)
+ / td->maxpacket,
+ td->pipe->pipetrn);
+ r8a66597_bset(r8a66597, TRENB,
+ td->pipe->pipetre);
+ }
+
+ pipe_start(r8a66597, td->pipe);
+ pipe_irq_enable(r8a66597, urb, td->pipenum);
+ }
+ }
+}
+
+/* this function must be called with interrupt disabled */
+static void prepare_packet_write(struct r8a66597 *r8a66597,
+ struct r8a66597_td *td)
+{
+ u16 tmp;
+ struct urb *urb = td->urb;
+
+ if (usb_pipecontrol(urb->pipe)) {
+ pipe_stop(r8a66597, td->pipe);
+ r8a66597_bset(r8a66597, R8A66597_DIR, DCPCFG);
+ r8a66597_mdfy(r8a66597, ISEL, ISEL | CURPIPE, CFIFOSEL);
+ r8a66597_reg_wait(r8a66597, CFIFOSEL, CURPIPE, 0);
+ if (urb->actual_length == 0) {
+ r8a66597_pipe_toggle(r8a66597, td->pipe, 1);
+ r8a66597_write(r8a66597, BCLR, CFIFOCTR);
+ }
+ } else {
+ if (urb->actual_length == 0)
+ pipe_setting(r8a66597, td);
+ if (td->pipe->pipetre)
+ r8a66597_bclr(r8a66597, TRENB, td->pipe->pipetre);
+ }
+ r8a66597_write(r8a66597, (u16)~(1 << td->pipenum), BRDYSTS);
+
+ fifo_change_from_pipe(r8a66597, td->pipe);
+ tmp = r8a66597_read(r8a66597, td->pipe->fifoctr);
+ if (unlikely((tmp & FRDY) == 0))
+ pipe_irq_enable(r8a66597, urb, td->pipenum);
+ else
+ packet_write(r8a66597, td->pipenum);
+ pipe_start(r8a66597, td->pipe);
+}
+
+/* this function must be called with interrupt disabled */
+static void prepare_status_packet(struct r8a66597 *r8a66597,
+ struct r8a66597_td *td)
+{
+ struct urb *urb = td->urb;
+
+ r8a66597_pipe_toggle(r8a66597, td->pipe, 1);
+
+ if (urb->setup_packet[0] & USB_ENDPOINT_DIR_MASK) {
+ r8a66597_bset(r8a66597, R8A66597_DIR, DCPCFG);
+ r8a66597_mdfy(r8a66597, ISEL, ISEL | CURPIPE, CFIFOSEL);
+ r8a66597_reg_wait(r8a66597, CFIFOSEL, CURPIPE, 0);
+ r8a66597_write(r8a66597, BVAL | BCLR, CFIFOCTR);
+ r8a66597_write(r8a66597, (u16)~BEMP0, BEMPSTS);
+ enable_irq_empty(r8a66597, 0);
+ } else {
+ r8a66597_bclr(r8a66597, R8A66597_DIR, DCPCFG);
+ r8a66597_mdfy(r8a66597, 0, ISEL | CURPIPE, CFIFOSEL);
+ r8a66597_reg_wait(r8a66597, CFIFOSEL, CURPIPE, 0);
+ r8a66597_write(r8a66597, BCLR, CFIFOCTR);
+ r8a66597_write(r8a66597, (u16)~BRDY0, BRDYSTS);
+ r8a66597_write(r8a66597, (u16)~BEMP0, BEMPSTS);
+ enable_irq_ready(r8a66597, 0);
+ }
+ enable_irq_nrdy(r8a66597, 0);
+ pipe_start(r8a66597, td->pipe);
+}
+
+/* this function must be called with interrupt disabled */
+static int start_transfer(struct r8a66597 *r8a66597, struct r8a66597_td *td)
+{
+ BUG_ON(!td);
+
+ switch (td->type) {
+ case USB_PID_SETUP:
+ if (td->urb->setup_packet[1] == USB_REQ_SET_ADDRESS) {
+ td->set_address = 1;
+ td->urb->setup_packet[2] = alloc_usb_address(r8a66597,
+ td->urb);
+ if (td->urb->setup_packet[2] == 0)
+ return -EPIPE;
+ }
+ prepare_setup_packet(r8a66597, td);
+ break;
+ case USB_PID_IN:
+ prepare_packet_read(r8a66597, td);
+ break;
+ case USB_PID_OUT:
+ prepare_packet_write(r8a66597, td);
+ break;
+ case USB_PID_ACK:
+ prepare_status_packet(r8a66597, td);
+ break;
+ default:
+ err("invalid type.");
+ break;
+ }
+
+ return 0;
+}
+
+static int check_transfer_finish(struct r8a66597_td *td, struct urb *urb)
+{
+ if (usb_pipeisoc(urb->pipe)) {
+ if (urb->number_of_packets == td->iso_cnt)
+ return 1;
+ }
+
+ /* control or bulk or interrupt */
+ if ((urb->transfer_buffer_length <= urb->actual_length) ||
+ (td->short_packet) || (td->zero_packet))
+ return 1;
+
+ return 0;
+}
+
+/* this function must be called with interrupt disabled */
+static void set_td_timer(struct r8a66597 *r8a66597, struct r8a66597_td *td)
+{
+ unsigned long time;
+
+ BUG_ON(!td);
+
+ if (!list_empty(&r8a66597->pipe_queue[td->pipenum]) &&
+ !usb_pipecontrol(td->urb->pipe) && usb_pipein(td->urb->pipe)) {
+ r8a66597->timeout_map |= 1 << td->pipenum;
+ switch (usb_pipetype(td->urb->pipe)) {
+ case PIPE_INTERRUPT:
+ case PIPE_ISOCHRONOUS:
+ time = 30;
+ break;
+ default:
+ time = 300;
+ break;
+ }
+
+ mod_timer(&r8a66597->td_timer[td->pipenum],
+ jiffies + msecs_to_jiffies(time));
+ }
+}
+
+/* this function must be called with interrupt disabled */
+static void done(struct r8a66597 *r8a66597, struct r8a66597_td *td,
+ u16 pipenum, struct urb *urb)
+{
+ int restart = 0;
+ struct usb_hcd *hcd = r8a66597_to_hcd(r8a66597);
+
+ r8a66597->timeout_map &= ~(1 << pipenum);
+
+ if (likely(td)) {
+ if (td->set_address && urb->status != 0)
+ r8a66597->address_map &= ~(1 << urb->setup_packet[2]);
+
+ pipe_toggle_save(r8a66597, td->pipe, urb);
+ list_del(&td->queue);
+ kfree(td);
+ }
+
+ if (!list_empty(&r8a66597->pipe_queue[pipenum]))
+ restart = 1;
+
+ if (likely(urb)) {
+ if (usb_pipeisoc(urb->pipe))
+ urb->start_frame = r8a66597_get_frame(hcd);
+
+ urb->hcpriv = NULL;
+ spin_unlock(&r8a66597->lock);
+ usb_hcd_giveback_urb(hcd, urb);
+ spin_lock(&r8a66597->lock);
+ }
+
+ if (restart) {
+ td = r8a66597_get_td(r8a66597, pipenum);
+ if (unlikely(!td))
+ return;
+
+ start_transfer(r8a66597, td);
+ set_td_timer(r8a66597, td);
+ }
+}
+
+/* this function must be called with interrupt disabled */
+static void finish_request(struct r8a66597 *r8a66597, struct r8a66597_td *td,
+ u16 pipenum, struct urb *urb)
+__releases(r8a66597->lock) __acquires(r8a66597->lock)
+{
+ done(r8a66597, td, pipenum, urb);
+}
+
+static void packet_read(struct r8a66597 *r8a66597, u16 pipenum)
+{
+ u16 tmp;
+ int rcv_len, bufsize, urb_len, size;
+ u16 *buf;
+ struct r8a66597_td *td = r8a66597_get_td(r8a66597, pipenum);
+ struct urb *urb;
+ int finish = 0;
+
+ if (unlikely(!td))
+ return;
+ urb = td->urb;
+
+ fifo_change_from_pipe(r8a66597, td->pipe);
+ tmp = r8a66597_read(r8a66597, td->pipe->fifoctr);
+ if (unlikely((tmp & FRDY) == 0)) {
+ urb->status = -EPIPE;
+ pipe_stop(r8a66597, td->pipe);
+ pipe_irq_disable(r8a66597, pipenum);
+ err("in fifo not ready (%d)", pipenum);
+ finish_request(r8a66597, td, pipenum, td->urb);
+ return;
+ }
+
+ /* prepare parameters */
+ rcv_len = tmp & DTLN;
+ bufsize = td->maxpacket;
+ if (usb_pipeisoc(urb->pipe)) {
+ buf = (u16 *)(urb->transfer_buffer +
+ urb->iso_frame_desc[td->iso_cnt].offset);
+ urb_len = urb->iso_frame_desc[td->iso_cnt].length;
+ } else {
+ buf = (void *)urb->transfer_buffer + urb->actual_length;
+ urb_len = urb->transfer_buffer_length - urb->actual_length;
+ }
+ if (rcv_len < bufsize)
+ size = min(rcv_len, urb_len);
+ else
+ size = min(bufsize, urb_len);
+
+ /* update parameters */
+ urb->actual_length += size;
+ if (rcv_len == 0)
+ td->zero_packet = 1;
+ if ((size % td->maxpacket) > 0) {
+ td->short_packet = 1;
+ if (urb->transfer_buffer_length != urb->actual_length &&
+ urb->transfer_flags & URB_SHORT_NOT_OK)
+ td->urb->status = -EREMOTEIO;
+ }
+ if (usb_pipeisoc(urb->pipe)) {
+ urb->iso_frame_desc[td->iso_cnt].actual_length = size;
+ urb->iso_frame_desc[td->iso_cnt].status = 0;
+ td->iso_cnt++;
+ }
+
+ /* check transfer finish */
+ if (check_transfer_finish(td, urb)) {
+ pipe_stop(r8a66597, td->pipe);
+ pipe_irq_disable(r8a66597, pipenum);
+ finish = 1;
+ }
+
+ /* read fifo */
+ if (urb->transfer_buffer) {
+ if (size == 0)
+ r8a66597_write(r8a66597, BCLR, td->pipe->fifoctr);
+ else
+ r8a66597_read_fifo(r8a66597, td->pipe->fifoaddr,
+ buf, size);
+ }
+
+ if (finish && pipenum != 0) {
+ if (td->urb->status == -EINPROGRESS)
+ td->urb->status = 0;
+ finish_request(r8a66597, td, pipenum, urb);
+ }
+}
+
+static void packet_write(struct r8a66597 *r8a66597, u16 pipenum)
+{
+ u16 tmp;
+ int bufsize, size;
+ u16 *buf;
+ struct r8a66597_td *td = r8a66597_get_td(r8a66597, pipenum);
+ struct urb *urb;
+
+ if (unlikely(!td))
+ return;
+ urb = td->urb;
+
+ fifo_change_from_pipe(r8a66597, td->pipe);
+ tmp = r8a66597_read(r8a66597, td->pipe->fifoctr);
+ if (unlikely((tmp & FRDY) == 0)) {
+ urb->status = -EPIPE;
+ pipe_stop(r8a66597, td->pipe);
+ pipe_irq_disable(r8a66597, pipenum);
+ err("out write fifo not ready. (%d)", pipenum);
+ finish_request(r8a66597, td, pipenum, td->urb);
+ return;
+ }
+
+ /* prepare parameters */
+ bufsize = td->maxpacket;
+ if (usb_pipeisoc(urb->pipe)) {
+ buf = (u16 *)(urb->transfer_buffer +
+ urb->iso_frame_desc[td->iso_cnt].offset);
+ size = min(bufsize,
+ (int)urb->iso_frame_desc[td->iso_cnt].length);
+ } else {
+ buf = (u16 *)(urb->transfer_buffer + urb->actual_length);
+ size = min((int)bufsize,
+ urb->transfer_buffer_length - urb->actual_length);
+ }
+
+ /* write fifo */
+ if (pipenum > 0)
+ r8a66597_write(r8a66597, (u16)~(1 << pipenum), BEMPSTS);
+ if (urb->transfer_buffer) {
+ r8a66597_write_fifo(r8a66597, td->pipe->fifoaddr, buf, size);
+ if (!usb_pipebulk(urb->pipe) || td->maxpacket != size)
+ r8a66597_write(r8a66597, BVAL, td->pipe->fifoctr);
+ }
+
+ /* update parameters */
+ urb->actual_length += size;
+ if (usb_pipeisoc(urb->pipe)) {
+ urb->iso_frame_desc[td->iso_cnt].actual_length = size;
+ urb->iso_frame_desc[td->iso_cnt].status = 0;
+ td->iso_cnt++;
+ }
+
+ /* check transfer finish */
+ if (check_transfer_finish(td, urb)) {
+ disable_irq_ready(r8a66597, pipenum);
+ enable_irq_empty(r8a66597, pipenum);
+ if (!usb_pipeisoc(urb->pipe))
+ enable_irq_nrdy(r8a66597, pipenum);
+ } else
+ pipe_irq_enable(r8a66597, urb, pipenum);
+}
+
+
+static void check_next_phase(struct r8a66597 *r8a66597)
+{
+ struct r8a66597_td *td = r8a66597_get_td(r8a66597, 0);
+ struct urb *urb;
+ u8 finish = 0;
+
+ if (unlikely(!td))
+ return;
+ urb = td->urb;
+
+ switch (td->type) {
+ case USB_PID_IN:
+ case USB_PID_OUT:
+ if (urb->status != -EINPROGRESS) {
+ finish = 1;
+ break;
+ }
+ if (check_transfer_finish(td, urb))
+ td->type = USB_PID_ACK;
+ break;
+ case USB_PID_SETUP:
+ if (urb->status != -EINPROGRESS)
+ finish = 1;
+ else if (urb->transfer_buffer_length == urb->actual_length) {
+ td->type = USB_PID_ACK;
+ urb->status = 0;
+ } else if (usb_pipeout(urb->pipe))
+ td->type = USB_PID_OUT;
+ else
+ td->type = USB_PID_IN;
+ break;
+ case USB_PID_ACK:
+ finish = 1;
+ if (urb->status == -EINPROGRESS)
+ urb->status = 0;
+ break;
+ }
+
+ if (finish)
+ finish_request(r8a66597, td, 0, urb);
+ else
+ start_transfer(r8a66597, td);
+}
+
+static void set_urb_error(struct r8a66597 *r8a66597, u16 pipenum)
+{
+ struct r8a66597_td *td = r8a66597_get_td(r8a66597, pipenum);
+
+ if (td && td->urb) {
+ u16 pid = r8a66597_read(r8a66597, td->pipe->pipectr) & PID;
+
+ if (pid == PID_NAK)
+ td->urb->status = -ECONNRESET;
+ else
+ td->urb->status = -EPIPE;
+ }
+}
+
+static void irq_pipe_ready(struct r8a66597 *r8a66597)
+{
+ u16 check;
+ u16 pipenum;
+ u16 mask;
+ struct r8a66597_td *td;
+
+ mask = r8a66597_read(r8a66597, BRDYSTS)
+ & r8a66597_read(r8a66597, BRDYENB);
+ r8a66597_write(r8a66597, (u16)~mask, BRDYSTS);
+ if (mask & BRDY0) {
+ td = r8a66597_get_td(r8a66597, 0);
+ if (td && td->type == USB_PID_IN)
+ packet_read(r8a66597, 0);
+ else
+ pipe_irq_disable(r8a66597, 0);
+ check_next_phase(r8a66597);
+ }
+
+ for (pipenum = 1; pipenum < R8A66597_MAX_NUM_PIPE; pipenum++) {
+ check = 1 << pipenum;
+ if (mask & check) {
+ td = r8a66597_get_td(r8a66597, pipenum);
+ if (unlikely(!td))
+ continue;
+
+ if (td->type == USB_PID_IN)
+ packet_read(r8a66597, pipenum);
+ else if (td->type == USB_PID_OUT)
+ packet_write(r8a66597, pipenum);
+ }
+ }
+}
+
+static void irq_pipe_empty(struct r8a66597 *r8a66597)
+{
+ u16 tmp;
+ u16 check;
+ u16 pipenum;
+ u16 mask;
+ struct r8a66597_td *td;
+
+ mask = r8a66597_read(r8a66597, BEMPSTS)
+ & r8a66597_read(r8a66597, BEMPENB);
+ r8a66597_write(r8a66597, (u16)~mask, BEMPSTS);
+ if (mask & BEMP0) {
+ cfifo_change(r8a66597, 0);
+ td = r8a66597_get_td(r8a66597, 0);
+ if (td && td->type != USB_PID_OUT)
+ disable_irq_empty(r8a66597, 0);
+ check_next_phase(r8a66597);
+ }
+
+ for (pipenum = 1; pipenum < R8A66597_MAX_NUM_PIPE; pipenum++) {
+ check = 1 << pipenum;
+ if (mask & check) {
+ struct r8a66597_td *td;
+ td = r8a66597_get_td(r8a66597, pipenum);
+ if (unlikely(!td))
+ continue;
+
+ tmp = r8a66597_read(r8a66597, td->pipe->pipectr);
+ if ((tmp & INBUFM) == 0) {
+ disable_irq_empty(r8a66597, pipenum);
+ pipe_irq_disable(r8a66597, pipenum);
+ if (td->urb->status == -EINPROGRESS)
+ td->urb->status = 0;
+ finish_request(r8a66597, td, pipenum, td->urb);
+ }
+ }
+ }
+}
+
+static void irq_pipe_nrdy(struct r8a66597 *r8a66597)
+{
+ u16 check;
+ u16 pipenum;
+ u16 mask;
+
+ mask = r8a66597_read(r8a66597, NRDYSTS)
+ & r8a66597_read(r8a66597, NRDYENB);
+ r8a66597_write(r8a66597, (u16)~mask, NRDYSTS);
+ if (mask & NRDY0) {
+ cfifo_change(r8a66597, 0);
+ set_urb_error(r8a66597, 0);
+ pipe_irq_disable(r8a66597, 0);
+ check_next_phase(r8a66597);
+ }
+
+ for (pipenum = 1; pipenum < R8A66597_MAX_NUM_PIPE; pipenum++) {
+ check = 1 << pipenum;
+ if (mask & check) {
+ struct r8a66597_td *td;
+ td = r8a66597_get_td(r8a66597, pipenum);
+ if (unlikely(!td))
+ continue;
+
+ set_urb_error(r8a66597, pipenum);
+ pipe_irq_disable(r8a66597, pipenum);
+ pipe_stop(r8a66597, td->pipe);
+ finish_request(r8a66597, td, pipenum, td->urb);
+ }
+ }
+}
+
+static void start_root_hub_sampling(struct r8a66597 *r8a66597, int port)
+{
+ struct r8a66597_root_hub *rh = &r8a66597->root_hub[port];
+
+ rh->old_syssts = r8a66597_read(r8a66597, get_syssts_reg(port)) & LNST;
+ rh->scount = R8A66597_MAX_SAMPLING;
+ mod_timer(&r8a66597->rh_timer, jiffies + msecs_to_jiffies(50));
+}
+
+static irqreturn_t r8a66597_irq(struct usb_hcd *hcd)
+{
+ struct r8a66597 *r8a66597 = hcd_to_r8a66597(hcd);
+ u16 intsts0, intsts1, intsts2;
+ u16 intenb0, intenb1, intenb2;
+ u16 mask0, mask1, mask2;
+
+ spin_lock(&r8a66597->lock);
+
+ intsts0 = r8a66597_read(r8a66597, INTSTS0);
+ intsts1 = r8a66597_read(r8a66597, INTSTS1);
+ intsts2 = r8a66597_read(r8a66597, INTSTS2);
+ intenb0 = r8a66597_read(r8a66597, INTENB0);
+ intenb1 = r8a66597_read(r8a66597, INTENB1);
+ intenb2 = r8a66597_read(r8a66597, INTENB2);
+
+ mask2 = intsts2 & intenb2;
+ mask1 = intsts1 & intenb1;
+ mask0 = intsts0 & intenb0 & (BEMP | NRDY | BRDY);
+ if (mask2) {
+ if (mask2 & ATTCH) {
+ r8a66597_write(r8a66597, (u16)~ATTCH, INTSTS2);
+ r8a66597_bclr(r8a66597, ATTCHE, INTENB2);
+
+ /* start usb bus sampling */
+ start_root_hub_sampling(r8a66597, 1);
+ }
+ if (mask2 & DTCH) {
+ r8a66597_write(r8a66597, (u16)~DTCH, INTSTS2);
+ r8a66597_bclr(r8a66597, DTCHE, INTENB2);
+ r8a66597_usb_disconnect(r8a66597, 1);
+ }
+ }
+
+ if (mask1) {
+ if (mask1 & ATTCH) {
+ r8a66597_write(r8a66597, (u16)~ATTCH, INTSTS1);
+ r8a66597_bclr(r8a66597, ATTCHE, INTENB1);
+
+ /* start usb bus sampling */
+ start_root_hub_sampling(r8a66597, 0);
+ }
+ if (mask1 & DTCH) {
+ r8a66597_write(r8a66597, (u16)~DTCH, INTSTS1);
+ r8a66597_bclr(r8a66597, DTCHE, INTENB1);
+ r8a66597_usb_disconnect(r8a66597, 0);
+ }
+ if (mask1 & SIGN) {
+ r8a66597_write(r8a66597, (u16)~SIGN, INTSTS1);
+ set_urb_error(r8a66597, 0);
+ check_next_phase(r8a66597);
+ }
+ if (mask1 & SACK) {
+ r8a66597_write(r8a66597, (u16)~SACK, INTSTS1);
+ check_next_phase(r8a66597);
+ }
+ }
+ if (mask0) {
+ if (mask0 & BRDY)
+ irq_pipe_ready(r8a66597);
+ if (mask0 & BEMP)
+ irq_pipe_empty(r8a66597);
+ if (mask0 & NRDY)
+ irq_pipe_nrdy(r8a66597);
+ }
+
+ spin_unlock(&r8a66597->lock);
+ return IRQ_HANDLED;
+}
+
+/* this function must be called with interrupt disabled */
+static void r8a66597_root_hub_control(struct r8a66597 *r8a66597, int port)
+{
+ u16 tmp;
+ struct r8a66597_root_hub *rh = &r8a66597->root_hub[port];
+
+ if (rh->port & (1 << USB_PORT_FEAT_RESET)) {
+ unsigned long dvstctr_reg = get_dvstctr_reg(port);
+
+ tmp = r8a66597_read(r8a66597, dvstctr_reg);
+ if ((tmp & USBRST) == USBRST) {
+ r8a66597_mdfy(r8a66597, UACT, USBRST | UACT,
+ dvstctr_reg);
+ mod_timer(&r8a66597->rh_timer,
+ jiffies + msecs_to_jiffies(50));
+ } else
+ r8a66597_usb_connect(r8a66597, port);
+ }
+
+ if (rh->scount > 0) {
+ tmp = r8a66597_read(r8a66597, get_syssts_reg(port)) & LNST;
+ if (tmp == rh->old_syssts) {
+ rh->scount--;
+ if (rh->scount == 0) {
+ if (tmp == FS_JSTS) {
+ r8a66597_bset(r8a66597, HSE,
+ get_syscfg_reg(port));
+ r8a66597_usb_preconnect(r8a66597, port);
+ } else if (tmp == LS_JSTS) {
+ r8a66597_bclr(r8a66597, HSE,
+ get_syscfg_reg(port));
+ r8a66597_usb_preconnect(r8a66597, port);
+ } else if (tmp == SE0)
+ r8a66597_bset(r8a66597, ATTCHE,
+ get_intenb_reg(port));
+ } else {
+ mod_timer(&r8a66597->rh_timer,
+ jiffies + msecs_to_jiffies(50));
+ }
+ } else {
+ rh->scount = R8A66597_MAX_SAMPLING;
+ rh->old_syssts = tmp;
+ mod_timer(&r8a66597->rh_timer,
+ jiffies + msecs_to_jiffies(50));
+ }
+ }
+}
+
+static void r8a66597_td_timer(unsigned long _r8a66597)
+{
+ struct r8a66597 *r8a66597 = (struct r8a66597 *)_r8a66597;
+ unsigned long flags;
+ u16 pipenum;
+ struct r8a66597_td *td, *new_td = NULL;
+ struct r8a66597_pipe *pipe;
+
+ spin_lock_irqsave(&r8a66597->lock, flags);
+ for (pipenum = 0; pipenum < R8A66597_MAX_NUM_PIPE; pipenum++) {
+ if (!(r8a66597->timeout_map & (1 << pipenum)))
+ continue;
+ if (timer_pending(&r8a66597->td_timer[pipenum]))
+ continue;
+
+ td = r8a66597_get_td(r8a66597, pipenum);
+ if (!td) {
+ r8a66597->timeout_map &= ~(1 << pipenum);
+ continue;
+ }
+
+ if (td->urb->actual_length) {
+ set_td_timer(r8a66597, td);
+ break;
+ }
+
+ pipe = td->pipe;
+ pipe_stop(r8a66597, pipe);
+
+ new_td = td;
+ do {
+ list_move_tail(&new_td->queue,
+ &r8a66597->pipe_queue[pipenum]);
+ new_td = r8a66597_get_td(r8a66597, pipenum);
+ if (!new_td) {
+ new_td = td;
+ break;
+ }
+ } while (td != new_td && td->address == new_td->address);
+
+ start_transfer(r8a66597, new_td);
+
+ if (td == new_td)
+ r8a66597->timeout_map &= ~(1 << pipenum);
+ else
+ set_td_timer(r8a66597, new_td);
+ break;
+ }
+ spin_unlock_irqrestore(&r8a66597->lock, flags);
+}
+
+static void r8a66597_timer(unsigned long _r8a66597)
+{
+ struct r8a66597 *r8a66597 = (struct r8a66597 *)_r8a66597;
+ unsigned long flags;
+
+ spin_lock_irqsave(&r8a66597->lock, flags);
+
+ r8a66597_root_hub_control(r8a66597, 0);
+ r8a66597_root_hub_control(r8a66597, 1);
+
+ spin_unlock_irqrestore(&r8a66597->lock, flags);
+}
+
+static int check_pipe_config(struct r8a66597 *r8a66597, struct urb *urb)
+{
+ struct r8a66597_device *dev = get_urb_to_r8a66597_dev(r8a66597, urb);
+
+ if (dev && dev->address && dev->state != USB_STATE_CONFIGURED &&
+ (urb->dev->state == USB_STATE_CONFIGURED))
+ return 1;
+ else
+ return 0;
+}
+
+static int r8a66597_start(struct usb_hcd *hcd)
+{
+ struct r8a66597 *r8a66597 = hcd_to_r8a66597(hcd);
+ int ret;
+
+ hcd->state = HC_STATE_RUNNING;
+ if ((ret = enable_controller(r8a66597)) < 0)
+ return ret;
+
+ return 0;
+}
+
+static void r8a66597_stop(struct usb_hcd *hcd)
+{
+ struct r8a66597 *r8a66597 = hcd_to_r8a66597(hcd);
+
+ disable_controller(r8a66597);
+}
+
+static void set_address_zero(struct r8a66597 *r8a66597, struct urb *urb)
+{
+ unsigned int usb_address = usb_pipedevice(urb->pipe);
+ u16 root_port, hub_port;
+
+ if (usb_address == 0) {
+ get_port_number(urb->dev->devpath,
+ &root_port, &hub_port);
+ set_devadd_reg(r8a66597, 0,
+ get_r8a66597_usb_speed(urb->dev->speed),
+ get_parent_r8a66597_address(r8a66597, urb->dev),
+ hub_port, root_port);
+ }
+}
+
+static struct r8a66597_td *r8a66597_make_td(struct r8a66597 *r8a66597,
+ struct urb *urb,
+ struct usb_host_endpoint *hep,
+ gfp_t mem_flags)
+{
+ struct r8a66597_td *td;
+ u16 pipenum;
+
+ td = kzalloc(sizeof(struct r8a66597_td), mem_flags);
+ if (td == NULL)
+ return NULL;
+
+ pipenum = r8a66597_get_pipenum(urb, hep);
+ td->pipenum = pipenum;
+ td->pipe = hep->hcpriv;
+ td->urb = urb;
+ td->address = get_urb_to_r8a66597_addr(r8a66597, urb);
+ td->maxpacket = usb_maxpacket(urb->dev, urb->pipe,
+ !usb_pipein(urb->pipe));
+ if (usb_pipecontrol(urb->pipe))
+ td->type = USB_PID_SETUP;
+ else if (usb_pipein(urb->pipe))
+ td->type = USB_PID_IN;
+ else
+ td->type = USB_PID_OUT;
+ INIT_LIST_HEAD(&td->queue);
+
+ return td;
+}
+
+static int r8a66597_urb_enqueue(struct usb_hcd *hcd,
+ struct usb_host_endpoint *hep,
+ struct urb *urb,
+ gfp_t mem_flags)
+{
+ struct r8a66597 *r8a66597 = hcd_to_r8a66597(hcd);
+ struct r8a66597_td *td = NULL;
+ int ret = 0, request = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&r8a66597->lock, flags);
+ if (!get_urb_to_r8a66597_dev(r8a66597, urb)) {
+ ret = -ENODEV;
+ goto error;
+ }
+
+ if (!hep->hcpriv) {
+ hep->hcpriv = kzalloc(sizeof(struct r8a66597_pipe), mem_flags);
+ if (!hep->hcpriv) {
+ ret = -ENOMEM;
+ goto error;
+ }
+ set_pipe_reg_addr(hep->hcpriv, R8A66597_PIPE_NO_DMA);
+ if (usb_pipeendpoint(urb->pipe))
+ init_pipe_info(r8a66597, urb, hep, &hep->desc);
+ }
+
+ if (unlikely(check_pipe_config(r8a66597, urb)))
+ init_pipe_config(r8a66597, urb);
+
+ set_address_zero(r8a66597, urb);
+ td = r8a66597_make_td(r8a66597, urb, hep, mem_flags);
+ if (td == NULL) {
+ ret = -ENOMEM;
+ goto error;
+ }
+ if (list_empty(&r8a66597->pipe_queue[td->pipenum]))
+ request = 1;
+ list_add_tail(&td->queue, &r8a66597->pipe_queue[td->pipenum]);
+
+ spin_lock(&urb->lock);
+ if (urb->status != -EINPROGRESS) {
+ spin_unlock(&urb->lock);
+ ret = -EPIPE;
+ goto error;
+ }
+ urb->hcpriv = td;
+ spin_unlock(&urb->lock);
+
+ if (request) {
+ ret = start_transfer(r8a66597, td);
+ if (ret < 0) {
+ list_del(&td->queue);
+ kfree(td);
+ }
+ } else
+ set_td_timer(r8a66597, td);
+
+error:
+ spin_unlock_irqrestore(&r8a66597->lock, flags);
+ return ret;
+}
+
+static int r8a66597_urb_dequeue(struct usb_hcd *hcd, struct urb *urb)
+{
+ struct r8a66597 *r8a66597 = hcd_to_r8a66597(hcd);
+ struct r8a66597_td *td;
+ unsigned long flags;
+
+ spin_lock_irqsave(&r8a66597->lock, flags);
+ if (urb->hcpriv) {
+ td = urb->hcpriv;
+ pipe_stop(r8a66597, td->pipe);
+ pipe_irq_disable(r8a66597, td->pipenum);
+ disable_irq_empty(r8a66597, td->pipenum);
+ done(r8a66597, td, td->pipenum, urb);
+ }
+ spin_unlock_irqrestore(&r8a66597->lock, flags);
+ return 0;
+}
+
+static void r8a66597_endpoint_disable(struct usb_hcd *hcd,
+ struct usb_host_endpoint *hep)
+{
+ struct r8a66597 *r8a66597 = hcd_to_r8a66597(hcd);
+ struct r8a66597_pipe *pipe = (struct r8a66597_pipe *)hep->hcpriv;
+ struct r8a66597_td *td;
+ struct urb *urb = NULL;
+ u16 pipenum;
+ unsigned long flags;
+
+ if (pipe == NULL)
+ return;
+ pipenum = pipe->info.pipenum;
+
+ if (pipenum == 0) {
+ kfree(hep->hcpriv);
+ hep->hcpriv = NULL;
+ return;
+ }
+
+ spin_lock_irqsave(&r8a66597->lock, flags);
+ pipe_stop(r8a66597, pipe);
+ pipe_irq_disable(r8a66597, pipenum);
+ disable_irq_empty(r8a66597, pipenum);
+ td = r8a66597_get_td(r8a66597, pipenum);
+ if (td)
+ urb = td->urb;
+ done(r8a66597, td, pipenum, urb);
+ kfree(hep->hcpriv);
+ hep->hcpriv = NULL;
+ spin_unlock_irqrestore(&r8a66597->lock, flags);
+}
+
+static int r8a66597_get_frame(struct usb_hcd *hcd)
+{
+ struct r8a66597 *r8a66597 = hcd_to_r8a66597(hcd);
+ return r8a66597_read(r8a66597, FRMNUM) & 0x03FF;
+}
+
+static void collect_usb_address_map(struct usb_device *udev, unsigned long *map)
+{
+ int chix;
+
+ if (udev->state == USB_STATE_CONFIGURED &&
+ udev->parent && udev->parent->devnum > 1 &&
+ udev->parent->descriptor.bDeviceClass == USB_CLASS_HUB)
+ map[udev->devnum/32] |= (1 << (udev->devnum % 32));
+
+ for (chix = 0; chix < udev->maxchild; chix++) {
+ struct usb_device *childdev = udev->children[chix];
+
+ if (childdev)
+ collect_usb_address_map(childdev, map);
+ }
+}
+
+/* this function must be called with interrupt disabled */
+static struct r8a66597_device *get_r8a66597_device(struct r8a66597 *r8a66597,
+ int addr)
+{
+ struct r8a66597_device *dev;
+ struct list_head *list = &r8a66597->child_device;
+
+ list_for_each_entry(dev, list, device_list) {
+ if (!dev)
+ continue;
+ if (dev->usb_address != addr)
+ continue;
+
+ return dev;
+ }
+
+ err("get_r8a66597_device fail.(%d)\n", addr);
+ return NULL;
+}
+
+static void update_usb_address_map(struct r8a66597 *r8a66597,
+ struct usb_device *root_hub,
+ unsigned long *map)
+{
+ int i, j, addr;
+ unsigned long diff;
+ unsigned long flags;
+
+ for (i = 0; i < 4; i++) {
+ diff = r8a66597->child_connect_map[i] ^ map[i];
+ if (!diff)
+ continue;
+
+ for (j = 0; j < 32; j++) {
+ if (!(diff & (1 << j)))
+ continue;
+
+ addr = i * 32 + j;
+ if (map[i] & (1 << j))
+ set_child_connect_map(r8a66597, addr);
+ else {
+ struct r8a66597_device *dev;
+
+ spin_lock_irqsave(&r8a66597->lock, flags);
+ dev = get_r8a66597_device(r8a66597, addr);
+ disable_r8a66597_pipe_all(r8a66597, dev);
+ free_usb_address(r8a66597, dev);
+ put_child_connect_map(r8a66597, addr);
+ spin_unlock_irqrestore(&r8a66597->lock, flags);
+ }
+ }
+ }
+}
+
+static void r8a66597_check_detect_child(struct r8a66597 *r8a66597,
+ struct usb_hcd *hcd)
+{
+ struct usb_bus *bus;
+ unsigned long now_map[4];
+
+ memset(now_map, 0, sizeof(now_map));
+
+ list_for_each_entry(bus, &usb_bus_list, bus_list) {
+ if (!bus->root_hub)
+ continue;
+
+ if (bus->busnum != hcd->self.busnum)
+ continue;
+
+ collect_usb_address_map(bus->root_hub, now_map);
+ update_usb_address_map(r8a66597, bus->root_hub, now_map);
+ }
+}
+
+static int r8a66597_hub_status_data(struct usb_hcd *hcd, char *buf)
+{
+ struct r8a66597 *r8a66597 = hcd_to_r8a66597(hcd);
+ unsigned long flags;
+ int i;
+
+ r8a66597_check_detect_child(r8a66597, hcd);
+
+ spin_lock_irqsave(&r8a66597->lock, flags);
+
+ *buf = 0; /* initialize (no change) */
+
+ for (i = 0; i < R8A66597_MAX_ROOT_HUB; i++) {
+ if (r8a66597->root_hub[i].port & 0xffff0000)
+ *buf |= 1 << (i + 1);
+ }
+
+ spin_unlock_irqrestore(&r8a66597->lock, flags);
+
+ return (*buf != 0);
+}
+
+static void r8a66597_hub_descriptor(struct r8a66597 *r8a66597,
+ struct usb_hub_descriptor *desc)
+{
+ desc->bDescriptorType = 0x29;
+ desc->bHubContrCurrent = 0;
+ desc->bNbrPorts = R8A66597_MAX_ROOT_HUB;
+ desc->bDescLength = 9;
+ desc->bPwrOn2PwrGood = 0;
+ desc->wHubCharacteristics = cpu_to_le16(0x0011);
+ desc->bitmap[0] = ((1 << R8A66597_MAX_ROOT_HUB) - 1) << 1;
+ desc->bitmap[1] = ~0;
+}
+
+static int r8a66597_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
+ u16 wIndex, char *buf, u16 wLength)
+{
+ struct r8a66597 *r8a66597 = hcd_to_r8a66597(hcd);
+ int ret;
+ int port = (wIndex & 0x00FF) - 1;
+ struct r8a66597_root_hub *rh = &r8a66597->root_hub[port];
+ unsigned long flags;
+
+ ret = 0;
+
+ spin_lock_irqsave(&r8a66597->lock, flags);
+ switch (typeReq) {
+ case ClearHubFeature:
+ case SetHubFeature:
+ switch (wValue) {
+ case C_HUB_OVER_CURRENT:
+ case C_HUB_LOCAL_POWER:
+ break;
+ default:
+ goto error;
+ }
+ break;
+ case ClearPortFeature:
+ if (wIndex > R8A66597_MAX_ROOT_HUB)
+ goto error;
+ if (wLength != 0)
+ goto error;
+
+ switch (wValue) {
+ case USB_PORT_FEAT_ENABLE:
+ rh->port &= (1 << USB_PORT_FEAT_POWER);
+ break;
+ case USB_PORT_FEAT_SUSPEND:
+ break;
+ case USB_PORT_FEAT_POWER:
+ r8a66597_port_power(r8a66597, port, 0);
+ break;
+ case USB_PORT_FEAT_C_ENABLE:
+ case USB_PORT_FEAT_C_SUSPEND:
+ case USB_PORT_FEAT_C_CONNECTION:
+ case USB_PORT_FEAT_C_OVER_CURRENT:
+ case USB_PORT_FEAT_C_RESET:
+ break;
+ default:
+ goto error;
+ }
+ rh->port &= ~(1 << wValue);
+ break;
+ case GetHubDescriptor:
+ r8a66597_hub_descriptor(r8a66597,
+ (struct usb_hub_descriptor *)buf);
+ break;
+ case GetHubStatus:
+ *buf = 0x00;
+ break;
+ case GetPortStatus:
+ if (wIndex > R8A66597_MAX_ROOT_HUB)
+ goto error;
+ *(u32 *)buf = rh->port;
+ break;
+ case SetPortFeature:
+ if (wIndex > R8A66597_MAX_ROOT_HUB)
+ goto error;
+ if (wLength != 0)
+ goto error;
+
+ switch (wValue) {
+ case USB_PORT_FEAT_SUSPEND:
+ break;
+ case USB_PORT_FEAT_POWER:
+ r8a66597_port_power(r8a66597, port, 1);
+ rh->port |= (1 << USB_PORT_FEAT_POWER);
+ break;
+ case USB_PORT_FEAT_RESET: {
+ struct r8a66597_device *dev = rh->dev;
+
+ rh->port |= (1 << USB_PORT_FEAT_RESET);
+
+ disable_r8a66597_pipe_all(r8a66597, dev);
+ free_usb_address(r8a66597, dev);
+
+ r8a66597_mdfy(r8a66597, USBRST, USBRST | UACT,
+ get_dvstctr_reg(port));
+ mod_timer(&r8a66597->rh_timer,
+ jiffies + msecs_to_jiffies(50));
+ }
+ break;
+ default:
+ goto error;
+ }
+ rh->port |= 1 << wValue;
+ break;
+ default:
+error:
+ ret = -EPIPE;
+ break;
+ }
+
+ spin_unlock_irqrestore(&r8a66597->lock, flags);
+ return ret;
+}
+
+static struct hc_driver r8a66597_hc_driver = {
+ .description = hcd_name,
+ .hcd_priv_size = sizeof(struct r8a66597),
+ .irq = r8a66597_irq,
+
+ /*
+ * generic hardware linkage
+ */
+ .flags = HCD_USB2,
+
+ .start = r8a66597_start,
+ .stop = r8a66597_stop,
+
+ /*
+ * managing i/o requests and associated device resources
+ */
+ .urb_enqueue = r8a66597_urb_enqueue,
+ .urb_dequeue = r8a66597_urb_dequeue,
+ .endpoint_disable = r8a66597_endpoint_disable,
+
+ /*
+ * periodic schedule support
+ */
+ .get_frame_number = r8a66597_get_frame,
+
+ /*
+ * root hub support
+ */
+ .hub_status_data = r8a66597_hub_status_data,
+ .hub_control = r8a66597_hub_control,
+};
+
+#if defined(CONFIG_PM)
+static int r8a66597_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ pdev->dev.power.power_state = state;
+ return 0;
+}
+
+static int r8a66597_resume(struct platform_device *pdev)
+{
+ pdev->dev.power.power_state = PMSG_ON;
+ return 0;
+}
+#else /* if defined(CONFIG_PM) */
+#define r8a66597_suspend NULL
+#define r8a66597_resume NULL
+#endif
+
+static int __init_or_module r8a66597_remove(struct platform_device *pdev)
+{
+ struct r8a66597 *r8a66597 = dev_get_drvdata(&pdev->dev);
+ struct usb_hcd *hcd = r8a66597_to_hcd(r8a66597);
+
+ del_timer_sync(&r8a66597->rh_timer);
+ iounmap((void *)r8a66597->reg);
+ usb_remove_hcd(hcd);
+ usb_put_hcd(hcd);
+ return 0;
+}
+
+#define resource_len(r) (((r)->end - (r)->start) + 1)
+static int __init r8a66597_probe(struct platform_device *pdev)
+{
+ struct resource *res = NULL;
+ int irq = -1;
+ void __iomem *reg = NULL;
+ struct usb_hcd *hcd = NULL;
+ struct r8a66597 *r8a66597;
+ int ret = 0;
+ int i;
+
+ if (pdev->dev.dma_mask) {
+ ret = -EINVAL;
+ err("dma not support");
+ goto clean_up;
+ }
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ (char *)hcd_name);
+ if (!res) {
+ ret = -ENODEV;
+ err("platform_get_resource_byname error.");
+ goto clean_up;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ ret = -ENODEV;
+ err("platform_get_irq error.");
+ goto clean_up;
+ }
+
+ reg = ioremap(res->start, resource_len(res));
+ if (reg == NULL) {
+ ret = -ENOMEM;
+ err("ioremap error.");
+ goto clean_up;
+ }
+
+ /* initialize hcd */
+ hcd = usb_create_hcd(&r8a66597_hc_driver, &pdev->dev, (char *)hcd_name);
+ if (!hcd) {
+ ret = -ENOMEM;
+ err("Failed to create hcd");
+ goto clean_up;
+ }
+ r8a66597 = hcd_to_r8a66597(hcd);
+ memset(r8a66597, 0, sizeof(struct r8a66597));
+ dev_set_drvdata(&pdev->dev, r8a66597);
+
+ spin_lock_init(&r8a66597->lock);
+ init_timer(&r8a66597->rh_timer);
+ r8a66597->rh_timer.function = r8a66597_timer;
+ r8a66597->rh_timer.data = (unsigned long)r8a66597;
+ r8a66597->reg = (unsigned long)reg;
+
+ for (i = 0; i < R8A66597_MAX_NUM_PIPE; i++) {
+ INIT_LIST_HEAD(&r8a66597->pipe_queue[i]);
+ init_timer(&r8a66597->td_timer[i]);
+ r8a66597->td_timer[i].function = r8a66597_td_timer;
+ r8a66597->td_timer[i].data = (unsigned long)r8a66597;
+ }
+ INIT_LIST_HEAD(&r8a66597->child_device);
+
+ hcd->rsrc_start = res->start;
+ ret = usb_add_hcd(hcd, irq, 0);
+ if (ret != 0) {
+ err("Failed to add hcd");
+ goto clean_up;
+ }
+
+ return 0;
+
+clean_up:
+ if (reg)
+ iounmap(reg);
+ if (res)
+ release_mem_region(res->start, 1);
+
+ return ret;
+}
+
+static struct platform_driver r8a66597_driver = {
+ .probe = r8a66597_probe,
+ .remove = r8a66597_remove,
+ .suspend = r8a66597_suspend,
+ .resume = r8a66597_resume,
+ .driver = {
+ .name = (char *) hcd_name,
+ },
+};
+
+static int __init r8a66597_init(void)
+{
+ if (usb_disabled())
+ return -ENODEV;
+
+ info("driver %s, %s", hcd_name, DRIVER_VERSION);
+ return platform_driver_register(&r8a66597_driver);
+}
+module_init(r8a66597_init);
+
+static void __exit r8a66597_cleanup(void)
+{
+ platform_driver_unregister(&r8a66597_driver);
+}
+module_exit(r8a66597_cleanup);
+
diff --git a/drivers/usb/host/r8a66597.h b/drivers/usb/host/r8a66597.h
new file mode 100644
index 00000000000..97c2a71ac7a
--- /dev/null
+++ b/drivers/usb/host/r8a66597.h
@@ -0,0 +1,634 @@
+/*
+ * R8A66597 HCD (Host Controller Driver)
+ *
+ * Copyright (C) 2006-2007 Renesas Solutions Corp.
+ * Portions Copyright (C) 2004 Psion Teklogix (for NetBook PRO)
+ * Portions Copyright (C) 2004-2005 David Brownell
+ * Portions Copyright (C) 1999 Roman Weissgaerber
+ *
+ * Author : Yoshihiro Shimoda <shimoda.yoshihiro@renesas.com>
+ *
+ * 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; version 2 of the License.
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifndef __R8A66597_H__
+#define __R8A66597_H__
+
+#define SYSCFG0 0x00
+#define SYSCFG1 0x02
+#define SYSSTS0 0x04
+#define SYSSTS1 0x06
+#define DVSTCTR0 0x08
+#define DVSTCTR1 0x0A
+#define TESTMODE 0x0C
+#define PINCFG 0x0E
+#define DMA0CFG 0x10
+#define DMA1CFG 0x12
+#define CFIFO 0x14
+#define D0FIFO 0x18
+#define D1FIFO 0x1C
+#define CFIFOSEL 0x20
+#define CFIFOCTR 0x22
+#define CFIFOSIE 0x24
+#define D0FIFOSEL 0x28
+#define D0FIFOCTR 0x2A
+#define D1FIFOSEL 0x2C
+#define D1FIFOCTR 0x2E
+#define INTENB0 0x30
+#define INTENB1 0x32
+#define INTENB2 0x34
+#define BRDYENB 0x36
+#define NRDYENB 0x38
+#define BEMPENB 0x3A
+#define SOFCFG 0x3C
+#define INTSTS0 0x40
+#define INTSTS1 0x42
+#define INTSTS2 0x44
+#define BRDYSTS 0x46
+#define NRDYSTS 0x48
+#define BEMPSTS 0x4A
+#define FRMNUM 0x4C
+#define UFRMNUM 0x4E
+#define USBADDR 0x50
+#define USBREQ 0x54
+#define USBVAL 0x56
+#define USBINDX 0x58
+#define USBLENG 0x5A
+#define DCPCFG 0x5C
+#define DCPMAXP 0x5E
+#define DCPCTR 0x60
+#define PIPESEL 0x64
+#define PIPECFG 0x68
+#define PIPEBUF 0x6A
+#define PIPEMAXP 0x6C
+#define PIPEPERI 0x6E
+#define PIPE1CTR 0x70
+#define PIPE2CTR 0x72
+#define PIPE3CTR 0x74
+#define PIPE4CTR 0x76
+#define PIPE5CTR 0x78
+#define PIPE6CTR 0x7A
+#define PIPE7CTR 0x7C
+#define PIPE8CTR 0x7E
+#define PIPE9CTR 0x80
+#define PIPE1TRE 0x90
+#define PIPE1TRN 0x92
+#define PIPE2TRE 0x94
+#define PIPE2TRN 0x96
+#define PIPE3TRE 0x98
+#define PIPE3TRN 0x9A
+#define PIPE4TRE 0x9C
+#define PIPE4TRN 0x9E
+#define PIPE5TRE 0xA0
+#define PIPE5TRN 0xA2
+#define DEVADD0 0xD0
+#define DEVADD1 0xD2
+#define DEVADD2 0xD4
+#define DEVADD3 0xD6
+#define DEVADD4 0xD8
+#define DEVADD5 0xDA
+#define DEVADD6 0xDC
+#define DEVADD7 0xDE
+#define DEVADD8 0xE0
+#define DEVADD9 0xE2
+#define DEVADDA 0xE4
+
+/* System Configuration Control Register */
+#define XTAL 0xC000 /* b15-14: Crystal selection */
+#define XTAL48 0x8000 /* 48MHz */
+#define XTAL24 0x4000 /* 24MHz */
+#define XTAL12 0x0000 /* 12MHz */
+#define XCKE 0x2000 /* b13: External clock enable */
+#define PLLC 0x0800 /* b11: PLL control */
+#define SCKE 0x0400 /* b10: USB clock enable */
+#define PCSDIS 0x0200 /* b9: not CS wakeup */
+#define LPSME 0x0100 /* b8: Low power sleep mode */
+#define HSE 0x0080 /* b7: Hi-speed enable */
+#define DCFM 0x0040 /* b6: Controller function select */
+#define DRPD 0x0020 /* b5: D+/- pull down control */
+#define DPRPU 0x0010 /* b4: D+ pull up control */
+#define USBE 0x0001 /* b0: USB module operation enable */
+
+/* System Configuration Status Register */
+#define OVCBIT 0x8000 /* b15-14: Over-current bit */
+#define OVCMON 0xC000 /* b15-14: Over-current monitor */
+#define SOFEA 0x0020 /* b5: SOF monitor */
+#define IDMON 0x0004 /* b3: ID-pin monitor */
+#define LNST 0x0003 /* b1-0: D+, D- line status */
+#define SE1 0x0003 /* SE1 */
+#define FS_KSTS 0x0002 /* Full-Speed K State */
+#define FS_JSTS 0x0001 /* Full-Speed J State */
+#define LS_JSTS 0x0002 /* Low-Speed J State */
+#define LS_KSTS 0x0001 /* Low-Speed K State */
+#define SE0 0x0000 /* SE0 */
+
+/* Device State Control Register */
+#define EXTLP0 0x0400 /* b10: External port */
+#define VBOUT 0x0200 /* b9: VBUS output */
+#define WKUP 0x0100 /* b8: Remote wakeup */
+#define RWUPE 0x0080 /* b7: Remote wakeup sense */
+#define USBRST 0x0040 /* b6: USB reset enable */
+#define RESUME 0x0020 /* b5: Resume enable */
+#define UACT 0x0010 /* b4: USB bus enable */
+#define RHST 0x0007 /* b1-0: Reset handshake status */
+#define HSPROC 0x0004 /* HS handshake is processing */
+#define HSMODE 0x0003 /* Hi-Speed mode */
+#define FSMODE 0x0002 /* Full-Speed mode */
+#define LSMODE 0x0001 /* Low-Speed mode */
+#define UNDECID 0x0000 /* Undecided */
+
+/* Test Mode Register */
+#define UTST 0x000F /* b3-0: Test select */
+#define H_TST_PACKET 0x000C /* HOST TEST Packet */
+#define H_TST_SE0_NAK 0x000B /* HOST TEST SE0 NAK */
+#define H_TST_K 0x000A /* HOST TEST K */
+#define H_TST_J 0x0009 /* HOST TEST J */
+#define H_TST_NORMAL 0x0000 /* HOST Normal Mode */
+#define P_TST_PACKET 0x0004 /* PERI TEST Packet */
+#define P_TST_SE0_NAK 0x0003 /* PERI TEST SE0 NAK */
+#define P_TST_K 0x0002 /* PERI TEST K */
+#define P_TST_J 0x0001 /* PERI TEST J */
+#define P_TST_NORMAL 0x0000 /* PERI Normal Mode */
+
+/* Data Pin Configuration Register */
+#define LDRV 0x8000 /* b15: Drive Current Adjust */
+#define VIF1 0x0000 /* VIF = 1.8V */
+#define VIF3 0x8000 /* VIF = 3.3V */
+#define INTA 0x0001 /* b1: USB INT-pin active */
+
+/* DMAx Pin Configuration Register */
+#define DREQA 0x4000 /* b14: Dreq active select */
+#define BURST 0x2000 /* b13: Burst mode */
+#define DACKA 0x0400 /* b10: Dack active select */
+#define DFORM 0x0380 /* b9-7: DMA mode select */
+#define CPU_ADR_RD_WR 0x0000 /* Address + RD/WR mode (CPU bus) */
+#define CPU_DACK_RD_WR 0x0100 /* DACK + RD/WR mode (CPU bus) */
+#define CPU_DACK_ONLY 0x0180 /* DACK only mode (CPU bus) */
+#define SPLIT_DACK_ONLY 0x0200 /* DACK only mode (SPLIT bus) */
+#define DENDA 0x0040 /* b6: Dend active select */
+#define PKTM 0x0020 /* b5: Packet mode */
+#define DENDE 0x0010 /* b4: Dend enable */
+#define OBUS 0x0004 /* b2: OUTbus mode */
+
+/* CFIFO/DxFIFO Port Select Register */
+#define RCNT 0x8000 /* b15: Read count mode */
+#define REW 0x4000 /* b14: Buffer rewind */
+#define DCLRM 0x2000 /* b13: DMA buffer clear mode */
+#define DREQE 0x1000 /* b12: DREQ output enable */
+#define MBW 0x0400 /* b10: Maximum bit width for FIFO access */
+#define MBW_8 0x0000 /* 8bit */
+#define MBW_16 0x0400 /* 16bit */
+#define BIGEND 0x0100 /* b8: Big endian mode */
+#define BYTE_LITTLE 0x0000 /* little dendian */
+#define BYTE_BIG 0x0100 /* big endifan */
+#define ISEL 0x0020 /* b5: DCP FIFO port direction select */
+#define CURPIPE 0x000F /* b2-0: PIPE select */
+
+/* CFIFO/DxFIFO Port Control Register */
+#define BVAL 0x8000 /* b15: Buffer valid flag */
+#define BCLR 0x4000 /* b14: Buffer clear */
+#define FRDY 0x2000 /* b13: FIFO ready */
+#define DTLN 0x0FFF /* b11-0: FIFO received data length */
+
+/* Interrupt Enable Register 0 */
+#define VBSE 0x8000 /* b15: VBUS interrupt */
+#define RSME 0x4000 /* b14: Resume interrupt */
+#define SOFE 0x2000 /* b13: Frame update interrupt */
+#define DVSE 0x1000 /* b12: Device state transition interrupt */
+#define CTRE 0x0800 /* b11: Control transfer stage transition interrupt */
+#define BEMPE 0x0400 /* b10: Buffer empty interrupt */
+#define NRDYE 0x0200 /* b9: Buffer not ready interrupt */
+#define BRDYE 0x0100 /* b8: Buffer ready interrupt */
+
+/* Interrupt Enable Register 1 */
+#define OVRCRE 0x8000 /* b15: Over-current interrupt */
+#define BCHGE 0x4000 /* b14: USB us chenge interrupt */
+#define DTCHE 0x1000 /* b12: Detach sense interrupt */
+#define ATTCHE 0x0800 /* b11: Attach sense interrupt */
+#define EOFERRE 0x0040 /* b6: EOF error interrupt */
+#define SIGNE 0x0020 /* b5: SETUP IGNORE interrupt */
+#define SACKE 0x0010 /* b4: SETUP ACK interrupt */
+
+/* BRDY Interrupt Enable/Status Register */
+#define BRDY9 0x0200 /* b9: PIPE9 */
+#define BRDY8 0x0100 /* b8: PIPE8 */
+#define BRDY7 0x0080 /* b7: PIPE7 */
+#define BRDY6 0x0040 /* b6: PIPE6 */
+#define BRDY5 0x0020 /* b5: PIPE5 */
+#define BRDY4 0x0010 /* b4: PIPE4 */
+#define BRDY3 0x0008 /* b3: PIPE3 */
+#define BRDY2 0x0004 /* b2: PIPE2 */
+#define BRDY1 0x0002 /* b1: PIPE1 */
+#define BRDY0 0x0001 /* b1: PIPE0 */
+
+/* NRDY Interrupt Enable/Status Register */
+#define NRDY9 0x0200 /* b9: PIPE9 */
+#define NRDY8 0x0100 /* b8: PIPE8 */
+#define NRDY7 0x0080 /* b7: PIPE7 */
+#define NRDY6 0x0040 /* b6: PIPE6 */
+#define NRDY5 0x0020 /* b5: PIPE5 */
+#define NRDY4 0x0010 /* b4: PIPE4 */
+#define NRDY3 0x0008 /* b3: PIPE3 */
+#define NRDY2 0x0004 /* b2: PIPE2 */
+#define NRDY1 0x0002 /* b1: PIPE1 */
+#define NRDY0 0x0001 /* b1: PIPE0 */
+
+/* BEMP Interrupt Enable/Status Register */
+#define BEMP9 0x0200 /* b9: PIPE9 */
+#define BEMP8 0x0100 /* b8: PIPE8 */
+#define BEMP7 0x0080 /* b7: PIPE7 */
+#define BEMP6 0x0040 /* b6: PIPE6 */
+#define BEMP5 0x0020 /* b5: PIPE5 */
+#define BEMP4 0x0010 /* b4: PIPE4 */
+#define BEMP3 0x0008 /* b3: PIPE3 */
+#define BEMP2 0x0004 /* b2: PIPE2 */
+#define BEMP1 0x0002 /* b1: PIPE1 */
+#define BEMP0 0x0001 /* b0: PIPE0 */
+
+/* SOF Pin Configuration Register */
+#define TRNENSEL 0x0100 /* b8: Select transaction enable period */
+#define BRDYM 0x0040 /* b6: BRDY clear timing */
+#define INTL 0x0020 /* b5: Interrupt sense select */
+#define EDGESTS 0x0010 /* b4: */
+#define SOFMODE 0x000C /* b3-2: SOF pin select */
+#define SOF_125US 0x0008 /* SOF OUT 125us Frame Signal */
+#define SOF_1MS 0x0004 /* SOF OUT 1ms Frame Signal */
+#define SOF_DISABLE 0x0000 /* SOF OUT Disable */
+
+/* Interrupt Status Register 0 */
+#define VBINT 0x8000 /* b15: VBUS interrupt */
+#define RESM 0x4000 /* b14: Resume interrupt */
+#define SOFR 0x2000 /* b13: SOF frame update interrupt */
+#define DVST 0x1000 /* b12: Device state transition interrupt */
+#define CTRT 0x0800 /* b11: Control transfer stage transition interrupt */
+#define BEMP 0x0400 /* b10: Buffer empty interrupt */
+#define NRDY 0x0200 /* b9: Buffer not ready interrupt */
+#define BRDY 0x0100 /* b8: Buffer ready interrupt */
+#define VBSTS 0x0080 /* b7: VBUS input port */
+#define DVSQ 0x0070 /* b6-4: Device state */
+#define DS_SPD_CNFG 0x0070 /* Suspend Configured */
+#define DS_SPD_ADDR 0x0060 /* Suspend Address */
+#define DS_SPD_DFLT 0x0050 /* Suspend Default */
+#define DS_SPD_POWR 0x0040 /* Suspend Powered */
+#define DS_SUSP 0x0040 /* Suspend */
+#define DS_CNFG 0x0030 /* Configured */
+#define DS_ADDS 0x0020 /* Address */
+#define DS_DFLT 0x0010 /* Default */
+#define DS_POWR 0x0000 /* Powered */
+#define DVSQS 0x0030 /* b5-4: Device state */
+#define VALID 0x0008 /* b3: Setup packet detected flag */
+#define CTSQ 0x0007 /* b2-0: Control transfer stage */
+#define CS_SQER 0x0006 /* Sequence error */
+#define CS_WRND 0x0005 /* Control write nodata status stage */
+#define CS_WRSS 0x0004 /* Control write status stage */
+#define CS_WRDS 0x0003 /* Control write data stage */
+#define CS_RDSS 0x0002 /* Control read status stage */
+#define CS_RDDS 0x0001 /* Control read data stage */
+#define CS_IDST 0x0000 /* Idle or setup stage */
+
+/* Interrupt Status Register 1 */
+#define OVRCR 0x8000 /* b15: Over-current interrupt */
+#define BCHG 0x4000 /* b14: USB bus chenge interrupt */
+#define DTCH 0x1000 /* b12: Detach sense interrupt */
+#define ATTCH 0x0800 /* b11: Attach sense interrupt */
+#define EOFERR 0x0040 /* b6: EOF-error interrupt */
+#define SIGN 0x0020 /* b5: Setup ignore interrupt */
+#define SACK 0x0010 /* b4: Setup acknowledge interrupt */
+
+/* Frame Number Register */
+#define OVRN 0x8000 /* b15: Overrun error */
+#define CRCE 0x4000 /* b14: Received data error */
+#define FRNM 0x07FF /* b10-0: Frame number */
+
+/* Micro Frame Number Register */
+#define UFRNM 0x0007 /* b2-0: Micro frame number */
+
+/* USB Address / Low Power Status Recovery Register */
+//#define USBADDR 0x007F /* b6-0: USB address */
+
+/* Default Control Pipe Maxpacket Size Register */
+/* Pipe Maxpacket Size Register */
+#define DEVSEL 0xF000 /* b15-14: Device address select */
+#define MAXP 0x007F /* b6-0: Maxpacket size of default control pipe */
+
+/* Default Control Pipe Control Register */
+#define BSTS 0x8000 /* b15: Buffer status */
+#define SUREQ 0x4000 /* b14: Send USB request */
+#define CSCLR 0x2000 /* b13: complete-split status clear */
+#define CSSTS 0x1000 /* b12: complete-split status */
+#define SUREQCLR 0x0800 /* b11: stop setup request */
+#define SQCLR 0x0100 /* b8: Sequence toggle bit clear */
+#define SQSET 0x0080 /* b7: Sequence toggle bit set */
+#define SQMON 0x0040 /* b6: Sequence toggle bit monitor */
+#define PBUSY 0x0020 /* b5: pipe busy */
+#define PINGE 0x0010 /* b4: ping enable */
+#define CCPL 0x0004 /* b2: Enable control transfer complete */
+#define PID 0x0003 /* b1-0: Response PID */
+#define PID_STALL11 0x0003 /* STALL */
+#define PID_STALL 0x0002 /* STALL */
+#define PID_BUF 0x0001 /* BUF */
+#define PID_NAK 0x0000 /* NAK */
+
+/* Pipe Window Select Register */
+#define PIPENM 0x0007 /* b2-0: Pipe select */
+
+/* Pipe Configuration Register */
+#define R8A66597_TYP 0xC000 /* b15-14: Transfer type */
+#define R8A66597_ISO 0xC000 /* Isochronous */
+#define R8A66597_INT 0x8000 /* Interrupt */
+#define R8A66597_BULK 0x4000 /* Bulk */
+#define R8A66597_BFRE 0x0400 /* b10: Buffer ready interrupt mode select */
+#define R8A66597_DBLB 0x0200 /* b9: Double buffer mode select */
+#define R8A66597_CNTMD 0x0100 /* b8: Continuous transfer mode select */
+#define R8A66597_SHTNAK 0x0080 /* b7: Transfer end NAK */
+#define R8A66597_DIR 0x0010 /* b4: Transfer direction select */
+#define R8A66597_EPNUM 0x000F /* b3-0: Eendpoint number select */
+
+/* Pipe Buffer Configuration Register */
+#define BUFSIZE 0x7C00 /* b14-10: Pipe buffer size */
+#define BUFNMB 0x007F /* b6-0: Pipe buffer number */
+#define PIPE0BUF 256
+#define PIPExBUF 64
+
+/* Pipe Maxpacket Size Register */
+#define MXPS 0x07FF /* b10-0: Maxpacket size */
+
+/* Pipe Cycle Configuration Register */
+#define IFIS 0x1000 /* b12: Isochronous in-buffer flush mode select */
+#define IITV 0x0007 /* b2-0: Isochronous interval */
+
+/* Pipex Control Register */
+#define BSTS 0x8000 /* b15: Buffer status */
+#define INBUFM 0x4000 /* b14: IN buffer monitor (Only for PIPE1 to 5) */
+#define CSCLR 0x2000 /* b13: complete-split status clear */
+#define CSSTS 0x1000 /* b12: complete-split status */
+#define ATREPM 0x0400 /* b10: Auto repeat mode */
+#define ACLRM 0x0200 /* b9: Out buffer auto clear mode */
+#define SQCLR 0x0100 /* b8: Sequence toggle bit clear */
+#define SQSET 0x0080 /* b7: Sequence toggle bit set */
+#define SQMON 0x0040 /* b6: Sequence toggle bit monitor */
+#define PBUSY 0x0020 /* b5: pipe busy */
+#define PID 0x0003 /* b1-0: Response PID */
+
+/* PIPExTRE */
+#define TRENB 0x0200 /* b9: Transaction counter enable */
+#define TRCLR 0x0100 /* b8: Transaction counter clear */
+
+/* PIPExTRN */
+#define TRNCNT 0xFFFF /* b15-0: Transaction counter */
+
+/* DEVADDx */
+#define UPPHUB 0x7800
+#define HUBPORT 0x0700
+#define USBSPD 0x00C0
+#define RTPORT 0x0001
+
+#define R8A66597_MAX_NUM_PIPE 10
+#define R8A66597_BUF_BSIZE 8
+#define R8A66597_MAX_DEVICE 10
+#define R8A66597_MAX_ROOT_HUB 2
+#define R8A66597_MAX_SAMPLING 10
+#define R8A66597_MAX_DMA_CHANNEL 2
+#define R8A66597_PIPE_NO_DMA R8A66597_MAX_DMA_CHANNEL
+#define check_bulk_or_isoc(pipenum) ((pipenum >= 1 && pipenum <= 5))
+#define check_interrupt(pipenum) ((pipenum >= 6 && pipenum <= 9))
+#define make_devsel(addr) (addr << 12)
+
+struct r8a66597_pipe_info {
+ u16 pipenum;
+ u16 address; /* R8A66597 HCD usb addres */
+ u16 epnum;
+ u16 maxpacket;
+ u16 type;
+ u16 bufnum;
+ u16 buf_bsize;
+ u16 interval;
+ u16 dir_in;
+};
+
+struct r8a66597_pipe {
+ struct r8a66597_pipe_info info;
+
+ unsigned long fifoaddr;
+ unsigned long fifosel;
+ unsigned long fifoctr;
+ unsigned long pipectr;
+ unsigned long pipetre;
+ unsigned long pipetrn;
+};
+
+struct r8a66597_td {
+ struct r8a66597_pipe *pipe;
+ struct urb *urb;
+ struct list_head queue;
+
+ u16 type;
+ u16 pipenum;
+ int iso_cnt;
+
+ u16 address; /* R8A66597's USB address */
+ u16 maxpacket;
+
+ unsigned zero_packet:1;
+ unsigned short_packet:1;
+ unsigned set_address:1;
+};
+
+struct r8a66597_device {
+ u16 address; /* R8A66597's USB address */
+ u16 hub_port;
+ u16 root_port;
+
+ unsigned short ep_in_toggle;
+ unsigned short ep_out_toggle;
+ unsigned char pipe_cnt[R8A66597_MAX_NUM_PIPE];
+ unsigned char dma_map;
+
+ enum usb_device_state state;
+
+ struct usb_device *udev;
+ int usb_address;
+ struct list_head device_list;
+};
+
+struct r8a66597_root_hub {
+ u32 port;
+ u16 old_syssts;
+ int scount;
+
+ struct r8a66597_device *dev;
+};
+
+struct r8a66597 {
+ spinlock_t lock;
+ unsigned long reg;
+
+ struct r8a66597_device device0;
+ struct r8a66597_root_hub root_hub[R8A66597_MAX_ROOT_HUB];
+ struct list_head pipe_queue[R8A66597_MAX_NUM_PIPE];
+
+ struct timer_list rh_timer;
+ struct timer_list td_timer[R8A66597_MAX_NUM_PIPE];
+
+ unsigned short address_map;
+ unsigned short timeout_map;
+ unsigned char pipe_cnt[R8A66597_MAX_NUM_PIPE];
+ unsigned char dma_map;
+
+ struct list_head child_device;
+ unsigned long child_connect_map[4];
+};
+
+static inline struct r8a66597 *hcd_to_r8a66597(struct usb_hcd *hcd)
+{
+ return (struct r8a66597 *)(hcd->hcd_priv);
+}
+
+static inline struct usb_hcd *r8a66597_to_hcd(struct r8a66597 *r8a66597)
+{
+ return container_of((void *)r8a66597, struct usb_hcd, hcd_priv);
+}
+
+static inline struct r8a66597_td *r8a66597_get_td(struct r8a66597 *r8a66597,
+ u16 pipenum)
+{
+ if (unlikely(list_empty(&r8a66597->pipe_queue[pipenum])))
+ return NULL;
+
+ return list_entry(r8a66597->pipe_queue[pipenum].next,
+ struct r8a66597_td, queue);
+}
+
+static inline struct urb *r8a66597_get_urb(struct r8a66597 *r8a66597,
+ u16 pipenum)
+{
+ struct r8a66597_td *td;
+
+ td = r8a66597_get_td(r8a66597, pipenum);
+ return (td ? td->urb : NULL);
+}
+
+static inline u16 r8a66597_read(struct r8a66597 *r8a66597, unsigned long offset)
+{
+ return inw(r8a66597->reg + offset);
+}
+
+static inline void r8a66597_read_fifo(struct r8a66597 *r8a66597,
+ unsigned long offset, u16 *buf,
+ int len)
+{
+ len = (len + 1) / 2;
+ insw(r8a66597->reg + offset, buf, len);
+}
+
+static inline void r8a66597_write(struct r8a66597 *r8a66597, u16 val,
+ unsigned long offset)
+{
+ outw(val, r8a66597->reg + offset);
+}
+
+static inline void r8a66597_write_fifo(struct r8a66597 *r8a66597,
+ unsigned long offset, u16 *buf,
+ int len)
+{
+ unsigned long fifoaddr = r8a66597->reg + offset;
+ int odd = len & 0x0001;
+
+ len = len / 2;
+ outsw(fifoaddr, buf, len);
+ if (unlikely(odd)) {
+ buf = &buf[len];
+ outb((unsigned char)*buf, fifoaddr);
+ }
+}
+
+static inline void r8a66597_mdfy(struct r8a66597 *r8a66597,
+ u16 val, u16 pat, unsigned long offset)
+{
+ u16 tmp;
+ tmp = r8a66597_read(r8a66597, offset);
+ tmp = tmp & (~pat);
+ tmp = tmp | val;
+ r8a66597_write(r8a66597, tmp, offset);
+}
+
+#define r8a66597_bclr(r8a66597, val, offset) \
+ r8a66597_mdfy(r8a66597, 0, val, offset)
+#define r8a66597_bset(r8a66597, val, offset) \
+ r8a66597_mdfy(r8a66597, val, 0, offset)
+
+static inline unsigned long get_syscfg_reg(int port)
+{
+ return port == 0 ? SYSCFG0 : SYSCFG1;
+}
+
+static inline unsigned long get_syssts_reg(int port)
+{
+ return port == 0 ? SYSSTS0 : SYSSTS1;
+}
+
+static inline unsigned long get_dvstctr_reg(int port)
+{
+ return port == 0 ? DVSTCTR0 : DVSTCTR1;
+}
+
+static inline unsigned long get_intenb_reg(int port)
+{
+ return port == 0 ? INTENB1 : INTENB2;
+}
+
+static inline unsigned long get_intsts_reg(int port)
+{
+ return port == 0 ? INTSTS1 : INTSTS2;
+}
+
+static inline u16 get_rh_usb_speed(struct r8a66597 *r8a66597, int port)
+{
+ unsigned long dvstctr_reg = get_dvstctr_reg(port);
+
+ return r8a66597_read(r8a66597, dvstctr_reg) & RHST;
+}
+
+static inline void r8a66597_port_power(struct r8a66597 *r8a66597, int port,
+ int power)
+{
+ unsigned long dvstctr_reg = get_dvstctr_reg(port);
+
+ if (power)
+ r8a66597_bset(r8a66597, VBOUT, dvstctr_reg);
+ else
+ r8a66597_bclr(r8a66597, VBOUT, dvstctr_reg);
+}
+
+#define get_pipectr_addr(pipenum) (PIPE1CTR + (pipenum - 1) * 2)
+#define get_pipetre_addr(pipenum) (PIPE1TRE + (pipenum - 1) * 4)
+#define get_pipetrn_addr(pipenum) (PIPE1TRN + (pipenum - 1) * 4)
+#define get_devadd_addr(address) (DEVADD0 + address * 2)
+
+#define enable_irq_ready(r8a66597, pipenum) \
+ enable_pipe_irq(r8a66597, pipenum, BRDYENB)
+#define disable_irq_ready(r8a66597, pipenum) \
+ disable_pipe_irq(r8a66597, pipenum, BRDYENB)
+#define enable_irq_empty(r8a66597, pipenum) \
+ enable_pipe_irq(r8a66597, pipenum, BEMPENB)
+#define disable_irq_empty(r8a66597, pipenum) \
+ disable_pipe_irq(r8a66597, pipenum, BEMPENB)
+#define enable_irq_nrdy(r8a66597, pipenum) \
+ enable_pipe_irq(r8a66597, pipenum, NRDYENB)
+#define disable_irq_nrdy(r8a66597, pipenum) \
+ disable_pipe_irq(r8a66597, pipenum, NRDYENB)
+
+#endif /* __R8A66597_H__ */
+
diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c
index d22da26ff16..76c555a67da 100644
--- a/drivers/usb/host/uhci-hcd.c
+++ b/drivers/usb/host/uhci-hcd.c
@@ -730,10 +730,9 @@ static int uhci_rh_resume(struct usb_hcd *hcd)
int rc = 0;
spin_lock_irq(&uhci->lock);
- if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) {
- dev_warn(&hcd->self.root_hub->dev, "HC isn't running!\n");
+ if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))
rc = -ESHUTDOWN;
- } else if (!uhci->dead)
+ else if (!uhci->dead)
wakeup_rh(uhci);
spin_unlock_irq(&uhci->lock);
return rc;