# This is a BitKeeper generated patch for the following project: # Project Name: Linux kernel tree # This patch format is intended for GNU patch command version 2.5 or higher. # This patch includes the following deltas: # ChangeSet 1.592 -> 1.592.2.1 # drivers/usb/host/usb-ohci.h 1.13 -> 1.14 # drivers/usb/host/Config.in 1.11 -> 1.12 # drivers/usb/host/usb-ohci.c 1.34 -> 1.35 # drivers/usb/Config.in 1.30 -> 1.31 # drivers/usb/Makefile 1.31 -> 1.32 # drivers/usb/host/Makefile 1.10 -> 1.11 # (new) -> 1.1 drivers/usb/host/usb-ohci-sa1111.c # (new) -> 1.1 drivers/usb/host/usb-ohci-pci.c # # The following is the BitKeeper ChangeSet Log # -------------------------------------------- # 02/05/29 greg@kroah.com 1.592.2.1 # USB OHCI driver: Added SA1111 support # # Added the patch from Russell King that splits # the usb-ohci driver into two pieces, enabling the sa1111 hardware to # work with the driver. # # I also added some changes to get the usb-ohci-pci.o module to build # and run properly. # -------------------------------------------- # diff -Nru a/drivers/usb/Config.in b/drivers/usb/Config.in --- a/drivers/usb/Config.in Fri May 31 00:41:19 2002 +++ b/drivers/usb/Config.in Fri May 31 00:41:19 2002 @@ -4,7 +4,13 @@ mainmenu_option next_comment comment 'USB support' -dep_tristate 'Support for USB' CONFIG_USB $CONFIG_PCI +# ARM SA1111 chips have a non-PCI based "OHCI-compatible" USB host interface. +if [ "$CONFIG_PCI" = "y" -o "$CONFIG_SA1111" = "y" ]; then + tristate 'Support for USB' CONFIG_USB +else + define_bool CONFIG_USB n +fi + if [ "$CONFIG_USB" = "y" -o "$CONFIG_USB" = "m" ]; then source drivers/usb/core/Config.in diff -Nru a/drivers/usb/Makefile b/drivers/usb/Makefile --- a/drivers/usb/Makefile Fri May 31 00:41:19 2002 +++ b/drivers/usb/Makefile Fri May 31 00:41:19 2002 @@ -6,9 +6,11 @@ mod-subdirs := serial obj-$(CONFIG_USB) += core/ + obj-$(CONFIG_USB_EHCI_HCD) += host/ obj-$(CONFIG_USB_OHCI_HCD) += host/ obj-$(CONFIG_USB_OHCI) += host/ +obj-$(CONFIG_USB_OHCI_SA1111) += host/ obj-$(CONFIG_USB_SL811HS) += host/ obj-$(CONFIG_USB_UHCI_ALT) += host/ obj-$(CONFIG_USB_UHCI_HCD_ALT) += host/ diff -Nru a/drivers/usb/host/Config.in b/drivers/usb/host/Config.in --- a/drivers/usb/host/Config.in Fri May 31 00:41:19 2002 +++ b/drivers/usb/host/Config.in Fri May 31 00:41:19 2002 @@ -20,5 +20,6 @@ #fi #dep_tristate ' OHCI (Compaq, iMacs, OPTi, SiS, ALi, ...) support' CONFIG_USB_OHCI $CONFIG_USB if [ "$CONFIG_ARM" = "y" ]; then + dep_tristate ' SA1111 OHCI-compatible host interface support' CONFIG_USB_OHCI_SA1111 $CONFIG_USB dep_tristate ' SL811HS support' CONFIG_USB_SL811HS $CONFIG_USB fi diff -Nru a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile --- a/drivers/usb/host/Makefile Fri May 31 00:41:19 2002 +++ b/drivers/usb/host/Makefile Fri May 31 00:41:19 2002 @@ -3,6 +3,8 @@ # framework and drivers # +export-objs := usb-ohci.o + obj-$(CONFIG_USB_EHCI_HCD) += ehci-hcd.o obj-$(CONFIG_USB_OHCI_HCD) += ohci-hcd.o obj-$(CONFIG_USB_UHCI_HCD) += usb-uhci-hcd.o @@ -10,7 +12,8 @@ obj-$(CONFIG_USB_UHCI) += usb-uhci.o obj-$(CONFIG_USB_UHCI_ALT) += uhci.o -obj-$(CONFIG_USB_OHCI) += usb-ohci.o +obj-$(CONFIG_USB_OHCI) += usb-ohci.o usb-ohci-pci.o +obj-$(CONFIG_USB_OHCI_SA1111) += usb-ohci.o usb-ohci-sa1111.o obj-$(CONFIG_USB_SL811HS) += hc_sl811.o include $(TOPDIR)/Rules.make diff -Nru a/drivers/usb/host/usb-ohci-pci.c b/drivers/usb/host/usb-ohci-pci.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/usb/host/usb-ohci-pci.c Fri May 31 00:41:19 2002 @@ -0,0 +1,445 @@ +#include +#include +#include +#include +#include +#include +#include +#include /* for in_interrupt() */ +#undef DEBUG +#include + +#include "../core/hcd.h" +#include "usb-ohci.h" + +#ifdef CONFIG_PMAC_PBOOK +#include +#include +#include +#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) +{ + u8 latency, limit; + ohci_t * ohci; + int ret; + + printk(KERN_INFO __FILE__ ": usb-%s, %s\n", dev->slot_name, dev->name); + + /* 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); + latency = limit; + } + } + + ret = hc_add_ohci(dev, irq, mem_base, id->driver_data, + &ohci, ohci_pci_driver.name, dev->slot_name); + + if (ret == 0) { + ohci->pci_latency = latency; + + if (hc_start (ohci, &ohci->ohci_dev->dev) < 0) { + err ("can't start usb-%s", ohci->slot_name); + hc_remove_ohci(ohci); + return -EBUSY; + } + +#ifdef DEBUG + ohci_dump (ohci, 1); +#endif + } + + return ret; +} + +/*-------------------------------------------------------------------------*/ + +#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; + int status; + + if (pci_enable_device(dev) < 0) + return -ENODEV; + + if (!dev->irq) { + err("found OHCI device with no IRQ assigned. check BIOS settings!"); + pci_disable_device (dev); + 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"); + pci_disable_device (dev); + return -EBUSY; + } + + mem_base = ioremap_nocache (mem_resource, mem_len); + if (!mem_base) { + err("Error mapping OHCI memory"); + release_mem_region(mem_resource, mem_len); + pci_disable_device (dev); + return -EFAULT; + } + + /* controller writes into our memory */ + pci_set_master (dev); + + status = hc_found_ohci (dev, dev->irq, mem_base, id); + if (status < 0) { + iounmap (mem_base); + release_mem_region(mem_resource, mem_len); + pci_disable_device (dev); + } + return status; +} + +/*-------------------------------------------------------------------------*/ + +/* 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 && _machine == _MACH_Pmac) + 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 && _machine == _MACH_Pmac) + 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); + +MODULE_LICENSE("GPL"); diff -Nru a/drivers/usb/host/usb-ohci-sa1111.c b/drivers/usb/host/usb-ohci-sa1111.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/usb/host/usb-ohci-sa1111.c Fri May 31 00:41:19 2002 @@ -0,0 +1,130 @@ +/* + * linux/drivers/usb/usb-ohci-sa1111.c + * + * The outline of this code was taken from Brad Parkers + * original OHCI driver modifications, and reworked into a cleaner form + * by Russell King . + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#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 *sa1111_ohci; + +static void __init sa1111_ohci_configure(void) +{ + unsigned int usb_rst = 0; + + if (machine_is_xp860() || + machine_has_neponset() || + machine_is_pfs168() || + machine_is_badge4()) + usb_rst = USB_RESET_PWRSENSELOW | USB_RESET_PWRCTRLLOW; + + /* + * Configure the power sense and control lines. Place the USB + * host controller in reset. + */ + USB_RESET = usb_rst | USB_RESET_FORCEIFRESET | USB_RESET_FORCEHCRESET; + + /* + * Now, carefully enable the USB clock, and take + * the USB host controller out of reset. + */ + SKPCR |= SKPCR_UCLKEN; + udelay(11); + USB_RESET = usb_rst; +} + +static int __init sa1111_ohci_init(void) +{ + int ret; + + if (!sa1111) + return -ENODEV; + + /* + * Request memory resources. + */ +// if (!request_mem_region(_USB_OHCI_OP_BASE, _USB_EXTENT, "usb-ohci")) +// return -EBUSY; + + sa1111_ohci_configure(); + + /* + * Initialise the generic OHCI driver. + */ + ret = hc_add_ohci((struct pci_dev *)1, NIRQHCIM, + (void *)&USB_OHCI_OP_BASE, 0, &sa1111_ohci, + "usb-ohci", "sa1111"); + + if (ret == 0) { + if (hc_start (sa1111_ohci, &sa1111->dev) < 0) { + err ("can't start usb-%s", sa1111_ohci->slot_name); + hc_remove_ohci (sa1111_ohci); + return -EBUSY; + } + +#ifdef DEBUG + ohci_dump (ohci, 1); +#endif +#ifdef CONFIG_SA1100_BADGE4 + if (machine_is_badge4()) { + /* found the controller, so now power the bus */ + badge4_set_5V(BADGE4_5V_USB, 1); + } +#endif + } +// else +// release_mem_region(_USB_OHCI_OP_BASE, _USB_EXTENT); + + return ret; +} + +static void __exit sa1111_ohci_exit(void) +{ + hc_remove_ohci(sa1111_ohci); + + /* + * Put the USB host controller into reset. + */ + USB_RESET |= USB_RESET_FORCEIFRESET | USB_RESET_FORCEHCRESET; + + /* + * Stop the USB clock. + */ + SKPCR &= ~SKPCR_UCLKEN; + + /* + * Release memory resources. + */ +// release_mem_region(_USB_OHCI_OP_BASE, _USB_EXTENT); + +#ifdef CONFIG_SA1100_BADGE4 + if (machine_is_badge4()) { + badge4_set_5V(BADGE4_5V_USB, 0); + } +#endif +} + +module_init(sa1111_ohci_init); +module_exit(sa1111_ohci_exit); diff -Nru a/drivers/usb/host/usb-ohci.c b/drivers/usb/host/usb-ohci.c --- a/drivers/usb/host/usb-ohci.c Fri May 31 00:41:19 2002 +++ b/drivers/usb/host/usb-ohci.c Fri May 31 00:41:19 2002 @@ -82,16 +82,6 @@ #include "usb-ohci.h" -#ifdef CONFIG_PMAC_PBOOK -#include -#include -#include -#ifndef CONFIG_PM -#define CONFIG_PM -#endif -#endif - - /* * Version Information */ @@ -99,14 +89,10 @@ #define DRIVER_AUTHOR "Roman Weissgaerber , David Brownell" #define DRIVER_DESC "USB OHCI Host Controller Driver" -/* For initializing controller (mask in an HCFS mode too) */ -#define OHCI_CONTROL_INIT \ - (OHCI_CTRL_CBSR & 0x3) | OHCI_CTRL_IE | OHCI_CTRL_PLE - #define OHCI_UNLINK_TIMEOUT (HZ / 10) static LIST_HEAD (ohci_hcd_list); -static spinlock_t usb_ed_lock = SPIN_LOCK_UNLOCKED; +spinlock_t usb_ed_lock = SPIN_LOCK_UNLOCKED; /*-------------------------------------------------------------------------*/ @@ -443,7 +429,7 @@ static void ohci_dump (ohci_t *controller, int verbose) { - dbg ("OHCI controller usb-%s state", controller->ohci_dev->slot_name); + dbg ("OHCI controller usb-%s state", controller->slot_name); // dumps some of the state we know about ohci_dump_status (controller); @@ -465,7 +451,7 @@ static int sohci_return_urb (struct ohci *hc, struct urb * urb) { urb_priv_t * urb_priv = urb->hcpriv; - struct urb * urbt; + struct urb * urbt = NULL; unsigned long flags; int i; @@ -499,7 +485,7 @@ break; case PIPE_ISOCHRONOUS: - for (urbt = urb->next; urbt && (urbt != urb); urbt = urbt->next); + // for (urbt = urb->next; urbt && (urbt != urb); urbt = urbt->next); if (urbt) { /* send the reply and requeue URB */ pci_unmap_single (hc->ohci_dev, urb_priv->td [0]->data_dma, @@ -865,7 +851,7 @@ if (ed->state == ED_OPER) { /* driver on that interface didn't unlink an urb */ dbg ("driver usb-%s dev %d ed 0x%x unfreed URB", - ohci->ohci_dev->slot_name, usb_dev->devnum, i); + ohci->slot_name, usb_dev->devnum, i); ep_unlink (ohci, ed); } ep_rm_ed (usb_dev, ed); @@ -910,7 +896,7 @@ } else { /* likely some interface's driver has a refcount bug */ err ("bus %s devnum %d deletion in interrupt", - ohci->ohci_dev->slot_name, usb_dev->devnum); + ohci->slot_name, usb_dev->devnum); BUG (); } } @@ -1529,7 +1515,7 @@ /* replies to the request have to be on a FIFO basis so * we reverse the reversed done-list */ -static td_t * dl_reverse_done_list (ohci_t * ohci) +td_t * dl_reverse_done_list (ohci_t * ohci) { __u32 td_list_hc; td_t * td_rev = NULL; @@ -1677,7 +1663,7 @@ /* td done list */ -static void dl_done_list (ohci_t * ohci, td_t * td_list) +void dl_done_list (ohci_t * ohci, td_t * td_list) { td_t * td_list_next = NULL; ed_t * ed; @@ -1822,7 +1808,7 @@ num_ports = roothub_a (ohci) & RH_A_NDP; if (num_ports > MAX_ROOT_PORTS) { err ("bogus NDP=%d for OHCI usb-%s", num_ports, - ohci->ohci_dev->slot_name); + ohci->slot_name); err ("rereads as NDP=%d", readl (&ohci->regs->roothub.a) & RH_A_NDP); /* retry later; "should not happen" */ @@ -2148,7 +2134,7 @@ /* reset the HC and BUS */ -static int hc_reset (ohci_t * ohci) +int hc_reset (ohci_t * ohci) { int timeout = 30; int smm_timeout = 50; /* 0,5 sec */ @@ -2169,7 +2155,7 @@ writel (OHCI_INTR_MIE, &ohci->regs->intrdisable); dbg("USB HC reset_hc usb-%s: ctrl = 0x%x ;", - ohci->ohci_dev->slot_name, + ohci->slot_name, readl (&ohci->regs->control)); /* Reset USB (needed by some controllers) */ @@ -2193,7 +2179,7 @@ * enable interrupts * connect the virtual root hub */ -static int hc_start (ohci_t * ohci) +int hc_start (ohci_t * ohci, struct device *parent_dev) { __u32 mask; unsigned int fminterval; @@ -2247,7 +2233,7 @@ dev = usb_to_ohci (usb_dev); ohci->bus->root_hub = usb_dev; usb_connect (usb_dev); - if (usb_register_root_hub (usb_dev, &ohci->ohci_dev->dev) != 0) { + if (usb_register_root_hub (usb_dev, parent_dev) != 0) { usb_free_dev (usb_dev); ohci->disabled = 1; return -ENODEV; @@ -2308,7 +2294,7 @@ if (ints & OHCI_INTR_UE) { ohci->disabled++; err ("OHCI Unrecoverable Error, controller usb-%s disabled", - ohci->ohci_dev->slot_name); + ohci->slot_name); // e.g. due to PCI Master/Target Abort #ifdef DEBUG @@ -2385,7 +2371,9 @@ ohci->regs = mem_base; ohci->ohci_dev = dev; +#ifdef CONFIG_PCI pci_set_drvdata(dev, ohci); +#endif INIT_LIST_HEAD (&ohci->ohci_hcd_list); list_add (&ohci->ohci_hcd_list, &ohci_hcd_list); @@ -2394,7 +2382,9 @@ ohci->bus = usb_alloc_bus (&sohci_device_operations); if (!ohci->bus) { +#ifdef CONFIG_PCI pci_set_drvdata (dev, NULL); +#endif pci_free_consistent (ohci->ohci_dev, sizeof *ohci->hcca, ohci->hcca, ohci->hcca_dma); kfree (ohci); @@ -2413,7 +2403,7 @@ static void hc_release_ohci (ohci_t * ohci) { - dbg ("USB HC release ohci usb-%s", ohci->ohci_dev->slot_name); + dbg ("USB HC release ohci usb-%s", ohci->slot_name); /* disconnect all devices */ if (ohci->bus->root_hub) @@ -2426,7 +2416,9 @@ free_irq (ohci->irq, ohci); ohci->irq = -1; } +#ifdef CONFIG_PCI pci_set_drvdata(ohci->ohci_dev, NULL); +#endif if (ohci->bus) { if (ohci->bus->busnum) usb_deregister_bus (ohci->bus); @@ -2448,18 +2440,15 @@ /*-------------------------------------------------------------------------*/ -/* 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) +/* + * Host bus independent add one OHCI host controller. + */ +int +hc_add_ohci(struct pci_dev *dev, int irq, void *mem_base, unsigned long flags, + ohci_t **ohcip, const char *name, const char *slot_name) { - ohci_t * ohci; - u8 latency, limit; char buf[8], *bufp = buf; + ohci_t * ohci; int ret; #ifndef __sparc__ @@ -2469,34 +2458,20 @@ #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; } + ohci->slot_name = slot_name; if ((ret = ohci_mem_init (ohci)) < 0) { hc_release_ohci (ohci); return ret; } - ohci->flags = id->driver_data; + ohci->flags = flags; 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; @@ -2508,134 +2483,23 @@ usb_register_bus (ohci->bus); - if (request_irq (irq, hc_interrupt, SA_SHIRQ, - ohci_pci_driver.name, ohci) != 0) { + if (request_irq (irq, hc_interrupt, SA_SHIRQ, 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; - } + *ohcip = ohci; -#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) +/* + * Host bus independent remove one OHCI host controller. + */ +void hc_remove_ohci(ohci_t *ohci) { - unsigned long mem_resource, mem_len; - void *mem_base; - int status; - - if (pci_enable_device(dev) < 0) - return -ENODEV; - - if (!dev->irq) { - err("found OHCI device with no IRQ assigned. check BIOS settings!"); - pci_disable_device (dev); - 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"); - pci_disable_device (dev); - return -EBUSY; - } - - mem_base = ioremap_nocache (mem_resource, mem_len); - if (!mem_base) { - err("Error mapping OHCI memory"); - release_mem_region (mem_resource, mem_len); - pci_disable_device (dev); - return -EFAULT; - } - - /* controller writes into our memory */ - pci_set_master (dev); - - status = hc_found_ohci (dev, dev->irq, mem_base, id); - if (status < 0) { - iounmap (mem_base); - release_mem_region (mem_resource, mem_len); - pci_disable_device (dev); - } - return status; -} - -/*-------------------------------------------------------------------------*/ - -/* 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 = 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" : "" - ); #ifdef DEBUG ohci_dump (ohci, 1); #endif @@ -2652,270 +2516,16 @@ &ohci->regs->control); hc_release_ohci (ohci); - - release_mem_region (pci_resource_start (dev, 0), pci_resource_len (dev, 0)); - pci_disable_device (dev); -} - - -#ifdef CONFIG_PM - -/*-------------------------------------------------------------------------*/ - -static int -ohci_pci_suspend (struct pci_dev *dev, u32 state) -{ - ohci_t *ohci = 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 = 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); - - MODULE_AUTHOR( DRIVER_AUTHOR ); MODULE_DESCRIPTION( DRIVER_DESC ); MODULE_LICENSE("GPL"); + +EXPORT_SYMBOL(hc_add_ohci); +EXPORT_SYMBOL(hc_remove_ohci); +EXPORT_SYMBOL(hc_start); +EXPORT_SYMBOL(hc_reset); +EXPORT_SYMBOL(dl_done_list); +EXPORT_SYMBOL(dl_reverse_done_list); +EXPORT_SYMBOL(usb_ed_lock); diff -Nru a/drivers/usb/host/usb-ohci.h b/drivers/usb/host/usb-ohci.h --- a/drivers/usb/host/usb-ohci.h Fri May 31 00:41:19 2002 +++ b/drivers/usb/host/usb-ohci.h Fri May 31 00:41:19 2002 @@ -403,6 +403,7 @@ /* 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; @@ -423,6 +424,10 @@ // #define ohci_to_usb(ohci) ((ohci)->usb) #define usb_to_ohci(usb) ((struct ohci_device *)(usb)->hcpriv) +/* For initializing controller (mask in an HCFS mode too) */ +#define OHCI_CONTROL_INIT \ + (OHCI_CTRL_CBSR & 0x3) | OHCI_CTRL_IE | OHCI_CTRL_PLE + /* hcd */ /* endpoint */ static int ep_link(ohci_t * ohci, ed_t * ed); @@ -447,11 +452,6 @@ # 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 void * @@ -640,4 +640,9 @@ { pci_pool_free (hc->dev_cache, dev, dev->dma); } + +extern spinlock_t usb_ed_lock; +extern void dl_done_list (ohci_t * ohci, td_t * td_list); +extern td_t * dl_reverse_done_list (ohci_t * ohci); +