diff -Naur -X linux-2.6.32-rc5-tty/Documentation/dontdiff linux-2.6.32-rc5-tty/arch/arm/plat-mxc/ehci.c linux-2.6.32-rc5-usb/arch/arm/plat-mxc/ehci.c --- linux-2.6.32-rc5-tty/arch/arm/plat-mxc/ehci.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.32-rc5-usb/arch/arm/plat-mxc/ehci.c 2009-10-30 12:27:50.000000000 -0700 @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2009 Daniel Mack + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include + +#include +#include + +#define USBCTRL_OTGBASE_OFFSET 0x600 + +#define MX31_OTG_SIC_SHIFT 29 +#define MX31_OTG_SIC_MASK (0xf << MX31_OTG_SIC_SHIFT) +#define MX31_OTG_PM_BIT (1 << 24) + +#define MX31_H2_SIC_SHIFT 21 +#define MX31_H2_SIC_MASK (0xf << MX31_H2_SIC_SHIFT) +#define MX31_H2_PM_BIT (1 << 16) +#define MX31_H2_DT_BIT (1 << 5) + +#define MX31_H1_SIC_SHIFT 13 +#define MX31_H1_SIC_MASK (0xf << MX31_H1_SIC_SHIFT) +#define MX31_H1_PM_BIT (1 << 8) +#define MX31_H1_DT_BIT (1 << 4) + +int mxc_set_usbcontrol(int port, unsigned int flags) +{ + unsigned int v; + + if (cpu_is_mx31()) { + v = readl(IO_ADDRESS(MX31_OTG_BASE_ADDR + + USBCTRL_OTGBASE_OFFSET)); + + switch (port) { + case 0: /* OTG port */ + v &= ~(MX31_OTG_SIC_MASK | MX31_OTG_PM_BIT); + v |= (flags & MXC_EHCI_INTERFACE_MASK) + << MX31_OTG_SIC_SHIFT; + if (flags & MXC_EHCI_POWER_PINS_ENABLED) + v |= MX31_OTG_PM_BIT; + + break; + case 1: /* H1 port */ + v &= ~(MX31_H1_SIC_MASK | MX31_H1_PM_BIT); + v |= (flags & MXC_EHCI_INTERFACE_MASK) + << MX31_H1_SIC_SHIFT; + if (flags & MXC_EHCI_POWER_PINS_ENABLED) + v |= MX31_H1_PM_BIT; + + if (!(flags & MXC_EHCI_TTL_ENABLED)) + v |= MX31_H1_DT_BIT; + + break; + case 2: /* H2 port */ + v &= ~(MX31_H2_SIC_MASK | MX31_H2_PM_BIT); + v |= (flags & MXC_EHCI_INTERFACE_MASK) + << MX31_H2_SIC_SHIFT; + if (!(flags & MXC_EHCI_POWER_PINS_ENABLED)) + v |= MX31_H2_PM_BIT; + + if (!(flags & MXC_EHCI_TTL_ENABLED)) + v |= MX31_H2_DT_BIT; + + break; + } + + writel(v, IO_ADDRESS(MX31_OTG_BASE_ADDR + + USBCTRL_OTGBASE_OFFSET)); + return 0; + } + + printk(KERN_WARNING + "%s() unable to setup USBCONTROL for this CPU\n", __func__); + return -EINVAL; +} +EXPORT_SYMBOL(mxc_set_usbcontrol); + diff -Naur -X linux-2.6.32-rc5-tty/Documentation/dontdiff linux-2.6.32-rc5-tty/arch/arm/plat-mxc/include/mach/mxc_ehci.h linux-2.6.32-rc5-usb/arch/arm/plat-mxc/include/mach/mxc_ehci.h --- linux-2.6.32-rc5-tty/arch/arm/plat-mxc/include/mach/mxc_ehci.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.32-rc5-usb/arch/arm/plat-mxc/include/mach/mxc_ehci.h 2009-10-30 12:27:50.000000000 -0700 @@ -0,0 +1,37 @@ +#ifndef __INCLUDE_ASM_ARCH_MXC_EHCI_H +#define __INCLUDE_ASM_ARCH_MXC_EHCI_H + +/* values for portsc field */ +#define MXC_EHCI_PHY_LOW_POWER_SUSPEND (1 << 23) +#define MXC_EHCI_FORCE_FS (1 << 24) +#define MXC_EHCI_UTMI_8BIT (0 << 28) +#define MXC_EHCI_UTMI_16BIT (1 << 28) +#define MXC_EHCI_SERIAL (1 << 29) +#define MXC_EHCI_MODE_UTMI (0 << 30) +#define MXC_EHCI_MODE_PHILIPS (1 << 30) +#define MXC_EHCI_MODE_ULPI (2 << 30) +#define MXC_EHCI_MODE_SERIAL (3 << 30) + +/* values for flags field */ +#define MXC_EHCI_INTERFACE_DIFF_UNI (0 << 0) +#define MXC_EHCI_INTERFACE_DIFF_BI (1 << 0) +#define MXC_EHCI_INTERFACE_SINGLE_UNI (2 << 0) +#define MXC_EHCI_INTERFACE_SINGLE_BI (3 << 0) +#define MXC_EHCI_INTERFACE_MASK (0xf) + +#define MXC_EHCI_POWER_PINS_ENABLED (1 << 5) +#define MXC_EHCI_TTL_ENABLED (1 << 6) + +struct mxc_usbh_platform_data { + int (*init)(struct platform_device *pdev); + int (*exit)(struct platform_device *pdev); + + unsigned int portsc; + unsigned int flags; + struct otg_transceiver *otg; +}; + +int mxc_set_usbcontrol(int port, unsigned int flags); + +#endif /* __INCLUDE_ASM_ARCH_MXC_EHCI_H */ + diff -Naur -X linux-2.6.32-rc5-tty/Documentation/dontdiff linux-2.6.32-rc5-tty/arch/arm/plat-mxc/Makefile linux-2.6.32-rc5-usb/arch/arm/plat-mxc/Makefile --- linux-2.6.32-rc5-tty/arch/arm/plat-mxc/Makefile 2009-10-05 16:45:01.000000000 -0700 +++ linux-2.6.32-rc5-usb/arch/arm/plat-mxc/Makefile 2009-10-30 12:27:50.000000000 -0700 @@ -9,3 +9,4 @@ obj-$(CONFIG_ARCH_MX2) += iomux-mx1-mx2.o dma-mx1-mx2.o obj-$(CONFIG_ARCH_MXC_IOMUX_V3) += iomux-v3.o obj-$(CONFIG_MXC_PWM) += pwm.o +obj-$(CONFIG_USB_EHCI_MXC) += ehci.o diff -Naur -X linux-2.6.32-rc5-tty/Documentation/dontdiff linux-2.6.32-rc5-tty/Documentation/ABI/testing/sysfs-class-uwb_rc-wusbhc linux-2.6.32-rc5-usb/Documentation/ABI/testing/sysfs-class-uwb_rc-wusbhc --- linux-2.6.32-rc5-tty/Documentation/ABI/testing/sysfs-class-uwb_rc-wusbhc 2009-10-15 20:35:37.000000000 -0700 +++ linux-2.6.32-rc5-usb/Documentation/ABI/testing/sysfs-class-uwb_rc-wusbhc 2009-10-30 12:27:49.000000000 -0700 @@ -23,3 +23,16 @@ Since this relates to security (specifically, the lifetime of PTKs and GTKs) it should not be changed from the default. + +What: /sys/class/uwb_rc/uwbN/wusbhc/wusb_phy_rate +Date: August 2009 +KernelVersion: 2.6.32 +Contact: David Vrabel +Description: + The maximum PHY rate to use for all connected devices. + This is only of limited use for testing and + development as the hardware's automatic rate + adaptation is better then this simple control. + + Refer to [ECMA-368] section 10.3.1.1 for the value to + use. diff -Naur -X linux-2.6.32-rc5-tty/Documentation/dontdiff linux-2.6.32-rc5-tty/Documentation/powerpc/dts-bindings/xilinx.txt linux-2.6.32-rc5-usb/Documentation/powerpc/dts-bindings/xilinx.txt --- linux-2.6.32-rc5-tty/Documentation/powerpc/dts-bindings/xilinx.txt 2009-10-05 16:45:00.000000000 -0700 +++ linux-2.6.32-rc5-usb/Documentation/powerpc/dts-bindings/xilinx.txt 2009-10-30 12:27:49.000000000 -0700 @@ -292,4 +292,15 @@ - reg-offset : A value of 3 is required - reg-shift : A value of 2 is required + vii) Xilinx USB Host controller + + The Xilinx USB host controller is EHCI compatible but with a different + base address for the EHCI registers, and it is always a big-endian + USB Host controller. The hardware can be configured as high speed only, + or high speed/full speed hybrid. + + Required properties: + - xlnx,support-usb-fs: A value 0 means the core is built as high speed + only. A value 1 means the core also supports + full speed devices. diff -Naur -X linux-2.6.32-rc5-tty/Documentation/dontdiff linux-2.6.32-rc5-tty/drivers/usb/class/usbtmc.c linux-2.6.32-rc5-usb/drivers/usb/class/usbtmc.c --- linux-2.6.32-rc5-tty/drivers/usb/class/usbtmc.c 2009-10-15 20:35:57.000000000 -0700 +++ linux-2.6.32-rc5-usb/drivers/usb/class/usbtmc.c 2009-10-30 12:27:49.000000000 -0700 @@ -1109,13 +1109,13 @@ kref_put(&data->kref, usbtmc_delete); } -static int usbtmc_suspend (struct usb_interface *intf, pm_message_t message) +static int usbtmc_suspend(struct usb_interface *intf, pm_message_t message) { /* this driver does not have pending URBs */ return 0; } -static int usbtmc_resume (struct usb_interface *intf) +static int usbtmc_resume(struct usb_interface *intf) { return 0; } diff -Naur -X linux-2.6.32-rc5-tty/Documentation/dontdiff linux-2.6.32-rc5-tty/drivers/usb/core/generic.c linux-2.6.32-rc5-usb/drivers/usb/core/generic.c --- linux-2.6.32-rc5-tty/drivers/usb/core/generic.c 2009-10-15 20:35:57.000000000 -0700 +++ linux-2.6.32-rc5-usb/drivers/usb/core/generic.c 2009-10-30 12:27:49.000000000 -0700 @@ -139,7 +139,7 @@ if (best) { i = best->desc.bConfigurationValue; - dev_info(&udev->dev, + dev_dbg(&udev->dev, "configuration #%d chosen from %d choice%s\n", i, num_configs, plural(num_configs)); } else { diff -Naur -X linux-2.6.32-rc5-tty/Documentation/dontdiff linux-2.6.32-rc5-tty/drivers/usb/core/hub.c linux-2.6.32-rc5-usb/drivers/usb/core/hub.c --- linux-2.6.32-rc5-tty/drivers/usb/core/hub.c 2009-10-15 20:35:57.000000000 -0700 +++ linux-2.6.32-rc5-usb/drivers/usb/core/hub.c 2009-10-30 12:27:50.000000000 -0700 @@ -45,7 +45,6 @@ /* buffer for urb ... with extra space in case of babble */ char (*buffer)[8]; - dma_addr_t buffer_dma; /* DMA address for buffer */ union { struct usb_hub_status hub; struct usb_port_status port; @@ -61,6 +60,8 @@ status change */ unsigned long busy_bits[1]; /* ports being reset or resumed */ + unsigned long removed_bits[1]; /* ports with a "removed" + device present */ #if USB_MAXCHILDREN > 31 /* 8*sizeof(unsigned long) - 1 */ #error event_bits[] is too short! #endif @@ -636,6 +637,33 @@ kick_khubd(hub); } +/** + * usb_remove_device - disable a device's port on its parent hub + * @udev: device to be disabled and removed + * Context: @udev locked, must be able to sleep. + * + * After @udev's port has been disabled, khubd is notified and it will + * see that the device has been disconnected. When the device is + * physically unplugged and something is plugged in, the events will + * be received and processed normally. + */ +int usb_remove_device(struct usb_device *udev) +{ + struct usb_hub *hub; + struct usb_interface *intf; + + if (!udev->parent) /* Can't remove a root hub */ + return -EINVAL; + hub = hdev_to_hub(udev->parent); + intf = to_usb_interface(hub->intfdev); + + usb_autopm_get_interface(intf); + set_bit(udev->portnum, hub->removed_bits); + hub_port_logical_disconnect(hub, udev->portnum); + usb_autopm_put_interface(intf); + return 0; +} + enum hub_activation_type { HUB_INIT, HUB_INIT2, HUB_INIT3, HUB_POST_RESET, HUB_RESUME, HUB_RESET_RESUME, @@ -731,6 +759,13 @@ USB_PORT_FEAT_C_ENABLE); } + /* We can forget about a "removed" device when there's a + * physical disconnect or the connect status changes. + */ + if (!(portstatus & USB_PORT_STAT_CONNECTION) || + (portchange & USB_PORT_STAT_C_CONNECTION)) + clear_bit(port1, hub->removed_bits); + if (!udev || udev->state == USB_STATE_NOTATTACHED) { /* Tell khubd to disconnect the device or * check for a new connection @@ -869,8 +904,7 @@ int maxp, ret; char *message = "out of memory"; - hub->buffer = usb_buffer_alloc(hdev, sizeof(*hub->buffer), GFP_KERNEL, - &hub->buffer_dma); + hub->buffer = kmalloc(sizeof(*hub->buffer), GFP_KERNEL); if (!hub->buffer) { ret = -ENOMEM; goto fail; @@ -1111,8 +1145,6 @@ usb_fill_int_urb(hub->urb, hdev, pipe, *hub->buffer, maxp, hub_irq, hub, endpoint->bInterval); - hub->urb->transfer_dma = hub->buffer_dma; - hub->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; /* maybe cycle the hub leds */ if (hub->has_indicators && blinkenlights) @@ -1162,8 +1194,7 @@ kfree(hub->port_owners); kfree(hub->descriptor); kfree(hub->status); - usb_buffer_free(hub->hdev, sizeof(*hub->buffer), hub->buffer, - hub->buffer_dma); + kfree(hub->buffer); kref_put(&hub->kref, hub_release); } @@ -2123,9 +2154,13 @@ USB_DEVICE_REMOTE_WAKEUP, 0, NULL, 0, USB_CTRL_SET_TIMEOUT); - if (status) + if (status) { dev_dbg(&udev->dev, "won't remote wakeup, status %d\n", status); + /* bail if autosuspend is requested */ + if (msg.event & PM_EVENT_AUTO) + return status; + } } /* see 7.1.7.6 */ @@ -2134,7 +2169,8 @@ dev_dbg(hub->intfdev, "can't suspend port %d, status %d\n", port1, status); /* paranoia: "should not happen" */ - (void) usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + if (udev->do_remote_wakeup) + (void) usb_control_msg(udev, usb_sndctrlpipe(udev, 0), USB_REQ_CLEAR_FEATURE, USB_RECIP_DEVICE, USB_DEVICE_REMOTE_WAKEUP, 0, NULL, 0, @@ -2965,6 +3001,13 @@ usb_disconnect(&hdev->children[port1-1]); clear_bit(port1, hub->change_bits); + /* We can forget about a "removed" device when there's a physical + * disconnect or the connect status changes. + */ + if (!(portstatus & USB_PORT_STAT_CONNECTION) || + (portchange & USB_PORT_STAT_C_CONNECTION)) + clear_bit(port1, hub->removed_bits); + if (portchange & (USB_PORT_STAT_C_CONNECTION | USB_PORT_STAT_C_ENABLE)) { status = hub_port_debounce(hub, port1); @@ -2978,8 +3021,11 @@ } } - /* Return now if debouncing failed or nothing is connected */ - if (!(portstatus & USB_PORT_STAT_CONNECTION)) { + /* Return now if debouncing failed or nothing is connected or + * the device was "removed". + */ + if (!(portstatus & USB_PORT_STAT_CONNECTION) || + test_bit(port1, hub->removed_bits)) { /* maybe switch power back on (e.g. root hub was reset) */ if ((wHubCharacteristics & HUB_CHAR_LPSM) < 2 diff -Naur -X linux-2.6.32-rc5-tty/Documentation/dontdiff linux-2.6.32-rc5-tty/drivers/usb/core/message.c linux-2.6.32-rc5-usb/drivers/usb/core/message.c --- linux-2.6.32-rc5-tty/drivers/usb/core/message.c 2009-10-15 20:35:57.000000000 -0700 +++ linux-2.6.32-rc5-usb/drivers/usb/core/message.c 2009-10-30 12:27:50.000000000 -0700 @@ -393,13 +393,7 @@ if (io->entries <= 0) return io->entries; - /* If we're running on an xHCI host controller, queue the whole scatter - * gather list with one call to urb_enqueue(). This is only for bulk, - * as that endpoint type does not care how the data gets broken up - * across frames. - */ - if (usb_pipebulk(pipe) && - bus_to_hcd(dev->bus)->driver->flags & HCD_USB3) { + if (dev->bus->sg_tablesize > 0) { io->urbs = kmalloc(sizeof *io->urbs, mem_flags); use_sg = true; } else { @@ -409,7 +403,7 @@ if (!io->urbs) goto nomem; - urb_flags = URB_NO_INTERRUPT; + urb_flags = 0; if (dma) urb_flags |= URB_NO_TRANSFER_DMA_MAP; if (usb_pipein(pipe)) @@ -441,6 +435,7 @@ io->urbs[0]->num_sgs = io->entries; io->entries = 1; } else { + urb_flags |= URB_NO_INTERRUPT; for_each_sg(sg, sg, io->entries, i) { unsigned len; diff -Naur -X linux-2.6.32-rc5-tty/Documentation/dontdiff linux-2.6.32-rc5-tty/drivers/usb/core/sysfs.c linux-2.6.32-rc5-usb/drivers/usb/core/sysfs.c --- linux-2.6.32-rc5-tty/drivers/usb/core/sysfs.c 2009-10-15 20:35:57.000000000 -0700 +++ linux-2.6.32-rc5-usb/drivers/usb/core/sysfs.c 2009-10-30 12:27:50.000000000 -0700 @@ -508,6 +508,28 @@ static DEVICE_ATTR(authorized, 0644, usb_dev_authorized_show, usb_dev_authorized_store); +/* "Safely remove a device" */ +static ssize_t usb_remove_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct usb_device *udev = to_usb_device(dev); + int rc = 0; + + usb_lock_device(udev); + if (udev->state != USB_STATE_NOTATTACHED) { + + /* To avoid races, first unconfigure and then remove */ + usb_set_configuration(udev, -1); + rc = usb_remove_device(udev); + } + if (rc == 0) + rc = count; + usb_unlock_device(udev); + return rc; +} +static DEVICE_ATTR(remove, 0200, NULL, usb_remove_store); + static struct attribute *dev_attrs[] = { /* current configuration's attributes */ @@ -533,6 +555,7 @@ &dev_attr_maxchild.attr, &dev_attr_quirks.attr, &dev_attr_authorized.attr, + &dev_attr_remove.attr, NULL, }; static struct attribute_group dev_attr_grp = { diff -Naur -X linux-2.6.32-rc5-tty/Documentation/dontdiff linux-2.6.32-rc5-tty/drivers/usb/core/urb.c linux-2.6.32-rc5-usb/drivers/usb/core/urb.c --- linux-2.6.32-rc5-tty/drivers/usb/core/urb.c 2009-10-05 16:45:24.000000000 -0700 +++ linux-2.6.32-rc5-usb/drivers/usb/core/urb.c 2009-10-30 12:27:49.000000000 -0700 @@ -429,8 +429,16 @@ case USB_ENDPOINT_XFER_ISOC: case USB_ENDPOINT_XFER_INT: /* too small? */ - if (urb->interval <= 0) - return -EINVAL; + switch (dev->speed) { + case USB_SPEED_VARIABLE: + if (urb->interval < 6) + return -EINVAL; + break; + default: + if (urb->interval <= 0) + return -EINVAL; + break; + } /* too big? */ switch (dev->speed) { case USB_SPEED_SUPER: /* units are 125us */ @@ -438,6 +446,10 @@ if (urb->interval > (1 << 15)) return -EINVAL; max = 1 << 15; + case USB_SPEED_VARIABLE: + if (urb->interval > 16) + return -EINVAL; + break; case USB_SPEED_HIGH: /* units are microframes */ /* NOTE usb handles 2^15 */ if (urb->interval > (1024 * 8)) @@ -461,8 +473,10 @@ default: return -EINVAL; } - /* Round down to a power of 2, no more than max */ - urb->interval = min(max, 1 << ilog2(urb->interval)); + if (dev->speed != USB_SPEED_VARIABLE) { + /* Round down to a power of 2, no more than max */ + urb->interval = min(max, 1 << ilog2(urb->interval)); + } } return usb_hcd_submit_urb(urb, mem_flags); diff -Naur -X linux-2.6.32-rc5-tty/Documentation/dontdiff linux-2.6.32-rc5-tty/drivers/usb/core/usb.h linux-2.6.32-rc5-usb/drivers/usb/core/usb.h --- linux-2.6.32-rc5-tty/drivers/usb/core/usb.h 2009-10-15 20:35:57.000000000 -0700 +++ linux-2.6.32-rc5-usb/drivers/usb/core/usb.h 2009-10-30 12:27:50.000000000 -0700 @@ -24,6 +24,7 @@ extern int usb_deauthorize_device(struct usb_device *); extern int usb_authorize_device(struct usb_device *); extern void usb_detect_quirks(struct usb_device *udev); +extern int usb_remove_device(struct usb_device *udev); extern int usb_get_device_descriptor(struct usb_device *dev, unsigned int size); diff -Naur -X linux-2.6.32-rc5-tty/Documentation/dontdiff linux-2.6.32-rc5-tty/drivers/usb/gadget/at91_udc.c linux-2.6.32-rc5-usb/drivers/usb/gadget/at91_udc.c --- linux-2.6.32-rc5-tty/drivers/usb/gadget/at91_udc.c 2009-10-15 20:35:57.000000000 -0700 +++ linux-2.6.32-rc5-usb/drivers/usb/gadget/at91_udc.c 2009-10-30 12:27:49.000000000 -0700 @@ -892,7 +892,7 @@ txvc |= AT91_UDP_TXVC_PUON; at91_udp_write(udc, AT91_UDP_TXVC, txvc); - } else if (cpu_is_at91sam9261()) { + } else if (cpu_is_at91sam9261() || cpu_is_at91sam9g10()) { u32 usbpucr; usbpucr = at91_sys_read(AT91_MATRIX_USBPUCR); @@ -910,7 +910,7 @@ txvc &= ~AT91_UDP_TXVC_PUON; at91_udp_write(udc, AT91_UDP_TXVC, txvc); - } else if (cpu_is_at91sam9261()) { + } else if (cpu_is_at91sam9261() || cpu_is_at91sam9g10()) { u32 usbpucr; usbpucr = at91_sys_read(AT91_MATRIX_USBPUCR); @@ -1692,7 +1692,7 @@ udc->ep[3].maxpacket = 64; udc->ep[4].maxpacket = 512; udc->ep[5].maxpacket = 512; - } else if (cpu_is_at91sam9261()) { + } else if (cpu_is_at91sam9261() || cpu_is_at91sam9g10()) { udc->ep[3].maxpacket = 64; } else if (cpu_is_at91sam9263()) { udc->ep[0].maxpacket = 64; diff -Naur -X linux-2.6.32-rc5-tty/Documentation/dontdiff linux-2.6.32-rc5-tty/drivers/usb/gadget/audio.c linux-2.6.32-rc5-usb/drivers/usb/gadget/audio.c --- linux-2.6.32-rc5-tty/drivers/usb/gadget/audio.c 2009-10-15 20:35:57.000000000 -0700 +++ linux-2.6.32-rc5-usb/drivers/usb/gadget/audio.c 2009-10-30 12:27:49.000000000 -0700 @@ -89,120 +89,6 @@ /*-------------------------------------------------------------------------*/ -/** - * Handle USB audio endpoint set/get command in setup class request - */ - -static int audio_set_endpoint_req(struct usb_configuration *c, - const struct usb_ctrlrequest *ctrl) -{ - struct usb_composite_dev *cdev = c->cdev; - int value = -EOPNOTSUPP; - u16 ep = le16_to_cpu(ctrl->wIndex); - u16 len = le16_to_cpu(ctrl->wLength); - u16 w_value = le16_to_cpu(ctrl->wValue); - - DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n", - ctrl->bRequest, w_value, len, ep); - - switch (ctrl->bRequest) { - case UAC_SET_CUR: - value = 0; - break; - - case UAC_SET_MIN: - break; - - case UAC_SET_MAX: - break; - - case UAC_SET_RES: - break; - - case UAC_SET_MEM: - break; - - default: - break; - } - - return value; -} - -static int audio_get_endpoint_req(struct usb_configuration *c, - const struct usb_ctrlrequest *ctrl) -{ - struct usb_composite_dev *cdev = c->cdev; - int value = -EOPNOTSUPP; - u8 ep = ((le16_to_cpu(ctrl->wIndex) >> 8) & 0xFF); - u16 len = le16_to_cpu(ctrl->wLength); - u16 w_value = le16_to_cpu(ctrl->wValue); - - DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n", - ctrl->bRequest, w_value, len, ep); - - switch (ctrl->bRequest) { - case UAC_GET_CUR: - case UAC_GET_MIN: - case UAC_GET_MAX: - case UAC_GET_RES: - value = 3; - break; - case UAC_GET_MEM: - break; - default: - break; - } - - return value; -} - -static int -audio_setup(struct usb_configuration *c, const struct usb_ctrlrequest *ctrl) -{ - struct usb_composite_dev *cdev = c->cdev; - struct usb_request *req = cdev->req; - int value = -EOPNOTSUPP; - u16 w_index = le16_to_cpu(ctrl->wIndex); - u16 w_value = le16_to_cpu(ctrl->wValue); - u16 w_length = le16_to_cpu(ctrl->wLength); - - /* composite driver infrastructure handles everything except - * Audio class messages; interface activation uses set_alt(). - */ - switch (ctrl->bRequestType) { - case USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_ENDPOINT: - value = audio_set_endpoint_req(c, ctrl); - break; - - case USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_ENDPOINT: - value = audio_get_endpoint_req(c, ctrl); - break; - - default: - ERROR(cdev, "Invalid control req%02x.%02x v%04x i%04x l%d\n", - ctrl->bRequestType, ctrl->bRequest, - w_value, w_index, w_length); - } - - /* respond with data transfer or status phase? */ - if (value >= 0) { - DBG(cdev, "Audio req%02x.%02x v%04x i%04x l%d\n", - ctrl->bRequestType, ctrl->bRequest, - w_value, w_index, w_length); - req->zero = 0; - req->length = value; - value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC); - if (value < 0) - ERROR(cdev, "Audio response on err %d\n", value); - } - - /* device either stalls (value < 0) or reports success */ - return value; -} - -/*-------------------------------------------------------------------------*/ - static int __init audio_do_config(struct usb_configuration *c) { /* FIXME alloc iConfiguration string, set it in c->strings */ @@ -220,7 +106,6 @@ static struct usb_configuration audio_config_driver = { .label = DRIVER_DESC, .bind = audio_do_config, - .setup = audio_setup, .bConfigurationValue = 1, /* .iConfiguration = DYNAMIC */ .bmAttributes = USB_CONFIG_ATT_SELFPOWER, diff -Naur -X linux-2.6.32-rc5-tty/Documentation/dontdiff linux-2.6.32-rc5-tty/drivers/usb/gadget/composite.c linux-2.6.32-rc5-usb/drivers/usb/gadget/composite.c --- linux-2.6.32-rc5-tty/drivers/usb/gadget/composite.c 2009-10-15 20:35:57.000000000 -0700 +++ linux-2.6.32-rc5-usb/drivers/usb/gadget/composite.c 2009-10-30 12:27:49.000000000 -0700 @@ -373,6 +373,8 @@ list_for_each_entry(f, &cdev->config->functions, list) { if (f->disable) f->disable(f); + + bitmap_zero(f->endpoints, 32); } cdev->config = NULL; } @@ -418,10 +420,35 @@ /* Initialize all interfaces by setting them to altsetting zero. */ for (tmp = 0; tmp < MAX_CONFIG_INTERFACES; tmp++) { struct usb_function *f = c->interface[tmp]; + struct usb_descriptor_header **descriptors; if (!f) break; + /* + * Record which endpoints are used by the function. This is used + * to dispatch control requests targeted at that endpoint to the + * function's setup callback instead of the current + * configuration's setup callback. + */ + if (gadget->speed == USB_SPEED_HIGH) + descriptors = f->hs_descriptors; + else + descriptors = f->descriptors; + + for (; *descriptors; ++descriptors) { + struct usb_endpoint_descriptor *ep; + int addr; + + if ((*descriptors)->bDescriptorType != USB_DT_ENDPOINT) + continue; + + ep = (struct usb_endpoint_descriptor *)*descriptors; + addr = ((ep->bEndpointAddress & 0x80) >> 3) + | (ep->bEndpointAddress & 0x0f); + set_bit(addr, f->endpoints); + } + result = f->set_alt(f, tmp, 0); if (result < 0) { DBG(cdev, "interface %d (%s/%p) alt 0 --> %d\n", @@ -688,6 +715,7 @@ u16 w_value = le16_to_cpu(ctrl->wValue); u16 w_length = le16_to_cpu(ctrl->wLength); struct usb_function *f = NULL; + u8 endp; /* partial re-init of the response message; the function or the * gadget might need to intercept e.g. a control-OUT completion @@ -800,23 +828,33 @@ ctrl->bRequestType, ctrl->bRequest, w_value, w_index, w_length); - /* functions always handle their interfaces ... punt other - * recipients (endpoint, other, WUSB, ...) to the current + /* functions always handle their interfaces and endpoints... + * punt other recipients (other, WUSB, ...) to the current * configuration code. * * REVISIT it could make sense to let the composite device * take such requests too, if that's ever needed: to work * in config 0, etc. */ - if ((ctrl->bRequestType & USB_RECIP_MASK) - == USB_RECIP_INTERFACE) { + switch (ctrl->bRequestType & USB_RECIP_MASK) { + case USB_RECIP_INTERFACE: f = cdev->config->interface[intf]; - if (f && f->setup) - value = f->setup(f, ctrl); - else + break; + + case USB_RECIP_ENDPOINT: + endp = ((w_index & 0x80) >> 3) | (w_index & 0x0f); + list_for_each_entry(f, &cdev->config->functions, list) { + if (test_bit(endp, f->endpoints)) + break; + } + if (&f->list == &cdev->config->functions) f = NULL; + break; } - if (value < 0 && !f) { + + if (f && f->setup) + value = f->setup(f, ctrl); + else { struct usb_configuration *c; c = cdev->config; diff -Naur -X linux-2.6.32-rc5-tty/Documentation/dontdiff linux-2.6.32-rc5-tty/drivers/usb/gadget/f_audio.c linux-2.6.32-rc5-usb/drivers/usb/gadget/f_audio.c --- linux-2.6.32-rc5-tty/drivers/usb/gadget/f_audio.c 2009-10-15 20:35:57.000000000 -0700 +++ linux-2.6.32-rc5-usb/drivers/usb/gadget/f_audio.c 2009-10-30 12:27:49.000000000 -0700 @@ -445,6 +445,70 @@ return len; } +static int audio_set_endpoint_req(struct usb_function *f, + const struct usb_ctrlrequest *ctrl) +{ + struct usb_composite_dev *cdev = f->config->cdev; + int value = -EOPNOTSUPP; + u16 ep = le16_to_cpu(ctrl->wIndex); + u16 len = le16_to_cpu(ctrl->wLength); + u16 w_value = le16_to_cpu(ctrl->wValue); + + DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n", + ctrl->bRequest, w_value, len, ep); + + switch (ctrl->bRequest) { + case UAC_SET_CUR: + value = 0; + break; + + case UAC_SET_MIN: + break; + + case UAC_SET_MAX: + break; + + case UAC_SET_RES: + break; + + case UAC_SET_MEM: + break; + + default: + break; + } + + return value; +} + +static int audio_get_endpoint_req(struct usb_function *f, + const struct usb_ctrlrequest *ctrl) +{ + struct usb_composite_dev *cdev = f->config->cdev; + int value = -EOPNOTSUPP; + u8 ep = ((le16_to_cpu(ctrl->wIndex) >> 8) & 0xFF); + u16 len = le16_to_cpu(ctrl->wLength); + u16 w_value = le16_to_cpu(ctrl->wValue); + + DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n", + ctrl->bRequest, w_value, len, ep); + + switch (ctrl->bRequest) { + case UAC_GET_CUR: + case UAC_GET_MIN: + case UAC_GET_MAX: + case UAC_GET_RES: + value = 3; + break; + case UAC_GET_MEM: + break; + default: + break; + } + + return value; +} + static int f_audio_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) { @@ -455,8 +519,8 @@ u16 w_value = le16_to_cpu(ctrl->wValue); u16 w_length = le16_to_cpu(ctrl->wLength); - /* composite driver infrastructure handles everything except - * Audio class messages; interface activation uses set_alt(). + /* composite driver infrastructure handles everything; interface + * activation uses set_alt(). */ switch (ctrl->bRequestType) { case USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE: @@ -467,6 +531,14 @@ value = audio_get_intf_req(f, ctrl); break; + case USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_ENDPOINT: + value = audio_set_endpoint_req(f, ctrl); + break; + + case USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_ENDPOINT: + value = audio_get_endpoint_req(f, ctrl); + break; + default: ERROR(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n", ctrl->bRequestType, ctrl->bRequest, diff -Naur -X linux-2.6.32-rc5-tty/Documentation/dontdiff linux-2.6.32-rc5-tty/drivers/usb/host/ehci-hcd.c linux-2.6.32-rc5-usb/drivers/usb/host/ehci-hcd.c --- linux-2.6.32-rc5-tty/drivers/usb/host/ehci-hcd.c 2009-10-15 20:35:57.000000000 -0700 +++ linux-2.6.32-rc5-usb/drivers/usb/host/ehci-hcd.c 2009-10-30 12:27:50.000000000 -0700 @@ -548,7 +548,7 @@ /* controllers may cache some of the periodic schedule ... */ hcc_params = ehci_readl(ehci, &ehci->caps->hcc_params); if (HCC_ISOC_CACHE(hcc_params)) // full frame cache - ehci->i_thresh = 8; + ehci->i_thresh = 2 + 8; else // N microframes cached ehci->i_thresh = 2 + HCC_ISOC_THRES(hcc_params); @@ -1103,11 +1103,21 @@ #define PLATFORM_DRIVER ehci_fsl_driver #endif +#ifdef CONFIG_USB_EHCI_MXC +#include "ehci-mxc.c" +#define PLATFORM_DRIVER ehci_mxc_driver +#endif + #ifdef CONFIG_SOC_AU1200 #include "ehci-au1xxx.c" #define PLATFORM_DRIVER ehci_hcd_au1xxx_driver #endif +#ifdef CONFIG_ARCH_OMAP34XX +#include "ehci-omap.c" +#define PLATFORM_DRIVER ehci_hcd_omap_driver +#endif + #ifdef CONFIG_PPC_PS3 #include "ehci-ps3.c" #define PS3_SYSTEM_BUS_DRIVER ps3_ehci_driver @@ -1118,6 +1128,11 @@ #define OF_PLATFORM_DRIVER ehci_hcd_ppc_of_driver #endif +#ifdef CONFIG_XPS_USB_HCD_XILINX +#include "ehci-xilinx-of.c" +#define OF_PLATFORM_DRIVER ehci_hcd_xilinx_of_driver +#endif + #ifdef CONFIG_PLAT_ORION #include "ehci-orion.c" #define PLATFORM_DRIVER ehci_orion_driver diff -Naur -X linux-2.6.32-rc5-tty/Documentation/dontdiff linux-2.6.32-rc5-tty/drivers/usb/host/ehci-hub.c linux-2.6.32-rc5-usb/drivers/usb/host/ehci-hub.c --- linux-2.6.32-rc5-tty/drivers/usb/host/ehci-hub.c 2009-10-15 20:35:57.000000000 -0700 +++ linux-2.6.32-rc5-usb/drivers/usb/host/ehci-hub.c 2009-10-30 12:27:49.000000000 -0700 @@ -236,7 +236,7 @@ } if (unlikely(ehci->debug)) { - if (ehci->debug && !dbgp_reset_prep()) + if (!dbgp_reset_prep()) ehci->debug = NULL; else dbgp_external_startup(); diff -Naur -X linux-2.6.32-rc5-tty/Documentation/dontdiff linux-2.6.32-rc5-tty/drivers/usb/host/ehci-mxc.c linux-2.6.32-rc5-usb/drivers/usb/host/ehci-mxc.c --- linux-2.6.32-rc5-tty/drivers/usb/host/ehci-mxc.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.32-rc5-usb/drivers/usb/host/ehci-mxc.c 2009-10-30 12:27:50.000000000 -0700 @@ -0,0 +1,296 @@ +/* + * Copyright (c) 2008 Sascha Hauer , Pengutronix + * Copyright (c) 2009 Daniel Mack + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include + +#include + +#define ULPI_VIEWPORT_OFFSET 0x170 +#define PORTSC_OFFSET 0x184 +#define USBMODE_OFFSET 0x1a8 +#define USBMODE_CM_HOST 3 + +struct ehci_mxc_priv { + struct clk *usbclk, *ahbclk; + struct usb_hcd *hcd; +}; + +/* called during probe() after chip reset completes */ +static int ehci_mxc_setup(struct usb_hcd *hcd) +{ + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + int retval; + + /* EHCI registers start at offset 0x100 */ + ehci->caps = hcd->regs + 0x100; + ehci->regs = hcd->regs + 0x100 + + HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase)); + dbg_hcs_params(ehci, "reset"); + dbg_hcc_params(ehci, "reset"); + + /* cache this readonly data; minimize chip reads */ + ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); + + retval = ehci_halt(ehci); + if (retval) + return retval; + + /* data structure init */ + retval = ehci_init(hcd); + if (retval) + return retval; + + hcd->has_tt = 1; + + ehci->sbrn = 0x20; + + ehci_reset(ehci); + + ehci_port_power(ehci, 0); + return 0; +} + +static const struct hc_driver ehci_mxc_hc_driver = { + .description = hcd_name, + .product_desc = "Freescale On-Chip EHCI Host Controller", + .hcd_priv_size = sizeof(struct ehci_hcd), + + /* + * generic hardware linkage + */ + .irq = ehci_irq, + .flags = HCD_USB2 | HCD_MEMORY, + + /* + * basic lifecycle operations + */ + .reset = ehci_mxc_setup, + .start = ehci_run, + .stop = ehci_stop, + .shutdown = ehci_shutdown, + + /* + * managing i/o requests and associated device resources + */ + .urb_enqueue = ehci_urb_enqueue, + .urb_dequeue = ehci_urb_dequeue, + .endpoint_disable = ehci_endpoint_disable, + + /* + * scheduling support + */ + .get_frame_number = ehci_get_frame, + + /* + * root hub support + */ + .hub_status_data = ehci_hub_status_data, + .hub_control = ehci_hub_control, + .bus_suspend = ehci_bus_suspend, + .bus_resume = ehci_bus_resume, + .relinquish_port = ehci_relinquish_port, + .port_handed_over = ehci_port_handed_over, +}; + +static int ehci_mxc_drv_probe(struct platform_device *pdev) +{ + struct mxc_usbh_platform_data *pdata = pdev->dev.platform_data; + struct usb_hcd *hcd; + struct resource *res; + int irq, ret, temp; + struct ehci_mxc_priv *priv; + struct device *dev = &pdev->dev; + + dev_info(&pdev->dev, "initializing i.MX USB Controller\n"); + + if (!pdata) { + dev_err(dev, "No platform data given, bailing out.\n"); + return -EINVAL; + } + + irq = platform_get_irq(pdev, 0); + + hcd = usb_create_hcd(&ehci_mxc_hc_driver, dev, dev_name(dev)); + if (!hcd) + return -ENOMEM; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) { + ret = -ENOMEM; + goto err_alloc; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(dev, "Found HC with no register addr. Check setup!\n"); + ret = -ENODEV; + goto err_get_resource; + } + + hcd->rsrc_start = res->start; + hcd->rsrc_len = resource_size(res); + + if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { + dev_dbg(dev, "controller already in use\n"); + ret = -EBUSY; + goto err_request_mem; + } + + hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); + if (!hcd->regs) { + dev_err(dev, "error mapping memory\n"); + ret = -EFAULT; + goto err_ioremap; + } + + /* enable clocks */ + priv->usbclk = clk_get(dev, "usb"); + if (IS_ERR(priv->usbclk)) { + ret = PTR_ERR(priv->usbclk); + goto err_clk; + } + clk_enable(priv->usbclk); + + if (!cpu_is_mx35()) { + priv->ahbclk = clk_get(dev, "usb_ahb"); + if (IS_ERR(priv->ahbclk)) { + ret = PTR_ERR(priv->ahbclk); + goto err_clk_ahb; + } + clk_enable(priv->ahbclk); + } + + /* set USBMODE to host mode */ + temp = readl(hcd->regs + USBMODE_OFFSET); + writel(temp | USBMODE_CM_HOST, hcd->regs + USBMODE_OFFSET); + + /* set up the PORTSCx register */ + writel(pdata->portsc, hcd->regs + PORTSC_OFFSET); + mdelay(10); + + /* setup USBCONTROL. */ + ret = mxc_set_usbcontrol(pdev->id, pdata->flags); + if (ret < 0) + goto err_init; + + /* call platform specific init function */ + if (pdata->init) { + ret = pdata->init(pdev); + if (ret) { + dev_err(dev, "platform init failed\n"); + goto err_init; + } + } + + /* most platforms need some time to settle changed IO settings */ + mdelay(10); + + /* Initialize the transceiver */ + if (pdata->otg) { + pdata->otg->io_priv = hcd->regs + ULPI_VIEWPORT_OFFSET; + if (otg_init(pdata->otg) != 0) + dev_err(dev, "unable to init transceiver\n"); + else if (otg_set_vbus(pdata->otg, 1) != 0) + dev_err(dev, "unable to enable vbus on transceiver\n"); + } + + priv->hcd = hcd; + platform_set_drvdata(pdev, priv); + + ret = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED); + if (ret) + goto err_add; + + return 0; + +err_add: + if (pdata && pdata->exit) + pdata->exit(pdev); +err_init: + if (priv->ahbclk) { + clk_disable(priv->ahbclk); + clk_put(priv->ahbclk); + } +err_clk_ahb: + clk_disable(priv->usbclk); + clk_put(priv->usbclk); +err_clk: + iounmap(hcd->regs); +err_ioremap: + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); +err_request_mem: +err_get_resource: + kfree(priv); +err_alloc: + usb_put_hcd(hcd); + return ret; +} + +static int __exit ehci_mxc_drv_remove(struct platform_device *pdev) +{ + struct mxc_usbh_platform_data *pdata = pdev->dev.platform_data; + struct ehci_mxc_priv *priv = platform_get_drvdata(pdev); + struct usb_hcd *hcd = priv->hcd; + + if (pdata && pdata->exit) + pdata->exit(pdev); + + if (pdata->otg) + otg_shutdown(pdata->otg); + + usb_remove_hcd(hcd); + iounmap(hcd->regs); + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); + usb_put_hcd(hcd); + platform_set_drvdata(pdev, NULL); + + clk_disable(priv->usbclk); + clk_put(priv->usbclk); + if (priv->ahbclk) { + clk_disable(priv->ahbclk); + clk_put(priv->ahbclk); + } + + kfree(priv); + + return 0; +} + +static void ehci_mxc_drv_shutdown(struct platform_device *pdev) +{ + struct ehci_mxc_priv *priv = platform_get_drvdata(pdev); + struct usb_hcd *hcd = priv->hcd; + + if (hcd->driver->shutdown) + hcd->driver->shutdown(hcd); +} + +MODULE_ALIAS("platform:mxc-ehci"); + +static struct platform_driver ehci_mxc_driver = { + .probe = ehci_mxc_drv_probe, + .remove = __exit_p(ehci_mxc_drv_remove), + .shutdown = ehci_mxc_drv_shutdown, + .driver = { + .name = "mxc-ehci", + }, +}; diff -Naur -X linux-2.6.32-rc5-tty/Documentation/dontdiff linux-2.6.32-rc5-tty/drivers/usb/host/ehci-omap.c linux-2.6.32-rc5-usb/drivers/usb/host/ehci-omap.c --- linux-2.6.32-rc5-tty/drivers/usb/host/ehci-omap.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.32-rc5-usb/drivers/usb/host/ehci-omap.c 2009-10-30 12:27:49.000000000 -0700 @@ -0,0 +1,755 @@ +/* + * ehci-omap.c - driver for USBHOST on OMAP 34xx processor + * + * Bus Glue for OMAP34xx USBHOST 3 port EHCI controller + * Tested on OMAP3430 ES2.0 SDP + * + * Copyright (C) 2007-2008 Texas Instruments, Inc. + * Author: Vikram Pandita + * + * Copyright (C) 2009 Nokia Corporation + * Contact: Felipe Balbi + * + * Based on "ehci-fsl.c" and "ehci-au1xxx.c" ehci glue layers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * TODO (last updated Feb 23rd, 2009): + * - add kernel-doc + * - enable AUTOIDLE + * - move DPLL5 programming to clock fw + * - add suspend/resume + * - move workarounds to board-files + */ + +#include +#include +#include +#include + +/* + * OMAP USBHOST Register addresses: VIRTUAL ADDRESSES + * Use ehci_omap_readl()/ehci_omap_writel() functions + */ + +/* TLL Register Set */ +#define OMAP_USBTLL_REVISION (0x00) +#define OMAP_USBTLL_SYSCONFIG (0x10) +#define OMAP_USBTLL_SYSCONFIG_CACTIVITY (1 << 8) +#define OMAP_USBTLL_SYSCONFIG_SIDLEMODE (1 << 3) +#define OMAP_USBTLL_SYSCONFIG_ENAWAKEUP (1 << 2) +#define OMAP_USBTLL_SYSCONFIG_SOFTRESET (1 << 1) +#define OMAP_USBTLL_SYSCONFIG_AUTOIDLE (1 << 0) + +#define OMAP_USBTLL_SYSSTATUS (0x14) +#define OMAP_USBTLL_SYSSTATUS_RESETDONE (1 << 0) + +#define OMAP_USBTLL_IRQSTATUS (0x18) +#define OMAP_USBTLL_IRQENABLE (0x1C) + +#define OMAP_TLL_SHARED_CONF (0x30) +#define OMAP_TLL_SHARED_CONF_USB_90D_DDR_EN (1 << 6) +#define OMAP_TLL_SHARED_CONF_USB_180D_SDR_EN (1 << 5) +#define OMAP_TLL_SHARED_CONF_USB_DIVRATION (1 << 2) +#define OMAP_TLL_SHARED_CONF_FCLK_REQ (1 << 1) +#define OMAP_TLL_SHARED_CONF_FCLK_IS_ON (1 << 0) + +#define OMAP_TLL_CHANNEL_CONF(num) (0x040 + 0x004 * num) +#define OMAP_TLL_CHANNEL_CONF_ULPINOBITSTUFF (1 << 11) +#define OMAP_TLL_CHANNEL_CONF_ULPI_ULPIAUTOIDLE (1 << 10) +#define OMAP_TLL_CHANNEL_CONF_UTMIAUTOIDLE (1 << 9) +#define OMAP_TLL_CHANNEL_CONF_ULPIDDRMODE (1 << 8) +#define OMAP_TLL_CHANNEL_CONF_CHANEN (1 << 0) + +#define OMAP_TLL_ULPI_FUNCTION_CTRL(num) (0x804 + 0x100 * num) +#define OMAP_TLL_ULPI_INTERFACE_CTRL(num) (0x807 + 0x100 * num) +#define OMAP_TLL_ULPI_OTG_CTRL(num) (0x80A + 0x100 * num) +#define OMAP_TLL_ULPI_INT_EN_RISE(num) (0x80D + 0x100 * num) +#define OMAP_TLL_ULPI_INT_EN_FALL(num) (0x810 + 0x100 * num) +#define OMAP_TLL_ULPI_INT_STATUS(num) (0x813 + 0x100 * num) +#define OMAP_TLL_ULPI_INT_LATCH(num) (0x814 + 0x100 * num) +#define OMAP_TLL_ULPI_DEBUG(num) (0x815 + 0x100 * num) +#define OMAP_TLL_ULPI_SCRATCH_REGISTER(num) (0x816 + 0x100 * num) + +#define OMAP_TLL_CHANNEL_COUNT 3 +#define OMAP_TLL_CHANNEL_1_EN_MASK (1 << 1) +#define OMAP_TLL_CHANNEL_2_EN_MASK (1 << 2) +#define OMAP_TLL_CHANNEL_3_EN_MASK (1 << 4) + +/* UHH Register Set */ +#define OMAP_UHH_REVISION (0x00) +#define OMAP_UHH_SYSCONFIG (0x10) +#define OMAP_UHH_SYSCONFIG_MIDLEMODE (1 << 12) +#define OMAP_UHH_SYSCONFIG_CACTIVITY (1 << 8) +#define OMAP_UHH_SYSCONFIG_SIDLEMODE (1 << 3) +#define OMAP_UHH_SYSCONFIG_ENAWAKEUP (1 << 2) +#define OMAP_UHH_SYSCONFIG_SOFTRESET (1 << 1) +#define OMAP_UHH_SYSCONFIG_AUTOIDLE (1 << 0) + +#define OMAP_UHH_SYSSTATUS (0x14) +#define OMAP_UHH_HOSTCONFIG (0x40) +#define OMAP_UHH_HOSTCONFIG_ULPI_BYPASS (1 << 0) +#define OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS (1 << 0) +#define OMAP_UHH_HOSTCONFIG_ULPI_P2_BYPASS (1 << 11) +#define OMAP_UHH_HOSTCONFIG_ULPI_P3_BYPASS (1 << 12) +#define OMAP_UHH_HOSTCONFIG_INCR4_BURST_EN (1 << 2) +#define OMAP_UHH_HOSTCONFIG_INCR8_BURST_EN (1 << 3) +#define OMAP_UHH_HOSTCONFIG_INCR16_BURST_EN (1 << 4) +#define OMAP_UHH_HOSTCONFIG_INCRX_ALIGN_EN (1 << 5) +#define OMAP_UHH_HOSTCONFIG_P1_CONNECT_STATUS (1 << 8) +#define OMAP_UHH_HOSTCONFIG_P2_CONNECT_STATUS (1 << 9) +#define OMAP_UHH_HOSTCONFIG_P3_CONNECT_STATUS (1 << 10) + +#define OMAP_UHH_DEBUG_CSR (0x44) + +/* EHCI Register Set */ +#define EHCI_INSNREG05_ULPI (0xA4) +#define EHCI_INSNREG05_ULPI_CONTROL_SHIFT 31 +#define EHCI_INSNREG05_ULPI_PORTSEL_SHIFT 24 +#define EHCI_INSNREG05_ULPI_OPSEL_SHIFT 22 +#define EHCI_INSNREG05_ULPI_REGADD_SHIFT 16 +#define EHCI_INSNREG05_ULPI_EXTREGADD_SHIFT 8 +#define EHCI_INSNREG05_ULPI_WRDATA_SHIFT 0 + +/*-------------------------------------------------------------------------*/ + +static inline void ehci_omap_writel(void __iomem *base, u32 reg, u32 val) +{ + __raw_writel(val, base + reg); +} + +static inline u32 ehci_omap_readl(void __iomem *base, u32 reg) +{ + return __raw_readl(base + reg); +} + +static inline void ehci_omap_writeb(void __iomem *base, u8 reg, u8 val) +{ + __raw_writeb(val, base + reg); +} + +static inline u8 ehci_omap_readb(void __iomem *base, u8 reg) +{ + return __raw_readb(base + reg); +} + +/*-------------------------------------------------------------------------*/ + +struct ehci_hcd_omap { + struct ehci_hcd *ehci; + struct device *dev; + + struct clk *usbhost_ick; + struct clk *usbhost2_120m_fck; + struct clk *usbhost1_48m_fck; + struct clk *usbtll_fck; + struct clk *usbtll_ick; + + /* FIXME the following two workarounds are + * board specific not silicon-specific so these + * should be moved to board-file instead. + * + * Maybe someone from TI will know better which + * board is affected and needs the workarounds + * to be applied + */ + + /* gpio for resetting phy */ + int reset_gpio_port[OMAP3_HS_USB_PORTS]; + + /* phy reset workaround */ + int phy_reset; + + /* desired phy_mode: TLL, PHY */ + enum ehci_hcd_omap_mode port_mode[OMAP3_HS_USB_PORTS]; + + void __iomem *uhh_base; + void __iomem *tll_base; + void __iomem *ehci_base; +}; + +/*-------------------------------------------------------------------------*/ + +static void omap_usb_utmi_init(struct ehci_hcd_omap *omap, u8 tll_channel_mask) +{ + unsigned reg; + int i; + + /* Program the 3 TLL channels upfront */ + for (i = 0; i < OMAP_TLL_CHANNEL_COUNT; i++) { + reg = ehci_omap_readl(omap->tll_base, OMAP_TLL_CHANNEL_CONF(i)); + + /* Disable AutoIdle, BitStuffing and use SDR Mode */ + reg &= ~(OMAP_TLL_CHANNEL_CONF_UTMIAUTOIDLE + | OMAP_TLL_CHANNEL_CONF_ULPINOBITSTUFF + | OMAP_TLL_CHANNEL_CONF_ULPIDDRMODE); + ehci_omap_writel(omap->tll_base, OMAP_TLL_CHANNEL_CONF(i), reg); + } + + /* Program Common TLL register */ + reg = ehci_omap_readl(omap->tll_base, OMAP_TLL_SHARED_CONF); + reg |= (OMAP_TLL_SHARED_CONF_FCLK_IS_ON + | OMAP_TLL_SHARED_CONF_USB_DIVRATION + | OMAP_TLL_SHARED_CONF_USB_180D_SDR_EN); + reg &= ~OMAP_TLL_SHARED_CONF_USB_90D_DDR_EN; + + ehci_omap_writel(omap->tll_base, OMAP_TLL_SHARED_CONF, reg); + + /* Enable channels now */ + for (i = 0; i < OMAP_TLL_CHANNEL_COUNT; i++) { + reg = ehci_omap_readl(omap->tll_base, OMAP_TLL_CHANNEL_CONF(i)); + + /* Enable only the reg that is needed */ + if (!(tll_channel_mask & 1<tll_base, OMAP_TLL_CHANNEL_CONF(i), reg); + + ehci_omap_writeb(omap->tll_base, + OMAP_TLL_ULPI_SCRATCH_REGISTER(i), 0xbe); + dev_dbg(omap->dev, "ULPI_SCRATCH_REG[ch=%d]= 0x%02x\n", + i+1, ehci_omap_readb(omap->tll_base, + OMAP_TLL_ULPI_SCRATCH_REGISTER(i))); + } +} + +/*-------------------------------------------------------------------------*/ + +/* omap_start_ehc + * - Start the TI USBHOST controller + */ +static int omap_start_ehc(struct ehci_hcd_omap *omap, struct usb_hcd *hcd) +{ + unsigned long timeout = jiffies + msecs_to_jiffies(1000); + u8 tll_ch_mask = 0; + unsigned reg = 0; + int ret = 0; + + dev_dbg(omap->dev, "starting TI EHCI USB Controller\n"); + + /* Enable Clocks for USBHOST */ + omap->usbhost_ick = clk_get(omap->dev, "usbhost_ick"); + if (IS_ERR(omap->usbhost_ick)) { + ret = PTR_ERR(omap->usbhost_ick); + goto err_host_ick; + } + clk_enable(omap->usbhost_ick); + + omap->usbhost2_120m_fck = clk_get(omap->dev, "usbhost_120m_fck"); + if (IS_ERR(omap->usbhost2_120m_fck)) { + ret = PTR_ERR(omap->usbhost2_120m_fck); + goto err_host_120m_fck; + } + clk_enable(omap->usbhost2_120m_fck); + + omap->usbhost1_48m_fck = clk_get(omap->dev, "usbhost_48m_fck"); + if (IS_ERR(omap->usbhost1_48m_fck)) { + ret = PTR_ERR(omap->usbhost1_48m_fck); + goto err_host_48m_fck; + } + clk_enable(omap->usbhost1_48m_fck); + + if (omap->phy_reset) { + /* Refer: ISSUE1 */ + if (gpio_is_valid(omap->reset_gpio_port[0])) { + gpio_request(omap->reset_gpio_port[0], + "USB1 PHY reset"); + gpio_direction_output(omap->reset_gpio_port[0], 0); + } + + if (gpio_is_valid(omap->reset_gpio_port[1])) { + gpio_request(omap->reset_gpio_port[1], + "USB2 PHY reset"); + gpio_direction_output(omap->reset_gpio_port[1], 0); + } + + /* Hold the PHY in RESET for enough time till DIR is high */ + udelay(10); + } + + /* Configure TLL for 60Mhz clk for ULPI */ + omap->usbtll_fck = clk_get(omap->dev, "usbtll_fck"); + if (IS_ERR(omap->usbtll_fck)) { + ret = PTR_ERR(omap->usbtll_fck); + goto err_tll_fck; + } + clk_enable(omap->usbtll_fck); + + omap->usbtll_ick = clk_get(omap->dev, "usbtll_ick"); + if (IS_ERR(omap->usbtll_ick)) { + ret = PTR_ERR(omap->usbtll_ick); + goto err_tll_ick; + } + clk_enable(omap->usbtll_ick); + + /* perform TLL soft reset, and wait until reset is complete */ + ehci_omap_writel(omap->tll_base, OMAP_USBTLL_SYSCONFIG, + OMAP_USBTLL_SYSCONFIG_SOFTRESET); + + /* Wait for TLL reset to complete */ + while (!(ehci_omap_readl(omap->tll_base, OMAP_USBTLL_SYSSTATUS) + & OMAP_USBTLL_SYSSTATUS_RESETDONE)) { + cpu_relax(); + + if (time_after(jiffies, timeout)) { + dev_dbg(omap->dev, "operation timed out\n"); + ret = -EINVAL; + goto err_sys_status; + } + } + + dev_dbg(omap->dev, "TLL RESET DONE\n"); + + /* (1<<3) = no idle mode only for initial debugging */ + ehci_omap_writel(omap->tll_base, OMAP_USBTLL_SYSCONFIG, + OMAP_USBTLL_SYSCONFIG_ENAWAKEUP | + OMAP_USBTLL_SYSCONFIG_SIDLEMODE | + OMAP_USBTLL_SYSCONFIG_CACTIVITY); + + + /* Put UHH in NoIdle/NoStandby mode */ + reg = ehci_omap_readl(omap->uhh_base, OMAP_UHH_SYSCONFIG); + reg |= (OMAP_UHH_SYSCONFIG_ENAWAKEUP + | OMAP_UHH_SYSCONFIG_SIDLEMODE + | OMAP_UHH_SYSCONFIG_CACTIVITY + | OMAP_UHH_SYSCONFIG_MIDLEMODE); + reg &= ~OMAP_UHH_SYSCONFIG_AUTOIDLE; + + ehci_omap_writel(omap->uhh_base, OMAP_UHH_SYSCONFIG, reg); + + reg = ehci_omap_readl(omap->uhh_base, OMAP_UHH_HOSTCONFIG); + + /* setup ULPI bypass and burst configurations */ + reg |= (OMAP_UHH_HOSTCONFIG_INCR4_BURST_EN + | OMAP_UHH_HOSTCONFIG_INCR8_BURST_EN + | OMAP_UHH_HOSTCONFIG_INCR16_BURST_EN); + reg &= ~OMAP_UHH_HOSTCONFIG_INCRX_ALIGN_EN; + + if (omap->port_mode[0] == EHCI_HCD_OMAP_MODE_UNKNOWN) + reg &= ~OMAP_UHH_HOSTCONFIG_P1_CONNECT_STATUS; + if (omap->port_mode[1] == EHCI_HCD_OMAP_MODE_UNKNOWN) + reg &= ~OMAP_UHH_HOSTCONFIG_P2_CONNECT_STATUS; + if (omap->port_mode[2] == EHCI_HCD_OMAP_MODE_UNKNOWN) + reg &= ~OMAP_UHH_HOSTCONFIG_P3_CONNECT_STATUS; + + /* Bypass the TLL module for PHY mode operation */ + if (omap_rev() <= OMAP3430_REV_ES2_1) { + dev_dbg(omap->dev, "OMAP3 ES version <= ES2.1 \n"); + if ((omap->port_mode[0] == EHCI_HCD_OMAP_MODE_PHY) || + (omap->port_mode[1] == EHCI_HCD_OMAP_MODE_PHY) || + (omap->port_mode[2] == EHCI_HCD_OMAP_MODE_PHY)) + reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_BYPASS; + else + reg |= OMAP_UHH_HOSTCONFIG_ULPI_BYPASS; + } else { + dev_dbg(omap->dev, "OMAP3 ES version > ES2.1\n"); + if (omap->port_mode[0] == EHCI_HCD_OMAP_MODE_PHY) + reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS; + else if (omap->port_mode[0] == EHCI_HCD_OMAP_MODE_TLL) + reg |= OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS; + + if (omap->port_mode[1] == EHCI_HCD_OMAP_MODE_PHY) + reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_P2_BYPASS; + else if (omap->port_mode[1] == EHCI_HCD_OMAP_MODE_TLL) + reg |= OMAP_UHH_HOSTCONFIG_ULPI_P2_BYPASS; + + if (omap->port_mode[2] == EHCI_HCD_OMAP_MODE_PHY) + reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_P3_BYPASS; + else if (omap->port_mode[2] == EHCI_HCD_OMAP_MODE_TLL) + reg |= OMAP_UHH_HOSTCONFIG_ULPI_P3_BYPASS; + + } + ehci_omap_writel(omap->uhh_base, OMAP_UHH_HOSTCONFIG, reg); + dev_dbg(omap->dev, "UHH setup done, uhh_hostconfig=%x\n", reg); + + + if ((omap->port_mode[0] == EHCI_HCD_OMAP_MODE_TLL) || + (omap->port_mode[1] == EHCI_HCD_OMAP_MODE_TLL) || + (omap->port_mode[2] == EHCI_HCD_OMAP_MODE_TLL)) { + + if (omap->port_mode[0] == EHCI_HCD_OMAP_MODE_TLL) + tll_ch_mask |= OMAP_TLL_CHANNEL_1_EN_MASK; + if (omap->port_mode[1] == EHCI_HCD_OMAP_MODE_TLL) + tll_ch_mask |= OMAP_TLL_CHANNEL_2_EN_MASK; + if (omap->port_mode[2] == EHCI_HCD_OMAP_MODE_TLL) + tll_ch_mask |= OMAP_TLL_CHANNEL_3_EN_MASK; + + /* Enable UTMI mode for required TLL channels */ + omap_usb_utmi_init(omap, tll_ch_mask); + } + + if (omap->phy_reset) { + /* Refer ISSUE1: + * Hold the PHY in RESET for enough time till + * PHY is settled and ready + */ + udelay(10); + + if (gpio_is_valid(omap->reset_gpio_port[0])) + gpio_set_value(omap->reset_gpio_port[0], 1); + + if (gpio_is_valid(omap->reset_gpio_port[1])) + gpio_set_value(omap->reset_gpio_port[1], 1); + } + + return 0; + +err_sys_status: + clk_disable(omap->usbtll_ick); + clk_put(omap->usbtll_ick); + +err_tll_ick: + clk_disable(omap->usbtll_fck); + clk_put(omap->usbtll_fck); + +err_tll_fck: + clk_disable(omap->usbhost1_48m_fck); + clk_put(omap->usbhost1_48m_fck); + + if (omap->phy_reset) { + if (gpio_is_valid(omap->reset_gpio_port[0])) + gpio_free(omap->reset_gpio_port[0]); + + if (gpio_is_valid(omap->reset_gpio_port[1])) + gpio_free(omap->reset_gpio_port[1]); + } + +err_host_48m_fck: + clk_disable(omap->usbhost2_120m_fck); + clk_put(omap->usbhost2_120m_fck); + +err_host_120m_fck: + clk_disable(omap->usbhost_ick); + clk_put(omap->usbhost_ick); + +err_host_ick: + return ret; +} + +static void omap_stop_ehc(struct ehci_hcd_omap *omap, struct usb_hcd *hcd) +{ + unsigned long timeout = jiffies + msecs_to_jiffies(100); + + dev_dbg(omap->dev, "stopping TI EHCI USB Controller\n"); + + /* Reset OMAP modules for insmod/rmmod to work */ + ehci_omap_writel(omap->uhh_base, OMAP_UHH_SYSCONFIG, + OMAP_UHH_SYSCONFIG_SOFTRESET); + while (!(ehci_omap_readl(omap->uhh_base, OMAP_UHH_SYSSTATUS) + & (1 << 0))) { + cpu_relax(); + + if (time_after(jiffies, timeout)) + dev_dbg(omap->dev, "operation timed out\n"); + } + + while (!(ehci_omap_readl(omap->uhh_base, OMAP_UHH_SYSSTATUS) + & (1 << 1))) { + cpu_relax(); + + if (time_after(jiffies, timeout)) + dev_dbg(omap->dev, "operation timed out\n"); + } + + while (!(ehci_omap_readl(omap->uhh_base, OMAP_UHH_SYSSTATUS) + & (1 << 2))) { + cpu_relax(); + + if (time_after(jiffies, timeout)) + dev_dbg(omap->dev, "operation timed out\n"); + } + + ehci_omap_writel(omap->tll_base, OMAP_USBTLL_SYSCONFIG, (1 << 1)); + + while (!(ehci_omap_readl(omap->tll_base, OMAP_USBTLL_SYSSTATUS) + & (1 << 0))) { + cpu_relax(); + + if (time_after(jiffies, timeout)) + dev_dbg(omap->dev, "operation timed out\n"); + } + + if (omap->usbtll_fck != NULL) { + clk_disable(omap->usbtll_fck); + clk_put(omap->usbtll_fck); + omap->usbtll_fck = NULL; + } + + if (omap->usbhost_ick != NULL) { + clk_disable(omap->usbhost_ick); + clk_put(omap->usbhost_ick); + omap->usbhost_ick = NULL; + } + + if (omap->usbhost1_48m_fck != NULL) { + clk_disable(omap->usbhost1_48m_fck); + clk_put(omap->usbhost1_48m_fck); + omap->usbhost1_48m_fck = NULL; + } + + if (omap->usbhost2_120m_fck != NULL) { + clk_disable(omap->usbhost2_120m_fck); + clk_put(omap->usbhost2_120m_fck); + omap->usbhost2_120m_fck = NULL; + } + + if (omap->usbtll_ick != NULL) { + clk_disable(omap->usbtll_ick); + clk_put(omap->usbtll_ick); + omap->usbtll_ick = NULL; + } + + if (omap->phy_reset) { + if (gpio_is_valid(omap->reset_gpio_port[0])) + gpio_free(omap->reset_gpio_port[0]); + + if (gpio_is_valid(omap->reset_gpio_port[1])) + gpio_free(omap->reset_gpio_port[1]); + } + + dev_dbg(omap->dev, "Clock to USB host has been disabled\n"); +} + +/*-------------------------------------------------------------------------*/ + +static const struct hc_driver ehci_omap_hc_driver; + +/* configure so an HC device and id are always provided */ +/* always called with process context; sleeping is OK */ + +/** + * ehci_hcd_omap_probe - initialize TI-based HCDs + * + * Allocates basic resources for this USB host controller, and + * then invokes the start() method for the HCD associated with it + * through the hotplug entry's driver_data. + */ +static int ehci_hcd_omap_probe(struct platform_device *pdev) +{ + struct ehci_hcd_omap_platform_data *pdata = pdev->dev.platform_data; + struct ehci_hcd_omap *omap; + struct resource *res; + struct usb_hcd *hcd; + + int irq = platform_get_irq(pdev, 0); + int ret = -ENODEV; + + if (!pdata) { + dev_dbg(&pdev->dev, "missing platform_data\n"); + goto err_pdata; + } + + if (usb_disabled()) + goto err_disabled; + + omap = kzalloc(sizeof(*omap), GFP_KERNEL); + if (!omap) { + ret = -ENOMEM; + goto err_create_hcd; + } + + hcd = usb_create_hcd(&ehci_omap_hc_driver, &pdev->dev, + dev_name(&pdev->dev)); + if (!hcd) { + dev_dbg(&pdev->dev, "failed to create hcd with err %d\n", ret); + ret = -ENOMEM; + goto err_create_hcd; + } + + platform_set_drvdata(pdev, omap); + omap->dev = &pdev->dev; + omap->phy_reset = pdata->phy_reset; + omap->reset_gpio_port[0] = pdata->reset_gpio_port[0]; + omap->reset_gpio_port[1] = pdata->reset_gpio_port[1]; + omap->reset_gpio_port[2] = pdata->reset_gpio_port[2]; + omap->port_mode[0] = pdata->port_mode[0]; + omap->port_mode[1] = pdata->port_mode[1]; + omap->port_mode[2] = pdata->port_mode[2]; + omap->ehci = hcd_to_ehci(hcd); + omap->ehci->sbrn = 0x20; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + hcd->rsrc_start = res->start; + hcd->rsrc_len = resource_size(res); + + hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); + if (!hcd->regs) { + dev_err(&pdev->dev, "EHCI ioremap failed\n"); + ret = -ENOMEM; + goto err_ioremap; + } + + /* we know this is the memory we want, no need to ioremap again */ + omap->ehci->caps = hcd->regs; + omap->ehci_base = hcd->regs; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + omap->uhh_base = ioremap(res->start, resource_size(res)); + if (!omap->uhh_base) { + dev_err(&pdev->dev, "UHH ioremap failed\n"); + ret = -ENOMEM; + goto err_uhh_ioremap; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 2); + omap->tll_base = ioremap(res->start, resource_size(res)); + if (!omap->tll_base) { + dev_err(&pdev->dev, "TLL ioremap failed\n"); + ret = -ENOMEM; + goto err_tll_ioremap; + } + + ret = omap_start_ehc(omap, hcd); + if (ret) { + dev_dbg(&pdev->dev, "failed to start ehci\n"); + goto err_start; + } + + omap->ehci->regs = hcd->regs + + HC_LENGTH(readl(&omap->ehci->caps->hc_capbase)); + + /* cache this readonly data; minimize chip reads */ + omap->ehci->hcs_params = readl(&omap->ehci->caps->hcs_params); + + /* SET 1 micro-frame Interrupt interval */ + writel(readl(&omap->ehci->regs->command) | (1 << 16), + &omap->ehci->regs->command); + + ret = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED); + if (ret) { + dev_dbg(&pdev->dev, "failed to add hcd with err %d\n", ret); + goto err_add_hcd; + } + + return 0; + +err_add_hcd: + omap_stop_ehc(omap, hcd); + +err_start: + iounmap(omap->tll_base); + +err_tll_ioremap: + iounmap(omap->uhh_base); + +err_uhh_ioremap: + iounmap(hcd->regs); + +err_ioremap: + usb_put_hcd(hcd); + +err_create_hcd: +err_disabled: +err_pdata: + return ret; +} + +/* may be called without controller electrically present */ +/* may be called with controller, bus, and devices active */ + +/** + * ehci_hcd_omap_remove - shutdown processing for EHCI HCDs + * @pdev: USB Host Controller being removed + * + * Reverses the effect of usb_ehci_hcd_omap_probe(), first invoking + * the HCD's stop() method. It is always called from a thread + * context, normally "rmmod", "apmd", or something similar. + */ +static int ehci_hcd_omap_remove(struct platform_device *pdev) +{ + struct ehci_hcd_omap *omap = platform_get_drvdata(pdev); + struct usb_hcd *hcd = ehci_to_hcd(omap->ehci); + + usb_remove_hcd(hcd); + omap_stop_ehc(omap, hcd); + iounmap(hcd->regs); + iounmap(omap->tll_base); + iounmap(omap->uhh_base); + usb_put_hcd(hcd); + + return 0; +} + +static void ehci_hcd_omap_shutdown(struct platform_device *pdev) +{ + struct ehci_hcd_omap *omap = platform_get_drvdata(pdev); + struct usb_hcd *hcd = ehci_to_hcd(omap->ehci); + + if (hcd->driver->shutdown) + hcd->driver->shutdown(hcd); +} + +static struct platform_driver ehci_hcd_omap_driver = { + .probe = ehci_hcd_omap_probe, + .remove = ehci_hcd_omap_remove, + .shutdown = ehci_hcd_omap_shutdown, + /*.suspend = ehci_hcd_omap_suspend, */ + /*.resume = ehci_hcd_omap_resume, */ + .driver = { + .name = "ehci-omap", + } +}; + +/*-------------------------------------------------------------------------*/ + +static const struct hc_driver ehci_omap_hc_driver = { + .description = hcd_name, + .product_desc = "OMAP-EHCI Host Controller", + .hcd_priv_size = sizeof(struct ehci_hcd), + + /* + * generic hardware linkage + */ + .irq = ehci_irq, + .flags = HCD_MEMORY | HCD_USB2, + + /* + * basic lifecycle operations + */ + .reset = ehci_init, + .start = ehci_run, + .stop = ehci_stop, + .shutdown = ehci_shutdown, + + /* + * managing i/o requests and associated device resources + */ + .urb_enqueue = ehci_urb_enqueue, + .urb_dequeue = ehci_urb_dequeue, + .endpoint_disable = ehci_endpoint_disable, + .endpoint_reset = ehci_endpoint_reset, + + /* + * scheduling support + */ + .get_frame_number = ehci_get_frame, + + /* + * root hub support + */ + .hub_status_data = ehci_hub_status_data, + .hub_control = ehci_hub_control, + .bus_suspend = ehci_bus_suspend, + .bus_resume = ehci_bus_resume, + + .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, +}; + +MODULE_ALIAS("platform:omap-ehci"); +MODULE_AUTHOR("Texas Instruments, Inc."); +MODULE_AUTHOR("Felipe Balbi "); + diff -Naur -X linux-2.6.32-rc5-tty/Documentation/dontdiff linux-2.6.32-rc5-tty/drivers/usb/host/ehci-sched.c linux-2.6.32-rc5-usb/drivers/usb/host/ehci-sched.c --- linux-2.6.32-rc5-tty/drivers/usb/host/ehci-sched.c 2009-10-15 20:35:57.000000000 -0700 +++ linux-2.6.32-rc5-usb/drivers/usb/host/ehci-sched.c 2009-10-30 12:27:50.000000000 -0700 @@ -1373,7 +1373,7 @@ * given EHCI_TUNE_FLS and the slop). Or, write a smarter scheduler! */ -#define SCHEDULE_SLOP 10 /* frames */ +#define SCHEDULE_SLOP 80 /* microframes */ static int iso_stream_schedule ( @@ -1382,12 +1382,13 @@ struct ehci_iso_stream *stream ) { - u32 now, start, max, period; + u32 now, next, start, period; int status; unsigned mod = ehci->periodic_size << 3; struct ehci_iso_sched *sched = urb->hcpriv; + struct pci_dev *pdev; - if (sched->span > (mod - 8 * SCHEDULE_SLOP)) { + if (sched->span > (mod - SCHEDULE_SLOP)) { ehci_dbg (ehci, "iso request %p too long\n", urb); status = -EFBIG; goto fail; @@ -1406,26 +1407,35 @@ now = ehci_readl(ehci, &ehci->regs->frame_index) % mod; - /* when's the last uframe this urb could start? */ - max = now + mod; - /* Typical case: reuse current schedule, stream is still active. * Hopefully there are no gaps from the host falling behind * (irq delays etc), but if there are we'll take the next * slot in the schedule, implicitly assuming URB_ISO_ASAP. */ if (likely (!list_empty (&stream->td_list))) { + pdev = to_pci_dev(ehci_to_hcd(ehci)->self.controller); start = stream->next_uframe; - if (start < now) - start += mod; + + /* For high speed devices, allow scheduling within the + * isochronous scheduling threshold. For full speed devices, + * don't. (Work around for Intel ICH9 bug.) + */ + if (!stream->highspeed && + pdev->vendor == PCI_VENDOR_ID_INTEL) + next = now + ehci->i_thresh; + else + next = now; /* Fell behind (by up to twice the slop amount)? */ - if (start >= max - 2 * 8 * SCHEDULE_SLOP) + if (((start - next) & (mod - 1)) >= + mod - 2 * SCHEDULE_SLOP) start += period * DIV_ROUND_UP( - max - start, period) - mod; + (next - start) & (mod - 1), + period); /* Tried to schedule too far into the future? */ - if (unlikely((start + sched->span) >= max)) { + if (unlikely(((start - now) & (mod - 1)) + sched->span + >= mod - 2 * SCHEDULE_SLOP)) { status = -EFBIG; goto fail; } @@ -1439,7 +1449,7 @@ * can also help high bandwidth if the dma and irq loads don't * jump until after the queue is primed. */ - start = SCHEDULE_SLOP * 8 + (now & ~0x07); + start = SCHEDULE_SLOP + (now & ~0x07); start %= mod; stream->next_uframe = start; @@ -1470,7 +1480,7 @@ /* no room in the schedule */ ehci_dbg (ehci, "iso %ssched full %p (now %d max %d)\n", list_empty (&stream->td_list) ? "" : "re", - urb, now, max); + urb, now, now + mod); status = -ENOSPC; fail: diff -Naur -X linux-2.6.32-rc5-tty/Documentation/dontdiff linux-2.6.32-rc5-tty/drivers/usb/host/ehci-xilinx-of.c linux-2.6.32-rc5-usb/drivers/usb/host/ehci-xilinx-of.c --- linux-2.6.32-rc5-tty/drivers/usb/host/ehci-xilinx-of.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.32-rc5-usb/drivers/usb/host/ehci-xilinx-of.c 2009-10-30 12:27:49.000000000 -0700 @@ -0,0 +1,300 @@ +/* + * EHCI HCD (Host Controller Driver) for USB. + * + * Bus Glue for Xilinx EHCI core on the of_platform bus + * + * Copyright (c) 2009 Xilinx, Inc. + * + * Based on "ehci-ppc-of.c" by Valentine Barshak + * and "ehci-ppc-soc.c" by Stefan Roese + * and "ohci-ppc-of.c" by Sylvain Munaut + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +#include +#include + +/** + * ehci_xilinx_of_setup - Initialize the device for ehci_reset() + * @hcd: Pointer to the usb_hcd device to which the host controller bound + * + * called during probe() after chip reset completes. + */ +static int ehci_xilinx_of_setup(struct usb_hcd *hcd) +{ + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + int retval; + + retval = ehci_halt(ehci); + if (retval) + return retval; + + retval = ehci_init(hcd); + if (retval) + return retval; + + ehci->sbrn = 0x20; + + return ehci_reset(ehci); +} + +/** + * ehci_xilinx_port_handed_over - hand the port out if failed to enable it + * @hcd: Pointer to the usb_hcd device to which the host controller bound + * @portnum:Port number to which the device is attached. + * + * This function is used as a place to tell the user that the Xilinx USB host + * controller does support LS devices. And in an HS only configuration, it + * does not support FS devices either. It is hoped that this can help a + * confused user. + * + * There are cases when the host controller fails to enable the port due to, + * for example, insufficient power that can be supplied to the device from + * the USB bus. In those cases, the messages printed here are not helpful. + */ +static int ehci_xilinx_port_handed_over(struct usb_hcd *hcd, int portnum) +{ + dev_warn(hcd->self.controller, "port %d cannot be enabled\n", portnum); + if (hcd->has_tt) { + dev_warn(hcd->self.controller, + "Maybe you have connected a low speed device?\n"); + + dev_warn(hcd->self.controller, + "We do not support low speed devices\n"); + } else { + dev_warn(hcd->self.controller, + "Maybe your device is not a high speed device?\n"); + dev_warn(hcd->self.controller, + "The USB host controller does not support full speed " + "nor low speed devices\n"); + dev_warn(hcd->self.controller, + "You can reconfigure the host controller to have " + "full speed support\n"); + } + + return 0; +} + + +static const struct hc_driver ehci_xilinx_of_hc_driver = { + .description = hcd_name, + .product_desc = "OF EHCI", + .hcd_priv_size = sizeof(struct ehci_hcd), + + /* + * generic hardware linkage + */ + .irq = ehci_irq, + .flags = HCD_MEMORY | HCD_USB2, + + /* + * basic lifecycle operations + */ + .reset = ehci_xilinx_of_setup, + .start = ehci_run, + .stop = ehci_stop, + .shutdown = ehci_shutdown, + + /* + * managing i/o requests and associated device resources + */ + .urb_enqueue = ehci_urb_enqueue, + .urb_dequeue = ehci_urb_dequeue, + .endpoint_disable = ehci_endpoint_disable, + + /* + * scheduling support + */ + .get_frame_number = ehci_get_frame, + + /* + * root hub support + */ + .hub_status_data = ehci_hub_status_data, + .hub_control = ehci_hub_control, +#ifdef CONFIG_PM + .bus_suspend = ehci_bus_suspend, + .bus_resume = ehci_bus_resume, +#endif + .relinquish_port = NULL, + .port_handed_over = ehci_xilinx_port_handed_over, + + .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, +}; + +/** + * ehci_hcd_xilinx_of_probe - Probe method for the USB host controller + * @op: pointer to the of_device to which the host controller bound + * @match: pointer to of_device_id structure, not used + * + * This function requests resources and sets up appropriate properties for the + * host controller. Because the Xilinx USB host controller can be configured + * as HS only or HS/FS only, it checks the configuration in the device tree + * entry, and sets an appropriate value for hcd->has_tt. + */ +static int __devinit +ehci_hcd_xilinx_of_probe(struct of_device *op, const struct of_device_id *match) +{ + struct device_node *dn = op->node; + struct usb_hcd *hcd; + struct ehci_hcd *ehci; + struct resource res; + int irq; + int rv; + int *value; + + if (usb_disabled()) + return -ENODEV; + + dev_dbg(&op->dev, "initializing XILINX-OF USB Controller\n"); + + rv = of_address_to_resource(dn, 0, &res); + if (rv) + return rv; + + hcd = usb_create_hcd(&ehci_xilinx_of_hc_driver, &op->dev, + "XILINX-OF USB"); + if (!hcd) + return -ENOMEM; + + hcd->rsrc_start = res.start; + hcd->rsrc_len = res.end - res.start + 1; + + if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { + printk(KERN_ERR __FILE__ ": request_mem_region failed\n"); + rv = -EBUSY; + goto err_rmr; + } + + irq = irq_of_parse_and_map(dn, 0); + if (irq == NO_IRQ) { + printk(KERN_ERR __FILE__ ": irq_of_parse_and_map failed\n"); + rv = -EBUSY; + goto err_irq; + } + + hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); + if (!hcd->regs) { + printk(KERN_ERR __FILE__ ": ioremap failed\n"); + rv = -ENOMEM; + goto err_ioremap; + } + + ehci = hcd_to_ehci(hcd); + + /* This core always has big-endian register interface and uses + * big-endian memory descriptors. + */ + ehci->big_endian_mmio = 1; + ehci->big_endian_desc = 1; + + /* Check whether the FS support option is selected in the hardware. + */ + value = (int *)of_get_property(dn, "xlnx,support-usb-fs", NULL); + if (value && (*value == 1)) { + ehci_dbg(ehci, "USB host controller supports FS devices\n"); + hcd->has_tt = 1; + } else { + ehci_dbg(ehci, + "USB host controller is HS only\n"); + hcd->has_tt = 0; + } + + /* Debug registers are at the first 0x100 region + */ + ehci->caps = hcd->regs + 0x100; + ehci->regs = hcd->regs + 0x100 + + HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase)); + + /* cache this readonly data; minimize chip reads */ + ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); + + rv = usb_add_hcd(hcd, irq, 0); + if (rv == 0) + return 0; + + iounmap(hcd->regs); + +err_ioremap: +err_irq: + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); +err_rmr: + usb_put_hcd(hcd); + + return rv; +} + +/** + * ehci_hcd_xilinx_of_remove - shutdown hcd and release resources + * @op: pointer to of_device structure that is to be removed + * + * Remove the hcd structure, and release resources that has been requested + * during probe. + */ +static int ehci_hcd_xilinx_of_remove(struct of_device *op) +{ + struct usb_hcd *hcd = dev_get_drvdata(&op->dev); + dev_set_drvdata(&op->dev, NULL); + + dev_dbg(&op->dev, "stopping XILINX-OF USB Controller\n"); + + usb_remove_hcd(hcd); + + iounmap(hcd->regs); + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); + + usb_put_hcd(hcd); + + return 0; +} + +/** + * ehci_hcd_xilinx_of_shutdown - shutdown the hcd + * @op: pointer to of_device structure that is to be removed + * + * Properly shutdown the hcd, call driver's shutdown routine. + */ +static int ehci_hcd_xilinx_of_shutdown(struct of_device *op) +{ + struct usb_hcd *hcd = dev_get_drvdata(&op->dev); + + if (hcd->driver->shutdown) + hcd->driver->shutdown(hcd); + + return 0; +} + + +static struct of_device_id ehci_hcd_xilinx_of_match[] = { + {.compatible = "xlnx,xps-usb-host-1.00.a",}, + {}, +}; +MODULE_DEVICE_TABLE(of, ehci_hcd_xilinx_of_match); + +static struct of_platform_driver ehci_hcd_xilinx_of_driver = { + .name = "xilinx-of-ehci", + .match_table = ehci_hcd_xilinx_of_match, + .probe = ehci_hcd_xilinx_of_probe, + .remove = ehci_hcd_xilinx_of_remove, + .shutdown = ehci_hcd_xilinx_of_shutdown, + .driver = { + .name = "xilinx-of-ehci", + .owner = THIS_MODULE, + }, +}; diff -Naur -X linux-2.6.32-rc5-tty/Documentation/dontdiff linux-2.6.32-rc5-tty/drivers/usb/host/Kconfig linux-2.6.32-rc5-usb/drivers/usb/host/Kconfig --- linux-2.6.32-rc5-tty/drivers/usb/host/Kconfig 2009-10-15 20:35:57.000000000 -0700 +++ linux-2.6.32-rc5-usb/drivers/usb/host/Kconfig 2009-10-30 12:27:50.000000000 -0700 @@ -90,14 +90,25 @@ config USB_EHCI_BIG_ENDIAN_MMIO bool - depends on USB_EHCI_HCD && (PPC_CELLEB || PPC_PS3 || 440EPX || ARCH_IXP4XX) + depends on USB_EHCI_HCD && (PPC_CELLEB || PPC_PS3 || 440EPX || ARCH_IXP4XX || XPS_USB_HCD_XILINX) default y config USB_EHCI_BIG_ENDIAN_DESC bool - depends on USB_EHCI_HCD && (440EPX || ARCH_IXP4XX) + depends on USB_EHCI_HCD && (440EPX || ARCH_IXP4XX || XPS_USB_HCD_XILINX) default y +config XPS_USB_HCD_XILINX + bool "Use Xilinx usb host EHCI controller core" + depends on USB_EHCI_HCD && (PPC32 || MICROBLAZE) + select USB_EHCI_BIG_ENDIAN_DESC + select USB_EHCI_BIG_ENDIAN_MMIO + ---help--- + Xilinx xps USB host controller core is EHCI compilant and has + transaction translator built-in. It can be configured to either + support both high speed and full speed devices, or high speed + devices only. + config USB_EHCI_FSL bool "Support for Freescale on-chip EHCI USB controller" depends on USB_EHCI_HCD && FSL_SOC @@ -105,6 +116,13 @@ ---help--- Variation of ARC USB block used in some Freescale chips. +config USB_EHCI_MXC + bool "Support for Freescale on-chip EHCI USB controller" + depends on USB_EHCI_HCD && ARCH_MXC + select USB_EHCI_ROOT_HUB_TT + ---help--- + Variation of ARC USB block used in some Freescale chips. + config USB_EHCI_HCD_PPC_OF bool "EHCI support for PPC USB controller on OF platform bus" depends on USB_EHCI_HCD && PPC_OF diff -Naur -X linux-2.6.32-rc5-tty/Documentation/dontdiff linux-2.6.32-rc5-tty/drivers/usb/host/ohci-at91.c linux-2.6.32-rc5-usb/drivers/usb/host/ohci-at91.c --- linux-2.6.32-rc5-tty/drivers/usb/host/ohci-at91.c 2009-10-15 20:35:57.000000000 -0700 +++ linux-2.6.32-rc5-usb/drivers/usb/host/ohci-at91.c 2009-10-30 12:27:49.000000000 -0700 @@ -35,7 +35,7 @@ static void at91_start_clock(void) { - if (cpu_is_at91sam9261()) + if (cpu_is_at91sam9261() || cpu_is_at91sam9g10()) clk_enable(hclk); clk_enable(iclk); clk_enable(fclk); @@ -46,7 +46,7 @@ { clk_disable(fclk); clk_disable(iclk); - if (cpu_is_at91sam9261()) + if (cpu_is_at91sam9261() || cpu_is_at91sam9g10()) clk_disable(hclk); clocked = 0; } @@ -142,7 +142,7 @@ iclk = clk_get(&pdev->dev, "ohci_clk"); fclk = clk_get(&pdev->dev, "uhpck"); - if (cpu_is_at91sam9261()) + if (cpu_is_at91sam9261() || cpu_is_at91sam9g10()) hclk = clk_get(&pdev->dev, "hck0"); at91_start_hc(pdev); @@ -155,7 +155,7 @@ /* Error handling */ at91_stop_hc(pdev); - if (cpu_is_at91sam9261()) + if (cpu_is_at91sam9261() || cpu_is_at91sam9g10()) clk_put(hclk); clk_put(fclk); clk_put(iclk); @@ -192,7 +192,7 @@ release_mem_region(hcd->rsrc_start, hcd->rsrc_len); usb_put_hcd(hcd); - if (cpu_is_at91sam9261()) + if (cpu_is_at91sam9261() || cpu_is_at91sam9g10()) clk_put(hclk); clk_put(fclk); clk_put(iclk); diff -Naur -X linux-2.6.32-rc5-tty/Documentation/dontdiff linux-2.6.32-rc5-tty/drivers/usb/host/ohci-pnx4008.c linux-2.6.32-rc5-usb/drivers/usb/host/ohci-pnx4008.c --- linux-2.6.32-rc5-tty/drivers/usb/host/ohci-pnx4008.c 2009-03-23 16:12:14.000000000 -0700 +++ linux-2.6.32-rc5-usb/drivers/usb/host/ohci-pnx4008.c 2009-10-30 12:27:49.000000000 -0700 @@ -98,8 +98,8 @@ #define ISP1301_I2C_INTERRUPT_RISING 0xE #define ISP1301_I2C_REG_CLEAR_ADDR 1 -struct i2c_driver isp1301_driver; -struct i2c_client *isp1301_i2c_client; +static struct i2c_driver isp1301_driver; +static struct i2c_client *isp1301_i2c_client; extern int usb_disabled(void); extern int ocpi_enable(void); @@ -120,12 +120,12 @@ return 0; } -const struct i2c_device_id isp1301_id[] = { +static const struct i2c_device_id isp1301_id[] = { { "isp1301_pnx", 0 }, { } }; -struct i2c_driver isp1301_driver = { +static struct i2c_driver isp1301_driver = { .driver = { .name = "isp1301_pnx", }, diff -Naur -X linux-2.6.32-rc5-tty/Documentation/dontdiff linux-2.6.32-rc5-tty/drivers/usb/host/whci/debug.c linux-2.6.32-rc5-usb/drivers/usb/host/whci/debug.c --- linux-2.6.32-rc5-tty/drivers/usb/host/whci/debug.c 2009-10-15 20:35:57.000000000 -0700 +++ linux-2.6.32-rc5-usb/drivers/usb/host/whci/debug.c 2009-10-30 12:27:49.000000000 -0700 @@ -31,17 +31,29 @@ void qset_print(struct seq_file *s, struct whc_qset *qset) { + static const char *qh_type[] = { + "ctrl", "isoc", "bulk", "intr", "rsvd", "rsvd", "rsvd", "lpintr", }; struct whc_std *std; struct urb *urb = NULL; int i; - seq_printf(s, "qset %08x\n", (u32)qset->qset_dma); + seq_printf(s, "qset %08x", (u32)qset->qset_dma); + if (&qset->list_node == qset->whc->async_list.prev) { + seq_printf(s, " (dummy)\n"); + } else { + seq_printf(s, " ep%d%s-%s maxpkt: %d\n", + qset->qh.info1 & 0x0f, + (qset->qh.info1 >> 4) & 0x1 ? "in" : "out", + qh_type[(qset->qh.info1 >> 5) & 0x7], + (qset->qh.info1 >> 16) & 0xffff); + } seq_printf(s, " -> %08x\n", (u32)qset->qh.link); seq_printf(s, " info: %08x %08x %08x\n", - qset->qh.info1, qset->qh.info2, qset->qh.info3); - seq_printf(s, " sts: %04x errs: %d\n", qset->qh.status, qset->qh.err_count); + qset->qh.info1, qset->qh.info2, qset->qh.info3); + seq_printf(s, " sts: %04x errs: %d curwin: %08x\n", + qset->qh.status, qset->qh.err_count, qset->qh.cur_window); seq_printf(s, " TD: sts: %08x opts: %08x\n", - qset->qh.overlay.qtd.status, qset->qh.overlay.qtd.options); + qset->qh.overlay.qtd.status, qset->qh.overlay.qtd.options); for (i = 0; i < WHCI_QSET_TD_MAX; i++) { seq_printf(s, " %c%c TD[%d]: sts: %08x opts: %08x ptr: %08x\n", diff -Naur -X linux-2.6.32-rc5-tty/Documentation/dontdiff linux-2.6.32-rc5-tty/drivers/usb/host/whci/hcd.c linux-2.6.32-rc5-usb/drivers/usb/host/whci/hcd.c --- linux-2.6.32-rc5-tty/drivers/usb/host/whci/hcd.c 2009-10-15 20:35:57.000000000 -0700 +++ linux-2.6.32-rc5-usb/drivers/usb/host/whci/hcd.c 2009-10-30 12:27:49.000000000 -0700 @@ -250,6 +250,7 @@ } usb_hcd->wireless = 1; + usb_hcd->self.sg_tablesize = 2048; /* somewhat arbitrary */ wusbhc = usb_hcd_to_wusbhc(usb_hcd); whc = wusbhc_to_whc(wusbhc); diff -Naur -X linux-2.6.32-rc5-tty/Documentation/dontdiff linux-2.6.32-rc5-tty/drivers/usb/host/whci/qset.c linux-2.6.32-rc5-usb/drivers/usb/host/whci/qset.c --- linux-2.6.32-rc5-tty/drivers/usb/host/whci/qset.c 2009-10-15 20:35:57.000000000 -0700 +++ linux-2.6.32-rc5-usb/drivers/usb/host/whci/qset.c 2009-10-30 12:27:49.000000000 -0700 @@ -49,16 +49,19 @@ * state * @urb: an urb for a transfer to this endpoint */ -static void qset_fill_qh(struct whc_qset *qset, struct urb *urb) +static void qset_fill_qh(struct whc *whc, struct whc_qset *qset, struct urb *urb) { struct usb_device *usb_dev = urb->dev; + struct wusb_dev *wusb_dev = usb_dev->wusb_dev; struct usb_wireless_ep_comp_descriptor *epcd; bool is_out; + uint8_t phy_rate; is_out = usb_pipeout(urb->pipe); - epcd = (struct usb_wireless_ep_comp_descriptor *)qset->ep->extra; + qset->max_packet = le16_to_cpu(urb->ep->desc.wMaxPacketSize); + epcd = (struct usb_wireless_ep_comp_descriptor *)qset->ep->extra; if (epcd) { qset->max_seq = epcd->bMaxSequence; qset->max_burst = epcd->bMaxBurst; @@ -67,12 +70,28 @@ qset->max_burst = 1; } + /* + * Initial PHY rate is 53.3 Mbit/s for control endpoints or + * the maximum supported by the device for other endpoints + * (unless limited by the user). + */ + if (usb_pipecontrol(urb->pipe)) + phy_rate = UWB_PHY_RATE_53; + else { + uint16_t phy_rates; + + phy_rates = le16_to_cpu(wusb_dev->wusb_cap_descr->wPHYRates); + phy_rate = fls(phy_rates) - 1; + if (phy_rate > whc->wusbhc.phy_rate) + phy_rate = whc->wusbhc.phy_rate; + } + qset->qh.info1 = cpu_to_le32( QH_INFO1_EP(usb_pipeendpoint(urb->pipe)) | (is_out ? QH_INFO1_DIR_OUT : QH_INFO1_DIR_IN) | usb_pipe_to_qh_type(urb->pipe) | QH_INFO1_DEV_INFO_IDX(wusb_port_no_to_idx(usb_dev->portnum)) - | QH_INFO1_MAX_PKT_LEN(usb_maxpacket(urb->dev, urb->pipe, is_out)) + | QH_INFO1_MAX_PKT_LEN(qset->max_packet) ); qset->qh.info2 = cpu_to_le32( QH_INFO2_BURST(qset->max_burst) @@ -86,7 +105,7 @@ * strength and can presumably guess the Tx power required * from that? */ qset->qh.info3 = cpu_to_le32( - QH_INFO3_TX_RATE_53_3 + QH_INFO3_TX_RATE(phy_rate) | QH_INFO3_TX_PWR(0) /* 0 == max power */ ); @@ -148,7 +167,7 @@ qset->ep = urb->ep; urb->ep->hcpriv = qset; - qset_fill_qh(qset, urb); + qset_fill_qh(whc, qset, urb); } return qset; } @@ -241,6 +260,36 @@ qset->ntds--; } +static void qset_copy_bounce_to_sg(struct whc *whc, struct whc_std *std) +{ + struct scatterlist *sg; + void *bounce; + size_t remaining, offset; + + bounce = std->bounce_buf; + remaining = std->len; + + sg = std->bounce_sg; + offset = std->bounce_offset; + + while (remaining) { + size_t len; + + len = min(sg->length - offset, remaining); + memcpy(sg_virt(sg) + offset, bounce, len); + + bounce += len; + remaining -= len; + + offset += len; + if (offset >= sg->length) { + sg = sg_next(sg); + offset = 0; + } + } + +} + /** * qset_free_std - remove an sTD and free it. * @whc: the WHCI host controller @@ -249,13 +298,29 @@ void qset_free_std(struct whc *whc, struct whc_std *std) { list_del(&std->list_node); - if (std->num_pointers) { - dma_unmap_single(whc->wusbhc.dev, std->dma_addr, - std->num_pointers * sizeof(struct whc_page_list_entry), - DMA_TO_DEVICE); + if (std->bounce_buf) { + bool is_out = usb_pipeout(std->urb->pipe); + dma_addr_t dma_addr; + + if (std->num_pointers) + dma_addr = le64_to_cpu(std->pl_virt[0].buf_ptr); + else + dma_addr = std->dma_addr; + + dma_unmap_single(whc->wusbhc.dev, dma_addr, + std->len, is_out ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + if (!is_out) + qset_copy_bounce_to_sg(whc, std); + kfree(std->bounce_buf); + } + if (std->pl_virt) { + if (std->dma_addr) + dma_unmap_single(whc->wusbhc.dev, std->dma_addr, + std->num_pointers * sizeof(struct whc_page_list_entry), + DMA_TO_DEVICE); kfree(std->pl_virt); + std->pl_virt = NULL; } - kfree(std); } @@ -293,12 +358,17 @@ { dma_addr_t dma_addr = std->dma_addr; dma_addr_t sp, ep; - size_t std_len = std->len; size_t pl_len; int p; - sp = ALIGN(dma_addr, WHCI_PAGE_SIZE); - ep = dma_addr + std_len; + /* Short buffers don't need a page list. */ + if (std->len <= WHCI_PAGE_SIZE) { + std->num_pointers = 0; + return 0; + } + + sp = dma_addr & ~(WHCI_PAGE_SIZE-1); + ep = dma_addr + std->len; std->num_pointers = DIV_ROUND_UP(ep - sp, WHCI_PAGE_SIZE); pl_len = std->num_pointers * sizeof(struct whc_page_list_entry); @@ -309,7 +379,7 @@ for (p = 0; p < std->num_pointers; p++) { std->pl_virt[p].buf_ptr = cpu_to_le64(dma_addr); - dma_addr = ALIGN(dma_addr + WHCI_PAGE_SIZE, WHCI_PAGE_SIZE); + dma_addr = (dma_addr + WHCI_PAGE_SIZE) & ~(WHCI_PAGE_SIZE-1); } return 0; @@ -339,6 +409,218 @@ spin_unlock_irqrestore(&whc->lock, flags); } +static struct whc_std *qset_new_std(struct whc *whc, struct whc_qset *qset, + struct urb *urb, gfp_t mem_flags) +{ + struct whc_std *std; + + std = kzalloc(sizeof(struct whc_std), mem_flags); + if (std == NULL) + return NULL; + + std->urb = urb; + std->qtd = NULL; + + INIT_LIST_HEAD(&std->list_node); + list_add_tail(&std->list_node, &qset->stds); + + return std; +} + +static int qset_add_urb_sg(struct whc *whc, struct whc_qset *qset, struct urb *urb, + gfp_t mem_flags) +{ + size_t remaining; + struct scatterlist *sg; + int i; + int ntds = 0; + struct whc_std *std = NULL; + struct whc_page_list_entry *entry; + dma_addr_t prev_end = 0; + size_t pl_len; + int p = 0; + + remaining = urb->transfer_buffer_length; + + for_each_sg(urb->sg->sg, sg, urb->num_sgs, i) { + dma_addr_t dma_addr; + size_t dma_remaining; + dma_addr_t sp, ep; + int num_pointers; + + if (remaining == 0) { + break; + } + + dma_addr = sg_dma_address(sg); + dma_remaining = min_t(size_t, sg_dma_len(sg), remaining); + + while (dma_remaining) { + size_t dma_len; + + /* + * We can use the previous std (if it exists) provided that: + * - the previous one ended on a page boundary. + * - the current one begins on a page boundary. + * - the previous one isn't full. + * + * If a new std is needed but the previous one + * did not end on a wMaxPacketSize boundary + * then this sg list cannot be mapped onto + * multiple qTDs. Return an error and let the + * caller sort it out. + */ + if (!std + || (prev_end & (WHCI_PAGE_SIZE-1)) + || (dma_addr & (WHCI_PAGE_SIZE-1)) + || std->len + WHCI_PAGE_SIZE > QTD_MAX_XFER_SIZE) { + if (prev_end % qset->max_packet != 0) + return -EINVAL; + std = qset_new_std(whc, qset, urb, mem_flags); + if (std == NULL) { + return -ENOMEM; + } + ntds++; + p = 0; + } + + dma_len = dma_remaining; + + /* + * If the remainder in this element doesn't + * fit in a single qTD, end the qTD on a + * wMaxPacketSize boundary. + */ + if (std->len + dma_len > QTD_MAX_XFER_SIZE) { + dma_len = QTD_MAX_XFER_SIZE - std->len; + ep = ((dma_addr + dma_len) / qset->max_packet) * qset->max_packet; + dma_len = ep - dma_addr; + } + + std->len += dma_len; + std->ntds_remaining = -1; /* filled in later */ + + sp = dma_addr & ~(WHCI_PAGE_SIZE-1); + ep = dma_addr + dma_len; + num_pointers = DIV_ROUND_UP(ep - sp, WHCI_PAGE_SIZE); + std->num_pointers += num_pointers; + + pl_len = std->num_pointers * sizeof(struct whc_page_list_entry); + + std->pl_virt = krealloc(std->pl_virt, pl_len, mem_flags); + if (std->pl_virt == NULL) { + return -ENOMEM; + } + + for (;p < std->num_pointers; p++, entry++) { + std->pl_virt[p].buf_ptr = cpu_to_le64(dma_addr); + dma_addr = (dma_addr + WHCI_PAGE_SIZE) & ~(WHCI_PAGE_SIZE-1); + } + + prev_end = dma_addr = ep; + dma_remaining -= dma_len; + remaining -= dma_len; + } + } + + /* Now the number of stds is know, go back and fill in + std->ntds_remaining. */ + list_for_each_entry(std, &qset->stds, list_node) { + if (std->ntds_remaining == -1) { + pl_len = std->num_pointers * sizeof(struct whc_page_list_entry); + std->ntds_remaining = ntds--; + std->dma_addr = dma_map_single(whc->wusbhc.dev, std->pl_virt, + pl_len, DMA_TO_DEVICE); + } + } + return 0; +} + +/** + * qset_add_urb_sg_linearize - add an urb with sg list, copying the data + * + * If the URB contains an sg list whose elements cannot be directly + * mapped to qTDs then the data must be transferred via bounce + * buffers. + */ +static int qset_add_urb_sg_linearize(struct whc *whc, struct whc_qset *qset, + struct urb *urb, gfp_t mem_flags) +{ + bool is_out = usb_pipeout(urb->pipe); + size_t max_std_len; + size_t remaining; + int ntds = 0; + struct whc_std *std = NULL; + void *bounce = NULL; + struct scatterlist *sg; + int i; + + /* limit maximum bounce buffer to 16 * 3.5 KiB ~= 28 k */ + max_std_len = qset->max_burst * qset->max_packet; + + remaining = urb->transfer_buffer_length; + + for_each_sg(urb->sg->sg, sg, urb->sg->nents, i) { + size_t len; + size_t sg_remaining; + void *orig; + + if (remaining == 0) { + break; + } + + sg_remaining = min_t(size_t, remaining, sg->length); + orig = sg_virt(sg); + + while (sg_remaining) { + if (!std || std->len == max_std_len) { + std = qset_new_std(whc, qset, urb, mem_flags); + if (std == NULL) + return -ENOMEM; + std->bounce_buf = kmalloc(max_std_len, mem_flags); + if (std->bounce_buf == NULL) + return -ENOMEM; + std->bounce_sg = sg; + std->bounce_offset = orig - sg_virt(sg); + bounce = std->bounce_buf; + ntds++; + } + + len = min(sg_remaining, max_std_len - std->len); + + if (is_out) + memcpy(bounce, orig, len); + + std->len += len; + std->ntds_remaining = -1; /* filled in later */ + + bounce += len; + orig += len; + sg_remaining -= len; + remaining -= len; + } + } + + /* + * For each of the new sTDs, map the bounce buffers, create + * page lists (if necessary), and fill in std->ntds_remaining. + */ + list_for_each_entry(std, &qset->stds, list_node) { + if (std->ntds_remaining != -1) + continue; + + std->dma_addr = dma_map_single(&whc->umc->dev, std->bounce_buf, std->len, + is_out ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + + if (qset_fill_page_list(whc, std, mem_flags) < 0) + return -ENOMEM; + + std->ntds_remaining = ntds--; + } + + return 0; +} + /** * qset_add_urb - add an urb to the qset's queue. * @@ -353,10 +635,7 @@ int remaining = urb->transfer_buffer_length; u64 transfer_dma = urb->transfer_dma; int ntds_remaining; - - ntds_remaining = DIV_ROUND_UP(remaining, QTD_MAX_XFER_SIZE); - if (ntds_remaining == 0) - ntds_remaining = 1; + int ret; wurb = kzalloc(sizeof(struct whc_urb), mem_flags); if (wurb == NULL) @@ -366,32 +645,39 @@ wurb->urb = urb; INIT_WORK(&wurb->dequeue_work, urb_dequeue_work); + if (urb->sg) { + ret = qset_add_urb_sg(whc, qset, urb, mem_flags); + if (ret == -EINVAL) { + qset_free_stds(qset, urb); + ret = qset_add_urb_sg_linearize(whc, qset, urb, mem_flags); + } + if (ret < 0) + goto err_no_mem; + return 0; + } + + ntds_remaining = DIV_ROUND_UP(remaining, QTD_MAX_XFER_SIZE); + if (ntds_remaining == 0) + ntds_remaining = 1; + while (ntds_remaining) { struct whc_std *std; size_t std_len; - std = kmalloc(sizeof(struct whc_std), mem_flags); - if (std == NULL) - goto err_no_mem; - std_len = remaining; if (std_len > QTD_MAX_XFER_SIZE) std_len = QTD_MAX_XFER_SIZE; - std->urb = urb; + std = qset_new_std(whc, qset, urb, mem_flags); + if (std == NULL) + goto err_no_mem; + std->dma_addr = transfer_dma; std->len = std_len; std->ntds_remaining = ntds_remaining; - std->qtd = NULL; - INIT_LIST_HEAD(&std->list_node); - list_add_tail(&std->list_node, &qset->stds); - - if (std_len > WHCI_PAGE_SIZE) { - if (qset_fill_page_list(whc, std, mem_flags) < 0) - goto err_no_mem; - } else - std->num_pointers = 0; + if (qset_fill_page_list(whc, std, mem_flags) < 0) + goto err_no_mem; ntds_remaining--; remaining -= std_len; diff -Naur -X linux-2.6.32-rc5-tty/Documentation/dontdiff linux-2.6.32-rc5-tty/drivers/usb/host/whci/whcd.h linux-2.6.32-rc5-usb/drivers/usb/host/whci/whcd.h --- linux-2.6.32-rc5-tty/drivers/usb/host/whci/whcd.h 2009-10-05 16:43:58.000000000 -0700 +++ linux-2.6.32-rc5-usb/drivers/usb/host/whci/whcd.h 2009-10-30 12:27:49.000000000 -0700 @@ -84,6 +84,11 @@ * @len: the length of data in the associated TD. * @ntds_remaining: number of TDs (starting from this one) in this transfer. * + * @bounce_buf: a bounce buffer if the std was from an urb with a sg + * list that could not be mapped to qTDs directly. + * @bounce_sg: the first scatterlist element bounce_buf is for. + * @bounce_offset: the offset into bounce_sg for the start of bounce_buf. + * * Queued URBs may require more TDs than are available in a qset so we * use a list of these "software TDs" (sTDs) to hold per-TD data. */ @@ -97,6 +102,10 @@ int num_pointers; dma_addr_t dma_addr; struct whc_page_list_entry *pl_virt; + + void *bounce_buf; + struct scatterlist *bounce_sg; + unsigned bounce_offset; }; /** diff -Naur -X linux-2.6.32-rc5-tty/Documentation/dontdiff linux-2.6.32-rc5-tty/drivers/usb/host/whci/whci-hc.h linux-2.6.32-rc5-usb/drivers/usb/host/whci/whci-hc.h --- linux-2.6.32-rc5-tty/drivers/usb/host/whci/whci-hc.h 2009-10-15 20:35:57.000000000 -0700 +++ linux-2.6.32-rc5-usb/drivers/usb/host/whci/whci-hc.h 2009-10-30 12:27:49.000000000 -0700 @@ -172,14 +172,7 @@ #define QH_INFO3_MAX_DELAY(d) ((d) << 0) /* maximum stream delay in 125 us units (isoc only) */ #define QH_INFO3_INTERVAL(i) ((i) << 16) /* segment interval in 125 us units (isoc only) */ -#define QH_INFO3_TX_RATE_53_3 (0 << 24) -#define QH_INFO3_TX_RATE_80 (1 << 24) -#define QH_INFO3_TX_RATE_106_7 (2 << 24) -#define QH_INFO3_TX_RATE_160 (3 << 24) -#define QH_INFO3_TX_RATE_200 (4 << 24) -#define QH_INFO3_TX_RATE_320 (5 << 24) -#define QH_INFO3_TX_RATE_400 (6 << 24) -#define QH_INFO3_TX_RATE_480 (7 << 24) +#define QH_INFO3_TX_RATE(r) ((r) << 24) /* PHY rate (see [ECMA-368] section 10.3.1.1) */ #define QH_INFO3_TX_PWR(p) ((p) << 29) /* transmit power (see [WUSB] section 5.2.1.2) */ #define QH_STATUS_FLOW_CTRL (1 << 15) @@ -267,8 +260,9 @@ unsigned reset:1; struct urb *pause_after_urb; struct completion remove_complete; - int max_burst; - int max_seq; + uint16_t max_packet; + uint8_t max_burst; + uint8_t max_seq; }; static inline void whc_qset_set_link_ptr(u64 *ptr, u64 target) diff -Naur -X linux-2.6.32-rc5-tty/Documentation/dontdiff linux-2.6.32-rc5-tty/drivers/usb/host/xhci.h linux-2.6.32-rc5-usb/drivers/usb/host/xhci.h --- linux-2.6.32-rc5-tty/drivers/usb/host/xhci.h 2009-10-15 20:35:57.000000000 -0700 +++ linux-2.6.32-rc5-usb/drivers/usb/host/xhci.h 2009-10-30 12:27:50.000000000 -0700 @@ -652,13 +652,17 @@ struct xhci_ring *new_ring; unsigned int ep_state; #define SET_DEQ_PENDING (1 << 0) -#define EP_HALTED (1 << 1) +#define EP_HALTED (1 << 1) /* For stall handling */ +#define EP_HALT_PENDING (1 << 2) /* For URB cancellation */ /* ---- Related to URB cancellation ---- */ struct list_head cancelled_td_list; - unsigned int cancels_pending; /* The TRB that was last reported in a stopped endpoint ring */ union xhci_trb *stopped_trb; struct xhci_td *stopped_td; + /* Watchdog timer for stop endpoint command to cancel URBs */ + struct timer_list stop_cmd_timer; + int stop_cmds_pending; + struct xhci_hcd *xhci; }; struct xhci_virt_device { @@ -1022,6 +1026,8 @@ #define ERST_ENTRIES 1 /* Poll every 60 seconds */ #define POLL_TIMEOUT 60 +/* Stop endpoint command timeout (secs) for URB cancellation watchdog timer */ +#define XHCI_STOP_EP_CMD_TIMEOUT 5 /* XXX: Make these module parameters */ @@ -1083,6 +1089,21 @@ struct timer_list event_ring_timer; int zombie; #endif + /* Host controller watchdog timer structures */ + unsigned int xhc_state; +/* Host controller is dying - not responding to commands. "I'm not dead yet!" + * + * xHC interrupts have been disabled and a watchdog timer will (or has already) + * halt the xHCI host, and complete all URBs with an -ESHUTDOWN code. Any code + * that sees this status (other than the timer that set it) should stop touching + * hardware immediately. Interrupt handlers should return immediately when + * they see this status (any time they drop and re-acquire xhci->lock). + * xhci_urb_dequeue() should call usb_hcd_check_unlink_urb() and return without + * putting the TD on the canceled list, etc. + * + * There are no reports of xHCI host controllers that display this issue. + */ +#define XHCI_STATE_DYING (1 << 0) /* Statistics */ int noops_submitted; int noops_handled; @@ -1223,6 +1244,7 @@ #endif /* xHCI host controller glue */ +void xhci_quiesce(struct xhci_hcd *xhci); int xhci_halt(struct xhci_hcd *xhci); int xhci_reset(struct xhci_hcd *xhci); int xhci_init(struct usb_hcd *hcd); @@ -1278,6 +1300,7 @@ void xhci_queue_config_ep_quirk(struct xhci_hcd *xhci, unsigned int slot_id, unsigned int ep_index, struct xhci_dequeue_state *deq_state); +void xhci_stop_endpoint_command_watchdog(unsigned long arg); /* xHCI roothub code */ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex, diff -Naur -X linux-2.6.32-rc5-tty/Documentation/dontdiff linux-2.6.32-rc5-tty/drivers/usb/host/xhci-hcd.c linux-2.6.32-rc5-usb/drivers/usb/host/xhci-hcd.c --- linux-2.6.32-rc5-tty/drivers/usb/host/xhci-hcd.c 2009-10-15 20:35:57.000000000 -0700 +++ linux-2.6.32-rc5-usb/drivers/usb/host/xhci-hcd.c 2009-10-30 12:27:50.000000000 -0700 @@ -67,22 +67,14 @@ } /* - * Force HC into halt state. - * - * Disable any IRQs and clear the run/stop bit. - * HC will complete any current and actively pipelined transactions, and - * should halt within 16 microframes of the run/stop bit being cleared. - * Read HC Halted bit in the status register to see when the HC is finished. - * XXX: shouldn't we set HC_STATE_HALT here somewhere? + * Disable interrupts and begin the xHCI halting process. */ -int xhci_halt(struct xhci_hcd *xhci) +void xhci_quiesce(struct xhci_hcd *xhci) { u32 halted; u32 cmd; u32 mask; - xhci_dbg(xhci, "// Halt the HC\n"); - /* Disable all interrupts from the host controller */ mask = ~(XHCI_IRQS); halted = xhci_readl(xhci, &xhci->op_regs->status) & STS_HALT; if (!halted) @@ -91,6 +83,21 @@ cmd = xhci_readl(xhci, &xhci->op_regs->command); cmd &= mask; xhci_writel(xhci, cmd, &xhci->op_regs->command); +} + +/* + * Force HC into halt state. + * + * Disable any IRQs and clear the run/stop bit. + * HC will complete any current and actively pipelined transactions, and + * should halt within 16 microframes of the run/stop bit being cleared. + * Read HC Halted bit in the status register to see when the HC is finished. + * XXX: shouldn't we set HC_STATE_HALT here somewhere? + */ +int xhci_halt(struct xhci_hcd *xhci) +{ + xhci_dbg(xhci, "// Halt the HC\n"); + xhci_quiesce(xhci); return handshake(xhci, &xhci->op_regs->status, STS_HALT, STS_HALT, XHCI_MAX_HALT_USEC); @@ -124,28 +131,6 @@ return handshake(xhci, &xhci->op_regs->command, CMD_RESET, 0, 250 * 1000); } -/* - * Stop the HC from processing the endpoint queues. - */ -static void xhci_quiesce(struct xhci_hcd *xhci) -{ - /* - * Queues are per endpoint, so we need to disable an endpoint or slot. - * - * To disable a slot, we need to insert a disable slot command on the - * command ring and ring the doorbell. This will also free any internal - * resources associated with the slot (which might not be what we want). - * - * A Release Endpoint command sounds better - doesn't free internal HC - * memory, but removes the endpoints from the schedule and releases the - * bandwidth, disables the doorbells, and clears the endpoint enable - * flag. Usually used prior to a set interface command. - * - * TODO: Implement after command ring code is done. - */ - BUG_ON(!HC_IS_RUNNING(xhci_to_hcd(xhci)->state)); - xhci_dbg(xhci, "Finished quiescing -- code not written yet\n"); -} #if 0 /* Set up MSI-X table for entry 0 (may claim other entries later) */ @@ -261,8 +246,14 @@ /* Flush posted writes */ xhci_readl(xhci, &xhci->ir_set->irq_pending); - /* FIXME this should be a delayed service routine that clears the EHB */ - xhci_handle_event(xhci); + if (xhci->xhc_state & XHCI_STATE_DYING) + xhci_dbg(xhci, "xHCI dying, ignoring interrupt. " + "Shouldn't IRQs be disabled?\n"); + else + /* FIXME this should be a delayed service routine + * that clears the EHB. + */ + xhci_handle_event(xhci); /* Clear the event handler busy flag (RW1C); the event ring should be empty. */ temp_64 = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue); @@ -335,7 +326,7 @@ spin_lock_irqsave(&xhci->lock, flags); temp = xhci_readl(xhci, &xhci->op_regs->status); xhci_dbg(xhci, "op reg status = 0x%x\n", temp); - if (temp == 0xffffffff) { + if (temp == 0xffffffff || (xhci->xhc_state & XHCI_STATE_DYING)) { xhci_dbg(xhci, "HW died, polling stopped.\n"); spin_unlock_irqrestore(&xhci->lock, flags); return; @@ -490,8 +481,6 @@ struct xhci_hcd *xhci = hcd_to_xhci(hcd); spin_lock_irq(&xhci->lock); - if (HC_IS_RUNNING(hcd->state)) - xhci_quiesce(xhci); xhci_halt(xhci); xhci_reset(xhci); spin_unlock_irq(&xhci->lock); @@ -727,16 +716,22 @@ * atomic context to this function, which may allocate memory. */ spin_lock_irqsave(&xhci->lock, flags); + if (xhci->xhc_state & XHCI_STATE_DYING) + goto dying; ret = xhci_queue_ctrl_tx(xhci, GFP_ATOMIC, urb, slot_id, ep_index); spin_unlock_irqrestore(&xhci->lock, flags); } else if (usb_endpoint_xfer_bulk(&urb->ep->desc)) { spin_lock_irqsave(&xhci->lock, flags); + if (xhci->xhc_state & XHCI_STATE_DYING) + goto dying; ret = xhci_queue_bulk_tx(xhci, GFP_ATOMIC, urb, slot_id, ep_index); spin_unlock_irqrestore(&xhci->lock, flags); } else if (usb_endpoint_xfer_int(&urb->ep->desc)) { spin_lock_irqsave(&xhci->lock, flags); + if (xhci->xhc_state & XHCI_STATE_DYING) + goto dying; ret = xhci_queue_intr_tx(xhci, GFP_ATOMIC, urb, slot_id, ep_index); spin_unlock_irqrestore(&xhci->lock, flags); @@ -745,6 +740,12 @@ } exit: return ret; +dying: + xhci_dbg(xhci, "Ep 0x%x: URB %p submitted for " + "non-responsive xHCI host.\n", + urb->ep->desc.bEndpointAddress, urb); + spin_unlock_irqrestore(&xhci->lock, flags); + return -ESHUTDOWN; } /* @@ -806,6 +807,17 @@ kfree(td); return ret; } + if (xhci->xhc_state & XHCI_STATE_DYING) { + xhci_dbg(xhci, "Ep 0x%x: URB %p to be canceled on " + "non-responsive xHCI host.\n", + urb->ep->desc.bEndpointAddress, urb); + /* Let the stop endpoint command watchdog timer (which set this + * state) finish cleaning up the endpoint TD lists. We must + * have caught it in the middle of dropping a lock and giving + * back an URB. + */ + goto done; + } xhci_dbg(xhci, "Cancel URB %p\n", urb); xhci_dbg(xhci, "Event ring:\n"); @@ -817,12 +829,16 @@ xhci_debug_ring(xhci, ep_ring); td = (struct xhci_td *) urb->hcpriv; - ep->cancels_pending++; list_add_tail(&td->cancelled_td_list, &ep->cancelled_td_list); /* Queue a stop endpoint command, but only if this is * the first cancellation to be handled. */ - if (ep->cancels_pending == 1) { + if (!(ep->ep_state & EP_HALT_PENDING)) { + ep->ep_state |= EP_HALT_PENDING; + ep->stop_cmds_pending++; + ep->stop_cmd_timer.expires = jiffies + + XHCI_STOP_EP_CMD_TIMEOUT * HZ; + add_timer(&ep->stop_cmd_timer); xhci_queue_stop_endpoint(xhci, urb->dev->slot_id, ep_index); xhci_ring_cmd_db(xhci); } @@ -1427,16 +1443,27 @@ void xhci_free_dev(struct usb_hcd *hcd, struct usb_device *udev) { struct xhci_hcd *xhci = hcd_to_xhci(hcd); + struct xhci_virt_device *virt_dev; unsigned long flags; u32 state; + int i; if (udev->slot_id == 0) return; + virt_dev = xhci->devs[udev->slot_id]; + if (!virt_dev) + return; + + /* Stop any wayward timer functions (which may grab the lock) */ + for (i = 0; i < 31; ++i) { + virt_dev->eps[i].ep_state &= ~EP_HALT_PENDING; + del_timer_sync(&virt_dev->eps[i].stop_cmd_timer); + } spin_lock_irqsave(&xhci->lock, flags); /* Don't disable the slot if the host controller is dead. */ state = xhci_readl(xhci, &xhci->op_regs->status); - if (state == 0xffffffff) { + if (state == 0xffffffff || (xhci->xhc_state & XHCI_STATE_DYING)) { xhci_free_virt_device(xhci, udev->slot_id); spin_unlock_irqrestore(&xhci->lock, flags); return; diff -Naur -X linux-2.6.32-rc5-tty/Documentation/dontdiff linux-2.6.32-rc5-tty/drivers/usb/host/xhci-mem.c linux-2.6.32-rc5-usb/drivers/usb/host/xhci-mem.c --- linux-2.6.32-rc5-tty/drivers/usb/host/xhci-mem.c 2009-10-15 20:35:57.000000000 -0700 +++ linux-2.6.32-rc5-usb/drivers/usb/host/xhci-mem.c 2009-10-30 12:27:50.000000000 -0700 @@ -248,6 +248,15 @@ (ctx->bytes + (ep_index * CTX_SIZE(xhci->hcc_params))); } +static void xhci_init_endpoint_timer(struct xhci_hcd *xhci, + struct xhci_virt_ep *ep) +{ + init_timer(&ep->stop_cmd_timer); + ep->stop_cmd_timer.data = (unsigned long) ep; + ep->stop_cmd_timer.function = xhci_stop_endpoint_command_watchdog; + ep->xhci = xhci; +} + /* All the xhci_tds in the ring's TD list should be freed at this point */ void xhci_free_virt_device(struct xhci_hcd *xhci, int slot_id) { @@ -309,9 +318,11 @@ xhci_dbg(xhci, "Slot %d input ctx = 0x%llx (dma)\n", slot_id, (unsigned long long)dev->in_ctx->dma); - /* Initialize the cancellation list for each endpoint */ - for (i = 0; i < 31; i++) + /* Initialize the cancellation list and watchdog timers for each ep */ + for (i = 0; i < 31; i++) { + xhci_init_endpoint_timer(xhci, &dev->eps[i]); INIT_LIST_HEAD(&dev->eps[i].cancelled_td_list); + } /* Allocate endpoint 0 ring */ dev->eps[0].ring = xhci_ring_alloc(xhci, 1, true, flags); diff -Naur -X linux-2.6.32-rc5-tty/Documentation/dontdiff linux-2.6.32-rc5-tty/drivers/usb/host/xhci-pci.c linux-2.6.32-rc5-usb/drivers/usb/host/xhci-pci.c --- linux-2.6.32-rc5-tty/drivers/usb/host/xhci-pci.c 2009-10-15 20:35:57.000000000 -0700 +++ linux-2.6.32-rc5-usb/drivers/usb/host/xhci-pci.c 2009-10-30 12:27:49.000000000 -0700 @@ -54,6 +54,8 @@ struct pci_dev *pdev = to_pci_dev(hcd->self.controller); int retval; + hcd->self.sg_tablesize = TRBS_PER_SEGMENT - 1; + xhci->cap_regs = hcd->regs; xhci->op_regs = hcd->regs + HC_LENGTH(xhci_readl(xhci, &xhci->cap_regs->hc_capbase)); diff -Naur -X linux-2.6.32-rc5-tty/Documentation/dontdiff linux-2.6.32-rc5-tty/drivers/usb/host/xhci-ring.c linux-2.6.32-rc5-usb/drivers/usb/host/xhci-ring.c --- linux-2.6.32-rc5-tty/drivers/usb/host/xhci-ring.c 2009-10-15 20:35:57.000000000 -0700 +++ linux-2.6.32-rc5-usb/drivers/usb/host/xhci-ring.c 2009-10-30 12:27:50.000000000 -0700 @@ -306,7 +306,7 @@ /* Don't ring the doorbell for this endpoint if there are pending * cancellations because the we don't want to interrupt processing. */ - if (!ep->cancels_pending && !(ep_state & SET_DEQ_PENDING) + if (!(ep_state & EP_HALT_PENDING) && !(ep_state & SET_DEQ_PENDING) && !(ep_state & EP_HALTED)) { field = xhci_readl(xhci, db_addr) & DB_MASK; xhci_writel(xhci, field | EPI_TO_DB(ep_index), db_addr); @@ -475,6 +475,35 @@ ep->ep_state |= SET_DEQ_PENDING; } +static inline void xhci_stop_watchdog_timer_in_irq(struct xhci_hcd *xhci, + struct xhci_virt_ep *ep) +{ + ep->ep_state &= ~EP_HALT_PENDING; + /* Can't del_timer_sync in interrupt, so we attempt to cancel. If the + * timer is running on another CPU, we don't decrement stop_cmds_pending + * (since we didn't successfully stop the watchdog timer). + */ + if (del_timer(&ep->stop_cmd_timer)) + ep->stop_cmds_pending--; +} + +/* Must be called with xhci->lock held in interrupt context */ +static void xhci_giveback_urb_in_irq(struct xhci_hcd *xhci, + struct xhci_td *cur_td, int status, char *adjective) +{ + struct usb_hcd *hcd = xhci_to_hcd(xhci); + + cur_td->urb->hcpriv = NULL; + usb_hcd_unlink_urb_from_ep(hcd, cur_td->urb); + xhci_dbg(xhci, "Giveback %s URB %p\n", adjective, cur_td->urb); + + spin_unlock(&xhci->lock); + usb_hcd_giveback_urb(hcd, cur_td->urb, status); + kfree(cur_td); + spin_lock(&xhci->lock); + xhci_dbg(xhci, "%s URB given back\n", adjective); +} + /* * When we get a command completion for a Stop Endpoint Command, we need to * unlink any cancelled TDs from the ring. There are two ways to do that: @@ -507,8 +536,11 @@ ep = &xhci->devs[slot_id]->eps[ep_index]; ep_ring = ep->ring; - if (list_empty(&ep->cancelled_td_list)) + if (list_empty(&ep->cancelled_td_list)) { + xhci_stop_watchdog_timer_in_irq(xhci, ep); + ring_ep_doorbell(xhci, slot_id, ep_index); return; + } /* Fix up the ep ring first, so HW stops executing cancelled TDs. * We have the xHCI lock, so nothing can modify this list until we drop @@ -535,9 +567,9 @@ * the cancelled TD list for URB completion later. */ list_del(&cur_td->td_list); - ep->cancels_pending--; } last_unlinked_td = cur_td; + xhci_stop_watchdog_timer_in_irq(xhci, ep); /* If necessary, queue a Set Transfer Ring Dequeue Pointer command */ if (deq_state.new_deq_ptr && deq_state.new_deq_seg) { @@ -565,23 +597,136 @@ hcd_stat_update(xhci->tp_stat, cur_td->urb->actual_length, ktime_sub(stop_time, cur_td->start_time)); #endif - cur_td->urb->hcpriv = NULL; - usb_hcd_unlink_urb_from_ep(xhci_to_hcd(xhci), cur_td->urb); - - xhci_dbg(xhci, "Giveback cancelled URB %p\n", cur_td->urb); - spin_unlock(&xhci->lock); /* Doesn't matter what we pass for status, since the core will * just overwrite it (because the URB has been unlinked). */ - usb_hcd_giveback_urb(xhci_to_hcd(xhci), cur_td->urb, 0); - kfree(cur_td); + xhci_giveback_urb_in_irq(xhci, cur_td, 0, "cancelled"); - spin_lock(&xhci->lock); + /* Stop processing the cancelled list if the watchdog timer is + * running. + */ + if (xhci->xhc_state & XHCI_STATE_DYING) + return; } while (cur_td != last_unlinked_td); /* Return to the event handler with xhci->lock re-acquired */ } +/* Watchdog timer function for when a stop endpoint command fails to complete. + * In this case, we assume the host controller is broken or dying or dead. The + * host may still be completing some other events, so we have to be careful to + * let the event ring handler and the URB dequeueing/enqueueing functions know + * through xhci->state. + * + * The timer may also fire if the host takes a very long time to respond to the + * command, and the stop endpoint command completion handler cannot delete the + * timer before the timer function is called. Another endpoint cancellation may + * sneak in before the timer function can grab the lock, and that may queue + * another stop endpoint command and add the timer back. So we cannot use a + * simple flag to say whether there is a pending stop endpoint command for a + * particular endpoint. + * + * Instead we use a combination of that flag and a counter for the number of + * pending stop endpoint commands. If the timer is the tail end of the last + * stop endpoint command, and the endpoint's command is still pending, we assume + * the host is dying. + */ +void xhci_stop_endpoint_command_watchdog(unsigned long arg) +{ + struct xhci_hcd *xhci; + struct xhci_virt_ep *ep; + struct xhci_virt_ep *temp_ep; + struct xhci_ring *ring; + struct xhci_td *cur_td; + int ret, i, j; + + ep = (struct xhci_virt_ep *) arg; + xhci = ep->xhci; + + spin_lock(&xhci->lock); + + ep->stop_cmds_pending--; + if (xhci->xhc_state & XHCI_STATE_DYING) { + xhci_dbg(xhci, "Stop EP timer ran, but another timer marked " + "xHCI as DYING, exiting.\n"); + spin_unlock(&xhci->lock); + return; + } + if (!(ep->stop_cmds_pending == 0 && (ep->ep_state & EP_HALT_PENDING))) { + xhci_dbg(xhci, "Stop EP timer ran, but no command pending, " + "exiting.\n"); + spin_unlock(&xhci->lock); + return; + } + + xhci_warn(xhci, "xHCI host not responding to stop endpoint command.\n"); + xhci_warn(xhci, "Assuming host is dying, halting host.\n"); + /* Oops, HC is dead or dying or at least not responding to the stop + * endpoint command. + */ + xhci->xhc_state |= XHCI_STATE_DYING; + /* Disable interrupts from the host controller and start halting it */ + xhci_quiesce(xhci); + spin_unlock(&xhci->lock); + + ret = xhci_halt(xhci); + + spin_lock(&xhci->lock); + if (ret < 0) { + /* This is bad; the host is not responding to commands and it's + * not allowing itself to be halted. At least interrupts are + * disabled, so we can set HC_STATE_HALT and notify the + * USB core. But if we call usb_hc_died(), it will attempt to + * disconnect all device drivers under this host. Those + * disconnect() methods will wait for all URBs to be unlinked, + * so we must complete them. + */ + xhci_warn(xhci, "Non-responsive xHCI host is not halting.\n"); + xhci_warn(xhci, "Completing active URBs anyway.\n"); + /* We could turn all TDs on the rings to no-ops. This won't + * help if the host has cached part of the ring, and is slow if + * we want to preserve the cycle bit. Skip it and hope the host + * doesn't touch the memory. + */ + } + for (i = 0; i < MAX_HC_SLOTS; i++) { + if (!xhci->devs[i]) + continue; + for (j = 0; j < 31; j++) { + temp_ep = &xhci->devs[i]->eps[j]; + ring = temp_ep->ring; + if (!ring) + continue; + xhci_dbg(xhci, "Killing URBs for slot ID %u, " + "ep index %u\n", i, j); + while (!list_empty(&ring->td_list)) { + cur_td = list_first_entry(&ring->td_list, + struct xhci_td, + td_list); + list_del(&cur_td->td_list); + if (!list_empty(&cur_td->cancelled_td_list)) + list_del(&cur_td->cancelled_td_list); + xhci_giveback_urb_in_irq(xhci, cur_td, + -ESHUTDOWN, "killed"); + } + while (!list_empty(&temp_ep->cancelled_td_list)) { + cur_td = list_first_entry( + &temp_ep->cancelled_td_list, + struct xhci_td, + cancelled_td_list); + list_del(&cur_td->cancelled_td_list); + xhci_giveback_urb_in_irq(xhci, cur_td, + -ESHUTDOWN, "killed"); + } + } + } + spin_unlock(&xhci->lock); + xhci_to_hcd(xhci)->state = HC_STATE_HALT; + xhci_dbg(xhci, "Calling usb_hc_died()\n"); + usb_hc_died(xhci_to_hcd(xhci)); + xhci_dbg(xhci, "xHCI host controller is dead.\n"); +} + /* * When we get a completion for a Set Transfer Ring Dequeue Pointer command, * we need to clear the set deq pending flag in the endpoint ring state, so that @@ -1246,10 +1391,9 @@ } list_del(&td->td_list); /* Was this TD slated to be cancelled but completed anyway? */ - if (!list_empty(&td->cancelled_td_list)) { + if (!list_empty(&td->cancelled_td_list)) list_del(&td->cancelled_td_list); - ep->cancels_pending--; - } + /* Leave the TD around for the reset endpoint function to use * (but only if it's not a control endpoint, since we already * queued the Set TR dequeue pointer command for stalled @@ -1328,6 +1472,14 @@ default: xhci->error_bitmask |= 1 << 3; } + /* Any of the above functions may drop and re-acquire the lock, so check + * to make sure a watchdog timer didn't mark the host as non-responsive. + */ + if (xhci->xhc_state & XHCI_STATE_DYING) { + xhci_dbg(xhci, "xHCI host dying, returning from " + "event handler.\n"); + return; + } if (update_ptrs) { /* Update SW and HC event ring dequeue pointer */ diff -Naur -X linux-2.6.32-rc5-tty/Documentation/dontdiff linux-2.6.32-rc5-tty/drivers/usb/Kconfig linux-2.6.32-rc5-usb/drivers/usb/Kconfig --- linux-2.6.32-rc5-tty/drivers/usb/Kconfig 2009-10-15 20:35:57.000000000 -0700 +++ linux-2.6.32-rc5-usb/drivers/usb/Kconfig 2009-10-30 12:27:50.000000000 -0700 @@ -60,6 +60,7 @@ default y if ARCH_IXP4XX default y if ARCH_W90X900 default y if ARCH_AT91SAM9G45 + default y if ARCH_MXC default PCI # ARM SA1111 chips have a non-PCI based "OHCI-compatible" USB host interface. diff -Naur -X linux-2.6.32-rc5-tty/Documentation/dontdiff linux-2.6.32-rc5-tty/drivers/usb/Makefile linux-2.6.32-rc5-usb/drivers/usb/Makefile --- linux-2.6.32-rc5-tty/drivers/usb/Makefile 2009-10-15 20:35:57.000000000 -0700 +++ linux-2.6.32-rc5-usb/drivers/usb/Makefile 2009-10-30 12:27:49.000000000 -0700 @@ -44,3 +44,5 @@ obj-$(CONFIG_USB_ATM) += atm/ obj-$(CONFIG_USB_SPEEDTOUCH) += atm/ + +obj-$(CONFIG_USB_ULPI) += otg/ diff -Naur -X linux-2.6.32-rc5-tty/Documentation/dontdiff linux-2.6.32-rc5-tty/drivers/usb/otg/Kconfig linux-2.6.32-rc5-usb/drivers/usb/otg/Kconfig --- linux-2.6.32-rc5-tty/drivers/usb/otg/Kconfig 2009-10-05 16:43:58.000000000 -0700 +++ linux-2.6.32-rc5-usb/drivers/usb/otg/Kconfig 2009-10-30 12:27:49.000000000 -0700 @@ -41,6 +41,15 @@ This driver can also be built as a module. If so, the module will be called isp1301_omap. +config USB_ULPI + bool "Generic ULPI Transceiver Driver" + depends on ARM + help + Enable this to support ULPI connected USB OTG transceivers which + are likely found on embedded boards. + + The only chip currently supported is NXP's ISP1504 + config TWL4030_USB tristate "TWL4030 USB Transceiver Driver" depends on TWL4030_CORE && REGULATOR_TWL4030 diff -Naur -X linux-2.6.32-rc5-tty/Documentation/dontdiff linux-2.6.32-rc5-tty/drivers/usb/otg/Makefile linux-2.6.32-rc5-usb/drivers/usb/otg/Makefile --- linux-2.6.32-rc5-tty/drivers/usb/otg/Makefile 2009-10-05 16:43:58.000000000 -0700 +++ linux-2.6.32-rc5-usb/drivers/usb/otg/Makefile 2009-10-30 12:27:49.000000000 -0700 @@ -10,6 +10,7 @@ obj-$(CONFIG_ISP1301_OMAP) += isp1301_omap.o obj-$(CONFIG_TWL4030_USB) += twl4030-usb.o obj-$(CONFIG_NOP_USB_XCEIV) += nop-usb-xceiv.o +obj-$(CONFIG_USB_ULPI) += ulpi.o ccflags-$(CONFIG_USB_DEBUG) += -DDEBUG ccflags-$(CONFIG_USB_GADGET_DEBUG) += -DDEBUG diff -Naur -X linux-2.6.32-rc5-tty/Documentation/dontdiff linux-2.6.32-rc5-tty/drivers/usb/otg/ulpi.c linux-2.6.32-rc5-usb/drivers/usb/otg/ulpi.c --- linux-2.6.32-rc5-tty/drivers/usb/otg/ulpi.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.32-rc5-usb/drivers/usb/otg/ulpi.c 2009-10-30 12:27:49.000000000 -0700 @@ -0,0 +1,136 @@ +/* + * Generic ULPI USB transceiver support + * + * Copyright (C) 2009 Daniel Mack + * + * Based on sources from + * + * Sascha Hauer + * Freescale Semiconductors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include + +/* ULPI register addresses */ +#define ULPI_VID_LOW 0x00 /* Vendor ID low */ +#define ULPI_VID_HIGH 0x01 /* Vendor ID high */ +#define ULPI_PID_LOW 0x02 /* Product ID low */ +#define ULPI_PID_HIGH 0x03 /* Product ID high */ +#define ULPI_ITFCTL 0x07 /* Interface Control */ +#define ULPI_OTGCTL 0x0A /* OTG Control */ + +/* add to above register address to access Set/Clear functions */ +#define ULPI_REG_SET 0x01 +#define ULPI_REG_CLEAR 0x02 + +/* ULPI OTG Control Register bits */ +#define ID_PULL_UP (1 << 0) /* enable ID Pull Up */ +#define DP_PULL_DOWN (1 << 1) /* enable DP Pull Down */ +#define DM_PULL_DOWN (1 << 2) /* enable DM Pull Down */ +#define DISCHRG_VBUS (1 << 3) /* Discharge Vbus */ +#define CHRG_VBUS (1 << 4) /* Charge Vbus */ +#define DRV_VBUS (1 << 5) /* Drive Vbus */ +#define DRV_VBUS_EXT (1 << 6) /* Drive Vbus external */ +#define USE_EXT_VBUS_IND (1 << 7) /* Use ext. Vbus indicator */ + +#define ULPI_ID(vendor, product) (((vendor) << 16) | (product)) + +#define TR_FLAG(flags, a, b) (((flags) & a) ? b : 0) + +/* ULPI hardcoded IDs, used for probing */ +static unsigned int ulpi_ids[] = { + ULPI_ID(0x04cc, 0x1504), /* NXP ISP1504 */ +}; + +static int ulpi_set_flags(struct otg_transceiver *otg) +{ + unsigned int flags = 0; + + if (otg->flags & USB_OTG_PULLUP_ID) + flags |= ID_PULL_UP; + + if (otg->flags & USB_OTG_PULLDOWN_DM) + flags |= DM_PULL_DOWN; + + if (otg->flags & USB_OTG_PULLDOWN_DP) + flags |= DP_PULL_DOWN; + + if (otg->flags & USB_OTG_EXT_VBUS_INDICATOR) + flags |= USE_EXT_VBUS_IND; + + return otg_io_write(otg, flags, ULPI_OTGCTL + ULPI_REG_SET); +} + +static int ulpi_init(struct otg_transceiver *otg) +{ + int i, vid, pid; + + vid = (otg_io_read(otg, ULPI_VID_HIGH) << 8) | + otg_io_read(otg, ULPI_VID_LOW); + pid = (otg_io_read(otg, ULPI_PID_HIGH) << 8) | + otg_io_read(otg, ULPI_PID_LOW); + + pr_info("ULPI transceiver vendor/product ID 0x%04x/0x%04x\n", vid, pid); + + for (i = 0; i < ARRAY_SIZE(ulpi_ids); i++) + if (ulpi_ids[i] == ULPI_ID(vid, pid)) + return ulpi_set_flags(otg); + + pr_err("ULPI ID does not match any known transceiver.\n"); + return -ENODEV; +} + +static int ulpi_set_vbus(struct otg_transceiver *otg, bool on) +{ + unsigned int flags = otg_io_read(otg, ULPI_OTGCTL); + + flags &= ~(DRV_VBUS | DRV_VBUS_EXT); + + if (on) { + if (otg->flags & USB_OTG_DRV_VBUS) + flags |= DRV_VBUS; + + if (otg->flags & USB_OTG_DRV_VBUS_EXT) + flags |= DRV_VBUS_EXT; + } + + return otg_io_write(otg, flags, ULPI_OTGCTL + ULPI_REG_SET); +} + +struct otg_transceiver * +otg_ulpi_create(struct otg_io_access_ops *ops, + unsigned int flags) +{ + struct otg_transceiver *otg; + + otg = kzalloc(sizeof(*otg), GFP_KERNEL); + if (!otg) + return NULL; + + otg->label = "ULPI"; + otg->flags = flags; + otg->io_ops = ops; + otg->init = ulpi_init; + otg->set_vbus = ulpi_set_vbus; + + return otg; +} +EXPORT_SYMBOL_GPL(otg_ulpi_create); + diff -Naur -X linux-2.6.32-rc5-tty/Documentation/dontdiff linux-2.6.32-rc5-tty/drivers/usb/serial/ftdi_sio.c linux-2.6.32-rc5-usb/drivers/usb/serial/ftdi_sio.c --- linux-2.6.32-rc5-tty/drivers/usb/serial/ftdi_sio.c 2009-10-15 20:35:57.000000000 -0700 +++ linux-2.6.32-rc5-usb/drivers/usb/serial/ftdi_sio.c 2009-10-30 12:27:48.000000000 -0700 @@ -2196,15 +2196,21 @@ /* Set number of data bits, parity, stop bits */ - termios->c_cflag &= ~CMSPAR; - urb_value = 0; urb_value |= (cflag & CSTOPB ? FTDI_SIO_SET_DATA_STOP_BITS_2 : FTDI_SIO_SET_DATA_STOP_BITS_1); - urb_value |= (cflag & PARENB ? - (cflag & PARODD ? FTDI_SIO_SET_DATA_PARITY_ODD : - FTDI_SIO_SET_DATA_PARITY_EVEN) : - FTDI_SIO_SET_DATA_PARITY_NONE); + if (cflag & PARENB) { + if (cflag & CMSPAR) + urb_value |= cflag & PARODD ? + FTDI_SIO_SET_DATA_PARITY_MARK : + FTDI_SIO_SET_DATA_PARITY_SPACE; + else + urb_value |= cflag & PARODD ? + FTDI_SIO_SET_DATA_PARITY_ODD : + FTDI_SIO_SET_DATA_PARITY_EVEN; + } else { + urb_value |= FTDI_SIO_SET_DATA_PARITY_NONE; + } if (cflag & CSIZE) { switch (cflag & CSIZE) { case CS5: urb_value |= 5; dbg("Setting CS5"); break; diff -Naur -X linux-2.6.32-rc5-tty/Documentation/dontdiff linux-2.6.32-rc5-tty/drivers/usb/storage/scsiglue.c linux-2.6.32-rc5-usb/drivers/usb/storage/scsiglue.c --- linux-2.6.32-rc5-tty/drivers/usb/storage/scsiglue.c 2009-10-05 16:45:25.000000000 -0700 +++ linux-2.6.32-rc5-usb/drivers/usb/storage/scsiglue.c 2009-10-30 12:27:49.000000000 -0700 @@ -73,7 +73,8 @@ static const char* host_info(struct Scsi_Host *host) { - return "SCSI emulation for USB Mass Storage devices"; + struct us_data *us = host_to_us(host); + return us->scsi_name; } static int slave_alloc (struct scsi_device *sdev) diff -Naur -X linux-2.6.32-rc5-tty/Documentation/dontdiff linux-2.6.32-rc5-tty/drivers/usb/storage/usb.c linux-2.6.32-rc5-usb/drivers/usb/storage/usb.c --- linux-2.6.32-rc5-tty/drivers/usb/storage/usb.c 2009-10-05 16:43:58.000000000 -0700 +++ linux-2.6.32-rc5-usb/drivers/usb/storage/usb.c 2009-10-30 12:27:49.000000000 -0700 @@ -45,6 +45,10 @@ * 675 Mass Ave, Cambridge, MA 02139, USA. */ +#ifdef CONFIG_USB_STORAGE_DEBUG +#define DEBUG +#endif + #include #include #include @@ -808,14 +812,13 @@ { struct us_data *us = (struct us_data *)__us; - printk(KERN_DEBUG - "usb-storage: device found at %d\n", us->pusb_dev->devnum); + dev_dbg(&us->pusb_intf->dev, "device found\n"); set_freezable(); /* Wait for the timeout to expire or for a disconnect */ if (delay_use > 0) { - printk(KERN_DEBUG "usb-storage: waiting for device " - "to settle before scanning\n"); + dev_dbg(&us->pusb_intf->dev, "waiting for device to settle " + "before scanning\n"); wait_event_freezable_timeout(us->delay_wait, test_bit(US_FLIDX_DONT_SCAN, &us->dflags), delay_use * HZ); @@ -832,7 +835,7 @@ mutex_unlock(&us->dev_mutex); } scsi_scan_host(us_to_host(us)); - printk(KERN_DEBUG "usb-storage: device scan complete\n"); + dev_dbg(&us->pusb_intf->dev, "scan complete\n"); /* Should we unbind if no devices were detected? */ } @@ -840,6 +843,15 @@ complete_and_exit(&us->scanning_done, 0); } +static unsigned int usb_stor_sg_tablesize(struct usb_interface *intf) +{ + struct usb_device *usb_dev = interface_to_usbdev(intf); + + if (usb_dev->bus->sg_tablesize) { + return usb_dev->bus->sg_tablesize; + } + return SG_ALL; +} /* First part of general USB mass-storage probing */ int usb_stor_probe1(struct us_data **pus, @@ -868,6 +880,7 @@ * Allow 16-byte CDBs and thus > 2TB */ host->max_cmd_len = 16; + host->sg_tablesize = usb_stor_sg_tablesize(intf); *pus = us = host_to_us(host); memset(us, 0, sizeof(struct us_data)); mutex_init(&(us->dev_mutex)); @@ -929,6 +942,8 @@ result = usb_stor_acquire_resources(us); if (result) goto BadDevice; + snprintf(us->scsi_name, sizeof(us->scsi_name), "usb-storage %s", + dev_name(&us->pusb_intf->dev)); result = scsi_add_host(us_to_host(us), &us->pusb_intf->dev); if (result) { printk(KERN_WARNING USB_STORAGE diff -Naur -X linux-2.6.32-rc5-tty/Documentation/dontdiff linux-2.6.32-rc5-tty/drivers/usb/storage/usb.h linux-2.6.32-rc5-usb/drivers/usb/storage/usb.h --- linux-2.6.32-rc5-tty/drivers/usb/storage/usb.h 2009-10-05 16:43:58.000000000 -0700 +++ linux-2.6.32-rc5-usb/drivers/usb/storage/usb.h 2009-10-30 12:27:49.000000000 -0700 @@ -132,6 +132,7 @@ /* SCSI interfaces */ struct scsi_cmnd *srb; /* current srb */ unsigned int tag; /* current dCBWTag */ + char scsi_name[32]; /* scsi_host name */ /* control and bulk communications data */ struct urb *current_urb; /* USB requests */ diff -Naur -X linux-2.6.32-rc5-tty/Documentation/dontdiff linux-2.6.32-rc5-tty/drivers/usb/usb-skeleton.c linux-2.6.32-rc5-usb/drivers/usb/usb-skeleton.c --- linux-2.6.32-rc5-tty/drivers/usb/usb-skeleton.c 2009-10-15 20:35:57.000000000 -0700 +++ linux-2.6.32-rc5-usb/drivers/usb/usb-skeleton.c 2009-10-30 12:27:49.000000000 -0700 @@ -358,7 +358,7 @@ rv = skel_do_read_io(dev, count); if (rv < 0) goto exit; - else if (!file->f_flags & O_NONBLOCK) + else if (!(file->f_flags & O_NONBLOCK)) goto retry; rv = -EAGAIN; } @@ -411,7 +411,7 @@ * limit the number of URBs in flight to stop a user from using up all * RAM */ - if (!file->f_flags & O_NONBLOCK) { + if (!(file->f_flags & O_NONBLOCK)) { if (down_interruptible(&dev->limit_sem)) { retval = -ERESTARTSYS; goto exit; diff -Naur -X linux-2.6.32-rc5-tty/Documentation/dontdiff linux-2.6.32-rc5-tty/drivers/usb/wusbcore/wusbhc.c linux-2.6.32-rc5-usb/drivers/usb/wusbcore/wusbhc.c --- linux-2.6.32-rc5-tty/drivers/usb/wusbcore/wusbhc.c 2009-10-05 16:43:58.000000000 -0700 +++ linux-2.6.32-rc5-usb/drivers/usb/wusbcore/wusbhc.c 2009-10-30 12:27:49.000000000 -0700 @@ -147,10 +147,40 @@ } static DEVICE_ATTR(wusb_chid, 0644, wusb_chid_show, wusb_chid_store); + +static ssize_t wusb_phy_rate_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct wusbhc *wusbhc = usbhc_dev_to_wusbhc(dev); + + return sprintf(buf, "%d\n", wusbhc->phy_rate); +} + +static ssize_t wusb_phy_rate_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct wusbhc *wusbhc = usbhc_dev_to_wusbhc(dev); + uint8_t phy_rate; + ssize_t result; + + result = sscanf(buf, "%hhu", &phy_rate); + if (result != 1) + return -EINVAL; + if (phy_rate >= UWB_PHY_RATE_INVALID) + return -EINVAL; + + wusbhc->phy_rate = phy_rate; + return size; +} +static DEVICE_ATTR(wusb_phy_rate, 0644, wusb_phy_rate_show, wusb_phy_rate_store); + /* Group all the WUSBHC attributes */ static struct attribute *wusbhc_attrs[] = { &dev_attr_wusb_trust_timeout.attr, &dev_attr_wusb_chid.attr, + &dev_attr_wusb_phy_rate.attr, NULL, }; @@ -177,6 +207,8 @@ int result = 0; wusbhc->trust_timeout = WUSB_TRUST_TIMEOUT_MS; + wusbhc->phy_rate = UWB_PHY_RATE_INVALID - 1; + mutex_init(&wusbhc->mutex); result = wusbhc_mmcie_create(wusbhc); if (result < 0) diff -Naur -X linux-2.6.32-rc5-tty/Documentation/dontdiff linux-2.6.32-rc5-tty/drivers/usb/wusbcore/wusbhc.h linux-2.6.32-rc5-usb/drivers/usb/wusbcore/wusbhc.h --- linux-2.6.32-rc5-tty/drivers/usb/wusbcore/wusbhc.h 2009-03-23 16:12:14.000000000 -0700 +++ linux-2.6.32-rc5-usb/drivers/usb/wusbcore/wusbhc.h 2009-10-30 12:27:49.000000000 -0700 @@ -253,6 +253,7 @@ unsigned trust_timeout; /* in jiffies */ struct wusb_ckhdid chid; + uint8_t phy_rate; struct wuie_host_info *wuie_host_info; struct mutex mutex; /* locks everything else */ diff -Naur -X linux-2.6.32-rc5-tty/Documentation/dontdiff linux-2.6.32-rc5-tty/include/linux/usb/composite.h linux-2.6.32-rc5-usb/include/linux/usb/composite.h --- linux-2.6.32-rc5-tty/include/linux/usb/composite.h 2009-10-05 16:45:30.000000000 -0700 +++ linux-2.6.32-rc5-usb/include/linux/usb/composite.h 2009-10-30 12:27:49.000000000 -0700 @@ -127,6 +127,7 @@ /* private: */ /* internals */ struct list_head list; + DECLARE_BITMAP(endpoints, 32); }; int usb_add_function(struct usb_configuration *, struct usb_function *); diff -Naur -X linux-2.6.32-rc5-tty/Documentation/dontdiff linux-2.6.32-rc5-tty/include/linux/usb/otg.h linux-2.6.32-rc5-usb/include/linux/usb/otg.h --- linux-2.6.32-rc5-tty/include/linux/usb/otg.h 2009-10-05 16:45:30.000000000 -0700 +++ linux-2.6.32-rc5-usb/include/linux/usb/otg.h 2009-10-30 12:27:49.000000000 -0700 @@ -33,6 +33,23 @@ OTG_STATE_A_VBUS_ERR, }; +#define USB_OTG_PULLUP_ID (1 << 0) +#define USB_OTG_PULLDOWN_DP (1 << 1) +#define USB_OTG_PULLDOWN_DM (1 << 2) +#define USB_OTG_EXT_VBUS_INDICATOR (1 << 3) +#define USB_OTG_DRV_VBUS (1 << 4) +#define USB_OTG_DRV_VBUS_EXT (1 << 5) + +struct otg_transceiver; + +/* for transceivers connected thru an ULPI interface, the user must + * provide access ops + */ +struct otg_io_access_ops { + int (*read)(struct otg_transceiver *otg, u32 reg); + int (*write)(struct otg_transceiver *otg, u32 val, u32 reg); +}; + /* * the otg driver needs to interact with both device side and host side * usb controllers. it decides which controller is active at a given @@ -42,6 +59,7 @@ struct otg_transceiver { struct device *dev; const char *label; + unsigned int flags; u8 default_a; enum usb_otg_state state; @@ -49,10 +67,17 @@ struct usb_bus *host; struct usb_gadget *gadget; + struct otg_io_access_ops *io_ops; + void __iomem *io_priv; + /* to pass extra port status to the root hub */ u16 port_status; u16 port_change; + /* initialize/shutdown the OTG controller */ + int (*init)(struct otg_transceiver *otg); + void (*shutdown)(struct otg_transceiver *otg); + /* bind/unbind the host controller */ int (*set_host)(struct otg_transceiver *otg, struct usb_bus *host); @@ -65,6 +90,10 @@ int (*set_power)(struct otg_transceiver *otg, unsigned mA); + /* effective for A-peripheral, ignored for B devices */ + int (*set_vbus)(struct otg_transceiver *otg, + bool enabled); + /* for non-OTG B devices: set transceiver into suspend mode */ int (*set_suspend)(struct otg_transceiver *otg, int suspend); @@ -85,6 +114,38 @@ extern void usb_nop_xceiv_register(void); extern void usb_nop_xceiv_unregister(void); +/* helpers for direct access thru low-level io interface */ +static inline int otg_io_read(struct otg_transceiver *otg, u32 reg) +{ + if (otg->io_ops && otg->io_ops->read) + return otg->io_ops->read(otg, reg); + + return -EINVAL; +} + +static inline int otg_io_write(struct otg_transceiver *otg, u32 reg, u32 val) +{ + if (otg->io_ops && otg->io_ops->write) + return otg->io_ops->write(otg, reg, val); + + return -EINVAL; +} + +static inline int +otg_init(struct otg_transceiver *otg) +{ + if (otg->init) + return otg->init(otg); + + return 0; +} + +static inline void +otg_shutdown(struct otg_transceiver *otg) +{ + if (otg->shutdown) + otg->shutdown(otg); +} /* for usb host and peripheral controller drivers */ extern struct otg_transceiver *otg_get_transceiver(void); @@ -97,6 +158,12 @@ return otg->start_hnp(otg); } +/* Context: can sleep */ +static inline int +otg_set_vbus(struct otg_transceiver *otg, bool enabled) +{ + return otg->set_vbus(otg, enabled); +} /* for HCDs */ static inline int @@ -105,7 +172,6 @@ return otg->set_host(otg, host); } - /* for usb peripheral controller drivers */ /* Context: can sleep */ diff -Naur -X linux-2.6.32-rc5-tty/Documentation/dontdiff linux-2.6.32-rc5-tty/include/linux/usb/ulpi.h linux-2.6.32-rc5-usb/include/linux/usb/ulpi.h --- linux-2.6.32-rc5-tty/include/linux/usb/ulpi.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.32-rc5-usb/include/linux/usb/ulpi.h 2009-10-30 12:27:49.000000000 -0700 @@ -0,0 +1,7 @@ +#ifndef __LINUX_USB_ULPI_H +#define __LINUX_USB_ULPI_H + +struct otg_transceiver *otg_ulpi_create(struct otg_io_access_ops *ops, + unsigned int flags); + +#endif /* __LINUX_USB_ULPI_H */ diff -Naur -X linux-2.6.32-rc5-tty/Documentation/dontdiff linux-2.6.32-rc5-tty/include/linux/usb.h linux-2.6.32-rc5-usb/include/linux/usb.h --- linux-2.6.32-rc5-tty/include/linux/usb.h 2009-10-15 20:35:59.000000000 -0700 +++ linux-2.6.32-rc5-usb/include/linux/usb.h 2009-10-30 12:27:49.000000000 -0700 @@ -331,6 +331,7 @@ u8 otg_port; /* 0, or number of OTG/HNP port */ unsigned is_b_host:1; /* true during some HNP roleswitches */ unsigned b_hnp_enable:1; /* OTG: did A-Host enable HNP? */ + unsigned sg_tablesize; /* 0 or largest number of sg list entries */ int devnum_next; /* Next open device number in * round-robin allocation */