USB: UHCI: Add support for big endian descriptors
This patch adds support for universal host controllers that use big endian descriptors. Support for BE descriptors requires a non-PCI host controller. For kernels with PCI-only UHCI there should be no change in behaviour. This patch tries to replicate the technique used to support BE descriptors in the EHCI HCD. Parts added to uhci-hcd.h are basically copy'n'paste from ehci.h. Signed-off-by: Jan Andersson <jan@gaisler.com> Acked-by: Alan Stern <stern@rowland.harvard.edu> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
bab1ff1bda
commit
51e2f62fe7
@@ -420,6 +420,10 @@ config USB_UHCI_BIG_ENDIAN_MMIO
|
|||||||
bool
|
bool
|
||||||
depends on USB_UHCI_SUPPORT_NON_PCI_HC
|
depends on USB_UHCI_SUPPORT_NON_PCI_HC
|
||||||
|
|
||||||
|
config USB_UHCI_BIG_ENDIAN_DESC
|
||||||
|
bool
|
||||||
|
depends on USB_UHCI_SUPPORT_NON_PCI_HC
|
||||||
|
|
||||||
config USB_FHCI_HCD
|
config USB_FHCI_HCD
|
||||||
tristate "Freescale QE USB Host Controller support"
|
tristate "Freescale QE USB Host Controller support"
|
||||||
depends on USB && OF_GPIO && QE_GPIO && QUICC_ENGINE
|
depends on USB && OF_GPIO && QE_GPIO && QUICC_ENGINE
|
||||||
|
@@ -37,7 +37,8 @@ static void lprintk(char *buf)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int uhci_show_td(struct uhci_td *td, char *buf, int len, int space)
|
static int uhci_show_td(struct uhci_hcd *uhci, struct uhci_td *td, char *buf,
|
||||||
|
int len, int space)
|
||||||
{
|
{
|
||||||
char *out = buf;
|
char *out = buf;
|
||||||
char *spid;
|
char *spid;
|
||||||
@@ -47,8 +48,9 @@ static int uhci_show_td(struct uhci_td *td, char *buf, int len, int space)
|
|||||||
if (len < 160)
|
if (len < 160)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
status = td_status(td);
|
status = td_status(uhci, td);
|
||||||
out += sprintf(out, "%*s[%p] link (%08x) ", space, "", td, le32_to_cpu(td->link));
|
out += sprintf(out, "%*s[%p] link (%08x) ", space, "", td,
|
||||||
|
hc32_to_cpu(uhci, td->link));
|
||||||
out += sprintf(out, "e%d %s%s%s%s%s%s%s%s%s%sLength=%x ",
|
out += sprintf(out, "e%d %s%s%s%s%s%s%s%s%s%sLength=%x ",
|
||||||
((status >> 27) & 3),
|
((status >> 27) & 3),
|
||||||
(status & TD_CTRL_SPD) ? "SPD " : "",
|
(status & TD_CTRL_SPD) ? "SPD " : "",
|
||||||
@@ -63,7 +65,7 @@ static int uhci_show_td(struct uhci_td *td, char *buf, int len, int space)
|
|||||||
(status & TD_CTRL_BITSTUFF) ? "BitStuff " : "",
|
(status & TD_CTRL_BITSTUFF) ? "BitStuff " : "",
|
||||||
status & 0x7ff);
|
status & 0x7ff);
|
||||||
|
|
||||||
token = td_token(td);
|
token = td_token(uhci, td);
|
||||||
switch (uhci_packetid(token)) {
|
switch (uhci_packetid(token)) {
|
||||||
case USB_PID_SETUP:
|
case USB_PID_SETUP:
|
||||||
spid = "SETUP";
|
spid = "SETUP";
|
||||||
@@ -86,12 +88,13 @@ static int uhci_show_td(struct uhci_td *td, char *buf, int len, int space)
|
|||||||
(token >> 8) & 127,
|
(token >> 8) & 127,
|
||||||
(token & 0xff),
|
(token & 0xff),
|
||||||
spid);
|
spid);
|
||||||
out += sprintf(out, "(buf=%08x)\n", le32_to_cpu(td->buffer));
|
out += sprintf(out, "(buf=%08x)\n", hc32_to_cpu(uhci, td->buffer));
|
||||||
|
|
||||||
return out - buf;
|
return out - buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int uhci_show_urbp(struct urb_priv *urbp, char *buf, int len, int space)
|
static int uhci_show_urbp(struct uhci_hcd *uhci, struct urb_priv *urbp,
|
||||||
|
char *buf, int len, int space)
|
||||||
{
|
{
|
||||||
char *out = buf;
|
char *out = buf;
|
||||||
struct uhci_td *td;
|
struct uhci_td *td;
|
||||||
@@ -130,9 +133,10 @@ static int uhci_show_urbp(struct urb_priv *urbp, char *buf, int len, int space)
|
|||||||
if (urbp->qh->type != USB_ENDPOINT_XFER_ISOC &&
|
if (urbp->qh->type != USB_ENDPOINT_XFER_ISOC &&
|
||||||
(++i <= 10 || debug > 2)) {
|
(++i <= 10 || debug > 2)) {
|
||||||
out += sprintf(out, "%*s%d: ", space + 2, "", i);
|
out += sprintf(out, "%*s%d: ", space + 2, "", i);
|
||||||
out += uhci_show_td(td, out, len - (out - buf), 0);
|
out += uhci_show_td(uhci, td, out,
|
||||||
|
len - (out - buf), 0);
|
||||||
} else {
|
} else {
|
||||||
if (td_status(td) & TD_CTRL_ACTIVE)
|
if (td_status(uhci, td) & TD_CTRL_ACTIVE)
|
||||||
++nactive;
|
++nactive;
|
||||||
else
|
else
|
||||||
++ninactive;
|
++ninactive;
|
||||||
@@ -151,7 +155,7 @@ static int uhci_show_qh(struct uhci_hcd *uhci,
|
|||||||
{
|
{
|
||||||
char *out = buf;
|
char *out = buf;
|
||||||
int i, nurbs;
|
int i, nurbs;
|
||||||
__le32 element = qh_element(qh);
|
__hc32 element = qh_element(qh);
|
||||||
char *qtype;
|
char *qtype;
|
||||||
|
|
||||||
/* Try to make sure there's enough memory */
|
/* Try to make sure there's enough memory */
|
||||||
@@ -168,7 +172,8 @@ static int uhci_show_qh(struct uhci_hcd *uhci,
|
|||||||
|
|
||||||
out += sprintf(out, "%*s[%p] %s QH link (%08x) element (%08x)\n",
|
out += sprintf(out, "%*s[%p] %s QH link (%08x) element (%08x)\n",
|
||||||
space, "", qh, qtype,
|
space, "", qh, qtype,
|
||||||
le32_to_cpu(qh->link), le32_to_cpu(element));
|
hc32_to_cpu(uhci, qh->link),
|
||||||
|
hc32_to_cpu(uhci, element));
|
||||||
if (qh->type == USB_ENDPOINT_XFER_ISOC)
|
if (qh->type == USB_ENDPOINT_XFER_ISOC)
|
||||||
out += sprintf(out, "%*s period %d phase %d load %d us, "
|
out += sprintf(out, "%*s period %d phase %d load %d us, "
|
||||||
"frame %x desc [%p]\n",
|
"frame %x desc [%p]\n",
|
||||||
@@ -178,22 +183,22 @@ static int uhci_show_qh(struct uhci_hcd *uhci,
|
|||||||
out += sprintf(out, "%*s period %d phase %d load %d us\n",
|
out += sprintf(out, "%*s period %d phase %d load %d us\n",
|
||||||
space, "", qh->period, qh->phase, qh->load);
|
space, "", qh->period, qh->phase, qh->load);
|
||||||
|
|
||||||
if (element & UHCI_PTR_QH)
|
if (element & UHCI_PTR_QH(uhci))
|
||||||
out += sprintf(out, "%*s Element points to QH (bug?)\n", space, "");
|
out += sprintf(out, "%*s Element points to QH (bug?)\n", space, "");
|
||||||
|
|
||||||
if (element & UHCI_PTR_DEPTH)
|
if (element & UHCI_PTR_DEPTH(uhci))
|
||||||
out += sprintf(out, "%*s Depth traverse\n", space, "");
|
out += sprintf(out, "%*s Depth traverse\n", space, "");
|
||||||
|
|
||||||
if (element & cpu_to_le32(8))
|
if (element & cpu_to_hc32(uhci, 8))
|
||||||
out += sprintf(out, "%*s Bit 3 set (bug?)\n", space, "");
|
out += sprintf(out, "%*s Bit 3 set (bug?)\n", space, "");
|
||||||
|
|
||||||
if (!(element & ~(UHCI_PTR_QH | UHCI_PTR_DEPTH)))
|
if (!(element & ~(UHCI_PTR_QH(uhci) | UHCI_PTR_DEPTH(uhci))))
|
||||||
out += sprintf(out, "%*s Element is NULL (bug?)\n", space, "");
|
out += sprintf(out, "%*s Element is NULL (bug?)\n", space, "");
|
||||||
|
|
||||||
if (list_empty(&qh->queue)) {
|
if (list_empty(&qh->queue)) {
|
||||||
out += sprintf(out, "%*s queue is empty\n", space, "");
|
out += sprintf(out, "%*s queue is empty\n", space, "");
|
||||||
if (qh == uhci->skel_async_qh)
|
if (qh == uhci->skel_async_qh)
|
||||||
out += uhci_show_td(uhci->term_td, out,
|
out += uhci_show_td(uhci, uhci->term_td, out,
|
||||||
len - (out - buf), 0);
|
len - (out - buf), 0);
|
||||||
} else {
|
} else {
|
||||||
struct urb_priv *urbp = list_entry(qh->queue.next,
|
struct urb_priv *urbp = list_entry(qh->queue.next,
|
||||||
@@ -201,13 +206,13 @@ static int uhci_show_qh(struct uhci_hcd *uhci,
|
|||||||
struct uhci_td *td = list_entry(urbp->td_list.next,
|
struct uhci_td *td = list_entry(urbp->td_list.next,
|
||||||
struct uhci_td, list);
|
struct uhci_td, list);
|
||||||
|
|
||||||
if (element != LINK_TO_TD(td))
|
if (element != LINK_TO_TD(uhci, td))
|
||||||
out += sprintf(out, "%*s Element != First TD\n",
|
out += sprintf(out, "%*s Element != First TD\n",
|
||||||
space, "");
|
space, "");
|
||||||
i = nurbs = 0;
|
i = nurbs = 0;
|
||||||
list_for_each_entry(urbp, &qh->queue, node) {
|
list_for_each_entry(urbp, &qh->queue, node) {
|
||||||
if (++i <= 10)
|
if (++i <= 10)
|
||||||
out += uhci_show_urbp(urbp, out,
|
out += uhci_show_urbp(uhci, urbp, out,
|
||||||
len - (out - buf), space + 2);
|
len - (out - buf), space + 2);
|
||||||
else
|
else
|
||||||
++nurbs;
|
++nurbs;
|
||||||
@@ -219,7 +224,8 @@ static int uhci_show_qh(struct uhci_hcd *uhci,
|
|||||||
|
|
||||||
if (qh->dummy_td) {
|
if (qh->dummy_td) {
|
||||||
out += sprintf(out, "%*s Dummy TD\n", space, "");
|
out += sprintf(out, "%*s Dummy TD\n", space, "");
|
||||||
out += uhci_show_td(qh->dummy_td, out, len - (out - buf), 0);
|
out += uhci_show_td(uhci, qh->dummy_td, out,
|
||||||
|
len - (out - buf), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
return out - buf;
|
return out - buf;
|
||||||
@@ -346,8 +352,8 @@ static int uhci_sprint_schedule(struct uhci_hcd *uhci, char *buf, int len)
|
|||||||
struct uhci_td *td;
|
struct uhci_td *td;
|
||||||
struct list_head *tmp, *head;
|
struct list_head *tmp, *head;
|
||||||
int nframes, nerrs;
|
int nframes, nerrs;
|
||||||
__le32 link;
|
__hc32 link;
|
||||||
__le32 fsbr_link;
|
__hc32 fsbr_link;
|
||||||
|
|
||||||
static const char * const qh_names[] = {
|
static const char * const qh_names[] = {
|
||||||
"unlink", "iso", "int128", "int64", "int32", "int16",
|
"unlink", "iso", "int128", "int64", "int32", "int16",
|
||||||
@@ -375,7 +381,7 @@ static int uhci_sprint_schedule(struct uhci_hcd *uhci, char *buf, int len)
|
|||||||
nframes = 10;
|
nframes = 10;
|
||||||
nerrs = 0;
|
nerrs = 0;
|
||||||
for (i = 0; i < UHCI_NUMFRAMES; ++i) {
|
for (i = 0; i < UHCI_NUMFRAMES; ++i) {
|
||||||
__le32 qh_dma;
|
__hc32 qh_dma;
|
||||||
|
|
||||||
j = 0;
|
j = 0;
|
||||||
td = uhci->frame_cpu[i];
|
td = uhci->frame_cpu[i];
|
||||||
@@ -385,7 +391,7 @@ static int uhci_sprint_schedule(struct uhci_hcd *uhci, char *buf, int len)
|
|||||||
|
|
||||||
if (nframes > 0) {
|
if (nframes > 0) {
|
||||||
out += sprintf(out, "- Frame %d -> (%08x)\n",
|
out += sprintf(out, "- Frame %d -> (%08x)\n",
|
||||||
i, le32_to_cpu(link));
|
i, hc32_to_cpu(uhci, link));
|
||||||
j = 1;
|
j = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -394,7 +400,7 @@ static int uhci_sprint_schedule(struct uhci_hcd *uhci, char *buf, int len)
|
|||||||
do {
|
do {
|
||||||
td = list_entry(tmp, struct uhci_td, fl_list);
|
td = list_entry(tmp, struct uhci_td, fl_list);
|
||||||
tmp = tmp->next;
|
tmp = tmp->next;
|
||||||
if (link != LINK_TO_TD(td)) {
|
if (link != LINK_TO_TD(uhci, td)) {
|
||||||
if (nframes > 0)
|
if (nframes > 0)
|
||||||
out += sprintf(out, " link does "
|
out += sprintf(out, " link does "
|
||||||
"not match list entry!\n");
|
"not match list entry!\n");
|
||||||
@@ -402,7 +408,7 @@ static int uhci_sprint_schedule(struct uhci_hcd *uhci, char *buf, int len)
|
|||||||
++nerrs;
|
++nerrs;
|
||||||
}
|
}
|
||||||
if (nframes > 0)
|
if (nframes > 0)
|
||||||
out += uhci_show_td(td, out,
|
out += uhci_show_td(uhci, td, out,
|
||||||
len - (out - buf), 4);
|
len - (out - buf), 4);
|
||||||
link = td->link;
|
link = td->link;
|
||||||
} while (tmp != head);
|
} while (tmp != head);
|
||||||
@@ -414,11 +420,12 @@ check_link:
|
|||||||
if (!j) {
|
if (!j) {
|
||||||
out += sprintf(out,
|
out += sprintf(out,
|
||||||
"- Frame %d -> (%08x)\n",
|
"- Frame %d -> (%08x)\n",
|
||||||
i, le32_to_cpu(link));
|
i, hc32_to_cpu(uhci, link));
|
||||||
j = 1;
|
j = 1;
|
||||||
}
|
}
|
||||||
out += sprintf(out, " link does not match "
|
out += sprintf(out, " link does not match "
|
||||||
"QH (%08x)!\n", le32_to_cpu(qh_dma));
|
"QH (%08x)!\n",
|
||||||
|
hc32_to_cpu(uhci, qh_dma));
|
||||||
} else
|
} else
|
||||||
++nerrs;
|
++nerrs;
|
||||||
}
|
}
|
||||||
@@ -439,11 +446,11 @@ check_link:
|
|||||||
|
|
||||||
/* Last QH is the Terminating QH, it's different */
|
/* Last QH is the Terminating QH, it's different */
|
||||||
if (i == SKEL_TERM) {
|
if (i == SKEL_TERM) {
|
||||||
if (qh_element(qh) != LINK_TO_TD(uhci->term_td))
|
if (qh_element(qh) != LINK_TO_TD(uhci, uhci->term_td))
|
||||||
out += sprintf(out, " skel_term_qh element is not set to term_td!\n");
|
out += sprintf(out, " skel_term_qh element is not set to term_td!\n");
|
||||||
link = fsbr_link;
|
link = fsbr_link;
|
||||||
if (!link)
|
if (!link)
|
||||||
link = LINK_TO_QH(uhci->skel_term_qh);
|
link = LINK_TO_QH(uhci, uhci->skel_term_qh);
|
||||||
goto check_qh_link;
|
goto check_qh_link;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -457,20 +464,20 @@ check_link:
|
|||||||
out += uhci_show_qh(uhci, qh, out,
|
out += uhci_show_qh(uhci, qh, out,
|
||||||
len - (out - buf), 4);
|
len - (out - buf), 4);
|
||||||
if (!fsbr_link && qh->skel >= SKEL_FSBR)
|
if (!fsbr_link && qh->skel >= SKEL_FSBR)
|
||||||
fsbr_link = LINK_TO_QH(qh);
|
fsbr_link = LINK_TO_QH(uhci, qh);
|
||||||
}
|
}
|
||||||
if ((cnt -= 10) > 0)
|
if ((cnt -= 10) > 0)
|
||||||
out += sprintf(out, " Skipped %d QHs\n", cnt);
|
out += sprintf(out, " Skipped %d QHs\n", cnt);
|
||||||
|
|
||||||
link = UHCI_PTR_TERM;
|
link = UHCI_PTR_TERM(uhci);
|
||||||
if (i <= SKEL_ISO)
|
if (i <= SKEL_ISO)
|
||||||
;
|
;
|
||||||
else if (i < SKEL_ASYNC)
|
else if (i < SKEL_ASYNC)
|
||||||
link = LINK_TO_QH(uhci->skel_async_qh);
|
link = LINK_TO_QH(uhci, uhci->skel_async_qh);
|
||||||
else if (!uhci->fsbr_is_on)
|
else if (!uhci->fsbr_is_on)
|
||||||
;
|
;
|
||||||
else
|
else
|
||||||
link = LINK_TO_QH(uhci->skel_term_qh);
|
link = LINK_TO_QH(uhci, uhci->skel_term_qh);
|
||||||
check_qh_link:
|
check_qh_link:
|
||||||
if (qh->link != link)
|
if (qh->link != link)
|
||||||
out += sprintf(out, " last QH not linked to next skeleton!\n");
|
out += sprintf(out, " last QH not linked to next skeleton!\n");
|
||||||
|
@@ -92,7 +92,7 @@ static void uhci_get_current_frame_number(struct uhci_hcd *uhci);
|
|||||||
/*
|
/*
|
||||||
* Calculate the link pointer DMA value for the first Skeleton QH in a frame.
|
* Calculate the link pointer DMA value for the first Skeleton QH in a frame.
|
||||||
*/
|
*/
|
||||||
static __le32 uhci_frame_skel_link(struct uhci_hcd *uhci, int frame)
|
static __hc32 uhci_frame_skel_link(struct uhci_hcd *uhci, int frame)
|
||||||
{
|
{
|
||||||
int skelnum;
|
int skelnum;
|
||||||
|
|
||||||
@@ -114,7 +114,7 @@ static __le32 uhci_frame_skel_link(struct uhci_hcd *uhci, int frame)
|
|||||||
skelnum = 8 - (int) __ffs(frame | UHCI_NUMFRAMES);
|
skelnum = 8 - (int) __ffs(frame | UHCI_NUMFRAMES);
|
||||||
if (skelnum <= 1)
|
if (skelnum <= 1)
|
||||||
skelnum = 9;
|
skelnum = 9;
|
||||||
return LINK_TO_QH(uhci->skelqh[skelnum]);
|
return LINK_TO_QH(uhci, uhci->skelqh[skelnum]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#include "uhci-debug.c"
|
#include "uhci-debug.c"
|
||||||
@@ -630,16 +630,16 @@ static int uhci_start(struct usb_hcd *hcd)
|
|||||||
* 8 Interrupt queues; link all higher int queues to int1 = async
|
* 8 Interrupt queues; link all higher int queues to int1 = async
|
||||||
*/
|
*/
|
||||||
for (i = SKEL_ISO + 1; i < SKEL_ASYNC; ++i)
|
for (i = SKEL_ISO + 1; i < SKEL_ASYNC; ++i)
|
||||||
uhci->skelqh[i]->link = LINK_TO_QH(uhci->skel_async_qh);
|
uhci->skelqh[i]->link = LINK_TO_QH(uhci, uhci->skel_async_qh);
|
||||||
uhci->skel_async_qh->link = UHCI_PTR_TERM;
|
uhci->skel_async_qh->link = UHCI_PTR_TERM(uhci);
|
||||||
uhci->skel_term_qh->link = LINK_TO_QH(uhci->skel_term_qh);
|
uhci->skel_term_qh->link = LINK_TO_QH(uhci, uhci->skel_term_qh);
|
||||||
|
|
||||||
/* This dummy TD is to work around a bug in Intel PIIX controllers */
|
/* This dummy TD is to work around a bug in Intel PIIX controllers */
|
||||||
uhci_fill_td(uhci->term_td, 0, uhci_explen(0) |
|
uhci_fill_td(uhci, uhci->term_td, 0, uhci_explen(0) |
|
||||||
(0x7f << TD_TOKEN_DEVADDR_SHIFT) | USB_PID_IN, 0);
|
(0x7f << TD_TOKEN_DEVADDR_SHIFT) | USB_PID_IN, 0);
|
||||||
uhci->term_td->link = UHCI_PTR_TERM;
|
uhci->term_td->link = UHCI_PTR_TERM(uhci);
|
||||||
uhci->skel_async_qh->element = uhci->skel_term_qh->element =
|
uhci->skel_async_qh->element = uhci->skel_term_qh->element =
|
||||||
LINK_TO_TD(uhci->term_td);
|
LINK_TO_TD(uhci, uhci->term_td);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Fill the frame list: make all entries point to the proper
|
* Fill the frame list: make all entries point to the proper
|
||||||
|
@@ -78,11 +78,11 @@
|
|||||||
#define USBPORT1EN 0x01
|
#define USBPORT1EN 0x01
|
||||||
#define USBPORT2EN 0x02
|
#define USBPORT2EN 0x02
|
||||||
|
|
||||||
#define UHCI_PTR_BITS cpu_to_le32(0x000F)
|
#define UHCI_PTR_BITS(uhci) cpu_to_hc32((uhci), 0x000F)
|
||||||
#define UHCI_PTR_TERM cpu_to_le32(0x0001)
|
#define UHCI_PTR_TERM(uhci) cpu_to_hc32((uhci), 0x0001)
|
||||||
#define UHCI_PTR_QH cpu_to_le32(0x0002)
|
#define UHCI_PTR_QH(uhci) cpu_to_hc32((uhci), 0x0002)
|
||||||
#define UHCI_PTR_DEPTH cpu_to_le32(0x0004)
|
#define UHCI_PTR_DEPTH(uhci) cpu_to_hc32((uhci), 0x0004)
|
||||||
#define UHCI_PTR_BREADTH cpu_to_le32(0x0000)
|
#define UHCI_PTR_BREADTH(uhci) cpu_to_hc32((uhci), 0x0000)
|
||||||
|
|
||||||
#define UHCI_NUMFRAMES 1024 /* in the frame list [array] */
|
#define UHCI_NUMFRAMES 1024 /* in the frame list [array] */
|
||||||
#define UHCI_MAX_SOF_NUMBER 2047 /* in an SOF packet */
|
#define UHCI_MAX_SOF_NUMBER 2047 /* in an SOF packet */
|
||||||
@@ -98,6 +98,22 @@
|
|||||||
#define QH_WAIT_TIMEOUT msecs_to_jiffies(200)
|
#define QH_WAIT_TIMEOUT msecs_to_jiffies(200)
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* __hc32 and __hc16 are "Host Controller" types, they may be equivalent to
|
||||||
|
* __leXX (normally) or __beXX (given UHCI_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_UHCI_BIG_ENDIAN_DESC
|
||||||
|
typedef __u32 __bitwise __hc32;
|
||||||
|
typedef __u16 __bitwise __hc16;
|
||||||
|
#else
|
||||||
|
#define __hc32 __le32
|
||||||
|
#define __hc16 __le16
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Queue Headers
|
* Queue Headers
|
||||||
*/
|
*/
|
||||||
@@ -130,8 +146,8 @@
|
|||||||
|
|
||||||
struct uhci_qh {
|
struct uhci_qh {
|
||||||
/* Hardware fields */
|
/* Hardware fields */
|
||||||
__le32 link; /* Next QH in the schedule */
|
__hc32 link; /* Next QH in the schedule */
|
||||||
__le32 element; /* Queue element (TD) pointer */
|
__hc32 element; /* Queue element (TD) pointer */
|
||||||
|
|
||||||
/* Software fields */
|
/* Software fields */
|
||||||
dma_addr_t dma_handle;
|
dma_addr_t dma_handle;
|
||||||
@@ -170,7 +186,8 @@ struct uhci_qh {
|
|||||||
*/
|
*/
|
||||||
#define qh_element(qh) ACCESS_ONCE((qh)->element)
|
#define qh_element(qh) ACCESS_ONCE((qh)->element)
|
||||||
|
|
||||||
#define LINK_TO_QH(qh) (UHCI_PTR_QH | cpu_to_le32((qh)->dma_handle))
|
#define LINK_TO_QH(uhci, qh) (UHCI_PTR_QH((uhci)) | \
|
||||||
|
cpu_to_hc32((uhci), (qh)->dma_handle))
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -207,7 +224,7 @@ struct uhci_qh {
|
|||||||
/*
|
/*
|
||||||
* for TD <info>: (a.k.a. Token)
|
* for TD <info>: (a.k.a. Token)
|
||||||
*/
|
*/
|
||||||
#define td_token(td) le32_to_cpu((td)->token)
|
#define td_token(uhci, td) hc32_to_cpu((uhci), (td)->token)
|
||||||
#define TD_TOKEN_DEVADDR_SHIFT 8
|
#define TD_TOKEN_DEVADDR_SHIFT 8
|
||||||
#define TD_TOKEN_TOGGLE_SHIFT 19
|
#define TD_TOKEN_TOGGLE_SHIFT 19
|
||||||
#define TD_TOKEN_TOGGLE (1 << 19)
|
#define TD_TOKEN_TOGGLE (1 << 19)
|
||||||
@@ -240,10 +257,10 @@ struct uhci_qh {
|
|||||||
*/
|
*/
|
||||||
struct uhci_td {
|
struct uhci_td {
|
||||||
/* Hardware fields */
|
/* Hardware fields */
|
||||||
__le32 link;
|
__hc32 link;
|
||||||
__le32 status;
|
__hc32 status;
|
||||||
__le32 token;
|
__hc32 token;
|
||||||
__le32 buffer;
|
__hc32 buffer;
|
||||||
|
|
||||||
/* Software fields */
|
/* Software fields */
|
||||||
dma_addr_t dma_handle;
|
dma_addr_t dma_handle;
|
||||||
@@ -258,9 +275,10 @@ struct uhci_td {
|
|||||||
* We need a special accessor for the control/status word because it is
|
* We need a special accessor for the control/status word because it is
|
||||||
* subject to asynchronous updates by the controller.
|
* subject to asynchronous updates by the controller.
|
||||||
*/
|
*/
|
||||||
#define td_status(td) le32_to_cpu(ACCESS_ONCE((td)->status))
|
#define td_status(uhci, td) hc32_to_cpu((uhci), \
|
||||||
|
ACCESS_ONCE((td)->status))
|
||||||
|
|
||||||
#define LINK_TO_TD(td) (cpu_to_le32((td)->dma_handle))
|
#define LINK_TO_TD(uhci, td) (cpu_to_hc32((uhci), (td)->dma_handle))
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -383,7 +401,7 @@ struct uhci_hcd {
|
|||||||
spinlock_t lock;
|
spinlock_t lock;
|
||||||
|
|
||||||
dma_addr_t frame_dma_handle; /* Hardware frame list */
|
dma_addr_t frame_dma_handle; /* Hardware frame list */
|
||||||
__le32 *frame;
|
__hc32 *frame;
|
||||||
void **frame_cpu; /* CPU's frame list */
|
void **frame_cpu; /* CPU's frame list */
|
||||||
|
|
||||||
enum uhci_rh_state rh_state;
|
enum uhci_rh_state rh_state;
|
||||||
@@ -412,6 +430,7 @@ struct uhci_hcd {
|
|||||||
unsigned int oc_low:1; /* OverCurrent bit active low */
|
unsigned int oc_low:1; /* OverCurrent bit active low */
|
||||||
unsigned int wait_for_hp:1; /* Wait for HP port reset */
|
unsigned int wait_for_hp:1; /* Wait for HP port reset */
|
||||||
unsigned int big_endian_mmio:1; /* Big endian registers */
|
unsigned int big_endian_mmio:1; /* Big endian registers */
|
||||||
|
unsigned int big_endian_desc:1; /* Big endian descriptors */
|
||||||
|
|
||||||
/* Support for port suspend/resume/reset */
|
/* Support for port suspend/resume/reset */
|
||||||
unsigned long port_c_suspend; /* Bit-arrays of ports */
|
unsigned long port_c_suspend; /* Bit-arrays of ports */
|
||||||
@@ -603,4 +622,43 @@ static inline void uhci_writeb(const struct uhci_hcd *uhci, u8 val, int reg)
|
|||||||
}
|
}
|
||||||
#endif /* CONFIG_USB_UHCI_SUPPORT_NON_PCI_HC */
|
#endif /* CONFIG_USB_UHCI_SUPPORT_NON_PCI_HC */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The GRLIB GRUSBHC controller can use big endian format for its descriptors.
|
||||||
|
*
|
||||||
|
* UHCI controllers accessed through PCI work normally (little-endian
|
||||||
|
* everywhere), so we don't bother supporting a BE-only mode.
|
||||||
|
*/
|
||||||
|
#ifdef CONFIG_USB_UHCI_BIG_ENDIAN_DESC
|
||||||
|
#define uhci_big_endian_desc(u) ((u)->big_endian_desc)
|
||||||
|
|
||||||
|
/* cpu to uhci */
|
||||||
|
static inline __hc32 cpu_to_hc32(const struct uhci_hcd *uhci, const u32 x)
|
||||||
|
{
|
||||||
|
return uhci_big_endian_desc(uhci)
|
||||||
|
? (__force __hc32)cpu_to_be32(x)
|
||||||
|
: (__force __hc32)cpu_to_le32(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* uhci to cpu */
|
||||||
|
static inline u32 hc32_to_cpu(const struct uhci_hcd *uhci, const __hc32 x)
|
||||||
|
{
|
||||||
|
return uhci_big_endian_desc(uhci)
|
||||||
|
? be32_to_cpu((__force __be32)x)
|
||||||
|
: le32_to_cpu((__force __le32)x);
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
/* cpu to uhci */
|
||||||
|
static inline __hc32 cpu_to_hc32(const struct uhci_hcd *uhci, const u32 x)
|
||||||
|
{
|
||||||
|
return cpu_to_le32(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* uhci to cpu */
|
||||||
|
static inline u32 hc32_to_cpu(const struct uhci_hcd *uhci, const __hc32 x)
|
||||||
|
{
|
||||||
|
return le32_to_cpu(x);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@@ -29,12 +29,12 @@ static void uhci_set_next_interrupt(struct uhci_hcd *uhci)
|
|||||||
{
|
{
|
||||||
if (uhci->is_stopped)
|
if (uhci->is_stopped)
|
||||||
mod_timer(&uhci_to_hcd(uhci)->rh_timer, jiffies);
|
mod_timer(&uhci_to_hcd(uhci)->rh_timer, jiffies);
|
||||||
uhci->term_td->status |= cpu_to_le32(TD_CTRL_IOC);
|
uhci->term_td->status |= cpu_to_hc32(uhci, TD_CTRL_IOC);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void uhci_clear_next_interrupt(struct uhci_hcd *uhci)
|
static inline void uhci_clear_next_interrupt(struct uhci_hcd *uhci)
|
||||||
{
|
{
|
||||||
uhci->term_td->status &= ~cpu_to_le32(TD_CTRL_IOC);
|
uhci->term_td->status &= ~cpu_to_hc32(uhci, TD_CTRL_IOC);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -53,7 +53,7 @@ static void uhci_fsbr_on(struct uhci_hcd *uhci)
|
|||||||
uhci->fsbr_is_on = 1;
|
uhci->fsbr_is_on = 1;
|
||||||
lqh = list_entry(uhci->skel_async_qh->node.prev,
|
lqh = list_entry(uhci->skel_async_qh->node.prev,
|
||||||
struct uhci_qh, node);
|
struct uhci_qh, node);
|
||||||
lqh->link = LINK_TO_QH(uhci->skel_term_qh);
|
lqh->link = LINK_TO_QH(uhci, uhci->skel_term_qh);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void uhci_fsbr_off(struct uhci_hcd *uhci)
|
static void uhci_fsbr_off(struct uhci_hcd *uhci)
|
||||||
@@ -65,7 +65,7 @@ static void uhci_fsbr_off(struct uhci_hcd *uhci)
|
|||||||
uhci->fsbr_is_on = 0;
|
uhci->fsbr_is_on = 0;
|
||||||
lqh = list_entry(uhci->skel_async_qh->node.prev,
|
lqh = list_entry(uhci->skel_async_qh->node.prev,
|
||||||
struct uhci_qh, node);
|
struct uhci_qh, node);
|
||||||
lqh->link = UHCI_PTR_TERM;
|
lqh->link = UHCI_PTR_TERM(uhci);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void uhci_add_fsbr(struct uhci_hcd *uhci, struct urb *urb)
|
static void uhci_add_fsbr(struct uhci_hcd *uhci, struct urb *urb)
|
||||||
@@ -131,12 +131,12 @@ static void uhci_free_td(struct uhci_hcd *uhci, struct uhci_td *td)
|
|||||||
dma_pool_free(uhci->td_pool, td, td->dma_handle);
|
dma_pool_free(uhci->td_pool, td, td->dma_handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void uhci_fill_td(struct uhci_td *td, u32 status,
|
static inline void uhci_fill_td(struct uhci_hcd *uhci, struct uhci_td *td,
|
||||||
u32 token, u32 buffer)
|
u32 status, u32 token, u32 buffer)
|
||||||
{
|
{
|
||||||
td->status = cpu_to_le32(status);
|
td->status = cpu_to_hc32(uhci, status);
|
||||||
td->token = cpu_to_le32(token);
|
td->token = cpu_to_hc32(uhci, token);
|
||||||
td->buffer = cpu_to_le32(buffer);
|
td->buffer = cpu_to_hc32(uhci, buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void uhci_add_td_to_urbp(struct uhci_td *td, struct urb_priv *urbp)
|
static void uhci_add_td_to_urbp(struct uhci_td *td, struct urb_priv *urbp)
|
||||||
@@ -170,11 +170,11 @@ static inline void uhci_insert_td_in_frame_list(struct uhci_hcd *uhci,
|
|||||||
|
|
||||||
td->link = ltd->link;
|
td->link = ltd->link;
|
||||||
wmb();
|
wmb();
|
||||||
ltd->link = LINK_TO_TD(td);
|
ltd->link = LINK_TO_TD(uhci, td);
|
||||||
} else {
|
} else {
|
||||||
td->link = uhci->frame[framenum];
|
td->link = uhci->frame[framenum];
|
||||||
wmb();
|
wmb();
|
||||||
uhci->frame[framenum] = LINK_TO_TD(td);
|
uhci->frame[framenum] = LINK_TO_TD(uhci, td);
|
||||||
uhci->frame_cpu[framenum] = td;
|
uhci->frame_cpu[framenum] = td;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -198,7 +198,7 @@ static inline void uhci_remove_td_from_frame_list(struct uhci_hcd *uhci,
|
|||||||
ntd = list_entry(td->fl_list.next,
|
ntd = list_entry(td->fl_list.next,
|
||||||
struct uhci_td,
|
struct uhci_td,
|
||||||
fl_list);
|
fl_list);
|
||||||
uhci->frame[td->frame] = LINK_TO_TD(ntd);
|
uhci->frame[td->frame] = LINK_TO_TD(uhci, ntd);
|
||||||
uhci->frame_cpu[td->frame] = ntd;
|
uhci->frame_cpu[td->frame] = ntd;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -255,8 +255,8 @@ static struct uhci_qh *uhci_alloc_qh(struct uhci_hcd *uhci,
|
|||||||
memset(qh, 0, sizeof(*qh));
|
memset(qh, 0, sizeof(*qh));
|
||||||
qh->dma_handle = dma_handle;
|
qh->dma_handle = dma_handle;
|
||||||
|
|
||||||
qh->element = UHCI_PTR_TERM;
|
qh->element = UHCI_PTR_TERM(uhci);
|
||||||
qh->link = UHCI_PTR_TERM;
|
qh->link = UHCI_PTR_TERM(uhci);
|
||||||
|
|
||||||
INIT_LIST_HEAD(&qh->queue);
|
INIT_LIST_HEAD(&qh->queue);
|
||||||
INIT_LIST_HEAD(&qh->node);
|
INIT_LIST_HEAD(&qh->node);
|
||||||
@@ -348,9 +348,9 @@ static int uhci_cleanup_queue(struct uhci_hcd *uhci, struct uhci_qh *qh,
|
|||||||
|
|
||||||
/* If the QH element pointer is UHCI_PTR_TERM then then currently
|
/* If the QH element pointer is UHCI_PTR_TERM then then currently
|
||||||
* executing URB has already been unlinked, so this one isn't it. */
|
* executing URB has already been unlinked, so this one isn't it. */
|
||||||
if (qh_element(qh) == UHCI_PTR_TERM)
|
if (qh_element(qh) == UHCI_PTR_TERM(uhci))
|
||||||
goto done;
|
goto done;
|
||||||
qh->element = UHCI_PTR_TERM;
|
qh->element = UHCI_PTR_TERM(uhci);
|
||||||
|
|
||||||
/* Control pipes don't have to worry about toggles */
|
/* Control pipes don't have to worry about toggles */
|
||||||
if (qh->type == USB_ENDPOINT_XFER_CONTROL)
|
if (qh->type == USB_ENDPOINT_XFER_CONTROL)
|
||||||
@@ -360,7 +360,7 @@ static int uhci_cleanup_queue(struct uhci_hcd *uhci, struct uhci_qh *qh,
|
|||||||
WARN_ON(list_empty(&urbp->td_list));
|
WARN_ON(list_empty(&urbp->td_list));
|
||||||
td = list_entry(urbp->td_list.next, struct uhci_td, list);
|
td = list_entry(urbp->td_list.next, struct uhci_td, list);
|
||||||
qh->needs_fixup = 1;
|
qh->needs_fixup = 1;
|
||||||
qh->initial_toggle = uhci_toggle(td_token(td));
|
qh->initial_toggle = uhci_toggle(td_token(uhci, td));
|
||||||
|
|
||||||
done:
|
done:
|
||||||
return ret;
|
return ret;
|
||||||
@@ -370,7 +370,8 @@ done:
|
|||||||
* Fix up the data toggles for URBs in a queue, when one of them
|
* Fix up the data toggles for URBs in a queue, when one of them
|
||||||
* terminates early (short transfer, error, or dequeued).
|
* terminates early (short transfer, error, or dequeued).
|
||||||
*/
|
*/
|
||||||
static void uhci_fixup_toggles(struct uhci_qh *qh, int skip_first)
|
static void uhci_fixup_toggles(struct uhci_hcd *uhci, struct uhci_qh *qh,
|
||||||
|
int skip_first)
|
||||||
{
|
{
|
||||||
struct urb_priv *urbp = NULL;
|
struct urb_priv *urbp = NULL;
|
||||||
struct uhci_td *td;
|
struct uhci_td *td;
|
||||||
@@ -384,7 +385,7 @@ static void uhci_fixup_toggles(struct uhci_qh *qh, int skip_first)
|
|||||||
|
|
||||||
/* When starting with the first URB, if the QH element pointer is
|
/* When starting with the first URB, if the QH element pointer is
|
||||||
* still valid then we know the URB's toggles are okay. */
|
* still valid then we know the URB's toggles are okay. */
|
||||||
else if (qh_element(qh) != UHCI_PTR_TERM)
|
else if (qh_element(qh) != UHCI_PTR_TERM(uhci))
|
||||||
toggle = 2;
|
toggle = 2;
|
||||||
|
|
||||||
/* Fix up the toggle for the URBs in the queue. Normally this
|
/* Fix up the toggle for the URBs in the queue. Normally this
|
||||||
@@ -396,15 +397,15 @@ static void uhci_fixup_toggles(struct uhci_qh *qh, int skip_first)
|
|||||||
/* If the first TD has the right toggle value, we don't
|
/* If the first TD has the right toggle value, we don't
|
||||||
* need to change any toggles in this URB */
|
* need to change any toggles in this URB */
|
||||||
td = list_entry(urbp->td_list.next, struct uhci_td, list);
|
td = list_entry(urbp->td_list.next, struct uhci_td, list);
|
||||||
if (toggle > 1 || uhci_toggle(td_token(td)) == toggle) {
|
if (toggle > 1 || uhci_toggle(td_token(uhci, td)) == toggle) {
|
||||||
td = list_entry(urbp->td_list.prev, struct uhci_td,
|
td = list_entry(urbp->td_list.prev, struct uhci_td,
|
||||||
list);
|
list);
|
||||||
toggle = uhci_toggle(td_token(td)) ^ 1;
|
toggle = uhci_toggle(td_token(uhci, td)) ^ 1;
|
||||||
|
|
||||||
/* Otherwise all the toggles in the URB have to be switched */
|
/* Otherwise all the toggles in the URB have to be switched */
|
||||||
} else {
|
} else {
|
||||||
list_for_each_entry(td, &urbp->td_list, list) {
|
list_for_each_entry(td, &urbp->td_list, list) {
|
||||||
td->token ^= cpu_to_le32(
|
td->token ^= cpu_to_hc32(uhci,
|
||||||
TD_TOKEN_TOGGLE);
|
TD_TOKEN_TOGGLE);
|
||||||
toggle ^= 1;
|
toggle ^= 1;
|
||||||
}
|
}
|
||||||
@@ -441,7 +442,7 @@ static void link_interrupt(struct uhci_hcd *uhci, struct uhci_qh *qh)
|
|||||||
pqh = list_entry(qh->node.prev, struct uhci_qh, node);
|
pqh = list_entry(qh->node.prev, struct uhci_qh, node);
|
||||||
qh->link = pqh->link;
|
qh->link = pqh->link;
|
||||||
wmb();
|
wmb();
|
||||||
pqh->link = LINK_TO_QH(qh);
|
pqh->link = LINK_TO_QH(uhci, qh);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -451,7 +452,7 @@ static void link_interrupt(struct uhci_hcd *uhci, struct uhci_qh *qh)
|
|||||||
static void link_async(struct uhci_hcd *uhci, struct uhci_qh *qh)
|
static void link_async(struct uhci_hcd *uhci, struct uhci_qh *qh)
|
||||||
{
|
{
|
||||||
struct uhci_qh *pqh;
|
struct uhci_qh *pqh;
|
||||||
__le32 link_to_new_qh;
|
__hc32 link_to_new_qh;
|
||||||
|
|
||||||
/* Find the predecessor QH for our new one and insert it in the list.
|
/* Find the predecessor QH for our new one and insert it in the list.
|
||||||
* The list of QHs is expected to be short, so linear search won't
|
* The list of QHs is expected to be short, so linear search won't
|
||||||
@@ -465,7 +466,7 @@ static void link_async(struct uhci_hcd *uhci, struct uhci_qh *qh)
|
|||||||
/* Link it into the schedule */
|
/* Link it into the schedule */
|
||||||
qh->link = pqh->link;
|
qh->link = pqh->link;
|
||||||
wmb();
|
wmb();
|
||||||
link_to_new_qh = LINK_TO_QH(qh);
|
link_to_new_qh = LINK_TO_QH(uhci, qh);
|
||||||
pqh->link = link_to_new_qh;
|
pqh->link = link_to_new_qh;
|
||||||
|
|
||||||
/* If this is now the first FSBR QH, link the terminating skeleton
|
/* If this is now the first FSBR QH, link the terminating skeleton
|
||||||
@@ -483,13 +484,13 @@ static void uhci_activate_qh(struct uhci_hcd *uhci, struct uhci_qh *qh)
|
|||||||
|
|
||||||
/* Set the element pointer if it isn't set already.
|
/* Set the element pointer if it isn't set already.
|
||||||
* This isn't needed for Isochronous queues, but it doesn't hurt. */
|
* This isn't needed for Isochronous queues, but it doesn't hurt. */
|
||||||
if (qh_element(qh) == UHCI_PTR_TERM) {
|
if (qh_element(qh) == UHCI_PTR_TERM(uhci)) {
|
||||||
struct urb_priv *urbp = list_entry(qh->queue.next,
|
struct urb_priv *urbp = list_entry(qh->queue.next,
|
||||||
struct urb_priv, node);
|
struct urb_priv, node);
|
||||||
struct uhci_td *td = list_entry(urbp->td_list.next,
|
struct uhci_td *td = list_entry(urbp->td_list.next,
|
||||||
struct uhci_td, list);
|
struct uhci_td, list);
|
||||||
|
|
||||||
qh->element = LINK_TO_TD(td);
|
qh->element = LINK_TO_TD(uhci, td);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Treat the queue as if it has just advanced */
|
/* Treat the queue as if it has just advanced */
|
||||||
@@ -533,7 +534,7 @@ static void unlink_interrupt(struct uhci_hcd *uhci, struct uhci_qh *qh)
|
|||||||
static void unlink_async(struct uhci_hcd *uhci, struct uhci_qh *qh)
|
static void unlink_async(struct uhci_hcd *uhci, struct uhci_qh *qh)
|
||||||
{
|
{
|
||||||
struct uhci_qh *pqh;
|
struct uhci_qh *pqh;
|
||||||
__le32 link_to_next_qh = qh->link;
|
__hc32 link_to_next_qh = qh->link;
|
||||||
|
|
||||||
pqh = list_entry(qh->node.prev, struct uhci_qh, node);
|
pqh = list_entry(qh->node.prev, struct uhci_qh, node);
|
||||||
pqh->link = link_to_next_qh;
|
pqh->link = link_to_next_qh;
|
||||||
@@ -757,8 +758,8 @@ static void uhci_free_urb_priv(struct uhci_hcd *uhci,
|
|||||||
/*
|
/*
|
||||||
* Map status to standard result codes
|
* Map status to standard result codes
|
||||||
*
|
*
|
||||||
* <status> is (td_status(td) & 0xF60000), a.k.a.
|
* <status> is (td_status(uhci, td) & 0xF60000), a.k.a.
|
||||||
* uhci_status_bits(td_status(td)).
|
* uhci_status_bits(td_status(uhci, td)).
|
||||||
* Note: <status> does not include the TD_CTRL_NAK bit.
|
* Note: <status> does not include the TD_CTRL_NAK bit.
|
||||||
* <dir_out> is True for output TDs and False for input TDs.
|
* <dir_out> is True for output TDs and False for input TDs.
|
||||||
*/
|
*/
|
||||||
@@ -794,7 +795,7 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb,
|
|||||||
int maxsze = le16_to_cpu(qh->hep->desc.wMaxPacketSize);
|
int maxsze = le16_to_cpu(qh->hep->desc.wMaxPacketSize);
|
||||||
int len = urb->transfer_buffer_length;
|
int len = urb->transfer_buffer_length;
|
||||||
dma_addr_t data = urb->transfer_dma;
|
dma_addr_t data = urb->transfer_dma;
|
||||||
__le32 *plink;
|
__hc32 *plink;
|
||||||
struct urb_priv *urbp = urb->hcpriv;
|
struct urb_priv *urbp = urb->hcpriv;
|
||||||
int skel;
|
int skel;
|
||||||
|
|
||||||
@@ -811,7 +812,7 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb,
|
|||||||
*/
|
*/
|
||||||
td = qh->dummy_td;
|
td = qh->dummy_td;
|
||||||
uhci_add_td_to_urbp(td, urbp);
|
uhci_add_td_to_urbp(td, urbp);
|
||||||
uhci_fill_td(td, status, destination | uhci_explen(8),
|
uhci_fill_td(uhci, td, status, destination | uhci_explen(8),
|
||||||
urb->setup_dma);
|
urb->setup_dma);
|
||||||
plink = &td->link;
|
plink = &td->link;
|
||||||
status |= TD_CTRL_ACTIVE;
|
status |= TD_CTRL_ACTIVE;
|
||||||
@@ -844,14 +845,14 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb,
|
|||||||
td = uhci_alloc_td(uhci);
|
td = uhci_alloc_td(uhci);
|
||||||
if (!td)
|
if (!td)
|
||||||
goto nomem;
|
goto nomem;
|
||||||
*plink = LINK_TO_TD(td);
|
*plink = LINK_TO_TD(uhci, td);
|
||||||
|
|
||||||
/* Alternate Data0/1 (start with Data1) */
|
/* Alternate Data0/1 (start with Data1) */
|
||||||
destination ^= TD_TOKEN_TOGGLE;
|
destination ^= TD_TOKEN_TOGGLE;
|
||||||
|
|
||||||
uhci_add_td_to_urbp(td, urbp);
|
uhci_add_td_to_urbp(td, urbp);
|
||||||
uhci_fill_td(td, status, destination | uhci_explen(pktsze),
|
uhci_fill_td(uhci, td, status,
|
||||||
data);
|
destination | uhci_explen(pktsze), data);
|
||||||
plink = &td->link;
|
plink = &td->link;
|
||||||
|
|
||||||
data += pktsze;
|
data += pktsze;
|
||||||
@@ -864,14 +865,14 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb,
|
|||||||
td = uhci_alloc_td(uhci);
|
td = uhci_alloc_td(uhci);
|
||||||
if (!td)
|
if (!td)
|
||||||
goto nomem;
|
goto nomem;
|
||||||
*plink = LINK_TO_TD(td);
|
*plink = LINK_TO_TD(uhci, td);
|
||||||
|
|
||||||
/* Change direction for the status transaction */
|
/* Change direction for the status transaction */
|
||||||
destination ^= (USB_PID_IN ^ USB_PID_OUT);
|
destination ^= (USB_PID_IN ^ USB_PID_OUT);
|
||||||
destination |= TD_TOKEN_TOGGLE; /* End in Data1 */
|
destination |= TD_TOKEN_TOGGLE; /* End in Data1 */
|
||||||
|
|
||||||
uhci_add_td_to_urbp(td, urbp);
|
uhci_add_td_to_urbp(td, urbp);
|
||||||
uhci_fill_td(td, status | TD_CTRL_IOC,
|
uhci_fill_td(uhci, td, status | TD_CTRL_IOC,
|
||||||
destination | uhci_explen(0), 0);
|
destination | uhci_explen(0), 0);
|
||||||
plink = &td->link;
|
plink = &td->link;
|
||||||
|
|
||||||
@@ -881,11 +882,11 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb,
|
|||||||
td = uhci_alloc_td(uhci);
|
td = uhci_alloc_td(uhci);
|
||||||
if (!td)
|
if (!td)
|
||||||
goto nomem;
|
goto nomem;
|
||||||
*plink = LINK_TO_TD(td);
|
*plink = LINK_TO_TD(uhci, td);
|
||||||
|
|
||||||
uhci_fill_td(td, 0, USB_PID_OUT | uhci_explen(0), 0);
|
uhci_fill_td(uhci, td, 0, USB_PID_OUT | uhci_explen(0), 0);
|
||||||
wmb();
|
wmb();
|
||||||
qh->dummy_td->status |= cpu_to_le32(TD_CTRL_ACTIVE);
|
qh->dummy_td->status |= cpu_to_hc32(uhci, TD_CTRL_ACTIVE);
|
||||||
qh->dummy_td = td;
|
qh->dummy_td = td;
|
||||||
|
|
||||||
/* Low-speed transfers get a different queue, and won't hog the bus.
|
/* Low-speed transfers get a different queue, and won't hog the bus.
|
||||||
@@ -921,7 +922,7 @@ static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb,
|
|||||||
int len = urb->transfer_buffer_length;
|
int len = urb->transfer_buffer_length;
|
||||||
int this_sg_len;
|
int this_sg_len;
|
||||||
dma_addr_t data;
|
dma_addr_t data;
|
||||||
__le32 *plink;
|
__hc32 *plink;
|
||||||
struct urb_priv *urbp = urb->hcpriv;
|
struct urb_priv *urbp = urb->hcpriv;
|
||||||
unsigned int toggle;
|
unsigned int toggle;
|
||||||
struct scatterlist *sg;
|
struct scatterlist *sg;
|
||||||
@@ -974,10 +975,10 @@ static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb,
|
|||||||
td = uhci_alloc_td(uhci);
|
td = uhci_alloc_td(uhci);
|
||||||
if (!td)
|
if (!td)
|
||||||
goto nomem;
|
goto nomem;
|
||||||
*plink = LINK_TO_TD(td);
|
*plink = LINK_TO_TD(uhci, td);
|
||||||
}
|
}
|
||||||
uhci_add_td_to_urbp(td, urbp);
|
uhci_add_td_to_urbp(td, urbp);
|
||||||
uhci_fill_td(td, status,
|
uhci_fill_td(uhci, td, status,
|
||||||
destination | uhci_explen(pktsze) |
|
destination | uhci_explen(pktsze) |
|
||||||
(toggle << TD_TOKEN_TOGGLE_SHIFT),
|
(toggle << TD_TOKEN_TOGGLE_SHIFT),
|
||||||
data);
|
data);
|
||||||
@@ -1010,10 +1011,10 @@ static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb,
|
|||||||
td = uhci_alloc_td(uhci);
|
td = uhci_alloc_td(uhci);
|
||||||
if (!td)
|
if (!td)
|
||||||
goto nomem;
|
goto nomem;
|
||||||
*plink = LINK_TO_TD(td);
|
*plink = LINK_TO_TD(uhci, td);
|
||||||
|
|
||||||
uhci_add_td_to_urbp(td, urbp);
|
uhci_add_td_to_urbp(td, urbp);
|
||||||
uhci_fill_td(td, status,
|
uhci_fill_td(uhci, td, status,
|
||||||
destination | uhci_explen(0) |
|
destination | uhci_explen(0) |
|
||||||
(toggle << TD_TOKEN_TOGGLE_SHIFT),
|
(toggle << TD_TOKEN_TOGGLE_SHIFT),
|
||||||
data);
|
data);
|
||||||
@@ -1028,7 +1029,7 @@ static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb,
|
|||||||
* fast side but not enough to justify delaying an interrupt
|
* fast side but not enough to justify delaying an interrupt
|
||||||
* more than 2 or 3 URBs, so we will ignore the URB_NO_INTERRUPT
|
* more than 2 or 3 URBs, so we will ignore the URB_NO_INTERRUPT
|
||||||
* flag setting. */
|
* flag setting. */
|
||||||
td->status |= cpu_to_le32(TD_CTRL_IOC);
|
td->status |= cpu_to_hc32(uhci, TD_CTRL_IOC);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Build the new dummy TD and activate the old one
|
* Build the new dummy TD and activate the old one
|
||||||
@@ -1036,11 +1037,11 @@ static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb,
|
|||||||
td = uhci_alloc_td(uhci);
|
td = uhci_alloc_td(uhci);
|
||||||
if (!td)
|
if (!td)
|
||||||
goto nomem;
|
goto nomem;
|
||||||
*plink = LINK_TO_TD(td);
|
*plink = LINK_TO_TD(uhci, td);
|
||||||
|
|
||||||
uhci_fill_td(td, 0, USB_PID_OUT | uhci_explen(0), 0);
|
uhci_fill_td(uhci, td, 0, USB_PID_OUT | uhci_explen(0), 0);
|
||||||
wmb();
|
wmb();
|
||||||
qh->dummy_td->status |= cpu_to_le32(TD_CTRL_ACTIVE);
|
qh->dummy_td->status |= cpu_to_hc32(uhci, TD_CTRL_ACTIVE);
|
||||||
qh->dummy_td = td;
|
qh->dummy_td = td;
|
||||||
|
|
||||||
usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe),
|
usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe),
|
||||||
@@ -1133,7 +1134,7 @@ static int uhci_fixup_short_transfer(struct uhci_hcd *uhci,
|
|||||||
* the queue at the status stage transaction, which is
|
* the queue at the status stage transaction, which is
|
||||||
* the last TD. */
|
* the last TD. */
|
||||||
WARN_ON(list_empty(&urbp->td_list));
|
WARN_ON(list_empty(&urbp->td_list));
|
||||||
qh->element = LINK_TO_TD(td);
|
qh->element = LINK_TO_TD(uhci, td);
|
||||||
tmp = td->list.prev;
|
tmp = td->list.prev;
|
||||||
ret = -EINPROGRESS;
|
ret = -EINPROGRESS;
|
||||||
|
|
||||||
@@ -1142,8 +1143,9 @@ static int uhci_fixup_short_transfer(struct uhci_hcd *uhci,
|
|||||||
/* When a bulk/interrupt transfer is short, we have to
|
/* When a bulk/interrupt transfer is short, we have to
|
||||||
* fix up the toggles of the following URBs on the queue
|
* fix up the toggles of the following URBs on the queue
|
||||||
* before restarting the queue at the next URB. */
|
* before restarting the queue at the next URB. */
|
||||||
qh->initial_toggle = uhci_toggle(td_token(qh->post_td)) ^ 1;
|
qh->initial_toggle =
|
||||||
uhci_fixup_toggles(qh, 1);
|
uhci_toggle(td_token(uhci, qh->post_td)) ^ 1;
|
||||||
|
uhci_fixup_toggles(uhci, qh, 1);
|
||||||
|
|
||||||
if (list_empty(&urbp->td_list))
|
if (list_empty(&urbp->td_list))
|
||||||
td = qh->post_td;
|
td = qh->post_td;
|
||||||
@@ -1178,7 +1180,7 @@ static int uhci_result_common(struct uhci_hcd *uhci, struct urb *urb)
|
|||||||
unsigned int ctrlstat;
|
unsigned int ctrlstat;
|
||||||
int len;
|
int len;
|
||||||
|
|
||||||
ctrlstat = td_status(td);
|
ctrlstat = td_status(uhci, td);
|
||||||
status = uhci_status_bits(ctrlstat);
|
status = uhci_status_bits(ctrlstat);
|
||||||
if (status & TD_CTRL_ACTIVE)
|
if (status & TD_CTRL_ACTIVE)
|
||||||
return -EINPROGRESS;
|
return -EINPROGRESS;
|
||||||
@@ -1188,7 +1190,7 @@ static int uhci_result_common(struct uhci_hcd *uhci, struct urb *urb)
|
|||||||
|
|
||||||
if (status) {
|
if (status) {
|
||||||
ret = uhci_map_status(status,
|
ret = uhci_map_status(status,
|
||||||
uhci_packetout(td_token(td)));
|
uhci_packetout(td_token(uhci, td)));
|
||||||
if ((debug == 1 && ret != -EPIPE) || debug > 1) {
|
if ((debug == 1 && ret != -EPIPE) || debug > 1) {
|
||||||
/* Some debugging code */
|
/* Some debugging code */
|
||||||
dev_dbg(&urb->dev->dev,
|
dev_dbg(&urb->dev->dev,
|
||||||
@@ -1204,7 +1206,7 @@ static int uhci_result_common(struct uhci_hcd *uhci, struct urb *urb)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Did we receive a short packet? */
|
/* Did we receive a short packet? */
|
||||||
} else if (len < uhci_expected_length(td_token(td))) {
|
} else if (len < uhci_expected_length(td_token(uhci, td))) {
|
||||||
|
|
||||||
/* For control transfers, go to the status TD if
|
/* For control transfers, go to the status TD if
|
||||||
* this isn't already the last data TD */
|
* this isn't already the last data TD */
|
||||||
@@ -1236,10 +1238,10 @@ err:
|
|||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
/* Note that the queue has stopped and save
|
/* Note that the queue has stopped and save
|
||||||
* the next toggle value */
|
* the next toggle value */
|
||||||
qh->element = UHCI_PTR_TERM;
|
qh->element = UHCI_PTR_TERM(uhci);
|
||||||
qh->is_stopped = 1;
|
qh->is_stopped = 1;
|
||||||
qh->needs_fixup = (qh->type != USB_ENDPOINT_XFER_CONTROL);
|
qh->needs_fixup = (qh->type != USB_ENDPOINT_XFER_CONTROL);
|
||||||
qh->initial_toggle = uhci_toggle(td_token(td)) ^
|
qh->initial_toggle = uhci_toggle(td_token(uhci, td)) ^
|
||||||
(ret == -EREMOTEIO);
|
(ret == -EREMOTEIO);
|
||||||
|
|
||||||
} else /* Short packet received */
|
} else /* Short packet received */
|
||||||
@@ -1335,14 +1337,14 @@ static int uhci_submit_isochronous(struct uhci_hcd *uhci, struct urb *urb,
|
|||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
uhci_add_td_to_urbp(td, urbp);
|
uhci_add_td_to_urbp(td, urbp);
|
||||||
uhci_fill_td(td, status, destination |
|
uhci_fill_td(uhci, td, status, destination |
|
||||||
uhci_explen(urb->iso_frame_desc[i].length),
|
uhci_explen(urb->iso_frame_desc[i].length),
|
||||||
urb->transfer_dma +
|
urb->transfer_dma +
|
||||||
urb->iso_frame_desc[i].offset);
|
urb->iso_frame_desc[i].offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set the interrupt-on-completion flag on the last packet. */
|
/* Set the interrupt-on-completion flag on the last packet. */
|
||||||
td->status |= cpu_to_le32(TD_CTRL_IOC);
|
td->status |= cpu_to_hc32(uhci, TD_CTRL_IOC);
|
||||||
|
|
||||||
/* Add the TDs to the frame list */
|
/* Add the TDs to the frame list */
|
||||||
frame = urb->start_frame;
|
frame = urb->start_frame;
|
||||||
@@ -1378,7 +1380,7 @@ static int uhci_result_isochronous(struct uhci_hcd *uhci, struct urb *urb)
|
|||||||
|
|
||||||
uhci_remove_tds_from_frame(uhci, qh->iso_frame);
|
uhci_remove_tds_from_frame(uhci, qh->iso_frame);
|
||||||
|
|
||||||
ctrlstat = td_status(td);
|
ctrlstat = td_status(uhci, td);
|
||||||
if (ctrlstat & TD_CTRL_ACTIVE) {
|
if (ctrlstat & TD_CTRL_ACTIVE) {
|
||||||
status = -EXDEV; /* TD was added too late? */
|
status = -EXDEV; /* TD was added too late? */
|
||||||
} else {
|
} else {
|
||||||
@@ -1629,7 +1631,7 @@ restart:
|
|||||||
* queue, the QH can now be re-activated. */
|
* queue, the QH can now be re-activated. */
|
||||||
if (!list_empty(&qh->queue)) {
|
if (!list_empty(&qh->queue)) {
|
||||||
if (qh->needs_fixup)
|
if (qh->needs_fixup)
|
||||||
uhci_fixup_toggles(qh, 0);
|
uhci_fixup_toggles(uhci, qh, 0);
|
||||||
|
|
||||||
/* If the first URB on the queue wants FSBR but its time
|
/* If the first URB on the queue wants FSBR but its time
|
||||||
* limit has expired, set the next TD to interrupt on
|
* limit has expired, set the next TD to interrupt on
|
||||||
@@ -1639,7 +1641,7 @@ restart:
|
|||||||
struct uhci_td *td = list_entry(urbp->td_list.next,
|
struct uhci_td *td = list_entry(urbp->td_list.next,
|
||||||
struct uhci_td, list);
|
struct uhci_td, list);
|
||||||
|
|
||||||
td->status |= __cpu_to_le32(TD_CTRL_IOC);
|
td->status |= cpu_to_hc32(uhci, TD_CTRL_IOC);
|
||||||
}
|
}
|
||||||
|
|
||||||
uhci_activate_qh(uhci, qh);
|
uhci_activate_qh(uhci, qh);
|
||||||
@@ -1686,7 +1688,7 @@ static int uhci_advance_check(struct uhci_hcd *uhci, struct uhci_qh *qh)
|
|||||||
} else {
|
} else {
|
||||||
urbp = list_entry(qh->queue.next, struct urb_priv, node);
|
urbp = list_entry(qh->queue.next, struct urb_priv, node);
|
||||||
td = list_entry(urbp->td_list.next, struct uhci_td, list);
|
td = list_entry(urbp->td_list.next, struct uhci_td, list);
|
||||||
status = td_status(td);
|
status = td_status(uhci, td);
|
||||||
if (!(status & TD_CTRL_ACTIVE)) {
|
if (!(status & TD_CTRL_ACTIVE)) {
|
||||||
|
|
||||||
/* We're okay, the queue has advanced */
|
/* We're okay, the queue has advanced */
|
||||||
@@ -1704,7 +1706,8 @@ static int uhci_advance_check(struct uhci_hcd *uhci, struct uhci_qh *qh)
|
|||||||
if (time_after(jiffies, qh->advance_jiffies + QH_WAIT_TIMEOUT)) {
|
if (time_after(jiffies, qh->advance_jiffies + QH_WAIT_TIMEOUT)) {
|
||||||
|
|
||||||
/* Detect the Intel bug and work around it */
|
/* Detect the Intel bug and work around it */
|
||||||
if (qh->post_td && qh_element(qh) == LINK_TO_TD(qh->post_td)) {
|
if (qh->post_td && qh_element(qh) ==
|
||||||
|
LINK_TO_TD(uhci, qh->post_td)) {
|
||||||
qh->element = qh->post_td->link;
|
qh->element = qh->post_td->link;
|
||||||
qh->advance_jiffies = jiffies;
|
qh->advance_jiffies = jiffies;
|
||||||
ret = 1;
|
ret = 1;
|
||||||
|
Reference in New Issue
Block a user