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

USB

ehci-0306, iso, philips, speedups
  
      - adds preliminary highspeed ISO support
      - tweaks the driver to support the Philips EHCI
      - does less in the IRQ handler
      - avoids accessing one immutable PCI register
  
The ISO support should be enough to start writing
drivers, not that I know of any ISO devices that are
really available yet, but it's not fully cooked yet.
  
As a functional milestone, this means Linux now
handles all kinds of highspeed device I/O.  (But it
doesn't yet handle split periodic transactions, to
full or low speed devices through USB 2.0 hubs.)
    
Thanks to Rory Bolt for the non-ISO bits here!
parent ddbdbc8c
/* /*
* Copyright (c) 2000-2001 by David Brownell * Copyright (c) 2000-2002 by David Brownell
* *
* This program is free software; you can redistribute it and/or modify it * 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 * under the terms of the GNU General Public License as published by the
...@@ -31,10 +31,6 @@ ...@@ -31,10 +31,6 @@
#include <linux/list.h> #include <linux/list.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#ifndef CONFIG_USB_DEBUG
#define CONFIG_USB_DEBUG /* this is still experimental! */
#endif
#ifdef CONFIG_USB_DEBUG #ifdef CONFIG_USB_DEBUG
#define DEBUG #define DEBUG
#else #else
...@@ -73,19 +69,25 @@ ...@@ -73,19 +69,25 @@
* ... * ...
* *
* HISTORY: * HISTORY:
*
* 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-14 Minor cleanup; version synch.
* 2002-01-08 Fix roothub handoff of FS/LS to companion controllers. * 2002-01-08 Fix roothub handoff of FS/LS to companion controllers.
* 2002-01-04 Control/Bulk queuing behaves. * 2002-01-04 Control/Bulk queuing behaves.
*
* 2001-12-12 Initial patch version for Linux 2.5.1 kernel. * 2001-12-12 Initial patch version for Linux 2.5.1 kernel.
* 2001-June Works with usb-storage and NEC EHCI on 2.4
*/ */
#define DRIVER_VERSION "$Revision: 0.26 $" #define DRIVER_VERSION "$Revision: 0.27 $"
#define DRIVER_AUTHOR "David Brownell" #define DRIVER_AUTHOR "David Brownell"
#define DRIVER_DESC "USB 2.0 'Enhanced' Host Controller (EHCI) Driver" #define DRIVER_DESC "USB 2.0 'Enhanced' Host Controller (EHCI) Driver"
// #define EHCI_VERBOSE_DEBUG // #define EHCI_VERBOSE_DEBUG
// #define have_iso // #define have_split_iso
#ifdef CONFIG_DEBUG_SLAB #ifdef CONFIG_DEBUG_SLAB
# define EHCI_SLAB_FLAGS (SLAB_POISON) # define EHCI_SLAB_FLAGS (SLAB_POISON)
...@@ -187,6 +189,9 @@ static int ehci_start (struct usb_hcd *hcd) ...@@ -187,6 +189,9 @@ static int ehci_start (struct usb_hcd *hcd)
dbg_hcs_params (ehci, "ehci_start"); dbg_hcs_params (ehci, "ehci_start");
dbg_hcc_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. * hw default: 1K periodic list heads, one per frame.
* periodic_size can shrink by USBCMD update if hcc_params allows. * periodic_size can shrink by USBCMD update if hcc_params allows.
...@@ -204,7 +209,7 @@ static int ehci_start (struct usb_hcd *hcd) ...@@ -204,7 +209,7 @@ static int ehci_start (struct usb_hcd *hcd)
ehci->async = 0; ehci->async = 0;
ehci->reclaim = 0; ehci->reclaim = 0;
ehci->next_frame = -1; ehci->next_uframe = -1;
/* controller state: unknown --> reset */ /* controller state: unknown --> reset */
...@@ -310,7 +315,7 @@ static void ehci_stop (struct usb_hcd *hcd) ...@@ -310,7 +315,7 @@ static void ehci_stop (struct usb_hcd *hcd)
// root hub is shut down separately (first, when possible) // root hub is shut down separately (first, when possible)
scan_async (ehci); scan_async (ehci);
if (ehci->next_frame != -1) if (ehci->next_uframe != -1)
scan_periodic (ehci); scan_periodic (ehci);
ehci_mem_cleanup (ehci); ehci_mem_cleanup (ehci);
...@@ -332,14 +337,12 @@ static int ehci_get_frame (struct usb_hcd *hcd) ...@@ -332,14 +337,12 @@ static int ehci_get_frame (struct usb_hcd *hcd)
static int ehci_suspend (struct usb_hcd *hcd, u32 state) static int ehci_suspend (struct usb_hcd *hcd, u32 state)
{ {
struct ehci_hcd *ehci = hcd_to_ehci (hcd); struct ehci_hcd *ehci = hcd_to_ehci (hcd);
u32 params;
int ports; int ports;
int i; int i;
dbg ("%s: suspend to %d", hcd->bus_name, state); dbg ("%s: suspend to %d", hcd->bus_name, state);
params = readl (&ehci->caps->hcs_params); ports = HCS_N_PORTS (ehci->hcs_params);
ports = HCS_N_PORTS (params);
// FIXME: This assumes what's probably a D3 level suspend... // FIXME: This assumes what's probably a D3 level suspend...
...@@ -375,14 +378,12 @@ dbg ("%s: suspend port %d", hcd->bus_name, i); ...@@ -375,14 +378,12 @@ dbg ("%s: suspend port %d", hcd->bus_name, i);
static int ehci_resume (struct usb_hcd *hcd) static int ehci_resume (struct usb_hcd *hcd)
{ {
struct ehci_hcd *ehci = hcd_to_ehci (hcd); struct ehci_hcd *ehci = hcd_to_ehci (hcd);
u32 params;
int ports; int ports;
int i; int i;
dbg ("%s: resume", hcd->bus_name); dbg ("%s: resume", hcd->bus_name);
params = readl (&ehci->caps->hcs_params); ports = HCS_N_PORTS (ehci->hcs_params);
ports = HCS_N_PORTS (params);
// FIXME: if controller didn't retain state, // FIXME: if controller didn't retain state,
// return and let generic code clean it up // return and let generic code clean it up
...@@ -426,7 +427,7 @@ static void ehci_tasklet (unsigned long param) ...@@ -426,7 +427,7 @@ static void ehci_tasklet (unsigned long param)
if (ehci->reclaim_ready) if (ehci->reclaim_ready)
end_unlink_async (ehci); end_unlink_async (ehci);
scan_async (ehci); scan_async (ehci);
if (ehci->next_frame != -1) if (ehci->next_uframe != -1)
scan_periodic (ehci); scan_periodic (ehci);
// FIXME: when nothing is connected to the root hub, // FIXME: when nothing is connected to the root hub,
...@@ -440,20 +441,20 @@ static void ehci_irq (struct usb_hcd *hcd) ...@@ -440,20 +441,20 @@ static void ehci_irq (struct usb_hcd *hcd)
{ {
struct ehci_hcd *ehci = hcd_to_ehci (hcd); struct ehci_hcd *ehci = hcd_to_ehci (hcd);
u32 status = readl (&ehci->regs->status); u32 status = readl (&ehci->regs->status);
int bh = 0; int bh;
/* clear (just) interrupts */
status &= INTR_MASK; status &= INTR_MASK;
if (!status) /* irq sharing? */
return;
/* clear (just) interrupts */
writel (status, &ehci->regs->status); writel (status, &ehci->regs->status);
readl (&ehci->regs->command); /* unblock posted write */ readl (&ehci->regs->command); /* unblock posted write */
bh = 0;
if (unlikely (hcd->state == USB_STATE_HALT)) /* irq sharing? */
return;
#ifdef EHCI_VERBOSE_DEBUG #ifdef EHCI_VERBOSE_DEBUG
/* unrequested/ignored: Port Change Detect, Frame List Rollover */ /* unrequested/ignored: Port Change Detect, Frame List Rollover */
if (status & INTR_MASK) dbg_status (ehci, "irq", status);
dbg_status (ehci, "irq", status);
#endif #endif
/* INT, ERR, and IAA interrupt rates can be throttled */ /* INT, ERR, and IAA interrupt rates can be throttled */
...@@ -520,17 +521,15 @@ static int ehci_urb_enqueue ( ...@@ -520,17 +521,15 @@ static int ehci_urb_enqueue (
return intr_submit (ehci, urb, &qtd_list, mem_flags); return intr_submit (ehci, urb, &qtd_list, mem_flags);
case PIPE_ISOCHRONOUS: case PIPE_ISOCHRONOUS:
#ifdef have_iso
if (urb->dev->speed == USB_SPEED_HIGH) if (urb->dev->speed == USB_SPEED_HIGH)
return itd_submit (ehci, urb); return itd_submit (ehci, urb, mem_flags);
#ifdef have_split_iso
else else
return sitd_submit (ehci, urb); return sitd_submit (ehci, urb, mem_flags);
#else #else
// FIXME highspeed iso stuff is written but never run/tested. dbg ("no split iso support yet");
// and the split iso support isn't even written yet.
dbg ("no iso support yet");
return -ENOSYS; return -ENOSYS;
#endif /* have_iso */ #endif /* have_split_iso */
} }
return 0; return 0;
......
/* /*
* 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 * 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 * under the terms of the GNU General Public License as published by the
...@@ -68,8 +68,7 @@ ehci_hub_status_data (struct usb_hcd *hcd, char *buf) ...@@ -68,8 +68,7 @@ ehci_hub_status_data (struct usb_hcd *hcd, char *buf)
/* init status to no-changes */ /* init status to no-changes */
buf [0] = 0; buf [0] = 0;
temp = readl (&ehci->caps->hcs_params); ports = HCS_N_PORTS (ehci->hcs_params);
ports = HCS_N_PORTS (temp);
if (ports > 7) { if (ports > 7) {
buf [1] = 0; buf [1] = 0;
retval++; retval++;
...@@ -107,8 +106,7 @@ ehci_hub_descriptor ( ...@@ -107,8 +106,7 @@ ehci_hub_descriptor (
struct ehci_hcd *ehci, struct ehci_hcd *ehci,
struct usb_hub_descriptor *desc struct usb_hub_descriptor *desc
) { ) {
u32 params = readl (&ehci->caps->hcs_params); int ports = HCS_N_PORTS (ehci->hcs_params);
int ports = HCS_N_PORTS (params);
u16 temp; u16 temp;
desc->bDescriptorType = 0x29; desc->bDescriptorType = 0x29;
...@@ -124,10 +122,10 @@ ehci_hub_descriptor ( ...@@ -124,10 +122,10 @@ ehci_hub_descriptor (
memset (&desc->bitmap [temp], 0xff, temp); memset (&desc->bitmap [temp], 0xff, temp);
temp = 0x0008; /* per-port overcurrent reporting */ temp = 0x0008; /* per-port overcurrent reporting */
if (HCS_PPC (params)) /* per-port power control */ if (HCS_PPC (ehci->hcs_params))
temp |= 0x0001; temp |= 0x0001; /* per-port power control */
if (HCS_INDICATOR (params)) /* per-port indicators (LEDs) */ if (HCS_INDICATOR (ehci->hcs_params))
temp |= 0x0080; temp |= 0x0080; /* per-port indicators (LEDs) */
desc->wHubCharacteristics = cpu_to_le16 (temp); desc->wHubCharacteristics = cpu_to_le16 (temp);
} }
...@@ -142,8 +140,7 @@ static int ehci_hub_control ( ...@@ -142,8 +140,7 @@ static int ehci_hub_control (
u16 wLength u16 wLength
) { ) {
struct ehci_hcd *ehci = hcd_to_ehci (hcd); struct ehci_hcd *ehci = hcd_to_ehci (hcd);
u32 params = readl (&ehci->caps->hcs_params); int ports = HCS_N_PORTS (ehci->hcs_params);
int ports = HCS_N_PORTS (params);
u32 temp; u32 temp;
unsigned long flags; unsigned long flags;
int retval = 0; int retval = 0;
...@@ -189,7 +186,7 @@ static int ehci_hub_control ( ...@@ -189,7 +186,7 @@ static int ehci_hub_control (
/* ? */ /* ? */
break; break;
case USB_PORT_FEAT_POWER: case USB_PORT_FEAT_POWER:
if (HCS_PPC (params)) if (HCS_PPC (ehci->hcs_params))
writel (temp & ~PORT_POWER, writel (temp & ~PORT_POWER,
&ehci->regs->port_status [wIndex]); &ehci->regs->port_status [wIndex]);
break; break;
...@@ -300,7 +297,7 @@ static int ehci_hub_control ( ...@@ -300,7 +297,7 @@ static int ehci_hub_control (
&ehci->regs->port_status [wIndex]); &ehci->regs->port_status [wIndex]);
break; break;
case USB_PORT_FEAT_POWER: case USB_PORT_FEAT_POWER:
if (HCS_PPC (params)) if (HCS_PPC (ehci->hcs_params))
writel (temp | PORT_POWER, writel (temp | PORT_POWER,
&ehci->regs->port_status [wIndex]); &ehci->regs->port_status [wIndex]);
break; break;
...@@ -312,6 +309,13 @@ static int ehci_hub_control ( ...@@ -312,6 +309,13 @@ static int ehci_hub_control (
hcd->bus_name, wIndex + 1); hcd->bus_name, wIndex + 1);
temp |= PORT_OWNER; temp |= PORT_OWNER;
} else { } else {
/* Philips 1562 wants CMD_RUN to reset */
if (!HCD_IS_RUNNING(ehci->hcd.state)) {
u32 cmd = readl (&ehci->regs->command);
cmd |= CMD_RUN;
writel (cmd, &ehci->regs->command);
ehci->hcd.state = USB_STATE_RUNNING;
}
vdbg ("%s port %d reset", vdbg ("%s port %d reset",
hcd->bus_name, wIndex + 1); hcd->bus_name, wIndex + 1);
temp |= PORT_RESET; temp |= PORT_RESET;
......
/* /*
* 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 * 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 * under the terms of the GNU General Public License as published by the
...@@ -643,12 +643,19 @@ ehci_qh_make ( ...@@ -643,12 +643,19 @@ ehci_qh_make (
if (usb_pipecontrol (urb->pipe)) { if (usb_pipecontrol (urb->pipe)) {
info1 |= 64 << 16; /* usb2 fixed maxpacket */ info1 |= 64 << 16; /* usb2 fixed maxpacket */
info1 |= 1 << 14; /* toggle from qtd */ info1 |= 1 << 14; /* toggle from qtd */
info2 |= (EHCI_TUNE_MULT_HS << 30);
} else if (usb_pipebulk (urb->pipe)) { } else if (usb_pipebulk (urb->pipe)) {
info1 |= 512 << 16; /* usb2 fixed maxpacket */ info1 |= 512 << 16; /* usb2 fixed maxpacket */
info2 |= (EHCI_TUNE_MULT_HS << 30); info2 |= (EHCI_TUNE_MULT_HS << 30);
} else } else {
info1 |= usb_maxpacket (urb->dev, urb->pipe, u32 temp;
usb_pipeout (urb->pipe)) << 16; 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; break;
default: default:
#ifdef DEBUG #ifdef DEBUG
......
/* /*
* 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 * 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 * under the terms of the GNU General Public License as published by the
...@@ -55,12 +55,12 @@ periodic_next_shadow (union ehci_shadow *periodic, int tag) ...@@ -55,12 +55,12 @@ periodic_next_shadow (union ehci_shadow *periodic, int tag)
return &periodic->qh->qh_next; return &periodic->qh->qh_next;
case Q_TYPE_FSTN: case Q_TYPE_FSTN:
return &periodic->fstn->fstn_next; return &periodic->fstn->fstn_next;
#ifdef have_iso
case Q_TYPE_ITD: case Q_TYPE_ITD:
return &periodic->itd->itd_next; return &periodic->itd->itd_next;
#ifdef have_split_iso
case Q_TYPE_SITD: case Q_TYPE_SITD:
return &periodic->sitd->sitd_next; return &periodic->sitd->sitd_next;
#endif /* have_iso */ #endif /* have_split_iso */
} }
dbg ("BAD shadow %p tag %d", periodic->ptr, tag); dbg ("BAD shadow %p tag %d", periodic->ptr, tag);
// BUG (); // BUG ();
...@@ -109,9 +109,6 @@ periodic_usecs (struct ehci_hcd *ehci, unsigned frame, unsigned uframe) ...@@ -109,9 +109,6 @@ periodic_usecs (struct ehci_hcd *ehci, unsigned frame, unsigned uframe)
u32 *hw_p = &ehci->periodic [frame]; u32 *hw_p = &ehci->periodic [frame];
union ehci_shadow *q = &ehci->pshadow [frame]; union ehci_shadow *q = &ehci->pshadow [frame];
unsigned usecs = 0; unsigned usecs = 0;
#ifdef have_iso
u32 temp = 0;
#endif
while (q->ptr) { while (q->ptr) {
switch (Q_NEXT_TYPE (*hw_p)) { switch (Q_NEXT_TYPE (*hw_p)) {
...@@ -130,15 +127,13 @@ periodic_usecs (struct ehci_hcd *ehci, unsigned frame, unsigned uframe) ...@@ -130,15 +127,13 @@ periodic_usecs (struct ehci_hcd *ehci, unsigned frame, unsigned uframe)
} }
q = &q->fstn->fstn_next; q = &q->fstn->fstn_next;
break; break;
#ifdef have_iso
case Q_TYPE_ITD: case Q_TYPE_ITD:
temp = le32_to_cpu (q->itd->transaction [uframe]); /* NOTE the "one uframe per itd" policy */
temp >>= 16; if (q->itd->hw_transaction [uframe] != 0)
temp &= 0x0fff; usecs += q->itd->usecs;
if (temp)
usecs += HS_USECS_ISO (temp);
q = &q->itd->itd_next; q = &q->itd->itd_next;
break; break;
#ifdef have_split_iso
case Q_TYPE_SITD: case Q_TYPE_SITD:
temp = q->sitd->hw_fullspeed_ep & temp = q->sitd->hw_fullspeed_ep &
__constant_cpu_to_le32 (1 << 31); __constant_cpu_to_le32 (1 << 31);
...@@ -163,7 +158,7 @@ periodic_usecs (struct ehci_hcd *ehci, unsigned frame, unsigned uframe) ...@@ -163,7 +158,7 @@ periodic_usecs (struct ehci_hcd *ehci, unsigned frame, unsigned uframe)
} }
q = &q->sitd->sitd_next; q = &q->sitd->sitd_next;
break; break;
#endif /* have_iso */ #endif /* have_split_iso */
default: default:
BUG (); BUG ();
} }
...@@ -178,6 +173,45 @@ periodic_usecs (struct ehci_hcd *ehci, unsigned frame, unsigned uframe) ...@@ -178,6 +173,45 @@ periodic_usecs (struct ehci_hcd *ehci, unsigned frame, unsigned uframe)
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
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 ( static void intr_deschedule (
struct ehci_hcd *ehci, struct ehci_hcd *ehci,
unsigned frame, unsigned frame,
...@@ -199,21 +233,9 @@ static void intr_deschedule ( ...@@ -199,21 +233,9 @@ static void intr_deschedule (
ehci->periodic_urbs--; ehci->periodic_urbs--;
/* maybe turn off periodic schedule */ /* maybe turn off periodic schedule */
if (!ehci->periodic_urbs) { if (!ehci->periodic_urbs)
u32 cmd = readl (&ehci->regs->command); disable_periodic (ehci);
else
/* 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
vdbg ("periodic schedule still enabled"); vdbg ("periodic schedule still enabled");
spin_unlock_irqrestore (&ehci->lock, flags); spin_unlock_irqrestore (&ehci->lock, flags);
...@@ -242,7 +264,7 @@ static int intr_submit ( ...@@ -242,7 +264,7 @@ static int intr_submit (
) { ) {
unsigned epnum, period; unsigned epnum, period;
unsigned temp; unsigned temp;
unsigned short mult, usecs; unsigned short usecs;
unsigned long flags; unsigned long flags;
struct ehci_qh *qh; struct ehci_qh *qh;
struct hcd_dev *dev; struct hcd_dev *dev;
...@@ -255,12 +277,7 @@ static int intr_submit ( ...@@ -255,12 +277,7 @@ static int intr_submit (
epnum |= 0x10; epnum |= 0x10;
} else } else
temp = urb->dev->epmaxpacketout [epnum]; temp = urb->dev->epmaxpacketout [epnum];
mult = 1; if (urb->dev->speed != USB_SPEED_HIGH) {
if (urb->dev->speed == USB_SPEED_HIGH) {
/* high speed "high bandwidth" is coded in ep maxpacket */
mult += (temp >> 11) & 0x03;
temp &= 0x03ff;
} else {
dbg ("no intr/tt scheduling yet"); dbg ("no intr/tt scheduling yet");
status = -ENOSYS; status = -ENOSYS;
goto done; goto done;
...@@ -279,21 +296,12 @@ static int intr_submit ( ...@@ -279,21 +296,12 @@ static int intr_submit (
usecs = HS_USECS (urb->transfer_buffer_length); usecs = HS_USECS (urb->transfer_buffer_length);
/* /* FIXME handle HS periods of less than 1 frame. */
* force a power-of-two (frames) sized polling interval if (urb->interval < 8)
* period = 1;
* NOTE: endpoint->bInterval for highspeed is measured in uframes, else
* while for full/low speeds it's in frames. Here we "know" that period = urb->interval >> 8;
* 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;
spin_lock_irqsave (&ehci->lock, flags); spin_lock_irqsave (&ehci->lock, flags);
/* get the qh (must be empty and idle) */ /* get the qh (must be empty and idle) */
...@@ -335,7 +343,6 @@ static int intr_submit ( ...@@ -335,7 +343,6 @@ static int intr_submit (
unsigned frame = urb->interval; unsigned frame = urb->interval;
qh->hw_next = EHCI_LIST_END; qh->hw_next = EHCI_LIST_END;
qh->hw_info2 |= cpu_to_le32 (mult << 30);
qh->usecs = usecs; qh->usecs = usecs;
urb->hcpriv = qh_put (qh); urb->hcpriv = qh_put (qh);
...@@ -378,7 +385,7 @@ static int intr_submit ( ...@@ -378,7 +385,7 @@ static int intr_submit (
/* stuff into the periodic schedule */ /* stuff into the periodic schedule */
qh->qh_state = QH_STATE_LINKED; 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); qh, qh->usecs, period, frame, uframe);
do { do {
if (unlikely (ehci->pshadow [frame].ptr != 0)) { if (unlikely (ehci->pshadow [frame].ptr != 0)) {
...@@ -397,23 +404,8 @@ static int intr_submit ( ...@@ -397,23 +404,8 @@ static int intr_submit (
usb_claim_bandwidth (urb->dev, urb, usecs, 0); usb_claim_bandwidth (urb->dev, urb, usecs, 0);
/* maybe enable periodic schedule processing */ /* maybe enable periodic schedule processing */
if (!ehci->periodic_urbs++) { if (!ehci->periodic_urbs++)
u32 cmd; enable_periodic (ehci);
/* 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);
}
break; break;
} while (frame); } while (frame);
...@@ -489,53 +481,56 @@ intr_complete ( ...@@ -489,53 +481,56 @@ intr_complete (
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
#ifdef have_iso static void
itd_free_list (struct ehci_hcd *ehci, struct urb *urb)
static inline void itd_free (struct ehci_hcd *ehci, struct ehci_itd *itd)
{ {
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;
} }
/* static int
* Create itd and allocate into uframes within specified frame. itd_fill (
* Caller must update the resulting uframe links.
*/
static struct ehci_itd *
itd_make (
struct ehci_hcd *ehci, struct ehci_hcd *ehci,
struct ehci_itd *itd,
struct urb *urb, struct urb *urb,
unsigned index, // urb->iso_frame_desc [index] unsigned index, // urb->iso_frame_desc [index]
unsigned frame, // scheduled start dma_addr_t dma // mapped transfer buffer
dma_addr_t dma, // mapped transfer buffer
int mem_flags
) { ) {
struct ehci_itd *itd;
u64 temp; u64 temp;
u32 buf1; u32 buf1;
unsigned epnum, maxp, multi, usecs; unsigned i, epnum, maxp, multi;
unsigned length; 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->hw_next = EHCI_LIST_END;
itd->urb = urb; itd->urb = urb;
itd->index = index; 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; length = urb->iso_frame_desc [index].length;
dma += urb->iso_frame_desc [index].offset; dma += urb->iso_frame_desc [index].offset;
temp = dma & ~0x0fff; 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 [i] = cpu_to_le32 ((u32) temp);
itd->hw_bufp_hi [i] = cpu_to_le32 ((u32)(temp >> 32)); 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, * this might be a "high bandwidth" highspeed endpoint,
...@@ -544,282 +539,407 @@ itd_make ( ...@@ -544,282 +539,407 @@ itd_make (
epnum = usb_pipeendpoint (urb->pipe); epnum = usb_pipeendpoint (urb->pipe);
if (usb_pipein (urb->pipe)) { if (usb_pipein (urb->pipe)) {
maxp = urb->dev->epmaxpacketin [epnum]; maxp = urb->dev->epmaxpacketin [epnum];
buf1 = (1 << 11) | maxp; buf1 = (1 << 11);
} else { } else {
maxp = urb->dev->epmaxpacketout [epnum]; maxp = urb->dev->epmaxpacketout [epnum];
buf1 = maxp; buf1 = 0;
} }
buf1 |= (maxp & 0x03ff);
multi = 1; multi = 1;
multi += (temp >> 11) & 0x03; multi += (maxp >> 11) & 0x03;
maxp &= 0x03ff; 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 */ /* "plus" info in low order bits of buffer pointers */
itd->hw_bufp [0] |= cpu_to_le32 ((epnum << 8) | urb->dev->devnum); itd->hw_bufp [0] |= cpu_to_le32 ((epnum << 8) | urb->dev->devnum);
itd->hw_bufp [1] |= cpu_to_le32 (buf1); itd->hw_bufp [1] |= cpu_to_le32 (buf1);
itd->hw_bufp [2] |= cpu_to_le32 (multi); itd->hw_bufp [2] |= cpu_to_le32 (multi);
/* schedule as many uframes as needed */ /* figure hw_transaction[] value (it's scheduled later) */
maxp *= multi; itd->transaction = EHCI_ISOC_ACTIVE;
usecs = HS_USECS_ISO (maxp); itd->transaction |= dma & 0x0fff; /* offset; buffer=0 */
bufnum = 0; if ((index + 1) == urb->number_of_packets)
for (i = 0; i < 8; i++) { itd->transaction |= EHCI_ITD_IOC; /* end-of-urb irq */
unsigned t, offset, scratch; itd->transaction |= length << 16;
cpu_to_le32s (&itd->transaction);
if (length <= 0) { return 0;
itd->hw_transaction [i] = 0; }
continue;
}
/* don't commit more than 80% periodic == 100 usec */ static int
if ((periodic_usecs (ehci, itd->uframe, i) + usecs) > 100) itd_urb_transaction (
continue; 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 */ /* set up one dma mapping for this urb */
t = EHCI_ISOC_ACTIVE; buf_dma = pci_map_single (ehci->hcd.pdev,
t |= bufnum << 12; // which buffer? urb->transfer_buffer, urb->transfer_buffer_length,
offset = temp & 0x0fff; // offset therein usb_pipein (urb->pipe)
t |= offset; ? PCI_DMA_FROMDEVICE
if ((offset + maxp) >= 4096) // hc auto-wraps end-of-"page" : PCI_DMA_TODEVICE);
bufnum++; if (buf_dma == 0)
if (length <= maxp) { return -ENOMEM;
// 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; /* allocate/init ITDs */
length -= scratch; for (frame_index = 0, first_itd = 0;
} frame_index < urb->number_of_packets;
if (length > 0) { frame_index++) {
dbg ("iso frame too big, urb %p [%d], %d extra (of %d)", itd = pci_pool_alloc (ehci->itd_pool, mem_flags, &itd_dma);
urb, index, length, urb->iso_frame_desc [index].length); if (!itd) {
itd_free (ehci, itd); status = -ENOMEM;
itd = 0; 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 static inline void
itd_link (struct ehci_hcd *ehci, unsigned frame, struct ehci_itd *itd) 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) { * return zero on success, else -errno
if (!itd->itd_next.ptr) { * - start holds first uframe to start scheduling into
itd->itd_next = ehci->pshadow [frame]; * - max is the first uframe it's NOT (!) OK to start scheduling into
itd->hw_next = ehci->periodic [frame]; * math to be done modulo "mod" (ehci->periodic_size << 3)
} else if (itd->itd_next.ptr != ehci->pshadow [frame].ptr) { */
dbg ("frame %d itd link goof", frame); static int get_iso_range (
BUG (); 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 /* explicit start frame? */
itd_complete (struct ehci_hcd *ehci, struct ehci_itd *itd, unsigned long flags) 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;
/* if not unlinking: */ for (i = 0; i < urb->number_of_packets; i++) {
if (!(urb->transfer_flags & EHCI_STATE_UNLINK) urb->iso_frame_desc [i].status = -EINPROGRESS;
&& ehci->hcd.state != USB_STATE_HALT) { urb->iso_frame_desc [i].actual_length = 0;
int i; }
struct usb_iso_packet_descriptor *desc;
struct ehci_itd *first_itd = urb->hcpriv;
/* update status for this frame's transfers */ if ((status = get_iso_range (ehci, urb, &start, &max, mod)) != 0)
desc = &urb->iso_frame_desc [itd->index]; return status;
desc->status = 0;
desc->actual_length = 0; do {
for (i = 0; i < 8; i++) { unsigned uframe;
u32 t = itd->hw_transaction [i]; unsigned usecs;
if (t & (ISO_ERRS | EHCI_ISOC_ACTIVE)) { struct ehci_itd *itd;
if (t & EHCI_ISOC_ACTIVE)
desc->status = -EXDEV; /* check schedule: enough space? */
else if (t & EHCI_ISOC_BUF_ERR) itd = urb->hcpriv;
desc->status = usb_pipein (urb->pipe) uframe = start;
? -ENOSR /* couldn't read */ for (i = 0, uframe = start;
: -ECOMM; /* couldn't write */ i < urb->number_of_packets;
else if (t & EHCI_ISOC_BABBLE) i++, uframe += urb->interval) {
desc->status = -EOVERFLOW; uframe %= mod;
else /* (t & EHCI_ISOC_XACTERR) */
desc->status = -EPROTO; /* can't commit more than 80% periodic == 100 usec */
if (periodic_usecs (ehci, uframe >> 3, uframe & 0x7)
> (100 - itd->usecs)) {
itd = 0;
break; 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;
itd = list_entry (itd->itd_list.next,
struct ehci_itd, itd_list);
} }
/* handle completion now? */ /* update bandwidth utilization records (for usbfs) */
if ((itd->index + 1) != urb->number_of_packets) /* FIXME usbcore expects per-frame average, which isn't
return flags; * 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);
i = usb_pipein (urb->pipe); /* maybe enable periodic schedule processing */
if (i) if (!ehci->periodic_urbs++)
pci_dma_sync_single (ehci->hcd.pdev, enable_periodic (ehci);
first_itd->buf_dma,
urb->transfer_buffer_length, return 0;
PCI_DMA_FROMDEVICE);
/* 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);
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;
}
/* unlink done only on the last itd */ } while ((start = ++start % mod) != max);
} else if ((itd->index + 1) != urb->number_of_packets)
/* 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;
struct usb_iso_packet_descriptor *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);
}
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; 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); spin_unlock_irqrestore (&ehci->lock, flags);
if (ehci->hcd.state == USB_STATE_HALT) usb_hcd_giveback_urb (&ehci->hcd, urb);
urb->status = -ESHUTDOWN;
itd = urb->hcpriv;
urb->hcpriv = 0;
ehci_urb_done (ehci, itd->buf_dma, urb);
spin_lock_irqsave (&ehci->lock, flags); spin_lock_irqsave (&ehci->lock, flags);
/* take itds out of the hc's periodic schedule */ /* defer stopping schedule; completion can submit */
list_entry (itd->itd_list.prev, struct ehci_itd, itd_list) ehci->periodic_urbs--;
->itd_list.next = 0; if (!ehci->periodic_urbs)
do { disable_periodic (ehci);
struct ehci_itd *next;
if (itd->itd_list.next)
next = list_entry (itd->itd_list.next,
struct ehci_itd, itd_list);
else
next = 0;
// 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; 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; int status;
unsigned frame_index; unsigned long flags;
dma_addr_t dma;
unsigned long flags;
dbg ("itd_submit");
/* set up one dma mapping for this urb */ dbg ("itd_submit urb %p", 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;
/* NOTE DMA mapping assumes this ... */
if (urb->iso_frame_desc [0].offset != 0)
return -EINVAL;
/* /*
* Schedule as needed. This is VERY optimistic about free * NOTE doing this for now, anticipating periodic URB models
* bandwidth! But the API assumes drivers can pick frames * get updated to be "explicit resubmit".
* intelligently (how?), so there's no other good option.
*
* FIXME this doesn't handle urb->next rings, or try to
* use the iso periodicity.
*/ */
if (urb->transfer_flags & USB_ISO_ASAP) { if (urb->next) {
urb->start_frame = ehci_get_frame (&ehci->hcd); dbg ("use explicit resubmit for ISO");
urb->start_frame++; 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 */ /* allocate ITDs w/o locking anything */
itd = first_itd; status = itd_urb_transaction (ehci, urb, mem_flags);
do { if (status < 0)
unsigned i; return status;
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;
/* schedule ... need to lock */
spin_lock_irqsave (&ehci->lock, flags);
status = itd_schedule (ehci, urb);
spin_unlock_irqrestore (&ehci->lock, flags); spin_unlock_irqrestore (&ehci->lock, flags);
return 0; if (status < 0)
itd_free_list (ehci, urb);
return status;
} }
#ifdef have_split_iso
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
/* /*
...@@ -827,7 +947,7 @@ static int itd_submit (struct ehci_hcd *ehci, struct urb *urb) ...@@ -827,7 +947,7 @@ static int itd_submit (struct ehci_hcd *ehci, struct urb *urb)
* the TTs in USB 2.0 hubs. * the TTs in USB 2.0 hubs.
*/ */
static inline void static void
sitd_free (struct ehci_hcd *ehci, struct ehci_sitd *sitd) sitd_free (struct ehci_hcd *ehci, struct ehci_sitd *sitd)
{ {
pci_pool_free (ehci->sitd_pool, sitd, sitd->sitd_dma); pci_pool_free (ehci->sitd_pool, sitd, sitd->sitd_dma);
...@@ -861,7 +981,7 @@ sitd_make ( ...@@ -861,7 +981,7 @@ sitd_make (
} }
static inline void static void
sitd_link (struct ehci_hcd *ehci, unsigned frame, struct ehci_sitd *sitd) sitd_link (struct ehci_hcd *ehci, unsigned frame, struct ehci_sitd *sitd)
{ {
u32 ptr; u32 ptr;
...@@ -894,12 +1014,11 @@ sitd_complete ( ...@@ -894,12 +1014,11 @@ sitd_complete (
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
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; // struct ehci_sitd *first_sitd = 0;
unsigned frame_index; unsigned frame_index;
dma_addr_t dma; dma_addr_t dma;
int mem_flags;
dbg ("NYI -- sitd_submit"); dbg ("NYI -- sitd_submit");
...@@ -908,8 +1027,6 @@ static int sitd_submit (struct ehci_hcd *ehci, struct urb *urb) ...@@ -908,8 +1027,6 @@ static int sitd_submit (struct ehci_hcd *ehci, struct urb *urb)
// FIXME: setup one big dma mapping // FIXME: setup one big dma mapping
dma = 0; dma = 0;
mem_flags = SLAB_ATOMIC;
for (frame_index = 0; for (frame_index = 0;
frame_index < urb->number_of_packets; frame_index < urb->number_of_packets;
frame_index++) { frame_index++) {
...@@ -941,53 +1058,62 @@ static int sitd_submit (struct ehci_hcd *ehci, struct urb *urb) ...@@ -941,53 +1058,62 @@ static int sitd_submit (struct ehci_hcd *ehci, struct urb *urb)
return -ENOSYS; return -ENOSYS;
} }
#endif /* have_split_iso */
#endif /* have_iso */
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
static void scan_periodic (struct ehci_hcd *ehci) static void scan_periodic (struct ehci_hcd *ehci)
{ {
unsigned frame; unsigned frame, clock, now_uframe, mod;
unsigned clock;
unsigned long flags; unsigned long flags;
mod = ehci->periodic_size << 3;
spin_lock_irqsave (&ehci->lock, flags); spin_lock_irqsave (&ehci->lock, flags);
/* /*
* When running, scan from last scan point up to "now" * 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. * 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_uframe >> 3;
frame = ehci->next_frame; if (HCD_IS_RUNNING (ehci->hcd.state))
clock = ehci_get_frame (&ehci->hcd); 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 (;;) { for (;;) {
union ehci_shadow q; union ehci_shadow q, *q_p;
u32 type; u32 type, *hw_p;
unsigned uframes;
restart: restart:
q.ptr = ehci->pshadow [frame].ptr; /* scan schedule to _before_ current frame index */
type = Q_NEXT_TYPE (ehci->periodic [frame]); 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 */ /* scan each element in frame's queue for completions */
while (q.ptr != 0) { while (q.ptr != 0) {
int last; int last;
unsigned uf;
union ehci_shadow temp; union ehci_shadow temp;
switch (type) { switch (type) {
case Q_TYPE_QH: case Q_TYPE_QH:
last = (q.qh->hw_next == EHCI_LIST_END); last = (q.qh->hw_next == EHCI_LIST_END);
temp = q.qh->qh_next;
type = Q_NEXT_TYPE (q.qh->hw_next);
flags = intr_complete (ehci, frame, flags = intr_complete (ehci, frame,
qh_put (q.qh), flags); qh_put (q.qh), flags);
type = Q_NEXT_TYPE (q.qh->hw_next);
temp = q.qh->qh_next;
qh_unput (ehci, q.qh); qh_unput (ehci, q.qh);
q = temp; q = temp;
break; break;
...@@ -1000,22 +1126,49 @@ static void scan_periodic (struct ehci_hcd *ehci) ...@@ -1000,22 +1126,49 @@ static void scan_periodic (struct ehci_hcd *ehci)
dbg ("ignoring completions from FSTNs"); dbg ("ignoring completions from FSTNs");
} }
type = Q_NEXT_TYPE (q.fstn->hw_next); type = Q_NEXT_TYPE (q.fstn->hw_next);
temp = q.fstn->fstn_next; q = q.fstn->fstn_next;
break; break;
#ifdef have_iso
case Q_TYPE_ITD: case Q_TYPE_ITD:
last = (q.itd->hw_next == EHCI_LIST_END); last = (q.itd->hw_next == EHCI_LIST_END);
flags = itd_complete (ehci, q.itd, flags);
type = Q_NEXT_TYPE (q.itd->hw_next); /* Unlink each (S)ITD we see, since the ISO
q = q.itd->itd_next; * 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; break;
#ifdef have_split_iso
case Q_TYPE_SITD: case Q_TYPE_SITD:
last = (q.sitd->hw_next == EHCI_LIST_END); last = (q.sitd->hw_next == EHCI_LIST_END);
flags = sitd_complete (ehci, q.sitd, flags); flags = sitd_complete (ehci, q.sitd, flags);
type = Q_NEXT_TYPE (q.sitd->hw_next); type = Q_NEXT_TYPE (q.sitd->hw_next);
// FIXME unlink SITD after split completes
q = q.sitd->sitd_next; q = q.sitd->sitd_next;
break; break;
#endif /* have_iso */ #endif /* have_split_iso */
default: default:
dbg ("corrupt type %d frame %d shadow %p", dbg ("corrupt type %d frame %d shadow %p",
type, frame, q.ptr); type, frame, q.ptr);
...@@ -1044,13 +1197,16 @@ static void scan_periodic (struct ehci_hcd *ehci) ...@@ -1044,13 +1197,16 @@ static void scan_periodic (struct ehci_hcd *ehci)
if (!HCD_IS_RUNNING (ehci->hcd.state)) if (!HCD_IS_RUNNING (ehci->hcd.state))
break; break;
ehci->next_frame = clock; ehci->next_uframe = now_uframe;
now = ehci_get_frame (&ehci->hcd); now = readl (&ehci->regs->frame_index) % mod;
if (clock == now) if (now_uframe == now)
break; break;
clock = now;
} else if (++frame >= ehci->periodic_size) /* rescan the rest of this frame, then ... */
frame = 0; now_uframe = now;
clock = now_uframe >> 3;
} else
frame = (frame + 1) % ehci->periodic_size;
} }
spin_unlock_irqrestore (&ehci->lock, flags); spin_unlock_irqrestore (&ehci->lock, flags);
} }
/* /*
* 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 * 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 * under the terms of the GNU General Public License as published by the
...@@ -49,7 +49,7 @@ struct ehci_hcd { /* one per controller */ ...@@ -49,7 +49,7 @@ struct ehci_hcd { /* one per controller */
unsigned i_thresh; /* uframes HC might cache */ unsigned i_thresh; /* uframes HC might cache */
union ehci_shadow *pshadow; /* mirror hw periodic table */ 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? */ unsigned periodic_urbs; /* how many urbs scheduled? */
/* deferred work from IRQ, etc */ /* deferred work from IRQ, etc */
...@@ -62,6 +62,7 @@ struct ehci_hcd { /* one per controller */ ...@@ -62,6 +62,7 @@ struct ehci_hcd { /* one per controller */
struct usb_hcd hcd; struct usb_hcd hcd;
struct ehci_caps *caps; struct ehci_caps *caps;
struct ehci_regs *regs; struct ehci_regs *regs;
u32 hcs_params; /* cached register copy */
/* per-HC memory pools (could be per-PCI-bus, but ...) */ /* per-HC memory pools (could be per-PCI-bus, but ...) */
struct pci_pool *qh_pool; /* qh per active urb */ struct pci_pool *qh_pool; /* qh per active urb */
...@@ -324,13 +325,14 @@ struct ehci_itd { ...@@ -324,13 +325,14 @@ struct ehci_itd {
union ehci_shadow itd_next; /* ptr to periodic q entry */ union ehci_shadow itd_next; /* ptr to periodic q entry */
struct urb *urb; struct urb *urb;
unsigned index; /* in urb->iso_frame_desc */
struct list_head itd_list; /* list of urb frames' itds */ struct list_head itd_list; /* list of urb frames' itds */
dma_addr_t buf_dma; /* frame's buffer address */ dma_addr_t buf_dma; /* frame's buffer address */
unsigned uframe; /* in periodic schedule */ /* for now, only one hw_transaction per itd */
u32 transaction [8]; /* copy of hw_transaction */ u32 transaction;
u16 index; /* in urb->iso_frame_desc */
u16 uframe; /* in periodic schedule */
u16 usecs;
} __attribute__ ((aligned (32))); } __attribute__ ((aligned (32)));
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
......
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