From 9a9b1d17ba59b78e4bae67f7a7cf546986a42e7d Mon Sep 17 00:00:00 2001 From: David Vrabel Date: Tue, 6 Jan 2009 17:58:02 +0000 Subject: wusb: return -ENOTCONN when resetting a port with no connected device If reading the device descriptor fails during hub_port_init() fails, then the port is disabled, disconnecting the device. The port is then reset at the start of the next init attempt but there is no device to reset. Signed-off-by: David Vrabel --- drivers/usb/wusbcore/devconnect.c | 1 + drivers/usb/wusbcore/rh.c | 3 +++ 2 files changed, 4 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/wusbcore/devconnect.c b/drivers/usb/wusbcore/devconnect.c index e2e7e4bc846..8e18141bb2e 100644 --- a/drivers/usb/wusbcore/devconnect.c +++ b/drivers/usb/wusbcore/devconnect.c @@ -386,6 +386,7 @@ static void __wusbhc_dev_disconnect(struct wusbhc *wusbhc, | USB_PORT_STAT_LOW_SPEED | USB_PORT_STAT_HIGH_SPEED); port->change |= USB_PORT_STAT_C_CONNECTION | USB_PORT_STAT_C_ENABLE; if (wusb_dev) { + dev_dbg(wusbhc->dev, "disconnecting device from port %d\n", wusb_dev->port_idx); if (!list_empty(&wusb_dev->cack_node)) list_del_init(&wusb_dev->cack_node); /* For the one in cack_add() */ diff --git a/drivers/usb/wusbcore/rh.c b/drivers/usb/wusbcore/rh.c index 95c6fa3bf6b..407a9fcc89c 100644 --- a/drivers/usb/wusbcore/rh.c +++ b/drivers/usb/wusbcore/rh.c @@ -100,6 +100,9 @@ static int wusbhc_rh_port_reset(struct wusbhc *wusbhc, u8 port_idx) struct wusb_port *port = wusb_port_by_idx(wusbhc, port_idx); struct wusb_dev *wusb_dev = port->wusb_dev; + if (wusb_dev == NULL) + return -ENOTCONN; + port->status |= USB_PORT_STAT_RESET; port->change |= USB_PORT_STAT_C_RESET; -- cgit v1.2.3 From a5e6ced58d423cb09c4fc0087dcfdb0b5deb5e1c Mon Sep 17 00:00:00 2001 From: David Vrabel Date: Wed, 7 Jan 2009 10:54:22 +0000 Subject: wusb: timeout when waiting for ASL/PZL updates in whci-hcd Timeout if an ASL or PZL update doesn't not complete and reset the hardware. Signed-off-by: David Vrabel --- drivers/usb/host/whci/asl.c | 9 +++++++-- drivers/usb/host/whci/hw.c | 15 +++++++++++++++ drivers/usb/host/whci/pzl.c | 9 +++++++-- drivers/usb/host/whci/whcd.h | 1 + 4 files changed, 30 insertions(+), 4 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/whci/asl.c b/drivers/usb/host/whci/asl.c index 577c0d29849..2291c5f5af5 100644 --- a/drivers/usb/host/whci/asl.c +++ b/drivers/usb/host/whci/asl.c @@ -170,12 +170,17 @@ void asl_stop(struct whc *whc) void asl_update(struct whc *whc, uint32_t wusbcmd) { struct wusbhc *wusbhc = &whc->wusbhc; + long t; mutex_lock(&wusbhc->mutex); if (wusbhc->active) { whc_write_wusbcmd(whc, wusbcmd, wusbcmd); - wait_event(whc->async_list_wq, - (le_readl(whc->base + WUSBCMD) & WUSBCMD_ASYNC_UPDATED) == 0); + t = wait_event_timeout( + whc->async_list_wq, + (le_readl(whc->base + WUSBCMD) & WUSBCMD_ASYNC_UPDATED) == 0, + msecs_to_jiffies(1000)); + if (t == 0) + whc_hw_error(whc, "ASL update timeout"); } mutex_unlock(&wusbhc->mutex); } diff --git a/drivers/usb/host/whci/hw.c b/drivers/usb/host/whci/hw.c index d498e720321..6afa2e37916 100644 --- a/drivers/usb/host/whci/hw.c +++ b/drivers/usb/host/whci/hw.c @@ -87,3 +87,18 @@ out: return ret; } + +/** + * whc_hw_error - recover from a hardware error + * @whc: the WHCI HC that broke. + * @reason: a description of the failure. + * + * Recover from broken hardware with a full reset. + */ +void whc_hw_error(struct whc *whc, const char *reason) +{ + struct wusbhc *wusbhc = &whc->wusbhc; + + dev_err(&whc->umc->dev, "hardware error: %s\n", reason); + wusbhc_reset_all(wusbhc); +} diff --git a/drivers/usb/host/whci/pzl.c b/drivers/usb/host/whci/pzl.c index 2ae5abf69a6..7dc85a0bee7 100644 --- a/drivers/usb/host/whci/pzl.c +++ b/drivers/usb/host/whci/pzl.c @@ -183,12 +183,17 @@ void pzl_stop(struct whc *whc) void pzl_update(struct whc *whc, uint32_t wusbcmd) { struct wusbhc *wusbhc = &whc->wusbhc; + long t; mutex_lock(&wusbhc->mutex); if (wusbhc->active) { whc_write_wusbcmd(whc, wusbcmd, wusbcmd); - wait_event(whc->periodic_list_wq, - (le_readl(whc->base + WUSBCMD) & WUSBCMD_PERIODIC_UPDATED) == 0); + t = wait_event_timeout( + whc->periodic_list_wq, + (le_readl(whc->base + WUSBCMD) & WUSBCMD_PERIODIC_UPDATED) == 0, + msecs_to_jiffies(1000)); + if (t == 0) + whc_hw_error(whc, "PZL update timeout"); } mutex_unlock(&wusbhc->mutex); } diff --git a/drivers/usb/host/whci/whcd.h b/drivers/usb/host/whci/whcd.h index 0f3540f04f5..d3543a181dc 100644 --- a/drivers/usb/host/whci/whcd.h +++ b/drivers/usb/host/whci/whcd.h @@ -137,6 +137,7 @@ void whc_clean_up(struct whc *whc); /* hw.c */ void whc_write_wusbcmd(struct whc *whc, u32 mask, u32 val); int whc_do_gencmd(struct whc *whc, u32 cmd, u32 params, void *addr, size_t len); +void whc_hw_error(struct whc *whc, const char *reason); /* wusb.c */ int whc_wusbhc_start(struct wusbhc *wusbhc); -- cgit v1.2.3 From 953a7e8476bbd7367cebdb868c326ba42968bc13 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 18 Jan 2009 12:52:38 +0000 Subject: [ARM] omap: ensure OMAP drivers pass a struct device to clk_get() Signed-off-by: Russell King --- drivers/usb/host/ohci-omap.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/ohci-omap.c b/drivers/usb/host/ohci-omap.c index 4bbddb73abd..f3aaba35e91 100644 --- a/drivers/usb/host/ohci-omap.c +++ b/drivers/usb/host/ohci-omap.c @@ -315,14 +315,14 @@ static int usb_hcd_omap_probe (const struct hc_driver *driver, return -ENODEV; } - usb_host_ck = clk_get(0, "usb_hhc_ck"); + usb_host_ck = clk_get(&pdev->dev, "usb_hhc_ck"); if (IS_ERR(usb_host_ck)) return PTR_ERR(usb_host_ck); if (!cpu_is_omap15xx()) - usb_dc_ck = clk_get(0, "usb_dc_ck"); + usb_dc_ck = clk_get(&pdev->dev, "usb_dc_ck"); else - usb_dc_ck = clk_get(0, "lb_ck"); + usb_dc_ck = clk_get(&pdev->dev, "lb_ck"); if (IS_ERR(usb_dc_ck)) { clk_put(usb_host_ck); -- cgit v1.2.3 From 95bec45d2051227ef037f1080d7cef003b88d852 Mon Sep 17 00:00:00 2001 From: Wolfgang Glas Date: Thu, 15 Jan 2009 23:11:53 +0100 Subject: USB: cp2101: add fasttrax GPS evaluation kit vendor/product ID This adds the vendor/product ID of the fasttrax GPS evaluation kit from: http://www.fastraxgps.com/products/evaluationtools/evaluationkit/ to the cp2101 module since this device is actually equipped with a CP210x USB to serial bridge. The vendor/product ID is: 0x10c4/0x826b. Signed-off-by: Wolfgang Glas Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/cp2101.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/cp2101.c b/drivers/usb/serial/cp2101.c index cfaf1f08553..e97eb054e38 100644 --- a/drivers/usb/serial/cp2101.c +++ b/drivers/usb/serial/cp2101.c @@ -85,6 +85,7 @@ static struct usb_device_id id_table [] = { { USB_DEVICE(0x10C4, 0x81E2) }, /* Lipowsky Industrie Elektronik GmbH, Baby-LIN */ { USB_DEVICE(0x10C4, 0x81E7) }, /* Aerocomm Radio */ { USB_DEVICE(0x10C4, 0x8218) }, /* Lipowsky Industrie Elektronik GmbH, HARP-1 */ + { USB_DEVICE(0x10C4, 0x826B) }, /* Cygnal Integrated Products, Inc., Fasttrax GPS demostration module */ { USB_DEVICE(0x10c4, 0x8293) }, /* Telegesys ETRX2USB */ { USB_DEVICE(0x10C4, 0x8341) }, /* Siemens MC35PU GPRS Modem */ { USB_DEVICE(0x10C4, 0x83A8) }, /* Amber Wireless AMB2560 */ -- cgit v1.2.3 From ddeac4e75f2527a340f9dc655bde49bb2429b39b Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Thu, 15 Jan 2009 17:03:33 -0500 Subject: USB: fix toggle mismatch in disable_endpoint paths This patch (as1200) finishes some fixes that were left incomplete by an earlier patch. Although nobody has addressed this issue in the past, it turns out that we need to distinguish between two different modes of disabling and enabling endpoints. In one mode only the data structures in usbcore are affected, and in the other mode the host controller and device hardware states are affected as well. The earlier patch added an extra argument to the routines in the enable_endpoint pathways to reflect this difference. This patch adds corresponding arguments to the disable_endpoint pathways. Without this change, the endpoint toggle state can get out of sync between the host and the device. The exact mechanism depends on the details of the host controller (whether or not it stores its own copy of the toggle values). Signed-off-by: Alan Stern Reported-by: Dan Streetman Tested-by: Dan Streetman Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/driver.c | 2 +- drivers/usb/core/hub.c | 4 ++-- drivers/usb/core/message.c | 40 ++++++++++++++++++++++++---------------- drivers/usb/core/usb.h | 5 +++-- 4 files changed, 30 insertions(+), 21 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 98760553bc9..d0a21a5f820 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -284,7 +284,7 @@ static int usb_unbind_interface(struct device *dev) * supports "soft" unbinding. */ if (!driver->soft_unbind) - usb_disable_interface(udev, intf); + usb_disable_interface(udev, intf, false); driver->disconnect(intf); usb_cancel_queued_reset(intf); diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 94d5ee263c2..cd50d86029e 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -2382,8 +2382,8 @@ static int hub_port_debounce(struct usb_hub *hub, int port1) void usb_ep0_reinit(struct usb_device *udev) { - usb_disable_endpoint(udev, 0 + USB_DIR_IN); - usb_disable_endpoint(udev, 0 + USB_DIR_OUT); + usb_disable_endpoint(udev, 0 + USB_DIR_IN, true); + usb_disable_endpoint(udev, 0 + USB_DIR_OUT, true); usb_enable_endpoint(udev, &udev->ep0, true); } EXPORT_SYMBOL_GPL(usb_ep0_reinit); diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index de51667dd64..31fb204f44c 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -1039,14 +1039,15 @@ static void remove_intf_ep_devs(struct usb_interface *intf) * @dev: the device whose endpoint is being disabled * @epaddr: the endpoint's address. Endpoint number for output, * endpoint number + USB_DIR_IN for input + * @reset_hardware: flag to erase any endpoint state stored in the + * controller hardware * - * Deallocates hcd/hardware state for this endpoint ... and nukes all - * pending urbs. - * - * If the HCD hasn't registered a disable() function, this sets the - * endpoint's maxpacket size to 0 to prevent further submissions. + * Disables the endpoint for URB submission and nukes all pending URBs. + * If @reset_hardware is set then also deallocates hcd/hardware state + * for the endpoint. */ -void usb_disable_endpoint(struct usb_device *dev, unsigned int epaddr) +void usb_disable_endpoint(struct usb_device *dev, unsigned int epaddr, + bool reset_hardware) { unsigned int epnum = epaddr & USB_ENDPOINT_NUMBER_MASK; struct usb_host_endpoint *ep; @@ -1056,15 +1057,18 @@ void usb_disable_endpoint(struct usb_device *dev, unsigned int epaddr) if (usb_endpoint_out(epaddr)) { ep = dev->ep_out[epnum]; - dev->ep_out[epnum] = NULL; + if (reset_hardware) + dev->ep_out[epnum] = NULL; } else { ep = dev->ep_in[epnum]; - dev->ep_in[epnum] = NULL; + if (reset_hardware) + dev->ep_in[epnum] = NULL; } if (ep) { ep->enabled = 0; usb_hcd_flush_endpoint(dev, ep); - usb_hcd_disable_endpoint(dev, ep); + if (reset_hardware) + usb_hcd_disable_endpoint(dev, ep); } } @@ -1072,17 +1076,21 @@ void usb_disable_endpoint(struct usb_device *dev, unsigned int epaddr) * usb_disable_interface -- Disable all endpoints for an interface * @dev: the device whose interface is being disabled * @intf: pointer to the interface descriptor + * @reset_hardware: flag to erase any endpoint state stored in the + * controller hardware * * Disables all the endpoints for the interface's current altsetting. */ -void usb_disable_interface(struct usb_device *dev, struct usb_interface *intf) +void usb_disable_interface(struct usb_device *dev, struct usb_interface *intf, + bool reset_hardware) { struct usb_host_interface *alt = intf->cur_altsetting; int i; for (i = 0; i < alt->desc.bNumEndpoints; ++i) { usb_disable_endpoint(dev, - alt->endpoint[i].desc.bEndpointAddress); + alt->endpoint[i].desc.bEndpointAddress, + reset_hardware); } } @@ -1103,8 +1111,8 @@ void usb_disable_device(struct usb_device *dev, int skip_ep0) dev_dbg(&dev->dev, "%s nuking %s URBs\n", __func__, skip_ep0 ? "non-ep0" : "all"); for (i = skip_ep0; i < 16; ++i) { - usb_disable_endpoint(dev, i); - usb_disable_endpoint(dev, i + USB_DIR_IN); + usb_disable_endpoint(dev, i, true); + usb_disable_endpoint(dev, i + USB_DIR_IN, true); } dev->toggle[0] = dev->toggle[1] = 0; @@ -1274,7 +1282,7 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate) remove_intf_ep_devs(iface); usb_remove_sysfs_intf_files(iface); } - usb_disable_interface(dev, iface); + usb_disable_interface(dev, iface, true); iface->cur_altsetting = alt; @@ -1353,8 +1361,8 @@ int usb_reset_configuration(struct usb_device *dev) */ for (i = 1; i < 16; ++i) { - usb_disable_endpoint(dev, i); - usb_disable_endpoint(dev, i + USB_DIR_IN); + usb_disable_endpoint(dev, i, true); + usb_disable_endpoint(dev, i + USB_DIR_IN, true); } config = dev->actconfig; diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index 386177867a8..9d0f33fe871 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -15,9 +15,10 @@ extern void usb_enable_endpoint(struct usb_device *dev, struct usb_host_endpoint *ep, bool reset_toggle); extern void usb_enable_interface(struct usb_device *dev, struct usb_interface *intf, bool reset_toggles); -extern void usb_disable_endpoint(struct usb_device *dev, unsigned int epaddr); +extern void usb_disable_endpoint(struct usb_device *dev, unsigned int epaddr, + bool reset_hardware); extern void usb_disable_interface(struct usb_device *dev, - struct usb_interface *intf); + struct usb_interface *intf, bool reset_hardware); extern void usb_release_interface_cache(struct kref *ref); extern void usb_disable_device(struct usb_device *dev, int skip_ep0); extern int usb_deauthorize_device(struct usb_device *); -- cgit v1.2.3 From b90de8aea36ae6fe8050a6e91b031369c4f251b2 Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Wed, 14 Jan 2009 16:17:19 +0100 Subject: USB: storage: add unusual devs entry This adds an unusual devs entry for 2116:0320 Signed-off-by: Oliver Neukum Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/unusual_devs.h | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index a7f9513fa19..ec40c26bd54 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -2041,6 +2041,12 @@ UNUSUAL_DEV( 0x19d2, 0x2000, 0x0000, 0x0000, US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_IGNORE_DEVICE), +UNUSUAL_DEV( 0x2116, 0x0320, 0x0001, 0x0001, + "ST", + "2A", + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_FIX_CAPACITY), + /* patch submitted by Davide Perini * and Renato Perini */ -- cgit v1.2.3 From bcca06efea883bdf3803a0bb0ffa60f26730387d Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 13 Jan 2009 11:35:54 -0500 Subject: USB: don't enable wakeup by default for PCI host controllers This patch (as1199) changes the initial wakeup settings for PCI USB host controllers. The controllers are marked as capable of waking the system, but wakeup is not enabled by default. It turns out that enabling wakeup for USB host controllers has a lot of bad consequences. As the simplest example, if a USB mouse or keyboard is unplugged immediately after the computer is put to sleep, the unplug will cause the system to wake back up again! We are better off marking them as wakeup-capable and leaving wakeup disabled. Signed-off-by: Alan Stern Reported-by: Rafael J. Wysocki CC: David Brownell Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hcd-pci.c | 1 - drivers/usb/host/ehci-pci.c | 2 +- drivers/usb/host/ohci-hcd.c | 8 +++----- 3 files changed, 4 insertions(+), 7 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c index 507741ed448..99432785f43 100644 --- a/drivers/usb/core/hcd-pci.c +++ b/drivers/usb/core/hcd-pci.c @@ -128,7 +128,6 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) } pci_set_master(dev); - device_set_wakeup_enable(&dev->dev, 1); retval = usb_add_hcd(hcd, dev->irq, IRQF_DISABLED | IRQF_SHARED); if (retval != 0) diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c index bdc6e86e1f8..9faa5c8fe02 100644 --- a/drivers/usb/host/ehci-pci.c +++ b/drivers/usb/host/ehci-pci.c @@ -230,7 +230,7 @@ static int ehci_pci_setup(struct usb_hcd *hcd) pci_read_config_word(pdev, 0x62, &port_wake); if (port_wake & 0x0001) { dev_warn(&pdev->dev, "Enabling legacy PCI PM\n"); - device_init_wakeup(&pdev->dev, 1); + device_set_wakeup_capable(&pdev->dev, 1); } } diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index 65a9609f4ad..5cf5f1eca4f 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -593,12 +593,10 @@ static int ohci_run (struct ohci_hcd *ohci) * to be checked in case boot firmware (BIOS/SMM/...) has set up * wakeup in a way the bus isn't aware of (e.g., legacy PCI PM). * If the bus glue detected wakeup capability then it should - * already be enabled. Either way, if wakeup should be enabled - * but isn't, we'll enable it now. + * already be enabled; if so we'll just enable it again. */ - if ((ohci->hc_control & OHCI_CTRL_RWC) != 0 - && !device_can_wakeup(hcd->self.controller)) - device_init_wakeup(hcd->self.controller, 1); + if ((ohci->hc_control & OHCI_CTRL_RWC) != 0) + device_set_wakeup_capable(hcd->self.controller, 1); switch (ohci->hc_control & OHCI_CTRL_HCFS) { case OHCI_USB_OPER: -- cgit v1.2.3 From a15d95a003fae154121733f049dd25e9c13dbef3 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 20 Jan 2009 01:26:56 +0100 Subject: USB: Fix suspend-resume of PCI USB controllers Commit a0d4922da2e4ccb0973095d8d29f36f6b1b5f703 (USB: fix up suspend and resume for PCI host controllers) attempted to fix the suspend-resume of PCI USB controllers, but unfortunately it did that incorrectly and interrupts are left enabled by the USB controllers' ->suspend_late() callback as a result. This leads to serious problems during suspend which are very difficult to debug. Fix the issue by removing the ->suspend_late() callback of PCI USB controllers and moving the code from there to the ->suspend() callback executed with interrupts enabled. Additionally, make the ->resume() callback of PCI USB controllers execute pci_enable_wake(dev, PCI_D0, false) to disable wake-up from the full power state (PCI_D0). Signed-off-by: Rafael J. Wysocki Tested-by: Andrey Borzenkov Tested-by: "Jeff Chua" Cc: Alan Stern Cc: Andrew Morton Cc: Christian Borntraeger Cc: "Zdenek Kabelac" Cc: Ingo Molnar Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hcd-pci.c | 116 +++++++++++--------------------------------- drivers/usb/core/hcd.h | 1 - drivers/usb/host/ehci-pci.c | 1 - drivers/usb/host/ohci-pci.c | 1 - drivers/usb/host/uhci-hcd.c | 1 - 5 files changed, 27 insertions(+), 93 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c index 99432785f43..c54fc40458b 100644 --- a/drivers/usb/core/hcd-pci.c +++ b/drivers/usb/core/hcd-pci.c @@ -200,6 +200,7 @@ int usb_hcd_pci_suspend(struct pci_dev *dev, pm_message_t message) struct usb_hcd *hcd = pci_get_drvdata(dev); int retval = 0; int wake, w; + int has_pci_pm; /* Root hub suspend should have stopped all downstream traffic, * and all bus master traffic. And done so for both the interface @@ -229,6 +230,15 @@ int usb_hcd_pci_suspend(struct pci_dev *dev, pm_message_t message) synchronize_irq(dev->irq); + /* Downstream ports from this root hub should already be quiesced, so + * there will be no DMA activity. Now we can shut down the upstream + * link (except maybe for PME# resume signaling) and enter some PCI + * low power state, if the hardware allows. + */ + pci_disable_device(dev); + + pci_save_state(dev); + /* Don't fail on error to enable wakeup. We rely on pci code * to reject requests the hardware can't implement, rather * than coding the same thing. @@ -240,35 +250,6 @@ int usb_hcd_pci_suspend(struct pci_dev *dev, pm_message_t message) wake = w; dev_dbg(&dev->dev, "wakeup: %d\n", wake); - /* Downstream ports from this root hub should already be quiesced, so - * there will be no DMA activity. Now we can shut down the upstream - * link (except maybe for PME# resume signaling) and enter some PCI - * low power state, if the hardware allows. - */ - pci_disable_device(dev); - done: - return retval; -} -EXPORT_SYMBOL_GPL(usb_hcd_pci_suspend); - -/** - * usb_hcd_pci_suspend_late - suspend a PCI-based HCD after IRQs are disabled - * @dev: USB Host Controller being suspended - * @message: Power Management message describing this state transition - * - * Store this function in the HCD's struct pci_driver as .suspend_late. - */ -int usb_hcd_pci_suspend_late(struct pci_dev *dev, pm_message_t message) -{ - int retval = 0; - int has_pci_pm; - - /* We might already be suspended (runtime PM -- not yet written) */ - if (dev->current_state != PCI_D0) - goto done; - - pci_save_state(dev); - /* Don't change state if we don't need to */ if (message.event == PM_EVENT_FREEZE || message.event == PM_EVENT_PRETHAW) { @@ -314,7 +295,7 @@ int usb_hcd_pci_suspend_late(struct pci_dev *dev, pm_message_t message) done: return retval; } -EXPORT_SYMBOL_GPL(usb_hcd_pci_suspend_late); +EXPORT_SYMBOL_GPL(usb_hcd_pci_suspend); /** * usb_hcd_pci_resume_early - resume a PCI-based HCD before IRQs are enabled @@ -324,65 +305,8 @@ EXPORT_SYMBOL_GPL(usb_hcd_pci_suspend_late); */ int usb_hcd_pci_resume_early(struct pci_dev *dev) { - int retval = 0; - pci_power_t state = dev->current_state; - -#ifdef CONFIG_PPC_PMAC - /* Reenable ASIC clocks for USB */ - if (machine_is(powermac)) { - struct device_node *of_node; - - of_node = pci_device_to_OF_node(dev); - if (of_node) - pmac_call_feature(PMAC_FTR_USB_ENABLE, - of_node, 0, 1); - } -#endif - - /* NOTE: chip docs cover clean "real suspend" cases (what Linux - * calls "standby", "suspend to RAM", and so on). There are also - * dirty cases when swsusp fakes a suspend in "shutdown" mode. - */ - if (state != PCI_D0) { -#ifdef DEBUG - int pci_pm; - u16 pmcr; - - pci_pm = pci_find_capability(dev, PCI_CAP_ID_PM); - pci_read_config_word(dev, pci_pm + PCI_PM_CTRL, &pmcr); - pmcr &= PCI_PM_CTRL_STATE_MASK; - if (pmcr) { - /* Clean case: power to USB and to HC registers was - * maintained; remote wakeup is easy. - */ - dev_dbg(&dev->dev, "resume from PCI D%d\n", pmcr); - } else { - /* Clean: HC lost Vcc power, D0 uninitialized - * + Vaux may have preserved port and transceiver - * state ... for remote wakeup from D3cold - * + or not; HCD must reinit + re-enumerate - * - * Dirty: D0 semi-initialized cases with swsusp - * + after BIOS init - * + after Linux init (HCD statically linked) - */ - dev_dbg(&dev->dev, "resume from previous PCI D%d\n", - state); - } -#endif - - retval = pci_set_power_state(dev, PCI_D0); - } else { - /* Same basic cases: clean (powered/not), dirty */ - dev_dbg(&dev->dev, "PCI legacy resume\n"); - } - - if (retval < 0) - dev_err(&dev->dev, "can't resume: %d\n", retval); - else - pci_restore_state(dev); - - return retval; + pci_restore_state(dev); + return 0; } EXPORT_SYMBOL_GPL(usb_hcd_pci_resume_early); @@ -397,6 +321,18 @@ int usb_hcd_pci_resume(struct pci_dev *dev) struct usb_hcd *hcd; int retval; +#ifdef CONFIG_PPC_PMAC + /* Reenable ASIC clocks for USB */ + if (machine_is(powermac)) { + struct device_node *of_node; + + of_node = pci_device_to_OF_node(dev); + if (of_node) + pmac_call_feature(PMAC_FTR_USB_ENABLE, + of_node, 0, 1); + } +#endif + hcd = pci_get_drvdata(dev); if (hcd->state != HC_STATE_SUSPENDED) { dev_dbg(hcd->self.controller, @@ -404,6 +340,8 @@ int usb_hcd_pci_resume(struct pci_dev *dev) return 0; } + pci_enable_wake(dev, PCI_D0, false); + retval = pci_enable_device(dev); if (retval < 0) { dev_err(&dev->dev, "can't re-enable after resume, %d!\n", diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h index 572d2cf46e8..5b94a56bec2 100644 --- a/drivers/usb/core/hcd.h +++ b/drivers/usb/core/hcd.h @@ -257,7 +257,6 @@ extern void usb_hcd_pci_remove(struct pci_dev *dev); #ifdef CONFIG_PM extern int usb_hcd_pci_suspend(struct pci_dev *dev, pm_message_t msg); -extern int usb_hcd_pci_suspend_late(struct pci_dev *dev, pm_message_t msg); extern int usb_hcd_pci_resume_early(struct pci_dev *dev); extern int usb_hcd_pci_resume(struct pci_dev *dev); #endif /* CONFIG_PM */ diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c index 9faa5c8fe02..bb21fb0a496 100644 --- a/drivers/usb/host/ehci-pci.c +++ b/drivers/usb/host/ehci-pci.c @@ -432,7 +432,6 @@ static struct pci_driver ehci_pci_driver = { #ifdef CONFIG_PM .suspend = usb_hcd_pci_suspend, - .suspend_late = usb_hcd_pci_suspend_late, .resume_early = usb_hcd_pci_resume_early, .resume = usb_hcd_pci_resume, #endif diff --git a/drivers/usb/host/ohci-pci.c b/drivers/usb/host/ohci-pci.c index 8b28ae7865b..5d625c3fd42 100644 --- a/drivers/usb/host/ohci-pci.c +++ b/drivers/usb/host/ohci-pci.c @@ -487,7 +487,6 @@ static struct pci_driver ohci_pci_driver = { #ifdef CONFIG_PM .suspend = usb_hcd_pci_suspend, - .suspend_late = usb_hcd_pci_suspend_late, .resume_early = usb_hcd_pci_resume_early, .resume = usb_hcd_pci_resume, #endif diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c index 4e221060f58..944f7e0ca4d 100644 --- a/drivers/usb/host/uhci-hcd.c +++ b/drivers/usb/host/uhci-hcd.c @@ -942,7 +942,6 @@ static struct pci_driver uhci_pci_driver = { #ifdef CONFIG_PM .suspend = usb_hcd_pci_suspend, - .suspend_late = usb_hcd_pci_suspend_late, .resume_early = usb_hcd_pci_resume_early, .resume = usb_hcd_pci_resume, #endif /* PM */ -- cgit v1.2.3 From 501950d846218ed80a776d2aae5aed9c8b92e778 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 13 Jan 2009 11:33:42 -0500 Subject: USB: fix char-device disconnect handling This patch (as1198) fixes a conceptual bug: Somewhere along the line we managed to confuse USB class devices with USB char devices. As a result, the code to send a disconnect signal to userspace would not be built if both CONFIG_USB_DEVICE_CLASS and CONFIG_USB_DEVICEFS were disabled. The usb_fs_classdev_common_remove() routine has been renamed to usbdev_remove() and it is now called whenever any USB device is removed, not just when a class device is unregistered. The notifier registration and unregistration calls are no longer conditionally compiled. And since the common removal code will always be called as part of the char device interface, there's no need to call it again as part of the usbfs interface; thus the invocation of usb_fs_classdev_common_remove() has been taken out of usbfs_remove_device(). Signed-off-by: Alan Stern Reported-by: Alon Bar-Lev Tested-by: Alon Bar-Lev Cc: stable --- drivers/usb/core/devio.c | 20 ++++++++++++-------- drivers/usb/core/inode.c | 1 - drivers/usb/core/usb.h | 1 - 3 files changed, 12 insertions(+), 10 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 26fece124e0..7513bb083c1 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -1700,7 +1700,7 @@ const struct file_operations usbdev_file_operations = { .release = usbdev_release, }; -void usb_fs_classdev_common_remove(struct usb_device *udev) +static void usbdev_remove(struct usb_device *udev) { struct dev_state *ps; struct siginfo sinfo; @@ -1742,10 +1742,15 @@ static void usb_classdev_remove(struct usb_device *dev) { if (dev->usb_classdev) device_unregister(dev->usb_classdev); - usb_fs_classdev_common_remove(dev); } -static int usb_classdev_notify(struct notifier_block *self, +#else +#define usb_classdev_add(dev) 0 +#define usb_classdev_remove(dev) do {} while (0) + +#endif + +static int usbdev_notify(struct notifier_block *self, unsigned long action, void *dev) { switch (action) { @@ -1755,15 +1760,15 @@ static int usb_classdev_notify(struct notifier_block *self, break; case USB_DEVICE_REMOVE: usb_classdev_remove(dev); + usbdev_remove(dev); break; } return NOTIFY_OK; } static struct notifier_block usbdev_nb = { - .notifier_call = usb_classdev_notify, + .notifier_call = usbdev_notify, }; -#endif static struct cdev usb_device_cdev; @@ -1798,9 +1803,8 @@ int __init usb_devio_init(void) * to /sys/dev */ usb_classdev_class->dev_kobj = NULL; - - usb_register_notify(&usbdev_nb); #endif + usb_register_notify(&usbdev_nb); out: return retval; @@ -1811,8 +1815,8 @@ error_cdev: void usb_devio_cleanup(void) { -#ifdef CONFIG_USB_DEVICE_CLASS usb_unregister_notify(&usbdev_nb); +#ifdef CONFIG_USB_DEVICE_CLASS class_destroy(usb_classdev_class); #endif cdev_del(&usb_device_cdev); diff --git a/drivers/usb/core/inode.c b/drivers/usb/core/inode.c index 2a129cb7bb5..dff5760a37f 100644 --- a/drivers/usb/core/inode.c +++ b/drivers/usb/core/inode.c @@ -717,7 +717,6 @@ static void usbfs_remove_device(struct usb_device *dev) fs_remove_file (dev->usbfs_dentry); dev->usbfs_dentry = NULL; } - usb_fs_classdev_common_remove(dev); } static int usbfs_notify(struct notifier_block *self, unsigned long action, void *dev) diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index 9d0f33fe871..79d8a9ea559 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -152,7 +152,6 @@ extern struct usb_driver usbfs_driver; extern const struct file_operations usbfs_devices_fops; extern const struct file_operations usbdev_file_operations; extern void usbfs_conn_disc_event(void); -extern void usb_fs_classdev_common_remove(struct usb_device *udev); extern int usb_devio_init(void); extern void usb_devio_cleanup(void); -- cgit v1.2.3 From 2bf5fa13fc8e34d7b86307b99f64a24cb7a83852 Mon Sep 17 00:00:00 2001 From: David Brownell Date: Sat, 24 Jan 2009 17:55:57 -0800 Subject: USB: omap1 ohci buildfix (otg related) > > drivers/built-in.o: In function `ohci_omap_init': > > hid-quirks.c:(.text+0x6c608): undefined reference to `otg_get_transceiver' > > drivers/built-in.o: In function `omap_udc_probe': > > hid-quirks.c:(.init.text+0x34c0): undefined reference to `otg_get_transceiver' > > hid-quirks.c:(.init.text+0x3d40): undefined reference to `otg_put_transceiver' Reported-by: Russell King Signed-off-by: David Brownell Acked-by: Tony Lindgren Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/Kconfig | 1 + drivers/usb/otg/Kconfig | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 2b476b6b3d4..5a988f4cdf8 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -140,6 +140,7 @@ config USB_OHCI_HCD tristate "OHCI HCD support" depends on USB && USB_ARCH_HAS_OHCI select ISP1301_OMAP if MACH_OMAP_H2 || MACH_OMAP_H3 + select USB_OTG_UTILS if ARCH_OMAP ---help--- The Open Host Controller Interface (OHCI) is a standard for accessing USB 1.1 host controller hardware. It does more in hardware than Intel's diff --git a/drivers/usb/otg/Kconfig b/drivers/usb/otg/Kconfig index 8e8dbdb9b39..ee55b449ffd 100644 --- a/drivers/usb/otg/Kconfig +++ b/drivers/usb/otg/Kconfig @@ -6,14 +6,14 @@ comment "OTG and related infrastructure" -if USB || USB_GADGET - config USB_OTG_UTILS bool help Select this to make sure the build includes objects from the OTG infrastructure directory. +if USB || USB_GADGET + # # USB Transceiver Drivers # -- cgit v1.2.3 From 10b4eadef140b09baf8b9ec1df37185e69773275 Mon Sep 17 00:00:00 2001 From: David Brownell Date: Sat, 24 Jan 2009 17:56:17 -0800 Subject: USB: musb davinci buildfix Trying once more to get this merged. The original was submitted for 2.6.27-rc2 or so, and never got correctly merged. Neither were any of the numerous subsequent resends. Sigh. CC drivers/usb/musb/davinci.o drivers/usb/musb/davinci.c:35:32: error: mach/arch/hardware.h: No such file or directory drivers/usb/musb/davinci.c:36:30: error: mach/arch/memory.h: No such file or directory drivers/usb/musb/davinci.c:37:28: error: mach/arch/gpio.h: No such file or directory drivers/usb/musb/davinci.c:373: error: redefinition of 'musb_platform_set_mode' drivers/usb/musb/davinci.c:368: error: previous definition of 'musb_platform_set_mode' was here Signed-off-by: David Brownell Acked-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/davinci.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/musb/davinci.c b/drivers/usb/musb/davinci.c index 0d566dc5ce0..5a8fd5d57a1 100644 --- a/drivers/usb/musb/davinci.c +++ b/drivers/usb/musb/davinci.c @@ -32,9 +32,10 @@ #include #include -#include -#include -#include +#include +#include +#include + #include #include "musb_core.h" @@ -370,12 +371,6 @@ int musb_platform_set_mode(struct musb *musb, u8 mode) return -EIO; } -int musb_platform_set_mode(struct musb *musb, u8 mode) -{ - /* EVM can't do this (right?) */ - return -EIO; -} - int __init musb_platform_init(struct musb *musb) { void __iomem *tibase = musb->ctrl_base; -- cgit v1.2.3 From 37daa925cf0d4dfd2d1d9ca01e2e0d74fba3d64a Mon Sep 17 00:00:00 2001 From: David Brownell Date: Sat, 24 Jan 2009 17:56:25 -0800 Subject: USB: musb_hdrc: another davinci buildfix (otg related) The DaVinci code had an implementation of the OTG transceiver glue too; make it use the new-standard one. Signed-off-by: David Brownell Acked-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/usb') diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig index 5af7379cd9a..e1d81d8de22 100644 --- a/drivers/usb/musb/Kconfig +++ b/drivers/usb/musb/Kconfig @@ -11,6 +11,7 @@ config USB_MUSB_HDRC depends on (USB || USB_GADGET) && HAVE_CLK depends on !SUPERH select TWL4030_USB if MACH_OMAP_3430SDP + select USB_OTG_UTILS tristate 'Inventra Highspeed Dual Role Controller (TI, ADI, ...)' help Say Y here if your system has a dual role high speed USB -- cgit v1.2.3 From 97a39896816489fe9a67c223e782e8dda06f25c9 Mon Sep 17 00:00:00 2001 From: Ajay Kumar Gupta Date: Sat, 24 Jan 2009 17:56:39 -0800 Subject: USB: musb free_irq bugfix Fixes insert module failure as free_irq() was not done in previous rmmod. Signed-off-by: Ajay Kumar Gupta Acked-by: Felipe Balbi Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/musb_core.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 6c7faacfb53..2cc34fa05b7 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -1824,8 +1824,9 @@ static void musb_free(struct musb *musb) musb_gadget_cleanup(musb); #endif - if (musb->nIrq >= 0 && musb->irq_wake) { - disable_irq_wake(musb->nIrq); + if (musb->nIrq >= 0) { + if (musb->irq_wake) + disable_irq_wake(musb->nIrq); free_irq(musb->nIrq, musb); } if (is_dma_capable() && musb->dma_controller) { -- cgit v1.2.3 From af7e0c5f126677fe8e6c4fbea37637b9c0c2fe2a Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Sat, 24 Jan 2009 17:57:15 -0800 Subject: USB: musb: tusb6010 buildfix drivers/usb/musb/tusb6010_omap.c:18:26: error: asm/arch/dma.h: No such file or directory drivers/usb/musb/tusb6010_omap.c:19:26: error: asm/arch/mux.h: No such file or directory Signed-off-by: Kalle Valo Acked-by: Felipe Balbi Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/tusb6010_omap.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/musb/tusb6010_omap.c b/drivers/usb/musb/tusb6010_omap.c index 52f7f29cebd..7e073a0d7ac 100644 --- a/drivers/usb/musb/tusb6010_omap.c +++ b/drivers/usb/musb/tusb6010_omap.c @@ -15,8 +15,8 @@ #include #include #include -#include -#include +#include +#include #include "musb_core.h" -- cgit v1.2.3 From 96bcd090fa434b4369e6e3a9cba937d1e513596d Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sat, 24 Jan 2009 17:57:24 -0800 Subject: USB: musb uses endpoint functions This set of patches introduces calls to the following set of functions: usb_endpoint_dir_in(epd) usb_endpoint_dir_out(epd) usb_endpoint_is_bulk_in(epd) usb_endpoint_is_bulk_out(epd) usb_endpoint_is_int_in(epd) usb_endpoint_is_int_out(epd) usb_endpoint_num(epd) usb_endpoint_type(epd) usb_endpoint_xfer_bulk(epd) usb_endpoint_xfer_control(epd) usb_endpoint_xfer_int(epd) usb_endpoint_xfer_isoc(epd) In some cases, introducing one of these functions is not possible, and it just replaces an explicit integer value by one of the following constants: USB_ENDPOINT_XFER_BULK USB_ENDPOINT_XFER_CONTROL USB_ENDPOINT_XFER_INT USB_ENDPOINT_XFER_ISOC An extract of the semantic patch that makes these changes is as follows: (http://www.emn.fr/x-info/coccinelle/) // @r1@ struct usb_endpoint_descriptor *epd; @@ - ((epd->bmAttributes & \(USB_ENDPOINT_XFERTYPE_MASK\|3\)) == - \(USB_ENDPOINT_XFER_CONTROL\|0\)) + usb_endpoint_xfer_control(epd) @r5@ struct usb_endpoint_descriptor *epd; @@ - ((epd->bEndpointAddress & \(USB_ENDPOINT_DIR_MASK\|0x80\)) == - \(USB_DIR_IN\|0x80\)) + usb_endpoint_dir_in(epd) @inc@ @@ #include @depends on !inc && (r1||r5)@ @@ + #include #include // Signed-off-by: Julia Lawall Acked-by: Felipe Balbi Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/musb_gadget.c | 6 +++--- drivers/usb/musb/musb_host.c | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c index 6197daeab8f..4ea30538798 100644 --- a/drivers/usb/musb/musb_gadget.c +++ b/drivers/usb/musb/musb_gadget.c @@ -874,10 +874,10 @@ static int musb_gadget_enable(struct usb_ep *ep, status = -EBUSY; goto fail; } - musb_ep->type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; + musb_ep->type = usb_endpoint_type(desc); /* check direction and (later) maxpacket size against endpoint */ - if ((desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK) != epnum) + if (usb_endpoint_num(desc) != epnum) goto fail; /* REVISIT this rules out high bandwidth periodic transfers */ @@ -890,7 +890,7 @@ static int musb_gadget_enable(struct usb_ep *ep, * packet size (or fail), set the mode, clear the fifo */ musb_ep_select(mbase, epnum); - if (desc->bEndpointAddress & USB_DIR_IN) { + if (usb_endpoint_dir_in(desc)) { u16 int_txe = musb_readw(mbase, MUSB_INTRTXE); if (hw_ep->is_shared_fifo) diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index 99fa6123487..a035ceccf95 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -1847,8 +1847,8 @@ static int musb_urb_enqueue( goto done; } - qh->epnum = epd->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; - qh->type = epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; + qh->epnum = usb_endpoint_num(epd); + qh->type = usb_endpoint_type(epd); /* NOTE: urb->dev->devnum is wrong during SET_ADDRESS */ qh->addr_reg = (u8) usb_pipedevice(urb->pipe); -- cgit v1.2.3 From 704a14854aaf9758a1248ea36a7d1b8cc42a4b3e Mon Sep 17 00:00:00 2001 From: Hugo Villeneuve Date: Sat, 24 Jan 2009 17:57:30 -0800 Subject: USB: musb cppi bugfixes These compilation errors are related to incorrect debugging macro and variable names and generated the following errors: drivers/usb/musb/cppi_dma.c:437:5: warning: "MUSB_DEBUG" is not defined drivers/usb/musb/cppi_dma.c: In function 'cppi_next_rx_segment': drivers/usb/musb/cppi_dma.c:884: error: 'debug' undeclared (first use in this function) Signed-off-by: Hugo Villeneuve Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/cppi_dma.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/musb/cppi_dma.c b/drivers/usb/musb/cppi_dma.c index 5ad6d0893cb..d8d5345519c 100644 --- a/drivers/usb/musb/cppi_dma.c +++ b/drivers/usb/musb/cppi_dma.c @@ -9,6 +9,7 @@ #include #include "musb_core.h" +#include "musb_debug.h" #include "cppi_dma.h" @@ -423,6 +424,7 @@ cppi_rndis_update(struct cppi_channel *c, int is_rx, } } +#ifdef CONFIG_USB_MUSB_DEBUG static void cppi_dump_rxbd(const char *tag, struct cppi_descriptor *bd) { pr_debug("RXBD/%s %08x: " @@ -431,10 +433,11 @@ static void cppi_dump_rxbd(const char *tag, struct cppi_descriptor *bd) bd->hw_next, bd->hw_bufp, bd->hw_off_len, bd->hw_options); } +#endif static void cppi_dump_rxq(int level, const char *tag, struct cppi_channel *rx) { -#if MUSB_DEBUG > 0 +#ifdef CONFIG_USB_MUSB_DEBUG struct cppi_descriptor *bd; if (!_dbg_level(level)) @@ -881,12 +884,14 @@ cppi_next_rx_segment(struct musb *musb, struct cppi_channel *rx, int onepacket) bd->hw_options |= CPPI_SOP_SET; tail->hw_options |= CPPI_EOP_SET; - if (debug >= 5) { +#ifdef CONFIG_USB_MUSB_DEBUG + if (_dbg_level(5)) { struct cppi_descriptor *d; for (d = rx->head; d; d = d->next) cppi_dump_rxbd("S", d); } +#endif /* in case the preceding transfer left some state... */ tail = rx->last_processed; -- cgit v1.2.3 From 191b776616838f035c2fe7eecc882b5c1f134353 Mon Sep 17 00:00:00 2001 From: Swaminathan S Date: Sat, 24 Jan 2009 17:57:37 -0800 Subject: USB: musb cppi dma fix Initializes the actual_len field to 0 before every DMA transaction. Signed-off-by: Swaminathan S Acked-by: Felipe Balbi Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/cppi_dma.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/usb') diff --git a/drivers/usb/musb/cppi_dma.c b/drivers/usb/musb/cppi_dma.c index d8d5345519c..569ef0fed0f 100644 --- a/drivers/usb/musb/cppi_dma.c +++ b/drivers/usb/musb/cppi_dma.c @@ -995,6 +995,7 @@ static int cppi_channel_program(struct dma_channel *ch, cppi_ch->offset = 0; cppi_ch->maxpacket = maxpacket; cppi_ch->buf_len = len; + cppi_ch->channel.actual_len = 0; /* TX channel? or RX? */ if (cppi_ch->transmit) -- cgit v1.2.3 From cd67435ef985d0d6279803f2ae48b5248a7178df Mon Sep 17 00:00:00 2001 From: David Brownell Date: Mon, 26 Jan 2009 02:05:43 -0800 Subject: USB: musb: Kconfig fix The Blackfin MUSB Kconfig text didn't properly parenthesise its dependencies. This was visible in non-Blackfin configs by the way the user interfaces lost track of dependencies, when doing a bunch of test builds. Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig index e1d81d8de22..9985db08e7d 100644 --- a/drivers/usb/musb/Kconfig +++ b/drivers/usb/musb/Kconfig @@ -50,7 +50,7 @@ comment "OMAP 343x high speed USB support" depends on USB_MUSB_HDRC && ARCH_OMAP34XX comment "Blackfin high speed USB Support" - depends on USB_MUSB_HDRC && (BF54x && !BF544) || (BF52x && !BF522 && !BF523) + depends on USB_MUSB_HDRC && ((BF54x && !BF544) || (BF52x && !BF522 && !BF523)) config USB_TUSB6010 boolean "TUSB 6010 support" -- cgit v1.2.3 From dd4dff8b035f6dda69ece98e20d4c2d76b9f97d1 Mon Sep 17 00:00:00 2001 From: Bryan Wu Date: Thu, 8 Jan 2009 00:21:18 +0800 Subject: USB: composite: Fix bug: should test set_alt function pointer before use it Signed-off-by: Bryan Wu Acked-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/composite.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index f2da0269e1b..363951eb333 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -772,7 +772,7 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) f = cdev->config->interface[w_index]; if (!f) break; - if (w_value && !f->get_alt) + if (w_value && !f->set_alt) break; value = f->set_alt(f, w_index, w_value); break; -- cgit v1.2.3 From 08889517b3713926169d79d99782192e86acdc67 Mon Sep 17 00:00:00 2001 From: Bryan Wu Date: Thu, 8 Jan 2009 00:21:19 +0800 Subject: USB: composite: Fix bug: low byte of w_index is the usb interface number not the whole 2 bytes of w_index In some usb gadget driver, for example usb audio class device, the high byte of w_index is the entity id and low byte is the interface number. If we use the 2 bytes of w_index as the array number, we will get a wrong pointer or NULL pointer. This patch fixes this issue. Signed-off-by: Bryan Wu Acked-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/composite.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index 363951eb333..5d11c291f1a 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -683,6 +683,7 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) struct usb_request *req = cdev->req; int value = -EOPNOTSUPP; u16 w_index = le16_to_cpu(ctrl->wIndex); + u8 intf = w_index & 0xFF; u16 w_value = le16_to_cpu(ctrl->wValue); u16 w_length = le16_to_cpu(ctrl->wLength); struct usb_function *f = NULL; @@ -769,7 +770,7 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) goto unknown; if (!cdev->config || w_index >= MAX_CONFIG_INTERFACES) break; - f = cdev->config->interface[w_index]; + f = cdev->config->interface[intf]; if (!f) break; if (w_value && !f->set_alt) @@ -781,7 +782,7 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) goto unknown; if (!cdev->config || w_index >= MAX_CONFIG_INTERFACES) break; - f = cdev->config->interface[w_index]; + f = cdev->config->interface[intf]; if (!f) break; /* lots of interfaces only need altsetting zero... */ @@ -808,7 +809,7 @@ unknown: */ if ((ctrl->bRequestType & USB_RECIP_MASK) == USB_RECIP_INTERFACE) { - f = cdev->config->interface[w_index]; + f = cdev->config->interface[intf]; if (f && f->setup) value = f->setup(f, ctrl); else -- cgit v1.2.3 From 837d84249611e9462dea6181a7ea30aa64e67d6a Mon Sep 17 00:00:00 2001 From: "James A. Treacy" Date: Sat, 24 Jan 2009 23:37:43 -0500 Subject: USB: cdc-acm: support some gps data loggers Below is a patch which allows a number of GPS loggers to work under linux. It is known to support the i-Blue 747 (all models), i-Blue 757, Qstarz BT-Q1000, i.Trek Z1, Konet BGL-32, and the Holux M-241. From: James A. Treacy Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/cdc-acm.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 00b47ea24f8..5664b4afe56 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -1349,6 +1349,9 @@ static struct usb_device_id acm_ids[] = { { USB_DEVICE(0x0e8d, 0x0003), /* FIREFLY, MediaTek Inc; andrey.arapov@gmail.com */ .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ }, + { USB_DEVICE(0x0e8d, 0x3329), /* i-blue 747, Qstarz BT-Q1000, Holux M-241 */ + .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ + }, { USB_DEVICE(0x0482, 0x0203), /* KYOCERA AH-K3001V */ .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ }, -- cgit v1.2.3 From 0f9c7b4a1cc24d6f05a848f0acf72dbff7c5d42d Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Tue, 23 Dec 2008 17:31:23 +0100 Subject: USB: CDC-ACM quirk for MTK GPS This patch adds a device quirk for a MediaTek Inc GPS chipset. The device implements USB CDC ACM, but is missing the union descriptor, so the ACM class driver fails to probe the device. I've tested this patch with an iBlue A+ GPS which uses this chipset and using kernel 2.6.28-rc9. Signed-off-by: Andrew Lunn, Acked-by: Oliver Neukum Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/cdc-acm.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 5664b4afe56..f2bfae7b398 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -1352,6 +1352,9 @@ static struct usb_device_id acm_ids[] = { { USB_DEVICE(0x0e8d, 0x3329), /* i-blue 747, Qstarz BT-Q1000, Holux M-241 */ .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ }, + { USB_DEVICE(0x0e8d, 0x3329), /* MediaTek Inc GPS */ + .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ + }, { USB_DEVICE(0x0482, 0x0203), /* KYOCERA AH-K3001V */ .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ }, -- cgit v1.2.3 From 296361ec3abbba7621e9fff01a572ac0873da903 Mon Sep 17 00:00:00 2001 From: sware Date: Wed, 7 Jan 2009 15:35:55 -0800 Subject: USB: remove vernier labpro from ldusb Labpro device is in both ldusb and vstusb device tables. Should only be a vstusb device. Signed-off-by: stephen ware Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/ldusb.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/misc/ldusb.c b/drivers/usb/misc/ldusb.c index 189a9db0350..ad4fb15b5dc 100644 --- a/drivers/usb/misc/ldusb.c +++ b/drivers/usb/misc/ldusb.c @@ -57,7 +57,6 @@ #define USB_DEVICE_ID_LD_MACHINETEST 0x2040 /* USB Product ID of Machine Test System */ #define USB_VENDOR_ID_VERNIER 0x08f7 -#define USB_DEVICE_ID_VERNIER_LABPRO 0x0001 #define USB_DEVICE_ID_VERNIER_GOTEMP 0x0002 #define USB_DEVICE_ID_VERNIER_SKIP 0x0003 #define USB_DEVICE_ID_VERNIER_CYCLOPS 0x0004 @@ -85,7 +84,6 @@ static struct usb_device_id ld_usb_table [] = { { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_NETWORKANALYSER) }, { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_POWERCONTROL) }, { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MACHINETEST) }, - { USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_LABPRO) }, { USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_GOTEMP) }, { USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_SKIP) }, { USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_CYCLOPS) }, -- cgit v1.2.3 From 06a743bfc42660f27fde5f24d7471e1eb4c71218 Mon Sep 17 00:00:00 2001 From: Brandon Philips Date: Mon, 5 Jan 2009 08:30:39 -0800 Subject: USB: usblp.c: add USBLP_QUIRK_BIDIR to Brother HL-1440 My Brother HL-1440 would print one document before CUPS would stop printing with the error "Printer not connected; will retry in 30 seconds...". I traced this down to the CUPS usb backend getting an EIO out of usblp on the IOCNR_GET_DEVICE_ID IOCTL. Adding the USBLP_QUIRK_BIDIR fixes the problem but is it the right solution? output from strace /usr/lib/cups/backend/usb after printing a document (Note: SNDCTL_DSP_SYNC == IOCNR_GET_DEVICE_ID): before patch open("/dev/usb/lp0", O_RDWR|O_EXCL) = 3 ioctl(3, SNDCTL_DSP_SYNC, 0x7fff2478cef0) = -1 EIO (Input/output error) after patch open("/dev/usb/lp0", O_RDWR|O_EXCL) = 3 ioctl(3, SNDCTL_DSP_SYNC, 0x7fffb8d474c0) = 0 Possibly related bug: https://bugs.launchpad.net/ubuntu/+source/cupsys/+bug/35638 Signed-off-by: Brandon Philips Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/usblp.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/usb') diff --git a/drivers/usb/class/usblp.c b/drivers/usb/class/usblp.c index b5775af3ba2..3f3ee135193 100644 --- a/drivers/usb/class/usblp.c +++ b/drivers/usb/class/usblp.c @@ -226,6 +226,7 @@ static const struct quirk_printer_struct quirk_printers[] = { { 0x0409, 0xf0be, USBLP_QUIRK_BIDIR }, /* NEC Picty920 (HP OEM) */ { 0x0409, 0xf1be, USBLP_QUIRK_BIDIR }, /* NEC Picty800 (HP OEM) */ { 0x0482, 0x0010, USBLP_QUIRK_BIDIR }, /* Kyocera Mita FS 820, by zut */ + { 0x04f9, 0x000d, USBLP_QUIRK_BIDIR }, /* Brother Industries, Ltd HL-1440 Laser Printer */ { 0x04b8, 0x0202, USBLP_QUIRK_BAD_CLASS }, /* Seiko Epson Receipt Printer M129C */ { 0, 0 } }; -- cgit v1.2.3 From 877e262c4e251352771cc391760a12665b5b210b Mon Sep 17 00:00:00 2001 From: Tomasz K Date: Sun, 4 Jan 2009 12:47:11 +0100 Subject: USB: cp2101 device My girl use modem GSM (EDGE) Commanader 2 on iPlus Polsih provider, PLEASE add this vendor=0x10C4 and product=0x822B to USB serial driver cp2101.c From: Tomasz K Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/cp2101.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/cp2101.c b/drivers/usb/serial/cp2101.c index e97eb054e38..027f4b7dde8 100644 --- a/drivers/usb/serial/cp2101.c +++ b/drivers/usb/serial/cp2101.c @@ -85,6 +85,7 @@ static struct usb_device_id id_table [] = { { USB_DEVICE(0x10C4, 0x81E2) }, /* Lipowsky Industrie Elektronik GmbH, Baby-LIN */ { USB_DEVICE(0x10C4, 0x81E7) }, /* Aerocomm Radio */ { USB_DEVICE(0x10C4, 0x8218) }, /* Lipowsky Industrie Elektronik GmbH, HARP-1 */ + { USB_DEVICE(0x10C4, 0x822B) }, /* Modem EDGE(GSM) Comander 2 */ { USB_DEVICE(0x10C4, 0x826B) }, /* Cygnal Integrated Products, Inc., Fasttrax GPS demostration module */ { USB_DEVICE(0x10c4, 0x8293) }, /* Telegesys ETRX2USB */ { USB_DEVICE(0x10C4, 0x8341) }, /* Siemens MC35PU GPRS Modem */ -- cgit v1.2.3 From 45eeff848bdfac96dc77aa722dda7c6cee6184f4 Mon Sep 17 00:00:00 2001 From: Robie Basak Date: Mon, 12 Jan 2009 23:05:59 +0000 Subject: USB: ftdi_sio: added Alti-2 VID and Neptune 3 PID This patch adds the vendor and product ID for the Alti-2 Neptune 3 (http://www.alti-2.com) which uses the FTDI chip. Signed-off-by: Robie Basak Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/ftdi_sio.c | 1 + drivers/usb/serial/ftdi_sio.h | 4 ++++ 2 files changed, 5 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index c70a8f667d8..a85946b212d 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -660,6 +660,7 @@ static struct usb_device_id id_table_combined [] = { { USB_DEVICE(PAPOUCH_VID, PAPOUCH_QUIDO4x4_PID) }, { USB_DEVICE(FTDI_VID, FTDI_DOMINTELL_DGQG_PID) }, { USB_DEVICE(FTDI_VID, FTDI_DOMINTELL_DUSB_PID) }, + { USB_DEVICE(ALTI2_VID, ALTI2_N3_PID) }, { }, /* Optional parameter entry */ { } /* Terminating entry */ }; diff --git a/drivers/usb/serial/ftdi_sio.h b/drivers/usb/serial/ftdi_sio.h index 373ee09975b..e4305765198 100644 --- a/drivers/usb/serial/ftdi_sio.h +++ b/drivers/usb/serial/ftdi_sio.h @@ -854,6 +854,10 @@ #define FTDI_DOMINTELL_DGQG_PID 0xEF50 /* Master */ #define FTDI_DOMINTELL_DUSB_PID 0xEF51 /* DUSB01 module */ +/* Alti-2 products http://www.alti-2.com */ +#define ALTI2_VID 0x1BC9 +#define ALTI2_N3_PID 0x6001 /* Neptune 3 */ + /* Commands */ #define FTDI_SIO_RESET 0 /* Reset the port */ #define FTDI_SIO_MODEM_CTRL 1 /* Set the modem control register */ -- cgit v1.2.3 From ca80801bfb24f7a41fe4fade4d2cf7c73f0b2f09 Mon Sep 17 00:00:00 2001 From: Mhayk Whandson Date: Fri, 9 Jan 2009 06:48:16 -0400 Subject: USB: ftdi_sio driver support of bar code scanner from Diebold Added the product id of bcs(bar code scanner) from Diebold Procomp Brazil. Signed-off-by: Mhayk Whandson Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/ftdi_sio.c | 1 + drivers/usb/serial/ftdi_sio.h | 5 +++++ 2 files changed, 6 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index a85946b212d..75597337583 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -661,6 +661,7 @@ static struct usb_device_id id_table_combined [] = { { USB_DEVICE(FTDI_VID, FTDI_DOMINTELL_DGQG_PID) }, { USB_DEVICE(FTDI_VID, FTDI_DOMINTELL_DUSB_PID) }, { USB_DEVICE(ALTI2_VID, ALTI2_N3_PID) }, + { USB_DEVICE(FTDI_VID, DIEBOLD_BCS_SE923_PID) }, { }, /* Optional parameter entry */ { } /* Terminating entry */ }; diff --git a/drivers/usb/serial/ftdi_sio.h b/drivers/usb/serial/ftdi_sio.h index e4305765198..1b62eff475d 100644 --- a/drivers/usb/serial/ftdi_sio.h +++ b/drivers/usb/serial/ftdi_sio.h @@ -884,6 +884,11 @@ #define RATOC_VENDOR_ID 0x0584 #define RATOC_PRODUCT_ID_USB60F 0xb020 +/* + * DIEBOLD BCS SE923 + */ +#define DIEBOLD_BCS_SE923_PID 0xfb99 + /* * BmRequestType: 1100 0000b * bRequest: FTDI_E2_READ -- cgit v1.2.3 From 7abce6bedc118eb39fe177c2c26be5d008505c14 Mon Sep 17 00:00:00 2001 From: Pete Zaitcev Date: Sat, 20 Dec 2008 12:56:08 -0700 Subject: USB: usbmon: Implement compat_ioctl Running a 32-bit usbmon(8) on 2.6.28-rc9 produces the following: ioctl32(usbmon:28563): Unknown cmd fd(3) cmd(400c9206){t:ffffff92;sz:12} arg(ffd3f458) on /dev/usbmon0 It happens because the compatibility mode was implemented for 2.6.18 and not updated for the fsops.compat_ioctl API. This patch relocates the pieces from under #ifdef CONFIG_COMPAT into compat_ioctl with no other changes except one new whitespace. Signed-off-by: Pete Zaitcev Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/mon/mon_bin.c | 105 +++++++++++++++++++++++++++++----------------- 1 file changed, 66 insertions(+), 39 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/mon/mon_bin.c b/drivers/usb/mon/mon_bin.c index e06810aef2d..4cf27c72423 100644 --- a/drivers/usb/mon/mon_bin.c +++ b/drivers/usb/mon/mon_bin.c @@ -37,6 +37,7 @@ #define MON_IOCX_GET _IOW(MON_IOC_MAGIC, 6, struct mon_bin_get) #define MON_IOCX_MFETCH _IOWR(MON_IOC_MAGIC, 7, struct mon_bin_mfetch) #define MON_IOCH_MFLUSH _IO(MON_IOC_MAGIC, 8) + #ifdef CONFIG_COMPAT #define MON_IOCX_GET32 _IOW(MON_IOC_MAGIC, 6, struct mon_bin_get32) #define MON_IOCX_MFETCH32 _IOWR(MON_IOC_MAGIC, 7, struct mon_bin_mfetch32) @@ -921,21 +922,6 @@ static int mon_bin_ioctl(struct inode *inode, struct file *file, } break; -#ifdef CONFIG_COMPAT - case MON_IOCX_GET32: { - struct mon_bin_get32 getb; - - if (copy_from_user(&getb, (void __user *)arg, - sizeof(struct mon_bin_get32))) - return -EFAULT; - - ret = mon_bin_get_event(file, rp, - compat_ptr(getb.hdr32), compat_ptr(getb.data32), - getb.alloc32); - } - break; -#endif - case MON_IOCX_MFETCH: { struct mon_bin_mfetch mfetch; @@ -962,7 +948,57 @@ static int mon_bin_ioctl(struct inode *inode, struct file *file, } break; + case MON_IOCG_STATS: { + struct mon_bin_stats __user *sp; + unsigned int nevents; + unsigned int ndropped; + + spin_lock_irqsave(&rp->b_lock, flags); + ndropped = rp->cnt_lost; + rp->cnt_lost = 0; + spin_unlock_irqrestore(&rp->b_lock, flags); + nevents = mon_bin_queued(rp); + + sp = (struct mon_bin_stats __user *)arg; + if (put_user(rp->cnt_lost, &sp->dropped)) + return -EFAULT; + if (put_user(nevents, &sp->queued)) + return -EFAULT; + + } + break; + + default: + return -ENOTTY; + } + + return ret; +} + #ifdef CONFIG_COMPAT +static long mon_bin_compat_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct mon_reader_bin *rp = file->private_data; + int ret; + + switch (cmd) { + + case MON_IOCX_GET32: { + struct mon_bin_get32 getb; + + if (copy_from_user(&getb, (void __user *)arg, + sizeof(struct mon_bin_get32))) + return -EFAULT; + + ret = mon_bin_get_event(file, rp, + compat_ptr(getb.hdr32), compat_ptr(getb.data32), + getb.alloc32); + if (ret < 0) + return ret; + } + return 0; + case MON_IOCX_MFETCH32: { struct mon_bin_mfetch32 mfetch; @@ -986,37 +1022,25 @@ static int mon_bin_ioctl(struct inode *inode, struct file *file, return ret; if (put_user(ret, &uptr->nfetch32)) return -EFAULT; - ret = 0; } - break; -#endif - - case MON_IOCG_STATS: { - struct mon_bin_stats __user *sp; - unsigned int nevents; - unsigned int ndropped; - - spin_lock_irqsave(&rp->b_lock, flags); - ndropped = rp->cnt_lost; - rp->cnt_lost = 0; - spin_unlock_irqrestore(&rp->b_lock, flags); - nevents = mon_bin_queued(rp); + return 0; - sp = (struct mon_bin_stats __user *)arg; - if (put_user(rp->cnt_lost, &sp->dropped)) - return -EFAULT; - if (put_user(nevents, &sp->queued)) - return -EFAULT; + case MON_IOCG_STATS: + return mon_bin_ioctl(NULL, file, cmd, + (unsigned long) compat_ptr(arg)); - } - break; + case MON_IOCQ_URB_LEN: + case MON_IOCQ_RING_SIZE: + case MON_IOCT_RING_SIZE: + case MON_IOCH_MFLUSH: + return mon_bin_ioctl(NULL, file, cmd, arg); default: - return -ENOTTY; + ; } - - return ret; + return -ENOTTY; } +#endif /* CONFIG_COMPAT */ static unsigned int mon_bin_poll(struct file *file, struct poll_table_struct *wait) @@ -1094,6 +1118,9 @@ static const struct file_operations mon_fops_binary = { /* .write = mon_text_write, */ .poll = mon_bin_poll, .ioctl = mon_bin_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = mon_bin_compat_ioctl, +#endif .release = mon_bin_release, .mmap = mon_bin_mmap, }; -- cgit v1.2.3 From 649150926b01c57e45a0376cbc1d3aa98eabfde2 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Onofre Date: Sat, 20 Dec 2008 20:11:55 +0100 Subject: USB: storage: support of Dane-Elec MediaTouch USB device This adds another unusual_devs.h entry for a device that can't handle more than 64k reads/writes in a single command. Signed-off-by: Jean-Baptiste Onofre Signed-off-by: Phil Dibowitz Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/unusual_devs.h | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index ec40c26bd54..c6c435899d8 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -995,6 +995,16 @@ UNUSUAL_DEV( 0x071b, 0x3203, 0x0000, 0x0000, US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_NO_WP_DETECT | US_FL_MAX_SECTORS_64), +/* Reported by Jean-Baptiste Onofre + * Support the following product : + * "Dane-Elec MediaTouch" + */ +UNUSUAL_DEV( 0x071b, 0x32bb, 0x0000, 0x0000, + "RockChip", + "MTP", + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_NO_WP_DETECT | US_FL_MAX_SECTORS_64), + /* Reported by Massimiliano Ghilardi * This USB MP3/AVI player device fails and disconnects if more than 128 * sectors (64kB) are read/written in a single command, and may be present -- cgit v1.2.3 From d547f13472adc99721d6eb756085276a8a342366 Mon Sep 17 00:00:00 2001 From: Phil Dibowitz Date: Sun, 11 Jan 2009 18:46:20 +0100 Subject: USB: Remove ZTE modem from unusual_devices The ZTE modem entry causes usb-storage to ignore the device, but for some versions of the device, usb-storage mode is required to get to modem ode. For both kinds the tool: http://www.draisberghof.de/usb_modeswitch/ should work. Note that the various versions of the device have the same ProductId, VendorId, and bcdDevice number, so we cannot have the entry for some and not others. Signed-off-by: Phil Dibowitz Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/unusual_devs.h | 10 ---------- 1 file changed, 10 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index c6c435899d8..ba16c9ed523 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -2041,16 +2041,6 @@ UNUSUAL_DEV( 0x1652, 0x6600, 0x0201, 0x0201, US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_IGNORE_RESIDUE ), -/* Reported by Mauro Andreolini - * This entry is needed to bypass the ZeroCD mechanism - * and to properly load as a modem device. - */ -UNUSUAL_DEV( 0x19d2, 0x2000, 0x0000, 0x0000, - "Onda ET502HS", - "USB MMC Storage", - US_SC_DEVICE, US_PR_DEVICE, NULL, - US_FL_IGNORE_DEVICE), - UNUSUAL_DEV( 0x2116, 0x0320, 0x0001, 0x0001, "ST", "2A", -- cgit v1.2.3 From 3b498a66a698c581535c0fcf1a8907f3fe9449cc Mon Sep 17 00:00:00 2001 From: Marcel Sebek Date: Sun, 28 Dec 2008 14:06:50 +0100 Subject: USB: 'option' driver - onda device MT503HS has wrong id While trying to make GSM modem Onda MT503HS working, I found a mismatch between device id in the driver code (0x0200) and id in the lsusb output (0x2000). This patch fixed it for me, but I don't know if the original device id was also correct and the new ID should be added instead of replacing the old one. Signed-off-by: Marcel Sebek Acked-by: Domenico Riccio Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/option.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 5ed183477aa..19f5f0ce2cc 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -224,7 +224,7 @@ static int option_send_setup(struct tty_struct *tty, struct usb_serial_port *po #define ONDA_VENDOR_ID 0x19d2 #define ONDA_PRODUCT_MSA501HS 0x0001 #define ONDA_PRODUCT_ET502HS 0x0002 -#define ONDA_PRODUCT_MT503HS 0x0200 +#define ONDA_PRODUCT_MT503HS 0x2000 #define BANDRICH_VENDOR_ID 0x1A8D #define BANDRICH_PRODUCT_C100_1 0x1002 -- cgit v1.2.3 From c89c60e9d6b306fb6963030abb3bd07cc3de66b2 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Sun, 11 Jan 2009 19:53:10 +0000 Subject: USB: cdc-acm: Add another conexant modem to the quirks Another Conexant, another device with the same quirk Signed-off-by: Alan Cox Acked-by: Oliver Neukum Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/cdc-acm.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index f2bfae7b398..97ba4a985ed 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -1376,6 +1376,9 @@ static struct usb_device_id acm_ids[] = { { USB_DEVICE(0x0572, 0x1321), /* Conexant USB MODEM CX93010 */ .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ }, + { USB_DEVICE(0x0572, 0x1324), /* Conexant USB MODEM RD02-D400 */ + .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ + }, /* control interfaces with various AT-command sets */ { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, -- cgit v1.2.3 From 1a1fab513734b3a4fca1bee8229e5ff7e1cb873c Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Mon, 12 Jan 2009 13:31:16 +0100 Subject: USB: new id for ti_usb_3410_5052 driver This adds a new device id Signed-off-by: Oliver Neukum Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/ti_usb_3410_5052.c | 3 +++ drivers/usb/serial/ti_usb_3410_5052.h | 2 ++ 2 files changed, 5 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c index 3cf41df302d..baf591137b8 100644 --- a/drivers/usb/serial/ti_usb_3410_5052.c +++ b/drivers/usb/serial/ti_usb_3410_5052.c @@ -184,6 +184,7 @@ static struct usb_device_id ti_id_table_3410[7+TI_EXTRA_VID_PID_COUNT+1] = { { USB_DEVICE(MTS_VENDOR_ID, MTS_CDMA_PRODUCT_ID) }, { USB_DEVICE(MTS_VENDOR_ID, MTS_GSM_PRODUCT_ID) }, { USB_DEVICE(MTS_VENDOR_ID, MTS_EDGE_PRODUCT_ID) }, + { USB_DEVICE(IBM_VENDOR_ID, IBM_4543_PRODUCT_ID) }, }; static struct usb_device_id ti_id_table_5052[4+TI_EXTRA_VID_PID_COUNT+1] = { @@ -191,6 +192,7 @@ static struct usb_device_id ti_id_table_5052[4+TI_EXTRA_VID_PID_COUNT+1] = { { USB_DEVICE(TI_VENDOR_ID, TI_5152_BOOT_PRODUCT_ID) }, { USB_DEVICE(TI_VENDOR_ID, TI_5052_EEPROM_PRODUCT_ID) }, { USB_DEVICE(TI_VENDOR_ID, TI_5052_FIRMWARE_PRODUCT_ID) }, + { USB_DEVICE(IBM_VENDOR_ID, IBM_4543_PRODUCT_ID) }, }; static struct usb_device_id ti_id_table_combined[6+2*TI_EXTRA_VID_PID_COUNT+1] = { @@ -205,6 +207,7 @@ static struct usb_device_id ti_id_table_combined[6+2*TI_EXTRA_VID_PID_COUNT+1] = { USB_DEVICE(TI_VENDOR_ID, TI_5152_BOOT_PRODUCT_ID) }, { USB_DEVICE(TI_VENDOR_ID, TI_5052_EEPROM_PRODUCT_ID) }, { USB_DEVICE(TI_VENDOR_ID, TI_5052_FIRMWARE_PRODUCT_ID) }, + { USB_DEVICE(IBM_VENDOR_ID, IBM_4543_PRODUCT_ID) }, { } }; diff --git a/drivers/usb/serial/ti_usb_3410_5052.h b/drivers/usb/serial/ti_usb_3410_5052.h index 7e4752fbf23..b7ea5dbadee 100644 --- a/drivers/usb/serial/ti_usb_3410_5052.h +++ b/drivers/usb/serial/ti_usb_3410_5052.h @@ -27,7 +27,9 @@ /* Vendor and product ids */ #define TI_VENDOR_ID 0x0451 +#define IBM_VENDOR_ID 0x04b3 #define TI_3410_PRODUCT_ID 0x3410 +#define IBM_4543_PRODUCT_ID 0x4543 #define TI_3410_EZ430_ID 0xF430 /* TI ez430 development tool */ #define TI_5052_BOOT_PRODUCT_ID 0x5052 /* no EEPROM, no firmware */ #define TI_5152_BOOT_PRODUCT_ID 0x5152 /* no EEPROM, no firmware */ -- cgit v1.2.3 From 0df2479232eeea20c924350a11788c724b8c218d Mon Sep 17 00:00:00 2001 From: Roel Kluin Date: Sat, 17 Jan 2009 16:52:17 +0100 Subject: USB: GADGET: fix !x & y ! has a higher precedence than & Signed-off-by: Roel Kluin Acked-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/imx_udc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/imx_udc.c b/drivers/usb/gadget/imx_udc.c index cde8fdf15d5..77c5d0a8a06 100644 --- a/drivers/usb/gadget/imx_udc.c +++ b/drivers/usb/gadget/imx_udc.c @@ -297,7 +297,7 @@ void imx_ep_stall(struct imx_ep_struct *imx_ep) for (i = 0; i < 100; i ++) { temp = __raw_readl(imx_usb->base + USB_EP_STAT(EP_NO(imx_ep))); - if (!temp & EPSTAT_STALL) + if (!(temp & EPSTAT_STALL)) break; udelay(20); } -- cgit v1.2.3 From a83775b1465ce80af5610cbe80216432212bc7ee Mon Sep 17 00:00:00 2001 From: Phil Dibowitz Date: Tue, 20 Jan 2009 23:48:36 +0100 Subject: USB: unusual_dev: usb-storage needs to ignore a device This patch adds an unusual_devs entry for a Sony Ericsson modem. Like many other modems, we have to ignore the storage device in order to access the modem. At this time usb_modeswitch does not work with this device. Reported-by: The Solutor . Signed-off-by: Phil Dibowitz Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/unusual_devs.h | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index ba16c9ed523..b3353bf7a62 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -1599,6 +1599,13 @@ UNUSUAL_DEV( 0x0fce, 0xd008, 0x0000, 0x0000, US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_NO_WP_DETECT ), +/* Reported by The Solutor */ +UNUSUAL_DEV( 0x0fce, 0xd0e1, 0x0000, 0x0000, + "Sony Ericsson", + "MD400", + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_IGNORE_DEVICE), + /* Reported by Jan Mate * and by Soeren Sonnenburg */ UNUSUAL_DEV( 0x0fce, 0xe030, 0x0000, 0x0000, -- cgit v1.2.3 From aa23c8d616c33578fb99aa6a0effd6705b5d0fa1 Mon Sep 17 00:00:00 2001 From: Phil Dibowitz Date: Tue, 20 Jan 2009 23:42:52 +0100 Subject: USB: storage: Add another unusual_dev for off-by-one bug Argosy has released another device with the off-by-one sector. This is a harddrive with an internal cardreader which is affected. Based on a patch written by Martijn Hijdra Signed-off-by: Phil Dibowitz Cc: Martijn Hijdra Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/unusual_devs.h | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index b3353bf7a62..69269f73956 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -1261,6 +1261,13 @@ UNUSUAL_DEV( 0x0840, 0x0084, 0x0001, 0x0001, US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_FIX_CAPACITY), +/* Reported by Martijn Hijdra */ +UNUSUAL_DEV( 0x0840, 0x0085, 0x0001, 0x0001, + "Argosy", + "Storage", + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_FIX_CAPACITY), + /* Entry and supporting patch by Theodore Kilgore . * Flag will support Bulk devices which use a standards-violating 32-byte * Command Block Wrapper. Here, the "DC2MEGA" cameras (several brands) with -- cgit v1.2.3 From fc91be2ad03e0d243418414a854665274d560ca2 Mon Sep 17 00:00:00 2001 From: "Alex.Cheng@quantatw.com" Date: Thu, 22 Jan 2009 16:01:57 +0800 Subject: USB: option: add QUANTA HSDPA Data Card device ids This patch adds the support for the QUANTA Q101 series HSDPA Data Card. With the vendor and product IDs are set properly, the data card can be detected and works fine. Signed-off-by: Alex Cheng Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/option.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 19f5f0ce2cc..6c89da9c6fe 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -158,6 +158,13 @@ static int option_send_setup(struct tty_struct *tty, struct usb_serial_port *po #define HUAWEI_PRODUCT_E143E 0x143E #define HUAWEI_PRODUCT_E143F 0x143F +#define QUANTA_VENDOR_ID 0x0408 +#define QUANTA_PRODUCT_Q101 0xEA02 +#define QUANTA_PRODUCT_Q111 0xEA03 +#define QUANTA_PRODUCT_GLX 0xEA04 +#define QUANTA_PRODUCT_GKE 0xEA05 +#define QUANTA_PRODUCT_GLE 0xEA06 + #define NOVATELWIRELESS_VENDOR_ID 0x1410 /* YISO PRODUCTS */ @@ -298,6 +305,11 @@ static struct usb_device_id option_ids[] = { { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_MODEM_GT) }, { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_MODEM_EX) }, { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_KOI_MODEM) }, + { USB_DEVICE(QUANTA_VENDOR_ID, QUANTA_PRODUCT_Q101) }, + { USB_DEVICE(QUANTA_VENDOR_ID, QUANTA_PRODUCT_Q111) }, + { USB_DEVICE(QUANTA_VENDOR_ID, QUANTA_PRODUCT_GLX) }, + { USB_DEVICE(QUANTA_VENDOR_ID, QUANTA_PRODUCT_GKE) }, + { USB_DEVICE(QUANTA_VENDOR_ID, QUANTA_PRODUCT_GLE) }, { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E600, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E220, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E220BIS, 0xff, 0xff, 0xff) }, -- cgit v1.2.3 From 236dd4d18f293e3c9798f35c08272196826a980d Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Sat, 10 Jan 2009 05:03:21 +0300 Subject: USB: Driver for Freescale QUICC Engine USB Host Controller This patch adds support for the FHCI USB controller, as found in the Freescale MPC836x and MPC832x processors. It can support Full or Low speed modes. Quite a lot the hardware is doing by itself (SOF generation, CRC generation and checking), though scheduling and retransmission is on software's shoulders. This controller does not integrate the root hub, so this driver also fakes one-port hub. External hub is required to support more than one device. Signed-off-by: Anton Vorontsov Signed-off-by: Greg Kroah-Hartman --- drivers/usb/Makefile | 1 + drivers/usb/host/Kconfig | 17 + drivers/usb/host/Makefile | 6 + drivers/usb/host/fhci-dbg.c | 139 +++++++ drivers/usb/host/fhci-hcd.c | 836 +++++++++++++++++++++++++++++++++++++++ drivers/usb/host/fhci-hub.c | 345 ++++++++++++++++ drivers/usb/host/fhci-mem.c | 113 ++++++ drivers/usb/host/fhci-q.c | 284 ++++++++++++++ drivers/usb/host/fhci-sched.c | 888 ++++++++++++++++++++++++++++++++++++++++++ drivers/usb/host/fhci-tds.c | 626 +++++++++++++++++++++++++++++ drivers/usb/host/fhci.h | 607 +++++++++++++++++++++++++++++ 11 files changed, 3862 insertions(+) create mode 100644 drivers/usb/host/fhci-dbg.c create mode 100644 drivers/usb/host/fhci-hcd.c create mode 100644 drivers/usb/host/fhci-hub.c create mode 100644 drivers/usb/host/fhci-mem.c create mode 100644 drivers/usb/host/fhci-q.c create mode 100644 drivers/usb/host/fhci-sched.c create mode 100644 drivers/usb/host/fhci-tds.c create mode 100644 drivers/usb/host/fhci.h (limited to 'drivers/usb') diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index 8b7c419b876..8bcde8cde55 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -13,6 +13,7 @@ obj-$(CONFIG_USB_EHCI_HCD) += host/ obj-$(CONFIG_USB_ISP116X_HCD) += host/ obj-$(CONFIG_USB_OHCI_HCD) += host/ obj-$(CONFIG_USB_UHCI_HCD) += host/ +obj-$(CONFIG_USB_FHCI_HCD) += host/ obj-$(CONFIG_USB_SL811_HCD) += host/ obj-$(CONFIG_USB_U132_HCD) += host/ obj-$(CONFIG_USB_R8A66597_HCD) += host/ diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 5a988f4cdf8..2c63bfb1f8d 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -239,6 +239,23 @@ config USB_UHCI_HCD To compile this driver as a module, choose M here: the module will be called uhci-hcd. +config USB_FHCI_HCD + tristate "Freescale QE USB Host Controller support" + depends on USB && OF_GPIO && QE_GPIO && QUICC_ENGINE + select FSL_GTM + select QE_USB + help + This driver enables support for Freescale QE USB Host Controller + (as found on MPC8360 and MPC8323 processors), the driver supports + Full and Low Speed USB. + +config FHCI_DEBUG + bool "Freescale QE USB Host Controller debug support" + depends on USB_FHCI_HCD && DEBUG_FS + help + Say "y" to see some FHCI debug information and statistics + throught debugfs. + config USB_U132_HCD tristate "Elan U132 Adapter Host Controller" depends on USB && USB_FTDI_ELAN diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index e5f3f20787e..f163571e33d 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -7,6 +7,11 @@ ifeq ($(CONFIG_USB_DEBUG),y) endif isp1760-objs := isp1760-hcd.o isp1760-if.o +fhci-objs := fhci-hcd.o fhci-hub.o fhci-q.o fhci-mem.o \ + fhci-tds.o fhci-sched.o +ifeq ($(CONFIG_FHCI_DEBUG),y) +fhci-objs += fhci-dbg.o +endif obj-$(CONFIG_USB_WHCI_HCD) += whci/ @@ -17,6 +22,7 @@ obj-$(CONFIG_USB_OXU210HP_HCD) += oxu210hp-hcd.o obj-$(CONFIG_USB_ISP116X_HCD) += isp116x-hcd.o obj-$(CONFIG_USB_OHCI_HCD) += ohci-hcd.o obj-$(CONFIG_USB_UHCI_HCD) += uhci-hcd.o +obj-$(CONFIG_USB_FHCI_HCD) += fhci.o obj-$(CONFIG_USB_SL811_HCD) += sl811-hcd.o obj-$(CONFIG_USB_SL811_CS) += sl811_cs.o obj-$(CONFIG_USB_U132_HCD) += u132-hcd.o diff --git a/drivers/usb/host/fhci-dbg.c b/drivers/usb/host/fhci-dbg.c new file mode 100644 index 00000000000..34e14edf390 --- /dev/null +++ b/drivers/usb/host/fhci-dbg.c @@ -0,0 +1,139 @@ +/* + * Freescale QUICC Engine USB Host Controller Driver + * + * Copyright (c) Freescale Semicondutor, Inc. 2006. + * Shlomi Gridish + * Jerry Huang + * Copyright (c) Logic Product Development, Inc. 2007 + * Peter Barada + * Copyright (c) MontaVista Software, Inc. 2008. + * Anton Vorontsov + * + * 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. + */ + +#include +#include +#include +#include +#include +#include "../core/hcd.h" +#include "fhci.h" + +void fhci_dbg_isr(struct fhci_hcd *fhci, int usb_er) +{ + int i; + + if (usb_er == -1) { + fhci->usb_irq_stat[12]++; + return; + } + + for (i = 0; i < 12; ++i) { + if (usb_er & (1 << i)) + fhci->usb_irq_stat[i]++; + } +} + +static int fhci_dfs_regs_show(struct seq_file *s, void *v) +{ + struct fhci_hcd *fhci = s->private; + struct fhci_regs __iomem *regs = fhci->regs; + + seq_printf(s, + "mode: 0x%x\n" "addr: 0x%x\n" + "command: 0x%x\n" "ep0: 0x%x\n" + "event: 0x%x\n" "mask: 0x%x\n" + "status: 0x%x\n" "SOF timer: %d\n" + "frame number: %d\n" + "lines status: 0x%x\n", + in_8(®s->usb_mod), in_8(®s->usb_addr), + in_8(®s->usb_comm), in_be16(®s->usb_ep[0]), + in_be16(®s->usb_event), in_be16(®s->usb_mask), + in_8(®s->usb_status), in_be16(®s->usb_sof_tmr), + in_be16(®s->usb_frame_num), + fhci_ioports_check_bus_state(fhci)); + + return 0; +} + +static int fhci_dfs_irq_stat_show(struct seq_file *s, void *v) +{ + struct fhci_hcd *fhci = s->private; + int *usb_irq_stat = fhci->usb_irq_stat; + + seq_printf(s, + "RXB: %d\n" "TXB: %d\n" "BSY: %d\n" + "SOF: %d\n" "TXE0: %d\n" "TXE1: %d\n" + "TXE2: %d\n" "TXE3: %d\n" "IDLE: %d\n" + "RESET: %d\n" "SFT: %d\n" "MSF: %d\n" + "IDLE_ONLY: %d\n", + usb_irq_stat[0], usb_irq_stat[1], usb_irq_stat[2], + usb_irq_stat[3], usb_irq_stat[4], usb_irq_stat[5], + usb_irq_stat[6], usb_irq_stat[7], usb_irq_stat[8], + usb_irq_stat[9], usb_irq_stat[10], usb_irq_stat[11], + usb_irq_stat[12]); + + return 0; +} + +static int fhci_dfs_regs_open(struct inode *inode, struct file *file) +{ + return single_open(file, fhci_dfs_regs_show, inode->i_private); +} + +static int fhci_dfs_irq_stat_open(struct inode *inode, struct file *file) +{ + return single_open(file, fhci_dfs_irq_stat_show, inode->i_private); +} + +static const struct file_operations fhci_dfs_regs_fops = { + .open = fhci_dfs_regs_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static const struct file_operations fhci_dfs_irq_stat_fops = { + .open = fhci_dfs_irq_stat_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +void fhci_dfs_create(struct fhci_hcd *fhci) +{ + struct device *dev = fhci_to_hcd(fhci)->self.controller; + + fhci->dfs_root = debugfs_create_dir(dev->bus_id, NULL); + if (!fhci->dfs_root) { + WARN_ON(1); + return; + } + + fhci->dfs_regs = debugfs_create_file("regs", S_IFREG | S_IRUGO, + fhci->dfs_root, fhci, &fhci_dfs_regs_fops); + + fhci->dfs_irq_stat = debugfs_create_file("irq_stat", + S_IFREG | S_IRUGO, fhci->dfs_root, fhci, + &fhci_dfs_irq_stat_fops); + + WARN_ON(!fhci->dfs_regs || !fhci->dfs_irq_stat); +} + +void fhci_dfs_destroy(struct fhci_hcd *fhci) +{ + if (!fhci->dfs_root) + return; + + if (fhci->dfs_irq_stat) + debugfs_remove(fhci->dfs_irq_stat); + + if (fhci->dfs_regs) + debugfs_remove(fhci->dfs_regs); + + debugfs_remove(fhci->dfs_root); +} diff --git a/drivers/usb/host/fhci-hcd.c b/drivers/usb/host/fhci-hcd.c new file mode 100644 index 00000000000..ba622cc8a9b --- /dev/null +++ b/drivers/usb/host/fhci-hcd.c @@ -0,0 +1,836 @@ +/* + * Freescale QUICC Engine USB Host Controller Driver + * + * Copyright (c) Freescale Semicondutor, Inc. 2006. + * Shlomi Gridish + * Jerry Huang + * Copyright (c) Logic Product Development, Inc. 2007 + * Peter Barada + * Copyright (c) MontaVista Software, Inc. 2008. + * Anton Vorontsov + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../core/hcd.h" +#include "fhci.h" + +void fhci_start_sof_timer(struct fhci_hcd *fhci) +{ + fhci_dbg(fhci, "-> %s\n", __func__); + + /* clear frame_n */ + out_be16(&fhci->pram->frame_num, 0); + + out_be16(&fhci->regs->usb_sof_tmr, 0); + setbits8(&fhci->regs->usb_mod, USB_MODE_SFTE); + + fhci_dbg(fhci, "<- %s\n", __func__); +} + +void fhci_stop_sof_timer(struct fhci_hcd *fhci) +{ + fhci_dbg(fhci, "-> %s\n", __func__); + + clrbits8(&fhci->regs->usb_mod, USB_MODE_SFTE); + gtm_stop_timer16(fhci->timer); + + fhci_dbg(fhci, "<- %s\n", __func__); +} + +u16 fhci_get_sof_timer_count(struct fhci_usb *usb) +{ + return be16_to_cpu(in_be16(&usb->fhci->regs->usb_sof_tmr) / 12); +} + +/* initialize the endpoint zero */ +static u32 endpoint_zero_init(struct fhci_usb *usb, + enum fhci_mem_alloc data_mem, + u32 ring_len) +{ + u32 rc; + + rc = fhci_create_ep(usb, data_mem, ring_len); + if (rc) + return rc; + + /* inilialize endpoint registers */ + fhci_init_ep_registers(usb, usb->ep0, data_mem); + + return 0; +} + +/* enable the USB interrupts */ +void fhci_usb_enable_interrupt(struct fhci_usb *usb) +{ + struct fhci_hcd *fhci = usb->fhci; + + if (usb->intr_nesting_cnt == 1) { + /* initialize the USB interrupt */ + enable_irq(fhci_to_hcd(fhci)->irq); + + /* initialize the event register and mask register */ + out_be16(&usb->fhci->regs->usb_event, 0xffff); + out_be16(&usb->fhci->regs->usb_mask, usb->saved_msk); + + /* enable the timer interrupts */ + enable_irq(fhci->timer->irq); + } else if (usb->intr_nesting_cnt > 1) + fhci_info(fhci, "unbalanced USB interrupts nesting\n"); + usb->intr_nesting_cnt--; +} + +/* diable the usb interrupt */ +void fhci_usb_disable_interrupt(struct fhci_usb *usb) +{ + struct fhci_hcd *fhci = usb->fhci; + + if (usb->intr_nesting_cnt == 0) { + /* diable the timer interrupt */ + disable_irq_nosync(fhci->timer->irq); + + /* disable the usb interrupt */ + disable_irq_nosync(fhci_to_hcd(fhci)->irq); + out_be16(&usb->fhci->regs->usb_mask, 0); + } + usb->intr_nesting_cnt++; +} + +/* enable the USB controller */ +static u32 fhci_usb_enable(struct fhci_hcd *fhci) +{ + struct fhci_usb *usb = fhci->usb_lld; + + out_be16(&usb->fhci->regs->usb_event, 0xffff); + out_be16(&usb->fhci->regs->usb_mask, usb->saved_msk); + setbits8(&usb->fhci->regs->usb_mod, USB_MODE_EN); + + mdelay(100); + + return 0; +} + +/* disable the USB controller */ +static u32 fhci_usb_disable(struct fhci_hcd *fhci) +{ + struct fhci_usb *usb = fhci->usb_lld; + + fhci_usb_disable_interrupt(usb); + fhci_port_disable(fhci); + + /* disable the usb controller */ + if (usb->port_status == FHCI_PORT_FULL || + usb->port_status == FHCI_PORT_LOW) + fhci_device_disconnected_interrupt(fhci); + + clrbits8(&usb->fhci->regs->usb_mod, USB_MODE_EN); + + return 0; +} + +/* check the bus state by polling the QE bit on the IO ports */ +int fhci_ioports_check_bus_state(struct fhci_hcd *fhci) +{ + u8 bits = 0; + + /* check USBOE,if transmitting,exit */ + if (!gpio_get_value(fhci->gpios[GPIO_USBOE])) + return -1; + + /* check USBRP */ + if (gpio_get_value(fhci->gpios[GPIO_USBRP])) + bits |= 0x2; + + /* check USBRN */ + if (gpio_get_value(fhci->gpios[GPIO_USBRN])) + bits |= 0x1; + + return bits; +} + +static void fhci_mem_free(struct fhci_hcd *fhci) +{ + struct ed *ed; + struct ed *next_ed; + struct td *td; + struct td *next_td; + + list_for_each_entry_safe(ed, next_ed, &fhci->empty_eds, node) { + list_del(&ed->node); + kfree(ed); + } + + list_for_each_entry_safe(td, next_td, &fhci->empty_tds, node) { + list_del(&td->node); + kfree(td); + } + + kfree(fhci->vroot_hub); + fhci->vroot_hub = NULL; + + kfree(fhci->hc_list); + fhci->hc_list = NULL; +} + +static int fhci_mem_init(struct fhci_hcd *fhci) +{ + int i; + + fhci->hc_list = kzalloc(sizeof(*fhci->hc_list), GFP_KERNEL); + if (!fhci->hc_list) + goto err; + + INIT_LIST_HEAD(&fhci->hc_list->ctrl_list); + INIT_LIST_HEAD(&fhci->hc_list->bulk_list); + INIT_LIST_HEAD(&fhci->hc_list->iso_list); + INIT_LIST_HEAD(&fhci->hc_list->intr_list); + INIT_LIST_HEAD(&fhci->hc_list->done_list); + + fhci->vroot_hub = kzalloc(sizeof(*fhci->vroot_hub), GFP_KERNEL); + if (!fhci->vroot_hub) + goto err; + + INIT_LIST_HEAD(&fhci->empty_eds); + INIT_LIST_HEAD(&fhci->empty_tds); + + /* initialize work queue to handle done list */ + fhci_tasklet.data = (unsigned long)fhci; + fhci->process_done_task = &fhci_tasklet; + + for (i = 0; i < MAX_TDS; i++) { + struct td *td; + + td = kmalloc(sizeof(*td), GFP_KERNEL); + if (!td) + goto err; + fhci_recycle_empty_td(fhci, td); + } + for (i = 0; i < MAX_EDS; i++) { + struct ed *ed; + + ed = kmalloc(sizeof(*ed), GFP_KERNEL); + if (!ed) + goto err; + fhci_recycle_empty_ed(fhci, ed); + } + + fhci->active_urbs = 0; + return 0; +err: + fhci_mem_free(fhci); + return -ENOMEM; +} + +/* destroy the fhci_usb structure */ +static void fhci_usb_free(void *lld) +{ + struct fhci_usb *usb = lld; + struct fhci_hcd *fhci = usb->fhci; + + if (usb) { + fhci_config_transceiver(fhci, FHCI_PORT_POWER_OFF); + fhci_ep0_free(usb); + kfree(usb->actual_frame); + kfree(usb); + } +} + +/* initialize the USB */ +static int fhci_usb_init(struct fhci_hcd *fhci) +{ + struct fhci_usb *usb = fhci->usb_lld; + + memset_io(usb->fhci->pram, 0, FHCI_PRAM_SIZE); + + usb->port_status = FHCI_PORT_DISABLED; + usb->max_frame_usage = FRAME_TIME_USAGE; + usb->sw_transaction_time = SW_FIX_TIME_BETWEEN_TRANSACTION; + + usb->actual_frame = kzalloc(sizeof(*usb->actual_frame), GFP_KERNEL); + if (!usb->actual_frame) { + fhci_usb_free(usb); + return -ENOMEM; + } + + INIT_LIST_HEAD(&usb->actual_frame->tds_list); + + /* initializing registers on chip, clear frame number */ + out_be16(&fhci->pram->frame_num, 0); + + /* clear rx state */ + out_be32(&fhci->pram->rx_state, 0); + + /* set mask register */ + usb->saved_msk = (USB_E_TXB_MASK | + USB_E_TXE1_MASK | + USB_E_IDLE_MASK | + USB_E_RESET_MASK | USB_E_SFT_MASK | USB_E_MSF_MASK); + + out_8(&usb->fhci->regs->usb_mod, USB_MODE_HOST | USB_MODE_EN); + + /* clearing the mask register */ + out_be16(&usb->fhci->regs->usb_mask, 0); + + /* initialing the event register */ + out_be16(&usb->fhci->regs->usb_event, 0xffff); + + if (endpoint_zero_init(usb, DEFAULT_DATA_MEM, DEFAULT_RING_LEN) != 0) { + fhci_usb_free(usb); + return -EINVAL; + } + + return 0; +} + +/* initialize the fhci_usb struct and the corresponding data staruct */ +static struct fhci_usb *fhci_create_lld(struct fhci_hcd *fhci) +{ + struct fhci_usb *usb; + + /* allocate memory for SCC data structure */ + usb = kzalloc(sizeof(*usb), GFP_KERNEL); + if (!usb) { + fhci_err(fhci, "no memory for SCC data struct\n"); + return NULL; + } + + usb->fhci = fhci; + usb->hc_list = fhci->hc_list; + usb->vroot_hub = fhci->vroot_hub; + + usb->transfer_confirm = fhci_transfer_confirm_callback; + + return usb; +} + +static int fhci_start(struct usb_hcd *hcd) +{ + int ret; + struct fhci_hcd *fhci = hcd_to_fhci(hcd); + + ret = fhci_mem_init(fhci); + if (ret) { + fhci_err(fhci, "failed to allocate memory\n"); + goto err; + } + + fhci->usb_lld = fhci_create_lld(fhci); + if (!fhci->usb_lld) { + fhci_err(fhci, "low level driver config failed\n"); + ret = -ENOMEM; + goto err; + } + + ret = fhci_usb_init(fhci); + if (ret) { + fhci_err(fhci, "low level driver initialize failed\n"); + goto err; + } + + spin_lock_init(&fhci->lock); + + /* connect the virtual root hub */ + fhci->vroot_hub->dev_num = 1; /* this field may be needed to fix */ + fhci->vroot_hub->hub.wHubStatus = 0; + fhci->vroot_hub->hub.wHubChange = 0; + fhci->vroot_hub->port.wPortStatus = 0; + fhci->vroot_hub->port.wPortChange = 0; + + hcd->state = HC_STATE_RUNNING; + + /* + * From here on, khubd concurrently accesses the root + * hub; drivers will be talking to enumerated devices. + * (On restart paths, khubd already knows about the root + * hub and could find work as soon as we wrote FLAG_CF.) + * + * Before this point the HC was idle/ready. After, khubd + * and device drivers may start it running. + */ + fhci_usb_enable(fhci); + return 0; +err: + fhci_mem_free(fhci); + return ret; +} + +static void fhci_stop(struct usb_hcd *hcd) +{ + struct fhci_hcd *fhci = hcd_to_fhci(hcd); + + fhci_usb_disable_interrupt(fhci->usb_lld); + fhci_usb_disable(fhci); + + fhci_usb_free(fhci->usb_lld); + fhci->usb_lld = NULL; + fhci_mem_free(fhci); +} + +static int fhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, + gfp_t mem_flags) +{ + struct fhci_hcd *fhci = hcd_to_fhci(hcd); + u32 pipe = urb->pipe; + int ret; + int i; + int size = 0; + struct urb_priv *urb_priv; + unsigned long flags; + + switch (usb_pipetype(pipe)) { + case PIPE_CONTROL: + /* 1 td fro setup,1 for ack */ + size = 2; + case PIPE_BULK: + /* one td for every 4096 bytes(can be upto 8k) */ + size += urb->transfer_buffer_length / 4096; + /* ...add for any remaining bytes... */ + if ((urb->transfer_buffer_length % 4096) != 0) + size++; + /* ..and maybe a zero length packet to wrap it up */ + if (size == 0) + size++; + else if ((urb->transfer_flags & URB_ZERO_PACKET) != 0 + && (urb->transfer_buffer_length + % usb_maxpacket(urb->dev, pipe, + usb_pipeout(pipe))) != 0) + size++; + break; + case PIPE_ISOCHRONOUS: + size = urb->number_of_packets; + if (size <= 0) + return -EINVAL; + for (i = 0; i < urb->number_of_packets; i++) { + urb->iso_frame_desc[i].actual_length = 0; + urb->iso_frame_desc[i].status = (u32) (-EXDEV); + } + break; + case PIPE_INTERRUPT: + size = 1; + } + + /* allocate the private part of the URB */ + urb_priv = kzalloc(sizeof(*urb_priv), mem_flags); + if (!urb_priv) + return -ENOMEM; + + /* allocate the private part of the URB */ + urb_priv->tds = kzalloc(size * sizeof(struct td), mem_flags); + if (!urb_priv->tds) { + kfree(urb_priv); + return -ENOMEM; + } + + spin_lock_irqsave(&fhci->lock, flags); + + ret = usb_hcd_link_urb_to_ep(hcd, urb); + if (ret) + goto err; + + /* fill the private part of the URB */ + urb_priv->num_of_tds = size; + + urb->status = -EINPROGRESS; + urb->actual_length = 0; + urb->error_count = 0; + urb->hcpriv = urb_priv; + + fhci_queue_urb(fhci, urb); +err: + if (ret) { + kfree(urb_priv->tds); + kfree(urb_priv); + } + spin_unlock_irqrestore(&fhci->lock, flags); + return ret; +} + +/* dequeue FHCI URB */ +static int fhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) +{ + struct fhci_hcd *fhci = hcd_to_fhci(hcd); + struct fhci_usb *usb = fhci->usb_lld; + int ret = -EINVAL; + unsigned long flags; + + if (!urb || !urb->dev || !urb->dev->bus) + goto out; + + spin_lock_irqsave(&fhci->lock, flags); + + ret = usb_hcd_check_unlink_urb(hcd, urb, status); + if (ret) + goto out2; + + if (usb->port_status != FHCI_PORT_DISABLED) { + struct urb_priv *urb_priv; + + /* + * flag the urb's data for deletion in some upcoming + * SF interrupt's delete list processing + */ + urb_priv = urb->hcpriv; + + if (!urb_priv || (urb_priv->state == URB_DEL)) + goto out2; + + urb_priv->state = URB_DEL; + + /* already pending? */ + urb_priv->ed->state = FHCI_ED_URB_DEL; + } else { + fhci_urb_complete_free(fhci, urb); + } + +out2: + spin_unlock_irqrestore(&fhci->lock, flags); +out: + return ret; +} + +static void fhci_endpoint_disable(struct usb_hcd *hcd, + struct usb_host_endpoint *ep) +{ + struct fhci_hcd *fhci; + struct ed *ed; + unsigned long flags; + + fhci = hcd_to_fhci(hcd); + spin_lock_irqsave(&fhci->lock, flags); + ed = ep->hcpriv; + if (ed) { + while (ed->td_head != NULL) { + struct td *td = fhci_remove_td_from_ed(ed); + fhci_urb_complete_free(fhci, td->urb); + } + fhci_recycle_empty_ed(fhci, ed); + ep->hcpriv = NULL; + } + spin_unlock_irqrestore(&fhci->lock, flags); +} + +static int fhci_get_frame_number(struct usb_hcd *hcd) +{ + struct fhci_hcd *fhci = hcd_to_fhci(hcd); + + return get_frame_num(fhci); +} + +static const struct hc_driver fhci_driver = { + .description = "fsl,usb-fhci", + .product_desc = "FHCI HOST Controller", + .hcd_priv_size = sizeof(struct fhci_hcd), + + /* generic hardware linkage */ + .irq = fhci_irq, + .flags = HCD_USB11 | HCD_MEMORY, + + /* basic lifecycle operation */ + .start = fhci_start, + .stop = fhci_stop, + + /* managing i/o requests and associated device resources */ + .urb_enqueue = fhci_urb_enqueue, + .urb_dequeue = fhci_urb_dequeue, + .endpoint_disable = fhci_endpoint_disable, + + /* scheduling support */ + .get_frame_number = fhci_get_frame_number, + + /* root hub support */ + .hub_status_data = fhci_hub_status_data, + .hub_control = fhci_hub_control, +}; + +static int __devinit of_fhci_probe(struct of_device *ofdev, + const struct of_device_id *ofid) +{ + struct device *dev = &ofdev->dev; + struct device_node *node = ofdev->node; + struct usb_hcd *hcd; + struct fhci_hcd *fhci; + struct resource usb_regs; + unsigned long pram_addr; + unsigned int usb_irq; + const char *sprop; + const u32 *iprop; + int size; + int ret; + int i; + int j; + + if (usb_disabled()) + return -ENODEV; + + sprop = of_get_property(node, "mode", NULL); + if (sprop && strcmp(sprop, "host")) + return -ENODEV; + + hcd = usb_create_hcd(&fhci_driver, dev, dev->bus_id); + if (!hcd) { + dev_err(dev, "could not create hcd\n"); + return -ENOMEM; + } + + fhci = hcd_to_fhci(hcd); + hcd->self.controller = dev; + dev_set_drvdata(dev, hcd); + + iprop = of_get_property(node, "hub-power-budget", &size); + if (iprop && size == sizeof(*iprop)) + hcd->power_budget = *iprop; + + /* FHCI registers. */ + ret = of_address_to_resource(node, 0, &usb_regs); + if (ret) { + dev_err(dev, "could not get regs\n"); + goto err_regs; + } + + hcd->regs = ioremap(usb_regs.start, usb_regs.end - usb_regs.start + 1); + if (!hcd->regs) { + dev_err(dev, "could not ioremap regs\n"); + ret = -ENOMEM; + goto err_regs; + } + fhci->regs = hcd->regs; + + /* Parameter RAM. */ + iprop = of_get_property(node, "reg", &size); + if (!iprop || size < sizeof(*iprop) * 4) { + dev_err(dev, "can't get pram offset\n"); + ret = -EINVAL; + goto err_pram; + } + + pram_addr = cpm_muram_alloc_fixed(iprop[2], FHCI_PRAM_SIZE); + if (IS_ERR_VALUE(pram_addr)) { + dev_err(dev, "failed to allocate usb pram\n"); + ret = -ENOMEM; + goto err_pram; + } + fhci->pram = cpm_muram_addr(pram_addr); + + /* GPIOs and pins */ + for (i = 0; i < NUM_GPIOS; i++) { + int gpio; + enum of_gpio_flags flags; + + gpio = of_get_gpio_flags(node, i, &flags); + fhci->gpios[i] = gpio; + fhci->alow_gpios[i] = flags & OF_GPIO_ACTIVE_LOW; + + if (!gpio_is_valid(gpio)) { + if (i < GPIO_SPEED) { + dev_err(dev, "incorrect GPIO%d: %d\n", + i, gpio); + goto err_gpios; + } else { + dev_info(dev, "assuming board doesn't have " + "%s gpio\n", i == GPIO_SPEED ? + "speed" : "power"); + continue; + } + } + + ret = gpio_request(gpio, dev->bus_id); + if (ret) { + dev_err(dev, "failed to request gpio %d", i); + goto err_gpios; + } + + if (i >= GPIO_SPEED) { + ret = gpio_direction_output(gpio, 0); + if (ret) { + dev_err(dev, "failed to set gpio %d as " + "an output\n", i); + i++; + goto err_gpios; + } + } + } + + for (j = 0; j < NUM_PINS; j++) { + fhci->pins[j] = qe_pin_request(ofdev->node, j); + if (IS_ERR(fhci->pins[j])) { + ret = PTR_ERR(fhci->pins[j]); + dev_err(dev, "can't get pin %d: %d\n", j, ret); + goto err_pins; + } + } + + /* Frame limit timer and its interrupt. */ + fhci->timer = gtm_get_timer16(); + if (IS_ERR(fhci->timer)) { + ret = PTR_ERR(fhci->timer); + dev_err(dev, "failed to request qe timer: %i", ret); + goto err_get_timer; + } + + ret = request_irq(fhci->timer->irq, fhci_frame_limit_timer_irq, + IRQF_DISABLED, "qe timer (usb)", hcd); + if (ret) { + dev_err(dev, "failed to request timer irq"); + goto err_timer_irq; + } + + /* USB Host interrupt. */ + usb_irq = irq_of_parse_and_map(node, 0); + if (usb_irq == NO_IRQ) { + dev_err(dev, "could not get usb irq\n"); + ret = -EINVAL; + goto err_usb_irq; + } + + /* Clocks. */ + sprop = of_get_property(node, "fsl,fullspeed-clock", NULL); + if (sprop) { + fhci->fullspeed_clk = qe_clock_source(sprop); + if (fhci->fullspeed_clk == QE_CLK_DUMMY) { + dev_err(dev, "wrong fullspeed-clock\n"); + ret = -EINVAL; + goto err_clocks; + } + } + + sprop = of_get_property(node, "fsl,lowspeed-clock", NULL); + if (sprop) { + fhci->lowspeed_clk = qe_clock_source(sprop); + if (fhci->lowspeed_clk == QE_CLK_DUMMY) { + dev_err(dev, "wrong lowspeed-clock\n"); + ret = -EINVAL; + goto err_clocks; + } + } + + if (fhci->fullspeed_clk == QE_CLK_NONE && + fhci->lowspeed_clk == QE_CLK_NONE) { + dev_err(dev, "no clocks specified\n"); + ret = -EINVAL; + goto err_clocks; + } + + dev_info(dev, "at 0x%p, irq %d\n", hcd->regs, usb_irq); + + fhci_config_transceiver(fhci, FHCI_PORT_POWER_OFF); + + /* Start with full-speed, if possible. */ + if (fhci->fullspeed_clk != QE_CLK_NONE) { + fhci_config_transceiver(fhci, FHCI_PORT_FULL); + qe_usb_clock_set(fhci->fullspeed_clk, USB_CLOCK); + } else { + fhci_config_transceiver(fhci, FHCI_PORT_LOW); + qe_usb_clock_set(fhci->lowspeed_clk, USB_CLOCK >> 3); + } + + /* Clear and disable any pending interrupts. */ + out_be16(&fhci->regs->usb_event, 0xffff); + out_be16(&fhci->regs->usb_mask, 0); + + ret = usb_add_hcd(hcd, usb_irq, IRQF_DISABLED); + if (ret < 0) + goto err_add_hcd; + + fhci_dfs_create(fhci); + + return 0; + +err_add_hcd: +err_clocks: + irq_dispose_mapping(usb_irq); +err_usb_irq: + free_irq(fhci->timer->irq, hcd); +err_timer_irq: + gtm_put_timer16(fhci->timer); +err_get_timer: +err_pins: + while (--j >= 0) + qe_pin_free(fhci->pins[j]); +err_gpios: + while (--i >= 0) { + if (gpio_is_valid(fhci->gpios[i])) + gpio_free(fhci->gpios[i]); + } + cpm_muram_free(pram_addr); +err_pram: + iounmap(hcd->regs); +err_regs: + usb_put_hcd(hcd); + return ret; +} + +static int __devexit fhci_remove(struct device *dev) +{ + struct usb_hcd *hcd = dev_get_drvdata(dev); + struct fhci_hcd *fhci = hcd_to_fhci(hcd); + int i; + int j; + + usb_remove_hcd(hcd); + free_irq(fhci->timer->irq, hcd); + gtm_put_timer16(fhci->timer); + cpm_muram_free(cpm_muram_offset(fhci->pram)); + for (i = 0; i < NUM_GPIOS; i++) { + if (!gpio_is_valid(fhci->gpios[i])) + continue; + gpio_free(fhci->gpios[i]); + } + for (j = 0; j < NUM_PINS; j++) + qe_pin_free(fhci->pins[j]); + fhci_dfs_destroy(fhci); + usb_put_hcd(hcd); + return 0; +} + +static int __devexit of_fhci_remove(struct of_device *ofdev) +{ + return fhci_remove(&ofdev->dev); +} + +static struct of_device_id of_fhci_match[] = { + { .compatible = "fsl,mpc8323-qe-usb", }, + {}, +}; +MODULE_DEVICE_TABLE(of, of_fhci_match); + +static struct of_platform_driver of_fhci_driver = { + .name = "fsl,usb-fhci", + .match_table = of_fhci_match, + .probe = of_fhci_probe, + .remove = __devexit_p(of_fhci_remove), +}; + +static int __init fhci_module_init(void) +{ + return of_register_platform_driver(&of_fhci_driver); +} +module_init(fhci_module_init); + +static void __exit fhci_module_exit(void) +{ + of_unregister_platform_driver(&of_fhci_driver); +} +module_exit(fhci_module_exit); + +MODULE_DESCRIPTION("USB Freescale Host Controller Interface Driver"); +MODULE_AUTHOR("Shlomi Gridish , " + "Jerry Huang , " + "Anton Vorontsov "); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/host/fhci-hub.c b/drivers/usb/host/fhci-hub.c new file mode 100644 index 00000000000..0cfaedc3e12 --- /dev/null +++ b/drivers/usb/host/fhci-hub.c @@ -0,0 +1,345 @@ +/* + * Freescale QUICC Engine USB Host Controller Driver + * + * Copyright (c) Freescale Semicondutor, Inc. 2006. + * Shlomi Gridish + * Jerry Huang + * Copyright (c) Logic Product Development, Inc. 2007 + * Peter Barada + * Copyright (c) MontaVista Software, Inc. 2008. + * Anton Vorontsov + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../core/hcd.h" +#include "fhci.h" + +/* virtual root hub specific descriptor */ +static u8 root_hub_des[] = { + 0x09, /* blength */ + 0x29, /* bDescriptorType;hub-descriptor */ + 0x01, /* bNbrPorts */ + 0x00, /* wHubCharacteristics */ + 0x00, + 0x01, /* bPwrOn2pwrGood;2ms */ + 0x00, /* bHubContrCurrent;0mA */ + 0x00, /* DeviceRemoveable */ + 0xff, /* PortPwrCtrlMask */ +}; + +static void fhci_gpio_set_value(struct fhci_hcd *fhci, int gpio_nr, bool on) +{ + int gpio = fhci->gpios[gpio_nr]; + bool alow = fhci->alow_gpios[gpio_nr]; + + if (!gpio_is_valid(gpio)) + return; + + gpio_set_value(gpio, on ^ alow); + mdelay(5); +} + +void fhci_config_transceiver(struct fhci_hcd *fhci, + enum fhci_port_status status) +{ + fhci_dbg(fhci, "-> %s: %d\n", __func__, status); + + switch (status) { + case FHCI_PORT_POWER_OFF: + fhci_gpio_set_value(fhci, GPIO_POWER, false); + break; + case FHCI_PORT_DISABLED: + case FHCI_PORT_WAITING: + fhci_gpio_set_value(fhci, GPIO_POWER, true); + break; + case FHCI_PORT_LOW: + fhci_gpio_set_value(fhci, GPIO_SPEED, false); + break; + case FHCI_PORT_FULL: + fhci_gpio_set_value(fhci, GPIO_SPEED, true); + break; + default: + WARN_ON(1); + break; + } + + fhci_dbg(fhci, "<- %s: %d\n", __func__, status); +} + +/* disable the USB port by clearing the EN bit in the USBMOD register */ +void fhci_port_disable(struct fhci_hcd *fhci) +{ + struct fhci_usb *usb = (struct fhci_usb *)fhci->usb_lld; + enum fhci_port_status port_status; + + fhci_dbg(fhci, "-> %s\n", __func__); + + fhci_stop_sof_timer(fhci); + + fhci_flush_all_transmissions(usb); + + fhci_usb_disable_interrupt((struct fhci_usb *)fhci->usb_lld); + port_status = usb->port_status; + usb->port_status = FHCI_PORT_DISABLED; + + /* Enable IDLE since we want to know if something comes along */ + usb->saved_msk |= USB_E_IDLE_MASK; + out_be16(&usb->fhci->regs->usb_mask, usb->saved_msk); + + /* check if during the disconnection process attached new device */ + if (port_status == FHCI_PORT_WAITING) + fhci_device_connected_interrupt(fhci); + usb->vroot_hub->port.wPortStatus &= ~USB_PORT_STAT_ENABLE; + usb->vroot_hub->port.wPortChange |= USB_PORT_STAT_C_ENABLE; + fhci_usb_enable_interrupt((struct fhci_usb *)fhci->usb_lld); + + fhci_dbg(fhci, "<- %s\n", __func__); +} + +/* enable the USB port by setting the EN bit in the USBMOD register */ +void fhci_port_enable(void *lld) +{ + struct fhci_usb *usb = (struct fhci_usb *)lld; + struct fhci_hcd *fhci = usb->fhci; + + fhci_dbg(fhci, "-> %s\n", __func__); + + fhci_config_transceiver(fhci, usb->port_status); + + if ((usb->port_status != FHCI_PORT_FULL) && + (usb->port_status != FHCI_PORT_LOW)) + fhci_start_sof_timer(fhci); + + usb->vroot_hub->port.wPortStatus |= USB_PORT_STAT_ENABLE; + usb->vroot_hub->port.wPortChange |= USB_PORT_STAT_C_ENABLE; + + fhci_dbg(fhci, "<- %s\n", __func__); +} + +void fhci_io_port_generate_reset(struct fhci_hcd *fhci) +{ + fhci_dbg(fhci, "-> %s\n", __func__); + + gpio_direction_output(fhci->gpios[GPIO_USBOE], 0); + gpio_direction_output(fhci->gpios[GPIO_USBTP], 0); + gpio_direction_output(fhci->gpios[GPIO_USBTN], 0); + + mdelay(5); + + qe_pin_set_dedicated(fhci->pins[PIN_USBOE]); + qe_pin_set_dedicated(fhci->pins[PIN_USBTP]); + qe_pin_set_dedicated(fhci->pins[PIN_USBTN]); + + fhci_dbg(fhci, "<- %s\n", __func__); +} + +/* generate the RESET condition on the bus */ +void fhci_port_reset(void *lld) +{ + struct fhci_usb *usb = (struct fhci_usb *)lld; + struct fhci_hcd *fhci = usb->fhci; + u8 mode; + u16 mask; + + fhci_dbg(fhci, "-> %s\n", __func__); + + fhci_stop_sof_timer(fhci); + /* disable the USB controller */ + mode = in_8(&fhci->regs->usb_mod); + out_8(&fhci->regs->usb_mod, mode & (~USB_MODE_EN)); + + /* disable idle interrupts */ + mask = in_be16(&fhci->regs->usb_mask); + out_be16(&fhci->regs->usb_mask, mask & (~USB_E_IDLE_MASK)); + + fhci_io_port_generate_reset(fhci); + + /* enable interrupt on this endpoint */ + out_be16(&fhci->regs->usb_mask, mask); + + /* enable the USB controller */ + mode = in_8(&fhci->regs->usb_mod); + out_8(&fhci->regs->usb_mod, mode | USB_MODE_EN); + fhci_start_sof_timer(fhci); + + fhci_dbg(fhci, "<- %s\n", __func__); +} + +int fhci_hub_status_data(struct usb_hcd *hcd, char *buf) +{ + struct fhci_hcd *fhci = hcd_to_fhci(hcd); + int ret = 0; + unsigned long flags; + + fhci_dbg(fhci, "-> %s\n", __func__); + + spin_lock_irqsave(&fhci->lock, flags); + + if (fhci->vroot_hub->port.wPortChange & (USB_PORT_STAT_C_CONNECTION | + USB_PORT_STAT_C_ENABLE | USB_PORT_STAT_C_SUSPEND | + USB_PORT_STAT_C_RESET | USB_PORT_STAT_C_OVERCURRENT)) { + *buf = 1 << 1; + ret = 1; + fhci_dbg(fhci, "-- %s\n", __func__); + } + + spin_unlock_irqrestore(&fhci->lock, flags); + + fhci_dbg(fhci, "<- %s\n", __func__); + + return ret; +} + +int fhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, + u16 wIndex, char *buf, u16 wLength) +{ + struct fhci_hcd *fhci = hcd_to_fhci(hcd); + int retval = 0; + int len = 0; + struct usb_hub_status *hub_status; + struct usb_port_status *port_status; + unsigned long flags; + + spin_lock_irqsave(&fhci->lock, flags); + + fhci_dbg(fhci, "-> %s\n", __func__); + + switch (typeReq) { + case ClearHubFeature: + switch (wValue) { + case C_HUB_LOCAL_POWER: + case C_HUB_OVER_CURRENT: + break; + default: + goto error; + } + break; + case ClearPortFeature: + fhci->vroot_hub->feature &= (1 << wValue); + + switch (wValue) { + case USB_PORT_FEAT_ENABLE: + fhci->vroot_hub->port.wPortStatus &= + ~USB_PORT_STAT_ENABLE; + fhci_port_disable(fhci); + break; + case USB_PORT_FEAT_C_ENABLE: + fhci->vroot_hub->port.wPortChange &= + ~USB_PORT_STAT_C_ENABLE; + break; + case USB_PORT_FEAT_SUSPEND: + fhci->vroot_hub->port.wPortStatus &= + ~USB_PORT_STAT_SUSPEND; + fhci_stop_sof_timer(fhci); + break; + case USB_PORT_FEAT_C_SUSPEND: + fhci->vroot_hub->port.wPortChange &= + ~USB_PORT_STAT_C_SUSPEND; + break; + case USB_PORT_FEAT_POWER: + fhci->vroot_hub->port.wPortStatus &= + ~USB_PORT_STAT_POWER; + fhci_config_transceiver(fhci, FHCI_PORT_POWER_OFF); + break; + case USB_PORT_FEAT_C_CONNECTION: + fhci->vroot_hub->port.wPortChange &= + ~USB_PORT_STAT_C_CONNECTION; + break; + case USB_PORT_FEAT_C_OVER_CURRENT: + fhci->vroot_hub->port.wPortChange &= + ~USB_PORT_STAT_C_OVERCURRENT; + break; + case USB_PORT_FEAT_C_RESET: + fhci->vroot_hub->port.wPortChange &= + ~USB_PORT_STAT_C_RESET; + break; + default: + goto error; + } + break; + case GetHubDescriptor: + memcpy(buf, root_hub_des, sizeof(root_hub_des)); + buf[3] = 0x11; /* per-port power, no ovrcrnt */ + len = (buf[0] < wLength) ? buf[0] : wLength; + break; + case GetHubStatus: + hub_status = (struct usb_hub_status *)buf; + hub_status->wHubStatus = + cpu_to_le16(fhci->vroot_hub->hub.wHubStatus); + hub_status->wHubChange = + cpu_to_le16(fhci->vroot_hub->hub.wHubChange); + len = 4; + break; + case GetPortStatus: + port_status = (struct usb_port_status *)buf; + port_status->wPortStatus = + cpu_to_le16(fhci->vroot_hub->port.wPortStatus); + port_status->wPortChange = + cpu_to_le16(fhci->vroot_hub->port.wPortChange); + len = 4; + break; + case SetHubFeature: + switch (wValue) { + case C_HUB_OVER_CURRENT: + case C_HUB_LOCAL_POWER: + break; + default: + goto error; + } + break; + case SetPortFeature: + fhci->vroot_hub->feature |= (1 << wValue); + + switch (wValue) { + case USB_PORT_FEAT_ENABLE: + fhci->vroot_hub->port.wPortStatus |= + USB_PORT_STAT_ENABLE; + fhci_port_enable(fhci->usb_lld); + break; + case USB_PORT_FEAT_SUSPEND: + fhci->vroot_hub->port.wPortStatus |= + USB_PORT_STAT_SUSPEND; + fhci_stop_sof_timer(fhci); + break; + case USB_PORT_FEAT_RESET: + fhci->vroot_hub->port.wPortStatus |= + USB_PORT_STAT_RESET; + fhci_port_reset(fhci->usb_lld); + fhci->vroot_hub->port.wPortStatus |= + USB_PORT_STAT_ENABLE; + fhci->vroot_hub->port.wPortStatus &= + ~USB_PORT_STAT_RESET; + break; + case USB_PORT_FEAT_POWER: + fhci->vroot_hub->port.wPortStatus |= + USB_PORT_STAT_POWER; + fhci_config_transceiver(fhci, FHCI_PORT_WAITING); + break; + default: + goto error; + } + break; + default: +error: + retval = -EPIPE; + } + + fhci_dbg(fhci, "<- %s\n", __func__); + + spin_unlock_irqrestore(&fhci->lock, flags); + + return retval; +} diff --git a/drivers/usb/host/fhci-mem.c b/drivers/usb/host/fhci-mem.c new file mode 100644 index 00000000000..2c0736c9971 --- /dev/null +++ b/drivers/usb/host/fhci-mem.c @@ -0,0 +1,113 @@ +/* + * Freescale QUICC Engine USB Host Controller Driver + * + * Copyright (c) Freescale Semicondutor, Inc. 2006. + * Shlomi Gridish + * Jerry Huang + * Copyright (c) Logic Product Development, Inc. 2007 + * Peter Barada + * Copyright (c) MontaVista Software, Inc. 2008. + * Anton Vorontsov + * + * 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. + */ + +#include +#include +#include +#include +#include +#include "../core/hcd.h" +#include "fhci.h" + +static void init_td(struct td *td) +{ + memset(td, 0, sizeof(*td)); + INIT_LIST_HEAD(&td->node); + INIT_LIST_HEAD(&td->frame_lh); +} + +static void init_ed(struct ed *ed) +{ + memset(ed, 0, sizeof(*ed)); + INIT_LIST_HEAD(&ed->td_list); + INIT_LIST_HEAD(&ed->node); +} + +static struct td *get_empty_td(struct fhci_hcd *fhci) +{ + struct td *td; + + if (!list_empty(&fhci->empty_tds)) { + td = list_entry(fhci->empty_tds.next, struct td, node); + list_del(fhci->empty_tds.next); + } else { + td = kmalloc(sizeof(*td), GFP_ATOMIC); + if (!td) + fhci_err(fhci, "No memory to allocate to TD\n"); + else + init_td(td); + } + + return td; +} + +void fhci_recycle_empty_td(struct fhci_hcd *fhci, struct td *td) +{ + init_td(td); + list_add(&td->node, &fhci->empty_tds); +} + +struct ed *fhci_get_empty_ed(struct fhci_hcd *fhci) +{ + struct ed *ed; + + if (!list_empty(&fhci->empty_eds)) { + ed = list_entry(fhci->empty_eds.next, struct ed, node); + list_del(fhci->empty_eds.next); + } else { + ed = kmalloc(sizeof(*ed), GFP_ATOMIC); + if (!ed) + fhci_err(fhci, "No memory to allocate to ED\n"); + else + init_ed(ed); + } + + return ed; +} + +void fhci_recycle_empty_ed(struct fhci_hcd *fhci, struct ed *ed) +{ + init_ed(ed); + list_add(&ed->node, &fhci->empty_eds); +} + +struct td *fhci_td_fill(struct fhci_hcd *fhci, struct urb *urb, + struct urb_priv *urb_priv, struct ed *ed, u16 index, + enum fhci_ta_type type, int toggle, u8 *data, u32 len, + u16 interval, u16 start_frame, bool ioc) +{ + struct td *td = get_empty_td(fhci); + + if (!td) + return NULL; + + td->urb = urb; + td->ed = ed; + td->type = type; + td->toggle = toggle; + td->data = data; + td->len = len; + td->iso_index = index; + td->interval = interval; + td->start_frame = start_frame; + td->ioc = ioc; + td->status = USB_TD_OK; + + urb_priv->tds[index] = td; + + return td; +} diff --git a/drivers/usb/host/fhci-q.c b/drivers/usb/host/fhci-q.c new file mode 100644 index 00000000000..b0a1446ba29 --- /dev/null +++ b/drivers/usb/host/fhci-q.c @@ -0,0 +1,284 @@ +/* + * Freescale QUICC Engine USB Host Controller Driver + * + * Copyright (c) Freescale Semicondutor, Inc. 2006. + * Shlomi Gridish + * Jerry Huang + * Copyright (c) Logic Product Development, Inc. 2007 + * Peter Barada + * Copyright (c) MontaVista Software, Inc. 2008. + * Anton Vorontsov + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include "../core/hcd.h" +#include "fhci.h" + +/* maps the hardware error code to the USB error code */ +static int status_to_error(u32 status) +{ + if (status == USB_TD_OK) + return 0; + else if (status & USB_TD_RX_ER_CRC) + return -EILSEQ; + else if (status & USB_TD_RX_ER_NONOCT) + return -EPROTO; + else if (status & USB_TD_RX_ER_OVERUN) + return -ECOMM; + else if (status & USB_TD_RX_ER_BITSTUFF) + return -EPROTO; + else if (status & USB_TD_RX_ER_PID) + return -EILSEQ; + else if (status & (USB_TD_TX_ER_NAK | USB_TD_TX_ER_TIMEOUT)) + return -ETIMEDOUT; + else if (status & USB_TD_TX_ER_STALL) + return -EPIPE; + else if (status & USB_TD_TX_ER_UNDERUN) + return -ENOSR; + else if (status & USB_TD_RX_DATA_UNDERUN) + return -EREMOTEIO; + else if (status & USB_TD_RX_DATA_OVERUN) + return -EOVERFLOW; + else + return -EINVAL; +} + +void fhci_add_td_to_frame(struct fhci_time_frame *frame, struct td *td) +{ + list_add_tail(&td->frame_lh, &frame->tds_list); +} + +void fhci_add_tds_to_ed(struct ed *ed, struct td **td_list, int number) +{ + int i; + + for (i = 0; i < number; i++) { + struct td *td = td_list[i]; + list_add_tail(&td->node, &ed->td_list); + } + if (ed->td_head == NULL) + ed->td_head = td_list[0]; +} + +static struct td *peek_td_from_ed(struct ed *ed) +{ + struct td *td; + + if (!list_empty(&ed->td_list)) + td = list_entry(ed->td_list.next, struct td, node); + else + td = NULL; + + return td; +} + +struct td *fhci_remove_td_from_frame(struct fhci_time_frame *frame) +{ + struct td *td; + + if (!list_empty(&frame->tds_list)) { + td = list_entry(frame->tds_list.next, struct td, frame_lh); + list_del_init(frame->tds_list.next); + } else + td = NULL; + + return td; +} + +struct td *fhci_peek_td_from_frame(struct fhci_time_frame *frame) +{ + struct td *td; + + if (!list_empty(&frame->tds_list)) + td = list_entry(frame->tds_list.next, struct td, frame_lh); + else + td = NULL; + + return td; +} + +struct td *fhci_remove_td_from_ed(struct ed *ed) +{ + struct td *td; + + if (!list_empty(&ed->td_list)) { + td = list_entry(ed->td_list.next, struct td, node); + list_del_init(ed->td_list.next); + + /* if this TD was the ED's head, find next TD */ + if (!list_empty(&ed->td_list)) + ed->td_head = list_entry(ed->td_list.next, struct td, + node); + else + ed->td_head = NULL; + } else + td = NULL; + + return td; +} + +struct td *fhci_remove_td_from_done_list(struct fhci_controller_list *p_list) +{ + struct td *td; + + if (!list_empty(&p_list->done_list)) { + td = list_entry(p_list->done_list.next, struct td, node); + list_del_init(p_list->done_list.next); + } else + td = NULL; + + return td; +} + +void fhci_move_td_from_ed_to_done_list(struct fhci_usb *usb, struct ed *ed) +{ + struct td *td; + + td = ed->td_head; + list_del_init(&td->node); + + /* If this TD was the ED's head,find next TD */ + if (!list_empty(&ed->td_list)) + ed->td_head = list_entry(ed->td_list.next, struct td, node); + else { + ed->td_head = NULL; + ed->state = FHCI_ED_SKIP; + } + ed->toggle_carry = td->toggle; + list_add_tail(&td->node, &usb->hc_list->done_list); + if (td->ioc) + usb->transfer_confirm(usb->fhci); +} + +/* free done FHCI URB resource such as ED and TD */ +static void free_urb_priv(struct fhci_hcd *fhci, struct urb *urb) +{ + int i; + struct urb_priv *urb_priv = urb->hcpriv; + struct ed *ed = urb_priv->ed; + + for (i = 0; i < urb_priv->num_of_tds; i++) { + list_del_init(&urb_priv->tds[i]->node); + fhci_recycle_empty_td(fhci, urb_priv->tds[i]); + } + + /* if this TD was the ED's head,find the next TD */ + if (!list_empty(&ed->td_list)) + ed->td_head = list_entry(ed->td_list.next, struct td, node); + else + ed->td_head = NULL; + + kfree(urb_priv->tds); + kfree(urb_priv); + urb->hcpriv = NULL; + + /* if this TD was the ED's head,find next TD */ + if (ed->td_head == NULL) + list_del_init(&ed->node); + fhci->active_urbs--; +} + +/* this routine called to complete and free done URB */ +void fhci_urb_complete_free(struct fhci_hcd *fhci, struct urb *urb) +{ + free_urb_priv(fhci, urb); + + if (urb->status == -EINPROGRESS) { + if (urb->actual_length != urb->transfer_buffer_length && + urb->transfer_flags & URB_SHORT_NOT_OK) + urb->status = -EREMOTEIO; + else + urb->status = 0; + } + + usb_hcd_unlink_urb_from_ep(fhci_to_hcd(fhci), urb); + + spin_unlock(&fhci->lock); + + usb_hcd_giveback_urb(fhci_to_hcd(fhci), urb, urb->status); + + spin_lock(&fhci->lock); +} + +/* + * caculate transfer length/stats and update the urb + * Precondition: irqsafe(only for urb-?status locking) + */ +void fhci_done_td(struct urb *urb, struct td *td) +{ + struct ed *ed = td->ed; + u32 cc = td->status; + + /* ISO...drivers see per-TD length/status */ + if (ed->mode == FHCI_TF_ISO) { + u32 len; + if (!(urb->transfer_flags & URB_SHORT_NOT_OK && + cc == USB_TD_RX_DATA_UNDERUN)) + cc = USB_TD_OK; + + if (usb_pipeout(urb->pipe)) + len = urb->iso_frame_desc[td->iso_index].length; + else + len = td->actual_len; + + urb->actual_length += len; + urb->iso_frame_desc[td->iso_index].actual_length = len; + urb->iso_frame_desc[td->iso_index].status = + status_to_error(cc); + } + + /* BULK,INT,CONTROL... drivers see aggregate length/status, + * except that "setup" bytes aren't counted and "short" transfers + * might not be reported as errors. + */ + else { + if (td->error_cnt >= 3) + urb->error_count = 3; + + /* control endpoint only have soft stalls */ + + /* update packet status if needed(short may be ok) */ + if (!(urb->transfer_flags & URB_SHORT_NOT_OK) && + cc == USB_TD_RX_DATA_UNDERUN) { + ed->state = FHCI_ED_OPER; + cc = USB_TD_OK; + } + if (cc != USB_TD_OK) { + if (urb->status == -EINPROGRESS) + urb->status = status_to_error(cc); + } + + /* count all non-empty packets except control SETUP packet */ + if (td->type != FHCI_TA_SETUP || td->iso_index != 0) + urb->actual_length += td->actual_len; + } +} + +/* there are some pedning request to unlink */ +void fhci_del_ed_list(struct fhci_hcd *fhci, struct ed *ed) +{ + struct td *td = peek_td_from_ed(ed); + struct urb *urb = td->urb; + struct urb_priv *urb_priv = urb->hcpriv; + + if (urb_priv->state == URB_DEL) { + td = fhci_remove_td_from_ed(ed); + /* HC may have partly processed this TD */ + if (td->status != USB_TD_INPROGRESS) + fhci_done_td(urb, td); + + /* URB is done;clean up */ + if (++(urb_priv->tds_cnt) == urb_priv->num_of_tds) + fhci_urb_complete_free(fhci, urb); + } +} diff --git a/drivers/usb/host/fhci-sched.c b/drivers/usb/host/fhci-sched.c new file mode 100644 index 00000000000..bb63b68ddb7 --- /dev/null +++ b/drivers/usb/host/fhci-sched.c @@ -0,0 +1,888 @@ +/* + * Freescale QUICC Engine USB Host Controller Driver + * + * Copyright (c) Freescale Semicondutor, Inc. 2006. + * Shlomi Gridish + * Jerry Huang + * Copyright (c) Logic Product Development, Inc. 2007 + * Peter Barada + * Copyright (c) MontaVista Software, Inc. 2008. + * Anton Vorontsov + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../core/hcd.h" +#include "fhci.h" + +static void recycle_frame(struct fhci_usb *usb, struct packet *pkt) +{ + pkt->data = NULL; + pkt->len = 0; + pkt->status = USB_TD_OK; + pkt->info = 0; + pkt->priv_data = NULL; + + cq_put(usb->ep0->empty_frame_Q, pkt); +} + +/* confirm submitted packet */ +void fhci_transaction_confirm(struct fhci_usb *usb, struct packet *pkt) +{ + struct td *td; + struct packet *td_pkt; + struct ed *ed; + u32 trans_len; + bool td_done = false; + + td = fhci_remove_td_from_frame(usb->actual_frame); + td_pkt = td->pkt; + trans_len = pkt->len; + td->status = pkt->status; + if (td->type == FHCI_TA_IN && td_pkt->info & PKT_DUMMY_PACKET) { + if ((td->data + td->actual_len) && trans_len) + memcpy(td->data + td->actual_len, pkt->data, + trans_len); + cq_put(usb->ep0->dummy_packets_Q, pkt->data); + } + + recycle_frame(usb, pkt); + + ed = td->ed; + if (ed->mode == FHCI_TF_ISO) { + if (ed->td_list.next->next != &ed->td_list) { + struct td *td_next = + list_entry(ed->td_list.next->next, struct td, + node); + + td_next->start_frame = usb->actual_frame->frame_num; + } + td->actual_len = trans_len; + td_done = true; + } else if ((td->status & USB_TD_ERROR) && + !(td->status & USB_TD_TX_ER_NAK)) { + /* + * There was an error on the transaction (but not NAK). + * If it is fatal error (data underrun, stall, bad pid or 3 + * errors exceeded), mark this TD as done. + */ + if ((td->status & USB_TD_RX_DATA_UNDERUN) || + (td->status & USB_TD_TX_ER_STALL) || + (td->status & USB_TD_RX_ER_PID) || + (++td->error_cnt >= 3)) { + ed->state = FHCI_ED_HALTED; + td_done = true; + + if (td->status & USB_TD_RX_DATA_UNDERUN) { + fhci_dbg(usb->fhci, "td err fu\n"); + td->toggle = !td->toggle; + td->actual_len += trans_len; + } else { + fhci_dbg(usb->fhci, "td err f!u\n"); + } + } else { + fhci_dbg(usb->fhci, "td err !f\n"); + /* it is not a fatal error -retry this transaction */ + td->nak_cnt = 0; + td->error_cnt++; + td->status = USB_TD_OK; + } + } else if (td->status & USB_TD_TX_ER_NAK) { + /* there was a NAK response */ + fhci_vdbg(usb->fhci, "td nack\n"); + td->nak_cnt++; + td->error_cnt = 0; + td->status = USB_TD_OK; + } else { + /* there was no error on transaction */ + td->error_cnt = 0; + td->nak_cnt = 0; + td->toggle = !td->toggle; + td->actual_len += trans_len; + + if (td->len == td->actual_len) + td_done = true; + } + + if (td_done) + fhci_move_td_from_ed_to_done_list(usb, ed); +} + +/* + * Flush all transmitted packets from BDs + * This routine is called when disabling the USB port to flush all + * transmissions that are allready scheduled in the BDs + */ +void fhci_flush_all_transmissions(struct fhci_usb *usb) +{ + u8 mode; + struct td *td; + + mode = in_8(&usb->fhci->regs->usb_mod); + clrbits8(&usb->fhci->regs->usb_mod, USB_MODE_EN); + + fhci_flush_bds(usb); + + while ((td = fhci_peek_td_from_frame(usb->actual_frame)) != NULL) { + struct packet *pkt = td->pkt; + + pkt->status = USB_TD_TX_ER_TIMEOUT; + fhci_transaction_confirm(usb, pkt); + } + + usb->actual_frame->frame_status = FRAME_END_TRANSMISSION; + + /* reset the event register */ + out_be16(&usb->fhci->regs->usb_event, 0xffff); + /* enable the USB controller */ + out_8(&usb->fhci->regs->usb_mod, mode | USB_MODE_EN); +} + +/* + * This function forms the packet and transmit the packet. This function + * will handle all endpoint type:ISO,interrupt,control and bulk + */ +static int add_packet(struct fhci_usb *usb, struct ed *ed, struct td *td) +{ + u32 fw_transaction_time, len = 0; + struct packet *pkt; + u8 *data = NULL; + + /* calcalate data address,len and toggle and then add the transaction */ + if (td->toggle == USB_TD_TOGGLE_CARRY) + td->toggle = ed->toggle_carry; + + switch (ed->mode) { + case FHCI_TF_ISO: + len = td->len; + if (td->type != FHCI_TA_IN) + data = td->data; + break; + case FHCI_TF_CTRL: + case FHCI_TF_BULK: + len = min(td->len - td->actual_len, ed->max_pkt_size); + if (!((td->type == FHCI_TA_IN) && + ((len + td->actual_len) == td->len))) + data = td->data + td->actual_len; + break; + case FHCI_TF_INTR: + len = min(td->len, ed->max_pkt_size); + if (!((td->type == FHCI_TA_IN) && + ((td->len + CRC_SIZE) >= ed->max_pkt_size))) + data = td->data; + break; + default: + break; + } + + if (usb->port_status == FHCI_PORT_FULL) + fw_transaction_time = (((len + PROTOCOL_OVERHEAD) * 11) >> 4); + else + fw_transaction_time = ((len + PROTOCOL_OVERHEAD) * 6); + + /* check if there's enough space in this frame to submit this TD */ + if (usb->actual_frame->total_bytes + len + PROTOCOL_OVERHEAD >= + usb->max_bytes_per_frame) { + fhci_vdbg(usb->fhci, "not enough space in this frame: " + "%d %d %d\n", usb->actual_frame->total_bytes, len, + usb->max_bytes_per_frame); + return -1; + } + + /* check if there's enough time in this frame to submit this TD */ + if (usb->actual_frame->frame_status != FRAME_IS_PREPARED && + (usb->actual_frame->frame_status & FRAME_END_TRANSMISSION || + (fw_transaction_time + usb->sw_transaction_time >= + 1000 - fhci_get_sof_timer_count(usb)))) { + fhci_dbg(usb->fhci, "not enough time in this frame\n"); + return -1; + } + + /* update frame object fields before transmitting */ + pkt = cq_get(usb->ep0->empty_frame_Q); + if (!pkt) { + fhci_dbg(usb->fhci, "there is no empty frame\n"); + return -1; + } + td->pkt = pkt; + + pkt->info = 0; + if (data == NULL) { + data = cq_get(usb->ep0->dummy_packets_Q); + BUG_ON(!data); + pkt->info = PKT_DUMMY_PACKET; + } + pkt->data = data; + pkt->len = len; + pkt->status = USB_TD_OK; + /* update TD status field before transmitting */ + td->status = USB_TD_INPROGRESS; + /* update actual frame time object with the actual transmission */ + usb->actual_frame->total_bytes += (len + PROTOCOL_OVERHEAD); + fhci_add_td_to_frame(usb->actual_frame, td); + + if (usb->port_status != FHCI_PORT_FULL && + usb->port_status != FHCI_PORT_LOW) { + pkt->status = USB_TD_TX_ER_TIMEOUT; + pkt->len = 0; + fhci_transaction_confirm(usb, pkt); + } else if (fhci_host_transaction(usb, pkt, td->type, ed->dev_addr, + ed->ep_addr, ed->mode, ed->speed, td->toggle)) { + /* remove TD from actual frame */ + list_del_init(&td->frame_lh); + td->status = USB_TD_OK; + if (pkt->info & PKT_DUMMY_PACKET) + cq_put(usb->ep0->dummy_packets_Q, pkt->data); + recycle_frame(usb, pkt); + usb->actual_frame->total_bytes -= (len + PROTOCOL_OVERHEAD); + fhci_err(usb->fhci, "host transaction failed\n"); + return -1; + } + + return len; +} + +static void move_head_to_tail(struct list_head *list) +{ + struct list_head *node = list->next; + + if (!list_empty(list)) { + list_del(node); + list_add_tail(node, list); + } +} + +/* + * This function goes through the endpoint list and schedules the + * transactions within this list + */ +static int scan_ed_list(struct fhci_usb *usb, + struct list_head *list, enum fhci_tf_mode list_type) +{ + static const int frame_part[4] = { + [FHCI_TF_CTRL] = MAX_BYTES_PER_FRAME, + [FHCI_TF_ISO] = (MAX_BYTES_PER_FRAME * + MAX_PERIODIC_FRAME_USAGE) / 100, + [FHCI_TF_BULK] = MAX_BYTES_PER_FRAME, + [FHCI_TF_INTR] = (MAX_BYTES_PER_FRAME * + MAX_PERIODIC_FRAME_USAGE) / 100 + }; + struct ed *ed; + struct td *td; + int ans = 1; + u32 save_transaction_time = usb->sw_transaction_time; + + list_for_each_entry(ed, list, node) { + td = ed->td_head; + + if (!td || (td && td->status == USB_TD_INPROGRESS)) + continue; + + if (ed->state != FHCI_ED_OPER) { + if (ed->state == FHCI_ED_URB_DEL) { + td->status = USB_TD_OK; + fhci_move_td_from_ed_to_done_list(usb, ed); + ed->state = FHCI_ED_SKIP; + } + continue; + } + + /* + * if it isn't interrupt pipe or it is not iso pipe and the + * interval time passed + */ + if ((list_type == FHCI_TF_INTR || list_type == FHCI_TF_ISO) && + (((usb->actual_frame->frame_num - + td->start_frame) & 0x7ff) < td->interval)) + continue; + + if (add_packet(usb, ed, td) < 0) + continue; + + /* update time stamps in the TD */ + td->start_frame = usb->actual_frame->frame_num; + usb->sw_transaction_time += save_transaction_time; + + if (usb->actual_frame->total_bytes >= + usb->max_bytes_per_frame) { + usb->actual_frame->frame_status = + FRAME_DATA_END_TRANSMISSION; + fhci_push_dummy_bd(usb->ep0); + ans = 0; + break; + } + + if (usb->actual_frame->total_bytes >= frame_part[list_type]) + break; + } + + /* be fair to each ED(move list head around) */ + move_head_to_tail(list); + usb->sw_transaction_time = save_transaction_time; + + return ans; +} + +static u32 rotate_frames(struct fhci_usb *usb) +{ + struct fhci_hcd *fhci = usb->fhci; + + if (!list_empty(&usb->actual_frame->tds_list)) { + if ((((in_be16(&fhci->pram->frame_num) & 0x07ff) - + usb->actual_frame->frame_num) & 0x7ff) > 5) + fhci_flush_actual_frame(usb); + else + return -EINVAL; + } + + usb->actual_frame->frame_status = FRAME_IS_PREPARED; + usb->actual_frame->frame_num = in_be16(&fhci->pram->frame_num) & 0x7ff; + usb->actual_frame->total_bytes = 0; + + return 0; +} + +/* + * This function schedule the USB transaction and will process the + * endpoint in the following order: iso, interrupt, control and bulk. + */ +void fhci_schedule_transactions(struct fhci_usb *usb) +{ + int left = 1; + + if (usb->actual_frame->frame_status & FRAME_END_TRANSMISSION) + if (rotate_frames(usb) != 0) + return; + + if (usb->actual_frame->frame_status & FRAME_END_TRANSMISSION) + return; + + if (usb->actual_frame->total_bytes == 0) { + /* + * schedule the next available ISO transfer + *or next stage of the ISO transfer + */ + scan_ed_list(usb, &usb->hc_list->iso_list, FHCI_TF_ISO); + + /* + * schedule the next available interrupt transfer or + * the next stage of the interrupt transfer + */ + scan_ed_list(usb, &usb->hc_list->intr_list, FHCI_TF_INTR); + + /* + * schedule the next available control transfer + * or the next stage of the control transfer + */ + left = scan_ed_list(usb, &usb->hc_list->ctrl_list, + FHCI_TF_CTRL); + } + + /* + * schedule the next available bulk transfer or the next stage of the + * bulk transfer + */ + if (left > 0) + scan_ed_list(usb, &usb->hc_list->bulk_list, FHCI_TF_BULK); +} + +/* Handles SOF interrupt */ +static void sof_interrupt(struct fhci_hcd *fhci) +{ + struct fhci_usb *usb = fhci->usb_lld; + + if ((usb->port_status == FHCI_PORT_DISABLED) && + (usb->vroot_hub->port.wPortStatus & USB_PORT_STAT_CONNECTION) && + !(usb->vroot_hub->port.wPortChange & USB_PORT_STAT_C_CONNECTION)) { + if (usb->vroot_hub->port.wPortStatus & USB_PORT_STAT_LOW_SPEED) + usb->port_status = FHCI_PORT_LOW; + else + usb->port_status = FHCI_PORT_FULL; + /* Disable IDLE */ + usb->saved_msk &= ~USB_E_IDLE_MASK; + out_be16(&usb->fhci->regs->usb_mask, usb->saved_msk); + } + + gtm_set_exact_timer16(fhci->timer, usb->max_frame_usage, false); + + fhci_host_transmit_actual_frame(usb); + usb->actual_frame->frame_status = FRAME_IS_TRANSMITTED; + + fhci_schedule_transactions(usb); +} + +/* Handles device disconnected interrupt on port */ +void fhci_device_disconnected_interrupt(struct fhci_hcd *fhci) +{ + struct fhci_usb *usb = fhci->usb_lld; + + fhci_dbg(fhci, "-> %s\n", __func__); + + fhci_usb_disable_interrupt(usb); + clrbits8(&usb->fhci->regs->usb_mod, USB_MODE_LSS); + usb->port_status = FHCI_PORT_DISABLED; + + fhci_stop_sof_timer(fhci); + + /* Enable IDLE since we want to know if something comes along */ + usb->saved_msk |= USB_E_IDLE_MASK; + out_be16(&usb->fhci->regs->usb_mask, usb->saved_msk); + + usb->vroot_hub->port.wPortStatus &= ~USB_PORT_STAT_CONNECTION; + usb->vroot_hub->port.wPortChange |= USB_PORT_STAT_C_CONNECTION; + usb->max_bytes_per_frame = 0; + fhci_usb_enable_interrupt(usb); + + fhci_dbg(fhci, "<- %s\n", __func__); +} + +/* detect a new device connected on the USB port */ +void fhci_device_connected_interrupt(struct fhci_hcd *fhci) +{ + + struct fhci_usb *usb = fhci->usb_lld; + int state; + int ret; + + fhci_dbg(fhci, "-> %s\n", __func__); + + fhci_usb_disable_interrupt(usb); + state = fhci_ioports_check_bus_state(fhci); + + /* low-speed device was connected to the USB port */ + if (state == 1) { + ret = qe_usb_clock_set(fhci->lowspeed_clk, USB_CLOCK >> 3); + if (ret) { + fhci_warn(fhci, "Low-Speed device is not supported, " + "try use BRGx\n"); + goto out; + } + + usb->port_status = FHCI_PORT_LOW; + setbits8(&usb->fhci->regs->usb_mod, USB_MODE_LSS); + usb->vroot_hub->port.wPortStatus |= + (USB_PORT_STAT_LOW_SPEED | + USB_PORT_STAT_CONNECTION); + usb->vroot_hub->port.wPortChange |= + USB_PORT_STAT_C_CONNECTION; + usb->max_bytes_per_frame = + (MAX_BYTES_PER_FRAME >> 3) - 7; + fhci_port_enable(usb); + } else if (state == 2) { + ret = qe_usb_clock_set(fhci->fullspeed_clk, USB_CLOCK); + if (ret) { + fhci_warn(fhci, "Full-Speed device is not supported, " + "try use CLKx\n"); + goto out; + } + + usb->port_status = FHCI_PORT_FULL; + clrbits8(&usb->fhci->regs->usb_mod, USB_MODE_LSS); + usb->vroot_hub->port.wPortStatus &= + ~USB_PORT_STAT_LOW_SPEED; + usb->vroot_hub->port.wPortStatus |= + USB_PORT_STAT_CONNECTION; + usb->vroot_hub->port.wPortChange |= + USB_PORT_STAT_C_CONNECTION; + usb->max_bytes_per_frame = (MAX_BYTES_PER_FRAME - 15); + fhci_port_enable(usb); + } +out: + fhci_usb_enable_interrupt(usb); + fhci_dbg(fhci, "<- %s\n", __func__); +} + +irqreturn_t fhci_frame_limit_timer_irq(int irq, void *_hcd) +{ + struct usb_hcd *hcd = _hcd; + struct fhci_hcd *fhci = hcd_to_fhci(hcd); + struct fhci_usb *usb = fhci->usb_lld; + + spin_lock(&fhci->lock); + + gtm_set_exact_timer16(fhci->timer, 1000, false); + + if (usb->actual_frame->frame_status == FRAME_IS_TRANSMITTED) { + usb->actual_frame->frame_status = FRAME_TIMER_END_TRANSMISSION; + fhci_push_dummy_bd(usb->ep0); + } + + fhci_schedule_transactions(usb); + + spin_unlock(&fhci->lock); + + return IRQ_HANDLED; +} + +/* Cancel transmission on the USB endpoint */ +static void abort_transmission(struct fhci_usb *usb) +{ + fhci_dbg(usb->fhci, "-> %s\n", __func__); + /* issue stop Tx command */ + qe_issue_cmd(QE_USB_STOP_TX, QE_CR_SUBBLOCK_USB, EP_ZERO, 0); + /* flush Tx FIFOs */ + out_8(&usb->fhci->regs->usb_comm, USB_CMD_FLUSH_FIFO | EP_ZERO); + udelay(1000); + /* reset Tx BDs */ + fhci_flush_bds(usb); + /* issue restart Tx command */ + qe_issue_cmd(QE_USB_RESTART_TX, QE_CR_SUBBLOCK_USB, EP_ZERO, 0); + fhci_dbg(usb->fhci, "<- %s\n", __func__); +} + +irqreturn_t fhci_irq(struct usb_hcd *hcd) +{ + struct fhci_hcd *fhci = hcd_to_fhci(hcd); + struct fhci_usb *usb; + u16 usb_er = 0; + unsigned long flags; + + spin_lock_irqsave(&fhci->lock, flags); + + usb = fhci->usb_lld; + + usb_er |= in_be16(&usb->fhci->regs->usb_event) & + in_be16(&usb->fhci->regs->usb_mask); + + /* clear event bits for next time */ + out_be16(&usb->fhci->regs->usb_event, usb_er); + + fhci_dbg_isr(fhci, usb_er); + + if (usb_er & USB_E_RESET_MASK) { + if ((usb->port_status == FHCI_PORT_FULL) || + (usb->port_status == FHCI_PORT_LOW)) { + fhci_device_disconnected_interrupt(fhci); + usb_er &= ~USB_E_IDLE_MASK; + } else if (usb->port_status == FHCI_PORT_WAITING) { + usb->port_status = FHCI_PORT_DISCONNECTING; + + /* Turn on IDLE since we want to disconnect */ + usb->saved_msk |= USB_E_IDLE_MASK; + out_be16(&usb->fhci->regs->usb_event, + usb->saved_msk); + } else if (usb->port_status == FHCI_PORT_DISABLED) { + if (fhci_ioports_check_bus_state(fhci) == 1 && + usb->port_status != FHCI_PORT_LOW && + usb->port_status != FHCI_PORT_FULL) + fhci_device_connected_interrupt(fhci); + } + usb_er &= ~USB_E_RESET_MASK; + } + + if (usb_er & USB_E_MSF_MASK) { + abort_transmission(fhci->usb_lld); + usb_er &= ~USB_E_MSF_MASK; + } + + if (usb_er & (USB_E_SOF_MASK | USB_E_SFT_MASK)) { + sof_interrupt(fhci); + usb_er &= ~(USB_E_SOF_MASK | USB_E_SFT_MASK); + } + + if (usb_er & USB_E_TXB_MASK) { + fhci_tx_conf_interrupt(fhci->usb_lld); + usb_er &= ~USB_E_TXB_MASK; + } + + if (usb_er & USB_E_TXE1_MASK) { + fhci_tx_conf_interrupt(fhci->usb_lld); + usb_er &= ~USB_E_TXE1_MASK; + } + + if (usb_er & USB_E_IDLE_MASK) { + if (usb->port_status == FHCI_PORT_DISABLED && + usb->port_status != FHCI_PORT_LOW && + usb->port_status != FHCI_PORT_FULL) { + usb_er &= ~USB_E_RESET_MASK; + fhci_device_connected_interrupt(fhci); + } else if (usb->port_status == + FHCI_PORT_DISCONNECTING) { + /* XXX usb->port_status = FHCI_PORT_WAITING; */ + /* Disable IDLE */ + usb->saved_msk &= ~USB_E_IDLE_MASK; + out_be16(&usb->fhci->regs->usb_mask, + usb->saved_msk); + } else { + fhci_dbg_isr(fhci, -1); + } + + usb_er &= ~USB_E_IDLE_MASK; + } + + spin_unlock_irqrestore(&fhci->lock, flags); + + return IRQ_HANDLED; +} + + +/* + * Process normal completions(error or sucess) and clean the schedule. + * + * This is the main path for handing urbs back to drivers. The only other patth + * is process_del_list(),which unlinks URBs by scanning EDs,instead of scanning + * the (re-reversed) done list as this does. + */ +static void process_done_list(unsigned long data) +{ + struct urb *urb; + struct ed *ed; + struct td *td; + struct urb_priv *urb_priv; + struct fhci_hcd *fhci = (struct fhci_hcd *)data; + + disable_irq(fhci->timer->irq); + disable_irq(fhci_to_hcd(fhci)->irq); + spin_lock(&fhci->lock); + + td = fhci_remove_td_from_done_list(fhci->hc_list); + while (td != NULL) { + urb = td->urb; + urb_priv = urb->hcpriv; + ed = td->ed; + + /* update URB's length and status from TD */ + fhci_done_td(urb, td); + urb_priv->tds_cnt++; + + /* + * if all this urb's TDs are done, call complete() + * Interrupt transfers are the onley special case: + * they are reissued,until "deleted" by usb_unlink_urb + * (real work done in a SOF intr, by process_del_list) + */ + if (urb_priv->tds_cnt == urb_priv->num_of_tds) { + fhci_urb_complete_free(fhci, urb); + } else if (urb_priv->state == URB_DEL && + ed->state == FHCI_ED_SKIP) { + fhci_del_ed_list(fhci, ed); + ed->state = FHCI_ED_OPER; + } else if (ed->state == FHCI_ED_HALTED) { + urb_priv->state = URB_DEL; + ed->state = FHCI_ED_URB_DEL; + fhci_del_ed_list(fhci, ed); + ed->state = FHCI_ED_OPER; + } + + td = fhci_remove_td_from_done_list(fhci->hc_list); + } + + spin_unlock(&fhci->lock); + enable_irq(fhci->timer->irq); + enable_irq(fhci_to_hcd(fhci)->irq); +} + +DECLARE_TASKLET(fhci_tasklet, process_done_list, 0); + +/* transfer complted callback */ +u32 fhci_transfer_confirm_callback(struct fhci_hcd *fhci) +{ + if (!fhci->process_done_task->state) + tasklet_schedule(fhci->process_done_task); + return 0; +} + +/* + * adds urb to the endpoint descriptor list + * arguments: + * fhci data structure for the Low level host controller + * ep USB Host endpoint data structure + * urb USB request block data structure + */ +void fhci_queue_urb(struct fhci_hcd *fhci, struct urb *urb) +{ + struct ed *ed = urb->ep->hcpriv; + struct urb_priv *urb_priv = urb->hcpriv; + u32 data_len = urb->transfer_buffer_length; + int urb_state = 0; + int toggle = 0; + struct td *td; + u8 *data; + u16 cnt = 0; + + if (ed == NULL) { + ed = fhci_get_empty_ed(fhci); + ed->dev_addr = usb_pipedevice(urb->pipe); + ed->ep_addr = usb_pipeendpoint(urb->pipe); + switch (usb_pipetype(urb->pipe)) { + case PIPE_CONTROL: + ed->mode = FHCI_TF_CTRL; + break; + case PIPE_BULK: + ed->mode = FHCI_TF_BULK; + break; + case PIPE_INTERRUPT: + ed->mode = FHCI_TF_INTR; + break; + case PIPE_ISOCHRONOUS: + ed->mode = FHCI_TF_ISO; + break; + default: + break; + } + ed->speed = (urb->dev->speed == USB_SPEED_LOW) ? + FHCI_LOW_SPEED : FHCI_FULL_SPEED; + ed->max_pkt_size = usb_maxpacket(urb->dev, + urb->pipe, usb_pipeout(urb->pipe)); + urb->ep->hcpriv = ed; + fhci_dbg(fhci, "new ep speed=%d max_pkt_size=%d\n", + ed->speed, ed->max_pkt_size); + } + + /* for ISO transfer calculate start frame index */ + if (ed->mode == FHCI_TF_ISO && urb->transfer_flags & URB_ISO_ASAP) + urb->start_frame = ed->td_head ? ed->last_iso + 1 : + get_frame_num(fhci); + + /* + * OHCI handles the DATA toggle itself,we just use the USB + * toggle bits + */ + if (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), + usb_pipeout(urb->pipe))) + toggle = USB_TD_TOGGLE_CARRY; + else { + toggle = USB_TD_TOGGLE_DATA0; + usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), + usb_pipeout(urb->pipe), 1); + } + + urb_priv->tds_cnt = 0; + urb_priv->ed = ed; + if (data_len > 0) + data = urb->transfer_buffer; + else + data = NULL; + + switch (ed->mode) { + case FHCI_TF_BULK: + if (urb->transfer_flags & URB_ZERO_PACKET && + urb->transfer_buffer_length > 0 && + ((urb->transfer_buffer_length % + usb_maxpacket(urb->dev, urb->pipe, + usb_pipeout(urb->pipe))) == 0)) + urb_state = US_BULK0; + while (data_len > 4096) { + td = fhci_td_fill(fhci, urb, urb_priv, ed, cnt, + usb_pipeout(urb->pipe) ? FHCI_TA_OUT : + FHCI_TA_IN, + cnt ? USB_TD_TOGGLE_CARRY : + toggle, + data, 4096, 0, 0, true); + data += 4096; + data_len -= 4096; + cnt++; + } + + td = fhci_td_fill(fhci, urb, urb_priv, ed, cnt, + usb_pipeout(urb->pipe) ? FHCI_TA_OUT : FHCI_TA_IN, + cnt ? USB_TD_TOGGLE_CARRY : toggle, + data, data_len, 0, 0, true); + cnt++; + + if (urb->transfer_flags & URB_ZERO_PACKET && + cnt < urb_priv->num_of_tds) { + td = fhci_td_fill(fhci, urb, urb_priv, ed, cnt, + usb_pipeout(urb->pipe) ? FHCI_TA_OUT : + FHCI_TA_IN, + USB_TD_TOGGLE_CARRY, NULL, 0, 0, 0, true); + cnt++; + } + break; + case FHCI_TF_INTR: + urb->start_frame = get_frame_num(fhci) + 1; + td = fhci_td_fill(fhci, urb, urb_priv, ed, cnt++, + usb_pipeout(urb->pipe) ? FHCI_TA_OUT : FHCI_TA_IN, + USB_TD_TOGGLE_DATA0, data, data_len, + urb->interval, urb->start_frame, true); + break; + case FHCI_TF_CTRL: + ed->dev_addr = usb_pipedevice(urb->pipe); + ed->max_pkt_size = usb_maxpacket(urb->dev, urb->pipe, + usb_pipeout(urb->pipe)); + td = fhci_td_fill(fhci, urb, urb_priv, ed, cnt++, FHCI_TA_SETUP, + USB_TD_TOGGLE_DATA0, urb->setup_packet, 8, 0, 0, true); + + if (data_len > 0) { + td = fhci_td_fill(fhci, urb, urb_priv, ed, cnt++, + usb_pipeout(urb->pipe) ? FHCI_TA_OUT : + FHCI_TA_IN, + USB_TD_TOGGLE_DATA1, data, data_len, 0, 0, + true); + } + td = fhci_td_fill(fhci, urb, urb_priv, ed, cnt++, + usb_pipeout(urb->pipe) ? FHCI_TA_IN : FHCI_TA_OUT, + USB_TD_TOGGLE_DATA1, data, 0, 0, 0, true); + urb_state = US_CTRL_SETUP; + break; + case FHCI_TF_ISO: + for (cnt = 0; cnt < urb->number_of_packets; cnt++) { + u16 frame = urb->start_frame; + + /* + * FIXME scheduling should handle frame counter + * roll-around ... exotic case (and OHCI has + * a 2^16 iso range, vs other HCs max of 2^10) + */ + frame += cnt * urb->interval; + frame &= 0x07ff; + td = fhci_td_fill(fhci, urb, urb_priv, ed, cnt, + usb_pipeout(urb->pipe) ? FHCI_TA_OUT : + FHCI_TA_IN, + USB_TD_TOGGLE_DATA0, + data + urb->iso_frame_desc[cnt].offset, + urb->iso_frame_desc[cnt].length, + urb->interval, frame, true); + } + break; + default: + break; + } + + /* + * set the state of URB + * control pipe:3 states -- setup,data,status + * interrupt and bulk pipe:1 state -- data + */ + urb->pipe &= ~0x1f; + urb->pipe |= urb_state & 0x1f; + + urb_priv->state = URB_INPROGRESS; + + if (!ed->td_head) { + ed->state = FHCI_ED_OPER; + switch (ed->mode) { + case FHCI_TF_CTRL: + list_add(&ed->node, &fhci->hc_list->ctrl_list); + break; + case FHCI_TF_BULK: + list_add(&ed->node, &fhci->hc_list->bulk_list); + break; + case FHCI_TF_INTR: + list_add(&ed->node, &fhci->hc_list->intr_list); + break; + case FHCI_TF_ISO: + list_add(&ed->node, &fhci->hc_list->iso_list); + break; + default: + break; + } + } + + fhci_add_tds_to_ed(ed, urb_priv->tds, urb_priv->num_of_tds); + fhci->active_urbs++; +} diff --git a/drivers/usb/host/fhci-tds.c b/drivers/usb/host/fhci-tds.c new file mode 100644 index 00000000000..b4033229031 --- /dev/null +++ b/drivers/usb/host/fhci-tds.c @@ -0,0 +1,626 @@ +/* + * Freescale QUICC Engine USB Host Controller Driver + * + * Copyright (c) Freescale Semicondutor, Inc. 2006. + * Shlomi Gridish + * Jerry Huang + * Copyright (c) Logic Product Development, Inc. 2007 + * Peter Barada + * Copyright (c) MontaVista Software, Inc. 2008. + * Anton Vorontsov + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include "../core/hcd.h" +#include "fhci.h" + +#define DUMMY_BD_BUFFER 0xdeadbeef +#define DUMMY2_BD_BUFFER 0xbaadf00d + +/* Transaction Descriptors bits */ +#define TD_R 0x8000 /* ready bit */ +#define TD_W 0x2000 /* wrap bit */ +#define TD_I 0x1000 /* interrupt on completion */ +#define TD_L 0x0800 /* last */ +#define TD_TC 0x0400 /* transmit CRC */ +#define TD_CNF 0x0200 /* CNF - Must be always 1 */ +#define TD_LSP 0x0100 /* Low-speed transaction */ +#define TD_PID 0x00c0 /* packet id */ +#define TD_RXER 0x0020 /* Rx error or not */ + +#define TD_NAK 0x0010 /* No ack. */ +#define TD_STAL 0x0008 /* Stall recieved */ +#define TD_TO 0x0004 /* time out */ +#define TD_UN 0x0002 /* underrun */ +#define TD_NO 0x0010 /* Rx Non Octet Aligned Packet */ +#define TD_AB 0x0008 /* Frame Aborted */ +#define TD_CR 0x0004 /* CRC Error */ +#define TD_OV 0x0002 /* Overrun */ +#define TD_BOV 0x0001 /* Buffer Overrun */ + +#define TD_ERRORS (TD_NAK | TD_STAL | TD_TO | TD_UN | \ + TD_NO | TD_AB | TD_CR | TD_OV | TD_BOV) + +#define TD_PID_DATA0 0x0080 /* Data 0 toggle */ +#define TD_PID_DATA1 0x00c0 /* Data 1 toggle */ +#define TD_PID_TOGGLE 0x00c0 /* Data 0/1 toggle mask */ + +#define TD_TOK_SETUP 0x0000 +#define TD_TOK_OUT 0x4000 +#define TD_TOK_IN 0x8000 +#define TD_ISO 0x1000 +#define TD_ENDP 0x0780 +#define TD_ADDR 0x007f + +#define TD_ENDP_SHIFT 7 + +struct usb_td { + __be16 status; + __be16 length; + __be32 buf_ptr; + __be16 extra; + __be16 reserved; +}; + +static struct usb_td __iomem *next_bd(struct usb_td __iomem *base, + struct usb_td __iomem *td, + u16 status) +{ + if (status & TD_W) + return base; + else + return ++td; +} + +void fhci_push_dummy_bd(struct endpoint *ep) +{ + if (ep->already_pushed_dummy_bd == false) { + u16 td_status = in_be16(&ep->empty_td->status); + + out_be32(&ep->empty_td->buf_ptr, DUMMY_BD_BUFFER); + /* get the next TD in the ring */ + ep->empty_td = next_bd(ep->td_base, ep->empty_td, td_status); + ep->already_pushed_dummy_bd = true; + } +} + +/* destroy an USB endpoint */ +void fhci_ep0_free(struct fhci_usb *usb) +{ + struct endpoint *ep; + int size; + + ep = usb->ep0; + if (ep) { + if (ep->td_base) + cpm_muram_free(cpm_muram_offset(ep->td_base)); + + if (ep->conf_frame_Q) { + size = cq_howmany(ep->conf_frame_Q); + for (; size; size--) { + struct packet *pkt = cq_get(ep->conf_frame_Q); + + kfree(pkt); + } + cq_delete(ep->conf_frame_Q); + } + + if (ep->empty_frame_Q) { + size = cq_howmany(ep->empty_frame_Q); + for (; size; size--) { + struct packet *pkt = cq_get(ep->empty_frame_Q); + + kfree(pkt); + } + cq_delete(ep->empty_frame_Q); + } + + if (ep->dummy_packets_Q) { + size = cq_howmany(ep->dummy_packets_Q); + for (; size; size--) { + u8 *buff = cq_get(ep->dummy_packets_Q); + + kfree(buff); + } + cq_delete(ep->dummy_packets_Q); + } + + kfree(ep); + usb->ep0 = NULL; + } +} + +/* + * create the endpoint structure + * + * arguments: + * usb A pointer to the data structure of the USB + * data_mem The data memory partition(BUS) + * ring_len TD ring length + */ +u32 fhci_create_ep(struct fhci_usb *usb, enum fhci_mem_alloc data_mem, + u32 ring_len) +{ + struct endpoint *ep; + struct usb_td __iomem *td; + unsigned long ep_offset; + char *err_for = "enpoint PRAM"; + int ep_mem_size; + u32 i; + + /* we need at least 3 TDs in the ring */ + if (!(ring_len > 2)) { + fhci_err(usb->fhci, "illegal TD ring length parameters\n"); + return -EINVAL; + } + + ep = kzalloc(sizeof(*ep), GFP_KERNEL); + if (!ep) + return -ENOMEM; + + ep_mem_size = ring_len * sizeof(*td) + sizeof(struct fhci_ep_pram); + ep_offset = cpm_muram_alloc(ep_mem_size, 32); + if (IS_ERR_VALUE(ep_offset)) + goto err; + ep->td_base = cpm_muram_addr(ep_offset); + + /* zero all queue pointers */ + ep->conf_frame_Q = cq_new(ring_len + 2); + ep->empty_frame_Q = cq_new(ring_len + 2); + ep->dummy_packets_Q = cq_new(ring_len + 2); + if (!ep->conf_frame_Q || !ep->empty_frame_Q || !ep->dummy_packets_Q) { + err_for = "frame_queues"; + goto err; + } + + for (i = 0; i < (ring_len + 1); i++) { + struct packet *pkt; + u8 *buff; + + pkt = kmalloc(sizeof(*pkt), GFP_KERNEL); + if (!pkt) { + err_for = "frame"; + goto err; + } + + buff = kmalloc(1028 * sizeof(*buff), GFP_KERNEL); + if (!buff) { + kfree(pkt); + err_for = "buffer"; + goto err; + } + cq_put(ep->empty_frame_Q, pkt); + cq_put(ep->dummy_packets_Q, buff); + } + + /* we put the endpoint parameter RAM right behind the TD ring */ + ep->ep_pram_ptr = (void __iomem *)ep->td_base + sizeof(*td) * ring_len; + + ep->conf_td = ep->td_base; + ep->empty_td = ep->td_base; + + ep->already_pushed_dummy_bd = false; + + /* initialize tds */ + td = ep->td_base; + for (i = 0; i < ring_len; i++) { + out_be32(&td->buf_ptr, 0); + out_be16(&td->status, 0); + out_be16(&td->length, 0); + out_be16(&td->extra, 0); + td++; + } + td--; + out_be16(&td->status, TD_W); /* for last TD set Wrap bit */ + out_be16(&td->length, 0); + + /* endpoint structure has been created */ + usb->ep0 = ep; + + return 0; +err: + fhci_ep0_free(usb); + kfree(ep); + fhci_err(usb->fhci, "no memory for the %s\n", err_for); + return -ENOMEM; +} + +/* + * initialize the endpoint register according to the given parameters + * + * artuments: + * usb A pointer to the data strucutre of the USB + * ep A pointer to the endpoint structre + * data_mem The data memory partition(BUS) + */ +void fhci_init_ep_registers(struct fhci_usb *usb, struct endpoint *ep, + enum fhci_mem_alloc data_mem) +{ + u8 rt; + + /* set the endpoint registers according to the endpoint */ + out_be16(&usb->fhci->regs->usb_ep[0], + USB_TRANS_CTR | USB_EP_MF | USB_EP_RTE); + out_be16(&usb->fhci->pram->ep_ptr[0], + cpm_muram_offset(ep->ep_pram_ptr)); + + rt = (BUS_MODE_BO_BE | BUS_MODE_GBL); +#ifdef MULTI_DATA_BUS + if (data_mem == MEM_SECONDARY) + rt |= BUS_MODE_DTB; +#endif + out_8(&ep->ep_pram_ptr->rx_func_code, rt); + out_8(&ep->ep_pram_ptr->tx_func_code, rt); + out_be16(&ep->ep_pram_ptr->rx_buff_len, 1028); + out_be16(&ep->ep_pram_ptr->rx_base, 0); + out_be16(&ep->ep_pram_ptr->tx_base, cpm_muram_offset(ep->td_base)); + out_be16(&ep->ep_pram_ptr->rx_bd_ptr, 0); + out_be16(&ep->ep_pram_ptr->tx_bd_ptr, cpm_muram_offset(ep->td_base)); + out_be32(&ep->ep_pram_ptr->tx_state, 0); +} + +/* + * Collect the submitted frames and inform the application about them + * It is also prepearing the TDs for new frames. If the Tx interrupts + * are diabled, the application should call that routine to get + * confirmation about the submitted frames. Otherwise, the routine is + * called frome the interrupt service routine during the Tx interrupt. + * In that case the application is informed by calling the application + * specific 'fhci_transaction_confirm' routine + */ +static void fhci_td_transaction_confirm(struct fhci_usb *usb) +{ + struct endpoint *ep = usb->ep0; + struct packet *pkt; + struct usb_td __iomem *td; + u16 extra_data; + u16 td_status; + u16 td_length; + u32 buf; + + /* + * collect transmitted BDs from the chip. The routine clears all BDs + * with R bit = 0 and the pointer to data buffer is not NULL, that is + * BDs which point to the transmitted data buffer + */ + while (1) { + td = ep->conf_td; + td_status = in_be16(&td->status); + td_length = in_be16(&td->length); + buf = in_be32(&td->buf_ptr); + extra_data = in_be16(&td->extra); + + /* check if the TD is empty */ + if (!(!(td_status & TD_R) && ((td_status & ~TD_W) || buf))) + break; + /* check if it is a dummy buffer */ + else if ((buf == DUMMY_BD_BUFFER) && !(td_status & ~TD_W)) + break; + + /* mark TD as empty */ + clrbits16(&td->status, ~TD_W); + out_be16(&td->length, 0); + out_be32(&td->buf_ptr, 0); + out_be16(&td->extra, 0); + /* advance the TD pointer */ + ep->conf_td = next_bd(ep->td_base, ep->conf_td, td_status); + + /* check if it is a dummy buffer(type2) */ + if ((buf == DUMMY2_BD_BUFFER) && !(td_status & ~TD_W)) + continue; + + pkt = cq_get(ep->conf_frame_Q); + if (!pkt) + fhci_err(usb->fhci, "no frame to confirm\n"); + + if (td_status & TD_ERRORS) { + if (td_status & TD_RXER) { + if (td_status & TD_CR) + pkt->status = USB_TD_RX_ER_CRC; + else if (td_status & TD_AB) + pkt->status = USB_TD_RX_ER_BITSTUFF; + else if (td_status & TD_OV) + pkt->status = USB_TD_RX_ER_OVERUN; + else if (td_status & TD_BOV) + pkt->status = USB_TD_RX_DATA_OVERUN; + else if (td_status & TD_NO) + pkt->status = USB_TD_RX_ER_NONOCT; + else + fhci_err(usb->fhci, "illegal error " + "occured\n"); + } else if (td_status & TD_NAK) + pkt->status = USB_TD_TX_ER_NAK; + else if (td_status & TD_TO) + pkt->status = USB_TD_TX_ER_TIMEOUT; + else if (td_status & TD_UN) + pkt->status = USB_TD_TX_ER_UNDERUN; + else if (td_status & TD_STAL) + pkt->status = USB_TD_TX_ER_STALL; + else + fhci_err(usb->fhci, "illegal error occured\n"); + } else if ((extra_data & TD_TOK_IN) && + pkt->len > td_length - CRC_SIZE) { + pkt->status = USB_TD_RX_DATA_UNDERUN; + } + + if (extra_data & TD_TOK_IN) + pkt->len = td_length - CRC_SIZE; + else if (pkt->info & PKT_ZLP) + pkt->len = 0; + else + pkt->len = td_length; + + fhci_transaction_confirm(usb, pkt); + } +} + +/* + * Submitting a data frame to a specified endpoint of a USB device + * The frame is put in the driver's transmit queue for this endpoint + * + * Arguments: + * usb A pointer to the USB structure + * pkt A pointer to the user frame structure + * trans_type Transaction tyep - IN,OUT or SETUP + * dest_addr Device address - 0~127 + * dest_ep Endpoint number of the device - 0~16 + * trans_mode Pipe type - ISO,Interrupt,bulk or control + * dest_speed USB speed - Low speed or FULL speed + * data_toggle Data sequence toggle - 0 or 1 + */ +u32 fhci_host_transaction(struct fhci_usb *usb, + struct packet *pkt, + enum fhci_ta_type trans_type, + u8 dest_addr, + u8 dest_ep, + enum fhci_tf_mode trans_mode, + enum fhci_speed dest_speed, u8 data_toggle) +{ + struct endpoint *ep = usb->ep0; + struct usb_td __iomem *td; + u16 extra_data; + u16 td_status; + + fhci_usb_disable_interrupt(usb); + /* start from the next BD that should be filled */ + td = ep->empty_td; + td_status = in_be16(&td->status); + + if (td_status & TD_R && in_be16(&td->length)) { + /* if the TD is not free */ + fhci_usb_enable_interrupt(usb); + return -1; + } + + /* get the next TD in the ring */ + ep->empty_td = next_bd(ep->td_base, ep->empty_td, td_status); + fhci_usb_enable_interrupt(usb); + pkt->priv_data = td; + out_be32(&td->buf_ptr, virt_to_phys(pkt->data)); + /* sets up transaction parameters - addr,endp,dir,and type */ + extra_data = (dest_ep << TD_ENDP_SHIFT) | dest_addr; + switch (trans_type) { + case FHCI_TA_IN: + extra_data |= TD_TOK_IN; + break; + case FHCI_TA_OUT: + extra_data |= TD_TOK_OUT; + break; + case FHCI_TA_SETUP: + extra_data |= TD_TOK_SETUP; + break; + } + if (trans_mode == FHCI_TF_ISO) + extra_data |= TD_ISO; + out_be16(&td->extra, extra_data); + + /* sets up the buffer descriptor */ + td_status = ((td_status & TD_W) | TD_R | TD_L | TD_I | TD_CNF); + if (!(pkt->info & PKT_NO_CRC)) + td_status |= TD_TC; + + switch (trans_type) { + case FHCI_TA_IN: + if (data_toggle) + pkt->info |= PKT_PID_DATA1; + else + pkt->info |= PKT_PID_DATA0; + break; + default: + if (data_toggle) { + td_status |= TD_PID_DATA1; + pkt->info |= PKT_PID_DATA1; + } else { + td_status |= TD_PID_DATA0; + pkt->info |= PKT_PID_DATA0; + } + break; + } + + if ((dest_speed == FHCI_LOW_SPEED) && + (usb->port_status == FHCI_PORT_FULL)) + td_status |= TD_LSP; + + out_be16(&td->status, td_status); + + /* set up buffer length */ + if (trans_type == FHCI_TA_IN) + out_be16(&td->length, pkt->len + CRC_SIZE); + else + out_be16(&td->length, pkt->len); + + /* put the frame to the confirmation queue */ + cq_put(ep->conf_frame_Q, pkt); + + if (cq_howmany(ep->conf_frame_Q) == 1) + out_8(&usb->fhci->regs->usb_comm, USB_CMD_STR_FIFO); + + return 0; +} + +/* Reset the Tx BD ring */ +void fhci_flush_bds(struct fhci_usb *usb) +{ + u16 extra_data; + u16 td_status; + u32 buf; + struct usb_td __iomem *td; + struct endpoint *ep = usb->ep0; + + td = ep->td_base; + while (1) { + td_status = in_be16(&td->status); + buf = in_be32(&td->buf_ptr); + extra_data = in_be16(&td->extra); + + /* if the TD is not empty - we'll confirm it as Timeout */ + if (td_status & TD_R) + out_be16(&td->status, (td_status & ~TD_R) | TD_TO); + /* if this TD is dummy - let's skip this TD */ + else if (in_be32(&td->buf_ptr) == DUMMY_BD_BUFFER) + out_be32(&td->buf_ptr, DUMMY2_BD_BUFFER); + /* if this is the last TD - break */ + if (td_status & TD_W) + break; + + td++; + } + + fhci_td_transaction_confirm(usb); + + td = ep->td_base; + do { + out_be16(&td->status, 0); + out_be16(&td->length, 0); + out_be32(&td->buf_ptr, 0); + out_be16(&td->extra, 0); + td++; + } while (!(in_be16(&td->status) & TD_W)); + out_be16(&td->status, TD_W); /* for last TD set Wrap bit */ + out_be16(&td->length, 0); + out_be32(&td->buf_ptr, 0); + out_be16(&td->extra, 0); + + out_be16(&ep->ep_pram_ptr->tx_bd_ptr, + in_be16(&ep->ep_pram_ptr->tx_base)); + out_be32(&ep->ep_pram_ptr->tx_state, 0); + out_be16(&ep->ep_pram_ptr->tx_cnt, 0); + ep->empty_td = ep->td_base; + ep->conf_td = ep->td_base; +} + +/* + * Flush all transmitted packets from TDs in the actual frame. + * This routine is called when something wrong with the controller and + * we want to get rid of the actual frame and start again next frame + */ +void fhci_flush_actual_frame(struct fhci_usb *usb) +{ + u8 mode; + u16 tb_ptr; + u16 extra_data; + u16 td_status; + u32 buf_ptr; + struct usb_td __iomem *td; + struct endpoint *ep = usb->ep0; + + /* disable the USB controller */ + mode = in_8(&usb->fhci->regs->usb_mod); + out_8(&usb->fhci->regs->usb_mod, mode & ~USB_MODE_EN); + + tb_ptr = in_be16(&ep->ep_pram_ptr->tx_bd_ptr); + td = cpm_muram_addr(tb_ptr); + td_status = in_be16(&td->status); + buf_ptr = in_be32(&td->buf_ptr); + extra_data = in_be16(&td->extra); + do { + if (td_status & TD_R) { + out_be16(&td->status, (td_status & ~TD_R) | TD_TO); + } else { + out_be32(&td->buf_ptr, 0); + ep->already_pushed_dummy_bd = false; + break; + } + + /* advance the TD pointer */ + td = next_bd(ep->td_base, td, td_status); + td_status = in_be16(&td->status); + buf_ptr = in_be32(&td->buf_ptr); + extra_data = in_be16(&td->extra); + } while ((td_status & TD_R) || buf_ptr); + + fhci_td_transaction_confirm(usb); + + out_be16(&ep->ep_pram_ptr->tx_bd_ptr, + in_be16(&ep->ep_pram_ptr->tx_base)); + out_be32(&ep->ep_pram_ptr->tx_state, 0); + out_be16(&ep->ep_pram_ptr->tx_cnt, 0); + ep->empty_td = ep->td_base; + ep->conf_td = ep->td_base; + + usb->actual_frame->frame_status = FRAME_TIMER_END_TRANSMISSION; + + /* reset the event register */ + out_be16(&usb->fhci->regs->usb_event, 0xffff); + /* enable the USB controller */ + out_8(&usb->fhci->regs->usb_mod, mode | USB_MODE_EN); +} + +/* handles Tx confirm and Tx error interrupt */ +void fhci_tx_conf_interrupt(struct fhci_usb *usb) +{ + fhci_td_transaction_confirm(usb); + + /* + * Schedule another transaction to this frame only if we have + * already confirmed all transaction in the frame. + */ + if (((fhci_get_sof_timer_count(usb) < usb->max_frame_usage) || + (usb->actual_frame->frame_status & FRAME_END_TRANSMISSION)) && + (list_empty(&usb->actual_frame->tds_list))) + fhci_schedule_transactions(usb); +} + +void fhci_host_transmit_actual_frame(struct fhci_usb *usb) +{ + u16 tb_ptr; + u16 td_status; + struct usb_td __iomem *td; + struct endpoint *ep = usb->ep0; + + tb_ptr = in_be16(&ep->ep_pram_ptr->tx_bd_ptr); + td = cpm_muram_addr(tb_ptr); + + if (in_be32(&td->buf_ptr) == DUMMY_BD_BUFFER) { + struct usb_td __iomem *old_td = td; + + ep->already_pushed_dummy_bd = false; + td_status = in_be16(&td->status); + /* gets the next TD in the ring */ + td = next_bd(ep->td_base, td, td_status); + tb_ptr = cpm_muram_offset(td); + out_be16(&ep->ep_pram_ptr->tx_bd_ptr, tb_ptr); + + /* start transmit only if we have something in the TDs */ + if (in_be16(&td->status) & TD_R) + out_8(&usb->fhci->regs->usb_comm, USB_CMD_STR_FIFO); + + if (in_be32(&ep->conf_td->buf_ptr) == DUMMY_BD_BUFFER) { + out_be32(&old_td->buf_ptr, 0); + ep->conf_td = next_bd(ep->td_base, ep->conf_td, + td_status); + } else { + out_be32(&old_td->buf_ptr, DUMMY2_BD_BUFFER); + } + } +} diff --git a/drivers/usb/host/fhci.h b/drivers/usb/host/fhci.h new file mode 100644 index 00000000000..7116284ed21 --- /dev/null +++ b/drivers/usb/host/fhci.h @@ -0,0 +1,607 @@ +/* + * Freescale QUICC Engine USB Host Controller Driver + * + * Copyright (c) Freescale Semicondutor, Inc. 2006. + * Shlomi Gridish + * Jerry Huang + * Copyright (c) Logic Product Development, Inc. 2007 + * Peter Barada + * Copyright (c) MontaVista Software, Inc. 2008. + * Anton Vorontsov + * + * 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. + */ + +#ifndef __FHCI_H +#define __FHCI_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include "../core/hcd.h" + +#define USB_CLOCK 48000000 + +#define FHCI_PRAM_SIZE 0x100 + +#define MAX_EDS 32 +#define MAX_TDS 32 + + +/* CRC16 field size */ +#define CRC_SIZE 2 + +/* USB protocol overhead for each frame transmitted from the host */ +#define PROTOCOL_OVERHEAD 7 + +/* Packet structure, info field */ +#define PKT_PID_DATA0 0x80000000 /* PID - Data toggle zero */ +#define PKT_PID_DATA1 0x40000000 /* PID - Data toggle one */ +#define PKT_PID_SETUP 0x20000000 /* PID - Setup bit */ +#define PKT_SETUP_STATUS 0x10000000 /* Setup status bit */ +#define PKT_SETADDR_STATUS 0x08000000 /* Set address status bit */ +#define PKT_SET_HOST_LAST 0x04000000 /* Last data packet */ +#define PKT_HOST_DATA 0x02000000 /* Data packet */ +#define PKT_FIRST_IN_FRAME 0x01000000 /* First packet in the frame */ +#define PKT_TOKEN_FRAME 0x00800000 /* Token packet */ +#define PKT_ZLP 0x00400000 /* Zero length packet */ +#define PKT_IN_TOKEN_FRAME 0x00200000 /* IN token packet */ +#define PKT_OUT_TOKEN_FRAME 0x00100000 /* OUT token packet */ +#define PKT_SETUP_TOKEN_FRAME 0x00080000 /* SETUP token packet */ +#define PKT_STALL_FRAME 0x00040000 /* STALL packet */ +#define PKT_NACK_FRAME 0x00020000 /* NACK packet */ +#define PKT_NO_PID 0x00010000 /* No PID */ +#define PKT_NO_CRC 0x00008000 /* don't append CRC */ +#define PKT_HOST_COMMAND 0x00004000 /* Host command packet */ +#define PKT_DUMMY_PACKET 0x00002000 /* Dummy packet, used for mmm */ +#define PKT_LOW_SPEED_PACKET 0x00001000 /* Low-Speed packet */ + +#define TRANS_OK (0) +#define TRANS_INPROGRESS (-1) +#define TRANS_DISCARD (-2) +#define TRANS_FAIL (-3) + +#define PS_INT 0 +#define PS_DISCONNECTED 1 +#define PS_CONNECTED 2 +#define PS_READY 3 +#define PS_MISSING 4 + +/* Transfer Descriptor status field */ +#define USB_TD_OK 0x00000000 /* TD transmited or received ok */ +#define USB_TD_INPROGRESS 0x80000000 /* TD is being transmitted */ +#define USB_TD_RX_ER_NONOCT 0x40000000 /* Tx Non Octet Aligned Packet */ +#define USB_TD_RX_ER_BITSTUFF 0x20000000 /* Frame Aborted-Received pkt */ +#define USB_TD_RX_ER_CRC 0x10000000 /* CRC error */ +#define USB_TD_RX_ER_OVERUN 0x08000000 /* Over - run occured */ +#define USB_TD_RX_ER_PID 0x04000000 /* wrong PID received */ +#define USB_TD_RX_DATA_UNDERUN 0x02000000 /* shorter than expected */ +#define USB_TD_RX_DATA_OVERUN 0x01000000 /* longer than expected */ +#define USB_TD_TX_ER_NAK 0x00800000 /* NAK handshake */ +#define USB_TD_TX_ER_STALL 0x00400000 /* STALL handshake */ +#define USB_TD_TX_ER_TIMEOUT 0x00200000 /* transmit time out */ +#define USB_TD_TX_ER_UNDERUN 0x00100000 /* transmit underrun */ + +#define USB_TD_ERROR (USB_TD_RX_ER_NONOCT | USB_TD_RX_ER_BITSTUFF | \ + USB_TD_RX_ER_CRC | USB_TD_RX_ER_OVERUN | USB_TD_RX_ER_PID | \ + USB_TD_RX_DATA_UNDERUN | USB_TD_RX_DATA_OVERUN | \ + USB_TD_TX_ER_NAK | USB_TD_TX_ER_STALL | \ + USB_TD_TX_ER_TIMEOUT | USB_TD_TX_ER_UNDERUN) + +/* Transfer Descriptor toggle field */ +#define USB_TD_TOGGLE_DATA0 0 +#define USB_TD_TOGGLE_DATA1 1 +#define USB_TD_TOGGLE_CARRY 2 + +/* #define MULTI_DATA_BUS */ + +/* Bus mode register RBMR/TBMR */ +#define BUS_MODE_GBL 0x20 /* Global snooping */ +#define BUS_MODE_BO 0x18 /* Byte ordering */ +#define BUS_MODE_BO_BE 0x10 /* Byte ordering - Big-endian */ +#define BUS_MODE_DTB 0x02 /* Data bus */ + +/* FHCI QE USB Register Description */ + +/* USB Mode Register bit define */ +#define USB_MODE_EN 0x01 +#define USB_MODE_HOST 0x02 +#define USB_MODE_TEST 0x04 +#define USB_MODE_SFTE 0x08 +#define USB_MODE_RESUME 0x40 +#define USB_MODE_LSS 0x80 + +/* USB Slave Address Register Mask */ +#define USB_SLVADDR_MASK 0x7F + +/* USB Endpoint register define */ +#define USB_EPNUM_MASK 0xF000 +#define USB_EPNUM_SHIFT 12 + +#define USB_TRANS_MODE_SHIFT 8 +#define USB_TRANS_CTR 0x0000 +#define USB_TRANS_INT 0x0100 +#define USB_TRANS_BULK 0x0200 +#define USB_TRANS_ISO 0x0300 + +#define USB_EP_MF 0x0020 +#define USB_EP_RTE 0x0010 + +#define USB_THS_SHIFT 2 +#define USB_THS_MASK 0x000c +#define USB_THS_NORMAL 0x0 +#define USB_THS_IGNORE_IN 0x0004 +#define USB_THS_NACK 0x0008 +#define USB_THS_STALL 0x000c + +#define USB_RHS_SHIFT 0 +#define USB_RHS_MASK 0x0003 +#define USB_RHS_NORMAL 0x0 +#define USB_RHS_IGNORE_OUT 0x0001 +#define USB_RHS_NACK 0x0002 +#define USB_RHS_STALL 0x0003 + +#define USB_RTHS_MASK 0x000f + +/* USB Command Register define */ +#define USB_CMD_STR_FIFO 0x80 +#define USB_CMD_FLUSH_FIFO 0x40 +#define USB_CMD_ISFT 0x20 +#define USB_CMD_DSFT 0x10 +#define USB_CMD_EP_MASK 0x03 + +/* USB Event and Mask Register define */ +#define USB_E_MSF_MASK 0x0800 +#define USB_E_SFT_MASK 0x0400 +#define USB_E_RESET_MASK 0x0200 +#define USB_E_IDLE_MASK 0x0100 +#define USB_E_TXE4_MASK 0x0080 +#define USB_E_TXE3_MASK 0x0040 +#define USB_E_TXE2_MASK 0x0020 +#define USB_E_TXE1_MASK 0x0010 +#define USB_E_SOF_MASK 0x0008 +#define USB_E_BSY_MASK 0x0004 +#define USB_E_TXB_MASK 0x0002 +#define USB_E_RXB_MASK 0x0001 + +/* Freescale USB Host controller registers */ +struct fhci_regs { + u8 usb_mod; /* mode register */ + u8 usb_addr; /* address register */ + u8 usb_comm; /* command register */ + u8 reserved1[1]; + __be16 usb_ep[4]; /* endpoint register */ + u8 reserved2[4]; + __be16 usb_event; /* event register */ + u8 reserved3[2]; + __be16 usb_mask; /* mask register */ + u8 reserved4[1]; + u8 usb_status; /* status register */ + __be16 usb_sof_tmr; /* Start Of Frame timer */ + u8 reserved5[2]; + __be16 usb_frame_num; /* frame number register */ + u8 reserved6[1]; +}; + +/* Freescale USB HOST */ +struct fhci_pram { + __be16 ep_ptr[4]; /* Endpoint porter reg */ + __be32 rx_state; /* Rx internal state */ + __be32 rx_ptr; /* Rx internal data pointer */ + __be16 frame_num; /* Frame number */ + __be16 rx_cnt; /* Rx byte count */ + __be32 rx_temp; /* Rx temp */ + __be32 rx_data_temp; /* Rx data temp */ + __be16 rx_u_ptr; /* Rx microcode return address temp */ + u8 reserved1[2]; /* reserved area */ + __be32 sof_tbl; /* SOF lookup table pointer */ + u8 sof_u_crc_temp; /* SOF micorcode CRC5 temp reg */ + u8 reserved2[0xdb]; +}; + +/* Freescale USB Endpoint*/ +struct fhci_ep_pram { + __be16 rx_base; /* Rx BD base address */ + __be16 tx_base; /* Tx BD base address */ + u8 rx_func_code; /* Rx function code */ + u8 tx_func_code; /* Tx function code */ + __be16 rx_buff_len; /* Rx buffer length */ + __be16 rx_bd_ptr; /* Rx BD pointer */ + __be16 tx_bd_ptr; /* Tx BD pointer */ + __be32 tx_state; /* Tx internal state */ + __be32 tx_ptr; /* Tx internal data pointer */ + __be16 tx_crc; /* temp transmit CRC */ + __be16 tx_cnt; /* Tx byte count */ + __be32 tx_temp; /* Tx temp */ + __be16 tx_u_ptr; /* Tx microcode return address temp */ + __be16 reserved; +}; + +struct fhci_controller_list { + struct list_head ctrl_list; /* control endpoints */ + struct list_head bulk_list; /* bulk endpoints */ + struct list_head iso_list; /* isochronous endpoints */ + struct list_head intr_list; /* interruput endpoints */ + struct list_head done_list; /* done transfers */ +}; + +struct virtual_root_hub { + int dev_num; /* USB address of the root hub */ + u32 feature; /* indicates what feature has been set */ + struct usb_hub_status hub; + struct usb_port_status port; +}; + +enum fhci_gpios { + GPIO_USBOE = 0, + GPIO_USBTP, + GPIO_USBTN, + GPIO_USBRP, + GPIO_USBRN, + /* these are optional */ + GPIO_SPEED, + GPIO_POWER, + NUM_GPIOS, +}; + +enum fhci_pins { + PIN_USBOE = 0, + PIN_USBTP, + PIN_USBTN, + NUM_PINS, +}; + +struct fhci_hcd { + enum qe_clock fullspeed_clk; + enum qe_clock lowspeed_clk; + struct qe_pin *pins[NUM_PINS]; + int gpios[NUM_GPIOS]; + bool alow_gpios[NUM_GPIOS]; + + struct fhci_regs __iomem *regs; /* I/O memory used to communicate */ + struct fhci_pram __iomem *pram; /* Parameter RAM */ + struct gtm_timer *timer; + + spinlock_t lock; + struct fhci_usb *usb_lld; /* Low-level driver */ + struct virtual_root_hub *vroot_hub; /* the virtual root hub */ + int active_urbs; + struct fhci_controller_list *hc_list; + struct tasklet_struct *process_done_task; /* tasklet for done list */ + + struct list_head empty_eds; + struct list_head empty_tds; + +#ifdef CONFIG_FHCI_DEBUG + int usb_irq_stat[13]; + struct dentry *dfs_root; + struct dentry *dfs_regs; + struct dentry *dfs_irq_stat; +#endif +}; + +#define USB_FRAME_USAGE 90 +#define FRAME_TIME_USAGE (USB_FRAME_USAGE*10) /* frame time usage */ +#define SW_FIX_TIME_BETWEEN_TRANSACTION 150 /* SW */ +#define MAX_BYTES_PER_FRAME (USB_FRAME_USAGE*15) +#define MAX_PERIODIC_FRAME_USAGE 90 + +/* transaction type */ +enum fhci_ta_type { + FHCI_TA_IN = 0, /* input transaction */ + FHCI_TA_OUT, /* output transaction */ + FHCI_TA_SETUP, /* setup transaction */ +}; + +/* transfer mode */ +enum fhci_tf_mode { + FHCI_TF_CTRL = 0, + FHCI_TF_ISO, + FHCI_TF_BULK, + FHCI_TF_INTR, +}; + +enum fhci_speed { + FHCI_FULL_SPEED, + FHCI_LOW_SPEED, +}; + +/* endpoint state */ +enum fhci_ed_state { + FHCI_ED_NEW = 0, /* pipe is new */ + FHCI_ED_OPER, /* pipe is operating */ + FHCI_ED_URB_DEL, /* pipe is in hold because urb is being deleted */ + FHCI_ED_SKIP, /* skip this pipe */ + FHCI_ED_HALTED, /* pipe is halted */ +}; + +enum fhci_port_status { + FHCI_PORT_POWER_OFF = 0, + FHCI_PORT_DISABLED, + FHCI_PORT_DISCONNECTING, + FHCI_PORT_WAITING, /* waiting for connection */ + FHCI_PORT_FULL, /* full speed connected */ + FHCI_PORT_LOW, /* low speed connected */ +}; + +enum fhci_mem_alloc { + MEM_CACHABLE_SYS = 0x00000001, /* primary DDR,cachable */ + MEM_NOCACHE_SYS = 0x00000004, /* primary DDR,non-cachable */ + MEM_SECONDARY = 0x00000002, /* either secondary DDR or SDRAM */ + MEM_PRAM = 0x00000008, /* multi-user RAM identifier */ +}; + +/* USB default parameters*/ +#define DEFAULT_RING_LEN 8 +#define DEFAULT_DATA_MEM MEM_CACHABLE_SYS + +struct ed { + u8 dev_addr; /* device address */ + u8 ep_addr; /* endpoint address */ + enum fhci_tf_mode mode; /* USB transfer mode */ + enum fhci_speed speed; + unsigned int max_pkt_size; + enum fhci_ed_state state; + struct list_head td_list; /* a list of all queued TD to this pipe */ + struct list_head node; + + /* read only parameters, should be cleared upon initialization */ + u8 toggle_carry; /* toggle carry from the last TD submitted */ + u32 last_iso; /* time stamp of last queued ISO transfer */ + struct td *td_head; /* a pointer to the current TD handled */ +}; + +struct td { + void *data; /* a pointer to the data buffer */ + unsigned int len; /* length of the data to be submitted */ + unsigned int actual_len; /* actual bytes transfered on this td */ + enum fhci_ta_type type; /* transaction type */ + u8 toggle; /* toggle for next trans. within this TD */ + u16 iso_index; /* ISO transaction index */ + u16 start_frame; /* start frame time stamp */ + u16 interval; /* interval between trans. (for ISO/Intr) */ + u32 status; /* status of the TD */ + struct ed *ed; /* a handle to the corresponding ED */ + struct urb *urb; /* a handle to the corresponding URB */ + bool ioc; /* Inform On Completion */ + struct list_head node; + + /* read only parameters should be cleared upon initialization */ + struct packet *pkt; + int nak_cnt; + int error_cnt; + struct list_head frame_lh; +}; + +struct packet { + u8 *data; /* packet data */ + u32 len; /* packet length */ + u32 status; /* status of the packet - equivalent to the status + * field for the corresponding structure td */ + u32 info; /* packet information */ + void __iomem *priv_data; /* private data of the driver (TDs or BDs) */ +}; + +/* struct for each URB */ +#define URB_INPROGRESS 0 +#define URB_DEL 1 + +/* URB states (state field) */ +#define US_BULK 0 +#define US_BULK0 1 + +/* three setup states */ +#define US_CTRL_SETUP 2 +#define US_CTRL_DATA 1 +#define US_CTRL_ACK 0 + +#define EP_ZERO 0 + +struct urb_priv { + int num_of_tds; + int tds_cnt; + int state; + + struct td **tds; + struct ed *ed; + struct timer_list time_out; +}; + +struct endpoint { + /* Pointer to ep parameter RAM */ + struct fhci_ep_pram __iomem *ep_pram_ptr; + + /* Host transactions */ + struct usb_td __iomem *td_base; /* first TD in the ring */ + struct usb_td __iomem *conf_td; /* next TD for confirm after transac */ + struct usb_td __iomem *empty_td;/* next TD for new transaction req. */ + struct kfifo *empty_frame_Q; /* Empty frames list to use */ + struct kfifo *conf_frame_Q; /* frames passed to TDs,waiting for tx */ + struct kfifo *dummy_packets_Q;/* dummy packets for the CRC overun */ + + bool already_pushed_dummy_bd; +}; + +/* struct for each 1mSec frame time */ +#define FRAME_IS_TRANSMITTED 0x00 +#define FRAME_TIMER_END_TRANSMISSION 0x01 +#define FRAME_DATA_END_TRANSMISSION 0x02 +#define FRAME_END_TRANSMISSION 0x03 +#define FRAME_IS_PREPARED 0x04 + +struct fhci_time_frame { + u16 frame_num; /* frame number */ + u16 total_bytes; /* total bytes submitted within this frame */ + u8 frame_status; /* flag that indicates to stop fill this frame */ + struct list_head tds_list; /* all tds of this frame */ +}; + +/* internal driver structure*/ +struct fhci_usb { + u16 saved_msk; /* saving of the USB mask register */ + struct endpoint *ep0; /* pointer for endpoint0 structure */ + int intr_nesting_cnt; /* interrupt nesting counter */ + u16 max_frame_usage; /* max frame time usage,in micro-sec */ + u16 max_bytes_per_frame; /* max byte can be tx in one time frame */ + u32 sw_transaction_time; /* sw complete trans time,in micro-sec */ + struct fhci_time_frame *actual_frame; + struct fhci_controller_list *hc_list; /* main structure for hc */ + struct virtual_root_hub *vroot_hub; + enum fhci_port_status port_status; /* v_rh port status */ + + u32 (*transfer_confirm)(struct fhci_hcd *fhci); + + struct fhci_hcd *fhci; +}; + +/* + * Various helpers and prototypes below. + */ + +static inline u16 get_frame_num(struct fhci_hcd *fhci) +{ + return in_be16(&fhci->pram->frame_num) & 0x07ff; +} + +#define fhci_dbg(fhci, fmt, args...) \ + dev_dbg(fhci_to_hcd(fhci)->self.controller, fmt, ##args) +#define fhci_vdbg(fhci, fmt, args...) \ + dev_vdbg(fhci_to_hcd(fhci)->self.controller, fmt, ##args) +#define fhci_err(fhci, fmt, args...) \ + dev_err(fhci_to_hcd(fhci)->self.controller, fmt, ##args) +#define fhci_info(fhci, fmt, args...) \ + dev_info(fhci_to_hcd(fhci)->self.controller, fmt, ##args) +#define fhci_warn(fhci, fmt, args...) \ + dev_warn(fhci_to_hcd(fhci)->self.controller, fmt, ##args) + +static inline struct fhci_hcd *hcd_to_fhci(struct usb_hcd *hcd) +{ + return (struct fhci_hcd *)hcd->hcd_priv; +} + +static inline struct usb_hcd *fhci_to_hcd(struct fhci_hcd *fhci) +{ + return container_of((void *)fhci, struct usb_hcd, hcd_priv); +} + +/* fifo of pointers */ +static inline struct kfifo *cq_new(int size) +{ + return kfifo_alloc(size * sizeof(void *), GFP_KERNEL, NULL); +} + +static inline void cq_delete(struct kfifo *kfifo) +{ + kfifo_free(kfifo); +} + +static inline unsigned int cq_howmany(struct kfifo *kfifo) +{ + return __kfifo_len(kfifo) / sizeof(void *); +} + +static inline int cq_put(struct kfifo *kfifo, void *p) +{ + return __kfifo_put(kfifo, (void *)&p, sizeof(p)); +} + +static inline void *cq_get(struct kfifo *kfifo) +{ + void *p = NULL; + + __kfifo_get(kfifo, (void *)&p, sizeof(p)); + return p; +} + +/* fhci-hcd.c */ +void fhci_start_sof_timer(struct fhci_hcd *fhci); +void fhci_stop_sof_timer(struct fhci_hcd *fhci); +u16 fhci_get_sof_timer_count(struct fhci_usb *usb); +void fhci_usb_enable_interrupt(struct fhci_usb *usb); +void fhci_usb_disable_interrupt(struct fhci_usb *usb); +int fhci_ioports_check_bus_state(struct fhci_hcd *fhci); + +/* fhci-mem.c */ +void fhci_recycle_empty_td(struct fhci_hcd *fhci, struct td *td); +void fhci_recycle_empty_ed(struct fhci_hcd *fhci, struct ed *ed); +struct ed *fhci_get_empty_ed(struct fhci_hcd *fhci); +struct td *fhci_td_fill(struct fhci_hcd *fhci, struct urb *urb, + struct urb_priv *urb_priv, struct ed *ed, u16 index, + enum fhci_ta_type type, int toggle, u8 *data, u32 len, + u16 interval, u16 start_frame, bool ioc); +void fhci_add_tds_to_ed(struct ed *ed, struct td **td_list, int number); + +/* fhci-hub.c */ +void fhci_config_transceiver(struct fhci_hcd *fhci, + enum fhci_port_status status); +void fhci_port_disable(struct fhci_hcd *fhci); +void fhci_port_enable(void *lld); +void fhci_io_port_generate_reset(struct fhci_hcd *fhci); +void fhci_port_reset(void *lld); +int fhci_hub_status_data(struct usb_hcd *hcd, char *buf); +int fhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, + u16 wIndex, char *buf, u16 wLength); + +/* fhci-tds.c */ +void fhci_flush_bds(struct fhci_usb *usb); +void fhci_flush_actual_frame(struct fhci_usb *usb); +u32 fhci_host_transaction(struct fhci_usb *usb, struct packet *pkt, + enum fhci_ta_type trans_type, u8 dest_addr, + u8 dest_ep, enum fhci_tf_mode trans_mode, + enum fhci_speed dest_speed, u8 data_toggle); +void fhci_host_transmit_actual_frame(struct fhci_usb *usb); +void fhci_tx_conf_interrupt(struct fhci_usb *usb); +void fhci_push_dummy_bd(struct endpoint *ep); +u32 fhci_create_ep(struct fhci_usb *usb, enum fhci_mem_alloc data_mem, + u32 ring_len); +void fhci_init_ep_registers(struct fhci_usb *usb, + struct endpoint *ep, + enum fhci_mem_alloc data_mem); +void fhci_ep0_free(struct fhci_usb *usb); + +/* fhci-sched.c */ +extern struct tasklet_struct fhci_tasklet; +void fhci_transaction_confirm(struct fhci_usb *usb, struct packet *pkt); +void fhci_flush_all_transmissions(struct fhci_usb *usb); +void fhci_schedule_transactions(struct fhci_usb *usb); +void fhci_device_connected_interrupt(struct fhci_hcd *fhci); +void fhci_device_disconnected_interrupt(struct fhci_hcd *fhci); +void fhci_queue_urb(struct fhci_hcd *fhci, struct urb *urb); +u32 fhci_transfer_confirm_callback(struct fhci_hcd *fhci); +irqreturn_t fhci_irq(struct usb_hcd *hcd); +irqreturn_t fhci_frame_limit_timer_irq(int irq, void *_hcd); + +/* fhci-q.h */ +void fhci_urb_complete_free(struct fhci_hcd *fhci, struct urb *urb); +struct td *fhci_remove_td_from_ed(struct ed *ed); +struct td *fhci_remove_td_from_frame(struct fhci_time_frame *frame); +void fhci_move_td_from_ed_to_done_list(struct fhci_usb *usb, struct ed *ed); +struct td *fhci_peek_td_from_frame(struct fhci_time_frame *frame); +void fhci_add_td_to_frame(struct fhci_time_frame *frame, struct td *td); +struct td *fhci_remove_td_from_done_list(struct fhci_controller_list *p_list); +void fhci_done_td(struct urb *urb, struct td *td); +void fhci_del_ed_list(struct fhci_hcd *fhci, struct ed *ed); + +#ifdef CONFIG_FHCI_DEBUG + +void fhci_dbg_isr(struct fhci_hcd *fhci, int usb_er); +void fhci_dfs_destroy(struct fhci_hcd *fhci); +void fhci_dfs_create(struct fhci_hcd *fhci); + +#else + +static inline void fhci_dbg_isr(struct fhci_hcd *fhci, int usb_er) {} +static inline void fhci_dfs_destroy(struct fhci_hcd *fhci) {} +static inline void fhci_dfs_create(struct fhci_hcd *fhci) {} + +#endif /* CONFIG_FHCI_DEBUG */ + +#endif /* __FHCI_H */ -- cgit v1.2.3 From 94f341db3dd080851f918da37e84659ef760da26 Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Thu, 25 Dec 2008 17:15:02 +0300 Subject: USB: fsl_qe_udc: Fix oops on QE UDC probe failure In case of probing errors the driver kfrees the udc_controller, but it doesn't set the pointer to NULL. When usb_gadget_register_driver is called, it checks for udc_controller != NULL, the check passes and the driver accesses nonexistent memory. Fix this by setting udc_controller to NULL in case of errors. While at it, also implement irq_of_parse_and_map()'s failure and cleanup cases. Signed-off-by: Anton Vorontsov Acked-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/fsl_qe_udc.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/fsl_qe_udc.c b/drivers/usb/gadget/fsl_qe_udc.c index d6c5bcd4006..e8f7862acb3 100644 --- a/drivers/usb/gadget/fsl_qe_udc.c +++ b/drivers/usb/gadget/fsl_qe_udc.c @@ -2604,6 +2604,10 @@ static int __devinit qe_udc_probe(struct of_device *ofdev, (unsigned long)udc_controller); /* request irq and disable DR */ udc_controller->usb_irq = irq_of_parse_and_map(np, 0); + if (!udc_controller->usb_irq) { + ret = -EINVAL; + goto err_noirq; + } ret = request_irq(udc_controller->usb_irq, qe_udc_irq, 0, driver_name, udc_controller); @@ -2625,6 +2629,8 @@ static int __devinit qe_udc_probe(struct of_device *ofdev, err6: free_irq(udc_controller->usb_irq, udc_controller); err5: + irq_dispose_mapping(udc_controller->usb_irq); +err_noirq: if (udc_controller->nullmap) { dma_unmap_single(udc_controller->gadget.dev.parent, udc_controller->nullp, 256, @@ -2648,7 +2654,7 @@ err2: iounmap(udc_controller->usb_regs); err1: kfree(udc_controller); - + udc_controller = NULL; return ret; } @@ -2710,6 +2716,7 @@ static int __devexit qe_udc_remove(struct of_device *ofdev) kfree(ep->txframe); free_irq(udc_controller->usb_irq, udc_controller); + irq_dispose_mapping(udc_controller->usb_irq); tasklet_kill(&udc_controller->rx_tasklet); -- cgit v1.2.3 From a30551db66afa1b53a4fa7ceadddb7122bdcf491 Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Thu, 25 Dec 2008 17:15:05 +0300 Subject: USB: fsl_qe_udc: Fix recursive locking bug in ch9getstatus() The call chain is this: qe_udc_irq() <- grabs the udc->lock spinlock rx_irq() qe_ep0_rx() ep0_setup_handle() setup_received_handle() ch9getstatus() qe_ep_queue() <- tries to grab the udc->lock again It seems unsafe to temporarily drop the lock in the ch9getstatus(), so to fix that bug the lock-less __qe_ep_queue() function implemented and used by the ch9getstatus(). Signed-off-by: Anton Vorontsov Acked-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/fsl_qe_udc.c | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/fsl_qe_udc.c b/drivers/usb/gadget/fsl_qe_udc.c index e8f7862acb3..064582fb6a8 100644 --- a/drivers/usb/gadget/fsl_qe_udc.c +++ b/drivers/usb/gadget/fsl_qe_udc.c @@ -1681,14 +1681,11 @@ static void qe_free_request(struct usb_ep *_ep, struct usb_request *_req) kfree(req); } -/* queues (submits) an I/O request to an endpoint */ -static int qe_ep_queue(struct usb_ep *_ep, struct usb_request *_req, - gfp_t gfp_flags) +static int __qe_ep_queue(struct usb_ep *_ep, struct usb_request *_req) { struct qe_ep *ep = container_of(_ep, struct qe_ep, ep); struct qe_req *req = container_of(_req, struct qe_req, req); struct qe_udc *udc; - unsigned long flags; int reval; udc = ep->udc; @@ -1732,7 +1729,7 @@ static int qe_ep_queue(struct usb_ep *_ep, struct usb_request *_req, list_add_tail(&req->queue, &ep->queue); dev_vdbg(udc->dev, "gadget have request in %s! %d\n", ep->name, req->req.length); - spin_lock_irqsave(&udc->lock, flags); + /* push the request to device */ if (ep_is_in(ep)) reval = ep_req_send(ep, req); @@ -1748,11 +1745,24 @@ static int qe_ep_queue(struct usb_ep *_ep, struct usb_request *_req, if (ep->dir == USB_DIR_OUT) reval = ep_req_receive(ep, req); - spin_unlock_irqrestore(&udc->lock, flags); - return 0; } +/* queues (submits) an I/O request to an endpoint */ +static int qe_ep_queue(struct usb_ep *_ep, struct usb_request *_req, + gfp_t gfp_flags) +{ + struct qe_ep *ep = container_of(_ep, struct qe_ep, ep); + struct qe_udc *udc = ep->udc; + unsigned long flags; + int ret; + + spin_lock_irqsave(&udc->lock, flags); + ret = __qe_ep_queue(_ep, _req); + spin_unlock_irqrestore(&udc->lock, flags); + return ret; +} + /* dequeues (cancels, unlinks) an I/O request from an endpoint */ static int qe_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) { @@ -2008,7 +2018,7 @@ static void ch9getstatus(struct qe_udc *udc, u8 request_type, u16 value, udc->ep0_dir = USB_DIR_IN; /* data phase */ - status = qe_ep_queue(&ep->ep, &req->req, GFP_ATOMIC); + status = __qe_ep_queue(&ep->ep, &req->req); if (status == 0) return; -- cgit v1.2.3 From 2247818a329687f30d1e5c3a62efc33d07c47522 Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Thu, 25 Dec 2008 17:15:07 +0300 Subject: USB: fsl_qe_udc: Fix QE USB controller initialization qe_udc_reg_init() leaves the USB controller enabled before muram memory initialized. Sometimes the uninitialized muram memory confuses the controller, and it start sending the busy interrupts. Fix this by disabling the controller, it will be enabled later by the gadget driver, at bind time. Signed-off-by: Anton Vorontsov Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/fsl_qe_udc.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/fsl_qe_udc.c b/drivers/usb/gadget/fsl_qe_udc.c index 064582fb6a8..1319f8f7acb 100644 --- a/drivers/usb/gadget/fsl_qe_udc.c +++ b/drivers/usb/gadget/fsl_qe_udc.c @@ -2452,8 +2452,12 @@ static int __devinit qe_udc_reg_init(struct qe_udc *udc) struct usb_ctlr __iomem *qe_usbregs; qe_usbregs = udc->usb_regs; - /* Init the usb register */ + /* Spec says that we must enable the USB controller to change mode. */ out_8(&qe_usbregs->usb_usmod, 0x01); + /* Mode changed, now disable it, since muram isn't initialized yet. */ + out_8(&qe_usbregs->usb_usmod, 0x00); + + /* Initialize the rest. */ out_be16(&qe_usbregs->usb_usbmr, 0); out_8(&qe_usbregs->usb_uscom, 0); out_be16(&qe_usbregs->usb_usber, USBER_ALL_CLEAR); -- cgit v1.2.3 From ef84e4055f3561495c4c0e0dfb0b9f4a6e20479d Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Thu, 25 Dec 2008 17:15:09 +0300 Subject: USB: fsl_qe_udc: Fix disconnects reporting during bus reset Freescale QE UDC controllers can't report the "port change" states, so the only way to handle disconnects is to process bus reset interrupts. The bus reset can take some time, that is, few irqs. Gadgets may print the disconnection events, and this causes few repetitive messages in the kernel log. This patch fixes the issue by using the usb_state machine, if the usb controller has been already reset, just quit the reset irq early. Signed-off-by: Anton Vorontsov Acked-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/fsl_qe_udc.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/fsl_qe_udc.c b/drivers/usb/gadget/fsl_qe_udc.c index 1319f8f7acb..7a820a3b4ac 100644 --- a/drivers/usb/gadget/fsl_qe_udc.c +++ b/drivers/usb/gadget/fsl_qe_udc.c @@ -2161,6 +2161,9 @@ static int reset_irq(struct qe_udc *udc) { unsigned char i; + if (udc->usb_state == USB_STATE_DEFAULT) + return 0; + qe_usb_disable(); out_8(&udc->usb_regs->usb_usadr, 0); -- cgit v1.2.3 From 82341b3690fce8f70998e3cfb79fbffff0eb7e6b Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Thu, 25 Dec 2008 17:15:11 +0300 Subject: USB: fsl_qe_udc: Fix muram corruption by disabled endpoints Before freeing an endpoint's muram memory, we should stop all activity of the endpoint, otherwise the QE UDC controller might do nasty things with the muram memory that isn't belong to that endpoint anymore. The qe_ep_reset() effectively flushes the hardware fifos, finishes all late transaction and thus prevents the corruption. Signed-off-by: Anton Vorontsov Acked-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/fsl_qe_udc.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/fsl_qe_udc.c b/drivers/usb/gadget/fsl_qe_udc.c index 7a820a3b4ac..32f415ebd3d 100644 --- a/drivers/usb/gadget/fsl_qe_udc.c +++ b/drivers/usb/gadget/fsl_qe_udc.c @@ -1622,6 +1622,7 @@ static int qe_ep_disable(struct usb_ep *_ep) nuke(ep, -ESHUTDOWN); ep->desc = NULL; ep->stopped = 1; + qe_ep_reset(udc, ep->epnum); spin_unlock_irqrestore(&udc->lock, flags); cpm_muram_free(cpm_muram_offset(ep->rxbase)); -- cgit v1.2.3 From af3ddbd76304f9f602c970f9b09a0c9d8cf8336c Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Thu, 25 Dec 2008 17:15:14 +0300 Subject: USB: fsl_qe_udc: Fix stalled TX requests bug While disabling an endpoint the driver nuking any pending requests, thus completing them with -ESHUTDOWN status. But the driver doesn't clear the tx_req, which means that a next TX request (after ep_enable), might get stalled, since the driver won't queue the new reqests. This patch fixes a bug I'm observing with ethernet gadget while playing with ifconfig usb0 up/down (the up/down sequence disables and enables `in' and `out' endpoints). Signed-off-by: Anton Vorontsov Acked-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/fsl_qe_udc.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/fsl_qe_udc.c b/drivers/usb/gadget/fsl_qe_udc.c index 32f415ebd3d..d701bf4698d 100644 --- a/drivers/usb/gadget/fsl_qe_udc.c +++ b/drivers/usb/gadget/fsl_qe_udc.c @@ -1622,6 +1622,7 @@ static int qe_ep_disable(struct usb_ep *_ep) nuke(ep, -ESHUTDOWN); ep->desc = NULL; ep->stopped = 1; + ep->tx_req = NULL; qe_ep_reset(udc, ep->epnum); spin_unlock_irqrestore(&udc->lock, flags); -- cgit v1.2.3 From 2057ac86da09955c9f8671e36d4f6bd1e7a5d7d2 Mon Sep 17 00:00:00 2001 From: James Treacy Date: Thu, 29 Jan 2009 20:17:17 -0500 Subject: USB: cdc-acm.c: remove duplicate lines for MTK gps support The same patch to add support for MTK gps loggers was submitted by two different people and applied twice. Remove the redundant lines. Signed-off-by: James Treacy Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/cdc-acm.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 97ba4a985ed..326dd7f65ee 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -1349,9 +1349,6 @@ static struct usb_device_id acm_ids[] = { { USB_DEVICE(0x0e8d, 0x0003), /* FIREFLY, MediaTek Inc; andrey.arapov@gmail.com */ .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ }, - { USB_DEVICE(0x0e8d, 0x3329), /* i-blue 747, Qstarz BT-Q1000, Holux M-241 */ - .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ - }, { USB_DEVICE(0x0e8d, 0x3329), /* MediaTek Inc GPS */ .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ }, -- cgit v1.2.3 From 6b40c0057a7935bcf63a38a924094c7e61d4731f Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 3 Feb 2009 16:02:21 -0800 Subject: Revert USB: option: add Pantech cards Revert 8b6346ec899713a90890c9e832f7eff91ea73504 as these devices really work just fine with the cdc-acm driver, as they follow the spec properly. Thanks to Chuck Ebbert for pointing out the problem here. Cc: Chuck Ebbert Cc: Dan Williams Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/option.c | 9 --------- 1 file changed, 9 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 6c89da9c6fe..9d2e77d9af2 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -274,12 +274,6 @@ static int option_send_setup(struct tty_struct *tty, struct usb_serial_port *po #define ERICSSON_VENDOR_ID 0x0bdb #define ERICSSON_PRODUCT_F3507G 0x1900 -/* Pantech products */ -#define PANTECH_VENDOR_ID 0x106c -#define PANTECH_PRODUCT_PC5740 0x3701 -#define PANTECH_PRODUCT_PC5750 0x3702 /* PX-500 */ -#define PANTECH_PRODUCT_UM150 0x3711 - static struct usb_device_id option_ids[] = { { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COLT) }, { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA) }, @@ -488,9 +482,6 @@ static struct usb_device_id option_ids[] = { { USB_DEVICE(ZTE_VENDOR_ID, ZTE_PRODUCT_MF628) }, { USB_DEVICE(ZTE_VENDOR_ID, ZTE_PRODUCT_CDMA_TECH) }, { USB_DEVICE(ERICSSON_VENDOR_ID, ERICSSON_PRODUCT_F3507G) }, - { USB_DEVICE(PANTECH_VENDOR_ID, PANTECH_PRODUCT_PC5740) }, - { USB_DEVICE(PANTECH_VENDOR_ID, PANTECH_PRODUCT_PC5750) }, - { USB_DEVICE(PANTECH_VENDOR_ID, PANTECH_PRODUCT_UM150) }, { } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, option_ids); -- cgit v1.2.3 From 0d020aae0a154cffce680a7775c74788fa0bea92 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Mon, 2 Feb 2009 09:51:01 -0500 Subject: USB: usb-storage: remove WARN from last-sector hacks This patch (as1201) removes the WARN() from the last-sector hacks in usb-storage, thereby making the code match the version now in .27-stable and .28-stable. The WARN() isn't needed, since there is no longer any intention of assuming that all storage devices have an even number of sectors, and it annoys users for no good reason. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/transport.c | 36 +++--------------------------------- 1 file changed, 3 insertions(+), 33 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c index 1d5438e6363..fb65d221ced 100644 --- a/drivers/usb/storage/transport.c +++ b/drivers/usb/storage/transport.c @@ -558,32 +558,10 @@ static void last_sector_hacks(struct us_data *us, struct scsi_cmnd *srb) if (srb->result == SAM_STAT_GOOD && scsi_get_resid(srb) == 0) { - /* The command succeeded. If the capacity is odd - * (i.e., if the sector number is even) then the - * "always-even" heuristic would be wrong for this - * device. Issue a WARN() so that the kerneloops.org - * project will be notified and we will then know to - * mark the device with a CAPACITY_OK flag. Hopefully - * this will occur for only a few devices. - * - * Use the sign of us->last_sector_hacks to tell whether - * the warning has already been issued; we don't need - * more than one warning per device. + /* The command succeeded. We know this device doesn't + * have the last-sector bug, so stop checking it. */ - if (!(sector & 1) && us->use_last_sector_hacks > 0) { - unsigned vid = le16_to_cpu( - us->pusb_dev->descriptor.idVendor); - unsigned pid = le16_to_cpu( - us->pusb_dev->descriptor.idProduct); - unsigned rev = le16_to_cpu( - us->pusb_dev->descriptor.bcdDevice); - - WARN(1, "%s: Successful last sector success at %u, " - "device %04x:%04x:%04x\n", - sdkp->disk->disk_name, sector, - vid, pid, rev); - us->use_last_sector_hacks = -1; - } + us->use_last_sector_hacks = 0; } else { /* The command failed. Allow up to 3 retries in case this @@ -599,14 +577,6 @@ static void last_sector_hacks(struct us_data *us, struct scsi_cmnd *srb) srb->result = SAM_STAT_CHECK_CONDITION; memcpy(srb->sense_buffer, record_not_found, sizeof(record_not_found)); - - /* In theory we might want to issue a WARN() here if the - * capacity is even, since it could indicate the device - * has the READ CAPACITY bug _and_ the real capacity is - * odd. But it could also indicate that the device - * simply can't access its last sector, a failure mode - * which is surprisingly common. So no warning. - */ } done: -- cgit v1.2.3 From 78c8fb3717cdff35ad118e17ac7495d0cf21a61f Mon Sep 17 00:00:00 2001 From: Dave Young Date: Sun, 1 Feb 2009 18:54:54 +0800 Subject: USB: usb-serial: fix the aircable_init failure path The failure path of aircable_init is wrong, fix the order of (goto) labels. Signed-off-by: Dave Young Acked-by: Naranjo Manuel Francisco Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/aircable.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/aircable.c b/drivers/usb/serial/aircable.c index 537f953bd7f..6d106e74265 100644 --- a/drivers/usb/serial/aircable.c +++ b/drivers/usb/serial/aircable.c @@ -621,9 +621,9 @@ static int __init aircable_init(void) goto failed_usb_register; return 0; -failed_serial_register: - usb_serial_deregister(&aircable_device); failed_usb_register: + usb_serial_deregister(&aircable_device); +failed_serial_register: return retval; } -- cgit v1.2.3 From e38c287447e5a3ff905a59dd81269c14cd12ffa1 Mon Sep 17 00:00:00 2001 From: Stephane Clerambault Date: Mon, 2 Feb 2009 13:39:12 -0800 Subject: USB: ftdi_sio: add support for the NDI Polaris system Add support for the NDI Polaris system *http://www.ndigital.com/). Cc: Ian Abbott Cc: Oliver Neukum Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/ftdi_sio.c | 1 + drivers/usb/serial/ftdi_sio.h | 3 +++ 2 files changed, 4 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 75597337583..64b84291c07 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -662,6 +662,7 @@ static struct usb_device_id id_table_combined [] = { { USB_DEVICE(FTDI_VID, FTDI_DOMINTELL_DUSB_PID) }, { USB_DEVICE(ALTI2_VID, ALTI2_N3_PID) }, { USB_DEVICE(FTDI_VID, DIEBOLD_BCS_SE923_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_NDI_HUC_PID) }, { }, /* Optional parameter entry */ { } /* Terminating entry */ }; diff --git a/drivers/usb/serial/ftdi_sio.h b/drivers/usb/serial/ftdi_sio.h index 1b62eff475d..e300c840f8c 100644 --- a/drivers/usb/serial/ftdi_sio.h +++ b/drivers/usb/serial/ftdi_sio.h @@ -844,6 +844,9 @@ #define TML_VID 0x1B91 /* Vendor ID */ #define TML_USB_SERIAL_PID 0x0064 /* USB - Serial Converter */ +/* NDI Polaris System */ +#define FTDI_NDI_HUC_PID 0xDA70 + /* Propox devices */ #define FTDI_PROPOX_JTAGCABLEII_PID 0xD738 -- cgit v1.2.3 From 506e9469833c66ed6bb9acd902e208f7301b6adb Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 4 Feb 2009 15:48:03 -0500 Subject: USB: usb-storage: add Pentax to the bad-vendor list MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch (as1202) adds Pentax to usb-storage's list of bad vendors whose devices always need the CAPACITY_HEURISTICS flag. This is in addition to the existing entries: Nokia, Nikon, and Motorola. Signed-off-by: Alan Stern Tested-by: Virgo Pärna Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/scsiglue.c | 2 ++ drivers/usb/storage/unusual_devs.h | 15 --------------- 2 files changed, 2 insertions(+), 15 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c index 2a42b862aa9..727c506417c 100644 --- a/drivers/usb/storage/scsiglue.c +++ b/drivers/usb/storage/scsiglue.c @@ -64,6 +64,7 @@ */ #define VENDOR_ID_NOKIA 0x0421 #define VENDOR_ID_NIKON 0x04b0 +#define VENDOR_ID_PENTAX 0x0a17 #define VENDOR_ID_MOTOROLA 0x22b8 /*********************************************************************** @@ -158,6 +159,7 @@ static int slave_configure(struct scsi_device *sdev) switch (le16_to_cpu(us->pusb_dev->descriptor.idVendor)) { case VENDOR_ID_NOKIA: case VENDOR_ID_NIKON: + case VENDOR_ID_PENTAX: case VENDOR_ID_MOTOROLA: if (!(us->fflags & (US_FL_FIX_CAPACITY | US_FL_CAPACITY_OK))) diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index 69269f73956..8e878cab8dc 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -1354,21 +1354,6 @@ UNUSUAL_DEV( 0x0a17, 0x0004, 0x1000, 0x1000, US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_FIX_INQUIRY ), - -/* Submitted by Per Winkvist */ -UNUSUAL_DEV( 0x0a17, 0x006, 0x0000, 0xffff, - "Pentax", - "Optio S/S4", - US_SC_DEVICE, US_PR_DEVICE, NULL, - US_FL_FIX_INQUIRY ), - -/* Reported by Jaak Ristioja */ -UNUSUAL_DEV( 0x0a17, 0x006e, 0x0100, 0x0100, - "Pentax", - "K10D", - US_SC_DEVICE, US_PR_DEVICE, NULL, - US_FL_FIX_CAPACITY ), - /* These are virtual windows driver CDs, which the zd1211rw driver * automatically converts into WLAN devices. */ UNUSUAL_DEV( 0x0ace, 0x2011, 0x0101, 0x0101, -- cgit v1.2.3 From 64905b48098761e779bb848e69365c018894ea81 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 3 Feb 2009 11:11:38 +0300 Subject: USB: ftdi_sio: unlock_kernel() on error in set_serial_info() There was one error path where unlock_kernel() wasn't called. This was found with a code checker (http://repo.or.cz/w/smatch.git/) Compile tested only, sorry. Signed-off-by: Dan Carpenter Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/ftdi_sio.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 64b84291c07..f92f4d77337 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -1065,8 +1065,10 @@ static int set_serial_info(struct tty_struct *tty, if (!capable(CAP_SYS_ADMIN)) { if (((new_serial.flags & ~ASYNC_USR_MASK) != - (priv->flags & ~ASYNC_USR_MASK))) + (priv->flags & ~ASYNC_USR_MASK))) { + unlock_kernel(); return -EPERM; + } priv->flags = ((priv->flags & ~ASYNC_USR_MASK) | (new_serial.flags & ASYNC_USR_MASK)); priv->custom_divisor = new_serial.custom_divisor; -- cgit v1.2.3 From 97dcf0416e390fc5c997d4ea60e6f975c7b7a1c3 Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Wed, 4 Feb 2009 16:38:33 +0100 Subject: USB: two more usb ids for ti_usb_3410_5052 This patch adds device IDs and balances the counts to make the hot ID additioning mechanism work. Signed-off-by: Oliver Neukum Cc: stable Cc: Chris Adams Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/ti_usb_3410_5052.c | 10 +++++++--- drivers/usb/serial/ti_usb_3410_5052.h | 2 ++ 2 files changed, 9 insertions(+), 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c index baf591137b8..2620bf6fe5e 100644 --- a/drivers/usb/serial/ti_usb_3410_5052.c +++ b/drivers/usb/serial/ti_usb_3410_5052.c @@ -176,7 +176,7 @@ static unsigned int product_5052_count; /* the array dimension is the number of default entries plus */ /* TI_EXTRA_VID_PID_COUNT user defined entries plus 1 terminating */ /* null entry */ -static struct usb_device_id ti_id_table_3410[7+TI_EXTRA_VID_PID_COUNT+1] = { +static struct usb_device_id ti_id_table_3410[10+TI_EXTRA_VID_PID_COUNT+1] = { { USB_DEVICE(TI_VENDOR_ID, TI_3410_PRODUCT_ID) }, { USB_DEVICE(TI_VENDOR_ID, TI_3410_EZ430_ID) }, { USB_DEVICE(MTS_VENDOR_ID, MTS_GSM_NO_FW_PRODUCT_ID) }, @@ -185,9 +185,11 @@ static struct usb_device_id ti_id_table_3410[7+TI_EXTRA_VID_PID_COUNT+1] = { { USB_DEVICE(MTS_VENDOR_ID, MTS_GSM_PRODUCT_ID) }, { USB_DEVICE(MTS_VENDOR_ID, MTS_EDGE_PRODUCT_ID) }, { USB_DEVICE(IBM_VENDOR_ID, IBM_4543_PRODUCT_ID) }, + { USB_DEVICE(IBM_VENDOR_ID, IBM_454B_PRODUCT_ID) }, + { USB_DEVICE(IBM_VENDOR_ID, IBM_454C_PRODUCT_ID) }, }; -static struct usb_device_id ti_id_table_5052[4+TI_EXTRA_VID_PID_COUNT+1] = { +static struct usb_device_id ti_id_table_5052[5+TI_EXTRA_VID_PID_COUNT+1] = { { USB_DEVICE(TI_VENDOR_ID, TI_5052_BOOT_PRODUCT_ID) }, { USB_DEVICE(TI_VENDOR_ID, TI_5152_BOOT_PRODUCT_ID) }, { USB_DEVICE(TI_VENDOR_ID, TI_5052_EEPROM_PRODUCT_ID) }, @@ -195,7 +197,7 @@ static struct usb_device_id ti_id_table_5052[4+TI_EXTRA_VID_PID_COUNT+1] = { { USB_DEVICE(IBM_VENDOR_ID, IBM_4543_PRODUCT_ID) }, }; -static struct usb_device_id ti_id_table_combined[6+2*TI_EXTRA_VID_PID_COUNT+1] = { +static struct usb_device_id ti_id_table_combined[14+2*TI_EXTRA_VID_PID_COUNT+1] = { { USB_DEVICE(TI_VENDOR_ID, TI_3410_PRODUCT_ID) }, { USB_DEVICE(TI_VENDOR_ID, TI_3410_EZ430_ID) }, { USB_DEVICE(MTS_VENDOR_ID, MTS_GSM_NO_FW_PRODUCT_ID) }, @@ -208,6 +210,8 @@ static struct usb_device_id ti_id_table_combined[6+2*TI_EXTRA_VID_PID_COUNT+1] = { USB_DEVICE(TI_VENDOR_ID, TI_5052_EEPROM_PRODUCT_ID) }, { USB_DEVICE(TI_VENDOR_ID, TI_5052_FIRMWARE_PRODUCT_ID) }, { USB_DEVICE(IBM_VENDOR_ID, IBM_4543_PRODUCT_ID) }, + { USB_DEVICE(IBM_VENDOR_ID, IBM_454B_PRODUCT_ID) }, + { USB_DEVICE(IBM_VENDOR_ID, IBM_454C_PRODUCT_ID) }, { } }; diff --git a/drivers/usb/serial/ti_usb_3410_5052.h b/drivers/usb/serial/ti_usb_3410_5052.h index b7ea5dbadee..f323c602585 100644 --- a/drivers/usb/serial/ti_usb_3410_5052.h +++ b/drivers/usb/serial/ti_usb_3410_5052.h @@ -30,6 +30,8 @@ #define IBM_VENDOR_ID 0x04b3 #define TI_3410_PRODUCT_ID 0x3410 #define IBM_4543_PRODUCT_ID 0x4543 +#define IBM_454B_PRODUCT_ID 0x454b +#define IBM_454C_PRODUCT_ID 0x454c #define TI_3410_EZ430_ID 0xF430 /* TI ez430 development tool */ #define TI_5052_BOOT_PRODUCT_ID 0x5052 /* no EEPROM, no firmware */ #define TI_5152_BOOT_PRODUCT_ID 0x5152 /* no EEPROM, no firmware */ -- cgit v1.2.3 From c200b9c9e8ec93cdd262cfa1699ad92e883d4876 Mon Sep 17 00:00:00 2001 From: Dirk De Schepper Date: Fri, 6 Feb 2009 20:48:34 +0000 Subject: USB: option: New mobile broadband modems to be supported - New Novatel and Dell mobile broadband modem products added - Dell pid variables used in stead of numerical PIDs for known products Signed-off-by: Dirk De Schepper Cc: stable Signed-off-by: Matthias Urlichs Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/option.c | 84 ++++++++++++++++++++++++++++++--------------- 1 file changed, 56 insertions(+), 28 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 9d2e77d9af2..bfd0b68cecc 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -199,14 +199,15 @@ static int option_send_setup(struct tty_struct *tty, struct usb_serial_port *po #define NOVATELWIRELESS_PRODUCT_MC950D 0x4400 /* FUTURE NOVATEL PRODUCTS */ -#define NOVATELWIRELESS_PRODUCT_EVDO_1 0x6000 -#define NOVATELWIRELESS_PRODUCT_HSPA_1 0x7000 -#define NOVATELWIRELESS_PRODUCT_EMBEDDED_1 0x8000 -#define NOVATELWIRELESS_PRODUCT_GLOBAL_1 0x9000 -#define NOVATELWIRELESS_PRODUCT_EVDO_2 0x6001 -#define NOVATELWIRELESS_PRODUCT_HSPA_2 0x7001 -#define NOVATELWIRELESS_PRODUCT_EMBEDDED_2 0x8001 -#define NOVATELWIRELESS_PRODUCT_GLOBAL_2 0x9001 +#define NOVATELWIRELESS_PRODUCT_EVDO_FULLSPEED 0X6000 +#define NOVATELWIRELESS_PRODUCT_EVDO_HIGHSPEED 0X6001 +#define NOVATELWIRELESS_PRODUCT_HSPA_FULLSPEED 0X7000 +#define NOVATELWIRELESS_PRODUCT_HSPA_HIGHSPEED 0X7001 +#define NOVATELWIRELESS_PRODUCT_EVDO_EMBEDDED_FULLSPEED 0X8000 +#define NOVATELWIRELESS_PRODUCT_EVDO_EMBEDDED_HIGHSPEED 0X8001 +#define NOVATELWIRELESS_PRODUCT_HSPA_EMBEDDED_FULLSPEED 0X9000 +#define NOVATELWIRELESS_PRODUCT_HSPA_EMBEDDED_HIGHSPEED 0X9001 +#define NOVATELWIRELESS_PRODUCT_GLOBAL 0XA001 /* AMOI PRODUCTS */ #define AMOI_VENDOR_ID 0x1614 @@ -216,6 +217,27 @@ static int option_send_setup(struct tty_struct *tty, struct usb_serial_port *po #define DELL_VENDOR_ID 0x413C +/* Dell modems */ +#define DELL_PRODUCT_5700_MINICARD 0x8114 +#define DELL_PRODUCT_5500_MINICARD 0x8115 +#define DELL_PRODUCT_5505_MINICARD 0x8116 +#define DELL_PRODUCT_5700_EXPRESSCARD 0x8117 +#define DELL_PRODUCT_5510_EXPRESSCARD 0x8118 + +#define DELL_PRODUCT_5700_MINICARD_SPRINT 0x8128 +#define DELL_PRODUCT_5700_MINICARD_TELUS 0x8129 + +#define DELL_PRODUCT_5720_MINICARD_VZW 0x8133 +#define DELL_PRODUCT_5720_MINICARD_SPRINT 0x8134 +#define DELL_PRODUCT_5720_MINICARD_TELUS 0x8135 +#define DELL_PRODUCT_5520_MINICARD_CINGULAR 0x8136 +#define DELL_PRODUCT_5520_MINICARD_GENERIC_L 0x8137 +#define DELL_PRODUCT_5520_MINICARD_GENERIC_I 0x8138 + +#define DELL_PRODUCT_5730_MINICARD_SPRINT 0x8180 +#define DELL_PRODUCT_5730_MINICARD_TELUS 0x8181 +#define DELL_PRODUCT_5730_MINICARD_VZW 0x8182 + #define KYOCERA_VENDOR_ID 0x0c88 #define KYOCERA_PRODUCT_KPC650 0x17da #define KYOCERA_PRODUCT_KPC680 0x180a @@ -389,31 +411,37 @@ static struct usb_device_id option_ids[] = { { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_EU870D) }, /* Novatel EU850D/EU860D/EU870D */ { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_MC950D) }, /* Novatel MC930D/MC950D */ { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_MC727) }, /* Novatel MC727/U727/USB727 */ - { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_EVDO_1) }, /* Novatel EVDO product */ - { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_HSPA_1) }, /* Novatel HSPA product */ - { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_EMBEDDED_1) }, /* Novatel Embedded product */ - { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_GLOBAL_1) }, /* Novatel Global product */ - { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_EVDO_2) }, /* Novatel EVDO product */ - { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_HSPA_2) }, /* Novatel HSPA product */ - { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_EMBEDDED_2) }, /* Novatel Embedded product */ - { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_GLOBAL_2) }, /* Novatel Global product */ + { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_EVDO_FULLSPEED) }, /* Novatel EVDO product */ + { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_HSPA_FULLSPEED) }, /* Novatel HSPA product */ + { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_EVDO_EMBEDDED_FULLSPEED) }, /* Novatel EVDO Embedded product */ + { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_HSPA_EMBEDDED_FULLSPEED) }, /* Novatel HSPA Embedded product */ + { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_EVDO_HIGHSPEED) }, /* Novatel EVDO product */ + { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_HSPA_HIGHSPEED) }, /* Novatel HSPA product */ + { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_EVDO_EMBEDDED_HIGHSPEED) }, /* Novatel EVDO Embedded product */ + { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_HSPA_EMBEDDED_HIGHSPEED) }, /* Novatel HSPA Embedded product */ + { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_GLOBAL) }, /* Novatel Global product */ { USB_DEVICE(AMOI_VENDOR_ID, AMOI_PRODUCT_H01) }, { USB_DEVICE(AMOI_VENDOR_ID, AMOI_PRODUCT_H01A) }, { USB_DEVICE(AMOI_VENDOR_ID, AMOI_PRODUCT_H02) }, - { USB_DEVICE(DELL_VENDOR_ID, 0x8114) }, /* Dell Wireless 5700 Mobile Broadband CDMA/EVDO Mini-Card == Novatel Expedite EV620 CDMA/EV-DO */ - { USB_DEVICE(DELL_VENDOR_ID, 0x8115) }, /* Dell Wireless 5500 Mobile Broadband HSDPA Mini-Card == Novatel Expedite EU740 HSDPA/3G */ - { USB_DEVICE(DELL_VENDOR_ID, 0x8116) }, /* Dell Wireless 5505 Mobile Broadband HSDPA Mini-Card == Novatel Expedite EU740 HSDPA/3G */ - { USB_DEVICE(DELL_VENDOR_ID, 0x8117) }, /* Dell Wireless 5700 Mobile Broadband CDMA/EVDO ExpressCard == Novatel Merlin XV620 CDMA/EV-DO */ - { USB_DEVICE(DELL_VENDOR_ID, 0x8118) }, /* Dell Wireless 5510 Mobile Broadband HSDPA ExpressCard == Novatel Merlin XU870 HSDPA/3G */ - { USB_DEVICE(DELL_VENDOR_ID, 0x8128) }, /* Dell Wireless 5700 Mobile Broadband CDMA/EVDO Mini-Card == Novatel Expedite E720 CDMA/EV-DO */ - { USB_DEVICE(DELL_VENDOR_ID, 0x8129) }, /* Dell Wireless 5700 Mobile Broadband CDMA/EVDO Mini-Card == Novatel Expedite ET620 CDMA/EV-DO */ - { USB_DEVICE(DELL_VENDOR_ID, 0x8133) }, /* Dell Wireless 5720 == Novatel EV620 CDMA/EV-DO */ - { USB_DEVICE(DELL_VENDOR_ID, 0x8136) }, /* Dell Wireless HSDPA 5520 == Novatel Expedite EU860D */ - { USB_DEVICE(DELL_VENDOR_ID, 0x8137) }, /* Dell Wireless HSDPA 5520 */ - { USB_DEVICE(DELL_VENDOR_ID, 0x8138) }, /* Dell Wireless 5520 Voda I Mobile Broadband (3G HSDPA) Minicard */ - { USB_DEVICE(DELL_VENDOR_ID, 0x8147) }, /* Dell Wireless 5530 Mobile Broadband (3G HSPA) Mini-Card */ + { USB_DEVICE(DELL_VENDOR_ID, DELL_PRODUCT_5700_MINICARD) }, /* Dell Wireless 5700 Mobile Broadband CDMA/EVDO Mini-Card == Novatel Expedite EV620 CDMA/EV-DO */ + { USB_DEVICE(DELL_VENDOR_ID, DELL_PRODUCT_5500_MINICARD) }, /* Dell Wireless 5500 Mobile Broadband HSDPA Mini-Card == Novatel Expedite EU740 HSDPA/3G */ + { USB_DEVICE(DELL_VENDOR_ID, DELL_PRODUCT_5505_MINICARD) }, /* Dell Wireless 5505 Mobile Broadband HSDPA Mini-Card == Novatel Expedite EU740 HSDPA/3G */ + { USB_DEVICE(DELL_VENDOR_ID, DELL_PRODUCT_5700_EXPRESSCARD) }, /* Dell Wireless 5700 Mobile Broadband CDMA/EVDO ExpressCard == Novatel Merlin XV620 CDMA/EV-DO */ + { USB_DEVICE(DELL_VENDOR_ID, DELL_PRODUCT_5510_EXPRESSCARD) }, /* Dell Wireless 5510 Mobile Broadband HSDPA ExpressCard == Novatel Merlin XU870 HSDPA/3G */ + { USB_DEVICE(DELL_VENDOR_ID, DELL_PRODUCT_5700_MINICARD_SPRINT) }, /* Dell Wireless 5700 Mobile Broadband CDMA/EVDO Mini-Card == Novatel Expedite E720 CDMA/EV-DO */ + { USB_DEVICE(DELL_VENDOR_ID, DELL_PRODUCT_5700_MINICARD_TELUS) }, /* Dell Wireless 5700 Mobile Broadband CDMA/EVDO Mini-Card == Novatel Expedite ET620 CDMA/EV-DO */ + { USB_DEVICE(DELL_VENDOR_ID, DELL_PRODUCT_5720_MINICARD_VZW) }, /* Dell Wireless 5720 == Novatel EV620 CDMA/EV-DO */ + { USB_DEVICE(DELL_VENDOR_ID, DELL_PRODUCT_5720_MINICARD_SPRINT) }, /* Dell Wireless 5720 == Novatel EV620 CDMA/EV-DO */ + { USB_DEVICE(DELL_VENDOR_ID, DELL_PRODUCT_5720_MINICARD_TELUS) }, /* Dell Wireless 5720 == Novatel EV620 CDMA/EV-DO */ + { USB_DEVICE(DELL_VENDOR_ID, DELL_PRODUCT_5520_MINICARD_CINGULAR) }, /* Dell Wireless HSDPA 5520 == Novatel Expedite EU860D */ + { USB_DEVICE(DELL_VENDOR_ID, DELL_PRODUCT_5520_MINICARD_GENERIC_L) }, /* Dell Wireless HSDPA 5520 */ + { USB_DEVICE(DELL_VENDOR_ID, DELL_PRODUCT_5520_MINICARD_GENERIC_I) }, /* Dell Wireless 5520 Voda I Mobile Broadband (3G HSDPA) Minicard */ + { USB_DEVICE(DELL_VENDOR_ID, 0x8147) }, /* Dell Wireless 5530 Mobile Broadband (3G HSPA) Mini-Card */ + { USB_DEVICE(DELL_VENDOR_ID, DELL_PRODUCT_5730_MINICARD_SPRINT) }, /* Dell Wireless 5730 Mobile Broadband EVDO/HSPA Mini-Card */ + { USB_DEVICE(DELL_VENDOR_ID, DELL_PRODUCT_5730_MINICARD_TELUS) }, /* Dell Wireless 5730 Mobile Broadband EVDO/HSPA Mini-Card */ + { USB_DEVICE(DELL_VENDOR_ID, DELL_PRODUCT_5730_MINICARD_VZW) }, /* Dell Wireless 5730 Mobile Broadband EVDO/HSPA Mini-Card */ { USB_DEVICE(ANYDATA_VENDOR_ID, ANYDATA_PRODUCT_ADU_E100A) }, /* ADU-E100, ADU-310 */ { USB_DEVICE(ANYDATA_VENDOR_ID, ANYDATA_PRODUCT_ADU_500A) }, { USB_DEVICE(ANYDATA_VENDOR_ID, ANYDATA_PRODUCT_ADU_620UW) }, -- cgit v1.2.3 From 26e1287594864169577327fef233befc9739be3b Mon Sep 17 00:00:00 2001 From: Ivan Kuten Date: Fri, 6 Feb 2009 17:42:34 +0800 Subject: USB: Correct Makefile to make isp1760 buildable Signed-off-by: Ivan Kuten Signed-off-by: Michael Hennerich Signed-off-by: Bryan Wu Signed-off-by: Greg Kroah-Hartman --- drivers/usb/Makefile | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/usb') diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index 8bcde8cde55..b2ceb4aff23 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_USB_MON) += mon/ obj-$(CONFIG_PCI) += host/ obj-$(CONFIG_USB_EHCI_HCD) += host/ obj-$(CONFIG_USB_ISP116X_HCD) += host/ +obj-$(CONFIG_USB_ISP1760_HCD) += host/ obj-$(CONFIG_USB_OHCI_HCD) += host/ obj-$(CONFIG_USB_UHCI_HCD) += host/ obj-$(CONFIG_USB_FHCI_HCD) += host/ -- cgit v1.2.3 From 049a6acb503ca7acc11d563db79a2d54f054e0d0 Mon Sep 17 00:00:00 2001 From: Nick Holloway Date: Sun, 25 Jan 2009 16:58:43 +0000 Subject: USB: Storage: Update unusual_devs entry for Datafab KECF-USB This device suffers from the off-by-one error when reporting the capacity, so add US_FL_FIX_CAPACITY to the existing entry. Signed-off-by: Nick Holloway Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/unusual_devs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index 8e878cab8dc..50dc33a6065 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -1214,7 +1214,7 @@ UNUSUAL_DEV( 0x07c4, 0xa400, 0x0000, 0xffff, "Datafab", "KECF-USB", US_SC_DEVICE, US_PR_DEVICE, NULL, - US_FL_FIX_INQUIRY ), + US_FL_FIX_INQUIRY | US_FL_FIX_CAPACITY ), /* Reported by Rauch Wolke */ UNUSUAL_DEV( 0x07c4, 0xa4a5, 0x0000, 0xffff, -- cgit v1.2.3 From a3c1239eb59c0a907f8be5587d42e950f44543f8 Mon Sep 17 00:00:00 2001 From: David Vrabel Date: Mon, 16 Feb 2009 14:37:12 +0000 Subject: wusb: whci-hcd: always lock whc->lock with interrupts disabled Always lock whc->lock with spin_lock_irq() or spin_lock_irqsave(). Signed-off-by: David Vrabel --- drivers/usb/host/whci/asl.c | 4 ++-- drivers/usb/host/whci/pzl.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/whci/asl.c b/drivers/usb/host/whci/asl.c index 2291c5f5af5..958751ccea4 100644 --- a/drivers/usb/host/whci/asl.c +++ b/drivers/usb/host/whci/asl.c @@ -227,13 +227,13 @@ void scan_async_work(struct work_struct *work) * Now that the ASL is updated, complete the removal of any * removed qsets. */ - spin_lock(&whc->lock); + spin_lock_irq(&whc->lock); list_for_each_entry_safe(qset, t, &whc->async_removed_list, list_node) { qset_remove_complete(whc, qset); } - spin_unlock(&whc->lock); + spin_unlock_irq(&whc->lock); } /** diff --git a/drivers/usb/host/whci/pzl.c b/drivers/usb/host/whci/pzl.c index 7dc85a0bee7..df8b85f0709 100644 --- a/drivers/usb/host/whci/pzl.c +++ b/drivers/usb/host/whci/pzl.c @@ -255,13 +255,13 @@ void scan_periodic_work(struct work_struct *work) * Now that the PZL is updated, complete the removal of any * removed qsets. */ - spin_lock(&whc->lock); + spin_lock_irq(&whc->lock); list_for_each_entry_safe(qset, t, &whc->periodic_removed_list, list_node) { qset_remove_complete(whc, qset); } - spin_unlock(&whc->lock); + spin_unlock_irq(&whc->lock); } /** -- cgit v1.2.3 From 3494252d5644993f407a45f01c3e8ad5ae38f93c Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 13 Feb 2009 23:41:12 +0100 Subject: USB/PCI: Fix resume breakage of controllers behind cardbus bridges If a USB PCI controller is behind a cardbus bridge, we are trying to restore its configuration registers too early, before the cardbus bridge is operational. To fix this, call pci_restore_state() from usb_hcd_pci_resume() and remove usb_hcd_pci_resume_early() which is no longer necessary (the configuration spaces of USB controllers that are not behind cardbus bridges will be restored by the PCI PM core with interrupts disabled anyway). This patch fixes the regression from 2.6.28 tracked as http://bugzilla.kernel.org/show_bug.cgi?id=12659 [ Side note: the proper long-term fix is probably to just force the unplug event at suspend time instead of doing a plug/unplug at resume time, but this patch is fine regardless - Linus ] Signed-off-by: Rafael J. Wysocki Reported-by: Miles Lane Signed-off-by: Linus Torvalds --- drivers/usb/core/hcd-pci.c | 15 ++------------- drivers/usb/core/hcd.h | 1 - drivers/usb/host/ehci-pci.c | 1 - drivers/usb/host/ohci-pci.c | 1 - drivers/usb/host/uhci-hcd.c | 1 - 5 files changed, 2 insertions(+), 17 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c index c54fc40458b..a4301dc02d2 100644 --- a/drivers/usb/core/hcd-pci.c +++ b/drivers/usb/core/hcd-pci.c @@ -297,19 +297,6 @@ int usb_hcd_pci_suspend(struct pci_dev *dev, pm_message_t message) } EXPORT_SYMBOL_GPL(usb_hcd_pci_suspend); -/** - * usb_hcd_pci_resume_early - resume a PCI-based HCD before IRQs are enabled - * @dev: USB Host Controller being resumed - * - * Store this function in the HCD's struct pci_driver as .resume_early. - */ -int usb_hcd_pci_resume_early(struct pci_dev *dev) -{ - pci_restore_state(dev); - return 0; -} -EXPORT_SYMBOL_GPL(usb_hcd_pci_resume_early); - /** * usb_hcd_pci_resume - power management resume of a PCI-based HCD * @dev: USB Host Controller being resumed @@ -333,6 +320,8 @@ int usb_hcd_pci_resume(struct pci_dev *dev) } #endif + pci_restore_state(dev); + hcd = pci_get_drvdata(dev); if (hcd->state != HC_STATE_SUSPENDED) { dev_dbg(hcd->self.controller, diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h index 5b94a56bec2..f750eb1ab59 100644 --- a/drivers/usb/core/hcd.h +++ b/drivers/usb/core/hcd.h @@ -257,7 +257,6 @@ extern void usb_hcd_pci_remove(struct pci_dev *dev); #ifdef CONFIG_PM extern int usb_hcd_pci_suspend(struct pci_dev *dev, pm_message_t msg); -extern int usb_hcd_pci_resume_early(struct pci_dev *dev); extern int usb_hcd_pci_resume(struct pci_dev *dev); #endif /* CONFIG_PM */ diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c index bb21fb0a496..abb9a7706ec 100644 --- a/drivers/usb/host/ehci-pci.c +++ b/drivers/usb/host/ehci-pci.c @@ -432,7 +432,6 @@ static struct pci_driver ehci_pci_driver = { #ifdef CONFIG_PM .suspend = usb_hcd_pci_suspend, - .resume_early = usb_hcd_pci_resume_early, .resume = usb_hcd_pci_resume, #endif .shutdown = usb_hcd_pci_shutdown, diff --git a/drivers/usb/host/ohci-pci.c b/drivers/usb/host/ohci-pci.c index 5d625c3fd42..f9961b4c0da 100644 --- a/drivers/usb/host/ohci-pci.c +++ b/drivers/usb/host/ohci-pci.c @@ -487,7 +487,6 @@ static struct pci_driver ohci_pci_driver = { #ifdef CONFIG_PM .suspend = usb_hcd_pci_suspend, - .resume_early = usb_hcd_pci_resume_early, .resume = usb_hcd_pci_resume, #endif diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c index 944f7e0ca4d..cf5e4cf7ea4 100644 --- a/drivers/usb/host/uhci-hcd.c +++ b/drivers/usb/host/uhci-hcd.c @@ -942,7 +942,6 @@ static struct pci_driver uhci_pci_driver = { #ifdef CONFIG_PM .suspend = usb_hcd_pci_suspend, - .resume_early = usb_hcd_pci_resume_early, .resume = usb_hcd_pci_resume, #endif /* PM */ }; -- cgit v1.2.3 From 22eb36f49e24e922ca6594a99157a3fcb92d3824 Mon Sep 17 00:00:00 2001 From: Roel Kluin Date: Thu, 19 Feb 2009 11:57:46 +0100 Subject: [ARM] 5403/1: pxa25x_ep_fifo_flush() *ep->reg_udccs always set to 0 *ep->reg_udccs is always set to 0. Signed-off-by: Roel Kluin Acked-by: Eric Miao Signed-off-by: Russell King --- drivers/usb/gadget/pxa25x_udc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/pxa25x_udc.c b/drivers/usb/gadget/pxa25x_udc.c index 9b36205c575..0ce4e281984 100644 --- a/drivers/usb/gadget/pxa25x_udc.c +++ b/drivers/usb/gadget/pxa25x_udc.c @@ -904,8 +904,8 @@ static void pxa25x_ep_fifo_flush(struct usb_ep *_ep) /* most IN status is the same, but ISO can't stall */ *ep->reg_udccs = UDCCS_BI_TPC|UDCCS_BI_FTF|UDCCS_BI_TUR - | (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC) - ? 0 : UDCCS_BI_SST; + | (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC + ? 0 : UDCCS_BI_SST); } -- cgit v1.2.3 From 9a6e184c804b33a2c2ea974efcd3c9798d30cb39 Mon Sep 17 00:00:00 2001 From: Li Yang Date: Fri, 13 Feb 2009 16:14:39 +0800 Subject: USB: fsl_usb2_udc: fix potential queue head corruption Clear next TD field and status field in queue head initialization code to prevent unpredictable result caused by residue of usb reset. Signed-off-by: Li Yang Acked-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/fsl_usb2_udc.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/fsl_usb2_udc.c b/drivers/usb/gadget/fsl_usb2_udc.c index f3c6703cffd..d8d9a52a44b 100644 --- a/drivers/usb/gadget/fsl_usb2_udc.c +++ b/drivers/usb/gadget/fsl_usb2_udc.c @@ -404,7 +404,10 @@ static void struct_ep_qh_setup(struct fsl_udc *udc, unsigned char ep_num, } if (zlt) tmp |= EP_QUEUE_HEAD_ZLT_SEL; + p_QH->max_pkt_length = cpu_to_le32(tmp); + p_QH->next_dtd_ptr = 1; + p_QH->size_ioc_int_sts = 0; return; } -- cgit v1.2.3 From 9aa09d2f8f4bc440d6db1c3414d4009642875240 Mon Sep 17 00:00:00 2001 From: Karsten Wiese Date: Sun, 8 Feb 2009 16:07:58 -0800 Subject: USB: EHCI: slow down ITD reuse Currently ITDs are immediately recycled whenever their URB completes. However, EHCI hardware can sometimes remember some ITD state. This means that when the ITD is reused before end-of-frame it may sometimes cause the hardware to reference bogus state. This patch defers reusing such ITDs by moving them into a new ehci member cached_itd_list. ITDs resting in cached_itd_list are moved back into their stream's free_list once scan_periodic() detects that the active frame has elapsed. This makes the snd_usb_us122l driver (in kernel since .28) work right when it's hooked up through EHCI. [ dbrownell@users.sourceforge.net: comment fixups ] Signed-off-by: Karsten Wiese Tested-by: Philippe Carriere Tested-by: Federico Briata Cc: stable Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-hcd.c | 2 ++ drivers/usb/host/ehci-mem.c | 1 + drivers/usb/host/ehci-sched.c | 56 ++++++++++++++++++++++++++++++++++++------- drivers/usb/host/ehci.h | 6 +++++ 4 files changed, 57 insertions(+), 8 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 4725d15d096..e551bb38852 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -485,6 +485,7 @@ static int ehci_init(struct usb_hcd *hcd) * periodic_size can shrink by USBCMD update if hcc_params allows. */ ehci->periodic_size = DEFAULT_I_TDPS; + INIT_LIST_HEAD(&ehci->cached_itd_list); if ((retval = ehci_mem_init(ehci, GFP_KERNEL)) < 0) return retval; @@ -497,6 +498,7 @@ static int ehci_init(struct usb_hcd *hcd) ehci->reclaim = NULL; ehci->next_uframe = -1; + ehci->clock_frame = -1; /* * dedicate a qh for the async ring head, since we couldn't unlink diff --git a/drivers/usb/host/ehci-mem.c b/drivers/usb/host/ehci-mem.c index 0431397836f..10d52919abb 100644 --- a/drivers/usb/host/ehci-mem.c +++ b/drivers/usb/host/ehci-mem.c @@ -128,6 +128,7 @@ static inline void qh_put (struct ehci_qh *qh) static void ehci_mem_cleanup (struct ehci_hcd *ehci) { + free_cached_itd_list(ehci); if (ehci->async) qh_put (ehci->async); ehci->async = NULL; diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index a081ee65bde..07bcb931021 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -1004,7 +1004,8 @@ iso_stream_put(struct ehci_hcd *ehci, struct ehci_iso_stream *stream) is_in = (stream->bEndpointAddress & USB_DIR_IN) ? 0x10 : 0; stream->bEndpointAddress &= 0x0f; - stream->ep->hcpriv = NULL; + if (stream->ep) + stream->ep->hcpriv = NULL; if (stream->rescheduled) { ehci_info (ehci, "ep%d%s-iso rescheduled " @@ -1653,14 +1654,28 @@ itd_complete ( (stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out"); } iso_stream_put (ehci, stream); - /* OK to recycle this ITD now that its completion callback ran. */ + done: usb_put_urb(urb); itd->urb = NULL; - itd->stream = NULL; - list_move(&itd->itd_list, &stream->free_list); - iso_stream_put(ehci, stream); - + if (ehci->clock_frame != itd->frame || itd->index[7] != -1) { + /* OK to recycle this ITD now. */ + itd->stream = NULL; + list_move(&itd->itd_list, &stream->free_list); + iso_stream_put(ehci, stream); + } else { + /* HW might remember this ITD, so we can't recycle it yet. + * Move it to a safe place until a new frame starts. + */ + list_move(&itd->itd_list, &ehci->cached_itd_list); + if (stream->refcount == 2) { + /* If iso_stream_put() were called here, stream + * would be freed. Instead, just prevent reuse. + */ + stream->ep->hcpriv = NULL; + stream->ep = NULL; + } + } return retval; } @@ -2101,6 +2116,20 @@ done: /*-------------------------------------------------------------------------*/ +static void free_cached_itd_list(struct ehci_hcd *ehci) +{ + struct ehci_itd *itd, *n; + + list_for_each_entry_safe(itd, n, &ehci->cached_itd_list, itd_list) { + struct ehci_iso_stream *stream = itd->stream; + itd->stream = NULL; + list_move(&itd->itd_list, &stream->free_list); + iso_stream_put(ehci, stream); + } +} + +/*-------------------------------------------------------------------------*/ + static void scan_periodic (struct ehci_hcd *ehci) { @@ -2115,10 +2144,17 @@ scan_periodic (struct ehci_hcd *ehci) * Touches as few pages as possible: cache-friendly. */ now_uframe = ehci->next_uframe; - if (HC_IS_RUNNING (ehci_to_hcd(ehci)->state)) + if (HC_IS_RUNNING(ehci_to_hcd(ehci)->state)) { clock = ehci_readl(ehci, &ehci->regs->frame_index); - else + clock_frame = (clock >> 3) % ehci->periodic_size; + } else { clock = now_uframe + mod - 1; + clock_frame = -1; + } + if (ehci->clock_frame != clock_frame) { + free_cached_itd_list(ehci); + ehci->clock_frame = clock_frame; + } clock %= mod; clock_frame = clock >> 3; @@ -2277,6 +2313,10 @@ restart: /* rescan the rest of this frame, then ... */ clock = now; clock_frame = clock >> 3; + if (ehci->clock_frame != clock_frame) { + free_cached_itd_list(ehci); + ehci->clock_frame = clock_frame; + } } else { now_uframe++; now_uframe %= mod; diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index fb7054ccf4f..262b00c9b33 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -87,6 +87,10 @@ struct ehci_hcd { /* one per controller */ int next_uframe; /* scan periodic, start here */ unsigned periodic_sched; /* periodic activity count */ + /* list of itds completed while clock_frame was still active */ + struct list_head cached_itd_list; + unsigned clock_frame; + /* per root hub port */ unsigned long reset_done [EHCI_MAX_ROOT_PORTS]; @@ -220,6 +224,8 @@ timer_action (struct ehci_hcd *ehci, enum ehci_timer_action action) } } +static void free_cached_itd_list(struct ehci_hcd *ehci); + /*-------------------------------------------------------------------------*/ #include -- cgit v1.2.3 From 29a46bf6f4f57df22f91573bb482a24237741347 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 10 Feb 2009 19:01:52 +0200 Subject: usb: gadget: obex: select correct ep descriptors We where selecting wrong ep descriptors causing some troubles while sending files over obex interface. The problem was a typo while usb_find_endpoint() was being called for HS endpoints. Acked-by: David Brownell Signed-off-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/f_obex.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/f_obex.c b/drivers/usb/gadget/f_obex.c index 80c2e7e9622..38aa896cc5d 100644 --- a/drivers/usb/gadget/f_obex.c +++ b/drivers/usb/gadget/f_obex.c @@ -366,9 +366,9 @@ obex_bind(struct usb_configuration *c, struct usb_function *f) f->hs_descriptors = usb_copy_descriptors(hs_function); obex->hs.obex_in = usb_find_endpoint(hs_function, - f->descriptors, &obex_hs_ep_in_desc); + f->hs_descriptors, &obex_hs_ep_in_desc); obex->hs.obex_out = usb_find_endpoint(hs_function, - f->descriptors, &obex_hs_ep_out_desc); + f->hs_descriptors, &obex_hs_ep_out_desc); } /* Avoid letting this gadget enumerate until the userspace -- cgit v1.2.3 From 28fb66821f884870987a0b5ab064ef651d9f7c16 Mon Sep 17 00:00:00 2001 From: Jesse Sung Date: Fri, 20 Feb 2009 21:13:45 -0800 Subject: USB: option: add BenQ 3g modem information This patch addes the BenQ 3g modem support to the option driver. From: Jesse Sung Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/option.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index bfd0b68cecc..c454b52ede3 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -296,6 +296,9 @@ static int option_send_setup(struct tty_struct *tty, struct usb_serial_port *po #define ERICSSON_VENDOR_ID 0x0bdb #define ERICSSON_PRODUCT_F3507G 0x1900 +#define BENQ_VENDOR_ID 0x04a5 +#define BENQ_PRODUCT_H10 0x4068 + static struct usb_device_id option_ids[] = { { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COLT) }, { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA) }, @@ -510,6 +513,8 @@ static struct usb_device_id option_ids[] = { { USB_DEVICE(ZTE_VENDOR_ID, ZTE_PRODUCT_MF628) }, { USB_DEVICE(ZTE_VENDOR_ID, ZTE_PRODUCT_CDMA_TECH) }, { USB_DEVICE(ERICSSON_VENDOR_ID, ERICSSON_PRODUCT_F3507G) }, + { USB_DEVICE(BENQ_VENDOR_ID, BENQ_PRODUCT_H10) }, + { USB_DEVICE(0x1da5, 0x4515) }, /* BenQ H20 */ { } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, option_ids); -- cgit v1.2.3 From 155df65ae11dfc322214c6f887185929c809df1b Mon Sep 17 00:00:00 2001 From: Dmitriy Taychenachev Date: Wed, 25 Feb 2009 12:36:51 +0800 Subject: USB: cdc-acm: add usb id for motomagx phones The Motorola MOTOMAGX phones (Z6, E8, Zn5 so far) are providing combined ACM/BLAN USB configuration. Since it has Vendor Specific class, the corresponding drivers (cdc-acm, zaurus) can't find it just by interface info. This patch adds usb id so the cdc-acm driver can properly handle this combined device. Signed-off-by: Dmitriy Taychenachev Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/cdc-acm.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 326dd7f65ee..00cba28d535 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -1376,6 +1376,8 @@ static struct usb_device_id acm_ids[] = { { USB_DEVICE(0x0572, 0x1324), /* Conexant USB MODEM RD02-D400 */ .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ }, + { USB_DEVICE(0x22b8, 0x6425), /* Motorola MOTOMAGX phones */ + }, /* control interfaces with various AT-command sets */ { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, -- cgit v1.2.3 From 5d7a4755d53a5305d05d836d87ef7c9ff94d6fa7 Mon Sep 17 00:00:00 2001 From: Patrik Kullman Date: Tue, 24 Feb 2009 13:38:53 -0800 Subject: USB: serial: add support for second revision of Ericsson F3507G WWAN card I noticed that my revision of the F3507G WWAN card isn't listed in drivers/usb/serial/option.c Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/option.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index c454b52ede3..b7c132bded7 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -294,7 +294,8 @@ static int option_send_setup(struct tty_struct *tty, struct usb_serial_port *po /* Ericsson products */ #define ERICSSON_VENDOR_ID 0x0bdb -#define ERICSSON_PRODUCT_F3507G 0x1900 +#define ERICSSON_PRODUCT_F3507G_1 0x1900 +#define ERICSSON_PRODUCT_F3507G_2 0x1902 #define BENQ_VENDOR_ID 0x04a5 #define BENQ_PRODUCT_H10 0x4068 @@ -512,7 +513,8 @@ static struct usb_device_id option_ids[] = { { USB_DEVICE(ZTE_VENDOR_ID, ZTE_PRODUCT_MF626) }, { USB_DEVICE(ZTE_VENDOR_ID, ZTE_PRODUCT_MF628) }, { USB_DEVICE(ZTE_VENDOR_ID, ZTE_PRODUCT_CDMA_TECH) }, - { USB_DEVICE(ERICSSON_VENDOR_ID, ERICSSON_PRODUCT_F3507G) }, + { USB_DEVICE(ERICSSON_VENDOR_ID, ERICSSON_PRODUCT_F3507G_1) }, + { USB_DEVICE(ERICSSON_VENDOR_ID, ERICSSON_PRODUCT_F3507G_2) }, { USB_DEVICE(BENQ_VENDOR_ID, BENQ_PRODUCT_H10) }, { USB_DEVICE(0x1da5, 0x4515) }, /* BenQ H20 */ { } /* Terminating entry */ -- cgit v1.2.3 From c332b4e1bfd56fe9028d8ef9708cb06179dd1a23 Mon Sep 17 00:00:00 2001 From: Adam Richter Date: Wed, 18 Feb 2009 16:17:15 -0800 Subject: USB: Quirk for Hummingbird huc56s / Conexant ACM modem Signed-off-by: Adam J. Richter Cc: Oliver Neukum Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/cdc-acm.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 00cba28d535..b3d5a23ab56 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -1378,6 +1378,13 @@ static struct usb_device_id acm_ids[] = { }, { USB_DEVICE(0x22b8, 0x6425), /* Motorola MOTOMAGX phones */ }, + { USB_DEVICE(0x0572, 0x1329), /* Hummingbird huc56s (Conexant) */ + .driver_info = NO_UNION_NORMAL, /* union descriptor misplaced on + data interface instead of + communications interface. + Maybe we should define a new + quirk for this. */ + }, /* control interfaces with various AT-command sets */ { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, -- cgit v1.2.3 From 5126a2674ddac0804450f59da25a058cca629d38 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Mon, 23 Feb 2009 12:02:05 -0500 Subject: USB: usb-storage: add IGNORE_RESIDUE flag for Genesys Logic adapters This patch (as1219) adds the IGNORE_RESIDUE flag to the unusual_devs entries for Genesys Logic's USB-IDE adapter. Although this device usually gets the residue correct, there is one command crucial to the operation of CD and DVD drives which it messes up. Tested-by: Mike Lampard Signed-off-by: Alan Stern Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/unusual_devs.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index 50dc33a6065..6f59c8e510e 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -907,13 +907,13 @@ UNUSUAL_DEV( 0x05e3, 0x0701, 0x0000, 0xffff, "Genesys Logic", "USB to IDE Optical", US_SC_DEVICE, US_PR_DEVICE, NULL, - US_FL_GO_SLOW | US_FL_MAX_SECTORS_64 ), + US_FL_GO_SLOW | US_FL_MAX_SECTORS_64 | US_FL_IGNORE_RESIDUE ), UNUSUAL_DEV( 0x05e3, 0x0702, 0x0000, 0xffff, "Genesys Logic", "USB to IDE Disk", US_SC_DEVICE, US_PR_DEVICE, NULL, - US_FL_GO_SLOW | US_FL_MAX_SECTORS_64 ), + US_FL_GO_SLOW | US_FL_MAX_SECTORS_64 | US_FL_IGNORE_RESIDUE ), /* Reported by Ben Efros */ UNUSUAL_DEV( 0x05e3, 0x0723, 0x9451, 0x9451, -- cgit v1.2.3 From ce459ec1d278b19be8e0719dbfd47dd1d6687bfb Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 24 Feb 2009 16:19:47 -0500 Subject: USB: g_file_storage: automatically disable stalls under Atmel This patch (as1220) automatically disables stalls when g_file_storage finds itself running with an Atmel device controller, because the Atmel hardware/driver isn't capable of halting bulk endpoints correctly. Reported-by: Stanislaw Gruszka Signed-off-by: Alan Stern Acked-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/file_storage.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c index b10fa31cc91..1ab9dac7e12 100644 --- a/drivers/usb/gadget/file_storage.c +++ b/drivers/usb/gadget/file_storage.c @@ -3879,7 +3879,11 @@ static int __init check_parameters(struct fsg_dev *fsg) mod_data.protocol_type = USB_SC_SCSI; mod_data.protocol_name = "Transparent SCSI"; - if (gadget_is_sh(fsg->gadget)) + /* Some peripheral controllers are known not to be able to + * halt bulk endpoints correctly. If one of them is present, + * disable stalls. + */ + if (gadget_is_sh(fsg->gadget) || gadget_is_at91(fsg->gadget)) mod_data.can_stall = 0; if (mod_data.release == 0xffff) { // Parameter wasn't set -- cgit v1.2.3 From 54b9ed35aea88b05d711884a3c2dc21bba047bd8 Mon Sep 17 00:00:00 2001 From: David Brownell Date: Wed, 11 Feb 2009 22:31:12 -0800 Subject: USB: gadget: fix build error in omap_apollon_2420_defconfig In apollon case, it only used udc, so udc configuration should select USB_OTG_UTILS also. Signed-off-by: Kyungmin Park Acked-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 3219d137340..e55fef52a5d 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -191,6 +191,7 @@ config USB_GADGET_OMAP boolean "OMAP USB Device Controller" depends on ARCH_OMAP select ISP1301_OMAP if MACH_OMAP_H2 || MACH_OMAP_H3 || MACH_OMAP_H4_OTG + select USB_OTG_UTILS if ARCH_OMAP help Many Texas Instruments OMAP processors have flexible full speed USB device controllers, with support for up to 30 -- cgit v1.2.3 From 67f5a4ba9741fcef3f4db3509ad03565d9e33af2 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Fri, 20 Feb 2009 16:33:08 -0500 Subject: USB: usb_get_string should check the descriptor type This patch (as1218) fixes a problem with a radio-control joystick used in the "walkera 4#3" helicopter. This device responds to the initial Get-String-Descriptor request for string 0 (which is really the list of supported languages) by sending its config descriptor! The usb_get_string() routine needs to check whether it got the right type of descriptor. Oddly enough, this sort of check is already present in usb_get_descriptor(). The patch changes the error code from -EPROTO to -ENODATA, because -EPROTO shows up in so many other contexts to indicate a hardware failure rather than a firmware error. Signed-off-by: Alan Stern Tested-by: Guillermo Jarabo Cc: stable Signed-off-by: Greg Kroah-Hartman =================================================================== --- drivers/usb/core/message.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index 31fb204f44c..49e7f56e0d7 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -653,7 +653,7 @@ int usb_get_descriptor(struct usb_device *dev, unsigned char type, if (result <= 0 && result != -ETIMEDOUT) continue; if (result > 1 && ((u8 *)buf)[1] != type) { - result = -EPROTO; + result = -ENODATA; continue; } break; @@ -696,8 +696,13 @@ static int usb_get_string(struct usb_device *dev, unsigned short langid, USB_REQ_GET_DESCRIPTOR, USB_DIR_IN, (USB_DT_STRING << 8) + index, langid, buf, size, USB_CTRL_GET_TIMEOUT); - if (!(result == 0 || result == -EPIPE)) - break; + if (result == 0 || result == -EPIPE) + continue; + if (result > 1 && ((u8 *) buf)[1] != USB_DT_STRING) { + result = -ENODATA; + continue; + } + break; } return result; } -- cgit v1.2.3 From 34f32c9701013ac5af89b82a6ae285e790b643e7 Mon Sep 17 00:00:00 2001 From: David Brownell Date: Fri, 20 Feb 2009 13:45:17 -0800 Subject: usb: musb: make Davinci *work* in mainline Now that the musb build fixes for DaVinci got merged (RC3?), kick in the other bits needed to get it finally *working* in mainline: - Use clk_enable()/clk_disable() ... the "always enable USB clocks" code this originally relied on has since been removed. - Initialize the USB device only after the relevant I2C GPIOs are available, so the host side can properly enable VBUS. - Tweak init sequencing to cope with mainline's relatively late init of the I2C system bus for power switches, transceivers, and so on. Sanity tested on DM6664 EVM for host and peripheral modes; that system won't boot with CONFIG_PM enabled, so OTG can't yet be tested. Also verified on OMAP3. (Unrelated: correct the MODULE_PARM_DESC spelling of musb_debug.) Signed-off-by: David Brownell Cc: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/davinci.c | 15 ++++----------- drivers/usb/musb/musb_core.c | 8 ++++---- 2 files changed, 8 insertions(+), 15 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/musb/davinci.c b/drivers/usb/musb/davinci.c index 5a8fd5d57a1..2dc7606f319 100644 --- a/drivers/usb/musb/davinci.c +++ b/drivers/usb/musb/davinci.c @@ -377,18 +377,8 @@ int __init musb_platform_init(struct musb *musb) u32 revision; musb->mregs += DAVINCI_BASE_OFFSET; -#if 0 - /* REVISIT there's something odd about clocking, this - * didn't appear do the job ... - */ - musb->clock = clk_get(pDevice, "usb"); - if (IS_ERR(musb->clock)) - return PTR_ERR(musb->clock); - status = clk_enable(musb->clock); - if (status < 0) - return -ENODEV; -#endif + clk_enable(musb->clock); /* returns zero if e.g. not clocked */ revision = musb_readl(tibase, DAVINCI_USB_VERSION_REG); @@ -453,5 +443,8 @@ int musb_platform_exit(struct musb *musb) } phy_off(); + + clk_disable(musb->clock); + return 0; } diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 2cc34fa05b7..b120fdcd889 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -115,7 +115,7 @@ unsigned musb_debug; -module_param(musb_debug, uint, S_IRUGO | S_IWUSR); +module_param_named(debug, musb_debug, uint, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(debug, "Debug message level. Default = 0"); #define DRIVER_AUTHOR "Mentor Graphics, Texas Instruments, Nokia" @@ -2243,10 +2243,10 @@ static int __init musb_init(void) return platform_driver_probe(&musb_driver, musb_probe); } -/* make us init after usbcore and before usb - * gadget and host-side drivers start to register +/* make us init after usbcore and i2c (transceivers, regulators, etc) + * and before usb gadget and host-side drivers start to register */ -subsys_initcall(musb_init); +fs_initcall(musb_init); static void __exit musb_cleanup(void) { -- cgit v1.2.3 From c2c963217bb1e8d53622d41b9e9ae706d0d02c07 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Sat, 21 Feb 2009 15:29:42 -0800 Subject: USB: musb: be careful with 64K+ transfer lengths (gadget side) request->actual is an unsigned and we should use the same variable type for fifo_count otherwise we might lose some data if request->length >= 64kbytes. [ dbrownell@users.sourceforge.net: fix compiler warning ] Signed-off-by: Felipe Balbi Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/musb_gadget.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c index 4ea30538798..c7ebd0867fc 100644 --- a/drivers/usb/musb/musb_gadget.c +++ b/drivers/usb/musb/musb_gadget.c @@ -575,7 +575,7 @@ static void rxstate(struct musb *musb, struct musb_request *req) struct usb_request *request = &req->request; struct musb_ep *musb_ep = &musb->endpoints[epnum].ep_out; void __iomem *epio = musb->endpoints[epnum].regs; - u16 fifo_count = 0; + unsigned fifo_count = 0; u16 len = musb_ep->packet_sz; csr = musb_readw(epio, MUSB_RXCSR); @@ -687,7 +687,7 @@ static void rxstate(struct musb *musb, struct musb_request *req) len, fifo_count, musb_ep->packet_sz); - fifo_count = min(len, fifo_count); + fifo_count = min_t(unsigned, len, fifo_count); #ifdef CONFIG_USB_TUSB_OMAP_DMA if (tusb_dma_omap() && musb_ep->dma) { -- cgit v1.2.3 From b7bdcb79de6de32e40dcc85a5e8c669bec2483d5 Mon Sep 17 00:00:00 2001 From: Dmitry Krivoschekov Date: Sat, 21 Feb 2009 15:30:15 -0800 Subject: USB: musb: fix musb_host_tx() for shared endpoint FIFO The input queue should be used for TX on endpoints which share FIFO hardware. The host TX path wasn't doing that. Shared FIFOs are most often configured for periodic endpoints, which are mostly used for RX/IN transfers ... that's probably how this bug managed to linger for a long time. [ dbrownell@users.sourceforge.net: update patch description ] Signed-off-by: Dmitry Krivoschekov Signed-off-by: Sergei Shtylyov Acked-by: David Brownell Cc: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/musb_host.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index a035ceccf95..b47ca948bcf 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -1161,7 +1161,8 @@ void musb_host_tx(struct musb *musb, u8 epnum) struct urb *urb; struct musb_hw_ep *hw_ep = musb->endpoints + epnum; void __iomem *epio = hw_ep->regs; - struct musb_qh *qh = hw_ep->out_qh; + struct musb_qh *qh = hw_ep->is_shared_fifo ? hw_ep->in_qh + : hw_ep->out_qh; u32 status = 0; void __iomem *mbase = musb->mregs; struct dma_channel *dma; -- cgit v1.2.3 From a2fd814e6a9e172f7077b68a2a9391bbde777a92 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Sat, 21 Feb 2009 15:30:45 -0800 Subject: USB: musb: fix urb_dequeue() method The urb_dequeue() method forgets to unlink 'struct musb_qh' from the control or bulk schedules when the URB being cancelled is the only one queued to its endpoint. That will cause musb_advance_schedule() to block once it reaches 'struct musb_qh' with now empty URB list, so URBs queued for other endpoints after the one being dequeued will not be served. Fix by unlinking the QH from the list except when it's already being handled (typically by musb_giveback). Since a QH with an empty URB list is now supposed to be freed, do that. And remove a now-useless check from musb_advance_schedule(). [ dbrownell@users.sourceforge.net: update patch description, and fold in a dequeue() comment patch ] Signed-off-by: Sergei Shtylyov Signed-off-by: David Brownell Cc: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/musb_host.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index b47ca948bcf..e32323938e9 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -432,7 +432,7 @@ musb_advance_schedule(struct musb *musb, struct urb *urb, else qh = musb_giveback(qh, urb, urb->status); - if (qh && qh->is_ready && !list_empty(&qh->hep->urb_list)) { + if (qh != NULL && qh->is_ready) { DBG(4, "... next ep%d %cX urb %p\n", hw_ep->epnum, is_in ? 'R' : 'T', next_urb(qh)); @@ -2038,9 +2038,9 @@ static int musb_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) goto done; /* Any URB not actively programmed into endpoint hardware can be - * immediately given back. Such an URB must be at the head of its + * immediately given back; that's any URB not at the head of an * endpoint queue, unless someday we get real DMA queues. And even - * then, it might not be known to the hardware... + * if it's at the head, it might not be known to the hardware... * * Otherwise abort current transfer, pending dma, etc.; urb->status * has already been updated. This is a synchronous abort; it'd be @@ -2079,6 +2079,15 @@ static int musb_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) qh->is_ready = 0; __musb_giveback(musb, urb, 0); qh->is_ready = ready; + + /* If nothing else (usually musb_giveback) is using it + * and its URB list has emptied, recycle this qh. + */ + if (ready && list_empty(&qh->hep->urb_list)) { + qh->hep->hcpriv = NULL; + list_del(&qh->ring); + kfree(qh); + } } else ret = musb_cleanup_urb(urb, qh, urb->pipe & USB_DIR_IN); done: -- cgit v1.2.3 From dc61d238b8c850c34632ae1fbbdea529f8c41d16 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Sat, 21 Feb 2009 15:31:01 -0800 Subject: USB: musb: host endpoint_disable() oops fixes The musb_h_disable() routine can oops in some cases: - It's not safe to read hep->hcpriv outside musb->lock, since it gets changed on completion IRQ paths. - The list iterators aren't safe to use in that way; just remove the first element while !list_empty(), so deletions on other code paths can't make trouble. We need two "scrub the list" loops because only one branch should touch hardware and advance the schedule. [ dbrownell@users.sourceforge.net: massively simplify patch description; add key points as code comments ] Signed-off-by: Sergei Shtylyov Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/musb_host.c | 36 ++++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 10 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index e32323938e9..c74ebadd4b9 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -2103,15 +2103,16 @@ musb_h_disable(struct usb_hcd *hcd, struct usb_host_endpoint *hep) unsigned long flags; struct musb *musb = hcd_to_musb(hcd); u8 is_in = epnum & USB_DIR_IN; - struct musb_qh *qh = hep->hcpriv; - struct urb *urb, *tmp; + struct musb_qh *qh; + struct urb *urb; struct list_head *sched; - if (!qh) - return; - spin_lock_irqsave(&musb->lock, flags); + qh = hep->hcpriv; + if (qh == NULL) + goto exit; + switch (qh->type) { case USB_ENDPOINT_XFER_CONTROL: sched = &musb->control; @@ -2145,13 +2146,28 @@ musb_h_disable(struct usb_hcd *hcd, struct usb_host_endpoint *hep) /* cleanup */ musb_cleanup_urb(urb, qh, urb->pipe & USB_DIR_IN); - } else - urb = NULL; - /* then just nuke all the others */ - list_for_each_entry_safe_from(urb, tmp, &hep->urb_list, urb_list) - musb_giveback(qh, urb, -ESHUTDOWN); + /* Then nuke all the others ... and advance the + * queue on hw_ep (e.g. bulk ring) when we're done. + */ + while (!list_empty(&hep->urb_list)) { + urb = next_urb(qh); + urb->status = -ESHUTDOWN; + musb_advance_schedule(musb, urb, qh->hw_ep, is_in); + } + } else { + /* Just empty the queue; the hardware is busy with + * other transfers, and since !qh->is_ready nothing + * will activate any of these as it advances. + */ + while (!list_empty(&hep->urb_list)) + __musb_giveback(musb, next_urb(qh), -ESHUTDOWN); + hep->hcpriv = NULL; + list_del(&qh->ring); + kfree(qh); + } +exit: spin_unlock_irqrestore(&musb->lock, flags); } -- cgit v1.2.3 From 51d9f3e100a8f8cc2be89d5f13d37de61e2da38a Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Sat, 21 Feb 2009 15:31:13 -0800 Subject: USB: musb: fix data toggle saving with shared FIFO For some strange reason the host side musb_giveback() decides that it's always got an IN transfer when the hardware endpoint is using a shared FIFO. This causes musb_save_toggle() to read the toggle state from the RXCSR register instead of TXCSR, and may also cause unneeded reloading of RX endpoint registers. Signed-off-by: Sergei Shtylyov Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/musb_host.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index c74ebadd4b9..fc79e76b384 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -335,16 +335,11 @@ musb_save_toggle(struct musb_hw_ep *ep, int is_in, struct urb *urb) static struct musb_qh * musb_giveback(struct musb_qh *qh, struct urb *urb, int status) { - int is_in; struct musb_hw_ep *ep = qh->hw_ep; struct musb *musb = ep->musb; + int is_in = usb_pipein(urb->pipe); int ready = qh->is_ready; - if (ep->is_shared_fifo) - is_in = 1; - else - is_in = usb_pipein(urb->pipe); - /* save toggle eagerly, for paranoia */ switch (qh->type) { case USB_ENDPOINT_XFER_BULK: -- cgit v1.2.3 From 3ecdb9acf343bbcf2bb2c287dc524ab709cfad7e Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Sat, 21 Feb 2009 15:31:23 -0800 Subject: USB: musb: be careful with 64K+ transfer lengths, host side Feeding 32-bit length cast down to 'u16' to min() to calculate the FIFO count in musb_host_tx() risks sending a short packet prematurely for transfer sizes over 64 KB. Similarly, although data transfer size shouldn't exceed 65535 bytes for the control endpoint, making musb_h_ep0_continue() more robust WRT URBs with possibly oversized buffer will not hurt either... Signed-off-by: Sergei Shtylyov Signed-off-by: David Brownell Cc: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/musb_host.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index fc79e76b384..23d3890fd54 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -937,8 +937,8 @@ static bool musb_h_ep0_continue(struct musb *musb, u16 len, struct urb *urb) switch (musb->ep0_stage) { case MUSB_EP0_IN: fifo_dest = urb->transfer_buffer + urb->actual_length; - fifo_count = min(len, ((u16) (urb->transfer_buffer_length - - urb->actual_length))); + fifo_count = min_t(size_t, len, urb->transfer_buffer_length - + urb->actual_length); if (fifo_count < len) urb->status = -EOVERFLOW; @@ -971,10 +971,9 @@ static bool musb_h_ep0_continue(struct musb *musb, u16 len, struct urb *urb) } /* FALLTHROUGH */ case MUSB_EP0_OUT: - fifo_count = min(qh->maxpacket, ((u16) - (urb->transfer_buffer_length - - urb->actual_length))); - + fifo_count = min_t(size_t, qh->maxpacket, + urb->transfer_buffer_length - + urb->actual_length); if (fifo_count) { fifo_dest = (u8 *) (urb->transfer_buffer + urb->actual_length); @@ -1304,7 +1303,8 @@ void musb_host_tx(struct musb *musb, u8 epnum) * packets before updating TXCSR ... other docs disagree ... */ /* PIO: start next packet in this URB */ - wLength = min(qh->maxpacket, (u16) wLength); + if (wLength > qh->maxpacket) + wLength = qh->maxpacket; musb_write_fifo(hw_ep, wLength, buf); qh->segsize = wLength; -- cgit v1.2.3 From 136733d6124a152ed2b61c3d38008c6581fc8685 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Sat, 21 Feb 2009 15:31:35 -0800 Subject: USB: musb: use right poll limit for low speed devices Remove wrongly applied upper limit on the interrupt transfer interval for low speed devices (not much of an error per se, according to USB specs). Signed-off-by: Sergei Shtylyov Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/musb_host.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index 23d3890fd54..6dbbd0786a6 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -1863,19 +1863,21 @@ static int musb_urb_enqueue( } qh->type_reg = type_reg; - /* precompute rxinterval/txinterval register */ - interval = min((u8)16, epd->bInterval); /* log encoding */ + /* Precompute RXINTERVAL/TXINTERVAL register */ switch (qh->type) { case USB_ENDPOINT_XFER_INT: - /* fullspeed uses linear encoding */ - if (USB_SPEED_FULL == urb->dev->speed) { - interval = epd->bInterval; - if (!interval) - interval = 1; + /* + * Full/low speeds use the linear encoding, + * high speed uses the logarithmic encoding. + */ + if (urb->dev->speed <= USB_SPEED_FULL) { + interval = max_t(u8, epd->bInterval, 1); + break; } /* FALLTHROUGH */ case USB_ENDPOINT_XFER_ISOC: - /* iso always uses log encoding */ + /* ISO always uses logarithmic encoding */ + interval = min_t(u8, epd->bInterval, 16); break; default: /* REVISIT we actually want to use NAK limits, hinting to the -- cgit v1.2.3 From 5c23c9078f8e3476982409b1075b54c8cd65e82c Mon Sep 17 00:00:00 2001 From: Anand Gadiyar Date: Sat, 21 Feb 2009 15:31:40 -0800 Subject: USB: musb: resume suspended root hub on disconnect If this is not done, khubd will not be informed of the disconnect and will assume the device is still there. Easily seen when a hub is connected with no device attached to it; it will autosuspend. When the hub is disconnected, it still shows up in /proc/bus/usb/devices Signed-off-by: Anand Gadiyar Acked-by: Felipe Balbi Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/musb_core.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/usb') diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index b120fdcd889..60cbaec1aa4 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -767,6 +767,7 @@ static irqreturn_t musb_stage2_irq(struct musb *musb, u8 int_usb, #ifdef CONFIG_USB_MUSB_HDRC_HCD case OTG_STATE_A_HOST: case OTG_STATE_A_SUSPEND: + usb_hcd_resume_root_hub(musb_to_hcd(musb)); musb_root_disconnect(musb); if (musb->a_wait_bcon != 0) musb_platform_try_idle(musb, jiffies -- cgit v1.2.3 From e747951240b9f820b94fd5582663c66d798c8fd1 Mon Sep 17 00:00:00 2001 From: Vikram Pandita Date: Sat, 21 Feb 2009 15:31:44 -0800 Subject: USB: musb: fix srp sysfs entry deletion The SRP sysfs attribute is dependent on gadget mode; any gadget may support SRP. But "rmmod musb_hdrc" didn't remove that attribute; fix. Signed-off-by: Vikram Pandita Acked-by: Felipe Balbi Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/musb_core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 60cbaec1aa4..af77e465900 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -1816,7 +1816,7 @@ static void musb_free(struct musb *musb) #ifdef CONFIG_SYSFS device_remove_file(musb->controller, &dev_attr_mode); device_remove_file(musb->controller, &dev_attr_vbus); -#ifdef CONFIG_USB_MUSB_OTG +#ifdef CONFIG_USB_GADGET_MUSB_HDRC device_remove_file(musb->controller, &dev_attr_srp); #endif #endif @@ -2064,7 +2064,7 @@ fail2: #ifdef CONFIG_SYSFS device_remove_file(musb->controller, &dev_attr_mode); device_remove_file(musb->controller, &dev_attr_vbus); -#ifdef CONFIG_USB_MUSB_OTG +#ifdef CONFIG_USB_GADGET_MUSB_HDRC device_remove_file(musb->controller, &dev_attr_srp); #endif #endif -- cgit v1.2.3