Commit 34271b63 authored by David Brownell's avatar David Brownell Committed by Vojtech Pavlik

[PATCH] ohci misc

This patch  includes the innocuous bits from a larger one that
I'm still working on (mostly unlink fixes):

- updates comments
- flags TDs that were seen in the donelist
- removes some bogus whitespace (at EOL etc) and tabs
- checks for an enumeration issue that might cause trouble
- delays IRQs a bit more aggressively
- shortens TD submit paths a smidgeon (smaller ".o")
- updates some of the debug output
- sanitizes usb_make_path() output on the SA-1111
parent 2fcd00c2
...@@ -12,13 +12,14 @@ ...@@ -12,13 +12,14 @@
#ifdef DEBUG #ifdef DEBUG
#define pipestring(pipe) ({ char *temp; \ #define edstring(ed_type) ({ char *temp; \
switch (usb_pipetype (pipe)) { \ switch (ed_type) { \
case PIPE_CONTROL: temp = "CTRL"; break; \ case PIPE_CONTROL: temp = "CTRL"; break; \
case PIPE_BULK: temp = "BULK"; break; \ case PIPE_BULK: temp = "BULK"; break; \
case PIPE_INTERRUPT: temp = "INTR"; break; \ case PIPE_INTERRUPT: temp = "INTR"; break; \
default: temp = "ISOC"; break; \ default: temp = "ISOC"; break; \
}; temp;}) }; temp;})
#define pipestring(pipe) edstring(usb_pipetype(pipe))
/* debug| print the main components of an URB /* debug| print the main components of an URB
* small: 0) header + data packets 1) just header * small: 0) header + data packets 1) just header
...@@ -35,9 +36,9 @@ static void urb_print (struct urb * urb, char * str, int small) ...@@ -35,9 +36,9 @@ static void urb_print (struct urb * urb, char * str, int small)
#ifndef OHCI_VERBOSE_DEBUG #ifndef OHCI_VERBOSE_DEBUG
if (urb->status != 0) if (urb->status != 0)
#endif #endif
dbg("%s:[%4x] dev:%d,ep=%d-%c,%s,flags:%4x,len:%d/%d,stat:%d", dbg("%s %p dev:%d,ep=%d-%c,%s,flags:%x,len:%d/%d,stat:%d",
str, str,
usb_get_current_frame_number (urb->dev), urb,
usb_pipedevice (pipe), usb_pipedevice (pipe),
usb_pipeendpoint (pipe), usb_pipeendpoint (pipe),
usb_pipeout (pipe)? 'O': 'I', usb_pipeout (pipe)? 'O': 'I',
...@@ -242,21 +243,25 @@ static void ohci_dump (struct ohci_hcd *controller, int verbose) ...@@ -242,21 +243,25 @@ static void ohci_dump (struct ohci_hcd *controller, int verbose)
ohci_dump_roothub (controller, 1); ohci_dump_roothub (controller, 1);
} }
static const char data0 [] = "DATA0";
static const char data1 [] = "DATA1";
static void ohci_dump_td (char *label, struct td *td) static void ohci_dump_td (char *label, struct td *td)
{ {
u32 tmp = le32_to_cpup (&td->hwINFO); u32 tmp = le32_to_cpup (&td->hwINFO);
dbg ("%s td %p; urb %p index %d; hw next td %08x", dbg ("%s td %p%s; urb %p index %d; hw next td %08x",
label, td, label, td,
(tmp & TD_DONE) ? " (DONE)" : "",
td->urb, td->index, td->urb, td->index,
le32_to_cpup (&td->hwNextTD)); le32_to_cpup (&td->hwNextTD));
if ((tmp & TD_ISO) == 0) { if ((tmp & TD_ISO) == 0) {
char *toggle, *pid; const char *toggle, *pid;
u32 cbp, be; u32 cbp, be;
switch (tmp & TD_T) { switch (tmp & TD_T) {
case TD_T_DATA0: toggle = "DATA0"; break; case TD_T_DATA0: toggle = data0; break;
case TD_T_DATA1: toggle = "DATA1"; break; case TD_T_DATA1: toggle = data1; break;
case TD_T_TOGGLE: toggle = "(CARRY)"; break; case TD_T_TOGGLE: toggle = "(CARRY)"; break;
default: toggle = "(?)"; break; default: toggle = "(?)"; break;
} }
...@@ -297,9 +302,9 @@ ohci_dump_ed (struct ohci_hcd *ohci, char *label, struct ed *ed, int verbose) ...@@ -297,9 +302,9 @@ ohci_dump_ed (struct ohci_hcd *ohci, char *label, struct ed *ed, int verbose)
u32 tmp = ed->hwINFO; u32 tmp = ed->hwINFO;
char *type = ""; char *type = "";
dbg ("%s: %s, ed %p state 0x%x type %d; next ed %08x", dbg ("%s: %s, ed %p state 0x%x type %s; next ed %08x",
ohci->hcd.self.bus_name, label, ohci->hcd.self.bus_name, label,
ed, ed->state, ed->type, ed, ed->state, edstring (ed->type),
le32_to_cpup (&ed->hwNextED)); le32_to_cpup (&ed->hwNextED));
switch (tmp & (ED_IN|ED_OUT)) { switch (tmp & (ED_IN|ED_OUT)) {
case ED_OUT: type = "-OUT"; break; case ED_OUT: type = "-OUT"; break;
...@@ -314,10 +319,10 @@ ohci_dump_ed (struct ohci_hcd *ohci, char *label, struct ed *ed, int verbose) ...@@ -314,10 +319,10 @@ ohci_dump_ed (struct ohci_hcd *ohci, char *label, struct ed *ed, int verbose)
0x000f & (le32_to_cpu (tmp) >> 7), 0x000f & (le32_to_cpu (tmp) >> 7),
type, type,
0x007f & le32_to_cpu (tmp)); 0x007f & le32_to_cpu (tmp));
dbg (" tds: head %08x%s%s tail %08x%s", dbg (" tds: head %08x %s%s tail %08x%s",
tmp = le32_to_cpup (&ed->hwHeadP), tmp = le32_to_cpup (&ed->hwHeadP),
(ed->hwHeadP & ED_C) ? data1 : data0,
(ed->hwHeadP & ED_H) ? " HALT" : "", (ed->hwHeadP & ED_H) ? " HALT" : "",
(ed->hwHeadP & ED_C) ? " CARRY" : "",
le32_to_cpup (&ed->hwTailP), le32_to_cpup (&ed->hwTailP),
verbose ? "" : " (not listing)"); verbose ? "" : " (not listing)");
if (verbose) { if (verbose) {
......
...@@ -197,7 +197,7 @@ static int ohci_urb_enqueue ( ...@@ -197,7 +197,7 @@ static int ohci_urb_enqueue (
/* allocate the TDs (updating hash chains) */ /* allocate the TDs (updating hash chains) */
spin_lock_irqsave (&ohci->lock, flags); spin_lock_irqsave (&ohci->lock, flags);
for (i = 0; i < size; i++) { for (i = 0; i < size; i++) {
urb_priv->td [i] = td_alloc (ohci, SLAB_ATOMIC); urb_priv->td [i] = td_alloc (ohci, SLAB_ATOMIC);
if (!urb_priv->td [i]) { if (!urb_priv->td [i]) {
urb_priv->length = i; urb_priv->length = i;
...@@ -208,6 +208,8 @@ static int ohci_urb_enqueue ( ...@@ -208,6 +208,8 @@ static int ohci_urb_enqueue (
} }
// FIXME: much of this switch should be generic, move to hcd code ... // FIXME: much of this switch should be generic, move to hcd code ...
// ... and what's not generic can't really be handled this way.
// need to consider periodicity for both types!
/* allocate and claim bandwidth if needed; ISO /* allocate and claim bandwidth if needed; ISO
* needs start frame index if it was't provided. * needs start frame index if it was't provided.
...@@ -247,14 +249,14 @@ static int ohci_urb_enqueue ( ...@@ -247,14 +249,14 @@ static int ohci_urb_enqueue (
spin_unlock_irqrestore (&ohci->lock, flags); spin_unlock_irqrestore (&ohci->lock, flags);
return 0; return 0;
} }
/* /*
* decouple the URB from the HC queues (TDs, urb_priv); it's * decouple the URB from the HC queues (TDs, urb_priv); it's
* already marked for deletion. reporting is always done * already marked using urb->status. reporting is always done
* asynchronously, and we might be dealing with an urb that's * asynchronously, and we might be dealing with an urb that's
* almost completed anyway... * partially transferred, or an ED with other urbs being unlinked.
*/ */
static int ohci_urb_dequeue (struct usb_hcd *hcd, struct urb *urb) static int ohci_urb_dequeue (struct usb_hcd *hcd, struct urb *urb)
{ {
......
...@@ -465,6 +465,25 @@ static struct ed *ed_get ( ...@@ -465,6 +465,25 @@ static struct ed *ed_get (
* we know it's already a power of 2 * we know it's already a power of 2
*/ */
ed->interval = interval; ed->interval = interval;
#ifdef DEBUG
/*
* There are two other cases we ought to change hwINFO, both during
* enumeration. There, the control request completes, unlinks, and
* the next request gets queued before the unlink completes, so it
* uses old/wrong hwINFO. How much of a problem is this? khubd is
* already retrying after such failures...
*/
} else if (type == PIPE_CONTROL) {
u32 info = le32_to_cpup (&ed->hwINFO);
if (!(info & 0x7f))
dbg ("RETRY ctrl: address != 0");
info >>= 16;
if (info != udev->epmaxpacketin [0])
dbg ("RETRY ctrl: maxpacket %d != 8",
udev->epmaxpacketin [0]);
#endif /* DEBUG */
} }
done: done:
...@@ -539,12 +558,15 @@ td_fill (struct ohci_hcd *ohci, unsigned int info, ...@@ -539,12 +558,15 @@ td_fill (struct ohci_hcd *ohci, unsigned int info,
/* aim for only one interrupt per urb. mostly applies to control /* aim for only one interrupt per urb. mostly applies to control
* and iso; other urbs rarely need more than one TD per urb. * and iso; other urbs rarely need more than one TD per urb.
* this way, only final tds (or ones with an error) cause IRQs.
* *
* NOTE: could delay interrupts even for the last TD, and get fewer * NOTE: could delay interrupts even for the last TD, and get fewer
* interrupts ... increasing per-urb latency by sharing interrupts. * interrupts ... increasing per-urb latency by sharing interrupts.
* Drivers that queue bulk urbs may request that behavior.
*/ */
if (index != (urb_priv->length - 1)) if (index != (urb_priv->length - 1)
info |= is_iso ? TD_DI_SET (7) : TD_DI_SET (1); || (urb->transfer_flags & URB_NO_INTERRUPT))
info |= TD_DI_SET (7);
/* use this td as the next dummy */ /* use this td as the next dummy */
td_pt = urb_priv->td [index]; td_pt = urb_priv->td [index];
...@@ -565,6 +587,7 @@ td_fill (struct ohci_hcd *ohci, unsigned int info, ...@@ -565,6 +587,7 @@ td_fill (struct ohci_hcd *ohci, unsigned int info,
td->hwINFO = cpu_to_le32 (info); td->hwINFO = cpu_to_le32 (info);
if (is_iso) { if (is_iso) {
td->hwCBP = cpu_to_le32 (data & 0xFFFFF000); td->hwCBP = cpu_to_le32 (data & 0xFFFFF000);
td->hwPSW [0] = cpu_to_le16 ((data & 0x0FFF) | 0xE000);
td->ed->intriso.last_iso = info & 0xffff; td->ed->intriso.last_iso = info & 0xffff;
} else { } else {
td->hwCBP = cpu_to_le32 (data); td->hwCBP = cpu_to_le32 (data);
...@@ -574,7 +597,6 @@ td_fill (struct ohci_hcd *ohci, unsigned int info, ...@@ -574,7 +597,6 @@ td_fill (struct ohci_hcd *ohci, unsigned int info,
else else
td->hwBE = 0; td->hwBE = 0;
td->hwNextTD = cpu_to_le32 (td_pt->td_dma); td->hwNextTD = cpu_to_le32 (td_pt->td_dma);
td->hwPSW [0] = cpu_to_le16 ((data & 0x0FFF) | 0xE000);
/* HC might read the TD right after we link it ... */ /* HC might read the TD right after we link it ... */
wmb (); wmb ();
...@@ -596,17 +618,17 @@ static void td_submit_urb (struct urb *urb) ...@@ -596,17 +618,17 @@ static void td_submit_urb (struct urb *urb)
int cnt = 0; int cnt = 0;
__u32 info = 0; __u32 info = 0;
unsigned int toggle = 0; unsigned int toggle = 0;
int is_out = usb_pipeout (urb->pipe);
/* OHCI handles the DATA-toggles itself, we just use the /* OHCI handles the DATA-toggles itself, we just use the
* USB-toggle bits for resetting * USB-toggle bits for resetting
*/ */
if (usb_gettoggle (urb->dev, usb_pipeendpoint (urb->pipe), if (usb_gettoggle (urb->dev, usb_pipeendpoint (urb->pipe), is_out)) {
usb_pipeout (urb->pipe))) {
toggle = TD_T_TOGGLE; toggle = TD_T_TOGGLE;
} else { } else {
toggle = TD_T_DATA0; toggle = TD_T_DATA0;
usb_settoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_settoggle (urb->dev, usb_pipeendpoint (urb->pipe),
usb_pipeout (urb->pipe), 1); is_out, 1);
} }
urb_priv->td_cnt = 0; urb_priv->td_cnt = 0;
...@@ -614,9 +636,9 @@ static void td_submit_urb (struct urb *urb) ...@@ -614,9 +636,9 @@ static void td_submit_urb (struct urb *urb)
if (data_len) { if (data_len) {
data = pci_map_single (ohci->hcd.pdev, data = pci_map_single (ohci->hcd.pdev,
urb->transfer_buffer, data_len, urb->transfer_buffer, data_len,
usb_pipeout (urb->pipe) is_out
? PCI_DMA_TODEVICE ? PCI_DMA_TODEVICE
: PCI_DMA_FROMDEVICE); : PCI_DMA_FROMDEVICE);
} else } else
data = 0; data = 0;
...@@ -625,18 +647,20 @@ static void td_submit_urb (struct urb *urb) ...@@ -625,18 +647,20 @@ static void td_submit_urb (struct urb *urb)
*/ */
switch (usb_pipetype (urb->pipe)) { switch (usb_pipetype (urb->pipe)) {
case PIPE_BULK: case PIPE_BULK:
info = usb_pipeout (urb->pipe) info = is_out
? TD_CC | TD_DP_OUT ? TD_CC | TD_DP_OUT
: TD_CC | TD_DP_IN ; : TD_CC | TD_DP_IN ;
/* TDs _could_ transfer up to 8K each */
while (data_len > 4096) { while (data_len > 4096) {
td_fill (ohci, td_fill (ohci,
info | (cnt? TD_T_TOGGLE:toggle), info | (cnt? TD_T_TOGGLE:toggle),
data, 4096, urb, cnt); data, 4096, urb, cnt);
data += 4096; data_len -= 4096; cnt++; data += 4096; data_len -= 4096; cnt++;
} }
info = usb_pipeout (urb->pipe)? /* maybe avoid ED halt on final TD short read */
TD_CC | TD_DP_OUT : TD_CC | TD_R | TD_DP_IN ; if (!(urb->transfer_flags & USB_DISABLE_SPD))
td_fill (ohci, info | (cnt? TD_T_TOGGLE:toggle), info |= TD_R;
td_fill (ohci, info | (cnt ? TD_T_TOGGLE : toggle),
data, data_len, urb, cnt); data, data_len, urb, cnt);
cnt++; cnt++;
if ((urb->transfer_flags & USB_ZERO_PACKET) if ((urb->transfer_flags & USB_ZERO_PACKET)
...@@ -653,8 +677,11 @@ static void td_submit_urb (struct urb *urb) ...@@ -653,8 +677,11 @@ static void td_submit_urb (struct urb *urb)
break; break;
case PIPE_INTERRUPT: case PIPE_INTERRUPT:
/* current policy: only one TD per request.
* otherwise identical to bulk, except for BLF
*/
info = TD_CC | toggle; info = TD_CC | toggle;
info |= usb_pipeout (urb->pipe) info |= is_out
? TD_DP_OUT ? TD_DP_OUT
: TD_R | TD_DP_IN; : TD_R | TD_DP_IN;
td_fill (ohci, info, data, data_len, urb, cnt++); td_fill (ohci, info, data, data_len, urb, cnt++);
...@@ -670,14 +697,12 @@ static void td_submit_urb (struct urb *urb) ...@@ -670,14 +697,12 @@ static void td_submit_urb (struct urb *urb)
8, urb, cnt++); 8, urb, cnt++);
if (data_len > 0) { if (data_len > 0) {
info = TD_CC | TD_R | TD_T_DATA1; info = TD_CC | TD_R | TD_T_DATA1;
info |= usb_pipeout (urb->pipe) info |= is_out ? TD_DP_OUT : TD_DP_IN;
? TD_DP_OUT
: TD_DP_IN;
/* NOTE: mishandles transfers >8K, some >4K */ /* NOTE: mishandles transfers >8K, some >4K */
td_fill (ohci, info, data, data_len, td_fill (ohci, info, data, data_len,
urb, cnt++); urb, cnt++);
} }
info = usb_pipeout (urb->pipe) info = is_out
? TD_CC | TD_DP_IN | TD_T_DATA1 ? TD_CC | TD_DP_IN | TD_T_DATA1
: TD_CC | TD_DP_OUT | TD_T_DATA1; : TD_CC | TD_DP_OUT | TD_T_DATA1;
td_fill (ohci, info, data, 0, urb, cnt++); td_fill (ohci, info, data, 0, urb, cnt++);
...@@ -806,10 +831,13 @@ static struct td *dl_reverse_done_list (struct ohci_hcd *ohci) ...@@ -806,10 +831,13 @@ static struct td *dl_reverse_done_list (struct ohci_hcd *ohci)
while (td_list_hc) { while (td_list_hc) {
td_list = dma_to_td (ohci, td_list_hc); td_list = dma_to_td (ohci, td_list_hc);
td_list->hwINFO |= cpu_to_le32 (TD_DONE);
if (TD_CC_GET (le32_to_cpup (&td_list->hwINFO))) { if (TD_CC_GET (le32_to_cpup (&td_list->hwINFO))) {
urb_priv = (urb_priv_t *) td_list->urb->hcpriv; urb_priv = (urb_priv_t *) td_list->urb->hcpriv;
/* typically the endpoint halts on error; un-halt, /* Non-iso endpoints can halt on error; un-halt,
* and maybe dequeue other TDs from this urb * and dequeue any other TDs from this urb.
* No other TD could have caused the halt.
*/ */
if (td_list->ed->hwHeadP & ED_H) { if (td_list->ed->hwHeadP & ED_H) {
if (urb_priv && ((td_list->index + 1) if (urb_priv && ((td_list->index + 1)
......
...@@ -175,7 +175,7 @@ int usb_hcd_sa1111_probe (const struct hc_driver *driver, struct usb_hcd **hcd_o ...@@ -175,7 +175,7 @@ int usb_hcd_sa1111_probe (const struct hc_driver *driver, struct usb_hcd **hcd_o
usb_bus_init (&hcd->self); usb_bus_init (&hcd->self);
hcd->self.op = &usb_hcd_operations; hcd->self.op = &usb_hcd_operations;
hcd->self.hcpriv = (void *) hcd; hcd->self.hcpriv = (void *) hcd;
hcd->self.bus_name = "SA-1111"; hcd->self.bus_name = "sa1111";
hcd->product_desc = "SA-1111 OHCI"; hcd->product_desc = "SA-1111 OHCI";
INIT_LIST_HEAD (&hcd->dev_list); INIT_LIST_HEAD (&hcd->dev_list);
......
...@@ -11,6 +11,9 @@ ...@@ -11,6 +11,9 @@
/* /*
* OHCI Endpoint Descriptor (ED) ... holds TD queue * OHCI Endpoint Descriptor (ED) ... holds TD queue
* See OHCI spec, section 4.2 * See OHCI spec, section 4.2
*
* This is a "Queue Head" for those transfers, which is why
* both EHCI and UHCI call similar structures a "QH".
*/ */
struct ed { struct ed {
/* first fields are hardware-specified, le32 */ /* first fields are hardware-specified, le32 */
...@@ -74,8 +77,8 @@ struct td { ...@@ -74,8 +77,8 @@ struct td {
/* these two bits are available for definition/use by HCDs in both /* these two bits are available for definition/use by HCDs in both
* general and iso tds ... others are available for only one type * general and iso tds ... others are available for only one type
*/ */
//#define TD____ 0x00020000 #define TD_DONE 0x00020000 /* retired to donelist */
#define TD_ISO 0x00010000 /* copy of ED_ISO */ #define TD_ISO 0x00010000 /* copy of ED_ISO */
/* hwINFO bits for general tds: */ /* hwINFO bits for general tds: */
#define TD_EC 0x0C000000 /* error count */ #define TD_EC 0x0C000000 /* error count */
...@@ -349,12 +352,14 @@ struct ohci_hcd { ...@@ -349,12 +352,14 @@ struct ohci_hcd {
struct device *parent_dev; struct device *parent_dev;
/* /*
* I/O memory used to communicate with the HC (uncached); * I/O memory used to communicate with the HC (dma-consistent)
*/ */
struct ohci_regs *regs; struct ohci_regs *regs;
/* /*
* main memory used to communicate with the HC (uncached) * main memory used to communicate with the HC (dma-consistent).
* hcd adds to schedule for a live hc any time, but removals finish
* only at the start of the next frame.
*/ */
struct ohci_hcca *hcca; struct ohci_hcca *hcca;
dma_addr_t hcca_dma; dma_addr_t hcca_dma;
...@@ -365,6 +370,9 @@ struct ohci_hcd { ...@@ -365,6 +370,9 @@ struct ohci_hcd {
struct ed *ed_controltail; /* last in ctrl list */ struct ed *ed_controltail; /* last in ctrl list */
struct ed *ed_isotail; /* last in iso list */ struct ed *ed_isotail; /* last in iso list */
/*
* memory management for queue data structures
*/
struct pci_pool *td_cache; struct pci_pool *td_cache;
struct pci_pool *ed_cache; struct pci_pool *ed_cache;
struct hash_list_t td_hash [TD_HASH_SIZE]; struct hash_list_t td_hash [TD_HASH_SIZE];
...@@ -380,6 +388,7 @@ struct ohci_hcd { ...@@ -380,6 +388,7 @@ struct ohci_hcd {
unsigned long flags; /* for HC bugs */ unsigned long flags; /* for HC bugs */
#define OHCI_QUIRK_AMD756 0x01 /* erratum #4 */ #define OHCI_QUIRK_AMD756 0x01 /* erratum #4 */
// there are also chip quirks/bugs in init logic
/* /*
* framework state * framework state
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment