ChangeSet 1.1722.97.55, 2004/06/08 16:09:55-07:00, david-b@pacbell.net [PATCH] lh7a404 USB host against 2.6.7-rc2 Support for the Sharp LH7A404 OHCI, another non-PCI implementation. This uses a platform_device and a workaround for a register read problem. Signed-off-by: Marc Singer Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman drivers/usb/Kconfig | 2 drivers/usb/host/ohci-dbg.c | 30 +-- drivers/usb/host/ohci-hcd.c | 42 ++-- drivers/usb/host/ohci-hub.c | 42 ++-- drivers/usb/host/ohci-q.c | 6 drivers/usb/host/ohci.h | 19 ++ usb/host/ohci-lh7a404.c | 387 ++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 472 insertions(+), 56 deletions(-) diff -Nru a/drivers/usb/Kconfig b/drivers/usb/Kconfig --- a/drivers/usb/Kconfig Fri Jun 18 10:58:18 2004 +++ b/drivers/usb/Kconfig Fri Jun 18 10:58:18 2004 @@ -7,7 +7,7 @@ # ARM SA1111 chips have a non-PCI based "OHCI-compatible" USB host interface. config USB tristate "Support for Host-side USB" - depends on PCI || SA1111 || ARCH_OMAP1510 || ARCH_OMAP1610 + depends on PCI || SA1111 || ARCH_OMAP1510 || ARCH_OMAP1610 || ARCH_LH7A404 ---help--- Universal Serial Bus (USB) is a specification for a serial bus subsystem which offers higher speeds and more features than the diff -Nru a/drivers/usb/host/ohci-dbg.c b/drivers/usb/host/ohci-dbg.c --- a/drivers/usb/host/ohci-dbg.c Fri Jun 18 10:58:18 2004 +++ b/drivers/usb/host/ohci-dbg.c Fri Jun 18 10:58:18 2004 @@ -134,13 +134,13 @@ struct ohci_regs *regs = controller->regs; u32 temp; - temp = readl (®s->revision) & 0xff; + temp = ohci_readl (®s->revision) & 0xff; ohci_dbg_sw (controller, next, size, "OHCI %d.%d, %s legacy support registers\n", 0x03 & (temp >> 4), (temp & 0x0f), (temp & 0x10) ? "with" : "NO"); - temp = readl (®s->control); + temp = ohci_readl (®s->control); ohci_dbg_sw (controller, next, size, "control 0x%03x%s%s%s HCFS=%s%s%s%s%s CBSR=%d\n", temp, @@ -155,7 +155,7 @@ temp & OHCI_CTRL_CBSR ); - temp = readl (®s->cmdstatus); + temp = ohci_readl (®s->cmdstatus); ohci_dbg_sw (controller, next, size, "cmdstatus 0x%05x SOC=%d%s%s%s%s\n", temp, (temp & OHCI_SOC) >> 16, @@ -166,26 +166,26 @@ ); ohci_dump_intr_mask (controller, "intrstatus", - readl (®s->intrstatus), next, size); + ohci_readl (®s->intrstatus), next, size); ohci_dump_intr_mask (controller, "intrenable", - readl (®s->intrenable), next, size); + ohci_readl (®s->intrenable), next, size); // intrdisable always same as intrenable maybe_print_eds (controller, "ed_periodcurrent", - readl (®s->ed_periodcurrent), next, size); + ohci_readl (®s->ed_periodcurrent), next, size); maybe_print_eds (controller, "ed_controlhead", - readl (®s->ed_controlhead), next, size); + ohci_readl (®s->ed_controlhead), next, size); maybe_print_eds (controller, "ed_controlcurrent", - readl (®s->ed_controlcurrent), next, size); + ohci_readl (®s->ed_controlcurrent), next, size); maybe_print_eds (controller, "ed_bulkhead", - readl (®s->ed_bulkhead), next, size); + ohci_readl (®s->ed_bulkhead), next, size); maybe_print_eds (controller, "ed_bulkcurrent", - readl (®s->ed_bulkcurrent), next, size); + ohci_readl (®s->ed_bulkcurrent), next, size); maybe_print_eds (controller, "donehead", - readl (®s->donehead), next, size); + ohci_readl (®s->donehead), next, size); } #define dbg_port_sw(hc,num,value,next,size) \ @@ -637,7 +637,7 @@ "hcca frame 0x%04x\n", OHCI_FRAME_NO(ohci->hcca)); /* other registers mostly affect frame timings */ - rdata = readl (®s->fminterval); + rdata = ohci_readl (®s->fminterval); temp = scnprintf (next, size, "fmintvl 0x%08x %sFSMPS=0x%04x FI=0x%04x\n", rdata, (rdata >> 31) ? " FIT" : "", @@ -645,20 +645,20 @@ size -= temp; next += temp; - rdata = readl (®s->fmremaining); + rdata = ohci_readl (®s->fmremaining); temp = scnprintf (next, size, "fmremaining 0x%08x %sFR=0x%04x\n", rdata, (rdata >> 31) ? " FRT" : "", rdata & 0x3fff); size -= temp; next += temp; - rdata = readl (®s->periodicstart); + rdata = ohci_readl (®s->periodicstart); temp = scnprintf (next, size, "periodicstart 0x%04x\n", rdata & 0x3fff); size -= temp; next += temp; - rdata = readl (®s->lsthresh); + rdata = ohci_readl (®s->lsthresh); temp = scnprintf (next, size, "lsthresh 0x%04x\n", rdata & 0x3fff); size -= temp; diff -Nru a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c --- a/drivers/usb/host/ohci-hcd.c Fri Jun 18 10:58:18 2004 +++ b/drivers/usb/host/ohci-hcd.c Fri Jun 18 10:58:18 2004 @@ -17,6 +17,7 @@ * * History: * + * 2004/03/24 LH7A404 support (Durgesh Pattamatta & Marc Singer) * 2004/02/04 use generic dma_* functions instead of pci_* (dsaxena@plexity.net) * 2003/02/24 show registers in sysfs (Kevin Brosius) * @@ -393,7 +394,7 @@ /* boot firmware should have set this up (5.1.1.3.1) */ if (!ohci->fminterval) { - temp = readl (&ohci->regs->fminterval); + temp = ohci_readl (&ohci->regs->fminterval); if (temp & 0x3fff0000) ohci->fminterval = temp; else @@ -405,7 +406,7 @@ * On PA-RISC, PDC can leave IR set incorrectly; ignore it there. */ #ifndef __hppa__ - if (readl (&ohci->regs->control) & OHCI_CTRL_IR) { + if (ohci_readl (&ohci->regs->control) & OHCI_CTRL_IR) { ohci_dbg (ohci, "USB HC TakeOver from BIOS/SMM\n"); /* this timeout is arbitrary. we make it long, so systems @@ -416,7 +417,7 @@ writel (OHCI_INTR_OC, &ohci->regs->intrenable); writel (OHCI_OCR, &ohci->regs->cmdstatus); - while (readl (&ohci->regs->control) & OHCI_CTRL_IR) { + while (ohci_readl (&ohci->regs->control) & OHCI_CTRL_IR) { msleep (10); if (--temp == 0) { ohci_err (ohci, "USB HC TakeOver failed!\n"); @@ -430,13 +431,13 @@ writel (OHCI_INTR_MIE, &ohci->regs->intrdisable); ohci_dbg (ohci, "reset, control = 0x%x\n", - readl (&ohci->regs->control)); + ohci_readl (&ohci->regs->control)); /* Reset USB (needed by some controllers); RemoteWakeupConnected * saved if boot firmware (BIOS/SMM/...) told us it's connected * (for OHCI integrated on mainboard, it normally is) */ - ohci->hc_control = readl (&ohci->regs->control); + ohci->hc_control = ohci_readl (&ohci->regs->control); ohci->hc_control &= OHCI_CTRL_RWC; /* hcfs 0 = RESET */ if (ohci->hc_control) ohci->hcd.can_wakeup = 1; @@ -450,13 +451,13 @@ &ohci->regs->roothub.portstatus [temp]); } // flush those pci writes - (void) readl (&ohci->regs->control); + (void) ohci_readl (&ohci->regs->control); msleep (50); /* HC Reset requires max 10 us delay */ writel (OHCI_HCR, &ohci->regs->cmdstatus); temp = 30; /* ... allow extra time */ - while ((readl (&ohci->regs->cmdstatus) & OHCI_HCR) != 0) { + while ((ohci_readl (&ohci->regs->cmdstatus) & OHCI_HCR) != 0) { if (--temp == 0) { ohci_err (ohci, "USB HC reset timed out!\n"); return -1; @@ -473,7 +474,7 @@ */ writel (ohci->hc_control, &ohci->regs->control); // flush those pci writes - (void) readl (&ohci->regs->control); + (void) ohci_readl (&ohci->regs->control); return 0; } @@ -505,8 +506,8 @@ /* some OHCI implementations are finicky about how they init. * bogus values here mean not even enumeration could work. */ - if ((readl (&ohci->regs->fminterval) & 0x3fff0000) == 0 - || !readl (&ohci->regs->periodicstart)) { + if ((ohci_readl (&ohci->regs->fminterval) & 0x3fff0000) == 0 + || !ohci_readl (&ohci->regs->periodicstart)) { ohci_err (ohci, "init err\n"); return -EOVERFLOW; } @@ -548,7 +549,7 @@ writel (RH_HS_LPSC, &ohci->regs->roothub.status); writel (power_switching ? RH_B_PPCM : 0, &ohci->regs->roothub.b); // flush those pci writes - (void) readl (&ohci->regs->control); + (void) ohci_readl (&ohci->regs->control); // POTPGT delay is bits 24-31, in 2 ms units. mdelay ((roothub_a (ohci) >> 23) & 0x1fe); @@ -592,19 +593,20 @@ struct ohci_regs *regs = ohci->regs; int ints; - /* we can eliminate a (slow) readl() if _only_ WDH caused this irq */ + /* we can eliminate a (slow) ohci_readl() + if _only_ WDH caused this irq */ if ((ohci->hcca->done_head != 0) && ! (le32_to_cpup (&ohci->hcca->done_head) & 0x01)) { ints = OHCI_INTR_WDH; /* cardbus/... hardware gone before remove() */ - } else if ((ints = readl (®s->intrstatus)) == ~(u32)0) { + } else if ((ints = ohci_readl (®s->intrstatus)) == ~(u32)0) { disable (ohci); ohci_dbg (ohci, "device removed!\n"); return IRQ_HANDLED; /* interrupt for some other device? */ - } else if ((ints &= readl (®s->intrenable)) == 0) { + } else if ((ints &= ohci_readl (®s->intrenable)) == 0) { return IRQ_NONE; } @@ -650,7 +652,7 @@ writel (ints, ®s->intrstatus); writel (OHCI_INTR_MIE, ®s->intrenable); // flush those pci writes - (void) readl (&ohci->regs->control); + (void) ohci_readl (&ohci->regs->control); } return IRQ_HANDLED; @@ -798,6 +800,14 @@ #include "ohci-omap.c" #endif -#if !(defined(CONFIG_PCI) || defined(CONFIG_SA1111) || defined(CONFIG_ARCH_OMAP)) +#ifdef CONFIG_ARCH_LH7A404 +#include "ohci-lh7a404.c" +#endif + +#if !(defined(CONFIG_PCI) \ + || defined(CONFIG_SA1111) \ + || defined(CONFIG_ARCH_OMAP) \ + || defined (CONFIG_ARCH_LH7A404) \ + ) #error "missing bus glue for ohci-hcd" #endif diff -Nru a/drivers/usb/host/ohci-hub.c b/drivers/usb/host/ohci-hub.c --- a/drivers/usb/host/ohci-hub.c Fri Jun 18 10:58:18 2004 +++ b/drivers/usb/host/ohci-hub.c Fri Jun 18 10:58:18 2004 @@ -20,20 +20,20 @@ * till some bits (mostly reserved) are clear; ok for all revs. */ #define read_roothub(hc, register, mask) ({ \ - u32 temp = readl (&hc->regs->roothub.register); \ + u32 temp = ohci_readl (&hc->regs->roothub.register); \ if (temp == -1) \ disable (hc); \ else if (hc->flags & OHCI_QUIRK_AMD756) \ while (temp & mask) \ - temp = readl (&hc->regs->roothub.register); \ + temp = ohci_readl (&hc->regs->roothub.register); \ temp; }) static u32 roothub_a (struct ohci_hcd *hc) { return read_roothub (hc, a, 0xfc0fe000); } static inline u32 roothub_b (struct ohci_hcd *hc) - { return readl (&hc->regs->roothub.b); } + { return ohci_readl (&hc->regs->roothub.b); } static inline u32 roothub_status (struct ohci_hcd *hc) - { return readl (&hc->regs->roothub.status); } + { return ohci_readl (&hc->regs->roothub.status); } static u32 roothub_portstatus (struct ohci_hcd *hc, int i) { return read_roothub (hc, portstatus [i], 0xffe0fce0); } @@ -83,14 +83,14 @@ spin_lock_irq (&ohci->lock); - ohci->hc_control = readl (&ohci->regs->control); + ohci->hc_control = ohci_readl (&ohci->regs->control); switch (ohci->hc_control & OHCI_CTRL_HCFS) { case OHCI_USB_RESUME: ohci_dbg (ohci, "resume/suspend?\n"); ohci->hc_control &= ~OHCI_CTRL_HCFS; ohci->hc_control |= OHCI_USB_RESET; writel (ohci->hc_control, &ohci->regs->control); - (void) readl (&ohci->regs->control); + (void) ohci_readl (&ohci->regs->control); /* FALL THROUGH */ case OHCI_USB_RESET: status = -EBUSY; @@ -109,7 +109,7 @@ ohci->hc_control &= ~OHCI_SCHED_ENABLES; writel (ohci->hc_control, &ohci->regs->control); - ohci->hc_control = readl (&ohci->regs->control); + ohci->hc_control = ohci_readl (&ohci->regs->control); writel (OHCI_INTR_SF, &ohci->regs->intrstatus); /* sched disables take effect on the next frame, @@ -120,7 +120,7 @@ while (limit > 0) { udelay (250); limit =- 250; - if (readl (&ohci->regs->intrstatus) & OHCI_INTR_SF) + if (ohci_readl (&ohci->regs->intrstatus) & OHCI_INTR_SF) break; } dl_done_list (ohci, 0); @@ -128,7 +128,7 @@ } dl_done_list (ohci, 0); finish_unlinks (ohci, OHCI_FRAME_NO(ohci->hcca), 0); - writel (readl (&ohci->regs->intrstatus), &ohci->regs->intrstatus); + writel (ohci_readl (&ohci->regs->intrstatus), &ohci->regs->intrstatus); /* maybe resume can wake root hub */ if (ohci->hcd.remote_wakeup) @@ -140,7 +140,7 @@ ohci->hc_control &= ~OHCI_CTRL_HCFS; ohci->hc_control |= OHCI_USB_SUSPEND; writel (ohci->hc_control, &ohci->regs->control); - (void) readl (&ohci->regs->control); + (void) ohci_readl (&ohci->regs->control); /* no resumes until devices finish suspending */ ohci->next_statechange = jiffies + msecs_to_jiffies (5); @@ -179,13 +179,13 @@ return -EAGAIN; spin_lock_irq (&ohci->lock); - ohci->hc_control = readl (&ohci->regs->control); + ohci->hc_control = ohci_readl (&ohci->regs->control); switch (ohci->hc_control & OHCI_CTRL_HCFS) { case OHCI_USB_SUSPEND: ohci->hc_control &= ~(OHCI_CTRL_HCFS|OHCI_SCHED_ENABLES); ohci->hc_control |= OHCI_USB_RESUME; writel (ohci->hc_control, &ohci->regs->control); - (void) readl (&ohci->regs->control); + (void) ohci_readl (&ohci->regs->control); ohci_dbg (ohci, "resume root hub\n"); break; case OHCI_USB_RESUME: @@ -210,7 +210,7 @@ temp = roothub_a (ohci) & RH_A_NDP; enables = 0; while (temp--) { - u32 stat = readl (&ohci->regs->roothub.portstatus [temp]); + u32 stat = ohci_readl (&ohci->regs->roothub.portstatus [temp]); /* force global, not selective, resume */ if (!(stat & RH_PS_PSS)) @@ -222,7 +222,7 @@ ohci->hcd.state = USB_STATE_RESUMING; mdelay (20 /* usb 11.5.1.10 */ + 15); - temp = readl (&ohci->regs->control); + temp = ohci_readl (&ohci->regs->control); temp &= OHCI_CTRL_HCFS; if (temp != OHCI_USB_RESUME) { ohci_err (ohci, "controller won't resume\n"); @@ -243,11 +243,11 @@ writel (OHCI_INTR_INIT, &ohci->regs->intrenable); if (ohci->ed_rm_list) writel (OHCI_INTR_SF, &ohci->regs->intrenable); - writel (readl (&ohci->regs->intrstatus), &ohci->regs->intrstatus); + writel (ohci_readl (&ohci->regs->intrstatus), &ohci->regs->intrstatus); /* Then re-enable operations */ writel (OHCI_USB_OPER, &ohci->regs->control); - (void) readl (&ohci->regs->control); + (void) ohci_readl (&ohci->regs->control); msleep (3); temp = OHCI_CONTROL_INIT | OHCI_USB_OPER; @@ -255,7 +255,7 @@ temp |= OHCI_CTRL_RWC; ohci->hc_control = temp; writel (temp, &ohci->regs->control); - (void) readl (&ohci->regs->control); + (void) ohci_readl (&ohci->regs->control); /* TRSMRCY */ msleep (10); @@ -290,7 +290,7 @@ writel (ohci->hc_control, &ohci->regs->control); if (temp) writel (status, &ohci->regs->cmdstatus); - (void) readl (&ohci->regs->control); + (void) ohci_readl (&ohci->regs->control); } ohci->hcd.state = USB_STATE_RUNNING; @@ -332,7 +332,7 @@ if (!HCD_IS_RUNNING(ohci->hcd.state)) return -ESHUTDOWN; ohci_err (ohci, "bogus NDP=%d, rereads as NDP=%d\n", - ports, readl (&ohci->regs->roothub.a) & RH_A_NDP); + ports, ohci_readl (&ohci->regs->roothub.a) & RH_A_NDP); /* retry later; "should not happen" */ return 0; } @@ -496,7 +496,7 @@ goto error; } writel (temp, &ohci->regs->roothub.portstatus [wIndex]); - // readl (&ohci->regs->roothub.portstatus [wIndex]); + // ohci_readl (&ohci->regs->roothub.portstatus [wIndex]); break; case GetHubDescriptor: ohci_hub_descriptor (ohci, (struct usb_hub_descriptor *) buf); @@ -541,7 +541,7 @@ &ohci->regs->roothub.portstatus [wIndex]); break; case USB_PORT_FEAT_RESET: - temp = readl (&ohci->regs->roothub.portstatus [wIndex]); + temp = ohci_readl (&ohci->regs->roothub.portstatus [wIndex]); if (temp & RH_PS_CCS) writel (RH_PS_PRS, &ohci->regs->roothub.portstatus [wIndex]); diff -Nru a/drivers/usb/host/ohci-q.c b/drivers/usb/host/ohci-q.c --- a/drivers/usb/host/ohci-q.c Fri Jun 18 10:58:18 2004 +++ b/drivers/usb/host/ohci-q.c Fri Jun 18 10:58:18 2004 @@ -321,7 +321,7 @@ if (!ed->hwNextED) { ohci->hc_control &= ~OHCI_CTRL_CLE; writel (ohci->hc_control, &ohci->regs->control); - // a readl() later syncs CLE with the HC + // a ohci_readl() later syncs CLE with the HC } else writel (le32_to_cpup (&ed->hwNextED), &ohci->regs->ed_controlhead); @@ -345,7 +345,7 @@ if (!ed->hwNextED) { ohci->hc_control &= ~OHCI_CTRL_BLE; writel (ohci->hc_control, &ohci->regs->control); - // a readl() later syncs BLE with the HC + // a ohci_readl() later syncs BLE with the HC } else writel (le32_to_cpup (&ed->hwNextED), &ohci->regs->ed_bulkhead); @@ -481,7 +481,7 @@ writel (OHCI_INTR_SF, &ohci->regs->intrstatus); writel (OHCI_INTR_SF, &ohci->regs->intrenable); // flush those writes, and get latest HCCA contents - (void) readl (&ohci->regs->control); + (void) ohci_readl (&ohci->regs->control); /* SF interrupt might get delayed; record the frame counter value that * indicates when the HC isn't looking at it, so concurrent unlinks diff -Nru a/drivers/usb/host/ohci.h b/drivers/usb/host/ohci.h --- a/drivers/usb/host/ohci.h Fri Jun 18 10:58:18 2004 +++ b/drivers/usb/host/ohci.h Fri Jun 18 10:58:18 2004 @@ -429,3 +429,22 @@ # define ohci_vdbg(ohci, fmt, args...) do { } while (0) #endif +#ifdef CONFIG_ARCH_LH7A404 + /* Marc Singer: at the time this code was written, the LH7A404 + * had a problem reading the USB host registers. This + * implementation of the ohci_readl function performs the read + * twice as a work-around. + */ +static inline unsigned int ohci_readl (void* regs) +{ + *(volatile unsigned int*) regs; + return *(volatile unsigned int*) regs; +} +#else + /* Standard version of ohci_readl uses standard, platform + * specific implementation. */ +static inline unsigned int ohci_readl (void* regs) +{ + return readl (regs); +} +#endif diff -Nru a/usb/host/ohci-lh7a404.c b/usb/host/ohci-lh7a404.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/usb/host/ohci-lh7a404.c Fri Jun 18 10:58:18 2004 @@ -0,0 +1,387 @@ +/* + * OHCI HCD (Host Controller Driver) for USB. + * + * (C) Copyright 1999 Roman Weissgaerber + * (C) Copyright 2000-2002 David Brownell + * (C) Copyright 2002 Hewlett-Packard Company + * + * Bus Glue for Sharp LH7A404 + * + * Written by Christopher Hoover + * Based on fragments of previous driver by Rusell King et al. + * + * Modified for LH7A404 from ohci-sa1111.c + * by Durgesh Pattamatta + * + * This file is licenced under the GPL. + */ + +#include +#include +#include + + +extern int usb_disabled(void); + +/*-------------------------------------------------------------------------*/ + +static void lh7a404_start_hc(struct platform_device *dev) +{ + printk(KERN_DEBUG __FILE__ + ": starting LH7A404 OHCI USB Controller\n"); + + /* + * Now, carefully enable the USB clock, and take + * the USB host controller out of reset. + */ + CSC_PWRCNT |= CSC_PWRCNT_USBH_EN; /* Enable clock */ + udelay(1000); + USBH_CMDSTATUS = OHCI_HCR; + + printk(KERN_DEBUG __FILE__ + ": Clock to USB host has been enabled \n"); +} + +static void lh7a404_stop_hc(struct platform_device *dev) +{ + printk(KERN_DEBUG __FILE__ + ": stopping LH7A404 OHCI USB Controller\n"); + + CSC_PWRCNT &= ~CSC_PWRCNT_USBH_EN; /* Disable clock */ +} + + +/*-------------------------------------------------------------------------*/ + + +static irqreturn_t usb_hcd_lh7a404_hcim_irq (int irq, void *__hcd, + struct pt_regs * r) +{ + struct usb_hcd *hcd = __hcd; + + return usb_hcd_irq(irq, hcd, r); +} + +/*-------------------------------------------------------------------------*/ + +void usb_hcd_lh7a404_remove (struct usb_hcd *, struct platform_device *); + +/* configure so an HC device and id are always provided */ +/* always called with process context; sleeping is OK */ + + +/** + * usb_hcd_lh7a404_probe - initialize LH7A404-based HCDs + * Context: !in_interrupt() + * + * Allocates basic resources for this USB host controller, and + * then invokes the start() method for the HCD associated with it + * through the hotplug entry's driver_data. + * + */ +int usb_hcd_lh7a404_probe (const struct hc_driver *driver, + struct usb_hcd **hcd_out, + struct platform_device *dev) +{ + int retval; + struct usb_hcd *hcd = 0; + + unsigned int *addr = NULL; + + if (!request_mem_region(dev->resource[0].start, + dev->resource[0].end + - dev->resource[0].start + 1, hcd_name)) { + pr_debug("request_mem_region failed"); + return -EBUSY; + } + + + lh7a404_start_hc(dev); + + addr = ioremap(dev->resource[0].start, + dev->resource[0].end + - dev->resource[0].start + 1); + if (!addr) { + pr_debug("ioremap failed"); + retval = -ENOMEM; + goto err1; + } + + + hcd = driver->hcd_alloc (); + if (hcd == NULL){ + pr_debug ("hcd_alloc failed"); + retval = -ENOMEM; + goto err1; + } + + if(dev->resource[1].flags != IORESOURCE_IRQ){ + pr_debug ("resource[1] is not IORESOURCE_IRQ"); + retval = -ENOMEM; + goto err1; + } + + hcd->driver = (struct hc_driver *) driver; + hcd->description = driver->description; + hcd->irq = dev->resource[1].start; + hcd->regs = addr; + hcd->self.controller = &dev->dev; + + retval = hcd_buffer_create (hcd); + if (retval != 0) { + pr_debug ("pool alloc fail"); + goto err1; + } + + retval = request_irq (hcd->irq, usb_hcd_lh7a404_hcim_irq, SA_INTERRUPT, + hcd->description, hcd); + if (retval != 0) { + pr_debug("request_irq failed"); + retval = -EBUSY; + goto err2; + } + + pr_debug ("%s (LH7A404) at 0x%p, irq %d", + hcd->description, hcd->regs, hcd->irq); + + usb_bus_init (&hcd->self); + hcd->self.op = &usb_hcd_operations; + hcd->self.hcpriv = (void *) hcd; + hcd->self.bus_name = "lh7a404"; + hcd->product_desc = "LH7A404 OHCI"; + + INIT_LIST_HEAD (&hcd->dev_list); + + usb_register_bus (&hcd->self); + + if ((retval = driver->start (hcd)) < 0) + { + usb_hcd_lh7a404_remove(hcd, dev); + return retval; + } + + *hcd_out = hcd; + return 0; + + err2: + hcd_buffer_destroy (hcd); + if (hcd) + driver->hcd_free(hcd); + err1: + lh7a404_stop_hc(dev); + release_mem_region(dev->resource[0].start, + dev->resource[0].end + - dev->resource[0].start + 1); + return retval; +} + + +/* may be called without controller electrically present */ +/* may be called with controller, bus, and devices active */ + +/** + * usb_hcd_lh7a404_remove - shutdown processing for LH7A404-based HCDs + * @dev: USB Host Controller being removed + * Context: !in_interrupt() + * + * Reverses the effect of usb_hcd_lh7a404_probe(), first invoking + * the HCD's stop() method. It is always called from a thread + * context, normally "rmmod", "apmd", or something similar. + * + */ +void usb_hcd_lh7a404_remove (struct usb_hcd *hcd, struct platform_device *dev) +{ + struct usb_device *hub; + void *base; + + pr_debug ("remove: %s, state %x", hcd->self.bus_name, hcd->state); + + if (in_interrupt ()) + BUG (); + + hub = hcd->self.root_hub; + hcd->state = USB_STATE_QUIESCING; + + pr_debug ("%s: roothub graceful disconnect", hcd->self.bus_name); + usb_disconnect (&hub); + + hcd->driver->stop (hcd); + hcd->state = USB_STATE_HALT; + + free_irq (hcd->irq, hcd); + hcd_buffer_destroy (hcd); + + usb_deregister_bus (&hcd->self); + + base = hcd->regs; + hcd->driver->hcd_free (hcd); + + lh7a404_stop_hc(dev); + release_mem_region(dev->resource[0].start, + dev->resource[0].end + - dev->resource[0].start + 1); +} + +/*-------------------------------------------------------------------------*/ + +static int __devinit +ohci_lh7a404_start (struct usb_hcd *hcd) +{ + struct ohci_hcd *ohci = hcd_to_ohci (hcd); + int ret; + + ohci_dbg (ohci, "ohci_lh7a404_start, ohci:%p", ohci); + + ohci->hcca = dma_alloc_coherent (hcd->self.controller, + sizeof *ohci->hcca, &ohci->hcca_dma, 0); + if (!ohci->hcca) + return -ENOMEM; + + ohci_dbg (ohci, "ohci_lh7a404_start, ohci->hcca:%p", + ohci->hcca); + + memset (ohci->hcca, 0, sizeof (struct ohci_hcca)); + + if ((ret = ohci_mem_init (ohci)) < 0) { + ohci_stop (hcd); + return ret; + } + ohci->regs = hcd->regs; + + if (hc_reset (ohci) < 0) { + ohci_stop (hcd); + return -ENODEV; + } + + if (hc_start (ohci) < 0) { + err ("can't start %s", ohci->hcd.self.bus_name); + ohci_stop (hcd); + return -EBUSY; + } + create_debug_files (ohci); + +#ifdef DEBUG + ohci_dump (ohci, 1); +#endif /*DEBUG*/ + return 0; +} + +/*-------------------------------------------------------------------------*/ + +static const struct hc_driver ohci_lh7a404_hc_driver = { + .description = hcd_name, + + /* + * generic hardware linkage + */ + .irq = ohci_irq, + .flags = HCD_USB11, + + /* + * basic lifecycle operations + */ + .start = ohci_lh7a404_start, +#ifdef CONFIG_PM + /* suspend: ohci_lh7a404_suspend, -- tbd */ + /* resume: ohci_lh7a404_resume, -- tbd */ +#endif /*CONFIG_PM*/ + .stop = ohci_stop, + + /* + * memory lifecycle (except per-request) + */ + .hcd_alloc = ohci_hcd_alloc, + .hcd_free = ohci_hcd_free, + + /* + * managing i/o requests and associated device resources + */ + .urb_enqueue = ohci_urb_enqueue, + .urb_dequeue = ohci_urb_dequeue, + .endpoint_disable = ohci_endpoint_disable, + + /* + * scheduling support + */ + .get_frame_number = ohci_get_frame, + + /* + * root hub support + */ + .hub_status_data = ohci_hub_status_data, + .hub_control = ohci_hub_control, +}; + +/*-------------------------------------------------------------------------*/ + +static int ohci_hcd_lh7a404_drv_probe(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct usb_hcd *hcd = NULL; + int ret; + + pr_debug ("In ohci_hcd_lh7a404_drv_probe"); + + if (usb_disabled()) + return -ENODEV; + + ret = usb_hcd_lh7a404_probe(&ohci_lh7a404_hc_driver, &hcd, pdev); + + if (ret == 0) + dev_set_drvdata(dev, hcd); + + return ret; +} + +static int ohci_hcd_lh7a404_drv_remove(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct usb_hcd *hcd = dev_get_drvdata(dev); + + usb_hcd_lh7a404_remove(hcd, pdev); + dev_set_drvdata(dev, NULL); + return 0; +} + /*TBD*/ +/*static int ohci_hcd_lh7a404_drv_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct usb_hcd *hcd = dev_get_drvdata(dev); + + return 0; +} +static int ohci_hcd_lh7a404_drv_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct usb_hcd *hcd = dev_get_drvdata(dev); + + + return 0; +} +*/ + +static struct device_driver ohci_hcd_lh7a404_driver = { + .name = "lh7a404-ohci", + .bus = &platform_bus_type, + .probe = ohci_hcd_lh7a404_drv_probe, + .remove = ohci_hcd_lh7a404_drv_remove, + /*.suspend = ohci_hcd_lh7a404_drv_suspend, */ + /*.resume = ohci_hcd_lh7a404_drv_resume, */ +}; + +static int __init ohci_hcd_lh7a404_init (void) +{ + pr_debug (DRIVER_INFO " (LH7A404)"); + pr_debug ("block sizes: ed %d td %d\n", + sizeof (struct ed), sizeof (struct td)); + + return driver_register(&ohci_hcd_lh7a404_driver); +} + +static void __exit ohci_hcd_lh7a404_cleanup (void) +{ + driver_unregister(&ohci_hcd_lh7a404_driver); +} + +module_init (ohci_hcd_lh7a404_init); +module_exit (ohci_hcd_lh7a404_cleanup);