|
From: Steve L. <slo...@us...> - 2002-11-01 19:35:06
|
Update of /cvsroot/linux-mips/linux/drivers/usb
In directory usw-pr-cvs1:/tmp/cvs-serv6625/usb
Added Files:
Config.in Makefile usb-ohci-nonpci.c usb-ohci-pci.c usb-ohci.c
usb-ohci.h
Log Message:
USB non-PCI OHCI support.
--- NEW FILE: Config.in ---
#
# USB device configuration
#
mainmenu_option next_comment
comment 'USB support'
dep_tristate 'Support for USB' CONFIG_USB $CONFIG_PCI
if [ "$CONFIG_USB" = "y" -o "$CONFIG_USB" = "m" ]; then
bool ' USB verbose debug messages' CONFIG_USB_DEBUG
comment 'Miscellaneous USB options'
bool ' Preliminary USB device filesystem' CONFIG_USB_DEVICEFS
if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
bool ' Enforce USB bandwidth allocation (EXPERIMENTAL)' CONFIG_USB_BANDWIDTH
else
define_bool CONFIG_USB_BANDWIDTH n
fi
bool ' Long timeout for slow-responding devices (some MGE Ellipse UPSes)' CONFIG_USB_LONG_TIMEOUT
fi
comment 'USB Controllers'
if [ "$CONFIG_USB_UHCI_ALT" != "y" ]; then
dep_tristate ' UHCI (Intel PIIX4, VIA, ...) support' CONFIG_USB_UHCI $CONFIG_USB
fi
if [ "$CONFIG_USB_UHCI" != "y" ]; then
dep_tristate ' UHCI Alternate Driver (JE) support' CONFIG_USB_UHCI_ALT $CONFIG_USB
else
define_bool CONFIG_USB_UHCI_ALT n
fi
dep_tristate ' OHCI (Compaq, iMacs, OPTi, SiS, ALi, ...) support' CONFIG_USB_OHCI $CONFIG_USB
dep_tristate ' Non-PCI OHCI support' CONFIG_USB_NON_PCI_OHCI $CONFIG_USB_OHCI
comment 'USB Device Class drivers'
dep_tristate ' USB Audio support' CONFIG_USB_AUDIO $CONFIG_USB $CONFIG_SOUND
dep_tristate ' USB Bluetooth support (EXPERIMENTAL)' CONFIG_USB_BLUETOOTH $CONFIG_USB $CONFIG_EXPERIMENTAL
if [ "$CONFIG_SCSI" = "n" ]; then
comment ' SCSI support is needed for USB Storage'
fi
dep_tristate ' USB Mass Storage support' CONFIG_USB_STORAGE $CONFIG_USB $CONFIG_SCSI
dep_mbool ' USB Mass Storage verbose debug' CONFIG_USB_STORAGE_DEBUG $CONFIG_USB_STORAGE
dep_mbool ' Datafab MDCFE-B Compact Flash Reader support' CONFIG_USB_STORAGE_DATAFAB $CONFIG_USB_STORAGE $CONFIG_EXPERIMENTAL
dep_mbool ' Freecom USB/ATAPI Bridge support' CONFIG_USB_STORAGE_FREECOM $CONFIG_USB_STORAGE
dep_mbool ' ISD-200 USB/ATA Bridge support' CONFIG_USB_STORAGE_ISD200 $CONFIG_USB_STORAGE
dep_mbool ' Microtech CompactFlash/SmartMedia support' CONFIG_USB_STORAGE_DPCM $CONFIG_USB_STORAGE
dep_mbool ' HP CD-Writer 82xx support' CONFIG_USB_STORAGE_HP8200e $CONFIG_USB_STORAGE $CONFIG_EXPERIMENTAL
dep_mbool ' SanDisk SDDR-09 (and other SmartMedia) support' CONFIG_USB_STORAGE_SDDR09 $CONFIG_USB_STORAGE $CONFIG_EXPERIMENTAL
dep_mbool ' Lexar Jumpshot Compact Flash Reader' CONFIG_USB_STORAGE_JUMPSHOT $CONFIG_USB_STORAGE $CONFIG_EXPERIMENTAL
dep_tristate ' USB Modem (CDC ACM) support' CONFIG_USB_ACM $CONFIG_USB
dep_tristate ' USB Printer support' CONFIG_USB_PRINTER $CONFIG_USB
comment 'USB Human Interface Devices (HID)'
if [ "$CONFIG_INPUT" = "n" ]; then
comment ' Input core support is needed for USB HID'
else
dep_tristate ' USB Human Interface Device (full HID) support' CONFIG_USB_HID $CONFIG_USB $CONFIG_INPUT
dep_mbool ' /dev/hiddev raw HID device support (EXPERIMENTAL)' CONFIG_USB_HIDDEV $CONFIG_USB_HID
if [ "$CONFIG_USB_HID" != "y" ]; then
dep_tristate ' USB HIDBP Keyboard (basic) support' CONFIG_USB_KBD $CONFIG_USB $CONFIG_INPUT
dep_tristate ' USB HIDBP Mouse (basic) support' CONFIG_USB_MOUSE $CONFIG_USB $CONFIG_INPUT
fi
dep_tristate ' Wacom Intuos/Graphire tablet support' CONFIG_USB_WACOM $CONFIG_USB $CONFIG_INPUT
fi
comment 'USB Imaging devices'
dep_tristate ' USB Kodak DC-2xx Camera support' CONFIG_USB_DC2XX $CONFIG_USB
dep_tristate ' USB Mustek MDC800 Digital Camera support (EXPERIMENTAL)' CONFIG_USB_MDC800 $CONFIG_USB $CONFIG_EXPERIMENTAL
dep_tristate ' USB Scanner support' CONFIG_USB_SCANNER $CONFIG_USB
dep_tristate ' Microtek X6USB scanner support' CONFIG_USB_MICROTEK $CONFIG_USB $CONFIG_SCSI
dep_tristate ' HP53xx USB scanner support (EXPERIMENTAL)' CONFIG_USB_HPUSBSCSI $CONFIG_USB $CONFIG_SCSI $CONFIG_EXPERIMENTAL
comment 'USB Multimedia devices'
if [ "$CONFIG_VIDEO_DEV" = "n" ]; then
comment ' Video4Linux support is needed for USB Multimedia device support'
else
dep_tristate ' USB IBM (Xirlink) C-it Camera support' CONFIG_USB_IBMCAM $CONFIG_USB $CONFIG_VIDEO_DEV
dep_tristate ' USB OV511 Camera support' CONFIG_USB_OV511 $CONFIG_USB $CONFIG_VIDEO_DEV
dep_tristate ' USB Philips Cameras' CONFIG_USB_PWC $CONFIG_USB $CONFIG_VIDEO_DEV
dep_tristate ' USB SE401 Camera support' CONFIG_USB_SE401 $CONFIG_USB $CONFIG_VIDEO_DEV
dep_tristate ' USB STV680 (Pencam) Camera support' CONFIG_USB_STV680 $CONFIG_USB $CONFIG_VIDEO_DEV
dep_tristate ' USB 3com HomeConnect (aka vicam) support (EXPERIMENTAL)' CONFIG_USB_VICAM $CONFIG_USB $CONFIG_VIDEO_DEV $CONFIG_EXPERIMENTAL
dep_tristate ' D-Link USB FM radio support (EXPERIMENTAL)' CONFIG_USB_DSBR $CONFIG_USB $CONFIG_VIDEO_DEV $CONFIG_EXPERIMENTAL
dep_tristate ' DABUSB driver' CONFIG_USB_DABUSB $CONFIG_USB
fi
comment 'USB Network adaptors'
if [ "$CONFIG_NET" = "n" ]; then
comment ' Networking support is needed for USB Networking device support'
else
dep_tristate ' USB ADMtek Pegasus-based ethernet device support (EXPERIMENTAL)' CONFIG_USB_PEGASUS $CONFIG_USB $CONFIG_NET $CONFIG_EXPERIMENTAL
dep_tristate ' USB KLSI KL5USB101-based ethernet device support (EXPERIMENTAL)' CONFIG_USB_KAWETH $CONFIG_USB $CONFIG_NET $CONFIG_EXPERIMENTAL
dep_tristate ' USB CATC NetMate-based Ethernet device support (EXPERIMENTAL)' CONFIG_USB_CATC $CONFIG_USB $CONFIG_NET $CONFIG_EXPERIMENTAL
dep_tristate ' USB Communication Class Ethernet device support (EXPERIMENTAL)' CONFIG_USB_CDCETHER $CONFIG_USB $CONFIG_NET $CONFIG_EXPERIMENTAL
dep_tristate ' USB-to-USB Networking cable device support (EXPERIMENTAL)' CONFIG_USB_USBNET $CONFIG_USB $CONFIG_NET $CONFIG_EXPERIMENTAL
fi
comment 'USB port drivers'
dep_tristate ' USS720 parport driver' CONFIG_USB_USS720 $CONFIG_USB $CONFIG_PARPORT
source drivers/usb/serial/Config.in
comment 'USB Miscellaneous drivers'
dep_tristate ' USB Diamond Rio500 support (EXPERIMENTAL)' CONFIG_USB_RIO500 $CONFIG_USB $CONFIG_EXPERIMENTAL
endmenu
--- NEW FILE: Makefile ---
#
# Makefile for the kernel USB device drivers.
#
# Subdirs.
# The target object and module list name.
O_TARGET := usbdrv.o
# Objects that export symbols.
export-objs := usb.o ov511.o pwc-uncompress.o
# Multipart objects.
list-multi := usbcore.o hid.o pwc.o
usbcore-objs := usb.o usb-debug.o hub.o
hid-objs := hid-core.o hid-input.o
pwc-objs := pwc-if.o pwc-misc.o pwc-ctrl.o pwc-uncompress.o
# Optional parts of multipart objects.
ifeq ($(CONFIG_USB_DEVICEFS),y)
usbcore-objs += devio.o inode.o drivers.o devices.o
endif
ifeq ($(CONFIG_USB_HIDDEV),y)
hid-objs += hiddev.o
endif
# Object file lists.
obj-y :=
obj-m :=
obj-n :=
obj- :=
# Each configuration option enables a list of files.
obj-$(CONFIG_USB) += usbcore.o
obj-$(CONFIG_USB_UHCI) += usb-uhci.o
obj-$(CONFIG_USB_UHCI_ALT) += uhci.o
obj-$(CONFIG_USB_OHCI) += usb-ohci.o
obj-$(CONFIG_USB_NON_PCI_OHCI) += usb-ohci-nonpci.o
obj-$(CONFIG_USB_MOUSE) += usbmouse.o
obj-$(CONFIG_USB_HID) += hid.o
obj-$(CONFIG_USB_KBD) += usbkbd.o
obj-$(CONFIG_USB_WACOM) += wacom.o
obj-$(CONFIG_USB_SCANNER) += scanner.o
obj-$(CONFIG_USB_ACM) += acm.o
obj-$(CONFIG_USB_PRINTER) += printer.o
obj-$(CONFIG_USB_AUDIO) += audio.o
obj-$(CONFIG_USB_IBMCAM) += ibmcam.o usbvideo.o ultracam.o
obj-$(CONFIG_USB_PWC) += pwc.o
obj-$(CONFIG_USB_DC2XX) += dc2xx.o
obj-$(CONFIG_USB_MDC800) += mdc800.o
obj-$(CONFIG_USB_USS720) += uss720.o
obj-$(CONFIG_USB_DABUSB) += dabusb.o
obj-$(CONFIG_USB_VICAM) += vicam.o
obj-$(CONFIG_USB_OV511) += ov511.o
obj-$(CONFIG_USB_SE401) += se401.o
obj-$(CONFIG_USB_STV680) += stv680.o
obj-$(CONFIG_USB_PEGASUS) += pegasus.o
obj-$(CONFIG_USB_CATC) += catc.o
obj-$(CONFIG_USB_KAWETH) += kaweth.o
obj-$(CONFIG_USB_CDCETHER) += CDCEther.o
obj-$(CONFIG_USB_RIO500) += rio500.o
obj-$(CONFIG_USB_DSBR) += dsbr100.o
obj-$(CONFIG_USB_MICROTEK) += microtek.o
obj-$(CONFIG_USB_HPUSBSCSI) += hpusbscsi.o
obj-$(CONFIG_USB_BLUETOOTH) += bluetooth.o
obj-$(CONFIG_USB_USBNET) += usbnet.o
# Object files in subdirectories
mod-subdirs := serial
subdir-$(CONFIG_USB_SERIAL) += serial
subdir-$(CONFIG_USB_STORAGE) += storage
ifeq ($(CONFIG_USB_SERIAL),y)
obj-y += serial/usb-serial.o
endif
ifeq ($(CONFIG_USB_STORAGE),y)
obj-y += storage/storage.o
endif
include $(TOPDIR)/Rules.make
# Link rules for multi-part drivers.
usbcore.o: $(usbcore-objs)
$(LD) -r -o $@ $(usbcore-objs)
hid.o: $(hid-objs)
$(LD) -r -o $@ $(hid-objs)
pwc.o: $(pwc-objs)
$(LD) -r -o $@ $(pwc-objs)
--- NEW FILE: usb-ohci-nonpci.c ---
/*
* BRIEF MODULE DESCRIPTION
* Non-PCI or "builtin" OHCI support.
*
* Copyright 2001 MontaVista Software Inc.
* Author: MontaVista Software, Inc.
* st...@mv... or so...@mv...
*
* 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
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Notes:
* Same as usb-ohci-sa1111.c, except that IO base and IRQ are
* module parameters, and controller poweron/shutdown needs
* to be handled elsewhere.
*
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/ioport.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/usb.h>
#include <linux/pci.h>
#include <asm/irq.h>
#include <asm/io.h>
#include "usb-ohci.h"
int __devinit
hc_add_ohci(struct pci_dev *dev, int irq, void *membase, unsigned long flags,
ohci_t **ohci, const char *name, const char *slot_name);
extern void hc_remove_ohci(ohci_t *ohci);
static ohci_t *nonpci_ohci;
// Boot options
static int ohci_base=0, ohci_len=0;
static int ohci_irq=-1;
// bogus pci_dev
static struct pci_dev bogus_pcidev;
// ioremapped ohci_base
static void *mem_base;
MODULE_PARM(ohci_base, "i");
MODULE_PARM(ohci_len, "i");
MODULE_PARM(ohci_irq, "i");
MODULE_PARM_DESC(ohci_base, "IO Base address of OHCI Oper. registers");
MODULE_PARM_DESC(ohci_len, "IO length of OHCI Oper. registers");
MODULE_PARM_DESC(ohci_irq, "IRQ for OHCI interrupts");
#ifndef MODULE
static int __init
ohci_setup (char* options)
{
char* this_opt;
if (!options || !*options)
return 0;
for(this_opt=strtok(options, ","); this_opt; this_opt=strtok(NULL, ",")) {
if (!strncmp(this_opt, "base:", 5)) {
ohci_base = simple_strtoul(this_opt+5, NULL, 0);
} else if (!strncmp(this_opt, "len:", 4)) {
ohci_len = simple_strtoul(this_opt+4, NULL, 0);
} else if (!strncmp(this_opt, "irq:", 4)) {
ohci_irq = simple_strtoul(this_opt+4, NULL, 0);
}
}
return 0;
}
__setup("usb_ohci=", ohci_setup);
#endif
static int __init nonpci_ohci_init(void)
{
int ret;
if (!ohci_base || !ohci_len || (ohci_irq < 0))
return -ENODEV;
if (!request_mem_region (ohci_base, ohci_len, "usb-ohci")) {
dbg ("controller already in use");
return -EBUSY;
}
mem_base = ioremap_nocache (ohci_base, ohci_len);
if (!mem_base) {
err("Error mapping OHCI memory");
return -EFAULT;
}
/*
* Fill in some fields of the bogus pci_dev.
*/
memset(&bogus_pcidev, 0, sizeof(struct pci_dev));
strcpy(bogus_pcidev.name, "non-PCI OHCI");
strcpy(bogus_pcidev.slot_name, "builtin");
bogus_pcidev.resource[0].name = "OHCI Operational Registers";
bogus_pcidev.resource[0].start = ohci_base;
bogus_pcidev.resource[0].end = ohci_base + ohci_len;
bogus_pcidev.resource[0].flags = 0;
bogus_pcidev.irq = ohci_irq;
/*
* Initialise the generic OHCI driver.
*/
ret = hc_add_ohci(&bogus_pcidev, ohci_irq, mem_base, 0,
&nonpci_ohci, "usb-ohci", "builtin");
if (ret) {
iounmap(mem_base);
release_mem_region(ohci_base, ohci_len);
}
return ret;
}
static void __exit nonpci_ohci_exit(void)
{
hc_remove_ohci(nonpci_ohci);
release_mem_region(ohci_base, ohci_len);
}
module_init(nonpci_ohci_init);
module_exit(nonpci_ohci_exit);
--- NEW FILE: usb-ohci-pci.c ---
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/interrupt.h> /* for in_interrupt() */
#undef DEBUG
#include <linux/usb.h>
#include "usb-ohci.h"
#ifdef CONFIG_PMAC_PBOOK
#include <asm/machdep.h>
#include <asm/pmac_feature.h>
#include <asm/pci-bridge.h>
#ifndef CONFIG_PM
#define CONFIG_PM
#endif
#endif
/*-------------------------------------------------------------------------*/
/* Increment the module usage count, start the control thread and
* return success. */
static struct pci_driver ohci_pci_driver;
static int __devinit
hc_found_ohci (struct pci_dev *dev, int irq,
void *mem_base, const struct pci_device_id *id)
{
ohci_t * ohci;
u8 latency, limit;
char buf[8], *bufp = buf;
int ret;
#ifndef __sparc__
sprintf(buf, "%d", irq);
#else
bufp = __irq_itoa(irq);
#endif
printk(KERN_INFO __FILE__ ": USB OHCI at membase 0x%lx, IRQ %s\n",
(unsigned long) mem_base, bufp);
printk(KERN_INFO __FILE__ ": usb-%s, %s\n", dev->slot_name, dev->name);
ohci = hc_alloc_ohci (dev, mem_base);
if (!ohci) {
return -ENOMEM;
}
if ((ret = ohci_mem_init (ohci)) < 0) {
hc_release_ohci (ohci);
return ret;
}
ohci->flags = id->driver_data;
if (ohci->flags & OHCI_QUIRK_AMD756)
printk (KERN_INFO __FILE__ ": AMD756 erratum 4 workaround\n");
/* bad pci latencies can contribute to overruns */
pci_read_config_byte (dev, PCI_LATENCY_TIMER, &latency);
if (latency) {
pci_read_config_byte (dev, PCI_MAX_LAT, &limit);
if (limit && limit < latency) {
dbg ("PCI latency reduced to max %d", limit);
pci_write_config_byte (dev, PCI_LATENCY_TIMER, limit);
ohci->pci_latency = limit;
} else {
/* it might already have been reduced */
ohci->pci_latency = latency;
}
}
if (hc_reset (ohci) < 0) {
hc_release_ohci (ohci);
return -ENODEV;
}
/* FIXME this is a second HC reset; why?? */
writel (ohci->hc_control = OHCI_USB_RESET, &ohci->regs->control);
wait_ms (10);
usb_register_bus (ohci->bus);
if (request_irq (irq, hc_interrupt, SA_SHIRQ,
ohci_pci_driver.name, ohci) != 0) {
err ("request interrupt %s failed", bufp);
hc_release_ohci (ohci);
return -EBUSY;
}
ohci->irq = irq;
if (hc_start (ohci) < 0) {
err ("can't start usb-%s", dev->slot_name);
hc_release_ohci (ohci);
return -EBUSY;
}
#ifdef DEBUG
ohci_dump (ohci, 1);
#endif
return 0;
}
/*-------------------------------------------------------------------------*/
#ifdef CONFIG_PM
/* controller died; cleanup debris, then restart */
/* must not be called from interrupt context */
static void hc_restart (ohci_t *ohci)
{
int temp;
int i;
if (ohci->pci_latency)
pci_write_config_byte (ohci->ohci_dev, PCI_LATENCY_TIMER, ohci->pci_latency);
ohci->disabled = 1;
ohci->sleeping = 0;
if (ohci->bus->root_hub)
usb_disconnect (&ohci->bus->root_hub);
/* empty the interrupt branches */
for (i = 0; i < NUM_INTS; i++) ohci->ohci_int_load[i] = 0;
for (i = 0; i < NUM_INTS; i++) ohci->hcca->int_table[i] = 0;
/* no EDs to remove */
ohci->ed_rm_list [0] = NULL;
ohci->ed_rm_list [1] = NULL;
/* empty control and bulk lists */
ohci->ed_isotail = NULL;
ohci->ed_controltail = NULL;
ohci->ed_bulktail = NULL;
if ((temp = hc_reset (ohci)) < 0 || (temp = hc_start (ohci)) < 0) {
err ("can't restart usb-%s, %d", ohci->ohci_dev->slot_name, temp);
} else
dbg ("restart usb-%s completed", ohci->ohci_dev->slot_name);
}
#endif /* CONFIG_PM */
/*-------------------------------------------------------------------------*/
/* configured so that an OHCI device is always provided */
/* always called with process context; sleeping is OK */
static int __devinit
ohci_pci_probe (struct pci_dev *dev, const struct pci_device_id *id)
{
unsigned long mem_resource, mem_len;
void *mem_base;
if (pci_enable_device(dev) < 0)
return -ENODEV;
if (!dev->irq) {
err("found OHCI device with no IRQ assigned. check BIOS settings!");
return -ENODEV;
}
/* we read its hardware registers as memory */
mem_resource = pci_resource_start(dev, 0);
mem_len = pci_resource_len(dev, 0);
if (!request_mem_region (mem_resource, mem_len, ohci_pci_driver.name)) {
dbg ("controller already in use");
return -EBUSY;
}
mem_base = ioremap_nocache (mem_resource, mem_len);
if (!mem_base) {
err("Error mapping OHCI memory");
return -EFAULT;
}
/* controller writes into our memory */
pci_set_master (dev);
return hc_found_ohci (dev, dev->irq, mem_base, id);
}
/*-------------------------------------------------------------------------*/
/* may be called from interrupt context [interface spec] */
/* may be called without controller present */
/* may be called with controller, bus, and devices active */
static void __devexit
ohci_pci_remove (struct pci_dev *dev)
{
ohci_t *ohci = (ohci_t *) pci_get_drvdata(dev);
dbg ("remove %s controller usb-%s%s%s",
hcfs2string (ohci->hc_control & OHCI_CTRL_HCFS),
dev->slot_name,
ohci->disabled ? " (disabled)" : "",
in_interrupt () ? " in interrupt" : ""
);
hc_remove_ohci(ohci);
release_mem_region (pci_resource_start (dev, 0), pci_resource_len (dev, 0));
}
#ifdef CONFIG_PM
/*-------------------------------------------------------------------------*/
static int
ohci_pci_suspend (struct pci_dev *dev, u32 state)
{
ohci_t *ohci = (ohci_t *) pci_get_drvdata(dev);
unsigned long flags;
u16 cmd;
if ((ohci->hc_control & OHCI_CTRL_HCFS) != OHCI_USB_OPER) {
dbg ("can't suspend usb-%s (state is %s)", dev->slot_name,
hcfs2string (ohci->hc_control & OHCI_CTRL_HCFS));
return -EIO;
}
/* act as if usb suspend can always be used */
info ("USB suspend: usb-%s", dev->slot_name);
ohci->sleeping = 1;
/* First stop processing */
spin_lock_irqsave (&usb_ed_lock, flags);
ohci->hc_control &= ~(OHCI_CTRL_PLE|OHCI_CTRL_CLE|OHCI_CTRL_BLE|OHCI_CTRL_IE);
writel (ohci->hc_control, &ohci->regs->control);
writel (OHCI_INTR_SF, &ohci->regs->intrstatus);
(void) readl (&ohci->regs->intrstatus);
spin_unlock_irqrestore (&usb_ed_lock, flags);
/* Wait a frame or two */
mdelay(1);
if (!readl (&ohci->regs->intrstatus) & OHCI_INTR_SF)
mdelay (1);
#ifdef CONFIG_PMAC_PBOOK
if (_machine == _MACH_Pmac)
disable_irq (ohci->irq);
/* else, 2.4 assumes shared irqs -- don't disable */
#endif
/* Enable remote wakeup */
writel (readl(&ohci->regs->intrenable) | OHCI_INTR_RD, &ohci->regs->intrenable);
/* Suspend chip and let things settle down a bit */
ohci->hc_control = OHCI_USB_SUSPEND;
writel (ohci->hc_control, &ohci->regs->control);
(void) readl (&ohci->regs->control);
mdelay (500); /* No schedule here ! */
switch (readl (&ohci->regs->control) & OHCI_CTRL_HCFS) {
case OHCI_USB_RESET:
dbg("Bus in reset phase ???");
break;
case OHCI_USB_RESUME:
dbg("Bus in resume phase ???");
break;
case OHCI_USB_OPER:
dbg("Bus in operational phase ???");
break;
case OHCI_USB_SUSPEND:
dbg("Bus suspended");
break;
}
/* In some rare situations, Apple's OHCI have happily trashed
* memory during sleep. We disable it's bus master bit during
* suspend
*/
pci_read_config_word (dev, PCI_COMMAND, &cmd);
cmd &= ~PCI_COMMAND_MASTER;
pci_write_config_word (dev, PCI_COMMAND, cmd);
#ifdef CONFIG_PMAC_PBOOK
{
struct device_node *of_node;
/* Disable USB PAD & cell clock */
of_node = pci_device_to_OF_node (ohci->ohci_dev);
if (of_node)
pmac_call_feature(PMAC_FTR_USB_ENABLE, of_node, 0, 0);
}
#endif
return 0;
}
/*-------------------------------------------------------------------------*/
static int
ohci_pci_resume (struct pci_dev *dev)
{
ohci_t *ohci = (ohci_t *) pci_get_drvdata(dev);
int temp;
unsigned long flags;
/* guard against multiple resumes */
atomic_inc (&ohci->resume_count);
if (atomic_read (&ohci->resume_count) != 1) {
err ("concurrent PCI resumes for usb-%s", dev->slot_name);
atomic_dec (&ohci->resume_count);
return 0;
}
#ifdef CONFIG_PMAC_PBOOK
{
struct device_node *of_node;
/* Re-enable USB PAD & cell clock */
of_node = pci_device_to_OF_node (ohci->ohci_dev);
if (of_node)
pmac_call_feature(PMAC_FTR_USB_ENABLE, of_node, 0, 1);
}
#endif
/* did we suspend, or were we powered off? */
ohci->hc_control = readl (&ohci->regs->control);
temp = ohci->hc_control & OHCI_CTRL_HCFS;
#ifdef DEBUG
/* the registers may look crazy here */
ohci_dump_status (ohci);
#endif
/* Re-enable bus mastering */
pci_set_master(ohci->ohci_dev);
switch (temp) {
case OHCI_USB_RESET: // lost power
info ("USB restart: usb-%s", dev->slot_name);
hc_restart (ohci);
break;
case OHCI_USB_SUSPEND: // host wakeup
case OHCI_USB_RESUME: // remote wakeup
info ("USB continue: usb-%s from %s wakeup", dev->slot_name,
(temp == OHCI_USB_SUSPEND)
? "host" : "remote");
ohci->hc_control = OHCI_USB_RESUME;
writel (ohci->hc_control, &ohci->regs->control);
(void) readl (&ohci->regs->control);
mdelay (20); /* no schedule here ! */
/* Some controllers (lucent) need a longer delay here */
mdelay (15);
temp = readl (&ohci->regs->control);
temp = ohci->hc_control & OHCI_CTRL_HCFS;
if (temp != OHCI_USB_RESUME) {
err ("controller usb-%s won't resume", dev->slot_name);
ohci->disabled = 1;
return -EIO;
}
/* Some chips likes being resumed first */
writel (OHCI_USB_OPER, &ohci->regs->control);
(void) readl (&ohci->regs->control);
mdelay (3);
/* Then re-enable operations */
spin_lock_irqsave (&usb_ed_lock, flags);
ohci->disabled = 0;
ohci->sleeping = 0;
ohci->hc_control = OHCI_CONTROL_INIT | OHCI_USB_OPER;
if (!ohci->ed_rm_list[0] && !ohci->ed_rm_list[1]) {
if (ohci->ed_controltail)
ohci->hc_control |= OHCI_CTRL_CLE;
if (ohci->ed_bulktail)
ohci->hc_control |= OHCI_CTRL_BLE;
}
writel (ohci->hc_control, &ohci->regs->control);
writel (OHCI_INTR_SF, &ohci->regs->intrstatus);
writel (OHCI_INTR_SF, &ohci->regs->intrenable);
/* Check for a pending done list */
writel (OHCI_INTR_WDH, &ohci->regs->intrdisable);
(void) readl (&ohci->regs->intrdisable);
spin_unlock_irqrestore (&usb_ed_lock, flags);
#ifdef CONFIG_PMAC_PBOOK
if (_machine == _MACH_Pmac)
enable_irq (ohci->irq);
#endif
if (ohci->hcca->done_head)
dl_done_list (ohci, dl_reverse_done_list (ohci));
writel (OHCI_INTR_WDH, &ohci->regs->intrenable);
writel (OHCI_BLF, &ohci->regs->cmdstatus); /* start bulk list */
writel (OHCI_CLF, &ohci->regs->cmdstatus); /* start Control list */
break;
default:
warn ("odd PCI resume for usb-%s", dev->slot_name);
}
/* controller is operational, extra resumes are harmless */
atomic_dec (&ohci->resume_count);
return 0;
}
#endif /* CONFIG_PM */
/*-------------------------------------------------------------------------*/
static const struct pci_device_id __devinitdata ohci_pci_ids [] = { {
/*
* AMD-756 [Viper] USB has a serious erratum when used with
* lowspeed devices like mice.
*/
vendor: 0x1022,
device: 0x740c,
subvendor: PCI_ANY_ID,
subdevice: PCI_ANY_ID,
driver_data: OHCI_QUIRK_AMD756,
} , {
/* handle any USB OHCI controller */
class: ((PCI_CLASS_SERIAL_USB << 8) | 0x10),
class_mask: ~0,
/* no matter who makes it */
vendor: PCI_ANY_ID,
device: PCI_ANY_ID,
subvendor: PCI_ANY_ID,
subdevice: PCI_ANY_ID,
}, { /* end: all zeroes */ }
};
MODULE_DEVICE_TABLE (pci, ohci_pci_ids);
static struct pci_driver ohci_pci_driver = {
name: "usb-ohci",
id_table: &ohci_pci_ids [0],
probe: ohci_pci_probe,
remove: __devexit_p(ohci_pci_remove),
#ifdef CONFIG_PM
suspend: ohci_pci_suspend,
resume: ohci_pci_resume,
#endif /* PM */
};
/*-------------------------------------------------------------------------*/
static int __init ohci_hcd_init (void)
{
return pci_module_init (&ohci_pci_driver);
}
/*-------------------------------------------------------------------------*/
static void __exit ohci_hcd_cleanup (void)
{
pci_unregister_driver (&ohci_pci_driver);
}
module_init (ohci_hcd_init);
module_exit (ohci_hcd_cleanup);
--- NEW FILE: usb-ohci.c ---
/*
* URB OHCI HCD (Host Controller Driver) for USB.
*
* (C) Copyright 1999 Roman Weissgaerber <we...@vi...>
* (C) Copyright 2000-2001 David Brownell <dbr...@us...>
*
* [ Initialisation is based on Linus' ]
* [ uhci code and gregs ohci fragments ]
* [ (C) Copyright 1999 Linus Torvalds ]
* [ (C) Copyright 1999 Gregory P. Smith]
*
*
* History:
*
* 2001/09/19 USB_ZERO_PACKET support (Jean Tourrilhes)
* 2001/07/17 power management and pmac cleanup (Benjamin Herrenschmidt)
* 2001/03/24 td/ed hashing to remove bus_to_virt (Steve Longerbeam);
pci_map_single (db)
* 2001/03/21 td and dev/ed allocation uses new pci_pool API (db)
[...2485 lines suppressed...]
dbg ("controller being disabled");
ohci->disabled = 1;
}
/* on return, USB will always be reset (if present) */
if (ohci->disabled)
writel (ohci->hc_control = OHCI_USB_RESET,
&ohci->regs->control);
hc_release_ohci (ohci);
}
MODULE_AUTHOR( DRIVER_AUTHOR );
MODULE_DESCRIPTION( DRIVER_DESC );
MODULE_LICENSE("GPL");
/* Hack until usb-ohci-pci is made completely independent */
#if !defined(CONFIG_USB_OHCI_SA1111) && !defined(CONFIG_USB_NON_PCI_OHCI)
#include "usb-ohci-pci.c"
#endif
--- NEW FILE: usb-ohci.h ---
#ifndef _USB_OHCI_H
#define _USB_OHCI_H
/*
* URB OHCI HCD (Host Controller Driver) for USB.
*
* (C) Copyright 1999 Roman Weissgaerber <we...@vi...>
* (C) Copyright 2000-2001 David Brownell <dbr...@us...>
*
* usb-ohci.h
*/
static int cc_to_error[16] = {
/* mapping of the OHCI CC status to error codes */
/* No Error */ USB_ST_NOERROR,
/* CRC Error */ USB_ST_CRC,
/* Bit Stuff */ USB_ST_BITSTUFF,
/* Data Togg */ USB_ST_CRC,
/* Stall */ USB_ST_STALL,
/* DevNotResp */ USB_ST_NORESPONSE,
/* PIDCheck */ USB_ST_BITSTUFF,
/* UnExpPID */ USB_ST_BITSTUFF,
/* DataOver */ USB_ST_DATAOVERRUN,
/* DataUnder */ USB_ST_DATAUNDERRUN,
/* reservd */ USB_ST_NORESPONSE,
/* reservd */ USB_ST_NORESPONSE,
/* BufferOver */ USB_ST_BUFFEROVERRUN,
/* BuffUnder */ USB_ST_BUFFERUNDERRUN,
/* Not Access */ USB_ST_NORESPONSE,
/* Not Access */ USB_ST_NORESPONSE
};
#include <linux/config.h>
/* ED States */
#define ED_NEW 0x00
#define ED_UNLINK 0x01
#define ED_OPER 0x02
#define ED_DEL 0x04
#define ED_URB_DEL 0x08
/* usb_ohci_ed */
struct ed {
__u32 hwINFO;
__u32 hwTailP;
__u32 hwHeadP;
__u32 hwNextED;
struct ed * ed_prev;
__u8 int_period;
__u8 int_branch;
__u8 int_load;
__u8 int_interval;
__u8 state;
__u8 type;
__u16 last_iso;
struct ed * ed_rm_list;
dma_addr_t dma;
__u32 unused[3];
} __attribute((aligned(16)));
typedef struct ed ed_t;
/* TD info field */
#define TD_CC 0xf0000000
#define TD_CC_GET(td_p) ((td_p >>28) & 0x0f)
#define TD_CC_SET(td_p, cc) (td_p) = ((td_p) & 0x0fffffff) | (((cc) & 0x0f) << 28)
#define TD_EC 0x0C000000
#define TD_T 0x03000000
#define TD_T_DATA0 0x02000000
#define TD_T_DATA1 0x03000000
#define TD_T_TOGGLE 0x00000000
#define TD_R 0x00040000
#define TD_DI 0x00E00000
#define TD_DI_SET(X) (((X) & 0x07)<< 21)
#define TD_DP 0x00180000
#define TD_DP_SETUP 0x00000000
#define TD_DP_IN 0x00100000
#define TD_DP_OUT 0x00080000
#define TD_ISO 0x00010000
#define TD_DEL 0x00020000
/* CC Codes */
#define TD_CC_NOERROR 0x00
#define TD_CC_CRC 0x01
#define TD_CC_BITSTUFFING 0x02
#define TD_CC_DATATOGGLEM 0x03
#define TD_CC_STALL 0x04
#define TD_DEVNOTRESP 0x05
#define TD_PIDCHECKFAIL 0x06
#define TD_UNEXPECTEDPID 0x07
#define TD_DATAOVERRUN 0x08
#define TD_DATAUNDERRUN 0x09
#define TD_BUFFEROVERRUN 0x0C
#define TD_BUFFERUNDERRUN 0x0D
#define TD_NOTACCESSED 0x0F
#define MAXPSW 1
struct td {
__u32 hwINFO;
__u32 hwCBP; /* Current Buffer Pointer */
__u32 hwNextTD; /* Next TD Pointer */
__u32 hwBE; /* Memory Buffer End Pointer */
__u16 hwPSW[MAXPSW];
__u8 unused;
__u8 index;
struct ed * ed;
struct td * next_dl_td;
urb_t * urb;
dma_addr_t td_dma;
dma_addr_t data_dma;
__u32 unused2[2];
} __attribute((aligned(32))); /* normally 16, iso needs 32 */
typedef struct td td_t;
#define OHCI_ED_SKIP (1 << 14)
/*
* The HCCA (Host Controller Communications Area) is a 256 byte
* structure defined in the OHCI spec. that the host controller is
* told the base address of. It must be 256-byte aligned.
*/
#define NUM_INTS 32 /* part of the OHCI standard */
struct ohci_hcca {
__u32 int_table[NUM_INTS]; /* Interrupt ED table */
__u16 frame_no; /* current frame number */
__u16 pad1; /* set to 0 on each frame_no change */
__u32 done_head; /* info returned for an interrupt */
u8 reserved_for_hc[116];
} __attribute((aligned(256)));
/*
* Maximum number of root hub ports.
*/
#define MAX_ROOT_PORTS 15 /* maximum OHCI root hub ports */
/*
* This is the structure of the OHCI controller's memory mapped I/O
* region. This is Memory Mapped I/O. You must use the readl() and
* writel() macros defined in asm/io.h to access these!!
*/
struct ohci_regs {
/* control and status registers */
__u32 revision;
__u32 control;
__u32 cmdstatus;
__u32 intrstatus;
__u32 intrenable;
__u32 intrdisable;
/* memory pointers */
__u32 hcca;
__u32 ed_periodcurrent;
__u32 ed_controlhead;
__u32 ed_controlcurrent;
__u32 ed_bulkhead;
__u32 ed_bulkcurrent;
__u32 donehead;
/* frame counters */
__u32 fminterval;
__u32 fmremaining;
__u32 fmnumber;
__u32 periodicstart;
__u32 lsthresh;
/* Root hub ports */
struct ohci_roothub_regs {
__u32 a;
__u32 b;
__u32 status;
__u32 portstatus[MAX_ROOT_PORTS];
} roothub;
} __attribute((aligned(32)));
/* OHCI CONTROL AND STATUS REGISTER MASKS */
/*
* HcControl (control) register masks
*/
#define OHCI_CTRL_CBSR (3 << 0) /* control/bulk service ratio */
#define OHCI_CTRL_PLE (1 << 2) /* periodic list enable */
#define OHCI_CTRL_IE (1 << 3) /* isochronous enable */
#define OHCI_CTRL_CLE (1 << 4) /* control list enable */
#define OHCI_CTRL_BLE (1 << 5) /* bulk list enable */
#define OHCI_CTRL_HCFS (3 << 6) /* host controller functional state */
#define OHCI_CTRL_IR (1 << 8) /* interrupt routing */
#define OHCI_CTRL_RWC (1 << 9) /* remote wakeup connected */
#define OHCI_CTRL_RWE (1 << 10) /* remote wakeup enable */
/* pre-shifted values for HCFS */
# define OHCI_USB_RESET (0 << 6)
# define OHCI_USB_RESUME (1 << 6)
# define OHCI_USB_OPER (2 << 6)
# define OHCI_USB_SUSPEND (3 << 6)
/*
* HcCommandStatus (cmdstatus) register masks
*/
#define OHCI_HCR (1 << 0) /* host controller reset */
#define OHCI_CLF (1 << 1) /* control list filled */
#define OHCI_BLF (1 << 2) /* bulk list filled */
#define OHCI_OCR (1 << 3) /* ownership change request */
#define OHCI_SOC (3 << 16) /* scheduling overrun count */
/*
* masks used with interrupt registers:
* HcInterruptStatus (intrstatus)
* HcInterruptEnable (intrenable)
* HcInterruptDisable (intrdisable)
*/
#define OHCI_INTR_SO (1 << 0) /* scheduling overrun */
#define OHCI_INTR_WDH (1 << 1) /* writeback of done_head */
#define OHCI_INTR_SF (1 << 2) /* start frame */
#define OHCI_INTR_RD (1 << 3) /* resume detect */
#define OHCI_INTR_UE (1 << 4) /* unrecoverable error */
#define OHCI_INTR_FNO (1 << 5) /* frame number overflow */
#define OHCI_INTR_RHSC (1 << 6) /* root hub status change */
#define OHCI_INTR_OC (1 << 30) /* ownership change */
#define OHCI_INTR_MIE (1 << 31) /* master interrupt enable */
/* Virtual Root HUB */
struct virt_root_hub {
int devnum; /* Address of Root Hub endpoint */
void * urb;
void * int_addr;
int send;
int interval;
struct timer_list rh_int_timer;
};
/* USB HUB CONSTANTS (not OHCI-specific; see hub.h) */
/* destination of request */
#define RH_INTERFACE 0x01
#define RH_ENDPOINT 0x02
#define RH_OTHER 0x03
#define RH_CLASS 0x20
#define RH_VENDOR 0x40
/* Requests: bRequest << 8 | bmRequestType */
#define RH_GET_STATUS 0x0080
#define RH_CLEAR_FEATURE 0x0100
#define RH_SET_FEATURE 0x0300
#define RH_SET_ADDRESS 0x0500
#define RH_GET_DESCRIPTOR 0x0680
#define RH_SET_DESCRIPTOR 0x0700
#define RH_GET_CONFIGURATION 0x0880
#define RH_SET_CONFIGURATION 0x0900
#define RH_GET_STATE 0x0280
#define RH_GET_INTERFACE 0x0A80
#define RH_SET_INTERFACE 0x0B00
#define RH_SYNC_FRAME 0x0C80
/* Our Vendor Specific Request */
#define RH_SET_EP 0x2000
/* Hub port features */
#define RH_PORT_CONNECTION 0x00
#define RH_PORT_ENABLE 0x01
#define RH_PORT_SUSPEND 0x02
#define RH_PORT_OVER_CURRENT 0x03
#define RH_PORT_RESET 0x04
#define RH_PORT_POWER 0x08
#define RH_PORT_LOW_SPEED 0x09
#define RH_C_PORT_CONNECTION 0x10
#define RH_C_PORT_ENABLE 0x11
#define RH_C_PORT_SUSPEND 0x12
#define RH_C_PORT_OVER_CURRENT 0x13
#define RH_C_PORT_RESET 0x14
/* Hub features */
#define RH_C_HUB_LOCAL_POWER 0x00
#define RH_C_HUB_OVER_CURRENT 0x01
#define RH_DEVICE_REMOTE_WAKEUP 0x00
#define RH_ENDPOINT_STALL 0x01
#define RH_ACK 0x01
#define RH_REQ_ERR -1
#define RH_NACK 0x00
/* OHCI ROOT HUB REGISTER MASKS */
/* roothub.portstatus [i] bits */
#define RH_PS_CCS 0x00000001 /* current connect status */
#define RH_PS_PES 0x00000002 /* port enable status*/
#define RH_PS_PSS 0x00000004 /* port suspend status */
#define RH_PS_POCI 0x00000008 /* port over current indicator */
#define RH_PS_PRS 0x00000010 /* port reset status */
#define RH_PS_PPS 0x00000100 /* port power status */
#define RH_PS_LSDA 0x00000200 /* low speed device attached */
#define RH_PS_CSC 0x00010000 /* connect status change */
#define RH_PS_PESC 0x00020000 /* port enable status change */
#define RH_PS_PSSC 0x00040000 /* port suspend status change */
#define RH_PS_OCIC 0x00080000 /* over current indicator change */
#define RH_PS_PRSC 0x00100000 /* port reset status change */
/* roothub.status bits */
#define RH_HS_LPS 0x00000001 /* local power status */
#define RH_HS_OCI 0x00000002 /* over current indicator */
#define RH_HS_DRWE 0x00008000 /* device remote wakeup enable */
#define RH_HS_LPSC 0x00010000 /* local power status change */
#define RH_HS_OCIC 0x00020000 /* over current indicator change */
#define RH_HS_CRWE 0x80000000 /* clear remote wakeup enable */
/* roothub.b masks */
#define RH_B_DR 0x0000ffff /* device removable flags */
#define RH_B_PPCM 0xffff0000 /* port power control mask */
/* roothub.a masks */
#define RH_A_NDP (0xff << 0) /* number of downstream ports */
#define RH_A_PSM (1 << 8) /* power switching mode */
#define RH_A_NPS (1 << 9) /* no power switching */
#define RH_A_DT (1 << 10) /* device type (mbz) */
#define RH_A_OCPM (1 << 11) /* over current protection mode */
#define RH_A_NOCP (1 << 12) /* no over current protection */
#define RH_A_POTPGT (0xff << 24) /* power on to power good time */
/* urb */
typedef struct
{
ed_t * ed;
__u16 length; // number of tds associated with this request
__u16 td_cnt; // number of tds already serviced
int state;
wait_queue_head_t * wait;
td_t * td[0]; // list pointer to all corresponding TDs associated with this request
} urb_priv_t;
#define URB_DEL 1
/* Hash struct used for TD/ED hashing */
struct hash_t {
void *virt;
dma_addr_t dma;
struct hash_t *next; // chaining for collision cases
};
/* List of TD/ED hash entries */
struct hash_list_t {
struct hash_t *head;
struct hash_t *tail;
};
#define TD_HASH_SIZE 64 /* power'o'two */
#define ED_HASH_SIZE 64 /* power'o'two */
#define TD_HASH_FUNC(td_dma) ((td_dma ^ (td_dma >> 5)) % TD_HASH_SIZE)
#define ED_HASH_FUNC(ed_dma) ((ed_dma ^ (ed_dma >> 5)) % ED_HASH_SIZE)
/*
* This is the full ohci controller description
*
* Note how the "proper" USB information is just
* a subset of what the full implementation needs. (Linus)
*/
typedef struct ohci {
struct ohci_hcca *hcca; /* hcca */
dma_addr_t hcca_dma;
int irq;
int disabled; /* e.g. got a UE, we're hung */
int sleeping;
atomic_t resume_count; /* defending against multiple resumes */
unsigned long flags; /* for HC bugs */
#define OHCI_QUIRK_AMD756 0x01 /* erratum #4 */
struct ohci_regs * regs; /* OHCI controller's memory */
struct list_head ohci_hcd_list; /* list of all ohci_hcd */
struct ohci * next; // chain of ohci device contexts
struct list_head timeout_list;
// struct list_head urb_list; // list of all pending urbs
// spinlock_t urb_list_lock; // lock to keep consistency
int ohci_int_load[32]; /* load of the 32 Interrupt Chains (for load balancing)*/
ed_t * ed_rm_list[2]; /* lists of all endpoints to be removed */
ed_t * ed_bulktail; /* last endpoint of bulk list */
ed_t * ed_controltail; /* last endpoint of control list */
ed_t * ed_isotail; /* last endpoint of iso list */
int intrstatus;
__u32 hc_control; /* copy of the hc control reg */
struct usb_bus * bus;
struct usb_device * dev[128];
struct virt_root_hub rh;
/* PCI device handle, settings, ... */
struct pci_dev *ohci_dev;
const char *slot_name;
u8 pci_latency;
struct pci_pool *td_cache;
struct pci_pool *dev_cache;
struct hash_list_t td_hash[TD_HASH_SIZE];
struct hash_list_t ed_hash[ED_HASH_SIZE];
} ohci_t;
#define NUM_EDS 32 /* num of preallocated endpoint descriptors */
struct ohci_device {
ed_t ed[NUM_EDS];
dma_addr_t dma;
int ed_cnt;
wait_queue_head_t * wait;
};
// #define ohci_to_usb(ohci) ((ohci)->usb)
#define usb_to_ohci(usb) ((struct ohci_device *)(usb)->hcpriv)
/* hcd */
/* endpoint */
static int ep_link(ohci_t * ohci, ed_t * ed);
static int ep_unlink(ohci_t * ohci, ed_t * ed);
static ed_t * ep_add_ed(struct usb_device * usb_dev, unsigned int pipe, int interval, int load, int mem_flags);
static void ep_rm_ed(struct usb_device * usb_dev, ed_t * ed);
/* td */
static void td_fill(ohci_t * ohci, unsigned int info, dma_addr_t data, int len, urb_t * urb, int index);
static void td_submit_urb(urb_t * urb);
/* root hub */
static int rh_submit_urb(urb_t * urb);
static int rh_unlink_urb(urb_t * urb);
static int rh_init_int_timer(urb_t * urb);
/*-------------------------------------------------------------------------*/
#define ALLOC_FLAGS (in_interrupt () ? GFP_ATOMIC : GFP_KERNEL)
#ifdef DEBUG
# define OHCI_MEM_FLAGS SLAB_POISON
#else
# define OHCI_MEM_FLAGS 0
#endif
#ifndef CONFIG_PCI
//# error "usb-ohci currently requires PCI-based controllers"
/* to support non-PCI OHCIs, you need custom bus/mem/... glue */
#endif
/* Recover a TD/ED using its collision chain */
static inline void *
dma_to_ed_td (struct hash_list_t * entry, dma_addr_t dma)
{
struct hash_t * scan = entry->head;
while (scan && scan->dma != dma)
scan = scan->next;
if (!scan)
BUG();
return scan->virt;
}
static inline struct ed *
dma_to_ed (struct ohci * hc, dma_addr_t ed_dma)
{
return (struct ed *) dma_to_ed_td(&(hc->ed_hash[ED_HASH_FUNC(ed_dma)]),
ed_dma);
}
static inline struct td *
dma_to_td (struct ohci * hc, dma_addr_t td_dma)
{
return (struct td *) dma_to_ed_td(&(hc->td_hash[TD_HASH_FUNC(td_dma)]),
td_dma);
}
/* Add a hash entry for a TD/ED; return true on success */
static inline int
hash_add_ed_td(struct hash_list_t * entry, void * virt, dma_addr_t dma)
{
struct hash_t * scan;
scan = (struct hash_t *)kmalloc(sizeof(struct hash_t), ALLOC_FLAGS);
if (!scan)
return 0;
if (!entry->tail) {
entry->head = entry->tail = scan;
} else {
entry->tail->next = scan;
entry->tail = scan;
}
scan->virt = virt;
scan->dma = dma;
scan->next = NULL;
return 1;
}
static inline int
hash_add_ed (struct ohci * hc, struct ed * ed)
{
return hash_add_ed_td (&(hc->ed_hash[ED_HASH_FUNC(ed->dma)]),
ed, ed->dma);
}
static inline int
hash_add_td (struct ohci * hc, struct td * td)
{
return hash_add_ed_td (&(hc->td_hash[TD_HASH_FUNC(td->td_dma)]),
td, td->td_dma);
}
static inline void
hash_free_ed_td (struct hash_list_t * entry, void * virt)
{
struct hash_t *scan, *prev;
scan = prev = entry->head;
// Find and unlink hash entry
while (scan && scan->virt != virt) {
prev = scan;
scan = scan->next;
}
if (scan) {
if (scan == entry->head) {
if (entry->head == entry->tail)
entry->head = entry->tail = NULL;
else
entry->head = scan->next;
} else if (scan == entry->tail) {
entry->tail = prev;
prev->next = NULL;
} else
prev->next = scan->next;
kfree(scan);
}
}
static inline void
hash_free_ed (struct ohci * hc, struct ed * ed)
{
hash_free_ed_td (&(hc->ed_hash[ED_HASH_FUNC(ed->dma)]), ed);
}
static inline void
hash_free_td (struct ohci * hc, struct td * td)
{
hash_free_ed_td (&(hc->td_hash[TD_HASH_FUNC(td->td_dma)]), td);
}
static int ohci_mem_init (struct ohci *ohci)
{
ohci->td_cache = pci_pool_create ("ohci_td", ohci->ohci_dev,
sizeof (struct td),
32 /* byte alignment */,
0 /* no page-crossing issues */,
GFP_KERNEL | OHCI_MEM_FLAGS);
if (!ohci->td_cache)
return -ENOMEM;
ohci->dev_cache = pci_pool_create ("ohci_dev", ohci->ohci_dev,
sizeof (struct ohci_device),
16 /* byte alignment */,
0 /* no page-crossing issues */,
GFP_KERNEL | OHCI_MEM_FLAGS);
if (!ohci->dev_cache)
return -ENOMEM;
return 0;
}
static void ohci_mem_cleanup (struct ohci *ohci)
{
if (ohci->td_cache) {
pci_pool_destroy (ohci->td_cache);
ohci->td_cache = 0;
}
if (ohci->dev_cache) {
pci_pool_destroy (ohci->dev_cache);
ohci->dev_cache = 0;
}
}
/* TDs ... */
static inline struct td *
td_alloc (struct ohci *hc, int mem_flags)
{
dma_addr_t dma;
struct td *td;
td = pci_pool_alloc (hc->td_cache, mem_flags, &dma);
if (td) {
td->td_dma = dma;
/* hash it for later reverse mapping */
if (!hash_add_td (hc, td)) {
pci_pool_free (hc->td_cache, td, dma);
return NULL;
}
}
return td;
}
static inline void
td_free (struct ohci *hc, struct td *td)
{
hash_free_td (hc, td);
pci_pool_free (hc->td_cache, td, td->td_dma);
}
/* DEV + EDs ... only the EDs need to be consistent */
static inline struct ohci_device *
dev_alloc (struct ohci *hc, int mem_flags)
{
dma_addr_t dma;
struct ohci_device *dev;
int i, offset;
dev = pci_pool_alloc (hc->dev_cache, mem_flags, &dma);
if (dev) {
memset (dev, 0, sizeof (*dev));
dev->dma = dma;
offset = ((char *)&dev->ed) - ((char *)dev);
for (i = 0; i < NUM_EDS; i++, offset += sizeof dev->ed [0])
dev->ed [i].dma = dma + offset;
/* add to hashtable if used */
}
return dev;
}
static inline void
dev_free (struct ohci *hc, struct ohci_device *dev)
{
pci_pool_free (hc->dev_cache, dev, dev->dma);
}
#endif
|