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
......
This diff is collapsed.
/* /*
* 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