Commit 81d10b10 authored by David Brownell's avatar David Brownell Committed by David S. Miller

[PATCH] ohci-hcd, speedups+misc

- Delays or eliminates some IRQs  It'll mostly affect control
   or iso transfers, which typically have multiple TDs per URB,
   by making only the last TD generate an IRQ.

- Shortens some of the submit path that needs to run with
   IRQs disabled ... no need to use the dma_to_td hashtable.
   (Of course that path is still pretty long...)

- Gets rid of case where the ED state was confused ... now
   there's only one such state, not two.
parent 3db8f6ff
...@@ -100,7 +100,7 @@ ...@@ -100,7 +100,7 @@
* - lots more testing!! * - lots more testing!!
*/ */
#define DRIVER_VERSION "2002-Jun-01" #define DRIVER_VERSION "2002-Jun-10"
#define DRIVER_AUTHOR "Roman Weissgaerber <weissg@vienna.at>, David Brownell" #define DRIVER_AUTHOR "Roman Weissgaerber <weissg@vienna.at>, David Brownell"
#define DRIVER_DESC "USB 1.1 'Open' Host Controller (OHCI) Driver" #define DRIVER_DESC "USB 1.1 'Open' Host Controller (OHCI) Driver"
...@@ -307,7 +307,6 @@ ohci_free_config (struct usb_hcd *hcd, struct usb_device *udev) ...@@ -307,7 +307,6 @@ ohci_free_config (struct usb_hcd *hcd, struct usb_device *udev)
spin_lock_irqsave (&ohci->lock, flags); spin_lock_irqsave (&ohci->lock, flags);
for (i = 0; i < 32; i++) { for (i = 0; i < 32; i++) {
struct ed *ed = dev->ep [i]; struct ed *ed = dev->ep [i];
struct td *tdTailP;
if (!ed) if (!ed)
continue; continue;
...@@ -319,10 +318,7 @@ ohci_free_config (struct usb_hcd *hcd, struct usb_device *udev) ...@@ -319,10 +318,7 @@ ohci_free_config (struct usb_hcd *hcd, struct usb_device *udev)
case ED_NEW: case ED_NEW:
break; break;
case ED_UNLINK: case ED_UNLINK:
tdTailP = dma_to_td (ohci, td_free (ohci, ed->dummy);
le32_to_cpup (&ed->hwTailP) & 0xfffffff0);
td_free (ohci, tdTailP); /* free dummy td */
hash_free_ed (ohci, ed);
break; break;
case ED_OPER: case ED_OPER:
......
...@@ -440,14 +440,16 @@ static struct ed *ep_add_ed ( ...@@ -440,14 +440,16 @@ static struct ed *ep_add_ed (
spin_unlock_irqrestore (&ohci->lock, flags); spin_unlock_irqrestore (&ohci->lock, flags);
return NULL; return NULL;
} }
ed->dummy = td;
ed->hwTailP = cpu_to_le32 (td->td_dma); ed->hwTailP = cpu_to_le32 (td->td_dma);
ed->hwHeadP = ed->hwTailP; ed->hwHeadP = ed->hwTailP; /* ED_C, ED_H zeroed */
ed->state = ED_UNLINK; ed->state = ED_UNLINK;
ed->type = usb_pipetype (pipe); ed->type = usb_pipetype (pipe);
} }
// FIXME: don't do this if it's linked to the HC, // FIXME: don't do this if it's linked to the HC, or without knowing it's
// we might clobber data toggle or other state ... // safe to clobber state/mode info tied to (previous) config/altsetting.
// (but dev0/ep0, used by set_address, must get clobbered)
ed->hwINFO = cpu_to_le32 (usb_pipedevice (pipe) ed->hwINFO = cpu_to_le32 (usb_pipedevice (pipe)
| usb_pipeendpoint (pipe) << 7 | usb_pipeendpoint (pipe) << 7
...@@ -523,27 +525,31 @@ td_fill (struct ohci_hcd *ohci, unsigned int info, ...@@ -523,27 +525,31 @@ td_fill (struct ohci_hcd *ohci, 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)
{ {
volatile struct td *td, *td_pt; struct td *td, *td_pt;
urb_priv_t *urb_priv = urb->hcpriv; urb_priv_t *urb_priv = urb->hcpriv;
int is_iso = info & TD_ISO;
if (index >= urb_priv->length) { if (index >= urb_priv->length) {
err ("internal OHCI error: TD index > length"); err ("internal OHCI error: TD index > length");
return; return;
} }
#if 0 /* aim for only one interrupt per urb. mostly applies to control
/* no interrupt needed except for URB's last TD */ * and iso; other urbs rarely need more than one TD per urb.
*
* NOTE: could delay interrupts even for the last TD, and get fewer
* interrupts ... increasing per-urb latency by sharing interrupts.
*/
if (index != (urb_priv->length - 1)) if (index != (urb_priv->length - 1))
info |= TD_DI; info |= is_iso ? TD_DI_SET (7) : TD_DI_SET (1);
#endif
/* 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];
td_pt->hwNextTD = 0; td_pt->hwNextTD = 0;
/* fill the old dummy TD */ /* fill the old dummy TD */
td = urb_priv->td [index] = dma_to_td (ohci, td = urb_priv->td [index] = urb_priv->ed->dummy;
le32_to_cpup (&urb_priv->ed->hwTailP)); urb_priv->ed->dummy = td_pt;
td->ed = urb_priv->ed; td->ed = urb_priv->ed;
td->next_dl_td = NULL; td->next_dl_td = NULL;
...@@ -554,7 +560,7 @@ td_fill (struct ohci_hcd *ohci, unsigned int info, ...@@ -554,7 +560,7 @@ td_fill (struct ohci_hcd *ohci, unsigned int info,
data = 0; data = 0;
td->hwINFO = cpu_to_le32 (info); td->hwINFO = cpu_to_le32 (info);
if ((td->ed->type) == PIPE_ISOCHRONOUS) { if (is_iso) {
td->hwCBP = cpu_to_le32 (data & 0xFFFFF000); td->hwCBP = cpu_to_le32 (data & 0xFFFFF000);
td->ed->intriso.last_iso = info & 0xffff; td->ed->intriso.last_iso = info & 0xffff;
} else { } else {
...@@ -901,6 +907,7 @@ static void finish_unlinks (struct ohci_hcd *ohci, u16 tick) ...@@ -901,6 +907,7 @@ static void finish_unlinks (struct ohci_hcd *ohci, u16 tick)
* [a2] some (earlier) URBs still linked, re-enable * [a2] some (earlier) URBs still linked, re-enable
* (b) finishing ED unlink * (b) finishing ED unlink
* [b1] no URBs queued, ED is truly idle now * [b1] no URBs queued, ED is truly idle now
* ... we could set state ED_NEW and free dummy
* [b2] URBs now queued, link ED back into schedule * [b2] URBs now queued, link ED back into schedule
* right now we only have (a) * right now we only have (a)
*/ */
...@@ -910,9 +917,6 @@ static void finish_unlinks (struct ohci_hcd *ohci, u16 tick) ...@@ -910,9 +917,6 @@ static void finish_unlinks (struct ohci_hcd *ohci, u16 tick)
if (tdHeadP == tdTailP) { if (tdHeadP == tdTailP) {
if (ed->state == ED_OPER) if (ed->state == ED_OPER)
start_ed_unlink (ohci, ed); start_ed_unlink (ohci, ed);
td_free (ohci, tdTailP);
ed->hwINFO = ED_SKIP;
ed->state = ED_NEW;
} else } else
ed->hwINFO &= ~ED_SKIP; ed->hwINFO &= ~ED_SKIP;
......
...@@ -29,6 +29,7 @@ struct ed { ...@@ -29,6 +29,7 @@ struct ed {
/* rest are purely for the driver's use */ /* rest are purely for the driver's use */
dma_addr_t dma; /* addr of ED */ dma_addr_t dma; /* addr of ED */
struct ed *ed_prev; /* for non-interrupt EDs */ struct ed *ed_prev; /* for non-interrupt EDs */
struct td *dummy;
u8 type; /* PIPE_{BULK,...} */ u8 type; /* PIPE_{BULK,...} */
u8 interval; /* interrupt, isochronous */ u8 interval; /* interrupt, isochronous */
...@@ -63,24 +64,33 @@ struct ed { ...@@ -63,24 +64,33 @@ struct ed {
struct td { struct td {
/* first fields are hardware-specified, le32 */ /* first fields are hardware-specified, le32 */
__u32 hwINFO; /* transfer info bitmask */ __u32 hwINFO; /* transfer info bitmask */
/* hwINFO bits for both general and iso tds: */
#define TD_CC 0xf0000000 /* condition code */ #define TD_CC 0xf0000000 /* condition code */
#define TD_CC_GET(td_p) ((td_p >>28) & 0x0f) #define TD_CC_GET(td_p) ((td_p >>28) & 0x0f)
//#define TD_CC_SET(td_p, cc) (td_p) = ((td_p) & 0x0fffffff) | (((cc) & 0x0f) << 28) //#define TD_CC_SET(td_p, cc) (td_p) = ((td_p) & 0x0fffffff) | (((cc) & 0x0f) << 28)
#define TD_DI 0x00E00000 /* frames before interrupt */
#define TD_DI_SET(X) (((X) & 0x07)<< 21)
/* these two bits are available for definition/use by HCDs in both
* general and iso tds ... others are available for only one type
*/
//#define TD____ 0x00020000
#define TD_ISO 0x00010000 /* copy of ED_ISO */
/* hwINFO bits for general tds: */
#define TD_EC 0x0C000000 /* error count */ #define TD_EC 0x0C000000 /* error count */
#define TD_T 0x03000000 /* data toggle state */ #define TD_T 0x03000000 /* data toggle state */
#define TD_T_DATA0 0x02000000 /* DATA0 */ #define TD_T_DATA0 0x02000000 /* DATA0 */
#define TD_T_DATA1 0x03000000 /* DATA1 */ #define TD_T_DATA1 0x03000000 /* DATA1 */
#define TD_T_TOGGLE 0x00000000 /* uses ED_C */ #define TD_T_TOGGLE 0x00000000 /* uses ED_C */
#define TD_DI 0x00E00000 /* frames before interrupt */
//#define TD_DI_SET(X) (((X) & 0x07)<< 21)
#define TD_DP 0x00180000 /* direction/pid */ #define TD_DP 0x00180000 /* direction/pid */
#define TD_DP_SETUP 0x00000000 /* SETUP pid */ #define TD_DP_SETUP 0x00000000 /* SETUP pid */
#define TD_DP_IN 0x00100000 /* IN pid */ #define TD_DP_IN 0x00100000 /* IN pid */
#define TD_DP_OUT 0x00080000 /* OUT pid */ #define TD_DP_OUT 0x00080000 /* OUT pid */
/* 0x00180000 rsvd */ /* 0x00180000 rsvd */
#define TD_R 0x00040000 /* round: short packets OK? */ #define TD_R 0x00040000 /* round: short packets OK? */
/* bits 0x1ffff are defined by HCD */
#define TD_ISO 0x00010000 /* copy of ED_ISO */ /* (no hwINFO #defines yet for iso tds) */
__u32 hwCBP; /* Current Buffer Pointer (or 0) */ __u32 hwCBP; /* Current Buffer Pointer (or 0) */
__u32 hwNextTD; /* Next TD Pointer */ __u32 hwNextTD; /* Next TD Pointer */
......
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