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 |