Commit 4e505316 authored by David Brownell's avatar David Brownell Committed by Greg Kroah-Hartman

[PATCH] ehci misc fixes

This removes some bugs:

 - a short read problem with control requests
 - only creates one control qh (memleak fix)
 - adds an omitted hardware handshake
 - reset timeout in octal, say what?
 - a couple BUG()s outlived their value

Plus it deletes unused stub code for split ISO
and updates some internal doc.
parent cee1d946
...@@ -190,7 +190,7 @@ static int ehci_reset (struct ehci_hcd *ehci) ...@@ -190,7 +190,7 @@ static int ehci_reset (struct ehci_hcd *ehci)
dbg_cmd (ehci, "reset", command); dbg_cmd (ehci, "reset", command);
writel (command, &ehci->regs->command); writel (command, &ehci->regs->command);
ehci->hcd.state = USB_STATE_HALT; ehci->hcd.state = USB_STATE_HALT;
return handshake (&ehci->regs->command, CMD_RESET, 0, 050); return handshake (&ehci->regs->command, CMD_RESET, 0, 250);
} }
/* idle the controller (from running) */ /* idle the controller (from running) */
...@@ -368,6 +368,7 @@ static int ehci_start (struct usb_hcd *hcd) ...@@ -368,6 +368,7 @@ static int ehci_start (struct usb_hcd *hcd)
* *
* NOTE: layered drivers can't yet tell when we enable that, * NOTE: layered drivers can't yet tell when we enable that,
* so they can't pass this info along (like NETIF_F_HIGHDMA) * so they can't pass this info along (like NETIF_F_HIGHDMA)
* (or like Scsi_Host.highmem_io) ... usb_bus.flags?
*/ */
if (HCC_64BIT_ADDR (hcc_params)) { if (HCC_64BIT_ADDR (hcc_params)) {
writel (0, &ehci->regs->segment); writel (0, &ehci->regs->segment);
...@@ -586,6 +587,10 @@ static void ehci_tasklet (unsigned long param) ...@@ -586,6 +587,10 @@ static void ehci_tasklet (unsigned long param)
struct ehci_hcd *ehci = (struct ehci_hcd *) param; struct ehci_hcd *ehci = (struct ehci_hcd *) param;
unsigned long flags; unsigned long flags;
// FIXME don't pass flags; on sparc they aren't really flags.
// qh_completions can just leave irqs blocked,
// then have scan_async() allow IRQs if it's very busy
spin_lock_irqsave (&ehci->lock, flags); spin_lock_irqsave (&ehci->lock, flags);
if (ehci->reclaim_ready) if (ehci->reclaim_ready)
......
...@@ -26,8 +26,7 @@ ...@@ -26,8 +26,7 @@
* Control, bulk, and interrupt traffic all use "qh" lists. They list "qtd" * Control, bulk, and interrupt traffic all use "qh" lists. They list "qtd"
* entries describing USB transactions, max 16-20kB/entry (with 4kB-aligned * entries describing USB transactions, max 16-20kB/entry (with 4kB-aligned
* buffers needed for the larger number). We use one QH per endpoint, queue * buffers needed for the larger number). We use one QH per endpoint, queue
* multiple (bulk or control) urbs per endpoint. URBs may need several qtds. * multiple urbs (all three types) per endpoint. URBs may need several qtds.
* A scheduled interrupt qh always (for now) has one qtd, one urb.
* *
* ISO traffic uses "ISO TD" (itd, and sitd) records, and (along with * ISO traffic uses "ISO TD" (itd, and sitd) records, and (along with
* interrupts) needs careful scheduling. Performance improvements can be * interrupts) needs careful scheduling. Performance improvements can be
...@@ -281,7 +280,12 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh, unsigned long flags) ...@@ -281,7 +280,12 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh, unsigned long flags)
|| (qh->qh_state == QH_STATE_IDLE); || (qh->qh_state == QH_STATE_IDLE);
// FIXME Remove the automagic unlink mode. // FIXME Remove the automagic unlink mode.
// Drivers can now clean up safely; its' their job. // Drivers can now clean up safely; it's their job.
//
// FIXME Removing it should fix the short read scenarios
// with "huge" urb data (more than one 16+KByte td) with
// the short read someplace other than the last data TD.
// Except the control case: 'retrigger' status ACKs.
/* fault: unlink the rest, since this qtd saw an error? */ /* fault: unlink the rest, since this qtd saw an error? */
if (unlikely ((token & QTD_STS_HALT) != 0)) { if (unlikely ((token & QTD_STS_HALT) != 0)) {
...@@ -391,7 +395,7 @@ qh_urb_transaction ( ...@@ -391,7 +395,7 @@ qh_urb_transaction (
struct ehci_qtd *qtd, *qtd_prev; struct ehci_qtd *qtd, *qtd_prev;
dma_addr_t buf; dma_addr_t buf;
int len, maxpacket; int len, maxpacket;
int is_input; int is_input, status_patch = 0;
u32 token; u32 token;
/* /*
...@@ -422,6 +426,9 @@ qh_urb_transaction ( ...@@ -422,6 +426,9 @@ qh_urb_transaction (
qtd->urb = urb; qtd->urb = urb;
qtd_prev->hw_next = QTD_NEXT (qtd->qtd_dma); qtd_prev->hw_next = QTD_NEXT (qtd->qtd_dma);
list_add_tail (&qtd->qtd_list, head); list_add_tail (&qtd->qtd_list, head);
if (!(urb->transfer_flags & URB_SHORT_NOT_OK))
status_patch = 1;
} }
/* /*
...@@ -499,6 +506,19 @@ qh_urb_transaction ( ...@@ -499,6 +506,19 @@ qh_urb_transaction (
} }
} }
/* if we're permitting a short control read, we want the hardware to
* just continue after short data and send the status ack. it can do
* that on the last data packet (typically the only one). for other
* packets, software fixup is needed (in qh_completions).
*/
if (status_patch) {
struct ehci_qtd *prev;
prev = list_entry (qtd->qtd_list.prev,
struct ehci_qtd, qtd_list);
prev->hw_alt_next = QTD_NEXT (qtd->qtd_dma);
}
/* by default, enable interrupt on urb completion */ /* by default, enable interrupt on urb completion */
if (likely (!(urb->transfer_flags & URB_NO_INTERRUPT))) if (likely (!(urb->transfer_flags & URB_NO_INTERRUPT)))
qtd->hw_token |= __constant_cpu_to_le32 (QTD_IOC); qtd->hw_token |= __constant_cpu_to_le32 (QTD_IOC);
...@@ -653,9 +673,8 @@ ehci_qh_make ( ...@@ -653,9 +673,8 @@ ehci_qh_make (
} }
break; break;
default: default:
#ifdef DEBUG dbg ("bogus dev %p speed %d", urb->dev, urb->dev->speed);
BUG (); return 0;
#endif
} }
/* NOTE: if (PIPE_INTERRUPT) { scheduler sets s-mask } */ /* NOTE: if (PIPE_INTERRUPT) { scheduler sets s-mask } */
...@@ -837,7 +856,7 @@ submit_async ( ...@@ -837,7 +856,7 @@ submit_async (
qtd = list_entry (qtd_list->next, struct ehci_qtd, qtd_list); qtd = list_entry (qtd_list->next, struct ehci_qtd, qtd_list);
dev = (struct hcd_dev *)urb->dev->hcpriv; dev = (struct hcd_dev *)urb->dev->hcpriv;
epnum = usb_pipeendpoint (urb->pipe); epnum = usb_pipeendpoint (urb->pipe);
if (usb_pipein (urb->pipe)) if (usb_pipein (urb->pipe) && !usb_pipecontrol (urb->pipe))
epnum |= 0x10; epnum |= 0x10;
vdbg ("%s: submit_async urb %p len %d ep %d-%s qtd %p [qh %p]", vdbg ("%s: submit_async urb %p len %d ep %d-%s qtd %p [qh %p]",
...@@ -923,9 +942,11 @@ static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh) ...@@ -923,9 +942,11 @@ static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
if (unlikely (qh == ehci->async && qh->qh_next.qh == qh)) { if (unlikely (qh == ehci->async && qh->qh_next.qh == qh)) {
/* can't get here without STS_ASS set */ /* can't get here without STS_ASS set */
if (ehci->hcd.state != USB_STATE_HALT) { if (ehci->hcd.state != USB_STATE_HALT) {
if (cmd & CMD_PSE) if (cmd & CMD_PSE) {
writel (cmd & ~CMD_ASE, &ehci->regs->command); writel (cmd & ~CMD_ASE, &ehci->regs->command);
else (void) handshake (&ehci->regs->status,
STS_ASS, 0, 150);
} else
ehci_ready (ehci); ehci_ready (ehci);
} }
qh->qh_next.qh = ehci->async = 0; qh->qh_next.qh = ehci->async = 0;
...@@ -944,10 +965,6 @@ static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh) ...@@ -944,10 +965,6 @@ static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
prev = ehci->async; prev = ehci->async;
while (prev->qh_next.qh != qh && prev->qh_next.qh != ehci->async) while (prev->qh_next.qh != qh && prev->qh_next.qh != ehci->async)
prev = prev->qh_next.qh; prev = prev->qh_next.qh;
#ifdef DEBUG
if (prev->qh_next.qh != qh)
BUG ();
#endif
if (qh->hw_info1 & __constant_cpu_to_le32 (QH_HEAD)) { if (qh->hw_info1 & __constant_cpu_to_le32 (QH_HEAD)) {
ehci->async = prev; ehci->async = prev;
......
...@@ -971,119 +971,10 @@ static int itd_submit (struct ehci_hcd *ehci, struct urb *urb, int mem_flags) ...@@ -971,119 +971,10 @@ static int itd_submit (struct ehci_hcd *ehci, struct urb *urb, int mem_flags)
/* /*
* "Split ISO TDs" ... used for USB 1.1 devices going through * "Split ISO TDs" ... used for USB 1.1 devices going through
* the TTs in USB 2.0 hubs. * the TTs in USB 2.0 hubs.
*
* FIXME not yet implemented
*/ */
static void
sitd_free (struct ehci_hcd *ehci, struct ehci_sitd *sitd)
{
pci_pool_free (ehci->sitd_pool, sitd, sitd->sitd_dma);
}
static struct ehci_sitd *
sitd_make (
struct ehci_hcd *ehci,
struct urb *urb,
unsigned index, // urb->iso_frame_desc [index]
unsigned uframe, // scheduled start
dma_addr_t dma, // mapped transfer buffer
int mem_flags
) {
struct ehci_sitd *sitd;
unsigned length;
sitd = pci_pool_alloc (ehci->sitd_pool, mem_flags, &dma);
if (!sitd)
return sitd;
sitd->urb = urb;
length = urb->iso_frame_desc [index].length;
dma += urb->iso_frame_desc [index].offset;
#if 0
// FIXME: do the rest!
#else
sitd_free (ehci, sitd);
return 0;
#endif
}
static void
sitd_link (struct ehci_hcd *ehci, unsigned frame, struct ehci_sitd *sitd)
{
u32 ptr;
ptr = cpu_to_le32 (sitd->sitd_dma | 2); // type 2 == sitd
if (ehci->pshadow [frame].ptr) {
if (!sitd->sitd_next.ptr) {
sitd->sitd_next = ehci->pshadow [frame];
sitd->hw_next = ehci->periodic [frame];
} else if (sitd->sitd_next.ptr != ehci->pshadow [frame].ptr) {
dbg ("frame %d sitd link goof", frame);
BUG ();
}
}
ehci->pshadow [frame].sitd = sitd;
ehci->periodic [frame] = ptr;
}
static unsigned long
sitd_complete (
struct ehci_hcd *ehci,
struct ehci_sitd *sitd,
unsigned long flags
) {
// FIXME -- implement!
dbg ("NYI -- sitd_complete");
return flags;
}
/*-------------------------------------------------------------------------*/
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;
dbg ("NYI -- sitd_submit");
// FIXME -- implement!
// FIXME: setup one big dma mapping
dma = 0;
for (frame_index = 0;
frame_index < urb->number_of_packets;
frame_index++) {
struct ehci_sitd *sitd;
unsigned uframe;
// FIXME: use real arguments, schedule this!
uframe = -1;
sitd = sitd_make (ehci, urb, frame_index,
uframe, dma, mem_flags);
if (sitd) {
/*
if (first_sitd)
list_add_tail (&sitd->sitd_list,
&first_sitd->sitd_list);
else
first_sitd = sitd;
*/
} else {
// FIXME: clean everything up
}
}
// if we have a first sitd, then
// store them all into the periodic schedule!
// urb->hcpriv = first sitd in sitd_list
return -ENOSYS;
}
#endif /* have_split_iso */ #endif /* have_split_iso */
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
......
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