Commit 97cf4c34 authored by David Brownell's avatar David Brownell Committed by Greg Kroah-Hartman

[PATCH] ohci-hcd cardbus unplug, remove interrupt length limit,

* handle another cardbus unplug misbehavior
     - root hub kept polling, never stopped
     - starts to update hcd->state to match internal state
* code to count/queue TDs for interrupt/bulk is now shared
     - removes (low level) interrupt transfer size limitation
     - both types already handled urb queueing
     - re-indents some TD queuing code (most of patch, by volume)
* cleanup
     - use new container_of() macro, not list_entry()
     - report a previously unreported error (control data >4K)
     - simplify intr/bulk toggle reset
     - tweak TD debug dump
     - more object code shrinkage (often fits in 3 pages)

Note that the control data size error is just a long-standing
limitation of this driver, not a USB limitation!  It could be
fixed, if anyone starts to run into it.
parent 75162c88
...@@ -281,16 +281,21 @@ static void ohci_dump_td (char *label, struct td *td) ...@@ -281,16 +281,21 @@ static void ohci_dump_td (char *label, struct td *td)
cbp ? (be + 1 - cbp) : 0); cbp ? (be + 1 - cbp) : 0);
} else { } else {
unsigned i; unsigned i;
dbg (" info %08x CC=%x DI=%d START=%04x", tmp, dbg (" info %08x CC=%x FC=%d DI=%d SF=%04x", tmp,
TD_CC_GET(tmp), /* FC, */ TD_CC_GET(tmp),
(tmp >> 24) & 0x07,
(tmp & TD_DI) >> 21, (tmp & TD_DI) >> 21,
tmp & 0x0000ffff); tmp & 0x0000ffff);
dbg (" bp0 %08x be %08x", dbg (" bp0 %08x be %08x",
le32_to_cpup (&td->hwCBP) & ~0x0fff, le32_to_cpup (&td->hwCBP) & ~0x0fff,
le32_to_cpup (&td->hwBE)); le32_to_cpup (&td->hwBE));
for (i = 0; i < MAXPSW; i++) { for (i = 0; i < MAXPSW; i++) {
dbg (" psw [%d] = %2x", i, u16 psw = le16_to_cpup (&td->hwPSW [i]);
le16_to_cpu (td->hwPSW [i])); int cc = (psw >> 12) & 0x0f;
dbg (" psw [%d] = %2x, CC=%x %s=%d", i,
psw, cc,
(cc >= 0x0e) ? "OFFSET" : "SIZE",
psw & 0x0fff);
} }
} }
} }
......
...@@ -125,6 +125,12 @@ ...@@ -125,6 +125,12 @@
#include "ohci.h" #include "ohci.h"
static inline void disable (struct ohci_hcd *ohci)
{
ohci->disabled = 1;
ohci->hcd.state = USB_STATE_HALT;
}
#include "ohci-hub.c" #include "ohci-hub.c"
#include "ohci-dbg.c" #include "ohci-dbg.c"
#include "ohci-mem.c" #include "ohci-mem.c"
...@@ -158,12 +164,18 @@ static int ohci_urb_enqueue ( ...@@ -158,12 +164,18 @@ static int ohci_urb_enqueue (
return -ENOMEM; return -ENOMEM;
/* for the private part of the URB we need the number of TDs (size) */ /* for the private part of the URB we need the number of TDs (size) */
switch (usb_pipetype (pipe)) { switch (ed->type) {
case PIPE_CONTROL: case PIPE_CONTROL:
/* td_submit_urb() doesn't yet handle these */
if (urb->transfer_buffer_length > 4096)
return -EMSGSIZE;
/* 1 TD for setup, 1 for ACK, plus ... */ /* 1 TD for setup, 1 for ACK, plus ... */
size = 2; size = 2;
/* FALLTHROUGH */ /* FALLTHROUGH */
case PIPE_BULK: // case PIPE_INTERRUPT:
// case PIPE_BULK:
default:
/* one TD for every 4096 Bytes (can be upto 8K) */ /* one TD for every 4096 Bytes (can be upto 8K) */
size += urb->transfer_buffer_length / 4096; size += urb->transfer_buffer_length / 4096;
/* ... and for any remaining bytes ... */ /* ... and for any remaining bytes ... */
...@@ -187,9 +199,6 @@ static int ohci_urb_enqueue ( ...@@ -187,9 +199,6 @@ static int ohci_urb_enqueue (
urb->iso_frame_desc [i].status = -EXDEV; urb->iso_frame_desc [i].status = -EXDEV;
} }
break; break;
case PIPE_INTERRUPT: /* one TD */
size = 1;
break;
} }
/* allocate the private part of the URB */ /* allocate the private part of the URB */
...@@ -242,9 +251,8 @@ static int ohci_urb_enqueue ( ...@@ -242,9 +251,8 @@ static int ohci_urb_enqueue (
bustime = usb_check_bandwidth (urb->dev, urb); bustime = usb_check_bandwidth (urb->dev, urb);
} }
if (bustime < 0) { if (bustime < 0) {
urb_free_priv (ohci, urb_priv); retval = bustime;
spin_unlock_irqrestore (&ohci->lock, flags); goto fail;
return bustime;
} }
usb_claim_bandwidth (urb->dev, urb, usb_claim_bandwidth (urb->dev, urb,
bustime, usb_pipeisoc (urb->pipe)); bustime, usb_pipeisoc (urb->pipe));
...@@ -259,7 +267,7 @@ static int ohci_urb_enqueue ( ...@@ -259,7 +267,7 @@ static int ohci_urb_enqueue (
/* fill the TDs and link them to the ed; and /* fill the TDs and link them to the ed; and
* enable that part of the schedule, if needed * enable that part of the schedule, if needed
*/ */
td_submit_urb (urb); td_submit_urb (ohci, urb);
fail: fail:
if (retval) if (retval)
...@@ -513,7 +521,7 @@ static int hc_start (struct ohci_hcd *ohci) ...@@ -513,7 +521,7 @@ static int hc_start (struct ohci_hcd *ohci)
ohci->hcd.self.root_hub = udev = usb_alloc_dev (NULL, &ohci->hcd.self); ohci->hcd.self.root_hub = udev = usb_alloc_dev (NULL, &ohci->hcd.self);
ohci->hcd.state = USB_STATE_READY; ohci->hcd.state = USB_STATE_READY;
if (!udev) { if (!udev) {
ohci->disabled = 1; disable (ohci);
ohci->hc_control &= ~OHCI_CTRL_HCFS; ohci->hc_control &= ~OHCI_CTRL_HCFS;
writel (ohci->hc_control, &ohci->regs->control); writel (ohci->hc_control, &ohci->regs->control);
return -ENOMEM; return -ENOMEM;
...@@ -523,7 +531,7 @@ static int hc_start (struct ohci_hcd *ohci) ...@@ -523,7 +531,7 @@ static int hc_start (struct ohci_hcd *ohci)
udev->speed = USB_SPEED_FULL; udev->speed = USB_SPEED_FULL;
if (usb_register_root_hub (udev, ohci->parent_dev) != 0) { if (usb_register_root_hub (udev, ohci->parent_dev) != 0) {
usb_free_dev (udev); usb_free_dev (udev);
ohci->disabled = 1; disable (ohci);
ohci->hc_control &= ~OHCI_CTRL_HCFS; ohci->hc_control &= ~OHCI_CTRL_HCFS;
writel (ohci->hc_control, &ohci->regs->control); writel (ohci->hc_control, &ohci->regs->control);
return -ENODEV; return -ENODEV;
...@@ -549,8 +557,8 @@ static void ohci_irq (struct usb_hcd *hcd) ...@@ -549,8 +557,8 @@ static void ohci_irq (struct usb_hcd *hcd)
/* cardbus/... hardware gone before remove() */ /* cardbus/... hardware gone before remove() */
} else if ((ints = readl (&regs->intrstatus)) == ~(u32)0) { } else if ((ints = readl (&regs->intrstatus)) == ~(u32)0) {
ohci->disabled++; disable (ohci);
err ("%s device removed!", hcd->self.bus_name); dbg ("%s device removed!", hcd->self.bus_name);
return; return;
/* interrupt for some other device? */ /* interrupt for some other device? */
...@@ -562,7 +570,7 @@ static void ohci_irq (struct usb_hcd *hcd) ...@@ -562,7 +570,7 @@ static void ohci_irq (struct usb_hcd *hcd)
// dbg ("Interrupt: %x frame: %x", ints, le16_to_cpu (ohci->hcca->frame_no)); // dbg ("Interrupt: %x frame: %x", ints, le16_to_cpu (ohci->hcca->frame_no));
if (ints & OHCI_INTR_UE) { if (ints & OHCI_INTR_UE) {
ohci->disabled++; disable (ohci);
err ("OHCI Unrecoverable Error, %s disabled", hcd->self.bus_name); err ("OHCI Unrecoverable Error, %s disabled", hcd->self.bus_name);
// e.g. due to PCI Master/Target Abort // e.g. due to PCI Master/Target Abort
......
...@@ -22,7 +22,9 @@ ...@@ -22,7 +22,9 @@
*/ */
#define read_roothub(hc, register, mask) ({ \ #define read_roothub(hc, register, mask) ({ \
u32 temp = readl (&hc->regs->roothub.register); \ u32 temp = readl (&hc->regs->roothub.register); \
if (hc->flags & OHCI_QUIRK_AMD756) \ if (temp == -1) \
disable (hc); \
else if (hc->flags & OHCI_QUIRK_AMD756) \
while (temp & mask) \ while (temp & mask) \
temp = readl (&hc->regs->roothub.register); \ temp = readl (&hc->regs->roothub.register); \
temp; }) temp; })
...@@ -71,8 +73,10 @@ ohci_hub_status_data (struct usb_hcd *hcd, char *buf) ...@@ -71,8 +73,10 @@ ohci_hub_status_data (struct usb_hcd *hcd, char *buf)
ports = roothub_a (ohci) & RH_A_NDP; ports = roothub_a (ohci) & RH_A_NDP;
if (ports > MAX_ROOT_PORTS) { if (ports > MAX_ROOT_PORTS) {
err ("%s: bogus NDP=%d", hcd->self.bus_name, ports); if (ohci->disabled)
err ("rereads as NDP=%d", return -ESHUTDOWN;
err ("%s bogus NDP=%d, rereads as NDP=%d",
hcd->self.bus_name, ports,
readl (&ohci->regs->roothub.a) & RH_A_NDP); readl (&ohci->regs->roothub.a) & RH_A_NDP);
/* retry later; "should not happen" */ /* retry later; "should not happen" */
return 0; return 0;
......
...@@ -77,12 +77,12 @@ static void finish_urb (struct ohci_hcd *ohci, struct urb *urb) ...@@ -77,12 +77,12 @@ static void finish_urb (struct ohci_hcd *ohci, struct urb *urb)
usb_hcd_giveback_urb (&ohci->hcd, urb); usb_hcd_giveback_urb (&ohci->hcd, urb);
} }
static void td_submit_urb (struct urb *urb); static void td_submit_urb (struct ohci_hcd *ohci, struct urb *urb);
/* Report interrupt transfer completion, maybe reissue */ /* Report interrupt transfer completion, maybe reissue */
static void intr_resub (struct ohci_hcd *hc, struct urb *urb) static inline void intr_resub (struct ohci_hcd *hc, struct urb *urb)
{ {
urb_priv_t *urb_priv = urb->hcpriv; struct urb_priv *urb_priv = urb->hcpriv;
unsigned long flags; unsigned long flags;
// FIXME rewrite this resubmit path. use pci_dma_sync_single() // FIXME rewrite this resubmit path. use pci_dma_sync_single()
...@@ -120,7 +120,7 @@ static void intr_resub (struct ohci_hcd *hc, struct urb *urb) ...@@ -120,7 +120,7 @@ static void intr_resub (struct ohci_hcd *hc, struct urb *urb)
spin_unlock (&urb->lock); spin_unlock (&urb->lock);
spin_lock (&hc->lock); spin_lock (&hc->lock);
td_submit_urb (urb); td_submit_urb (hc, urb);
spin_unlock_irqrestore (&hc->lock, flags); spin_unlock_irqrestore (&hc->lock, flags);
} }
...@@ -518,12 +518,12 @@ static void start_urb_unlink (struct ohci_hcd *ohci, struct ed *ed) ...@@ -518,12 +518,12 @@ static void start_urb_unlink (struct ohci_hcd *ohci, struct ed *ed)
/* enqueue next TD for this URB (OHCI spec 5.2.8.2) */ /* enqueue next TD for this URB (OHCI spec 5.2.8.2) */
static void static void
td_fill (struct ohci_hcd *ohci, unsigned int info, td_fill (unsigned int info,
dma_addr_t data, int len, dma_addr_t data, int len,
struct urb *urb, int index) struct urb *urb, int index)
{ {
struct td *td, *td_pt; struct td *td, *td_pt;
urb_priv_t *urb_priv = urb->hcpriv; struct urb_priv *urb_priv = urb->hcpriv;
int is_iso = info & TD_ISO; int is_iso = info & TD_ISO;
if (index >= urb_priv->length) { if (index >= urb_priv->length) {
...@@ -582,28 +582,30 @@ td_fill (struct ohci_hcd *ohci, unsigned int info, ...@@ -582,28 +582,30 @@ td_fill (struct ohci_hcd *ohci, unsigned int info,
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
/* prepare all TDs of a transfer */ /* Prepare all TDs of a transfer, and queue them onto the ED.
* Caller guarantees HC is active.
static void td_submit_urb (struct urb *urb) * Usually the ED is already on the schedule, so TDs might be
{ * processed as soon as they're queued.
urb_priv_t *urb_priv = urb->hcpriv; */
struct ohci_hcd *ohci = hcd_to_ohci (urb->dev->bus->hcpriv); static void td_submit_urb (
struct ohci_hcd *ohci,
struct urb *urb
) {
struct urb_priv *urb_priv = urb->hcpriv;
dma_addr_t data; dma_addr_t data;
int data_len = urb->transfer_buffer_length; int data_len = urb->transfer_buffer_length;
int cnt = 0; int cnt = 0;
__u32 info = 0; u32 info = 0;
unsigned int toggle = 0;
int is_out = usb_pipeout (urb->pipe); int is_out = usb_pipeout (urb->pipe);
/* OHCI handles the DATA-toggles itself, we just use the /* OHCI handles the bulk/interrupt data toggles itself. We just
* USB-toggle bits for resetting * use the device toggle bits for resetting, and rely on the fact
* that resetting toggle is meaningless if the endpoint is active.
*/ */
if (usb_gettoggle (urb->dev, usb_pipeendpoint (urb->pipe), is_out)) { if (!usb_gettoggle (urb->dev, usb_pipeendpoint (urb->pipe), is_out)) {
toggle = TD_T_TOGGLE;
} else {
toggle = TD_T_DATA0;
usb_settoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_settoggle (urb->dev, usb_pipeendpoint (urb->pipe),
is_out, 1); is_out, 1);
urb_priv->ed->hwHeadP &= ~ED_C;
} }
urb_priv->td_cnt = 0; urb_priv->td_cnt = 0;
...@@ -619,91 +621,88 @@ static void td_submit_urb (struct urb *urb) ...@@ -619,91 +621,88 @@ static void td_submit_urb (struct urb *urb)
/* NOTE: TD_CC is set so we can tell which TDs the HC processed by /* NOTE: TD_CC is set so we can tell which TDs the HC processed by
* using TD_CC_GET, as well as by seeing them on the done list. * using TD_CC_GET, as well as by seeing them on the done list.
* (CC = NotAccessed ... 0x0F, or 0x0E in PSWs for ISO.)
*/ */
switch (usb_pipetype (urb->pipe)) { switch (urb_priv->ed->type) {
case PIPE_BULK:
info = is_out /* Bulk and interrupt are identical except for where in the schedule
? TD_CC | TD_DP_OUT * their EDs live.
: TD_CC | TD_DP_IN ; */
/* TDs _could_ transfer up to 8K each */ // case PIPE_BULK:
while (data_len > 4096) { // case PIPE_INTERRUPT:
td_fill (ohci, default:
info | (cnt? TD_T_TOGGLE:toggle), info = is_out
data, 4096, urb, cnt); ? TD_T_TOGGLE | TD_CC | TD_DP_OUT
data += 4096; data_len -= 4096; cnt++; : TD_T_TOGGLE | TD_CC | TD_DP_IN;
} /* TDs _could_ transfer up to 8K each */
/* maybe avoid ED halt on final TD short read */ while (data_len > 4096) {
if (!(urb->transfer_flags & URB_SHORT_NOT_OK)) td_fill (info, data, 4096, urb, cnt);
info |= TD_R; data += 4096;
td_fill (ohci, info | (cnt ? TD_T_TOGGLE : toggle), data_len -= 4096;
data, data_len, urb, cnt);
cnt++; cnt++;
if ((urb->transfer_flags & USB_ZERO_PACKET) }
&& cnt < urb_priv->length) { /* maybe avoid ED halt on final TD short read */
td_fill (ohci, info | (cnt? TD_T_TOGGLE:toggle), if (!(urb->transfer_flags & URB_SHORT_NOT_OK))
0, 0, urb, cnt); info |= TD_R;
cnt++; td_fill (info, data, data_len, urb, cnt);
} cnt++;
/* start bulk list */ if ((urb->transfer_flags & USB_ZERO_PACKET)
if (!ohci->sleeping) { && cnt < urb_priv->length) {
wmb (); td_fill (info, 0, 0, urb, cnt);
writel (OHCI_BLF, &ohci->regs->cmdstatus); cnt++;
} }
break; /* maybe kickstart bulk list */
if (urb_priv->ed->type == PIPE_BULK) {
wmb ();
writel (OHCI_BLF, &ohci->regs->cmdstatus);
}
break;
case PIPE_INTERRUPT: /* control manages DATA0/DATA1 toggle per-request; SETUP resets it,
/* current policy: only one TD per request. * any DATA phase works normally, and the STATUS ack is special.
* otherwise identical to bulk, except for BLF */
*/ case PIPE_CONTROL:
info = TD_CC | toggle; info = TD_CC | TD_DP_SETUP | TD_T_DATA0;
info |= is_out td_fill (info,
? TD_DP_OUT pci_map_single (ohci->hcd.pdev,
: TD_R | TD_DP_IN; urb->setup_packet, 8,
td_fill (ohci, info, data, data_len, urb, cnt++); PCI_DMA_TODEVICE),
break; 8, urb, cnt++);
if (data_len > 0) {
case PIPE_CONTROL: info = TD_CC | TD_R | TD_T_DATA1;
/* control requests don't use toggle state */ info |= is_out ? TD_DP_OUT : TD_DP_IN;
info = TD_CC | TD_DP_SETUP | TD_T_DATA0; /* NOTE: mishandles transfers >8K, some >4K */
td_fill (ohci, info, td_fill (info, data, data_len, urb, cnt++);
pci_map_single (ohci->hcd.pdev, }
urb->setup_packet, 8, info = is_out
PCI_DMA_TODEVICE), ? TD_CC | TD_DP_IN | TD_T_DATA1
8, urb, cnt++); : TD_CC | TD_DP_OUT | TD_T_DATA1;
if (data_len > 0) { td_fill (info, data, 0, urb, cnt++);
info = TD_CC | TD_R | TD_T_DATA1; /* maybe kickstart control list */
info |= is_out ? TD_DP_OUT : TD_DP_IN; wmb ();
/* NOTE: mishandles transfers >8K, some >4K */ writel (OHCI_CLF, &ohci->regs->cmdstatus);
td_fill (ohci, info, data, data_len, break;
urb, cnt++);
} /* ISO has no retransmit, so no toggle; and it uses special TDs.
info = is_out * Each TD could handle multiple consecutive frames (interval 1);
? TD_CC | TD_DP_IN | TD_T_DATA1 * we could often reduce the number of TDs here.
: TD_CC | TD_DP_OUT | TD_T_DATA1; */
td_fill (ohci, info, data, 0, urb, cnt++); case PIPE_ISOCHRONOUS:
/* start control list */ for (cnt = 0; cnt < urb->number_of_packets; cnt++) {
if (!ohci->sleeping) { int frame = urb->start_frame;
wmb ();
writel (OHCI_CLF, &ohci->regs->cmdstatus); // FIXME scheduling should handle frame counter
} // roll-around ... exotic case (and OHCI has
break; // a 2^16 iso range, vs other HCs max of 2^10)
frame += cnt * urb->interval;
case PIPE_ISOCHRONOUS: frame &= 0xffff;
for (cnt = 0; cnt < urb->number_of_packets; cnt++) { td_fill (TD_CC | TD_ISO | frame,
int frame = urb->start_frame; data + urb->iso_frame_desc [cnt].offset,
urb->iso_frame_desc [cnt].length, urb, cnt);
// FIXME scheduling should handle frame counter }
// roll-around ... exotic case (and OHCI has break;
// a 2^16 iso range, vs other HCs max of 2^10) }
frame += cnt * urb->interval; if (urb_priv->length != cnt)
frame &= 0xffff;
td_fill (ohci, TD_CC | TD_ISO | frame,
data + urb->iso_frame_desc [cnt].offset,
urb->iso_frame_desc [cnt].length, urb, cnt);
}
break;
}
if (urb_priv->length != cnt)
dbg ("TD LENGTH %d != CNT %d", urb_priv->length, cnt); dbg ("TD LENGTH %d != CNT %d", urb_priv->length, cnt);
} }
...@@ -724,8 +723,10 @@ static void td_done (struct urb *urb, struct td *td) ...@@ -724,8 +723,10 @@ static void td_done (struct urb *urb, struct td *td)
u16 tdPSW = le16_to_cpu (td->hwPSW [0]); u16 tdPSW = le16_to_cpu (td->hwPSW [0]);
int dlen = 0; int dlen = 0;
/* NOTE: assumes FC in tdINFO == 0 (and MAXPSW == 1) */
cc = (tdPSW >> 12) & 0xF; cc = (tdPSW >> 12) & 0xF;
if (cc >= 0x0E) /* hc didn't touch? */ if (tdINFO & TD_CC) /* hc didn't touch? */
return; return;
if (usb_pipeout (urb->pipe)) if (usb_pipeout (urb->pipe))
......
...@@ -401,5 +401,5 @@ struct ohci_hcd { ...@@ -401,5 +401,5 @@ struct ohci_hcd {
struct usb_hcd hcd; struct usb_hcd hcd;
}; };
#define hcd_to_ohci(hcd_ptr) list_entry(hcd_ptr, struct ohci_hcd, hcd) #define hcd_to_ohci(hcd_ptr) container_of(hcd_ptr, struct ohci_hcd, hcd)
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