# This is a BitKeeper generated patch for the following project: # Project Name: Linux kernel tree # This patch format is intended for GNU patch command version 2.5 or higher. # This patch includes the following deltas: # ChangeSet 1.383.20.1 -> 1.383.20.2 # drivers/usb/hcd/ehci-q.c 1.1 -> 1.2 # drivers/usb/hcd/ehci-hub.c 1.1 -> 1.2 # drivers/usb/hcd/ehci.h 1.1 -> 1.2 # drivers/usb/hcd/ehci-sched.c 1.1 -> 1.2 # drivers/usb/hcd/ehci-mem.c 1.2 -> 1.3 # drivers/usb/hcd/ehci-hcd.c 1.3 -> 1.4 # # The following is the BitKeeper ChangeSet Log # -------------------------------------------- # 02/05/02 david-b@pacbell.net 1.383.20.2 # [PATCH] PATCH 2.4.19-pre7 sync ehci-hcd with 2.5 # # USB ehci update # # Short version of the story: syncs the driver with the 2.5 version. # Includes preliminary high speed ISO support, fixes for interrupt # transfer scheduling and some (rare) submission path errors. # That's most of the patch, by volume; there are other updates. # -------------------------------------------- # diff -Nru a/drivers/usb/hcd/ehci-hcd.c b/drivers/usb/hcd/ehci-hcd.c --- a/drivers/usb/hcd/ehci-hcd.c Fri May 3 14:51:44 2002 +++ b/drivers/usb/hcd/ehci-hcd.c Fri May 3 14:51:44 2002 @@ -70,6 +70,11 @@ * * HISTORY: * + * 2002-04-19 Control/bulk/interrupt submit no longer uses giveback() on + * errors in submit path. Bugfixes to interrupt scheduling/processing. + * 2002-03-05 Initial high-speed ISO support; reduce ITD memory; shift + * more checking to generic hcd framework (db). Make it work with + * Philips EHCI; reduce PCI traffic; shorten IRQ path (Rory Bolt). * 2002-01-14 Minor cleanup; version synch. * 2002-01-08 Fix roothub handoff of FS/LS to companion controllers. * 2002-01-04 Control/Bulk queuing behaves. @@ -84,7 +89,7 @@ // #define EHCI_VERBOSE_DEBUG -// #define have_iso +// #define have_split_iso /* magic numbers that can affect system performance */ #define EHCI_TUNE_CERR 3 /* 0-3 qtd retries; 0 == don't stop */ @@ -180,6 +185,9 @@ dbg_hcs_params (ehci, "ehci_start"); dbg_hcc_params (ehci, "ehci_start"); + /* cache this readonly data; minimize PCI reads */ + ehci->hcs_params = readl (&ehci->caps->hcs_params); + /* * hw default: 1K periodic list heads, one per frame. * periodic_size can shrink by USBCMD update if hcc_params allows. @@ -197,11 +205,12 @@ ehci->async = 0; ehci->reclaim = 0; - ehci->next_frame = -1; + ehci->next_uframe = -1; /* controller state: unknown --> reset */ /* EHCI spec section 4.1 */ + // FIXME require STS_HALT before reset... ehci_reset (ehci); writel (INTR_MASK, &ehci->regs->intr_enable); writel (ehci->periodic_dma, &ehci->regs->frame_list); @@ -307,7 +316,7 @@ // root hub is shut down separately (first, when possible) scan_async (ehci); - if (ehci->next_frame != -1) + if (ehci->next_uframe != -1) scan_periodic (ehci); ehci_mem_cleanup (ehci); @@ -329,14 +338,12 @@ static int ehci_suspend (struct usb_hcd *hcd, u32 state) { struct ehci_hcd *ehci = hcd_to_ehci (hcd); - u32 params; int ports; int i; dbg ("%s: suspend to %d", hcd->bus_name, state); - params = readl (&ehci->caps->hcs_params); - ports = HCS_N_PORTS (params); + ports = HCS_N_PORTS (ehci->hcs_params); // FIXME: This assumes what's probably a D3 level suspend... @@ -372,14 +379,12 @@ static int ehci_resume (struct usb_hcd *hcd) { struct ehci_hcd *ehci = hcd_to_ehci (hcd); - u32 params; int ports; int i; dbg ("%s: resume", hcd->bus_name); - params = readl (&ehci->caps->hcs_params); - ports = HCS_N_PORTS (params); + ports = HCS_N_PORTS (ehci->hcs_params); // FIXME: if controller didn't retain state, // return and let generic code clean it up @@ -425,7 +430,7 @@ if (ehci->reclaim_ready) end_unlink_async (ehci); scan_async (ehci); - if (ehci->next_frame != -1) + if (ehci->next_uframe != -1) scan_periodic (ehci); // FIXME: when nothing is connected to the root hub, @@ -510,8 +515,7 @@ case PIPE_BULK: if (!qh_urb_transaction (ehci, urb, &qtd_list, mem_flags)) return -ENOMEM; - submit_async (ehci, urb, &qtd_list, mem_flags); - break; + return submit_async (ehci, urb, &qtd_list, mem_flags); case PIPE_INTERRUPT: if (!qh_urb_transaction (ehci, urb, &qtd_list, mem_flags)) @@ -519,20 +523,19 @@ return intr_submit (ehci, urb, &qtd_list, mem_flags); case PIPE_ISOCHRONOUS: -#ifdef have_iso if (urb->dev->speed == USB_SPEED_HIGH) - return itd_submit (ehci, urb); + return itd_submit (ehci, urb, mem_flags); +#ifdef have_split_iso else - return sitd_submit (ehci, urb); + return sitd_submit (ehci, urb, mem_flags); #else - // FIXME highspeed iso stuff is written but never run/tested. - // and the split iso support isn't even written yet. - dbg ("no iso support yet"); + dbg ("no split iso support yet"); return -ENOSYS; -#endif /* have_iso */ +#endif /* have_split_iso */ + default: /* can't happen */ + return -ENOSYS; } - return 0; } /* remove from hardware lists @@ -587,7 +590,7 @@ // itd or sitd ... // wait till next completion, do it then. - // completion irqs can wait up to 128 msec, + // completion irqs can wait up to 1024 msec, urb->transfer_flags |= EHCI_STATE_UNLINK; return 0; } @@ -614,10 +617,7 @@ if (dev->ep [i]) { struct ehci_qh *qh; - // FIXME: this might be an itd/sitd too ... - // or an interrupt urb (not on async list) - // can use "union ehci_shadow" - + /* dev->ep never has ITDs or SITDs */ qh = (struct ehci_qh *) dev->ep [i]; vdbg ("free_config, ep 0x%02x qh %p", i, qh); if (!list_empty (&qh->qtd_list)) { @@ -644,7 +644,7 @@ spin_lock_irqsave (&ehci->lock, flags); } } - qh_unput (ehci, qh); + qh_put (ehci, qh); } } diff -Nru a/drivers/usb/hcd/ehci-hub.c b/drivers/usb/hcd/ehci-hub.c --- a/drivers/usb/hcd/ehci-hub.c Fri May 3 14:51:44 2002 +++ b/drivers/usb/hcd/ehci-hub.c Fri May 3 14:51:44 2002 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001 by David Brownell + * Copyright (c) 2001-2002 by David Brownell * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -68,8 +68,7 @@ /* init status to no-changes */ buf [0] = 0; - temp = readl (&ehci->caps->hcs_params); - ports = HCS_N_PORTS (temp); + ports = HCS_N_PORTS (ehci->hcs_params); if (ports > 7) { buf [1] = 0; retval++; @@ -92,7 +91,10 @@ if (!(temp & PORT_CONNECT)) ehci->reset_done [i] = 0; if ((temp & (PORT_CSC | PORT_PEC | PORT_OCC)) != 0) { - set_bit (i, buf); + if (i < 7) + buf [0] |= 1 << (i + 1); + else + buf [1] |= 1 << (i - 7); status = STS_PCD; } } @@ -107,8 +109,7 @@ struct ehci_hcd *ehci, struct usb_hub_descriptor *desc ) { - u32 params = readl (&ehci->caps->hcs_params); - int ports = HCS_N_PORTS (params); + int ports = HCS_N_PORTS (ehci->hcs_params); u16 temp; desc->bDescriptorType = 0x29; @@ -124,10 +125,10 @@ memset (&desc->bitmap [temp], 0xff, temp); temp = 0x0008; /* per-port overcurrent reporting */ - if (HCS_PPC (params)) /* per-port power control */ - temp |= 0x0001; - if (HCS_INDICATOR (params)) /* per-port indicators (LEDs) */ - temp |= 0x0080; + if (HCS_PPC (ehci->hcs_params)) + temp |= 0x0001; /* per-port power control */ + if (HCS_INDICATOR (ehci->hcs_params)) + temp |= 0x0080; /* per-port indicators (LEDs) */ desc->wHubCharacteristics = cpu_to_le16 (temp); } @@ -142,9 +143,8 @@ u16 wLength ) { struct ehci_hcd *ehci = hcd_to_ehci (hcd); - u32 params = readl (&ehci->caps->hcs_params); - int ports = HCS_N_PORTS (params); - u32 temp; + int ports = HCS_N_PORTS (ehci->hcs_params); + u32 temp, status; unsigned long flags; int retval = 0; @@ -189,7 +189,7 @@ /* ? */ break; case USB_PORT_FEAT_POWER: - if (HCS_PPC (params)) + if (HCS_PPC (ehci->hcs_params)) writel (temp & ~PORT_POWER, &ehci->regs->port_status [wIndex]); break; @@ -222,22 +222,22 @@ if (!wIndex || wIndex > ports) goto error; wIndex--; - memset (buf, 0, 4); + status = 0; temp = readl (&ehci->regs->port_status [wIndex]); // wPortChange bits if (temp & PORT_CSC) - set_bit (USB_PORT_FEAT_C_CONNECTION, buf); + status |= 1 << USB_PORT_FEAT_C_CONNECTION; if (temp & PORT_PEC) - set_bit (USB_PORT_FEAT_C_ENABLE, buf); + status |= 1 << USB_PORT_FEAT_C_ENABLE; // USB_PORT_FEAT_C_SUSPEND if (temp & PORT_OCC) - set_bit (USB_PORT_FEAT_C_OVER_CURRENT, buf); + status |= 1 << USB_PORT_FEAT_C_OVER_CURRENT; /* whoever resets must GetPortStatus to complete it!! */ if ((temp & PORT_RESET) && jiffies > ehci->reset_done [wIndex]) { - set_bit (USB_PORT_FEAT_C_RESET, buf); + status |= 1 << USB_PORT_FEAT_C_RESET; /* force reset to complete */ writel (temp & ~PORT_RESET, @@ -255,26 +255,27 @@ // don't show wPortStatus if it's owned by a companion hc if (!(temp & PORT_OWNER)) { if (temp & PORT_CONNECT) { - set_bit (USB_PORT_FEAT_CONNECTION, buf); - set_bit (USB_PORT_FEAT_HIGHSPEED, buf); + status |= 1 << USB_PORT_FEAT_CONNECTION; + status |= 1 << USB_PORT_FEAT_HIGHSPEED; } if (temp & PORT_PE) - set_bit (USB_PORT_FEAT_ENABLE, buf); + status |= 1 << USB_PORT_FEAT_ENABLE; if (temp & PORT_SUSPEND) - set_bit (USB_PORT_FEAT_SUSPEND, buf); + status |= 1 << USB_PORT_FEAT_SUSPEND; if (temp & PORT_OC) - set_bit (USB_PORT_FEAT_OVER_CURRENT, buf); + status |= 1 << USB_PORT_FEAT_OVER_CURRENT; if (temp & PORT_RESET) - set_bit (USB_PORT_FEAT_RESET, buf); + status |= 1 << USB_PORT_FEAT_RESET; if (temp & PORT_POWER) - set_bit (USB_PORT_FEAT_POWER, buf); + status |= 1 << USB_PORT_FEAT_POWER; } #ifndef EHCI_VERBOSE_DEBUG - if (*(u16*)(buf+2)) /* only if wPortChange is interesting */ + if (status & ~0xffff) /* only if wPortChange is interesting */ #endif dbg_port (hcd, "GetStatus", wIndex + 1, temp); - cpu_to_le32s ((u32 *) buf); + // we "know" this alignment is good, caller used kmalloc()... + *((u32 *) buf) = cpu_to_le32 (status); break; case SetHubFeature: switch (wValue) { @@ -300,7 +301,7 @@ &ehci->regs->port_status [wIndex]); break; case USB_PORT_FEAT_POWER: - if (HCS_PPC (params)) + if (HCS_PPC (ehci->hcs_params)) writel (temp | PORT_POWER, &ehci->regs->port_status [wIndex]); break; diff -Nru a/drivers/usb/hcd/ehci-mem.c b/drivers/usb/hcd/ehci-mem.c --- a/drivers/usb/hcd/ehci-mem.c Fri May 3 14:51:44 2002 +++ b/drivers/usb/hcd/ehci-mem.c Fri May 3 14:51:44 2002 @@ -98,16 +98,16 @@ } /* to share a qh (cpu threads, or hc) */ -static inline struct ehci_qh *qh_put (/* ehci, */ struct ehci_qh *qh) +static inline struct ehci_qh *qh_get (/* ehci, */ struct ehci_qh *qh) { - // dbg ("put %p (%d++)", qh, qh->refcount.counter); + // dbg ("get %p (%d++)", qh, qh->refcount.counter); atomic_inc (&qh->refcount); return qh; } -static void qh_unput (struct ehci_hcd *ehci, struct ehci_qh *qh) +static void qh_put (struct ehci_hcd *ehci, struct ehci_qh *qh) { - // dbg ("unput %p (--%d)", qh, qh->refcount.counter); + // dbg ("put %p (--%d)", qh, qh->refcount.counter); if (!atomic_dec_and_test (&qh->refcount)) return; /* clean qtds first, and know this is not linked */ diff -Nru a/drivers/usb/hcd/ehci-q.c b/drivers/usb/hcd/ehci-q.c --- a/drivers/usb/hcd/ehci-q.c Fri May 3 14:51:44 2002 +++ b/drivers/usb/hcd/ehci-q.c Fri May 3 14:51:44 2002 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001 by David Brownell + * Copyright (c) 2001-2002 by David Brownell * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -194,7 +194,7 @@ ? PCI_DMA_FROMDEVICE : PCI_DMA_TODEVICE); if (likely (urb->hcpriv != 0)) { - qh_unput (ehci, (struct ehci_qh *) urb->hcpriv); + qh_put (ehci, (struct ehci_qh *) urb->hcpriv); urb->hcpriv = 0; } @@ -280,6 +280,8 @@ /* if these qtds were queued to the HC, some may be active. * else we're cleaning up after a failed URB submission. + * + * FIXME can simplify: cleanup case is gone now. */ if (likely (qh != 0)) { int qh_halted; @@ -406,6 +408,49 @@ /*-------------------------------------------------------------------------*/ /* + * reverse of qh_urb_transaction: free a list of TDs. + * used for cleanup after errors, before HC sees an URB's TDs. + */ +static void qtd_list_free ( + struct ehci_hcd *ehci, + struct urb *urb, + struct list_head *qtd_list +) { + struct list_head *entry, *temp; + int unmapped = 0; + + list_for_each_safe (entry, temp, qtd_list) { + struct ehci_qtd *qtd; + + qtd = list_entry (entry, struct ehci_qtd, qtd_list); + list_del (&qtd->qtd_list); + if (unmapped != 2) { + int direction; + size_t size; + + /* for ctrl unmap twice: SETUP and DATA; + * else (bulk, intr) just once: DATA + */ + if (!unmapped++ && usb_pipecontrol (urb->pipe)) { + direction = PCI_DMA_TODEVICE; + size = sizeof (devrequest); + } else { + direction = usb_pipein (urb->pipe) + ? PCI_DMA_FROMDEVICE + : PCI_DMA_TODEVICE; + size = qtd->urb->transfer_buffer_length; + unmapped++; + } + if (qtd->buf_dma) + pci_unmap_single (ehci->hcd.pdev, + qtd->buf_dma, + size, direction); + } + ehci_qtd_free (ehci, qtd); + } +} + +/* * create a list of filled qtds for this URB; won't link into qh. */ static struct list_head * @@ -436,14 +481,6 @@ if (usb_pipecontrol (urb->pipe)) { /* control request data is passed in the "setup" pid */ - - /* NOTE: this isn't smart about 64bit DMA, since it uses the - * default (32bit) mask rather than using the whole address - * space. we could set pdev->dma_mask to all-ones while - * getting this mapping, locking it and restoring before - * allocating qtd/qh/... or maybe only do that for the main - * data phase (below). - */ qtd->buf_dma = pci_map_single ( ehci->hcd.pdev, urb->setup_packet, @@ -472,7 +509,6 @@ */ len = urb->transfer_buffer_length; if (likely (len > 0)) { - /* NOTE: sub-optimal mapping with 64bit DMA (see above) */ buf = map_buf = pci_map_single (ehci->hcd.pdev, urb->transfer_buffer, len, usb_pipein (urb->pipe) @@ -556,8 +592,7 @@ return head; cleanup: - urb->status = -ENOMEM; - qh_completions (ehci, head, 1); + qtd_list_free (ehci, urb, head); return 0; } @@ -643,12 +678,19 @@ if (usb_pipecontrol (urb->pipe)) { info1 |= 64 << 16; /* usb2 fixed maxpacket */ info1 |= 1 << 14; /* toggle from qtd */ + info2 |= (EHCI_TUNE_MULT_HS << 30); } else if (usb_pipebulk (urb->pipe)) { info1 |= 512 << 16; /* usb2 fixed maxpacket */ info2 |= (EHCI_TUNE_MULT_HS << 30); - } else - info1 |= usb_maxpacket (urb->dev, urb->pipe, - usb_pipeout (urb->pipe)) << 16; + } else { + u32 temp; + temp = usb_maxpacket (urb->dev, urb->pipe, + usb_pipeout (urb->pipe)); + info1 |= (temp & 0x3ff) << 16; /* maxpacket */ + /* HS intr can be "high bandwidth" */ + temp = 1 + ((temp >> 11) & 0x03); + info2 |= temp << 30; /* mult */ + } break; default: #ifdef DEBUG @@ -715,7 +757,7 @@ /*-------------------------------------------------------------------------*/ -static void +static int submit_async ( struct ehci_hcd *ehci, struct urb *urb, @@ -809,21 +851,23 @@ if (likely (qh != 0)) { // dbg_qh ("new qh", ehci, qh); dev->ep [epnum] = qh; - } else - urb->status = -ENOMEM; + } } /* Control/bulk operations through TTs don't need scheduling, * the HC and TT handle it when the TT has a buffer ready. */ if (likely (qh != 0)) { - urb->hcpriv = qh_put (qh); + urb->hcpriv = qh_get (qh); if (likely (qh->qh_state == QH_STATE_IDLE)) - qh_link_async (ehci, qh_put (qh)); + qh_link_async (ehci, qh_get (qh)); } spin_unlock_irqrestore (&ehci->lock, flags); - if (unlikely (!qh)) - qh_completions (ehci, qtd_list, 1); + if (unlikely (qh == 0)) { + qtd_list_free (ehci, urb, qtd_list); + return -ENOMEM; + } + return 0; } /*-------------------------------------------------------------------------*/ @@ -837,7 +881,7 @@ qh->qh_state = QH_STATE_IDLE; qh->qh_next.qh = 0; - qh_unput (ehci, qh); // refcount from reclaim + qh_put (ehci, qh); // refcount from reclaim ehci->reclaim = 0; ehci->reclaim_ready = 0; @@ -849,7 +893,7 @@ && HCD_IS_RUNNING (ehci->hcd.state)) qh_link_async (ehci, qh); else - qh_unput (ehci, qh); // refcount from async list + qh_put (ehci, qh); // refcount from async list } @@ -874,7 +918,7 @@ #endif qh->qh_state = QH_STATE_UNLINK; - ehci->reclaim = qh = qh_put (qh); + ehci->reclaim = qh = qh_get (qh); // dbg_qh ("start unlink", ehci, qh); @@ -939,14 +983,14 @@ /* clean any finished work for this qh */ if (!list_empty (&qh->qtd_list)) { // dbg_qh ("scan_async", ehci, qh); - qh = qh_put (qh); + qh = qh_get (qh); spin_unlock_irqrestore (&ehci->lock, flags); /* concurrent unlink could happen here */ qh_completions (ehci, &qh->qtd_list, 1); spin_lock_irqsave (&ehci->lock, flags); - qh_unput (ehci, qh); + qh_put (ehci, qh); } /* unlink idle entries (reduces PCI usage) */ diff -Nru a/drivers/usb/hcd/ehci-sched.c b/drivers/usb/hcd/ehci-sched.c --- a/drivers/usb/hcd/ehci-sched.c Fri May 3 14:51:44 2002 +++ b/drivers/usb/hcd/ehci-sched.c Fri May 3 14:51:44 2002 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001 by David Brownell + * Copyright (c) 2001-2002 by David Brownell * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -55,12 +55,12 @@ return &periodic->qh->qh_next; case Q_TYPE_FSTN: return &periodic->fstn->fstn_next; -#ifdef have_iso case Q_TYPE_ITD: return &periodic->itd->itd_next; +#ifdef have_split_iso case Q_TYPE_SITD: return &periodic->sitd->sitd_next; -#endif /* have_iso */ +#endif /* have_split_iso */ } dbg ("BAD shadow %p tag %d", periodic->ptr, tag); // BUG (); @@ -109,9 +109,6 @@ u32 *hw_p = &ehci->periodic [frame]; union ehci_shadow *q = &ehci->pshadow [frame]; unsigned usecs = 0; -#ifdef have_iso - u32 temp = 0; -#endif while (q->ptr) { switch (Q_NEXT_TYPE (*hw_p)) { @@ -130,15 +127,13 @@ } q = &q->fstn->fstn_next; break; -#ifdef have_iso case Q_TYPE_ITD: - temp = le32_to_cpu (q->itd->transaction [uframe]); - temp >>= 16; - temp &= 0x0fff; - if (temp) - usecs += HS_USECS_ISO (temp); + /* NOTE the "one uframe per itd" policy */ + if (q->itd->hw_transaction [uframe] != 0) + usecs += q->itd->usecs; q = &q->itd->itd_next; break; +#ifdef have_split_iso case Q_TYPE_SITD: temp = q->sitd->hw_fullspeed_ep & __constant_cpu_to_le32 (1 << 31); @@ -163,7 +158,7 @@ } q = &q->sitd->sitd_next; break; -#endif /* have_iso */ +#endif /* have_split_iso */ default: BUG (); } @@ -178,6 +173,45 @@ /*-------------------------------------------------------------------------*/ +static void enable_periodic (struct ehci_hcd *ehci) +{ + u32 cmd; + + /* did clearing PSE did take effect yet? + * takes effect only at frame boundaries... + */ + while (readl (&ehci->regs->status) & STS_PSS) + udelay (20); + + cmd = readl (&ehci->regs->command) | CMD_PSE; + writel (cmd, &ehci->regs->command); + /* posted write ... PSS happens later */ + ehci->hcd.state = USB_STATE_RUNNING; + + /* make sure tasklet scans these */ + ehci->next_uframe = readl (&ehci->regs->frame_index) + % (ehci->periodic_size << 3); +} + +static void disable_periodic (struct ehci_hcd *ehci) +{ + u32 cmd; + + /* did setting PSE not take effect yet? + * takes effect only at frame boundaries... + */ + while (!(readl (&ehci->regs->status) & STS_PSS)) + udelay (20); + + cmd = readl (&ehci->regs->command) & ~CMD_PSE; + writel (cmd, &ehci->regs->command); + /* posted write ... */ + + ehci->next_uframe = -1; +} + +/*-------------------------------------------------------------------------*/ + static void intr_deschedule ( struct ehci_hcd *ehci, unsigned frame, @@ -186,11 +220,13 @@ ) { unsigned long flags; + period >>= 3; // FIXME microframe periods not handled yet + spin_lock_irqsave (&ehci->lock, flags); do { periodic_unlink (ehci, frame, qh); - qh_unput (ehci, qh); + qh_put (ehci, qh); frame += period; } while (frame < ehci->periodic_size); @@ -199,21 +235,9 @@ ehci->periodic_urbs--; /* maybe turn off periodic schedule */ - if (!ehci->periodic_urbs) { - u32 cmd = readl (&ehci->regs->command); - - /* did setting PSE not take effect yet? - * takes effect only at frame boundaries... - */ - while (!(readl (&ehci->regs->status) & STS_PSS)) - udelay (20); - - cmd &= ~CMD_PSE; - writel (cmd, &ehci->regs->command); - /* posted write ... */ - - ehci->next_frame = -1; - } else + if (!ehci->periodic_urbs) + disable_periodic (ehci); + else vdbg ("periodic schedule still enabled"); spin_unlock_irqrestore (&ehci->lock, flags); @@ -234,6 +258,38 @@ atomic_read (&qh->refcount), ehci->periodic_urbs); } +static int check_period ( + struct ehci_hcd *ehci, + unsigned frame, + int uframe, + unsigned period, + unsigned usecs +) { + /* + * 80% periodic == 100 usec/uframe available + * convert "usecs we need" to "max already claimed" + */ + usecs = 100 - usecs; + + do { + int claimed; + +// FIXME delete when intr_submit handles non-empty queues +// this gives us a one intr/frame limit (vs N/uframe) + if (ehci->pshadow [frame].ptr) + return 0; + + claimed = periodic_usecs (ehci, frame, uframe); + if (claimed > usecs) + return 0; + +// FIXME update to handle sub-frame periods + } while ((frame += period) < ehci->periodic_size); + + // success! + return 1; +} + static int intr_submit ( struct ehci_hcd *ehci, struct urb *urb, @@ -241,8 +297,7 @@ int mem_flags ) { unsigned epnum, period; - unsigned temp; - unsigned short mult, usecs; + unsigned short usecs; unsigned long flags; struct ehci_qh *qh; struct hcd_dev *dev; @@ -250,17 +305,9 @@ /* get endpoint and transfer data */ epnum = usb_pipeendpoint (urb->pipe); - if (usb_pipein (urb->pipe)) { - temp = urb->dev->epmaxpacketin [epnum]; + if (usb_pipein (urb->pipe)) epnum |= 0x10; - } else - temp = urb->dev->epmaxpacketout [epnum]; - mult = 1; - if (urb->dev->speed == USB_SPEED_HIGH) { - /* high speed "high bandwidth" is coded in ep maxpacket */ - mult += (temp >> 11) & 0x03; - temp &= 0x03ff; - } else { + if (urb->dev->speed != USB_SPEED_HIGH) { dbg ("no intr/tt scheduling yet"); status = -ENOSYS; goto done; @@ -270,6 +317,13 @@ * NOTE: current completion/restart logic doesn't handle more than * one qtd in a periodic qh ... 16-20 KB/urb is pretty big for this. * such big requests need many periods to transfer. + * + * FIXME want to change hcd core submit model to expect queuing + * for all transfer types ... not just ISO and (with flag) BULK. + * that means: getting rid of this check; handling the "interrupt + * urb already queued" case below like bulk queuing is handled (no + * errors possible!); and completly getting rid of that annoying + * qh restart logic. simpler/smaller overall, and more flexible. */ if (unlikely (qtd_list->next != qtd_list->prev)) { dbg ("only one intr qtd per urb allowed"); @@ -279,20 +333,13 @@ usecs = HS_USECS (urb->transfer_buffer_length); - /* - * force a power-of-two (frames) sized polling interval - * - * NOTE: endpoint->bInterval for highspeed is measured in uframes, - * while for full/low speeds it's in frames. Here we "know" that - * urb->interval doesn't give acccess to high interrupt rates. - */ - period = ehci->periodic_size; - temp = period; - if (unlikely (urb->interval < 1)) - urb->interval = 1; - while (temp > urb->interval) - temp >>= 1; - period = urb->interval = temp; + /* FIXME handle HS periods of less than 1 frame. */ + period = urb->interval >> 3; + if (period < 1) { + dbg ("intr period %d uframes, NYET!", urb->interval); + status = -EINVAL; + goto done; + } spin_lock_irqsave (&ehci->lock, flags); @@ -318,55 +365,47 @@ list_splice (qtd_list, &qh->qtd_list); qh_update (qh, list_entry (qtd_list->next, struct ehci_qtd, qtd_list)); + qtd_list = &qh->qtd_list; } } else { /* can't sleep here, we have ehci->lock... */ qh = ehci_qh_make (ehci, urb, qtd_list, SLAB_ATOMIC); - qtd_list = &qh->qtd_list; if (likely (qh != 0)) { // dbg ("new INTR qh %p", qh); dev->ep [epnum] = qh; + qtd_list = &qh->qtd_list; } else status = -ENOMEM; } /* Schedule this periodic QH. */ if (likely (status == 0)) { - unsigned frame = urb->interval; + unsigned frame = period; qh->hw_next = EHCI_LIST_END; - qh->hw_info2 |= cpu_to_le32 (mult << 30); qh->usecs = usecs; - urb->hcpriv = qh_put (qh); + urb->hcpriv = qh_get (qh); status = -ENOSPC; /* pick a set of schedule slots, link the QH into them */ do { int uframe; - /* Select some frame 0..(urb->interval - 1) with a - * microframe that can hold this transaction. + /* pick a set of slots such that all uframes have + * enough periodic bandwidth available. * * FIXME for TT splits, need uframes for start and end. * FSTNs can put end into next frame (uframes 0 or 1). */ frame--; for (uframe = 0; uframe < 8; uframe++) { - int claimed; - claimed = periodic_usecs (ehci, frame, uframe); - /* 80% periodic == 100 usec max committed */ - if ((claimed + usecs) <= 100) { - vdbg ("frame %d.%d: %d usecs, plus %d", - frame, uframe, claimed, usecs); + if (check_period (ehci, frame, uframe, + period, usecs) != 0) break; - } } if (uframe == 8) continue; -// FIXME delete when code below handles non-empty queues - if (ehci->pshadow [frame].ptr) - continue; /* QH will run once each period, starting there */ urb->start_frame = frame; @@ -378,15 +417,16 @@ /* stuff into the periodic schedule */ qh->qh_state = QH_STATE_LINKED; - vdbg ("qh %p usecs %d period %d starting frame %d.%d", + vdbg ("qh %p usecs %d period %d starting %d.%d", qh, qh->usecs, period, frame, uframe); do { if (unlikely (ehci->pshadow [frame].ptr != 0)) { -// FIXME -- just link to the end, before any qh with a shorter period, +// FIXME -- just link toward the end, before any qh with a shorter period, // AND handle it already being (implicitly) linked into this frame +// AS WELL AS updating the check_period() logic BUG (); } else { - ehci->pshadow [frame].qh = qh_put (qh); + ehci->pshadow [frame].qh = qh_get (qh); ehci->periodic [frame] = QH_NEXT (qh->qh_dma); } @@ -394,40 +434,20 @@ } while (frame < ehci->periodic_size); /* update bandwidth utilization records (for usbfs) */ - usb_claim_bandwidth (urb->dev, urb, usecs, 0); + usb_claim_bandwidth (urb->dev, urb, usecs/period, 0); /* maybe enable periodic schedule processing */ - if (!ehci->periodic_urbs++) { - u32 cmd; - - /* did clearing PSE did take effect yet? - * takes effect only at frame boundaries... - */ - while (readl (&ehci->regs->status) & STS_PSS) - udelay (20); - - cmd = readl (&ehci->regs->command) | CMD_PSE; - writel (cmd, &ehci->regs->command); - /* posted write ... PSS happens later */ - ehci->hcd.state = USB_STATE_RUNNING; - - /* make sure tasklet scans these */ - ehci->next_frame = ehci_get_frame (&ehci->hcd); - } + if (!ehci->periodic_urbs++) + enable_periodic (ehci); break; } while (frame); } spin_unlock_irqrestore (&ehci->lock, flags); done: - if (status) { - usb_complete_t complete = urb->complete; + if (status) + qtd_list_free (ehci, urb, qtd_list); - urb->complete = 0; - urb->status = status; - qh_completions (ehci, qtd_list, 1); - urb->complete = complete; - } return status; } @@ -446,6 +466,10 @@ if (likely ((qh->hw_token & __constant_cpu_to_le32 (QTD_STS_ACTIVE)) != 0)) return flags; + if (unlikely (list_empty (&qh->qtd_list))) { + dbg ("intr qh %p no TDs?", qh); + return flags; + } qtd = list_entry (qh->qtd_list.next, struct ehci_qtd, qtd_list); urb = qtd->urb; @@ -489,53 +513,56 @@ /*-------------------------------------------------------------------------*/ -#ifdef have_iso - -static inline void itd_free (struct ehci_hcd *ehci, struct ehci_itd *itd) +static void +itd_free_list (struct ehci_hcd *ehci, struct urb *urb) { - pci_pool_free (ehci->itd_pool, itd, itd->itd_dma); + struct ehci_itd *first_itd = urb->hcpriv; + + pci_unmap_single (ehci->hcd.pdev, + first_itd->buf_dma, urb->transfer_buffer_length, + usb_pipein (urb->pipe) + ? PCI_DMA_FROMDEVICE + : PCI_DMA_TODEVICE); + while (!list_empty (&first_itd->itd_list)) { + struct ehci_itd *itd; + + itd = list_entry ( + first_itd->itd_list.next, + struct ehci_itd, itd_list); + list_del (&itd->itd_list); + pci_pool_free (ehci->itd_pool, itd, itd->itd_dma); + } + pci_pool_free (ehci->itd_pool, first_itd, first_itd->itd_dma); + urb->hcpriv = 0; } -/* - * Create itd and allocate into uframes within specified frame. - * Caller must update the resulting uframe links. - */ -static struct ehci_itd * -itd_make ( +static int +itd_fill ( struct ehci_hcd *ehci, + struct ehci_itd *itd, struct urb *urb, unsigned index, // urb->iso_frame_desc [index] - unsigned frame, // scheduled start - dma_addr_t dma, // mapped transfer buffer - int mem_flags + dma_addr_t dma // mapped transfer buffer ) { - struct ehci_itd *itd; u64 temp; u32 buf1; - unsigned epnum, maxp, multi, usecs; + unsigned i, epnum, maxp, multi; unsigned length; - unsigned i, bufnum; - - /* allocate itd, start to fill it */ - itd = pci_pool_alloc (ehci->itd_pool, mem_flags, &dma); - if (!itd) - return itd; itd->hw_next = EHCI_LIST_END; itd->urb = urb; itd->index = index; - INIT_LIST_HEAD (&itd->itd_list); - itd->uframe = (frame * 8) % ehci->periodic_size; - /* tell itd about the buffer its transfers will consume */ + /* tell itd about its transfer buffer, max 2 pages */ length = urb->iso_frame_desc [index].length; dma += urb->iso_frame_desc [index].offset; temp = dma & ~0x0fff; - for (i = 0; i < 7; i++) { + for (i = 0; i < 2; i++) { itd->hw_bufp [i] = cpu_to_le32 ((u32) temp); itd->hw_bufp_hi [i] = cpu_to_le32 ((u32)(temp >> 32)); - temp += 0x0fff; + temp += 0x1000; } + itd->buf_dma = dma; /* * this might be a "high bandwidth" highspeed endpoint, @@ -544,282 +571,407 @@ epnum = usb_pipeendpoint (urb->pipe); if (usb_pipein (urb->pipe)) { maxp = urb->dev->epmaxpacketin [epnum]; - buf1 = (1 << 11) | maxp; + buf1 = (1 << 11); } else { maxp = urb->dev->epmaxpacketout [epnum]; - buf1 = maxp; + buf1 = 0; } + buf1 |= (maxp & 0x03ff); multi = 1; - multi += (temp >> 11) & 0x03; + multi += (maxp >> 11) & 0x03; maxp &= 0x03ff; + maxp *= multi; + + /* transfer can't fit in any uframe? */ + if (length < 0 || maxp < length) { + dbg ("BAD iso packet: %d bytes, max %d, urb %p [%d] (of %d)", + length, maxp, urb, index, + urb->iso_frame_desc [index].length); + return -ENOSPC; + } + itd->usecs = HS_USECS_ISO (length); /* "plus" info in low order bits of buffer pointers */ itd->hw_bufp [0] |= cpu_to_le32 ((epnum << 8) | urb->dev->devnum); itd->hw_bufp [1] |= cpu_to_le32 (buf1); itd->hw_bufp [2] |= cpu_to_le32 (multi); - /* schedule as many uframes as needed */ - maxp *= multi; - usecs = HS_USECS_ISO (maxp); - bufnum = 0; - for (i = 0; i < 8; i++) { - unsigned t, offset, scratch; + /* figure hw_transaction[] value (it's scheduled later) */ + itd->transaction = EHCI_ISOC_ACTIVE; + itd->transaction |= dma & 0x0fff; /* offset; buffer=0 */ + if ((index + 1) == urb->number_of_packets) + itd->transaction |= EHCI_ITD_IOC; /* end-of-urb irq */ + itd->transaction |= length << 16; + cpu_to_le32s (&itd->transaction); - if (length <= 0) { - itd->hw_transaction [i] = 0; - continue; - } + return 0; +} - /* don't commit more than 80% periodic == 100 usec */ - if ((periodic_usecs (ehci, itd->uframe, i) + usecs) > 100) - continue; +static int +itd_urb_transaction ( + struct ehci_hcd *ehci, + struct urb *urb, + int mem_flags +) { + int frame_index; + struct ehci_itd *first_itd, *itd; + int status; + dma_addr_t buf_dma, itd_dma; - /* we'll use this uframe; figure hw_transaction */ - t = EHCI_ISOC_ACTIVE; - t |= bufnum << 12; // which buffer? - offset = temp & 0x0fff; // offset therein - t |= offset; - if ((offset + maxp) >= 4096) // hc auto-wraps end-of-"page" - bufnum++; - if (length <= maxp) { - // interrupt only needed at end-of-urb - if ((index + 1) == urb->number_of_packets) - t |= EHCI_ITD_IOC; - scratch = length; - } else - scratch = maxp; - t |= scratch << 16; - t = cpu_to_le32 (t); - - itd->hw_transaction [i] = itd->transaction [i] = t; - length -= scratch; - } - if (length > 0) { - dbg ("iso frame too big, urb %p [%d], %d extra (of %d)", - urb, index, length, urb->iso_frame_desc [index].length); - itd_free (ehci, itd); - itd = 0; + /* set up one dma mapping for this urb */ + buf_dma = pci_map_single (ehci->hcd.pdev, + urb->transfer_buffer, urb->transfer_buffer_length, + usb_pipein (urb->pipe) + ? PCI_DMA_FROMDEVICE + : PCI_DMA_TODEVICE); + if (buf_dma == 0) + return -ENOMEM; + + /* allocate/init ITDs */ + for (frame_index = 0, first_itd = 0; + frame_index < urb->number_of_packets; + frame_index++) { + itd = pci_pool_alloc (ehci->itd_pool, mem_flags, &itd_dma); + if (!itd) { + status = -ENOMEM; + goto fail; + } + memset (itd, 0, sizeof *itd); + itd->itd_dma = itd_dma; + + status = itd_fill (ehci, itd, urb, frame_index, buf_dma); + if (status != 0) + goto fail; + + if (first_itd) + list_add_tail (&itd->itd_list, + &first_itd->itd_list); + else { + INIT_LIST_HEAD (&itd->itd_list); + urb->hcpriv = first_itd = itd; + } } - return itd; + urb->error_count = 0; + return 0; + +fail: + if (urb->hcpriv) + itd_free_list (ehci, urb); + return status; } +/*-------------------------------------------------------------------------*/ + static inline void itd_link (struct ehci_hcd *ehci, unsigned frame, struct ehci_itd *itd) { - u32 ptr; + /* always prepend ITD/SITD ... only QH tree is order-sensitive */ + itd->itd_next = ehci->pshadow [frame]; + itd->hw_next = ehci->periodic [frame]; + ehci->pshadow [frame].itd = itd; + ehci->periodic [frame] = cpu_to_le32 (itd->itd_dma) | Q_TYPE_ITD; +} - ptr = cpu_to_le32 (itd->itd_dma); // type 0 == itd - if (ehci->pshadow [frame].ptr) { - if (!itd->itd_next.ptr) { - itd->itd_next = ehci->pshadow [frame]; - itd->hw_next = ehci->periodic [frame]; - } else if (itd->itd_next.ptr != ehci->pshadow [frame].ptr) { - dbg ("frame %d itd link goof", frame); - BUG (); +/* + * return zero on success, else -errno + * - start holds first uframe to start scheduling into + * - max is the first uframe it's NOT (!) OK to start scheduling into + * math to be done modulo "mod" (ehci->periodic_size << 3) + */ +static int get_iso_range ( + struct ehci_hcd *ehci, + struct urb *urb, + unsigned *start, + unsigned *max, + unsigned mod +) { + struct list_head *lh; + struct hcd_dev *dev = urb->dev->hcpriv; + int last = -1; + unsigned now, span, end; + + span = urb->interval * urb->number_of_packets; + + /* first see if we know when the next transfer SHOULD happen */ + list_for_each (lh, &dev->urb_list) { + struct urb *u; + struct ehci_itd *itd; + unsigned s; + + u = list_entry (lh, struct urb, urb_list); + if (u == urb || u->pipe != urb->pipe) + continue; + if (u->interval != urb->interval) { /* must not change! */ + dbg ("urb %p interval %d ... != %p interval %d", + u, u->interval, urb, urb->interval); + return -EINVAL; + } + + /* URB for this endpoint... covers through when? */ + itd = urb->hcpriv; + s = itd->uframe + u->interval * u->number_of_packets; + if (last < 0) + last = s; + else { + /* + * So far we can only queue two ISO URBs... + * + * FIXME do interval math, figure out whether + * this URB is "before" or not ... also, handle + * the case where the URB might have completed, + * but hasn't yet been processed. + */ + dbg ("NYET: queue >2 URBs per ISO endpoint"); + return -EDOM; } } - ehci->pshadow [frame].itd = itd; - ehci->periodic [frame] = ptr; -} -#define ISO_ERRS (EHCI_ISOC_BUF_ERR | EHCI_ISOC_BABBLE | EHCI_ISOC_XACTERR) + /* calculate the legal range [start,max) */ + now = readl (&ehci->regs->frame_index) + 1; /* next uframe */ + if (!ehci->periodic_urbs) + now += 8; /* startup delay */ + now %= mod; + end = now + mod; + if (last < 0) { + *start = now + ehci->i_thresh + /* paranoia */ 1; + *max = end - span; + if (*max < *start + 1) + *max = *start + 1; + } else { + *start = last % mod; + *max = (last + 1) % mod; + } -static unsigned long -itd_complete (struct ehci_hcd *ehci, struct ehci_itd *itd, unsigned long flags) + /* explicit start frame? */ + if (!(urb->transfer_flags & USB_ISO_ASAP)) { + unsigned temp; + + /* sanity check: must be in range */ + urb->start_frame %= ehci->periodic_size; + temp = urb->start_frame << 3; + if (temp < *start) + temp += mod; + if (temp > *max) + return -EDOM; + + /* use that explicit start frame */ + *start = urb->start_frame << 3; + temp += 8; + if (temp < *max) + *max = temp; + } + + // FIXME minimize wraparound to "now" ... insist max+span + // (and start+span) remains a few frames short of "end" + + *max %= ehci->periodic_size; + if ((*start + span) < end) + return 0; + return -EFBIG; +} + +static int +itd_schedule (struct ehci_hcd *ehci, struct urb *urb) { - struct urb *urb = itd->urb; + unsigned start, max, i; + int status; + unsigned mod = ehci->periodic_size << 3; + + for (i = 0; i < urb->number_of_packets; i++) { + urb->iso_frame_desc [i].status = -EINPROGRESS; + urb->iso_frame_desc [i].actual_length = 0; + } - /* if not unlinking: */ - if (!(urb->transfer_flags & EHCI_STATE_UNLINK) - && ehci->hcd.state != USB_STATE_HALT) { - int i; - iso_packet_descriptor_t *desc; - struct ehci_itd *first_itd = urb->hcpriv; + if ((status = get_iso_range (ehci, urb, &start, &max, mod)) != 0) + return status; - /* update status for this frame's transfers */ - desc = &urb->iso_frame_desc [itd->index]; - desc->status = 0; - desc->actual_length = 0; - for (i = 0; i < 8; i++) { - u32 t = itd->hw_transaction [i]; - if (t & (ISO_ERRS | EHCI_ISOC_ACTIVE)) { - if (t & EHCI_ISOC_ACTIVE) - desc->status = -EXDEV; - else if (t & EHCI_ISOC_BUF_ERR) - desc->status = usb_pipein (urb->pipe) - ? -ENOSR /* couldn't read */ - : -ECOMM; /* couldn't write */ - else if (t & EHCI_ISOC_BABBLE) - desc->status = -EOVERFLOW; - else /* (t & EHCI_ISOC_XACTERR) */ - desc->status = -EPROTO; + do { + unsigned uframe; + unsigned usecs; + struct ehci_itd *itd; + + /* check schedule: enough space? */ + itd = urb->hcpriv; + uframe = start; + for (i = 0, uframe = start; + i < urb->number_of_packets; + i++, uframe += urb->interval) { + uframe %= mod; + + /* can't commit more than 80% periodic == 100 usec */ + if (periodic_usecs (ehci, uframe >> 3, uframe & 0x7) + > (100 - itd->usecs)) { + itd = 0; break; } - desc->actual_length += EHCI_ITD_LENGTH (t); + itd = list_entry (itd->itd_list.next, + struct ehci_itd, itd_list); } + if (!itd) + continue; + + /* that's where we'll schedule this! */ + itd = urb->hcpriv; + urb->start_frame = start >> 3; + vdbg ("ISO urb %p (%d packets period %d) starting %d.%d", + urb, urb->number_of_packets, urb->interval, + urb->start_frame, start & 0x7); + for (i = 0, uframe = start, usecs = 0; + i < urb->number_of_packets; + i++, uframe += urb->interval) { + uframe %= mod; + + itd->uframe = uframe; + itd->hw_transaction [uframe & 0x07] = itd->transaction; + itd_link (ehci, (uframe >> 3) % ehci->periodic_size, + itd); + usecs += itd->usecs; - /* handle completion now? */ - if ((itd->index + 1) != urb->number_of_packets) - return flags; + itd = list_entry (itd->itd_list.next, + struct ehci_itd, itd_list); + } - i = usb_pipein (urb->pipe); - if (i) - pci_dma_sync_single (ehci->hcd.pdev, - first_itd->buf_dma, - urb->transfer_buffer_length, - PCI_DMA_FROMDEVICE); + /* update bandwidth utilization records (for usbfs) */ + /* FIXME usbcore expects per-frame average, which isn't + * most accurate model... this provides the total claim, + * and expects the average to be computed only display. + */ + usb_claim_bandwidth (urb->dev, urb, usecs, 1); - /* call completion with no locks; it can unlink ... */ - spin_unlock_irqrestore (&ehci->lock, flags); - urb->complete (urb); - spin_lock_irqsave (&ehci->lock, flags); - - /* re-activate this URB? or unlink? */ - if (!(urb->transfer_flags & EHCI_STATE_UNLINK) - && ehci->hcd.state != USB_STATE_HALT) { - if (!i) - pci_dma_sync_single (ehci->hcd.pdev, - first_itd->buf_dma, - urb->transfer_buffer_length, - PCI_DMA_TODEVICE); + /* maybe enable periodic schedule processing */ + if (!ehci->periodic_urbs++) + enable_periodic (ehci); - itd = urb->hcpriv; - do { - for (i = 0; i < 8; i++) - itd->hw_transaction [i] - = itd->transaction [i]; - itd = list_entry (itd->itd_list.next, - struct ehci_itd, itd_list); - } while (itd != urb->hcpriv); - return flags; - } + return 0; + + } while ((start = ++start % mod) != max); + + /* no room in the schedule */ + dbg ("urb %p, CAN'T SCHEDULE", urb); + return -ENOSPC; +} + +/*-------------------------------------------------------------------------*/ + +#define ISO_ERRS (EHCI_ISOC_BUF_ERR | EHCI_ISOC_BABBLE | EHCI_ISOC_XACTERR) + +static unsigned long +itd_complete ( + struct ehci_hcd *ehci, + struct ehci_itd *itd, + unsigned uframe, + unsigned long flags +) { + struct urb *urb = itd->urb; + iso_packet_descriptor_t *desc; + u32 t; + + /* update status for this uframe's transfers */ + desc = &urb->iso_frame_desc [itd->index]; + + t = itd->hw_transaction [uframe]; + itd->hw_transaction [uframe] = 0; + if (t & EHCI_ISOC_ACTIVE) + desc->status = -EXDEV; + else if (t & ISO_ERRS) { + urb->error_count++; + if (t & EHCI_ISOC_BUF_ERR) + desc->status = usb_pipein (urb->pipe) + ? -ENOSR /* couldn't read */ + : -ECOMM; /* couldn't write */ + else if (t & EHCI_ISOC_BABBLE) + desc->status = -EOVERFLOW; + else /* (t & EHCI_ISOC_XACTERR) */ + desc->status = -EPROTO; + + /* HC need not update length with this error */ + if (!(t & EHCI_ISOC_BABBLE)) + desc->actual_length += EHCI_ITD_LENGTH (t); + } else { + desc->status = 0; + desc->actual_length += EHCI_ITD_LENGTH (t); + } - /* unlink done only on the last itd */ - } else if ((itd->index + 1) != urb->number_of_packets) + vdbg ("itd %p urb %p packet %d/%d trans %x status %d len %d", + itd, urb, itd->index + 1, urb->number_of_packets, + t, desc->status, desc->actual_length); + + /* handle completion now? */ + if ((itd->index + 1) != urb->number_of_packets) return flags; - /* we're unlinking ... */ + /* + * For now, always give the urb back to the driver ... expect it + * to submit a new urb (or resubmit this), and to have another + * already queued when un-interrupted transfers are needed. + * No, that's not what OHCI or UHCI are now doing. + * + * FIXME Revisit the ISO URB model. It's cleaner not to have all + * the special case magic, but it'd be faster to reuse existing + * ITD/DMA setup and schedule state. Easy to dma_sync/complete(), + * then either reschedule or, if unlinking, free and giveback(). + * But we can't overcommit like the full and low speed HCs do, and + * there's no clean way to report an error when rescheduling... + * + * NOTE that for now we don't accelerate ISO unlinks; they just + * happen according to the current schedule. Means a delay of + * up to about a second (max). + */ + itd_free_list (ehci, urb); + if (urb->status == -EINPROGRESS) + urb->status = 0; - /* decouple urb from the hcd */ spin_unlock_irqrestore (&ehci->lock, flags); - if (ehci->hcd.state == USB_STATE_HALT) - urb->status = -ESHUTDOWN; - itd = urb->hcpriv; - urb->hcpriv = 0; - ehci_urb_done (ehci, itd->buf_dma, urb); + usb_hcd_giveback_urb (&ehci->hcd, urb); spin_lock_irqsave (&ehci->lock, flags); - /* take itds out of the hc's periodic schedule */ - list_entry (itd->itd_list.prev, struct ehci_itd, itd_list) - ->itd_list.next = 0; - do { - struct ehci_itd *next; - - if (itd->itd_list.next) - next = list_entry (itd->itd_list.next, - struct ehci_itd, itd_list); - else - next = 0; + /* defer stopping schedule; completion can submit */ + ehci->periodic_urbs--; + if (!ehci->periodic_urbs) + disable_periodic (ehci); - // FIXME: hc WILL (!) lap us here, if we get behind - // by 128 msec (or less, with smaller periodic_size). - // Reading/caching these itds will cause trouble... - - periodic_unlink (ehci, itd->uframe, itd); - itd_free (ehci, itd); - itd = next; - } while (itd); return flags; } /*-------------------------------------------------------------------------*/ -static int itd_submit (struct ehci_hcd *ehci, struct urb *urb) +static int itd_submit (struct ehci_hcd *ehci, struct urb *urb, int mem_flags) { - struct ehci_itd *first_itd = 0, *itd; - unsigned frame_index; - dma_addr_t dma; - unsigned long flags; - - dbg ("itd_submit"); + int status; + unsigned long flags; - /* set up one dma mapping for this urb */ - dma = pci_map_single (ehci->hcd.pdev, - urb->transfer_buffer, urb->transfer_buffer_length, - usb_pipein (urb->pipe) - ? PCI_DMA_FROMDEVICE - : PCI_DMA_TODEVICE); - if (dma == 0) - return -ENOMEM; + dbg ("itd_submit urb %p", urb); + /* NOTE DMA mapping assumes this ... */ + if (urb->iso_frame_desc [0].offset != 0) + return -EINVAL; + /* - * Schedule as needed. This is VERY optimistic about free - * bandwidth! But the API assumes drivers can pick frames - * intelligently (how?), so there's no other good option. - * - * FIXME this doesn't handle urb->next rings, or try to - * use the iso periodicity. + * NOTE doing this for now, anticipating periodic URB models + * get updated to be "explicit resubmit". */ - if (urb->transfer_flags & USB_ISO_ASAP) { - urb->start_frame = ehci_get_frame (&ehci->hcd); - urb->start_frame++; + if (urb->next) { + dbg ("use explicit resubmit for ISO"); + return -EINVAL; } - urb->start_frame %= ehci->periodic_size; - /* create and populate itds (doing uframe scheduling) */ - spin_lock_irqsave (&ehci->lock, flags); - for (frame_index = 0; - frame_index < urb->number_of_packets; - frame_index++) { - itd = itd_make (ehci, urb, frame_index, - urb->start_frame + frame_index, - dma, SLAB_ATOMIC); - if (itd) { - if (first_itd) - list_add_tail (&itd->itd_list, - &first_itd->itd_list); - else - first_itd = itd; - } else { - spin_unlock_irqrestore (&ehci->lock, flags); - if (first_itd) { - while (!list_empty (&first_itd->itd_list)) { - itd = list_entry ( - first_itd->itd_list.next, - struct ehci_itd, itd_list); - list_del (&itd->itd_list); - itd_free (ehci, itd); - } - itd_free (ehci, first_itd); - } - pci_unmap_single (ehci->hcd.pdev, - dma, urb->transfer_buffer_length, - usb_pipein (urb->pipe) - ? PCI_DMA_FROMDEVICE - : PCI_DMA_TODEVICE); - return -ENOMEM; - } - } - - /* stuff into the schedule */ - itd = first_itd; - do { - unsigned i; - - for (i = 0; i < 8; i++) { - if (!itd->hw_transaction [i]) - continue; - itd_link (ehci, itd->uframe + i, itd); - } - itd = list_entry (itd->itd_list.next, - struct ehci_itd, itd_list); - } while (itd != first_itd); - urb->hcpriv = first_itd; + /* allocate ITDs w/o locking anything */ + status = itd_urb_transaction (ehci, urb, mem_flags); + if (status < 0) + return status; + /* schedule ... need to lock */ + spin_lock_irqsave (&ehci->lock, flags); + status = itd_schedule (ehci, urb); spin_unlock_irqrestore (&ehci->lock, flags); - return 0; + if (status < 0) + itd_free_list (ehci, urb); + + return status; } +#ifdef have_split_iso + /*-------------------------------------------------------------------------*/ /* @@ -827,7 +979,7 @@ * the TTs in USB 2.0 hubs. */ -static inline void +static void sitd_free (struct ehci_hcd *ehci, struct ehci_sitd *sitd) { pci_pool_free (ehci->sitd_pool, sitd, sitd->sitd_dma); @@ -861,7 +1013,7 @@ } -static inline void +static void sitd_link (struct ehci_hcd *ehci, unsigned frame, struct ehci_sitd *sitd) { u32 ptr; @@ -894,12 +1046,11 @@ /*-------------------------------------------------------------------------*/ -static int sitd_submit (struct ehci_hcd *ehci, struct urb *urb) +static int sitd_submit (struct ehci_hcd *ehci, struct urb *urb, int mem_flags) { // struct ehci_sitd *first_sitd = 0; unsigned frame_index; dma_addr_t dma; - int mem_flags; dbg ("NYI -- sitd_submit"); @@ -908,8 +1059,6 @@ // FIXME: setup one big dma mapping dma = 0; - mem_flags = SLAB_ATOMIC; - for (frame_index = 0; frame_index < urb->number_of_packets; frame_index++) { @@ -941,54 +1090,63 @@ return -ENOSYS; } - -#endif /* have_iso */ +#endif /* have_split_iso */ /*-------------------------------------------------------------------------*/ static void scan_periodic (struct ehci_hcd *ehci) { - unsigned frame; - unsigned clock; + unsigned frame, clock, now_uframe, mod; unsigned long flags; + mod = ehci->periodic_size << 3; spin_lock_irqsave (&ehci->lock, flags); /* * When running, scan from last scan point up to "now" + * else clean up by scanning everything that's left. * Touches as few pages as possible: cache-friendly. - * It's safe to scan entries more than once, though. + * Don't scan ISO entries more than once, though. */ - if (HCD_IS_RUNNING (ehci->hcd.state)) { - frame = ehci->next_frame; - clock = ehci_get_frame (&ehci->hcd); + frame = ehci->next_uframe >> 3; + if (HCD_IS_RUNNING (ehci->hcd.state)) + now_uframe = readl (&ehci->regs->frame_index); + else + now_uframe = (frame << 3) - 1; + now_uframe %= mod; + clock = now_uframe >> 3; - /* when shutting down, scan everything for thoroughness */ - } else { - frame = 0; - clock = ehci->periodic_size - 1; - } for (;;) { - union ehci_shadow q; - u32 type; + union ehci_shadow q, *q_p; + u32 type, *hw_p; + unsigned uframes; restart: - q.ptr = ehci->pshadow [frame].ptr; - type = Q_NEXT_TYPE (ehci->periodic [frame]); + /* scan schedule to _before_ current frame index */ + if (frame == clock) + uframes = now_uframe & 0x07; + else + uframes = 8; + + q_p = &ehci->pshadow [frame]; + hw_p = &ehci->periodic [frame]; + q.ptr = q_p->ptr; + type = Q_NEXT_TYPE (*hw_p); /* scan each element in frame's queue for completions */ while (q.ptr != 0) { int last; + unsigned uf; union ehci_shadow temp; switch (type) { case Q_TYPE_QH: last = (q.qh->hw_next == EHCI_LIST_END); - flags = intr_complete (ehci, frame, - qh_put (q.qh), flags); - type = Q_NEXT_TYPE (q.qh->hw_next); temp = q.qh->qh_next; - qh_unput (ehci, q.qh); + type = Q_NEXT_TYPE (q.qh->hw_next); + flags = intr_complete (ehci, frame, + qh_get (q.qh), flags); + qh_put (ehci, q.qh); q = temp; break; case Q_TYPE_FSTN: @@ -1000,22 +1158,49 @@ dbg ("ignoring completions from FSTNs"); } type = Q_NEXT_TYPE (q.fstn->hw_next); - temp = q.fstn->fstn_next; + q = q.fstn->fstn_next; break; -#ifdef have_iso case Q_TYPE_ITD: last = (q.itd->hw_next == EHCI_LIST_END); - flags = itd_complete (ehci, q.itd, flags); - type = Q_NEXT_TYPE (q.itd->hw_next); - q = q.itd->itd_next; + + /* Unlink each (S)ITD we see, since the ISO + * URB model forces constant rescheduling. + * That complicates sharing uframes in ITDs, + * and means we need to skip uframes the HC + * hasn't yet processed. + */ + for (uf = 0; uf < uframes; uf++) { + if (q.itd->hw_transaction [uf] != 0) { + temp = q; + *q_p = q.itd->itd_next; + *hw_p = q.itd->hw_next; + type = Q_NEXT_TYPE (*hw_p); + + /* might free q.itd ... */ + flags = itd_complete (ehci, + temp.itd, uf, flags); + break; + } + } + /* we might skip this ITD's uframe ... */ + if (uf == uframes) { + q_p = &q.itd->itd_next; + hw_p = &q.itd->hw_next; + type = Q_NEXT_TYPE (q.itd->hw_next); + } + + q = *q_p; break; +#ifdef have_split_iso case Q_TYPE_SITD: last = (q.sitd->hw_next == EHCI_LIST_END); flags = sitd_complete (ehci, q.sitd, flags); type = Q_NEXT_TYPE (q.sitd->hw_next); + + // FIXME unlink SITD after split completes q = q.sitd->sitd_next; break; -#endif /* have_iso */ +#endif /* have_split_iso */ default: dbg ("corrupt type %d frame %d shadow %p", type, frame, q.ptr); @@ -1044,13 +1229,16 @@ if (!HCD_IS_RUNNING (ehci->hcd.state)) break; - ehci->next_frame = clock; - now = ehci_get_frame (&ehci->hcd); - if (clock == now) + ehci->next_uframe = now_uframe; + now = readl (&ehci->regs->frame_index) % mod; + if (now_uframe == now) break; - clock = now; - } else if (++frame >= ehci->periodic_size) - frame = 0; + + /* rescan the rest of this frame, then ... */ + now_uframe = now; + clock = now_uframe >> 3; + } else + frame = (frame + 1) % ehci->periodic_size; } spin_unlock_irqrestore (&ehci->lock, flags); - } +} diff -Nru a/drivers/usb/hcd/ehci.h b/drivers/usb/hcd/ehci.h --- a/drivers/usb/hcd/ehci.h Fri May 3 14:51:44 2002 +++ b/drivers/usb/hcd/ehci.h Fri May 3 14:51:44 2002 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001 by David Brownell + * Copyright (c) 2001-2002 by David Brownell * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -49,7 +49,7 @@ unsigned i_thresh; /* uframes HC might cache */ union ehci_shadow *pshadow; /* mirror hw periodic table */ - int next_frame; /* scan periodic, start here */ + int next_uframe; /* scan periodic, start here */ unsigned periodic_urbs; /* how many urbs scheduled? */ /* deferred work from IRQ, etc */ @@ -62,6 +62,7 @@ struct usb_hcd hcd; struct ehci_caps *caps; struct ehci_regs *regs; + u32 hcs_params; /* cached register copy */ /* per-HC memory pools (could be per-PCI-bus, but ...) */ struct pci_pool *qh_pool; /* qh per active urb */ @@ -324,13 +325,14 @@ union ehci_shadow itd_next; /* ptr to periodic q entry */ struct urb *urb; - unsigned index; /* in urb->iso_frame_desc */ struct list_head itd_list; /* list of urb frames' itds */ dma_addr_t buf_dma; /* frame's buffer address */ - unsigned uframe; /* in periodic schedule */ - u32 transaction [8]; /* copy of hw_transaction */ - + /* for now, only one hw_transaction per itd */ + u32 transaction; + u16 index; /* in urb->iso_frame_desc */ + u16 uframe; /* in periodic schedule */ + u16 usecs; } __attribute__ ((aligned (32))); /*-------------------------------------------------------------------------*/