Ooops, Sorry for the confusion... The patch (ehci-ssb support) wasn't applied correctly in my distribution. I had to apply the patch manually.
Something wrong happens applying this patch (wl500gp-v2): //////ping message///// PING 192.168.1.1 (192.168.1.1) 56(84) bytes of data. >From 192.168.1.15 icmp_seq=2 Destination Host Unreachable >From 192.168.1.15 icmp_seq=3 Destination Host Unreachable >From 192.168.1.15 icmp_seq=4 Destination Host Unreachable >From 192.168.1.15 icmp_seq=6 Destination Host Unreachable >From 192.168.1.15 icmp_seq=7 Destination Host Unreachable >From 192.168.1.15 icmp_seq=8 Destination Host Unreachable 64 bytes from 192.168.1.1: icmp_seq=9 ttl=100 time=1807 ms 64 bytes from 192.168.1.1: icmp_seq=10 ttl=100 time=808 ms 64 bytes from 192.168.1.1: icmp_seq=11 ttl=100 time=1.92 ms 64 bytes from 192.168.1.1: icmp_seq=25 ttl=100 time=4.80 ms 64 bytes from 192.168.1.1: icmp_seq=39 ttl=100 time=4.62 ms 64 bytes from 192.168.1.1: icmp_seq=53 ttl=100 time=4.49 ms 64 bytes from 192.168.1.1: icmp_seq=58 ttl=64 time=5.73 ms --- 192.168.1.1 ping statistics --- 59 packets transmitted, 7 received, +6 errors, 88% packet loss, time 58002ms rtt min/avg/max/mdev = 1.924/376.922/1807.924/646.814 ms, pipe 3 ////--------------------------------------- The ohci-ssb attach function looks as follow: static int ssb_ohci_attach(struct ssb_device *dev) { struct ssb_ohci_device *ohcidev; struct usb_hcd *hcd; int err = -ENOMEM; u32 tmp, flags = 0; if (!(ssb_read32(dev, SSB_TMSLOW) & SSB_TMSLOW_RESET)) goto core_already_enabled; /* * THE FOLLOWING COMMENTS PRESERVED FROM GPL SOURCE RELEASE * * The USB core requires a special bit to be set during core * reset to enable host (OHCI) mode. Resetting the SB core in * pcibios_enable_device() is a hack for compatibility with * vanilla usb-ohci so that it does not have to know about * SB. A driver that wants to use the USB core in device mode * should know about SB and should reset the bit back to 0 * after calling pcibios_enable_device(). */ if (dev->id.coreid == SSB_DEV_USB11_HOSTDEV) { if (ssb_device_is_enabled(dev)) goto core_already_enabled; flags |= SSB_OHCI_TMSLOW_HOSTMODE; ssb_device_enable(dev, flags); } /* * USB 2.0 special considerations: * * 1. Since the core supports both OHCI and EHCI functions, it must * only be reset once. * * 2. In addition to the standard SB reset sequence, the Host Control * Register must be programmed to bring the USB core and various * phy components out of reset. */ //else if (dev->id.coreid == SSB_DEV_USB20_HOST) { else if (dev->id.coreid == SSB_DEV_USB11_HOST) { if (ssb_device_is_enabled(dev)) goto core_already_enabled; ssb_device_enable(dev, 0); ssb_write32(dev, 0x200, 0x7ff); udelay(1); if (dev->id.revision == 1) { /* bug in rev 1 */ /* Change Flush control reg */ tmp = ssb_read32(dev, 0x400); tmp &= ~8; ssb_write32(dev, 0x400, tmp); tmp = ssb_read32(dev, 0x400); printk(KERN_INFO "USB20H fcr: 0x%0x\n", tmp); /* Change Shim control reg */ tmp = ssb_read32(dev, 0x304); tmp &= ~0x100; ssb_write32(dev, 0x304, tmp); tmp = ssb_read32(dev, 0x304); printk(KERN_INFO "USB20H shim: 0x%0x\n", tmp); } } else ssb_device_enable(dev, 0); core_already_enabled: /* * Set dma mask - 32 bit mask is just an assumption */ if (ssb_dma_set_mask(dev, DMA_32BIT_MASK)) return -EOPNOTSUPP; hcd = usb_create_hcd(&ssb_ohci_hc_driver, dev->dev, dev->dev->bus_id); if (!hcd) goto err_dev_disable; ohcidev = hcd_to_ssb_ohci(hcd); ohcidev->enable_flags = flags; tmp = ssb_read32(dev, SSB_ADMATCH0); hcd->rsrc_start = ssb_admatch_base(tmp); hcd->rsrc_len = ssb_admatch_size(tmp); hcd->regs = ioremap_nocache(hcd->rsrc_start, hcd->rsrc_len); if (!hcd->regs) goto err_put_hcd; err = usb_add_hcd(hcd, dev->irq, IRQF_DISABLED | IRQF_SHARED); if (err) goto err_iounmap; ssb_set_drvdata(dev, hcd); return err; err_iounmap: iounmap(hcd->regs); err_put_hcd: usb_put_hcd(hcd); err_dev_disable: ssb_device_disable(dev, flags); return err; } EXPORT_SYMBOL_GPL(ssb_ohci_attach); Thanks for any help On Thu, 2008-06-12 at 21:35 -0400, Felipe Maya wrote: > With the Patch submited by Michael Buesch (follow the patch in this > email), and the Steve Brown Patch, Simultaneously USB ports, seems work > fine!! > > good work!!! > > Index: wireless-testing/drivers/ssb/main.c > =================================================================== > --- wireless-testing.orig/drivers/ssb/main.c 2008-05-17 > 18:44:32.000000000 +0200 > +++ wireless-testing/drivers/ssb/main.c 2008-06-12 15:24:04.000000000 > +0200 > @@ -1165,21 +1165,27 @@ u32 ssb_dma_translation(struct ssb_devic > } > EXPORT_SYMBOL(ssb_dma_translation); > > int ssb_dma_set_mask(struct ssb_device *ssb_dev, u64 mask) > { > struct device *dma_dev = ssb_dev->dma_dev; > + int err = 0; > > #ifdef CONFIG_SSB_PCIHOST > - if (ssb_dev->bus->bustype == SSB_BUSTYPE_PCI) > - return dma_set_mask(dma_dev, mask); > + if (ssb_dev->bus->bustype == SSB_BUSTYPE_PCI) { > + err = pci_set_dma_mask(ssb_dev->bus->host_pci, mask); > + if (err) > + return err; > + err = pci_set_consistent_dma_mask(ssb_dev->bus->host_pci, > mask); > + return err; > + } > #endif > dma_dev->coherent_dma_mask = mask; > dma_dev->dma_mask = &dma_dev->coherent_dma_mask; > > - return 0; > + return err; > } > EXPORT_SYMBOL(ssb_dma_set_mask); > > int ssb_bus_may_powerdown(struct ssb_bus *bus) > { > struct ssb_chipcommon *cc; > > > On Wed, 2008-06-11 at 15:30 -0700, Steve Brown wrote: > > Felipe Maya wrote: > > > The patch works with one USB port, but with two USB simultaneously > > > something wrong happens. > > > > > > I changed the driver_mipscore.c to enable SSB_DEV_USB11_HOST for bcm5354 > > > at this line (208): > > > > > > > > > if (((bus->chip_id == 0x4710) || (bus->chip_id == 0x5354)) && (irq <= > > > 4)) > > > > > > instead > > > > > > if (((bus->chip_id == 0x4710) && (irq <= 4)) > > > > > > > > > and it seems work fine!!!! > > > > > > > > > for (irq = 2, i = 0; i < bus->nr_devices; i++) { > > > dev = &(bus->devices[i]); > > > dev->irq = ssb_mips_irq(dev) + 2; > > > switch (dev->id.coreid) { > > > case SSB_DEV_USB11_HOST: > > > /* shouldn't need a separate irq line for > > > non-4710, most of them have a proper > > > * external usb controller on the pci */ > > > if (((bus->chip_id == 0x4710) || (bus->chip_id > > > == 0x5354)) && (irq <= 4)) { > > > set_irq(dev, irq++); > > > break; > > > } > > > /* fallthrough */ > > > case SSB_DEV_PCI: > > > .... > > > .... > > > .... > > > > > What goes wrong? > > Are you using the patch I posted on this list? > > Also, what router is your 5354 in? > > > > This is very puzzling. One of the major changes was to get rid of the > > fake USB11 device. The single USB20 device now gets shared between the > > ehci and ohci drivers. I don't understand how the code in that case even > > got executed. > > > > I just got a wl500gpv2 that has a 5354 and 2 usb ports. As soon as I get > > some headers soldered to it, I'll try both ports and see if I can get it > > to break. > > > > Steve > > > > My time, is my time!!
--- a/drivers/usb/host/ehci-ssb.c 2008-06-19 09:53:49.000000000 -0400 +++ b/usb/host/ehci-ssb.c 2008-06-19 09:54:20.000000000 -0400 @@ -23,6 +23,8 @@ */ #include <linux/ssb/ssb.h> +extern int ssb_ohci_attach(struct ssb_device *dev); +extern void ssb_ohci_detach(struct ssb_device *dev); #define SSB_OHCI_TMSLOW_HOSTMODE (1 << 29) @@ -135,14 +137,16 @@ .endpoint_disable = ehci_endpoint_disable, .get_frame_number = ehci_get_frame, - + .hub_status_data = ehci_hub_status_data, .hub_control = ehci_hub_control, +// .hub_irq_enable = ehci_rhsc_enable, #ifdef CONFIG_PM .bus_suspend = ehci_bus_suspend, .bus_resume = ehci_bus_resume, #endif +// .start_port_reset = ehci_start_port_reset, }; static void ssb_ehci_detach(struct ssb_device *dev) @@ -152,7 +156,14 @@ usb_remove_hcd(hcd); iounmap(hcd->regs); usb_put_hcd(hcd); + +/* + * Also detach the companion ohci device + */ + ssb_ohci_detach(dev->companion); +// ssb_device_disable(dev, 0); } +EXPORT_SYMBOL_GPL(ssb_ehci_detach); static int ssb_ehci_attach(struct ssb_device *dev) { @@ -190,10 +201,7 @@ ssb_device_enable(dev, 0); ssb_write32(dev, 0x200, 0x7ff); udelay(1); - /* - * Workaround for bug in rev 1 - */ - if (dev->id.revision == 1) { + if (dev->id.revision == 1) { // bug in rev 1 /* Change Flush control reg */ tmp = ssb_read32(dev, 0x400); @@ -227,8 +235,8 @@ ehcidev->enable_flags = flags; tmp = ssb_read32(dev, SSB_ADMATCH0); - hcd->rsrc_start = ssb_admatch_base(tmp) + 0x800;/* ehci core offset */ - hcd->rsrc_len = 0x100; /* size of ehci reg block */ + hcd->rsrc_start = ssb_admatch_base(tmp) + 0x800; // offset for ehci core + hcd->rsrc_len = 0x100 /*ssb_admatch_size(tmp)*/; // size of ehci reg block /* * start & size modified per sbutils.c */ @@ -241,8 +249,27 @@ ssb_set_drvdata(dev, hcd); + /* + * attach ohci (companion) device in this core + * + * add new device as a copy of the ehci device + * change coreid to SSB_DEV_USB11_HOSTDEV + * call ohci_ssb_attach(dev) to attach ohci device in this core + */ + + dev->companion = &dev->bus->devices[dev->bus->nr_devices]; + dev->bus->nr_devices; + if (dev->bus->nr_devices > ARRAY_SIZE(dev->bus->devices)) { + printk(KERN_ERR + "More than %d devs, could not create ohci dev (%d)\n", + SSB_MAX_NR_CORES, dev->bus->nr_devices); + return err; + } + memcpy(dev->companion, dev, sizeof(struct ssb_device)); + dev->companion->id.coreid = SSB_DEV_USB11_HOSTDEV; + err = ssb_ohci_attach(dev->companion); return err; - + err_iounmap: iounmap(hcd->regs); err_put_hcd: @@ -251,6 +278,7 @@ ssb_device_disable(dev, flags); return err; } +EXPORT_SYMBOL_GPL(ssb_ehci_attach); static int ssb_ehci_probe(struct ssb_device *dev, const struct ssb_device_id *id)
--- /tmp/linux-2.6.23.17/drivers/usb/host/ohci-ssb.c 2008-06-19 10:11:30.000000000 -0400 +++ drivers/usb/host/ohci-ssb.c 2008-06-19 10:24:25.000000000 -0400 @@ -17,7 +17,6 @@ */ #include <linux/ssb/ssb.h> - #define SSB_OHCI_TMSLOW_HOSTMODE (1 << 29) struct ssb_ohci_device { @@ -128,11 +127,11 @@ static void ssb_ohci_detach(struct ssb_device *dev) { struct usb_hcd *hcd = ssb_get_drvdata(dev); - usb_remove_hcd(hcd); iounmap(hcd->regs); usb_put_hcd(hcd); } +EXPORT_SYMBOL_GPL(ssb_ohci_detach); static int ssb_ohci_attach(struct ssb_device *dev) { @@ -153,12 +152,12 @@ * vanilla usb-ohci so that it does not have to know about * SB. A driver that wants to use the USB core in device mode * should know about SB and should reset the bit back to 0 - * after calling pcibios_enable_device() + * after calling pcibios_enable_device(). */ if (dev->id.coreid == SSB_DEV_USB11_HOSTDEV) { if (ssb_device_is_enabled(dev)) - goto core_already_enabled; + goto core_already_enabled; flags |= SSB_OHCI_TMSLOW_HOSTMODE; ssb_device_enable(dev, flags); } @@ -168,20 +167,22 @@ * * 1. Since the core supports both OHCI and EHCI functions, it must * only be reset once. - * + * * 2. In addition to the standard SB reset sequence, the Host Control * Register must be programmed to bring the USB core and various - * phy components out of reset. + * phy components out of reset. */ - - else if (dev->id.coreid == SSB_DEV_USB20_HOST) { -// else if (dev->id.coreid == SSB_DEV_USB11_HOST) { + + //else if (dev->id.coreid == SSB_DEV_USB20_HOST) { + else if (dev->id.coreid == SSB_DEV_USB11_HOST) { if (ssb_device_is_enabled(dev)) goto core_already_enabled; + ssb_device_enable(dev, 0); ssb_write32(dev, 0x200, 0x7ff); udelay(1); - if (dev->id.revision == 1) { // bug in rev 1 + + if (dev->id.revision == 1) { /* bug in rev 1 */ /* Change Flush control reg */ tmp = ssb_read32(dev, 0x400); @@ -197,12 +198,10 @@ tmp = ssb_read32(dev, 0x304); printk(KERN_INFO "USB20H shim: 0x%0x\n", tmp); } - } - else + } else ssb_device_enable(dev, 0); core_already_enabled: - /* * Set dma mask - 32 bit mask is just an assumption */ @@ -238,6 +237,7 @@ ssb_device_disable(dev, flags); return err; } +EXPORT_SYMBOL_GPL(ssb_ohci_attach); static int ssb_ohci_probe(struct ssb_device *dev, const struct ssb_device_id *id) @@ -275,7 +275,6 @@ static int ssb_ohci_suspend(struct ssb_device *dev, pm_message_t state) { ssb_device_disable(dev, 0); - return 0; } @@ -286,6 +285,7 @@ ssb_device_enable(dev, ohcidev->enable_flags); + ohci_finish_controller_resume(hcd); return 0; } @@ -297,7 +297,7 @@ static const struct ssb_device_id ssb_ohci_table[] = { SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_USB11_HOSTDEV, SSB_ANY_REV), SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_USB11_HOST, SSB_ANY_REV), - SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_USB20_HOST, SSB_ANY_REV), +// SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_USB20_HOST, SSB_ANY_REV), SSB_DEVTABLE_END }; MODULE_DEVICE_TABLE(ssb, ssb_ohci_table); @@ -310,4 +310,3 @@ .suspend = ssb_ohci_suspend, .resume = ssb_ohci_resume, }; -
--- ../../toolchain-mipsel_gcc4.1.2/linux/drivers/usb/host/ehci-hcd.c 2008-02-25 19:14:28.000000000 -0500 +++ drivers/usb/host/ehci-hcd.c 2008-06-18 17:12:42.000000000 -0400 @@ -33,6 +33,7 @@ #include <linux/usb.h> #include <linux/moduleparam.h> #include <linux/dma-mapping.h> +#include <linux/debugfs.h> #include "../core/hcd.h" @@ -109,7 +110,7 @@ #define EHCI_TUNE_MULT_TT 1 #define EHCI_TUNE_FLS 2 /* (small) 256 frame schedule */ -#define EHCI_IAA_JIFFIES (HZ/100) /* arbitrary; ~10 msec */ +#define EHCI_IAA_MSECS 10 /* arbitrary */ #define EHCI_IO_JIFFIES (HZ/10) /* io watchdog > irq_thresh */ #define EHCI_ASYNC_JIFFIES (HZ/20) /* async idle timeout */ #define EHCI_SHRINK_JIFFIES (HZ/200) /* async qh unlink delay */ @@ -266,6 +267,7 @@ /*-------------------------------------------------------------------------*/ +static void end_unlink_async(struct ehci_hcd *ehci); static void ehci_work(struct ehci_hcd *ehci); #include "ehci-hub.c" @@ -275,24 +277,61 @@ /*-------------------------------------------------------------------------*/ -static void ehci_watchdog (unsigned long param) +static void ehci_iaa_watchdog(unsigned long param) { struct ehci_hcd *ehci = (struct ehci_hcd *) param; unsigned long flags; spin_lock_irqsave (&ehci->lock, flags); - /* lost IAA irqs wedge things badly; seen with a vt8235 */ - if (ehci->reclaim) { - u32 status = ehci_readl(ehci, &ehci->regs->status); - if (status & STS_IAA) { - ehci_vdbg (ehci, "lost IAA\n"); + /* Lost IAA irqs wedge things badly; seen first with a vt8235. + * So we need this watchdog, but must protect it against both + * (a) SMP races against real IAA firing and retriggering, and + * (b) clean HC shutdown, when IAA watchdog was pending. + */ + if (ehci->reclaim + && !timer_pending(&ehci->iaa_watchdog) + && HC_IS_RUNNING(ehci_to_hcd(ehci)->state)) { + u32 cmd, status; + + /* If we get here, IAA is *REALLY* late. It's barely + * conceivable that the system is so busy that CMD_IAAD + * is still legitimately set, so let's be sure it's + * clear before we read STS_IAA. (The HC should clear + * CMD_IAAD when it sets STS_IAA.) + */ + cmd = ehci_readl(ehci, &ehci->regs->command); + if (cmd & CMD_IAAD) + ehci_writel(ehci, cmd & ~CMD_IAAD, + &ehci->regs->command); + + /* If IAA is set here it either legitimately triggered + * before we cleared IAAD above (but _way_ late, so we'll + * still count it as lost) ... or a silicon erratum: + * - VIA seems to set IAA without triggering the IRQ; + * - IAAD potentially cleared without setting IAA. + */ + status = ehci_readl(ehci, &ehci->regs->status); + if ((status & STS_IAA) || !(cmd & CMD_IAAD)) { COUNT (ehci->stats.lost_iaa); ehci_writel(ehci, STS_IAA, &ehci->regs->status); - ehci->reclaim_ready = 1; } + + ehci_vdbg(ehci, "IAA watchdog: status %x cmd %x\n", + status, cmd); + end_unlink_async(ehci); } + spin_unlock_irqrestore(&ehci->lock, flags); +} + +static void ehci_watchdog(unsigned long param) +{ + struct ehci_hcd *ehci = (struct ehci_hcd *) param; + unsigned long flags; + + spin_lock_irqsave(&ehci->lock, flags); + /* stop async processing after it's idled a bit */ if (test_bit (TIMER_ASYNC_OFF, &ehci->actions)) start_unlink_async (ehci, ehci->async); @@ -363,8 +402,6 @@ static void ehci_work (struct ehci_hcd *ehci) { timer_action_done (ehci, TIMER_IO_WATCHDOG); - if (ehci->reclaim_ready) - end_unlink_async (ehci); /* another CPU may drop ehci->lock during a schedule scan while * it reports urb completions. this flag guards against bogus @@ -399,6 +436,7 @@ /* no more interrupts ... */ del_timer_sync (&ehci->watchdog); + del_timer_sync(&ehci->iaa_watchdog); spin_lock_irq(&ehci->lock); if (HC_IS_RUNNING (hcd->state)) @@ -447,6 +485,10 @@ ehci->watchdog.function = ehci_watchdog; ehci->watchdog.data = (unsigned long) ehci; + init_timer(&ehci->iaa_watchdog); + ehci->iaa_watchdog.function = ehci_iaa_watchdog; + ehci->iaa_watchdog.data = (unsigned long) ehci; + /* * hw default: 1K periodic list heads, one per frame. * periodic_size can shrink by USBCMD update if hcc_params allows. @@ -463,7 +505,6 @@ ehci->i_thresh = 2 + HCC_ISOC_THRES(hcc_params); ehci->reclaim = NULL; - ehci->reclaim_ready = 0; ehci->next_uframe = -1; /* @@ -611,7 +652,7 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd) { struct ehci_hcd *ehci = hcd_to_ehci (hcd); - u32 status, pcd_status = 0; + u32 status, pcd_status = 0, cmd; int bh; spin_lock (&ehci->lock); @@ -632,7 +673,7 @@ /* clear (just) interrupts */ ehci_writel(ehci, status, &ehci->regs->status); - ehci_readl(ehci, &ehci->regs->command); /* unblock posted write */ + cmd = ehci_readl(ehci, &ehci->regs->command); bh = 0; #ifdef EHCI_VERBOSE_DEBUG @@ -653,9 +694,17 @@ /* complete the unlinking of some qh [4.15.2.3] */ if (status & STS_IAA) { - COUNT (ehci->stats.reclaim); - ehci->reclaim_ready = 1; - bh = 1; + /* guard against (alleged) silicon errata */ + if (cmd & CMD_IAAD) { + ehci_writel(ehci, cmd & ~CMD_IAAD, + &ehci->regs->command); + ehci_dbg(ehci, "IAA with IAAD still set?\n"); + } + if (ehci->reclaim) { + COUNT(ehci->stats.reclaim); + end_unlink_async(ehci); + } else + ehci_dbg(ehci, "IAA with nothing to reclaim?\n"); } /* remote wakeup [4.3.1] */ @@ -730,7 +779,6 @@ */ static int ehci_urb_enqueue ( struct usb_hcd *hcd, - struct usb_host_endpoint *ep, struct urb *urb, gfp_t mem_flags ) { @@ -745,12 +793,12 @@ default: if (!qh_urb_transaction (ehci, urb, &qtd_list, mem_flags)) return -ENOMEM; - return submit_async (ehci, ep, urb, &qtd_list, mem_flags); + return submit_async(ehci, urb, &qtd_list, mem_flags); case PIPE_INTERRUPT: if (!qh_urb_transaction (ehci, urb, &qtd_list, mem_flags)) return -ENOMEM; - return intr_submit (ehci, ep, urb, &qtd_list, mem_flags); + return intr_submit(ehci, urb, &qtd_list, mem_flags); case PIPE_ISOCHRONOUS: if (urb->dev->speed == USB_SPEED_HIGH) @@ -762,10 +810,16 @@ static void unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh) { - /* if we need to use IAA and it's busy, defer */ - if (qh->qh_state == QH_STATE_LINKED - && ehci->reclaim - && HC_IS_RUNNING (ehci_to_hcd(ehci)->state)) { + /* failfast */ + if (!HC_IS_RUNNING(ehci_to_hcd(ehci)->state) && ehci->reclaim) + end_unlink_async(ehci); + + /* if it's not linked then there's nothing to do */ + if (qh->qh_state != QH_STATE_LINKED) + ; + + /* defer till later if busy */ + else if (ehci->reclaim) { struct ehci_qh *last; for (last = ehci->reclaim; @@ -775,12 +829,8 @@ qh->qh_state = QH_STATE_UNLINK_WAIT; last->reclaim = qh; - /* bypass IAA if the hc can't care */ - } else if (!HC_IS_RUNNING (ehci_to_hcd(ehci)->state) && ehci->reclaim) - end_unlink_async (ehci); - - /* something else might have unlinked the qh by now */ - if (qh->qh_state == QH_STATE_LINKED) + /* start IAA cycle */ + } else start_unlink_async (ehci, qh); } @@ -788,13 +838,18 @@ * completions normally happen asynchronously */ -static int ehci_urb_dequeue (struct usb_hcd *hcd, struct urb *urb) +static int ehci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) { struct ehci_hcd *ehci = hcd_to_ehci (hcd); struct ehci_qh *qh; unsigned long flags; + int rc; spin_lock_irqsave (&ehci->lock, flags); + rc = usb_hcd_check_unlink_urb(hcd, urb, status); + if (rc) + goto done; + switch (usb_pipetype (urb->pipe)) { // case PIPE_CONTROL: // case PIPE_BULK: @@ -802,7 +857,19 @@ qh = (struct ehci_qh *) urb->hcpriv; if (!qh) break; - unlink_async (ehci, qh); + switch (qh->qh_state) { + case QH_STATE_LINKED: + case QH_STATE_COMPLETING: + unlink_async(ehci, qh); + break; + case QH_STATE_UNLINK: + case QH_STATE_UNLINK_WAIT: + /* already started */ + break; + case QH_STATE_IDLE: + WARN_ON(1); + break; + } break; case PIPE_INTERRUPT: @@ -825,18 +892,18 @@ /* reschedule QH iff another request is queued */ if (!list_empty (&qh->qtd_list) && HC_IS_RUNNING (hcd->state)) { - int status; - - status = qh_schedule (ehci, qh); - spin_unlock_irqrestore (&ehci->lock, flags); + rc = qh_schedule(ehci, qh); - if (status != 0) { - // shouldn't happen often, but ... - // FIXME kill those tds' urbs - err ("can't reschedule qh %p, err %d", - qh, status); - } - return status; + /* An error here likely indicates handshake failure + * or no space left in the schedule. Neither fault + * should happen often ... + * + * FIXME kill the now-dysfunctional queued urbs + */ + if (rc != 0) + ehci_err(ehci, + "can't reschedule qh %p, err %d", + qh, rc); } break; @@ -849,7 +916,7 @@ } done: spin_unlock_irqrestore (&ehci->lock, flags); - return 0; + return rc; } /*-------------------------------------------------------------------------*/ @@ -894,6 +961,7 @@ unlink_async (ehci, qh); /* FALL THROUGH */ case QH_STATE_UNLINK: /* wait for hw to finish? */ + case QH_STATE_UNLINK_WAIT: idle_timeout: spin_unlock_irqrestore (&ehci->lock, flags); schedule_timeout_uninterruptible(1); @@ -955,13 +1023,36 @@ #define PS3_SYSTEM_BUS_DRIVER ps3_ehci_driver #endif -#ifdef CONFIG_440EPX +#if defined(CONFIG_440EPX) && !defined(CONFIG_PPC_MERGE) #include "ehci-ppc-soc.c" #define PLATFORM_DRIVER ehci_ppc_soc_driver #endif -#if !defined(PCI_DRIVER) && !defined(PLATFORM_DRIVER) && \ - !defined(PS3_SYSTEM_BUS_DRIVER) +#ifdef CONFIG_USB_EHCI_HCD_PPC_OF +#include "ehci-ppc-of.c" +#define OF_PLATFORM_DRIVER ehci_hcd_ppc_of_driver +#endif + +#ifdef CONFIG_ARCH_ORION +#include "ehci-orion.c" +#define PLATFORM_DRIVER ehci_orion_driver +#endif + +#ifdef CONFIG_ARCH_IXP4XX +#include "ehci-ixp4xx.c" +#define PLATFORM_DRIVER ixp4xx_ehci_driver +#endif + +#ifdef CONFIG_USB_EHCI_HCD_SSB +#include "ehci-ssb.c" +#define SSB_EHCI_DRIVER ssb_ehci_driver +#endif + +#if !defined(PCI_DRIVER) && \ + !defined(PLATFORM_DRIVER) && \ + !defined(PS3_SYSTEM_BUS_DRIVER) && \ + !defined(OF_PLATFORM_DRIVER) && \ + !defined(SSB_EHCI_DRIVER) #error "missing bus glue for ehci-hcd" #endif @@ -974,50 +1065,93 @@ sizeof(struct ehci_qh), sizeof(struct ehci_qtd), sizeof(struct ehci_itd), sizeof(struct ehci_sitd)); +#ifdef DEBUG + ehci_debug_root = debugfs_create_dir("ehci", NULL); + if (!ehci_debug_root) + return -ENOENT; +#endif + #ifdef PLATFORM_DRIVER retval = platform_driver_register(&PLATFORM_DRIVER); if (retval < 0) - return retval; + goto clean0; #endif #ifdef PCI_DRIVER retval = pci_register_driver(&PCI_DRIVER); - if (retval < 0) { -#ifdef PLATFORM_DRIVER - platform_driver_unregister(&PLATFORM_DRIVER); -#endif - return retval; - } + if (retval < 0) + goto clean1; #endif #ifdef PS3_SYSTEM_BUS_DRIVER retval = ps3_ehci_driver_register(&PS3_SYSTEM_BUS_DRIVER); - if (retval < 0) { -#ifdef PLATFORM_DRIVER - platform_driver_unregister(&PLATFORM_DRIVER); -#endif -#ifdef PCI_DRIVER - pci_unregister_driver(&PCI_DRIVER); + if (retval < 0) + goto clean2; #endif - return retval; - } + +#ifdef OF_PLATFORM_DRIVER + retval = of_register_platform_driver(&OF_PLATFORM_DRIVER); + if (retval < 0) + goto clean3; #endif +#ifdef SSB_EHCI_DRIVER + retval = ssb_driver_register(&SSB_EHCI_DRIVER); + if (retval < 0) + goto clean4; +#endif + return retval; + +#ifdef SSB_EHCI_DRIVER + clean4: + ssb_driver_unregister(&SSB_EHCI_DRIVER); +#endif + +#ifdef OF_PLATFORM_DRIVER + /* of_unregister_platform_driver(&OF_PLATFORM_DRIVER); */ +clean3: +#endif +#ifdef PS3_SYSTEM_BUS_DRIVER + ps3_ehci_driver_unregister(&PS3_SYSTEM_BUS_DRIVER); +clean2: +#endif +#ifdef PCI_DRIVER + pci_unregister_driver(&PCI_DRIVER); +clean1: +#endif +#ifdef PLATFORM_DRIVER + platform_driver_unregister(&PLATFORM_DRIVER); +clean0: +#endif +#ifdef DEBUG + debugfs_remove(ehci_debug_root); + ehci_debug_root = NULL; +#endif +#ifdef SSB_EHCI_DRIVER + ssb_driver_unregister(&SSB_EHCI_DRIVER); +#endif + return retval; } module_init(ehci_hcd_init); static void __exit ehci_hcd_cleanup(void) { +#ifdef OF_PLATFORM_DRIVER + of_unregister_platform_driver(&OF_PLATFORM_DRIVER); +#endif #ifdef PLATFORM_DRIVER - platform_driver_unregister(&PLATFORM_DRIVER); + platform_driver_unregister(&PLATFORM_DRIVER); #endif #ifdef PCI_DRIVER - pci_unregister_driver(&PCI_DRIVER); + pci_unregister_driver(&PCI_DRIVER); #endif #ifdef PS3_SYSTEM_BUS_DRIVER ps3_ehci_driver_unregister(&PS3_SYSTEM_BUS_DRIVER); #endif +#ifdef DEBUG + debugfs_remove(ehci_debug_root); +#endif } module_exit(ehci_hcd_cleanup);
_______________________________________________ openwrt-devel mailing list openwrt-devel@lists.openwrt.org http://lists.openwrt.org/cgi-bin/mailman/listinfo/openwrt-devel