diff options
author | Linus Torvalds | 2011-10-25 12:23:15 +0200 |
---|---|---|
committer | Linus Torvalds | 2011-10-25 12:23:15 +0200 |
commit | 1be025d3cb40cd295123af2c394f7229ef9b30ca (patch) | |
tree | 5dc14e1ea412cc7fdc3e563ad23187059fe8bfb5 | |
parent | 2d03423b2319cc854adeb28a03f65de5b5e0ab63 (diff) | |
parent | a2c76b83fdd763c826f38a55127ccf25708099ce (diff) |
Merge branch 'usb-next' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb
* 'usb-next' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (260 commits)
usb: renesas_usbhs: fixup inconsistent return from usbhs_pkt_push()
usb/isp1760: Allow to optionally trigger low-level chip reset via GPIOLIB.
USB: gadget: midi: memory leak in f_midi_bind_config()
USB: gadget: midi: fix range check in f_midi_out_open()
QE/FHCI: fixed the CONTROL bug
usb: renesas_usbhs: tidyup for smatch warnings
USB: Fix USB Kconfig dependency problem on 85xx/QoirQ platforms
EHCI: workaround for MosChip controller bug
usb: gadget: file_storage: fix race on unloading
USB: ftdi_sio.c: Use ftdi async_icount structure for TIOCMIWAIT, as in other drivers
USB: ftdi_sio.c:Fill MSR fields of the ftdi async_icount structure
USB: ftdi_sio.c: Fill LSR fields of the ftdi async_icount structure
USB: ftdi_sio.c:Fill TX field of the ftdi async_icount structure
USB: ftdi_sio.c: Fill the RX field of the ftdi async_icount structure
USB: ftdi_sio.c: Basic icount infrastructure for ftdi_sio
usb/isp1760: Let OF bindings depend on general CONFIG_OF instead of PPC_OF .
USB: ftdi_sio: Support TI/Luminary Micro Stellaris BD-ICDI Board
USB: Fix runtime wakeup on OHCI
xHCI/USB: Make xHCI driver have a BOS descriptor.
usb: gadget: add new usb gadget for ACM and mass storage
...
247 files changed, 15136 insertions, 4175 deletions
diff --git a/Documentation/ABI/testing/sysfs-bus-pci-drivers-ehci_hcd b/Documentation/ABI/testing/sysfs-bus-pci-drivers-ehci_hcd new file mode 100644 index 000000000000..60c60fa624b2 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-pci-drivers-ehci_hcd @@ -0,0 +1,46 @@ +What: /sys/bus/pci/drivers/ehci_hcd/.../companion + /sys/bus/usb/devices/usbN/../companion +Date: January 2007 +KernelVersion: 2.6.21 +Contact: Alan Stern <stern@rowland.harvard.edu> +Description: + PCI-based EHCI USB controllers (i.e., high-speed USB-2.0 + controllers) are often implemented along with a set of + "companion" full/low-speed USB-1.1 controllers. When a + high-speed device is plugged in, the connection is routed + to the EHCI controller; when a full- or low-speed device + is plugged in, the connection is routed to the companion + controller. + + Sometimes you want to force a high-speed device to connect + at full speed, which can be accomplished by forcing the + connection to be routed to the companion controller. + That's what this file does. Writing a port number to the + file causes connections on that port to be routed to the + companion controller, and writing the negative of a port + number returns the port to normal operation. + + For example: To force the high-speed device attached to + port 4 on bus 2 to run at full speed: + + echo 4 >/sys/bus/usb/devices/usb2/../companion + + To return the port to high-speed operation: + + echo -4 >/sys/bus/usb/devices/usb2/../companion + + Reading the file gives the list of ports currently forced + to the companion controller. + + Note: Some EHCI controllers do not have companions; they + may contain an internal "transaction translator" or they + may be attached directly to a "rate-matching hub". This + mechanism will not work with such controllers. Also, it + cannot be used to force a port on a high-speed hub to + connect at full speed. + + Note: When this file was first added, it appeared in a + different sysfs directory. The location given above is + correct for 2.6.35 (and probably several earlier kernel + versions as well). + diff --git a/Documentation/ABI/testing/sysfs-bus-usb b/Documentation/ABI/testing/sysfs-bus-usb index 294aa864a60a..e647378e9e88 100644 --- a/Documentation/ABI/testing/sysfs-bus-usb +++ b/Documentation/ABI/testing/sysfs-bus-usb @@ -142,3 +142,18 @@ Description: such devices. Users: usb_modeswitch + +What: /sys/bus/usb/devices/.../power/usb2_hardware_lpm +Date: September 2011 +Contact: Andiry Xu <andiry.xu@amd.com> +Description: + If CONFIG_USB_SUSPEND is set and a USB 2.0 lpm-capable device + is plugged in to a xHCI host which support link PM, it will + perform a LPM test; if the test is passed and host supports + USB2 hardware LPM (xHCI 1.0 feature), USB2 hardware LPM will + be enabled for the device and the USB device directory will + contain a file named power/usb2_hardware_lpm. The file holds + a string value (enable or disable) indicating whether or not + USB2 hardware LPM is enabled for the device. Developer can + write y/Y/1 or n/N/0 to the file to enable/disable the + feature. diff --git a/Documentation/usb/dwc3.txt b/Documentation/usb/dwc3.txt new file mode 100644 index 000000000000..7b590edae145 --- /dev/null +++ b/Documentation/usb/dwc3.txt @@ -0,0 +1,45 @@ + + TODO +~~~~~~ +Please pick something while reading :) + +- Convert interrupt handler to per-ep-thread-irq + + As it turns out some DWC3-commands ~1ms to complete. Currently we spin + until the command completes which is bad. + + Implementation idea: + - dwc core implements a demultiplexing irq chip for interrupts per + endpoint. The interrupt numbers are allocated during probe and belong + to the device. If MSI provides per-endpoint interrupt this dummy + interrupt chip can be replaced with "real" interrupts. + - interrupts are requested / allocated on usb_ep_enable() and removed on + usb_ep_disable(). Worst case are 32 interrupts, the lower limit is two + for ep0/1. + - dwc3_send_gadget_ep_cmd() will sleep in wait_for_completion_timeout() + until the command completes. + - the interrupt handler is split into the following pieces: + - primary handler of the device + goes through every event and calls generic_handle_irq() for event + it. On return from generic_handle_irq() in acknowledges the event + counter so interrupt goes away (eventually). + + - threaded handler of the device + none + + - primary handler of the EP-interrupt + reads the event and tries to process it. Everything that requries + sleeping is handed over to the Thread. The event is saved in an + per-endpoint data-structure. + We probably have to pay attention not to process events once we + handed something to thread so we don't process event X prio Y + where X > Y. + + - threaded handler of the EP-interrupt + handles the remaining EP work which might sleep such as waiting + for command completion. + + Latency: + There should be no increase in latency since the interrupt-thread has a + high priority and will be run before an average task in user land + (except the user changed priorities). diff --git a/Documentation/usb/power-management.txt b/Documentation/usb/power-management.txt index c9ffa9ced7ee..9d85d96ec6e0 100644 --- a/Documentation/usb/power-management.txt +++ b/Documentation/usb/power-management.txt @@ -487,3 +487,29 @@ succeed, it may still remain active and thus cause the system to resume as soon as the system suspend is complete. Or the remote wakeup may fail and get lost. Which outcome occurs depends on timing and on the hardware and firmware design. + + + xHCI hardware link PM + --------------------- + +xHCI host controller provides hardware link power management to usb2.0 +(xHCI 1.0 feature) and usb3.0 devices which support link PM. By +enabling hardware LPM, the host can automatically put the device into +lower power state(L1 for usb2.0 devices, or U1/U2 for usb3.0 devices), +which state device can enter and resume very quickly. + +The user interface for controlling USB2 hardware LPM is located in the +power/ subdirectory of each USB device's sysfs directory, that is, in +/sys/bus/usb/devices/.../power/ where "..." is the device's ID. The +relevant attribute files is usb2_hardware_lpm. + + power/usb2_hardware_lpm + + When a USB2 device which support LPM is plugged to a + xHCI host root hub which support software LPM, the + host will run a software LPM test for it; if the device + enters L1 state and resume successfully and the host + supports USB2 hardware LPM, this file will show up and + driver will enable hardware LPM for the device. You + can write y/Y/1 or n/N/0 to the file to enable/disable + USB2 hardware LPM manually. This is for test purpose mainly. diff --git a/MAINTAINERS b/MAINTAINERS index e1b0c92b48fc..37489ec1da30 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2136,6 +2136,14 @@ M: Matthew Garrett <mjg59@srcf.ucam.org> S: Maintained F: drivers/platform/x86/dell-wmi.c +DESIGNWARE USB3 DRD IP DRIVER +M: Felipe Balbi <balbi@ti.com> +L: linux-usb@vger.kernel.org +L: linux-omap@vger.kernel.org +T: git git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb.git +S: Maintained +F: drivers/usb/dwc3/ + DEVICE NUMBER REGISTRY M: Torben Mathiasen <device@lanana.org> W: http://lanana.org/docs/device-list/index.html diff --git a/arch/arm/mach-mmp/include/mach/pxa168.h b/arch/arm/mach-mmp/include/mach/pxa168.h index 7f005843a707..7fb568d2845b 100644 --- a/arch/arm/mach-mmp/include/mach/pxa168.h +++ b/arch/arm/mach-mmp/include/mach/pxa168.h @@ -35,6 +35,13 @@ extern struct pxa_device_desc pxa168_device_fb; extern struct pxa_device_desc pxa168_device_keypad; extern struct pxa_device_desc pxa168_device_eth; +struct pxa168_usb_pdata { + /* If NULL, default phy init routine for PXA168 would be called */ + int (*phy_init)(void __iomem *usb_phy_reg_base); +}; +/* pdata can be NULL */ +int __init pxa168_add_usb_host(struct pxa168_usb_pdata *pdata); + static inline int pxa168_add_uart(int id) { struct pxa_device_desc *d = NULL; diff --git a/arch/arm/mach-mmp/pxa168.c b/arch/arm/mach-mmp/pxa168.c index 0156f535dae7..b2b280c517dd 100644 --- a/arch/arm/mach-mmp/pxa168.c +++ b/arch/arm/mach-mmp/pxa168.c @@ -25,6 +25,9 @@ #include <mach/dma.h> #include <mach/devices.h> #include <mach/mfp.h> +#include <linux/platform_device.h> +#include <linux/dma-mapping.h> +#include <mach/pxa168.h> #include "common.h" #include "clock.h" @@ -83,6 +86,7 @@ static APBC_CLK(keypad, PXA168_KPC, 0, 32000); static APMU_CLK(nand, NAND, 0x19b, 156000000); static APMU_CLK(lcd, LCD, 0x7f, 312000000); static APMU_CLK(eth, ETH, 0x09, 0); +static APMU_CLK(usb, USB, 0x12, 0); /* device and clock bindings */ static struct clk_lookup pxa168_clkregs[] = { @@ -104,6 +108,7 @@ static struct clk_lookup pxa168_clkregs[] = { INIT_CLKREG(&clk_lcd, "pxa168-fb", NULL), INIT_CLKREG(&clk_keypad, "pxa27x-keypad", NULL), INIT_CLKREG(&clk_eth, "pxa168-eth", "MFUCLK"), + INIT_CLKREG(&clk_usb, "pxa168-ehci", "PXA168-USBCLK"), }; static int __init pxa168_init(void) @@ -169,3 +174,44 @@ PXA168_DEVICE(ssp5, "pxa168-ssp", 4, SSP5, 0xd4021000, 0x40, 60, 61); PXA168_DEVICE(fb, "pxa168-fb", -1, LCD, 0xd420b000, 0x1c8); PXA168_DEVICE(keypad, "pxa27x-keypad", -1, KEYPAD, 0xd4012000, 0x4c); PXA168_DEVICE(eth, "pxa168-eth", -1, MFU, 0xc0800000, 0x0fff); + +struct resource pxa168_usb_host_resources[] = { + /* USB Host conroller register base */ + [0] = { + .start = 0xd4209000, + .end = 0xd4209000 + 0x200, + .flags = IORESOURCE_MEM, + .name = "pxa168-usb-host", + }, + /* USB PHY register base */ + [1] = { + .start = 0xd4206000, + .end = 0xd4206000 + 0xff, + .flags = IORESOURCE_MEM, + .name = "pxa168-usb-phy", + }, + [2] = { + .start = IRQ_PXA168_USB2, + .end = IRQ_PXA168_USB2, + .flags = IORESOURCE_IRQ, + }, +}; + +static u64 pxa168_usb_host_dmamask = DMA_BIT_MASK(32); +struct platform_device pxa168_device_usb_host = { + .name = "pxa168-ehci", + .id = -1, + .dev = { + .dma_mask = &pxa168_usb_host_dmamask, + .coherent_dma_mask = DMA_BIT_MASK(32), + }, + + .num_resources = ARRAY_SIZE(pxa168_usb_host_resources), + .resource = pxa168_usb_host_resources, +}; + +int __init pxa168_add_usb_host(struct pxa168_usb_pdata *pdata) +{ + pxa168_device_usb_host.dev.platform_data = pdata; + return platform_device_register(&pxa168_device_usb_host); +} diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile index f34336560437..7317a2b39dd1 100644 --- a/arch/arm/mach-omap2/Makefile +++ b/arch/arm/mach-omap2/Makefile @@ -242,14 +242,11 @@ obj-$(CONFIG_MACH_IGEP0020) += board-igep0020.o \ obj-$(CONFIG_MACH_OMAP3_TOUCHBOOK) += board-omap3touchbook.o \ hsmmc.o obj-$(CONFIG_MACH_OMAP_4430SDP) += board-4430sdp.o \ - hsmmc.o \ - omap_phy_internal.o + hsmmc.o obj-$(CONFIG_MACH_OMAP4_PANDA) += board-omap4panda.o \ - hsmmc.o \ - omap_phy_internal.o + hsmmc.o -obj-$(CONFIG_MACH_OMAP3517EVM) += board-am3517evm.o \ - omap_phy_internal.o \ +obj-$(CONFIG_MACH_OMAP3517EVM) += board-am3517evm.o obj-$(CONFIG_MACH_CRANEBOARD) += board-am3517crane.o @@ -260,6 +257,8 @@ obj-$(CONFIG_MACH_TI8168EVM) += board-ti8168evm.o usbfs-$(CONFIG_ARCH_OMAP_OTG) := usb-fs.o obj-y += $(usbfs-m) $(usbfs-y) obj-y += usb-musb.o +obj-y += omap_phy_internal.o + obj-$(CONFIG_MACH_OMAP2_TUSB6010) += usb-tusb6010.o obj-y += usb-host.o diff --git a/arch/arm/mach-shmobile/board-mackerel.c b/arch/arm/mach-shmobile/board-mackerel.c index 17c19dc25604..802aa58ef9d8 100644 --- a/arch/arm/mach-shmobile/board-mackerel.c +++ b/arch/arm/mach-shmobile/board-mackerel.c @@ -810,6 +810,7 @@ static struct usbhs_private usbhs1_private = { }, .driver_param = { .buswait_bwait = 4, + .has_otg = 1, .pipe_type = usbhs1_pipe_cfg, .pipe_size = ARRAY_SIZE(usbhs1_pipe_cfg), .d0_tx_id = SHDMA_SLAVE_USB1_TX, diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig index 48f1781352f1..4ac2750491de 100644 --- a/drivers/usb/Kconfig +++ b/drivers/usb/Kconfig @@ -12,6 +12,11 @@ menuconfig USB_SUPPORT if USB_SUPPORT +config USB_COMMON + tristate + default y + depends on USB || USB_GADGET + # Host-side USB depends on having a host controller # NOTE: dummy_hcd is always an option, but it's ignored here ... # NOTE: SL-811 option should be board-specific ... @@ -19,6 +24,7 @@ config USB_ARCH_HAS_HCD boolean default y if USB_ARCH_HAS_OHCI default y if USB_ARCH_HAS_EHCI + default y if USB_ARCH_HAS_XHCI default y if PCMCIA && !M32R # sl811_cs default y if ARM # SL-811 default y if BLACKFIN # SL-811 @@ -54,7 +60,7 @@ config USB_ARCH_HAS_OHCI # some non-PCI hcds implement EHCI config USB_ARCH_HAS_EHCI boolean - default y if PPC_83xx + default y if FSL_SOC default y if PPC_MPC512x default y if SOC_AU1200 default y if ARCH_IXP4XX @@ -69,6 +75,12 @@ config USB_ARCH_HAS_EHCI default y if ARCH_MSM default y if MICROBLAZE default y if SPARC_LEON + default y if ARCH_MMP + default PCI + +# some non-PCI HCDs implement xHCI +config USB_ARCH_HAS_XHCI + boolean default PCI # ARM SA1111 chips have a non-PCI based "OHCI-compatible" USB host interface. @@ -110,6 +122,8 @@ config USB source "drivers/usb/core/Kconfig" +source "drivers/usb/dwc3/Kconfig" + source "drivers/usb/mon/Kconfig" source "drivers/usb/wusbcore/Kconfig" diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index 30ddf8dc4f72..75eca7645227 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -6,6 +6,8 @@ obj-$(CONFIG_USB) += core/ +obj-$(CONFIG_USB_DWC3) += dwc3/ + obj-$(CONFIG_USB_MON) += mon/ obj-$(CONFIG_PCI) += host/ @@ -51,3 +53,5 @@ obj-$(CONFIG_USB_MUSB_HDRC) += musb/ obj-$(CONFIG_USB_RENESAS_USBHS) += renesas_usbhs/ obj-$(CONFIG_USB_OTG_UTILS) += otg/ obj-$(CONFIG_USB_GADGET) += gadget/ + +obj-$(CONFIG_USB_COMMON) += usb-common.o diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index dac7676ce21b..3ec6699ab725 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -1058,11 +1058,11 @@ made_compressed_probe: goto alloc_fail; } - ctrlsize = le16_to_cpu(epctrl->wMaxPacketSize); - readsize = le16_to_cpu(epread->wMaxPacketSize) * + ctrlsize = usb_endpoint_maxp(epctrl); + readsize = usb_endpoint_maxp(epread) * (quirks == SINGLE_RX_URB ? 1 : 2); acm->combined_interfaces = combined_interfaces; - acm->writesize = le16_to_cpu(epwrite->wMaxPacketSize) * 20; + acm->writesize = usb_endpoint_maxp(epwrite) * 20; acm->control = control_interface; acm->data = data_interface; acm->minor = minor; @@ -1534,6 +1534,9 @@ static const struct usb_device_id acm_ids[] = { { NOKIA_PCSUITE_ACM_INFO(0x03cd), }, /* Nokia C7 */ { SAMSUNG_PCSUITE_ACM_INFO(0x6651), }, /* Samsung GTi8510 (INNOV8) */ + /* Support for Owen devices */ + { USB_DEVICE(0x03eb, 0x0030), }, /* Owen SI30 */ + /* NOTE: non-Nokia COMM/ACM/0xff is likely MSFT RNDIS... NOT a modem! */ /* Support Lego NXT using pbLua firmware */ diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c index 2b9ff518b509..1d26a7135dd9 100644 --- a/drivers/usb/class/cdc-wdm.c +++ b/drivers/usb/class/cdc-wdm.c @@ -682,7 +682,7 @@ next_desc: if (!ep || !usb_endpoint_is_int_in(ep)) goto err; - desc->wMaxPacketSize = le16_to_cpu(ep->wMaxPacketSize); + desc->wMaxPacketSize = usb_endpoint_maxp(ep); desc->bMaxPacketSize0 = udev->descriptor.bMaxPacketSize0; desc->orq = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL); diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c index 3f94ac34dce3..12cf5e7395a8 100644 --- a/drivers/usb/class/usbtmc.c +++ b/drivers/usb/class/usbtmc.c @@ -186,8 +186,7 @@ static int usbtmc_ioctl_abort_bulk_in(struct usbtmc_device_data *data) for (n = 0; n < current_setting->desc.bNumEndpoints; n++) if (current_setting->endpoint[n].desc.bEndpointAddress == data->bulk_in) - max_size = le16_to_cpu(current_setting->endpoint[n]. - desc.wMaxPacketSize); + max_size = usb_endpoint_maxp(¤t_setting->endpoint[n].desc); if (max_size == 0) { dev_err(dev, "Couldn't get wMaxPacketSize\n"); @@ -636,7 +635,7 @@ static int usbtmc_ioctl_clear(struct usbtmc_device_data *data) for (n = 0; n < current_setting->desc.bNumEndpoints; n++) { desc = ¤t_setting->endpoint[n].desc; if (desc->bEndpointAddress == data->bulk_in) - max_size = le16_to_cpu(desc->wMaxPacketSize); + max_size = usb_endpoint_maxp(desc); } if (max_size == 0) { diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c index 26678cadfb21..f4bdd0ce8d56 100644 --- a/drivers/usb/core/config.c +++ b/drivers/usb/core/config.c @@ -124,9 +124,9 @@ static void usb_parse_ss_endpoint_companion(struct device *ddev, int cfgno, if (usb_endpoint_xfer_isoc(&ep->desc)) max_tx = (desc->bMaxBurst + 1) * (desc->bmAttributes + 1) * - le16_to_cpu(ep->desc.wMaxPacketSize); + usb_endpoint_maxp(&ep->desc); else if (usb_endpoint_xfer_int(&ep->desc)) - max_tx = le16_to_cpu(ep->desc.wMaxPacketSize) * + max_tx = usb_endpoint_maxp(&ep->desc) * (desc->bMaxBurst + 1); else max_tx = 999999; @@ -241,7 +241,7 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum, cfgno, inum, asnum, d->bEndpointAddress); endpoint->desc.bmAttributes = USB_ENDPOINT_XFER_INT; endpoint->desc.bInterval = 1; - if (le16_to_cpu(endpoint->desc.wMaxPacketSize) > 8) + if (usb_endpoint_maxp(&endpoint->desc) > 8) endpoint->desc.wMaxPacketSize = cpu_to_le16(8); } @@ -254,7 +254,7 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum, && usb_endpoint_xfer_bulk(d)) { unsigned maxp; - maxp = le16_to_cpu(endpoint->desc.wMaxPacketSize) & 0x07ff; + maxp = usb_endpoint_maxp(&endpoint->desc) & 0x07ff; if (maxp != 512) dev_warn(ddev, "config %d interface %d altsetting %d " "bulk endpoint 0x%X has invalid maxpacket %d\n", @@ -755,3 +755,106 @@ err2: dev_err(ddev, "out of memory\n"); return result; } + +void usb_release_bos_descriptor(struct usb_device *dev) +{ + if (dev->bos) { + kfree(dev->bos->desc); + kfree(dev->bos); + dev->bos = NULL; + } +} + +/* Get BOS descriptor set */ +int usb_get_bos_descriptor(struct usb_device *dev) +{ + struct device *ddev = &dev->dev; + struct usb_bos_descriptor *bos; + struct usb_dev_cap_header *cap; + unsigned char *buffer; + int length, total_len, num, i; + int ret; + + bos = kzalloc(sizeof(struct usb_bos_descriptor), GFP_KERNEL); + if (!bos) + return -ENOMEM; + + /* Get BOS descriptor */ + ret = usb_get_descriptor(dev, USB_DT_BOS, 0, bos, USB_DT_BOS_SIZE); + if (ret < USB_DT_BOS_SIZE) { + dev_err(ddev, "unable to get BOS descriptor\n"); + if (ret >= 0) + ret = -ENOMSG; + kfree(bos); + return ret; + } + + length = bos->bLength; + total_len = le16_to_cpu(bos->wTotalLength); + num = bos->bNumDeviceCaps; + kfree(bos); + if (total_len < length) + return -EINVAL; + + dev->bos = kzalloc(sizeof(struct usb_host_bos), GFP_KERNEL); + if (!dev->bos) + return -ENOMEM; + + /* Now let's get the whole BOS descriptor set */ + buffer = kzalloc(total_len, GFP_KERNEL); + if (!buffer) { + ret = -ENOMEM; + goto err; + } + dev->bos->desc = (struct usb_bos_descriptor *)buffer; + + ret = usb_get_descriptor(dev, USB_DT_BOS, 0, buffer, total_len); + if (ret < total_len) { + dev_err(ddev, "unable to get BOS descriptor set\n"); + if (ret >= 0) + ret = -ENOMSG; + goto err; + } + total_len -= length; + + for (i = 0; i < num; i++) { + buffer += length; + cap = (struct usb_dev_cap_header *)buffer; + length = cap->bLength; + + if (total_len < length) + break; + total_len -= length; + + if (cap->bDescriptorType != USB_DT_DEVICE_CAPABILITY) { + dev_warn(ddev, "descriptor type invalid, skip\n"); + continue; + } + + switch (cap->bDevCapabilityType) { + case USB_CAP_TYPE_WIRELESS_USB: + /* Wireless USB cap descriptor is handled by wusb */ + break; + case USB_CAP_TYPE_EXT: + dev->bos->ext_cap = + (struct usb_ext_cap_descriptor *)buffer; + break; + case USB_SS_CAP_TYPE: + dev->bos->ss_cap = + (struct usb_ss_cap_descriptor *)buffer; + break; + case CONTAINER_ID_TYPE: + dev->bos->ss_id = + (struct usb_ss_container_id_descriptor *)buffer; + break; + default: + break; + } + } + + return 0; + +err: + usb_release_bos_descriptor(dev); + return ret; +} diff --git a/drivers/usb/core/devices.c b/drivers/usb/core/devices.c index 0149c0976e9c..d95696584762 100644 --- a/drivers/usb/core/devices.c +++ b/drivers/usb/core/devices.c @@ -190,7 +190,7 @@ static char *usb_dump_endpoint_descriptor(int speed, char *start, char *end, dir = usb_endpoint_dir_in(desc) ? 'I' : 'O'; if (speed == USB_SPEED_HIGH) { - switch (le16_to_cpu(desc->wMaxPacketSize) & (0x03 << 11)) { + switch (usb_endpoint_maxp(desc) & (0x03 << 11)) { case 1 << 11: bandwidth = 2; break; case 2 << 11: @@ -240,7 +240,7 @@ static char *usb_dump_endpoint_descriptor(int speed, char *start, char *end, start += sprintf(start, format_endpt, desc->bEndpointAddress, dir, desc->bmAttributes, type, - (le16_to_cpu(desc->wMaxPacketSize) & 0x07ff) * + (usb_endpoint_maxp(desc) & 0x07ff) * bandwidth, interval, unit); return start; diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 37518dfdeb98..e3beaf229ee3 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -46,6 +46,7 @@ #include <linux/cdev.h> #include <linux/notifier.h> #include <linux/security.h> +#include <linux/user_namespace.h> #include <asm/uaccess.h> #include <asm/byteorder.h> #include <linux/moduleparam.h> @@ -68,7 +69,7 @@ struct dev_state { wait_queue_head_t wait; /* wake up if a request completed */ unsigned int discsignr; struct pid *disc_pid; - uid_t disc_uid, disc_euid; + const struct cred *cred; void __user *disccontext; unsigned long ifclaimed; u32 secid; @@ -79,7 +80,7 @@ struct async { struct list_head asynclist; struct dev_state *ps; struct pid *pid; - uid_t uid, euid; + const struct cred *cred; unsigned int signr; unsigned int ifnum; void __user *userbuffer; @@ -248,6 +249,7 @@ static struct async *alloc_async(unsigned int numisoframes) static void free_async(struct async *as) { put_pid(as->pid); + put_cred(as->cred); kfree(as->urb->transfer_buffer); kfree(as->urb->setup_packet); usb_free_urb(as->urb); @@ -393,9 +395,8 @@ static void async_completed(struct urb *urb) struct dev_state *ps = as->ps; struct siginfo sinfo; struct pid *pid = NULL; - uid_t uid = 0; - uid_t euid = 0; u32 secid = 0; + const struct cred *cred = NULL; int signr; spin_lock(&ps->lock); @@ -407,9 +408,8 @@ static void async_completed(struct urb *urb) sinfo.si_errno = as->status; sinfo.si_code = SI_ASYNCIO; sinfo.si_addr = as->userurb; - pid = as->pid; - uid = as->uid; - euid = as->euid; + pid = get_pid(as->pid); + cred = get_cred(as->cred); secid = as->secid; } snoop(&urb->dev->dev, "urb complete\n"); @@ -422,9 +422,11 @@ static void async_completed(struct urb *urb) cancel_bulk_urbs(ps, as->bulk_addr); spin_unlock(&ps->lock); - if (signr) - kill_pid_info_as_uid(sinfo.si_signo, &sinfo, pid, uid, - euid, secid); + if (signr) { + kill_pid_info_as_cred(sinfo.si_signo, &sinfo, pid, cred, secid); + put_pid(pid); + put_cred(cred); + } wake_up(&ps->wait); } @@ -607,9 +609,10 @@ static int findintfep(struct usb_device *dev, unsigned int ep) } static int check_ctrlrecip(struct dev_state *ps, unsigned int requesttype, - unsigned int index) + unsigned int request, unsigned int index) { int ret = 0; + struct usb_host_interface *alt_setting; if (ps->dev->state != USB_STATE_UNAUTHENTICATED && ps->dev->state != USB_STATE_ADDRESS @@ -618,6 +621,19 @@ static int check_ctrlrecip(struct dev_state *ps, unsigned int requesttype, if (USB_TYPE_VENDOR == (USB_TYPE_MASK & requesttype)) return 0; + /* + * check for the special corner case 'get_device_id' in the printer + * class specification, where wIndex is (interface << 8 | altsetting) + * instead of just interface + */ + if (requesttype == 0xa1 && request == 0) { + alt_setting = usb_find_alt_setting(ps->dev->actconfig, + index >> 8, index & 0xff); + if (alt_setting + && alt_setting->desc.bInterfaceClass == USB_CLASS_PRINTER) + index >>= 8; + } + index &= 0xff; switch (requesttype & USB_RECIP_MASK) { case USB_RECIP_ENDPOINT: @@ -656,7 +672,6 @@ static int usbdev_open(struct inode *inode, struct file *file) { struct usb_device *dev = NULL; struct dev_state *ps; - const struct cred *cred = current_cred(); int ret; ret = -ENOMEM; @@ -706,8 +721,7 @@ static int usbdev_open(struct inode *inode, struct file *file) init_waitqueue_head(&ps->wait); ps->discsignr = 0; ps->disc_pid = get_pid(task_pid(current)); - ps->disc_uid = cred->uid; - ps->disc_euid = cred->euid; + ps->cred = get_current_cred(); ps->disccontext = NULL; ps->ifclaimed = 0; security_task_getsecid(current, &ps->secid); @@ -749,6 +763,7 @@ static int usbdev_release(struct inode *inode, struct file *file) usb_unlock_device(dev); usb_put_dev(dev); put_pid(ps->disc_pid); + put_cred(ps->cred); as = async_getcompleted(ps); while (as) { @@ -770,7 +785,8 @@ static int proc_control(struct dev_state *ps, void __user *arg) if (copy_from_user(&ctrl, arg, sizeof(ctrl))) return -EFAULT; - ret = check_ctrlrecip(ps, ctrl.bRequestType, ctrl.wIndex); + ret = check_ctrlrecip(ps, ctrl.bRequestType, ctrl.bRequest, + ctrl.wIndex); if (ret) return ret; wLength = ctrl.wLength; /* To suppress 64k PAGE_SIZE warning */ @@ -1048,7 +1064,6 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, struct usb_host_endpoint *ep; struct async *as; struct usb_ctrlrequest *dr = NULL; - const struct cred *cred = current_cred(); unsigned int u, totlen, isofrmlen; int ret, ifnum = -1; int is_in; @@ -1100,7 +1115,7 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, kfree(dr); return -EINVAL; } - ret = check_ctrlrecip(ps, dr->bRequestType, + ret = check_ctrlrecip(ps, dr->bRequestType, dr->bRequest, le16_to_cpup(&dr->wIndex)); if (ret) { kfree(dr); @@ -1262,8 +1277,7 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, as->signr = uurb->signr; as->ifnum = ifnum; as->pid = get_pid(task_pid(current)); - as->uid = cred->uid; - as->euid = cred->euid; + as->cred = get_current_cred(); security_task_getsecid(current, &as->secid); if (!is_in && uurb->buffer_length > 0) { if (copy_from_user(as->urb->transfer_buffer, uurb->buffer, @@ -1981,9 +1995,8 @@ static void usbdev_remove(struct usb_device *udev) sinfo.si_errno = EPIPE; sinfo.si_code = SI_ASYNCIO; sinfo.si_addr = ps->disccontext; - kill_pid_info_as_uid(ps->discsignr, &sinfo, - ps->disc_pid, ps->disc_uid, - ps->disc_euid, ps->secid); + kill_pid_info_as_cred(ps->discsignr, &sinfo, + ps->disc_pid, ps->cred, ps->secid); } } } diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 34e3da5aa72a..adf5ca8a2396 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -1583,7 +1583,7 @@ int usb_autopm_get_interface_async(struct usb_interface *intf) dev_vdbg(&intf->dev, "%s: cnt %d -> %d\n", __func__, atomic_read(&intf->dev.power.usage_count), status); - if (status > 0) + if (status > 0 || status == -EINPROGRESS) status = 0; return status; } @@ -1700,6 +1700,20 @@ int usb_runtime_idle(struct device *dev) return 0; } +int usb_set_usb2_hardware_lpm(struct usb_device *udev, int enable) +{ + struct usb_hcd *hcd = bus_to_hcd(udev->bus); + int ret = -EPERM; + + if (hcd->driver->set_usb2_hw_lpm) { + ret = hcd->driver->set_usb2_hw_lpm(hcd, udev, enable); + if (!ret) + udev->usb2_hw_lpm_enabled = enable; + } + + return ret; +} + #endif /* CONFIG_USB_SUSPEND */ struct bus_type usb_bus_type = { diff --git a/drivers/usb/core/endpoint.c b/drivers/usb/core/endpoint.c index df502a98d0df..db7fe50c23d4 100644 --- a/drivers/usb/core/endpoint.c +++ b/drivers/usb/core/endpoint.c @@ -56,7 +56,7 @@ static ssize_t show_ep_wMaxPacketSize(struct device *dev, { struct ep_device *ep = to_ep_device(dev); return sprintf(buf, "%04x\n", - le16_to_cpu(ep->desc->wMaxPacketSize) & 0x07ff); + usb_endpoint_maxp(ep->desc) & 0x07ff); } static DEVICE_ATTR(wMaxPacketSize, S_IRUGO, show_ep_wMaxPacketSize, NULL); diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c index ce22f4a84ed0..a004db35f6d0 100644 --- a/drivers/usb/core/hcd-pci.c +++ b/drivers/usb/core/hcd-pci.c @@ -242,7 +242,7 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) pci_set_master(dev); - retval = usb_add_hcd(hcd, dev->irq, IRQF_DISABLED | IRQF_SHARED); + retval = usb_add_hcd(hcd, dev->irq, IRQF_SHARED); if (retval != 0) goto unmap_registers; set_hs_companion(dev, hcd); diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 73cbbd85219f..b3b7d062906d 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -442,7 +442,11 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb) struct usb_ctrlrequest *cmd; u16 typeReq, wValue, wIndex, wLength; u8 *ubuf = urb->transfer_buffer; - u8 tbuf [sizeof (struct usb_hub_descriptor)] + /* + * tbuf should be as big as the BOS descriptor and + * the USB hub descriptor. + */ + u8 tbuf[USB_DT_BOS_SIZE + USB_DT_USB_SS_CAP_SIZE] __attribute__((aligned(4))); const u8 *bufp = tbuf; unsigned len = 0; @@ -562,6 +566,8 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb) else /* unsupported IDs --> "protocol stall" */ goto error; break; + case USB_DT_BOS << 8: + goto nongeneric; default: goto error; } @@ -596,6 +602,7 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb) /* CLASS REQUESTS (and errors) */ default: +nongeneric: /* non-generic request */ switch (typeReq) { case GetHubStatus: @@ -605,6 +612,9 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb) case GetHubDescriptor: len = sizeof (struct usb_hub_descriptor); break; + case DeviceRequest | USB_REQ_GET_DESCRIPTOR: + /* len is returned by hub_control */ + break; } status = hcd->driver->hub_control (hcd, typeReq, wValue, wIndex, @@ -615,7 +625,7 @@ error: status = -EPIPE; } - if (status) { + if (status < 0) { len = 0; if (status != -EPIPE) { dev_dbg (hcd->self.controller, @@ -624,6 +634,10 @@ error: typeReq, wValue, wIndex, wLength, status); } + } else if (status > 0) { + /* hub_control may return the length of data copied. */ + len = status; + status = 0; } if (len) { if (urb->transfer_buffer_length < len) @@ -2429,7 +2443,7 @@ int usb_add_hcd(struct usb_hcd *hcd, * but drivers can override it in reset() if needed, along with * recording the overall controller's system wakeup capability. */ - device_init_wakeup(&rhdev->dev, 1); + device_set_wakeup_capable(&rhdev->dev, 1); /* HCD_FLAG_RH_RUNNING doesn't matter until the root hub is * registered. But since the controller can die at any time, @@ -2478,6 +2492,13 @@ int usb_add_hcd(struct usb_hcd *hcd, } if (hcd->uses_new_polling && HCD_POLL_RH(hcd)) usb_hcd_poll_rh_status(hcd); + + /* + * Host controllers don't generate their own wakeup requests; + * they only forward requests from the root hub. Therefore + * controllers should always be enabled for remote wakeup. + */ + device_wakeup_enable(hcd->self.controller); return retval; error_create_attr_group: diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index a428aa080a36..d6cc83249341 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -1636,11 +1636,6 @@ void usb_disconnect(struct usb_device **pdev) int i; struct usb_hcd *hcd = bus_to_hcd(udev->bus); - if (!udev) { - pr_debug ("%s nodev\n", __func__); - return; - } - /* mark the device as inactive, so any further urb submissions for * this device (and any of its children) will fail immediately. * this quiesces everything except pending urbs. @@ -2030,11 +2025,23 @@ static unsigned hub_is_wusb(struct usb_hub *hub) #define HUB_ROOT_RESET_TIME 50 /* times are in msec */ #define HUB_SHORT_RESET_TIME 10 +#define HUB_BH_RESET_TIME 50 #define HUB_LONG_RESET_TIME 200 #define HUB_RESET_TIMEOUT 500 +static int hub_port_reset(struct usb_hub *hub, int port1, + struct usb_device *udev, unsigned int delay, bool warm); + +/* Is a USB 3.0 port in the Inactive state? */ +static bool hub_port_inactive(struct usb_hub *hub, u16 portstatus) +{ + return hub_is_superspeed(hub->hdev) && + (portstatus & USB_PORT_STAT_LINK_STATE) == + USB_SS_PORT_LS_SS_INACTIVE; +} + static int hub_port_wait_reset(struct usb_hub *hub, int port1, - struct usb_device *udev, unsigned int delay) + struct usb_device *udev, unsigned int delay, bool warm) { int delay_time, ret; u16 portstatus; @@ -2051,28 +2058,71 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1, if (ret < 0) return ret; - /* Device went away? */ - if (!(portstatus & USB_PORT_STAT_CONNECTION)) - return -ENOTCONN; - - /* bomb out completely if the connection bounced */ - if ((portchange & USB_PORT_STAT_C_CONNECTION)) - return -ENOTCONN; - - /* if we`ve finished resetting, then break out of the loop */ - if (!(portstatus & USB_PORT_STAT_RESET) && - (portstatus & USB_PORT_STAT_ENABLE)) { - if (hub_is_wusb(hub)) - udev->speed = USB_SPEED_WIRELESS; - else if (hub_is_superspeed(hub->hdev)) - udev->speed = USB_SPEED_SUPER; - else if (portstatus & USB_PORT_STAT_HIGH_SPEED) - udev->speed = USB_SPEED_HIGH; - else if (portstatus & USB_PORT_STAT_LOW_SPEED) - udev->speed = USB_SPEED_LOW; - else - udev->speed = USB_SPEED_FULL; - return 0; + /* + * Some buggy devices require a warm reset to be issued even + * when the port appears not to be connected. + */ + if (!warm) { + /* + * Some buggy devices can cause an NEC host controller + * to transition to the "Error" state after a hot port + * reset. This will show up as the port state in + * "Inactive", and the port may also report a + * disconnect. Forcing a warm port reset seems to make + * the device work. + * + * See https://bugzilla.kernel.org/show_bug.cgi?id=41752 + */ + if (hub_port_inactive(hub, portstatus)) { + int ret; + + if ((portchange & USB_PORT_STAT_C_CONNECTION)) + clear_port_feature(hub->hdev, port1, + USB_PORT_FEAT_C_CONNECTION); + if (portchange & USB_PORT_STAT_C_LINK_STATE) + clear_port_feature(hub->hdev, port1, + USB_PORT_FEAT_C_PORT_LINK_STATE); + if (portchange & USB_PORT_STAT_C_RESET) + clear_port_feature(hub->hdev, port1, + USB_PORT_FEAT_C_RESET); + dev_dbg(hub->intfdev, "hot reset failed, warm reset port %d\n", + port1); + ret = hub_port_reset(hub, port1, + udev, HUB_BH_RESET_TIME, + true); + if ((portchange & USB_PORT_STAT_C_CONNECTION)) + clear_port_feature(hub->hdev, port1, + USB_PORT_FEAT_C_CONNECTION); + return ret; + } + /* Device went away? */ + if (!(portstatus & USB_PORT_STAT_CONNECTION)) + return -ENOTCONN; + + /* bomb out completely if the connection bounced */ + if ((portchange & USB_PORT_STAT_C_CONNECTION)) + return -ENOTCONN; + + /* if we`ve finished resetting, then break out of + * the loop + */ + if (!(portstatus & USB_PORT_STAT_RESET) && + (portstatus & USB_PORT_STAT_ENABLE)) { + if (hub_is_wusb(hub)) + udev->speed = USB_SPEED_WIRELESS; + else if (hub_is_superspeed(hub->hdev)) + udev->speed = USB_SPEED_SUPER; + else if (portstatus & USB_PORT_STAT_HIGH_SPEED) + udev->speed = USB_SPEED_HIGH; + else if (portstatus & USB_PORT_STAT_LOW_SPEED) + udev->speed = USB_SPEED_LOW; + else + udev->speed = USB_SPEED_FULL; + return 0; + } + } else { + if (portchange & USB_PORT_STAT_C_BH_RESET) + return 0; } /* switch to the long delay after two short delay failures */ @@ -2080,35 +2130,84 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1, delay = HUB_LONG_RESET_TIME; dev_dbg (hub->intfdev, - "port %d not reset yet, waiting %dms\n", - port1, delay); + "port %d not %sreset yet, waiting %dms\n", + port1, warm ? "warm " : "", delay); } return -EBUSY; } +static void hub_port_finish_reset(struct usb_hub *hub, int port1, + struct usb_device *udev, int *status, bool warm) +{ + switch (*status) { + case 0: + if (!warm) { + struct usb_hcd *hcd; + /* TRSTRCY = 10 ms; plus some extra */ + msleep(10 + 40); + update_devnum(udev, 0); + hcd = bus_to_hcd(udev->bus); + if (hcd->driver->reset_device) { + *status = hcd->driver->reset_device(hcd, udev); + if (*status < 0) { + dev_err(&udev->dev, "Cannot reset " + "HCD device state\n"); + break; + } + } + } + /* FALL THROUGH */ + case -ENOTCONN: + case -ENODEV: + clear_port_feature(hub->hdev, + port1, USB_PORT_FEAT_C_RESET); + /* FIXME need disconnect() for NOTATTACHED device */ + if (warm) { + clear_port_feature(hub->hdev, port1, + USB_PORT_FEAT_C_BH_PORT_RESET); + clear_port_feature(hub->hdev, port1, + USB_PORT_FEAT_C_PORT_LINK_STATE); + } else { + usb_set_device_state(udev, *status + ? USB_STATE_NOTATTACHED + : USB_STATE_DEFAULT); + } + break; + } +} + +/* Handle port reset and port warm(BH) reset (for USB3 protocol ports) */ static int hub_port_reset(struct usb_hub *hub, int port1, - struct usb_device *udev, unsigned int delay) + struct usb_device *udev, unsigned int delay, bool warm) { int i, status; - struct usb_hcd *hcd; - hcd = bus_to_hcd(udev->bus); - /* Block EHCI CF initialization during the port reset. - * Some companion controllers don't like it when they mix. - */ - down_read(&ehci_cf_port_reset_rwsem); + if (!warm) { + /* Block EHCI CF initialization during the port reset. + * Some companion controllers don't like it when they mix. + */ + down_read(&ehci_cf_port_reset_rwsem); + } else { + if (!hub_is_superspeed(hub->hdev)) { + dev_err(hub->intfdev, "only USB3 hub support " + "warm reset\n"); + return -EINVAL; + } + } /* Reset the port */ for (i = 0; i < PORT_RESET_TRIES; i++) { - status = set_port_feature(hub->hdev, - port1, USB_PORT_FEAT_RESET); - if (status) + status = set_port_feature(hub->hdev, port1, (warm ? + USB_PORT_FEAT_BH_PORT_RESET : + USB_PORT_FEAT_RESET)); + if (status) { dev_err(hub->intfdev, - "cannot reset port %d (err = %d)\n", - port1, status); - else { - status = hub_port_wait_reset(hub, port1, udev, delay); + "cannot %sreset port %d (err = %d)\n", + warm ? "warm " : "", port1, status); + } else { + status = hub_port_wait_reset(hub, port1, udev, delay, + warm); if (status && status != -ENOTCONN) dev_dbg(hub->intfdev, "port_wait_reset: err = %d\n", @@ -2116,34 +2215,14 @@ static int hub_port_reset(struct usb_hub *hub, int port1, } /* return on disconnect or reset */ - switch (status) { - case 0: - /* TRSTRCY = 10 ms; plus some extra */ - msleep(10 + 40); - update_devnum(udev, 0); - if (hcd->driver->reset_device) { - status = hcd->driver->reset_device(hcd, udev); - if (status < 0) { - dev_err(&udev->dev, "Cannot reset " - "HCD device state\n"); - break; - } - } - /* FALL THROUGH */ - case -ENOTCONN: - case -ENODEV: - clear_port_feature(hub->hdev, - port1, USB_PORT_FEAT_C_RESET); - /* FIXME need disconnect() for NOTATTACHED device */ - usb_set_device_state(udev, status - ? USB_STATE_NOTATTACHED - : USB_STATE_DEFAULT); + if (status == 0 || status == -ENOTCONN || status == -ENODEV) { + hub_port_finish_reset(hub, port1, udev, &status, warm); goto done; } dev_dbg (hub->intfdev, - "port %d not enabled, trying reset again...\n", - port1); + "port %d not enabled, trying %sreset again...\n", + port1, warm ? "warm " : ""); delay = HUB_LONG_RESET_TIME; } @@ -2151,45 +2230,11 @@ static int hub_port_reset(struct usb_hub *hub, int port1, "Cannot enable port %i. Maybe the USB cable is bad?\n", port1); - done: - up_read(&ehci_cf_port_reset_rwsem); - return status; -} - -/* Warm reset a USB3 protocol port */ -static int hub_port_warm_reset(struct usb_hub *hub, int port) -{ - int ret; - u16 portstatus, portchange; - - if (!hub_is_superspeed(hub->hdev)) { - dev_err(hub->intfdev, "only USB3 hub support warm reset\n"); - return -EINVAL; - } - - /* Warm reset the port */ - ret = set_port_feature(hub->hdev, - port, USB_PORT_FEAT_BH_PORT_RESET); - if (ret) { - dev_err(hub->intfdev, "cannot warm reset port %d\n", port); - return ret; - } - - msleep(20); - ret = hub_port_status(hub, port, &portstatus, &portchange); - - if (portchange & USB_PORT_STAT_C_RESET) - clear_port_feature(hub->hdev, port, USB_PORT_FEAT_C_RESET); - - if (portchange & USB_PORT_STAT_C_BH_RESET) - clear_port_feature(hub->hdev, port, - USB_PORT_FEAT_C_BH_PORT_RESET); - - if (portchange & USB_PORT_STAT_C_LINK_STATE) - clear_port_feature(hub->hdev, port, - USB_PORT_FEAT_C_PORT_LINK_STATE); +done: + if (!warm) + up_read(&ehci_cf_port_reset_rwsem); - return ret; + return status; } /* Check if a port is power on */ @@ -2347,6 +2392,10 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg) } } + /* disable USB2 hardware LPM */ + if (udev->usb2_hw_lpm_enabled == 1) + usb_set_usb2_hardware_lpm(udev, 0); + /* see 7.1.7.6 */ if (hub_is_superspeed(hub->hdev)) status = set_port_feature(hub->hdev, @@ -2558,7 +2607,12 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg) if (status < 0) { dev_dbg(&udev->dev, "can't resume, status %d\n", status); hub_port_logical_disconnect(hub, port1); + } else { + /* Try to enable USB2 hardware LPM */ + if (udev->usb2_hw_lpm_capable == 1) + usb_set_usb2_hardware_lpm(udev, 1); } + return status; } @@ -2798,7 +2852,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, int i, j, retval; unsigned delay = HUB_SHORT_RESET_TIME; enum usb_device_speed oldspeed = udev->speed; - char *speed, *type; + const char *speed; int devnum = udev->devnum; /* root hub ports have a slightly longer reset period @@ -2819,7 +2873,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, /* Reset the device; full speed may morph to high speed */ /* FIXME a USB 2.0 device may morph into SuperSpeed on reset. */ - retval = hub_port_reset(hub, port1, udev, delay); + retval = hub_port_reset(hub, port1, udev, delay, false); if (retval < 0) /* error or disconnect */ goto fail; /* success, speed is known */ @@ -2858,25 +2912,16 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, default: goto fail; } - - type = ""; - switch (udev->speed) { - case USB_SPEED_LOW: speed = "low"; break; - case USB_SPEED_FULL: speed = "full"; break; - case USB_SPEED_HIGH: speed = "high"; break; - case USB_SPEED_SUPER: - speed = "super"; - break; - case USB_SPEED_WIRELESS: - speed = "variable"; - type = "Wireless "; - break; - default: speed = "?"; break; - } + + if (udev->speed == USB_SPEED_WIRELESS) + speed = "variable speed Wireless"; + else + speed = usb_speed_string(udev->speed); + if (udev->speed != USB_SPEED_SUPER) dev_info(&udev->dev, - "%s %s speed %sUSB device number %d using %s\n", - (udev->config) ? "reset" : "new", speed, type, + "%s %s USB device number %d using %s\n", + (udev->config) ? "reset" : "new", speed, devnum, udev->bus->controller->driver->name); /* Set up TT records, if needed */ @@ -2949,7 +2994,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, buf->bMaxPacketSize0; kfree(buf); - retval = hub_port_reset(hub, port1, udev, delay); + retval = hub_port_reset(hub, port1, udev, delay, false); if (retval < 0) /* error or disconnect */ goto fail; if (oldspeed != udev->speed) { @@ -3023,7 +3068,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, i = 512; else i = udev->descriptor.bMaxPacketSize0; - if (le16_to_cpu(udev->ep0.desc.wMaxPacketSize) != i) { + if (usb_endpoint_maxp(&udev->ep0.desc) != i) { if (udev->speed == USB_SPEED_LOW || !(i == 8 || i == 16 || i == 32 || i == 64)) { dev_err(&udev->dev, "Invalid ep0 maxpacket: %d\n", i); @@ -3047,6 +3092,15 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, goto fail; } + if (udev->wusb == 0 && le16_to_cpu(udev->descriptor.bcdUSB) >= 0x0201) { + retval = usb_get_bos_descriptor(udev); + if (!retval) { + if (udev->bos->ext_cap && (USB_LPM_SUPPORT & + le32_to_cpu(udev->bos->ext_cap->bmAttributes))) + udev->lpm_capable = 1; + } + } + retval = 0; /* notify HCD that we have a device connected and addressed */ if (hcd->driver->update_device) @@ -3570,7 +3624,8 @@ static void hub_events(void) (portstatus & USB_PORT_STAT_LINK_STATE) == USB_SS_PORT_LS_SS_INACTIVE) { dev_dbg(hub_dev, "warm reset port %d\n", i); - hub_port_warm_reset(hub, i); + hub_port_reset(hub, i, NULL, + HUB_BH_RESET_TIME, true); } if (connect_change) diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index 0b5ec234c787..b3bdfede45e6 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -435,7 +435,7 @@ int usb_sg_init(struct usb_sg_request *io, struct usb_device *dev, len = sg->length; if (length) { - len = min_t(unsigned, len, length); + len = min_t(size_t, len, length); length -= len; if (length == 0) io->entries = i + 1; diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c index 81ce6a8e1d94..d6a8d8269bfb 100644 --- a/drivers/usb/core/quirks.c +++ b/drivers/usb/core/quirks.c @@ -38,6 +38,27 @@ static const struct usb_device_id usb_quirk_list[] = { /* Creative SB Audigy 2 NX */ { USB_DEVICE(0x041e, 0x3020), .driver_info = USB_QUIRK_RESET_RESUME }, + /* Logitech Webcam C200 */ + { USB_DEVICE(0x046d, 0x0802), .driver_info = USB_QUIRK_RESET_RESUME }, + + /* Logitech Webcam C250 */ + { USB_DEVICE(0x046d, 0x0804), .driver_info = USB_QUIRK_RESET_RESUME }, + + /* Logitech Webcam C300 */ + { USB_DEVICE(0x046d, 0x0805), .driver_info = USB_QUIRK_RESET_RESUME }, + + /* Logitech Webcam B/C500 */ + { USB_DEVICE(0x046d, 0x0807), .driver_info = USB_QUIRK_RESET_RESUME }, + + /* Logitech Webcam Pro 9000 */ + { USB_DEVICE(0x046d, 0x0809), .driver_info = USB_QUIRK_RESET_RESUME }, + + /* Logitech Webcam C310 */ + { USB_DEVICE(0x046d, 0x081b), .driver_info = USB_QUIRK_RESET_RESUME }, + + /* Logitech Webcam C270 */ + { USB_DEVICE(0x046d, 0x0825), .driver_info = USB_QUIRK_RESET_RESUME }, + /* Logitech Harmony 700-series */ { USB_DEVICE(0x046d, 0xc122), .driver_info = USB_QUIRK_DELAY_INIT }, @@ -69,6 +90,9 @@ static const struct usb_device_id usb_quirk_list[] = { { USB_DEVICE(0x06a3, 0x0006), .driver_info = USB_QUIRK_CONFIG_INTF_STRINGS }, + /* Guillemot Webcam Hercules Dualpix Exchange*/ + { USB_DEVICE(0x06f8, 0x0804), .driver_info = USB_QUIRK_RESET_RESUME }, + /* M-Systems Flash Disk Pioneers */ { USB_DEVICE(0x08ec, 0x1000), .driver_info = USB_QUIRK_RESET_RESUME }, diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c index cf05b97693ea..662c0cf3a3e1 100644 --- a/drivers/usb/core/sysfs.c +++ b/drivers/usb/core/sysfs.c @@ -412,6 +412,56 @@ set_level(struct device *dev, struct device_attribute *attr, static DEVICE_ATTR(level, S_IRUGO | S_IWUSR, show_level, set_level); +static ssize_t +show_usb2_hardware_lpm(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct usb_device *udev = to_usb_device(dev); + const char *p; + + if (udev->usb2_hw_lpm_enabled == 1) + p = "enabled"; + else + p = "disabled"; + + return sprintf(buf, "%s\n", p); +} + +static ssize_t +set_usb2_hardware_lpm(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct usb_device *udev = to_usb_device(dev); + bool value; + int ret; + + usb_lock_device(udev); + + ret = strtobool(buf, &value); + + if (!ret) + ret = usb_set_usb2_hardware_lpm(udev, value); + + usb_unlock_device(udev); + + if (!ret) + return count; + + return ret; +} + +static DEVICE_ATTR(usb2_hardware_lpm, S_IRUGO | S_IWUSR, show_usb2_hardware_lpm, + set_usb2_hardware_lpm); + +static struct attribute *usb2_hardware_lpm_attr[] = { + &dev_attr_usb2_hardware_lpm.attr, + NULL, +}; +static struct attribute_group usb2_hardware_lpm_attr_group = { + .name = power_group_name, + .attrs = usb2_hardware_lpm_attr, +}; + static struct attribute *power_attrs[] = { &dev_attr_autosuspend.attr, &dev_attr_level.attr, @@ -428,13 +478,20 @@ static int add_power_attributes(struct device *dev) { int rc = 0; - if (is_usb_device(dev)) + if (is_usb_device(dev)) { + struct usb_device *udev = to_usb_device(dev); rc = sysfs_merge_group(&dev->kobj, &power_attr_group); + if (udev->usb2_hw_lpm_capable == 1) + rc = sysfs_merge_group(&dev->kobj, + &usb2_hardware_lpm_attr_group); + } + return rc; } static void remove_power_attributes(struct device *dev) { + sysfs_unmerge_group(&dev->kobj, &usb2_hardware_lpm_attr_group); sysfs_unmerge_group(&dev->kobj, &power_attr_group); } diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c index ae334b067c13..909625b91eb3 100644 --- a/drivers/usb/core/urb.c +++ b/drivers/usb/core/urb.c @@ -350,7 +350,7 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags) dev->state < USB_STATE_CONFIGURED) return -ENODEV; - max = le16_to_cpu(ep->desc.wMaxPacketSize); + max = usb_endpoint_maxp(&ep->desc); if (max <= 0) { dev_dbg(&dev->dev, "bogus endpoint ep%d%s in %s (bad maxpacket %d)\n", diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 8706fc97e60f..73cd90012ec5 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -225,6 +225,7 @@ static void usb_release_dev(struct device *dev) hcd = bus_to_hcd(udev->bus); usb_destroy_configuration(udev); + usb_release_bos_descriptor(udev); usb_put_hcd(hcd); kfree(udev->product); kfree(udev->manufacturer); diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index d44d4b7bbf17..3888778582c4 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -28,6 +28,8 @@ extern int usb_remove_device(struct usb_device *udev); extern int usb_get_device_descriptor(struct usb_device *dev, unsigned int size); +extern int usb_get_bos_descriptor(struct usb_device *dev); +extern void usb_release_bos_descriptor(struct usb_device *dev); extern char *usb_cache_string(struct usb_device *udev, int index); extern int usb_set_configuration(struct usb_device *dev, int configuration); extern int usb_choose_configuration(struct usb_device *udev); @@ -80,6 +82,7 @@ extern int usb_remote_wakeup(struct usb_device *dev); extern int usb_runtime_suspend(struct device *dev); extern int usb_runtime_resume(struct device *dev); extern int usb_runtime_idle(struct device *dev); +extern int usb_set_usb2_hardware_lpm(struct usb_device *udev, int enable); #else @@ -94,6 +97,10 @@ static inline int usb_remote_wakeup(struct usb_device *udev) return 0; } +static inline int usb_set_usb2_hardware_lpm(struct usb_device *udev, int enable) +{ + return 0; +} #endif extern struct bus_type usb_bus_type; diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig new file mode 100644 index 000000000000..3c1d67d324fd --- /dev/null +++ b/drivers/usb/dwc3/Kconfig @@ -0,0 +1,25 @@ +config USB_DWC3 + tristate "DesignWare USB3 DRD Core Support" + depends on (USB || USB_GADGET) + select USB_OTG_UTILS + help + Say Y or M here if your system has a Dual Role SuperSpeed + USB controller based on the DesignWare USB3 IP Core. + + If you choose to build this driver is a dynamically linked + module, the module will be called dwc3.ko. + +if USB_DWC3 + +config USB_DWC3_DEBUG + bool "Enable Debugging Messages" + help + Say Y here to enable debugging messages on DWC3 Driver. + +config USB_DWC3_VERBOSE + bool "Enable Verbose Debugging Messages" + depends on USB_DWC3_DEBUG + help + Say Y here to enable verbose debugging messages on DWC3 Driver. + +endif diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile new file mode 100644 index 000000000000..593d1dbc465b --- /dev/null +++ b/drivers/usb/dwc3/Makefile @@ -0,0 +1,36 @@ +ccflags-$(CONFIG_USB_DWC3_DEBUG) := -DDEBUG +ccflags-$(CONFIG_USB_DWC3_VERBOSE) += -DVERBOSE_DEBUG + +obj-$(CONFIG_USB_DWC3) += dwc3.o + +dwc3-y := core.o + +ifneq ($(CONFIG_USB_GADGET_DWC3),) + dwc3-y += gadget.o ep0.o +endif + +ifneq ($(CONFIG_DEBUG_FS),) + dwc3-y += debugfs.o +endif + +## +# Platform-specific glue layers go here +# +# NOTICE: Make sure your glue layer doesn't depend on anything +# which is arch-specific and that it compiles on all situations. +# +# We want to keep this requirement in order to be able to compile +# the entire driver (with all its glue layers) on several architectures +# and make sure it compiles fine. This will also help with allmodconfig +# and allyesconfig builds. +# +# The only exception is the PCI glue layer, but that's only because +# PCI doesn't provide nops if CONFIG_PCI isn't enabled. +## + +obj-$(CONFIG_USB_DWC3) += dwc3-omap.o + +ifneq ($(CONFIG_PCI),) + obj-$(CONFIG_USB_DWC3) += dwc3-pci.o +endif + diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c new file mode 100644 index 000000000000..717ebc9ff941 --- /dev/null +++ b/drivers/usb/dwc3/core.c @@ -0,0 +1,484 @@ +/** + * core.c - DesignWare USB3 DRD Controller Core file + * + * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com + * + * Authors: Felipe Balbi <balbi@ti.com>, + * Sebastian Andrzej Siewior <bigeasy@linutronix.de> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The names of the above-listed copyright holders may not be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2, as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/spinlock.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/interrupt.h> +#include <linux/ioport.h> +#include <linux/io.h> +#include <linux/list.h> +#include <linux/delay.h> +#include <linux/dma-mapping.h> + +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> +#include <linux/module.h> + +#include "core.h" +#include "gadget.h" +#include "io.h" + +#include "debug.h" + +/** + * dwc3_core_soft_reset - Issues core soft reset and PHY reset + * @dwc: pointer to our context structure + */ +static void dwc3_core_soft_reset(struct dwc3 *dwc) +{ + u32 reg; + + /* Before Resetting PHY, put Core in Reset */ + reg = dwc3_readl(dwc->regs, DWC3_GCTL); + reg |= DWC3_GCTL_CORESOFTRESET; + dwc3_writel(dwc->regs, DWC3_GCTL, reg); + + /* Assert USB3 PHY reset */ + reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0)); + reg |= DWC3_GUSB3PIPECTL_PHYSOFTRST; + dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg); + + /* Assert USB2 PHY reset */ + reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); + reg |= DWC3_GUSB2PHYCFG_PHYSOFTRST; + dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); + + mdelay(100); + + /* Clear USB3 PHY reset */ + reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0)); + reg &= ~DWC3_GUSB3PIPECTL_PHYSOFTRST; + dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg); + + /* Clear USB2 PHY reset */ + reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); + reg &= ~DWC3_GUSB2PHYCFG_PHYSOFTRST; + dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); + + /* After PHYs are stable we can take Core out of reset state */ + reg = dwc3_readl(dwc->regs, DWC3_GCTL); + reg &= ~DWC3_GCTL_CORESOFTRESET; + dwc3_writel(dwc->regs, DWC3_GCTL, reg); +} + +/** + * dwc3_free_one_event_buffer - Frees one event buffer + * @dwc: Pointer to our controller context structure + * @evt: Pointer to event buffer to be freed + */ +static void dwc3_free_one_event_buffer(struct dwc3 *dwc, + struct dwc3_event_buffer *evt) +{ + dma_free_coherent(dwc->dev, evt->length, evt->buf, evt->dma); + kfree(evt); +} + +/** + * dwc3_alloc_one_event_buffer - Allocated one event buffer structure + * @dwc: Pointer to our controller context structure + * @length: size of the event buffer + * + * Returns a pointer to the allocated event buffer structure on succes + * otherwise ERR_PTR(errno). + */ +static struct dwc3_event_buffer *__devinit +dwc3_alloc_one_event_buffer(struct dwc3 *dwc, unsigned length) +{ + struct dwc3_event_buffer *evt; + + evt = kzalloc(sizeof(*evt), GFP_KERNEL); + if (!evt) + return ERR_PTR(-ENOMEM); + + evt->dwc = dwc; + evt->length = length; + evt->buf = dma_alloc_coherent(dwc->dev, length, + &evt->dma, GFP_KERNEL); + if (!evt->buf) { + kfree(evt); + return ERR_PTR(-ENOMEM); + } + + return evt; +} + +/** + * dwc3_free_event_buffers - frees all allocated event buffers + * @dwc: Pointer to our controller context structure + */ +static void dwc3_free_event_buffers(struct dwc3 *dwc) +{ + struct dwc3_event_buffer *evt; + int i; + + for (i = 0; i < DWC3_EVENT_BUFFERS_NUM; i++) { + evt = dwc->ev_buffs[i]; + if (evt) { + dwc3_free_one_event_buffer(dwc, evt); + dwc->ev_buffs[i] = NULL; + } + } +} + +/** + * dwc3_alloc_event_buffers - Allocates @num event buffers of size @length + * @dwc: Pointer to out controller context structure + * @num: number of event buffers to allocate + * @length: size of event buffer + * + * Returns 0 on success otherwise negative errno. In error the case, dwc + * may contain some buffers allocated but not all which were requested. + */ +static int __devinit dwc3_alloc_event_buffers(struct dwc3 *dwc, unsigned num, + unsigned length) +{ + int i; + + for (i = 0; i < num; i++) { + struct dwc3_event_buffer *evt; + + evt = dwc3_alloc_one_event_buffer(dwc, length); + if (IS_ERR(evt)) { + dev_err(dwc->dev, "can't allocate event buffer\n"); + return PTR_ERR(evt); + } + dwc->ev_buffs[i] = evt; + } + + return 0; +} + +/** + * dwc3_event_buffers_setup - setup our allocated event buffers + * @dwc: Pointer to out controller context structure + * + * Returns 0 on success otherwise negative errno. + */ +static int __devinit dwc3_event_buffers_setup(struct dwc3 *dwc) +{ + struct dwc3_event_buffer *evt; + int n; + + for (n = 0; n < DWC3_EVENT_BUFFERS_NUM; n++) { + evt = dwc->ev_buffs[n]; + dev_dbg(dwc->dev, "Event buf %p dma %08llx length %d\n", + evt->buf, (unsigned long long) evt->dma, + evt->length); + + dwc3_writel(dwc->regs, DWC3_GEVNTADRLO(n), + lower_32_bits(evt->dma)); + dwc3_writel(dwc->regs, DWC3_GEVNTADRHI(n), + upper_32_bits(evt->dma)); + dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(n), + evt->length & 0xffff); + dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(n), 0); + } + + return 0; +} + +static void dwc3_event_buffers_cleanup(struct dwc3 *dwc) +{ + struct dwc3_event_buffer *evt; + int n; + + for (n = 0; n < DWC3_EVENT_BUFFERS_NUM; n++) { + evt = dwc->ev_buffs[n]; + dwc3_writel(dwc->regs, DWC3_GEVNTADRLO(n), 0); + dwc3_writel(dwc->regs, DWC3_GEVNTADRHI(n), 0); + dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(n), 0); + dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(n), 0); + } +} + +static void __devinit dwc3_cache_hwparams(struct dwc3 *dwc) +{ + struct dwc3_hwparams *parms = &dwc->hwparams; + + parms->hwparams0 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS0); + parms->hwparams1 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS1); + parms->hwparams2 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS2); + parms->hwparams3 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS3); + parms->hwparams4 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS4); + parms->hwparams5 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS5); + parms->hwparams6 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS6); + parms->hwparams7 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS7); + parms->hwparams8 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS8); +} + +/** + * dwc3_core_init - Low-level initialization of DWC3 Core + * @dwc: Pointer to our controller context structure + * + * Returns 0 on success otherwise negative errno. + */ +static int __devinit dwc3_core_init(struct dwc3 *dwc) +{ + unsigned long timeout; + u32 reg; + int ret; + + reg = dwc3_readl(dwc->regs, DWC3_GSNPSID); + /* This should read as U3 followed by revision number */ + if ((reg & DWC3_GSNPSID_MASK) != 0x55330000) { + dev_err(dwc->dev, "this is not a DesignWare USB3 DRD Core\n"); + ret = -ENODEV; + goto err0; + } + dwc->revision = reg & DWC3_GSNPSREV_MASK; + + dwc3_core_soft_reset(dwc); + + /* issue device SoftReset too */ + timeout = jiffies + msecs_to_jiffies(500); + dwc3_writel(dwc->regs, DWC3_DCTL, DWC3_DCTL_CSFTRST); + do { + reg = dwc3_readl(dwc->regs, DWC3_DCTL); + if (!(reg & DWC3_DCTL_CSFTRST)) + break; + + if (time_after(jiffies, timeout)) { + dev_err(dwc->dev, "Reset Timed Out\n"); + ret = -ETIMEDOUT; + goto err0; + } + + cpu_relax(); + } while (true); + + ret = dwc3_alloc_event_buffers(dwc, DWC3_EVENT_BUFFERS_NUM, + DWC3_EVENT_BUFFERS_SIZE); + if (ret) { + dev_err(dwc->dev, "failed to allocate event buffers\n"); + ret = -ENOMEM; + goto err1; + } + + ret = dwc3_event_buffers_setup(dwc); + if (ret) { + dev_err(dwc->dev, "failed to setup event buffers\n"); + goto err1; + } + + dwc3_cache_hwparams(dwc); + + return 0; + +err1: + dwc3_free_event_buffers(dwc); + +err0: + return ret; +} + +static void dwc3_core_exit(struct dwc3 *dwc) +{ + dwc3_event_buffers_cleanup(dwc); + dwc3_free_event_buffers(dwc); +} + +#define DWC3_ALIGN_MASK (16 - 1) + +static int __devinit dwc3_probe(struct platform_device *pdev) +{ + const struct platform_device_id *id = platform_get_device_id(pdev); + struct resource *res; + struct dwc3 *dwc; + void __iomem *regs; + unsigned int features = id->driver_data; + int ret = -ENOMEM; + int irq; + void *mem; + + mem = kzalloc(sizeof(*dwc) + DWC3_ALIGN_MASK, GFP_KERNEL); + if (!mem) { + dev_err(&pdev->dev, "not enough memory\n"); + goto err0; + } + dwc = PTR_ALIGN(mem, DWC3_ALIGN_MASK + 1); + dwc->mem = mem; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "missing resource\n"); + goto err1; + } + + res = request_mem_region(res->start, resource_size(res), + dev_name(&pdev->dev)); + if (!res) { + dev_err(&pdev->dev, "can't request mem region\n"); + goto err1; + } + + regs = ioremap(res->start, resource_size(res)); + if (!regs) { + dev_err(&pdev->dev, "ioremap failed\n"); + goto err2; + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "missing IRQ\n"); + goto err3; + } + + spin_lock_init(&dwc->lock); + platform_set_drvdata(pdev, dwc); + + dwc->regs = regs; + dwc->regs_size = resource_size(res); + dwc->dev = &pdev->dev; + dwc->irq = irq; + + pm_runtime_enable(&pdev->dev); + pm_runtime_get_sync(&pdev->dev); + pm_runtime_forbid(&pdev->dev); + + ret = dwc3_core_init(dwc); + if (ret) { + dev_err(&pdev->dev, "failed to initialize core\n"); + goto err3; + } + + if (features & DWC3_HAS_PERIPHERAL) { + ret = dwc3_gadget_init(dwc); + if (ret) { + dev_err(&pdev->dev, "failed to initialized gadget\n"); + goto err4; + } + } + + ret = dwc3_debugfs_init(dwc); + if (ret) { + dev_err(&pdev->dev, "failed to initialize debugfs\n"); + goto err5; + } + + pm_runtime_allow(&pdev->dev); + + return 0; + +err5: + if (features & DWC3_HAS_PERIPHERAL) + dwc3_gadget_exit(dwc); + +err4: + dwc3_core_exit(dwc); + +err3: + iounmap(regs); + +err2: + release_mem_region(res->start, resource_size(res)); + +err1: + kfree(dwc->mem); + +err0: + return ret; +} + +static int __devexit dwc3_remove(struct platform_device *pdev) +{ + const struct platform_device_id *id = platform_get_device_id(pdev); + struct dwc3 *dwc = platform_get_drvdata(pdev); + struct resource *res; + unsigned int features = id->driver_data; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + pm_runtime_put(&pdev->dev); + pm_runtime_disable(&pdev->dev); + + dwc3_debugfs_exit(dwc); + + if (features & DWC3_HAS_PERIPHERAL) + dwc3_gadget_exit(dwc); + + dwc3_core_exit(dwc); + release_mem_region(res->start, resource_size(res)); + iounmap(dwc->regs); + kfree(dwc->mem); + + return 0; +} + +static const struct platform_device_id dwc3_id_table[] __devinitconst = { + { + .name = "dwc3-omap", + .driver_data = (DWC3_HAS_PERIPHERAL + | DWC3_HAS_XHCI + | DWC3_HAS_OTG), + }, + { + .name = "dwc3-pci", + .driver_data = DWC3_HAS_PERIPHERAL, + }, + { }, /* Terminating Entry */ +}; +MODULE_DEVICE_TABLE(platform, dwc3_id_table); + +static struct platform_driver dwc3_driver = { + .probe = dwc3_probe, + .remove = __devexit_p(dwc3_remove), + .driver = { + .name = "dwc3", + }, + .id_table = dwc3_id_table, +}; + +MODULE_AUTHOR("Felipe Balbi <balbi@ti.com>"); +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_DESCRIPTION("DesignWare USB3 DRD Controller Driver"); + +static int __devinit dwc3_init(void) +{ + return platform_driver_register(&dwc3_driver); +} +module_init(dwc3_init); + +static void __exit dwc3_exit(void) +{ + platform_driver_unregister(&dwc3_driver); +} +module_exit(dwc3_exit); diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h new file mode 100644 index 000000000000..29a8e1679e12 --- /dev/null +++ b/drivers/usb/dwc3/core.h @@ -0,0 +1,768 @@ +/** + * core.h - DesignWare USB3 DRD Core Header + * + * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com + * + * Authors: Felipe Balbi <balbi@ti.com>, + * Sebastian Andrzej Siewior <bigeasy@linutronix.de> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The names of the above-listed copyright holders may not be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2, as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __DRIVERS_USB_DWC3_CORE_H +#define __DRIVERS_USB_DWC3_CORE_H + +#include <linux/device.h> +#include <linux/spinlock.h> +#include <linux/list.h> +#include <linux/dma-mapping.h> +#include <linux/mm.h> +#include <linux/debugfs.h> + +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> + +/* Global constants */ +#define DWC3_ENDPOINTS_NUM 32 + +#define DWC3_EVENT_BUFFERS_NUM 2 +#define DWC3_EVENT_BUFFERS_SIZE PAGE_SIZE +#define DWC3_EVENT_TYPE_MASK 0xfe + +#define DWC3_EVENT_TYPE_DEV 0 +#define DWC3_EVENT_TYPE_CARKIT 3 +#define DWC3_EVENT_TYPE_I2C 4 + +#define DWC3_DEVICE_EVENT_DISCONNECT 0 +#define DWC3_DEVICE_EVENT_RESET 1 +#define DWC3_DEVICE_EVENT_CONNECT_DONE 2 +#define DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE 3 +#define DWC3_DEVICE_EVENT_WAKEUP 4 +#define DWC3_DEVICE_EVENT_EOPF 6 +#define DWC3_DEVICE_EVENT_SOF 7 +#define DWC3_DEVICE_EVENT_ERRATIC_ERROR 9 +#define DWC3_DEVICE_EVENT_CMD_CMPL 10 +#define DWC3_DEVICE_EVENT_OVERFLOW 11 + +#define DWC3_GEVNTCOUNT_MASK 0xfffc +#define DWC3_GSNPSID_MASK 0xffff0000 +#define DWC3_GSNPSREV_MASK 0xffff + +/* Global Registers */ +#define DWC3_GSBUSCFG0 0xc100 +#define DWC3_GSBUSCFG1 0xc104 +#define DWC3_GTXTHRCFG 0xc108 +#define DWC3_GRXTHRCFG 0xc10c +#define DWC3_GCTL 0xc110 +#define DWC3_GEVTEN 0xc114 +#define DWC3_GSTS 0xc118 +#define DWC3_GSNPSID 0xc120 +#define DWC3_GGPIO 0xc124 +#define DWC3_GUID 0xc128 +#define DWC3_GUCTL 0xc12c +#define DWC3_GBUSERRADDR0 0xc130 +#define DWC3_GBUSERRADDR1 0xc134 +#define DWC3_GPRTBIMAP0 0xc138 +#define DWC3_GPRTBIMAP1 0xc13c +#define DWC3_GHWPARAMS0 0xc140 +#define DWC3_GHWPARAMS1 0xc144 +#define DWC3_GHWPARAMS2 0xc148 +#define DWC3_GHWPARAMS3 0xc14c +#define DWC3_GHWPARAMS4 0xc150 +#define DWC3_GHWPARAMS5 0xc154 +#define DWC3_GHWPARAMS6 0xc158 +#define DWC3_GHWPARAMS7 0xc15c +#define DWC3_GDBGFIFOSPACE 0xc160 +#define DWC3_GDBGLTSSM 0xc164 +#define DWC3_GPRTBIMAP_HS0 0xc180 +#define DWC3_GPRTBIMAP_HS1 0xc184 +#define DWC3_GPRTBIMAP_FS0 0xc188 +#define DWC3_GPRTBIMAP_FS1 0xc18c + +#define DWC3_GUSB2PHYCFG(n) (0xc200 + (n * 0x04)) +#define DWC3_GUSB2I2CCTL(n) (0xc240 + (n * 0x04)) + +#define DWC3_GUSB2PHYACC(n) (0xc280 + (n * 0x04)) + +#define DWC3_GUSB3PIPECTL(n) (0xc2c0 + (n * 0x04)) + +#define DWC3_GTXFIFOSIZ(n) (0xc300 + (n * 0x04)) +#define DWC3_GRXFIFOSIZ(n) (0xc380 + (n * 0x04)) + +#define DWC3_GEVNTADRLO(n) (0xc400 + (n * 0x10)) +#define DWC3_GEVNTADRHI(n) (0xc404 + (n * 0x10)) +#define DWC3_GEVNTSIZ(n) (0xc408 + (n * 0x10)) +#define DWC3_GEVNTCOUNT(n) (0xc40c + (n * 0x10)) + +#define DWC3_GHWPARAMS8 0xc600 + +/* Device Registers */ +#define DWC3_DCFG 0xc700 +#define DWC3_DCTL 0xc704 +#define DWC3_DEVTEN 0xc708 +#define DWC3_DSTS 0xc70c +#define DWC3_DGCMDPAR 0xc710 +#define DWC3_DGCMD 0xc714 +#define DWC3_DALEPENA 0xc720 +#define DWC3_DEPCMDPAR2(n) (0xc800 + (n * 0x10)) +#define DWC3_DEPCMDPAR1(n) (0xc804 + (n * 0x10)) +#define DWC3_DEPCMDPAR0(n) (0xc808 + (n * 0x10)) +#define DWC3_DEPCMD(n) (0xc80c + (n * 0x10)) + +/* OTG Registers */ +#define DWC3_OCFG 0xcc00 +#define DWC3_OCTL 0xcc04 +#define DWC3_OEVTEN 0xcc08 +#define DWC3_OSTS 0xcc0C + +/* Bit fields */ + +/* Global Configuration Register */ +#define DWC3_GCTL_PWRDNSCALE(n) (n << 19) +#define DWC3_GCTL_U2RSTECN (1 << 16) +#define DWC3_GCTL_RAMCLKSEL(x) ((x & DWC3_GCTL_CLK_MASK) << 6) +#define DWC3_GCTL_CLK_BUS (0) +#define DWC3_GCTL_CLK_PIPE (1) +#define DWC3_GCTL_CLK_PIPEHALF (2) +#define DWC3_GCTL_CLK_MASK (3) + +#define DWC3_GCTL_PRTCAPDIR(n) (n << 12) +#define DWC3_GCTL_PRTCAP_HOST 1 +#define DWC3_GCTL_PRTCAP_DEVICE 2 +#define DWC3_GCTL_PRTCAP_OTG 3 + +#define DWC3_GCTL_CORESOFTRESET (1 << 11) +#define DWC3_GCTL_SCALEDOWN(n) (n << 4) +#define DWC3_GCTL_DISSCRAMBLE (1 << 3) +#define DWC3_GCTL_DSBLCLKGTNG (1 << 0) + +/* Global USB2 PHY Configuration Register */ +#define DWC3_GUSB2PHYCFG_PHYSOFTRST (1 << 31) +#define DWC3_GUSB2PHYCFG_SUSPHY (1 << 6) + +/* Global USB3 PIPE Control Register */ +#define DWC3_GUSB3PIPECTL_PHYSOFTRST (1 << 31) +#define DWC3_GUSB3PIPECTL_SUSPHY (1 << 17) + +/* Global HWPARAMS1 Register */ +#define DWC3_GHWPARAMS1_EN_PWROPT(n) ((n & (3 << 24)) >> 24) +#define DWC3_GHWPARAMS1_EN_PWROPT_NO 0 +#define DWC3_GHWPARAMS1_EN_PWROPT_CLK 1 + +/* Device Configuration Register */ +#define DWC3_DCFG_DEVADDR(addr) ((addr) << 3) +#define DWC3_DCFG_DEVADDR_MASK DWC3_DCFG_DEVADDR(0x7f) + +#define DWC3_DCFG_SPEED_MASK (7 << 0) +#define DWC3_DCFG_SUPERSPEED (4 << 0) +#define DWC3_DCFG_HIGHSPEED (0 << 0) +#define DWC3_DCFG_FULLSPEED2 (1 << 0) +#define DWC3_DCFG_LOWSPEED (2 << 0) +#define DWC3_DCFG_FULLSPEED1 (3 << 0) + +/* Device Control Register */ +#define DWC3_DCTL_RUN_STOP (1 << 31) +#define DWC3_DCTL_CSFTRST (1 << 30) +#define DWC3_DCTL_LSFTRST (1 << 29) + +#define DWC3_DCTL_HIRD_THRES_MASK (0x1f << 24) +#define DWC3_DCTL_HIRD_THRES(n) (((n) & DWC3_DCTL_HIRD_THRES_MASK) >> 24) + +#define DWC3_DCTL_APPL1RES (1 << 23) + +#define DWC3_DCTL_INITU2ENA (1 << 12) +#define DWC3_DCTL_ACCEPTU2ENA (1 << 11) +#define DWC3_DCTL_INITU1ENA (1 << 10) +#define DWC3_DCTL_ACCEPTU1ENA (1 << 9) +#define DWC3_DCTL_TSTCTRL_MASK (0xf << 1) + +#define DWC3_DCTL_ULSTCHNGREQ_MASK (0x0f << 5) +#define DWC3_DCTL_ULSTCHNGREQ(n) (((n) << 5) & DWC3_DCTL_ULSTCHNGREQ_MASK) + +#define DWC3_DCTL_ULSTCHNG_NO_ACTION (DWC3_DCTL_ULSTCHNGREQ(0)) +#define DWC3_DCTL_ULSTCHNG_SS_DISABLED (DWC3_DCTL_ULSTCHNGREQ(4)) +#define DWC3_DCTL_ULSTCHNG_RX_DETECT (DWC3_DCTL_ULSTCHNGREQ(5)) +#define DWC3_DCTL_ULSTCHNG_SS_INACTIVE (DWC3_DCTL_ULSTCHNGREQ(6)) +#define DWC3_DCTL_ULSTCHNG_RECOVERY (DWC3_DCTL_ULSTCHNGREQ(8)) +#define DWC3_DCTL_ULSTCHNG_COMPLIANCE (DWC3_DCTL_ULSTCHNGREQ(10)) +#define DWC3_DCTL_ULSTCHNG_LOOPBACK (DWC3_DCTL_ULSTCHNGREQ(11)) + +/* Device Event Enable Register */ +#define DWC3_DEVTEN_VNDRDEVTSTRCVEDEN (1 << 12) +#define DWC3_DEVTEN_EVNTOVERFLOWEN (1 << 11) +#define DWC3_DEVTEN_CMDCMPLTEN (1 << 10) +#define DWC3_DEVTEN_ERRTICERREN (1 << 9) +#define DWC3_DEVTEN_SOFEN (1 << 7) +#define DWC3_DEVTEN_EOPFEN (1 << 6) +#define DWC3_DEVTEN_WKUPEVTEN (1 << 4) +#define DWC3_DEVTEN_ULSTCNGEN (1 << 3) +#define DWC3_DEVTEN_CONNECTDONEEN (1 << 2) +#define DWC3_DEVTEN_USBRSTEN (1 << 1) +#define DWC3_DEVTEN_DISCONNEVTEN (1 << 0) + +/* Device Status Register */ +#define DWC3_DSTS_PWRUPREQ (1 << 24) +#define DWC3_DSTS_COREIDLE (1 << 23) +#define DWC3_DSTS_DEVCTRLHLT (1 << 22) + +#define DWC3_DSTS_USBLNKST_MASK (0x0f << 18) +#define DWC3_DSTS_USBLNKST(n) (((n) & DWC3_DSTS_USBLNKST_MASK) >> 18) + +#define DWC3_DSTS_RXFIFOEMPTY (1 << 17) + +#define DWC3_DSTS_SOFFN_MASK (0x3ff << 3) +#define DWC3_DSTS_SOFFN(n) (((n) & DWC3_DSTS_SOFFN_MASK) >> 3) + +#define DWC3_DSTS_CONNECTSPD (7 << 0) + +#define DWC3_DSTS_SUPERSPEED (4 << 0) +#define DWC3_DSTS_HIGHSPEED (0 << 0) +#define DWC3_DSTS_FULLSPEED2 (1 << 0) +#define DWC3_DSTS_LOWSPEED (2 << 0) +#define DWC3_DSTS_FULLSPEED1 (3 << 0) + +/* Device Generic Command Register */ +#define DWC3_DGCMD_SET_LMP 0x01 +#define DWC3_DGCMD_SET_PERIODIC_PAR 0x02 +#define DWC3_DGCMD_XMIT_FUNCTION 0x03 +#define DWC3_DGCMD_SELECTED_FIFO_FLUSH 0x09 +#define DWC3_DGCMD_ALL_FIFO_FLUSH 0x0a +#define DWC3_DGCMD_SET_ENDPOINT_NRDY 0x0c +#define DWC3_DGCMD_RUN_SOC_BUS_LOOPBACK 0x10 + +/* Device Endpoint Command Register */ +#define DWC3_DEPCMD_PARAM_SHIFT 16 +#define DWC3_DEPCMD_PARAM(x) (x << DWC3_DEPCMD_PARAM_SHIFT) +#define DWC3_DEPCMD_GET_RSC_IDX(x) ((x >> DWC3_DEPCMD_PARAM_SHIFT) & 0x7f) +#define DWC3_DEPCMD_STATUS_MASK (0x0f << 12) +#define DWC3_DEPCMD_STATUS(x) ((x & DWC3_DEPCMD_STATUS_MASK) >> 12) +#define DWC3_DEPCMD_HIPRI_FORCERM (1 << 11) +#define DWC3_DEPCMD_CMDACT (1 << 10) +#define DWC3_DEPCMD_CMDIOC (1 << 8) + +#define DWC3_DEPCMD_DEPSTARTCFG (0x09 << 0) +#define DWC3_DEPCMD_ENDTRANSFER (0x08 << 0) +#define DWC3_DEPCMD_UPDATETRANSFER (0x07 << 0) +#define DWC3_DEPCMD_STARTTRANSFER (0x06 << 0) +#define DWC3_DEPCMD_CLEARSTALL (0x05 << 0) +#define DWC3_DEPCMD_SETSTALL (0x04 << 0) +#define DWC3_DEPCMD_GETSEQNUMBER (0x03 << 0) +#define DWC3_DEPCMD_SETTRANSFRESOURCE (0x02 << 0) +#define DWC3_DEPCMD_SETEPCONFIG (0x01 << 0) + +/* The EP number goes 0..31 so ep0 is always out and ep1 is always in */ +#define DWC3_DALEPENA_EP(n) (1 << n) + +#define DWC3_DEPCMD_TYPE_CONTROL 0 +#define DWC3_DEPCMD_TYPE_ISOC 1 +#define DWC3_DEPCMD_TYPE_BULK 2 +#define DWC3_DEPCMD_TYPE_INTR 3 + +/* Structures */ + +struct dwc3_trb_hw; + +/** + * struct dwc3_event_buffer - Software event buffer representation + * @list: a list of event buffers + * @buf: _THE_ buffer + * @length: size of this buffer + * @dma: dma_addr_t + * @dwc: pointer to DWC controller + */ +struct dwc3_event_buffer { + void *buf; + unsigned length; + unsigned int lpos; + + dma_addr_t dma; + + struct dwc3 *dwc; +}; + +#define DWC3_EP_FLAG_STALLED (1 << 0) +#define DWC3_EP_FLAG_WEDGED (1 << 1) + +#define DWC3_EP_DIRECTION_TX true +#define DWC3_EP_DIRECTION_RX false + +#define DWC3_TRB_NUM 32 +#define DWC3_TRB_MASK (DWC3_TRB_NUM - 1) + +/** + * struct dwc3_ep - device side endpoint representation + * @endpoint: usb endpoint + * @request_list: list of requests for this endpoint + * @req_queued: list of requests on this ep which have TRBs setup + * @trb_pool: array of transaction buffers + * @trb_pool_dma: dma address of @trb_pool + * @free_slot: next slot which is going to be used + * @busy_slot: first slot which is owned by HW + * @desc: usb_endpoint_descriptor pointer + * @dwc: pointer to DWC controller + * @flags: endpoint flags (wedged, stalled, ...) + * @current_trb: index of current used trb + * @number: endpoint number (1 - 15) + * @type: set to bmAttributes & USB_ENDPOINT_XFERTYPE_MASK + * @res_trans_idx: Resource transfer index + * @interval: the intervall on which the ISOC transfer is started + * @name: a human readable name e.g. ep1out-bulk + * @direction: true for TX, false for RX + * @stream_capable: true when streams are enabled + */ +struct dwc3_ep { + struct usb_ep endpoint; + struct list_head request_list; + struct list_head req_queued; + + struct dwc3_trb_hw *trb_pool; + dma_addr_t trb_pool_dma; + u32 free_slot; + u32 busy_slot; + const struct usb_endpoint_descriptor *desc; + struct dwc3 *dwc; + + unsigned flags; +#define DWC3_EP_ENABLED (1 << 0) +#define DWC3_EP_STALL (1 << 1) +#define DWC3_EP_WEDGE (1 << 2) +#define DWC3_EP_BUSY (1 << 4) +#define DWC3_EP_PENDING_REQUEST (1 << 5) + + /* This last one is specific to EP0 */ +#define DWC3_EP0_DIR_IN (1 << 31) + + unsigned current_trb; + + u8 number; + u8 type; + u8 res_trans_idx; + u32 interval; + + char name[20]; + + unsigned direction:1; + unsigned stream_capable:1; +}; + +enum dwc3_phy { + DWC3_PHY_UNKNOWN = 0, + DWC3_PHY_USB3, + DWC3_PHY_USB2, +}; + +enum dwc3_ep0_next { + DWC3_EP0_UNKNOWN = 0, + DWC3_EP0_COMPLETE, + DWC3_EP0_NRDY_SETUP, + DWC3_EP0_NRDY_DATA, + DWC3_EP0_NRDY_STATUS, +}; + +enum dwc3_ep0_state { + EP0_UNCONNECTED = 0, + EP0_SETUP_PHASE, + EP0_DATA_PHASE, + EP0_STATUS_PHASE, +}; + +enum dwc3_link_state { + /* In SuperSpeed */ + DWC3_LINK_STATE_U0 = 0x00, /* in HS, means ON */ + DWC3_LINK_STATE_U1 = 0x01, + DWC3_LINK_STATE_U2 = 0x02, /* in HS, means SLEEP */ + DWC3_LINK_STATE_U3 = 0x03, /* in HS, means SUSPEND */ + DWC3_LINK_STATE_SS_DIS = 0x04, + DWC3_LINK_STATE_RX_DET = 0x05, /* in HS, means Early Suspend */ + DWC3_LINK_STATE_SS_INACT = 0x06, + DWC3_LINK_STATE_POLL = 0x07, + DWC3_LINK_STATE_RECOV = 0x08, + DWC3_LINK_STATE_HRESET = 0x09, + DWC3_LINK_STATE_CMPLY = 0x0a, + DWC3_LINK_STATE_LPBK = 0x0b, + DWC3_LINK_STATE_MASK = 0x0f, +}; + +enum dwc3_device_state { + DWC3_DEFAULT_STATE, + DWC3_ADDRESS_STATE, + DWC3_CONFIGURED_STATE, +}; + +/** + * struct dwc3_trb - transfer request block + * @bpl: lower 32bit of the buffer + * @bph: higher 32bit of the buffer + * @length: buffer size (up to 16mb - 1) + * @pcm1: packet count m1 + * @trbsts: trb status + * 0 = ok + * 1 = missed isoc + * 2 = setup pending + * @hwo: hardware owner of descriptor + * @lst: last trb + * @chn: chain buffers + * @csp: continue on short packets (only supported on isoc eps) + * @trbctl: trb control + * 1 = normal + * 2 = control-setup + * 3 = control-status-2 + * 4 = control-status-3 + * 5 = control-data (first trb of data stage) + * 6 = isochronous-first (first trb of service interval) + * 7 = isochronous + * 8 = link trb + * others = reserved + * @isp_imi: interrupt on short packet / interrupt on missed isoc + * @ioc: interrupt on complete + * @sid_sofn: Stream ID / SOF Number + */ +struct dwc3_trb { + u64 bplh; + + union { + struct { + u32 length:24; + u32 pcm1:2; + u32 reserved27_26:2; + u32 trbsts:4; +#define DWC3_TRB_STS_OKAY 0 +#define DWC3_TRB_STS_MISSED_ISOC 1 +#define DWC3_TRB_STS_SETUP_PENDING 2 + }; + u32 len_pcm; + }; + + union { + struct { + u32 hwo:1; + u32 lst:1; + u32 chn:1; + u32 csp:1; + u32 trbctl:6; + u32 isp_imi:1; + u32 ioc:1; + u32 reserved13_12:2; + u32 sid_sofn:16; + u32 reserved31_30:2; + }; + u32 control; + }; +} __packed; + +/** + * struct dwc3_trb_hw - transfer request block (hw format) + * @bpl: DW0-3 + * @bph: DW4-7 + * @size: DW8-B + * @trl: DWC-F + */ +struct dwc3_trb_hw { + __le32 bpl; + __le32 bph; + __le32 size; + __le32 ctrl; +} __packed; + +static inline void dwc3_trb_to_hw(struct dwc3_trb *nat, struct dwc3_trb_hw *hw) +{ + hw->bpl = cpu_to_le32(lower_32_bits(nat->bplh)); + hw->bph = cpu_to_le32(upper_32_bits(nat->bplh)); + hw->size = cpu_to_le32p(&nat->len_pcm); + /* HWO is written last */ + hw->ctrl = cpu_to_le32p(&nat->control); +} + +static inline void dwc3_trb_to_nat(struct dwc3_trb_hw *hw, struct dwc3_trb *nat) +{ + u64 bplh; + + bplh = le32_to_cpup(&hw->bpl); + bplh |= (u64) le32_to_cpup(&hw->bph) << 32; + nat->bplh = bplh; + + nat->len_pcm = le32_to_cpup(&hw->size); + nat->control = le32_to_cpup(&hw->ctrl); +} + +/** + * dwc3_hwparams - copy of HWPARAMS registers + * @hwparams0 - GHWPARAMS0 + * @hwparams1 - GHWPARAMS1 + * @hwparams2 - GHWPARAMS2 + * @hwparams3 - GHWPARAMS3 + * @hwparams4 - GHWPARAMS4 + * @hwparams5 - GHWPARAMS5 + * @hwparams6 - GHWPARAMS6 + * @hwparams7 - GHWPARAMS7 + * @hwparams8 - GHWPARAMS8 + */ +struct dwc3_hwparams { + u32 hwparams0; + u32 hwparams1; + u32 hwparams2; + u32 hwparams3; + u32 hwparams4; + u32 hwparams5; + u32 hwparams6; + u32 hwparams7; + u32 hwparams8; +}; + +/** + * struct dwc3 - representation of our controller + * @ctrl_req: usb control request which is used for ep0 + * @ep0_trb: trb which is used for the ctrl_req + * @ep0_bounce: bounce buffer for ep0 + * @setup_buf: used while precessing STD USB requests + * @ctrl_req_addr: dma address of ctrl_req + * @ep0_trb: dma address of ep0_trb + * @ep0_usb_req: dummy req used while handling STD USB requests + * @setup_buf_addr: dma address of setup_buf + * @ep0_bounce_addr: dma address of ep0_bounce + * @lock: for synchronizing + * @dev: pointer to our struct device + * @event_buffer_list: a list of event buffers + * @gadget: device side representation of the peripheral controller + * @gadget_driver: pointer to the gadget driver + * @regs: base address for our registers + * @regs_size: address space size + * @irq: IRQ number + * @revision: revision register contents + * @is_selfpowered: true when we are selfpowered + * @three_stage_setup: set if we perform a three phase setup + * @ep0_status_pending: ep0 status response without a req is pending + * @ep0_bounced: true when we used bounce buffer + * @ep0_expect_in: true when we expect a DATA IN transfer + * @start_config_issued: true when StartConfig command has been issued + * @ep0_next_event: hold the next expected event + * @ep0state: state of endpoint zero + * @link_state: link state + * @speed: device speed (super, high, full, low) + * @mem: points to start of memory which is used for this struct. + * @hwparams: copy of hwparams registers + * @root: debugfs root folder pointer + */ +struct dwc3 { + struct usb_ctrlrequest *ctrl_req; + struct dwc3_trb_hw *ep0_trb; + void *ep0_bounce; + u8 *setup_buf; + dma_addr_t ctrl_req_addr; + dma_addr_t ep0_trb_addr; + dma_addr_t setup_buf_addr; + dma_addr_t ep0_bounce_addr; + struct usb_request ep0_usb_req; + /* device lock */ + spinlock_t lock; + struct device *dev; + + struct dwc3_event_buffer *ev_buffs[DWC3_EVENT_BUFFERS_NUM]; + struct dwc3_ep *eps[DWC3_ENDPOINTS_NUM]; + + struct usb_gadget gadget; + struct usb_gadget_driver *gadget_driver; + + void __iomem *regs; + size_t regs_size; + + int irq; + + u32 revision; + +#define DWC3_REVISION_173A 0x5533173a +#define DWC3_REVISION_175A 0x5533175a +#define DWC3_REVISION_180A 0x5533180a +#define DWC3_REVISION_183A 0x5533183a +#define DWC3_REVISION_185A 0x5533185a +#define DWC3_REVISION_188A 0x5533188a +#define DWC3_REVISION_190A 0x5533190a + + unsigned is_selfpowered:1; + unsigned three_stage_setup:1; + unsigned ep0_status_pending:1; + unsigned ep0_bounced:1; + unsigned ep0_expect_in:1; + unsigned start_config_issued:1; + + enum dwc3_ep0_next ep0_next_event; + enum dwc3_ep0_state ep0state; + enum dwc3_link_state link_state; + enum dwc3_device_state dev_state; + + u8 speed; + void *mem; + + struct dwc3_hwparams hwparams; + struct dentry *root; +}; + +/* -------------------------------------------------------------------------- */ + +#define DWC3_TRBSTS_OK 0 +#define DWC3_TRBSTS_MISSED_ISOC 1 +#define DWC3_TRBSTS_SETUP_PENDING 2 + +#define DWC3_TRBCTL_NORMAL 1 +#define DWC3_TRBCTL_CONTROL_SETUP 2 +#define DWC3_TRBCTL_CONTROL_STATUS2 3 +#define DWC3_TRBCTL_CONTROL_STATUS3 4 +#define DWC3_TRBCTL_CONTROL_DATA 5 +#define DWC3_TRBCTL_ISOCHRONOUS_FIRST 6 +#define DWC3_TRBCTL_ISOCHRONOUS 7 +#define DWC3_TRBCTL_LINK_TRB 8 + +/* -------------------------------------------------------------------------- */ + +struct dwc3_event_type { + u32 is_devspec:1; + u32 type:6; + u32 reserved8_31:25; +} __packed; + +#define DWC3_DEPEVT_XFERCOMPLETE 0x01 +#define DWC3_DEPEVT_XFERINPROGRESS 0x02 +#define DWC3_DEPEVT_XFERNOTREADY 0x03 +#define DWC3_DEPEVT_RXTXFIFOEVT 0x04 +#define DWC3_DEPEVT_STREAMEVT 0x06 +#define DWC3_DEPEVT_EPCMDCMPLT 0x07 + +/** + * struct dwc3_event_depvt - Device Endpoint Events + * @one_bit: indicates this is an endpoint event (not used) + * @endpoint_number: number of the endpoint + * @endpoint_event: The event we have: + * 0x00 - Reserved + * 0x01 - XferComplete + * 0x02 - XferInProgress + * 0x03 - XferNotReady + * 0x04 - RxTxFifoEvt (IN->Underrun, OUT->Overrun) + * 0x05 - Reserved + * 0x06 - StreamEvt + * 0x07 - EPCmdCmplt + * @reserved11_10: Reserved, don't use. + * @status: Indicates the status of the event. Refer to databook for + * more information. + * @parameters: Parameters of the current event. Refer to databook for + * more information. + */ +struct dwc3_event_depevt { + u32 one_bit:1; + u32 endpoint_number:5; + u32 endpoint_event:4; + u32 reserved11_10:2; + u32 status:4; +#define DEPEVT_STATUS_BUSERR (1 << 0) +#define DEPEVT_STATUS_SHORT (1 << 1) +#define DEPEVT_STATUS_IOC (1 << 2) +#define DEPEVT_STATUS_LST (1 << 3) + +/* Stream event only */ +#define DEPEVT_STREAMEVT_FOUND 1 +#define DEPEVT_STREAMEVT_NOTFOUND 2 + +/* Control-only Status */ +#define DEPEVT_STATUS_CONTROL_SETUP 0 +#define DEPEVT_STATUS_CONTROL_DATA 1 +#define DEPEVT_STATUS_CONTROL_STATUS 2 + + u32 parameters:16; +} __packed; + +/** + * struct dwc3_event_devt - Device Events + * @one_bit: indicates this is a non-endpoint event (not used) + * @device_event: indicates it's a device event. Should read as 0x00 + * @type: indicates the type of device event. + * 0 - DisconnEvt + * 1 - USBRst + * 2 - ConnectDone + * 3 - ULStChng + * 4 - WkUpEvt + * 5 - Reserved + * 6 - EOPF + * 7 - SOF + * 8 - Reserved + * 9 - ErrticErr + * 10 - CmdCmplt + * 11 - EvntOverflow + * 12 - VndrDevTstRcved + * @reserved15_12: Reserved, not used + * @event_info: Information about this event + * @reserved31_24: Reserved, not used + */ +struct dwc3_event_devt { + u32 one_bit:1; + u32 device_event:7; + u32 type:4; + u32 reserved15_12:4; + u32 event_info:8; + u32 reserved31_24:8; +} __packed; + +/** + * struct dwc3_event_gevt - Other Core Events + * @one_bit: indicates this is a non-endpoint event (not used) + * @device_event: indicates it's (0x03) Carkit or (0x04) I2C event. + * @phy_port_number: self-explanatory + * @reserved31_12: Reserved, not used. + */ +struct dwc3_event_gevt { + u32 one_bit:1; + u32 device_event:7; + u32 phy_port_number:4; + u32 reserved31_12:20; +} __packed; + +/** + * union dwc3_event - representation of Event Buffer contents + * @raw: raw 32-bit event + * @type: the type of the event + * @depevt: Device Endpoint Event + * @devt: Device Event + * @gevt: Global Event + */ +union dwc3_event { + u32 raw; + struct dwc3_event_type type; + struct dwc3_event_depevt depevt; + struct dwc3_event_devt devt; + struct dwc3_event_gevt gevt; +}; + +/* + * DWC3 Features to be used as Driver Data + */ + +#define DWC3_HAS_PERIPHERAL BIT(0) +#define DWC3_HAS_XHCI BIT(1) +#define DWC3_HAS_OTG BIT(3) + +#endif /* __DRIVERS_USB_DWC3_CORE_H */ diff --git a/drivers/usb/dwc3/debug.h b/drivers/usb/dwc3/debug.h new file mode 100644 index 000000000000..5894ee8222af --- /dev/null +++ b/drivers/usb/dwc3/debug.h @@ -0,0 +1,50 @@ +/** + * debug.h - DesignWare USB3 DRD Controller Debug Header + * + * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com + * + * Authors: Felipe Balbi <balbi@ti.com>, + * Sebastian Andrzej Siewior <bigeasy@linutronix.de> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The names of the above-listed copyright holders may not be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2, as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "core.h" + +#ifdef CONFIG_DEBUG_FS +extern int dwc3_debugfs_init(struct dwc3 *); +extern void dwc3_debugfs_exit(struct dwc3 *); +#else +static inline int dwc3_debugfs_init(struct dwc3 *d) +{ return 0; } +static inline void dwc3_debugfs_exit(struct dwc3 *d) +{ } +#endif + diff --git a/drivers/usb/dwc3/debugfs.c b/drivers/usb/dwc3/debugfs.c new file mode 100644 index 000000000000..da1ad77d8d51 --- /dev/null +++ b/drivers/usb/dwc3/debugfs.c @@ -0,0 +1,441 @@ +/** + * debugfs.c - DesignWare USB3 DRD Controller DebugFS file + * + * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com + * + * Authors: Felipe Balbi <balbi@ti.com>, + * Sebastian Andrzej Siewior <bigeasy@linutronix.de> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The names of the above-listed copyright holders may not be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2, as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/ptrace.h> +#include <linux/types.h> +#include <linux/spinlock.h> +#include <linux/debugfs.h> +#include <linux/seq_file.h> +#include <linux/delay.h> + +#include <asm/uaccess.h> + +#include "core.h" +#include "gadget.h" +#include "io.h" + +struct dwc3_register { + const char *name; + u32 offset; +}; + +#define dump_register(nm) \ +{ \ + .name = __stringify(nm), \ + .offset = DWC3_ ##nm, \ +} + +static const struct dwc3_register dwc3_regs[] = { + dump_register(GSBUSCFG0), + dump_register(GSBUSCFG1), + dump_register(GTXTHRCFG), + dump_register(GRXTHRCFG), + dump_register(GCTL), + dump_register(GEVTEN), + dump_register(GSTS), + dump_register(GSNPSID), + dump_register(GGPIO), + dump_register(GUID), + dump_register(GUCTL), + dump_register(GBUSERRADDR0), + dump_register(GBUSERRADDR1), + dump_register(GPRTBIMAP0), + dump_register(GPRTBIMAP1), + dump_register(GHWPARAMS0), + dump_register(GHWPARAMS1), + dump_register(GHWPARAMS2), + dump_register(GHWPARAMS3), + dump_register(GHWPARAMS4), + dump_register(GHWPARAMS5), + dump_register(GHWPARAMS6), + dump_register(GHWPARAMS7), + dump_register(GDBGFIFOSPACE), + dump_register(GDBGLTSSM), + dump_register(GPRTBIMAP_HS0), + dump_register(GPRTBIMAP_HS1), + dump_register(GPRTBIMAP_FS0), + dump_register(GPRTBIMAP_FS1), + + dump_register(GUSB2PHYCFG(0)), + dump_register(GUSB2PHYCFG(1)), + dump_register(GUSB2PHYCFG(2)), + dump_register(GUSB2PHYCFG(3)), + dump_register(GUSB2PHYCFG(4)), + dump_register(GUSB2PHYCFG(5)), + dump_register(GUSB2PHYCFG(6)), + dump_register(GUSB2PHYCFG(7)), + dump_register(GUSB2PHYCFG(8)), + dump_register(GUSB2PHYCFG(9)), + dump_register(GUSB2PHYCFG(10)), + dump_register(GUSB2PHYCFG(11)), + dump_register(GUSB2PHYCFG(12)), + dump_register(GUSB2PHYCFG(13)), + dump_register(GUSB2PHYCFG(14)), + dump_register(GUSB2PHYCFG(15)), + + dump_register(GUSB2I2CCTL(0)), + dump_register(GUSB2I2CCTL(1)), + dump_register(GUSB2I2CCTL(2)), + dump_register(GUSB2I2CCTL(3)), + dump_register(GUSB2I2CCTL(4)), + dump_register(GUSB2I2CCTL(5)), + dump_register(GUSB2I2CCTL(6)), + dump_register(GUSB2I2CCTL(7)), + dump_register(GUSB2I2CCTL(8)), + dump_register(GUSB2I2CCTL(9)), + dump_register(GUSB2I2CCTL(10)), + dump_register(GUSB2I2CCTL(11)), + dump_register(GUSB2I2CCTL(12)), + dump_register(GUSB2I2CCTL(13)), + dump_register(GUSB2I2CCTL(14)), + dump_register(GUSB2I2CCTL(15)), + + dump_register(GUSB2PHYACC(0)), + dump_register(GUSB2PHYACC(1)), + dump_register(GUSB2PHYACC(2)), + dump_register(GUSB2PHYACC(3)), + dump_register(GUSB2PHYACC(4)), + dump_register(GUSB2PHYACC(5)), + dump_register(GUSB2PHYACC(6)), + dump_register(GUSB2PHYACC(7)), + dump_register(GUSB2PHYACC(8)), + dump_register(GUSB2PHYACC(9)), + dump_register(GUSB2PHYACC(10)), + dump_register(GUSB2PHYACC(11)), + dump_register(GUSB2PHYACC(12)), + dump_register(GUSB2PHYACC(13)), + dump_register(GUSB2PHYACC(14)), + dump_register(GUSB2PHYACC(15)), + + dump_register(GUSB3PIPECTL(0)), + dump_register(GUSB3PIPECTL(1)), + dump_register(GUSB3PIPECTL(2)), + dump_register(GUSB3PIPECTL(3)), + dump_register(GUSB3PIPECTL(4)), + dump_register(GUSB3PIPECTL(5)), + dump_register(GUSB3PIPECTL(6)), + dump_register(GUSB3PIPECTL(7)), + dump_register(GUSB3PIPECTL(8)), + dump_register(GUSB3PIPECTL(9)), + dump_register(GUSB3PIPECTL(10)), + dump_register(GUSB3PIPECTL(11)), + dump_register(GUSB3PIPECTL(12)), + dump_register(GUSB3PIPECTL(13)), + dump_register(GUSB3PIPECTL(14)), + dump_register(GUSB3PIPECTL(15)), + + dump_register(GTXFIFOSIZ(0)), + dump_register(GTXFIFOSIZ(1)), + dump_register(GTXFIFOSIZ(2)), + dump_register(GTXFIFOSIZ(3)), + dump_register(GTXFIFOSIZ(4)), + dump_register(GTXFIFOSIZ(5)), + dump_register(GTXFIFOSIZ(6)), + dump_register(GTXFIFOSIZ(7)), + dump_register(GTXFIFOSIZ(8)), + dump_register(GTXFIFOSIZ(9)), + dump_register(GTXFIFOSIZ(10)), + dump_register(GTXFIFOSIZ(11)), + dump_register(GTXFIFOSIZ(12)), + dump_register(GTXFIFOSIZ(13)), + dump_register(GTXFIFOSIZ(14)), + dump_register(GTXFIFOSIZ(15)), + dump_register(GTXFIFOSIZ(16)), + dump_register(GTXFIFOSIZ(17)), + dump_register(GTXFIFOSIZ(18)), + dump_register(GTXFIFOSIZ(19)), + dump_register(GTXFIFOSIZ(20)), + dump_register(GTXFIFOSIZ(21)), + dump_register(GTXFIFOSIZ(22)), + dump_register(GTXFIFOSIZ(23)), + dump_register(GTXFIFOSIZ(24)), + dump_register(GTXFIFOSIZ(25)), + dump_register(GTXFIFOSIZ(26)), + dump_register(GTXFIFOSIZ(27)), + dump_register(GTXFIFOSIZ(28)), + dump_register(GTXFIFOSIZ(29)), + dump_register(GTXFIFOSIZ(30)), + dump_register(GTXFIFOSIZ(31)), + + dump_register(GRXFIFOSIZ(0)), + dump_register(GRXFIFOSIZ(1)), + dump_register(GRXFIFOSIZ(2)), + dump_register(GRXFIFOSIZ(3)), + dump_register(GRXFIFOSIZ(4)), + dump_register(GRXFIFOSIZ(5)), + dump_register(GRXFIFOSIZ(6)), + dump_register(GRXFIFOSIZ(7)), + dump_register(GRXFIFOSIZ(8)), + dump_register(GRXFIFOSIZ(9)), + dump_register(GRXFIFOSIZ(10)), + dump_register(GRXFIFOSIZ(11)), + dump_register(GRXFIFOSIZ(12)), + dump_register(GRXFIFOSIZ(13)), + dump_register(GRXFIFOSIZ(14)), + dump_register(GRXFIFOSIZ(15)), + dump_register(GRXFIFOSIZ(16)), + dump_register(GRXFIFOSIZ(17)), + dump_register(GRXFIFOSIZ(18)), + dump_register(GRXFIFOSIZ(19)), + dump_register(GRXFIFOSIZ(20)), + dump_register(GRXFIFOSIZ(21)), + dump_register(GRXFIFOSIZ(22)), + dump_register(GRXFIFOSIZ(23)), + dump_register(GRXFIFOSIZ(24)), + dump_register(GRXFIFOSIZ(25)), + dump_register(GRXFIFOSIZ(26)), + dump_register(GRXFIFOSIZ(27)), + dump_register(GRXFIFOSIZ(28)), + dump_register(GRXFIFOSIZ(29)), + dump_register(GRXFIFOSIZ(30)), + dump_register(GRXFIFOSIZ(31)), + + dump_register(GEVNTADRLO(0)), + dump_register(GEVNTADRHI(0)), + dump_register(GEVNTSIZ(0)), + dump_register(GEVNTCOUNT(0)), + + dump_register(GHWPARAMS8), + dump_register(DCFG), + dump_register(DCTL), + dump_register(DEVTEN), + dump_register(DSTS), + dump_register(DGCMDPAR), + dump_register(DGCMD), + dump_register(DALEPENA), + + dump_register(DEPCMDPAR2(0)), + dump_register(DEPCMDPAR2(1)), + dump_register(DEPCMDPAR2(2)), + dump_register(DEPCMDPAR2(3)), + dump_register(DEPCMDPAR2(4)), + dump_register(DEPCMDPAR2(5)), + dump_register(DEPCMDPAR2(6)), + dump_register(DEPCMDPAR2(7)), + dump_register(DEPCMDPAR2(8)), + dump_register(DEPCMDPAR2(9)), + dump_register(DEPCMDPAR2(10)), + dump_register(DEPCMDPAR2(11)), + dump_register(DEPCMDPAR2(12)), + dump_register(DEPCMDPAR2(13)), + dump_register(DEPCMDPAR2(14)), + dump_register(DEPCMDPAR2(15)), + dump_register(DEPCMDPAR2(16)), + dump_register(DEPCMDPAR2(17)), + dump_register(DEPCMDPAR2(18)), + dump_register(DEPCMDPAR2(19)), + dump_register(DEPCMDPAR2(20)), + dump_register(DEPCMDPAR2(21)), + dump_register(DEPCMDPAR2(22)), + dump_register(DEPCMDPAR2(23)), + dump_register(DEPCMDPAR2(24)), + dump_register(DEPCMDPAR2(25)), + dump_register(DEPCMDPAR2(26)), + dump_register(DEPCMDPAR2(27)), + dump_register(DEPCMDPAR2(28)), + dump_register(DEPCMDPAR2(29)), + dump_register(DEPCMDPAR2(30)), + dump_register(DEPCMDPAR2(31)), + + dump_register(DEPCMDPAR1(0)), + dump_register(DEPCMDPAR1(1)), + dump_register(DEPCMDPAR1(2)), + dump_register(DEPCMDPAR1(3)), + dump_register(DEPCMDPAR1(4)), + dump_register(DEPCMDPAR1(5)), + dump_register(DEPCMDPAR1(6)), + dump_register(DEPCMDPAR1(7)), + dump_register(DEPCMDPAR1(8)), + dump_register(DEPCMDPAR1(9)), + dump_register(DEPCMDPAR1(10)), + dump_register(DEPCMDPAR1(11)), + dump_register(DEPCMDPAR1(12)), + dump_register(DEPCMDPAR1(13)), + dump_register(DEPCMDPAR1(14)), + dump_register(DEPCMDPAR1(15)), + dump_register(DEPCMDPAR1(16)), + dump_register(DEPCMDPAR1(17)), + dump_register(DEPCMDPAR1(18)), + dump_register(DEPCMDPAR1(19)), + dump_register(DEPCMDPAR1(20)), + dump_register(DEPCMDPAR1(21)), + dump_register(DEPCMDPAR1(22)), + dump_register(DEPCMDPAR1(23)), + dump_register(DEPCMDPAR1(24)), + dump_register(DEPCMDPAR1(25)), + dump_register(DEPCMDPAR1(26)), + dump_register(DEPCMDPAR1(27)), + dump_register(DEPCMDPAR1(28)), + dump_register(DEPCMDPAR1(29)), + dump_register(DEPCMDPAR1(30)), + dump_register(DEPCMDPAR1(31)), + + dump_register(DEPCMDPAR0(0)), + dump_register(DEPCMDPAR0(1)), + dump_register(DEPCMDPAR0(2)), + dump_register(DEPCMDPAR0(3)), + dump_register(DEPCMDPAR0(4)), + dump_register(DEPCMDPAR0(5)), + dump_register(DEPCMDPAR0(6)), + dump_register(DEPCMDPAR0(7)), + dump_register(DEPCMDPAR0(8)), + dump_register(DEPCMDPAR0(9)), + dump_register(DEPCMDPAR0(10)), + dump_register(DEPCMDPAR0(11)), + dump_register(DEPCMDPAR0(12)), + dump_register(DEPCMDPAR0(13)), + dump_register(DEPCMDPAR0(14)), + dump_register(DEPCMDPAR0(15)), + dump_register(DEPCMDPAR0(16)), + dump_register(DEPCMDPAR0(17)), + dump_register(DEPCMDPAR0(18)), + dump_register(DEPCMDPAR0(19)), + dump_register(DEPCMDPAR0(20)), + dump_register(DEPCMDPAR0(21)), + dump_register(DEPCMDPAR0(22)), + dump_register(DEPCMDPAR0(23)), + dump_register(DEPCMDPAR0(24)), + dump_register(DEPCMDPAR0(25)), + dump_register(DEPCMDPAR0(26)), + dump_register(DEPCMDPAR0(27)), + dump_register(DEPCMDPAR0(28)), + dump_register(DEPCMDPAR0(29)), + dump_register(DEPCMDPAR0(30)), + dump_register(DEPCMDPAR0(31)), + + dump_register(DEPCMD(0)), + dump_register(DEPCMD(1)), + dump_register(DEPCMD(2)), + dump_register(DEPCMD(3)), + dump_register(DEPCMD(4)), + dump_register(DEPCMD(5)), + dump_register(DEPCMD(6)), + dump_register(DEPCMD(7)), + dump_register(DEPCMD(8)), + dump_register(DEPCMD(9)), + dump_register(DEPCMD(10)), + dump_register(DEPCMD(11)), + dump_register(DEPCMD(12)), + dump_register(DEPCMD(13)), + dump_register(DEPCMD(14)), + dump_register(DEPCMD(15)), + dump_register(DEPCMD(16)), + dump_register(DEPCMD(17)), + dump_register(DEPCMD(18)), + dump_register(DEPCMD(19)), + dump_register(DEPCMD(20)), + dump_register(DEPCMD(21)), + dump_register(DEPCMD(22)), + dump_register(DEPCMD(23)), + dump_register(DEPCMD(24)), + dump_register(DEPCMD(25)), + dump_register(DEPCMD(26)), + dump_register(DEPCMD(27)), + dump_register(DEPCMD(28)), + dump_register(DEPCMD(29)), + dump_register(DEPCMD(30)), + dump_register(DEPCMD(31)), + + dump_register(OCFG), + dump_register(OCTL), + dump_register(OEVTEN), + dump_register(OSTS), +}; + +static int dwc3_regdump_show(struct seq_file *s, void *unused) +{ + struct dwc3 *dwc = s->private; + int i; + + seq_printf(s, "DesignWare USB3 Core Register Dump\n"); + + for (i = 0; i < ARRAY_SIZE(dwc3_regs); i++) { + seq_printf(s, "%-20s : %08x\n", dwc3_regs[i].name, + dwc3_readl(dwc->regs, dwc3_regs[i].offset)); + } + + return 0; +} + +static int dwc3_regdump_open(struct inode *inode, struct file *file) +{ + return single_open(file, dwc3_regdump_show, inode->i_private); +} + +static const struct file_operations dwc3_regdump_fops = { + .open = dwc3_regdump_open, + .read = seq_read, + .release = single_release, +}; + +int __devinit dwc3_debugfs_init(struct dwc3 *dwc) +{ + struct dentry *root; + struct dentry *file; + int ret; + + root = debugfs_create_dir(dev_name(dwc->dev), NULL); + if (IS_ERR(root)){ + ret = PTR_ERR(root); + goto err0; + } + + dwc->root = root; + + file = debugfs_create_file("regdump", S_IRUGO, root, dwc, + &dwc3_regdump_fops); + if (IS_ERR(file)) { + ret = PTR_ERR(file); + goto err1; + } + return 0; + +err1: + debugfs_remove_recursive(root); + +err0: + return ret; +} + +void __devexit dwc3_debugfs_exit(struct dwc3 *dwc) +{ + debugfs_remove_recursive(dwc->root); + dwc->root = NULL; +} diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c new file mode 100644 index 000000000000..062552b5fc8a --- /dev/null +++ b/drivers/usb/dwc3/dwc3-omap.c @@ -0,0 +1,401 @@ +/** + * dwc3-omap.c - OMAP Specific Glue layer + * + * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com + * + * Authors: Felipe Balbi <balbi@ti.com>, + * Sebastian Andrzej Siewior <bigeasy@linutronix.de> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The names of the above-listed copyright holders may not be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2, as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/spinlock.h> +#include <linux/platform_device.h> +#include <linux/platform_data/dwc3-omap.h> +#include <linux/dma-mapping.h> +#include <linux/ioport.h> +#include <linux/io.h> +#include <linux/module.h> + +#include "io.h" + +/* + * All these registers belong to OMAP's Wrapper around the + * DesignWare USB3 Core. + */ + +#define USBOTGSS_REVISION 0x0000 +#define USBOTGSS_SYSCONFIG 0x0010 +#define USBOTGSS_IRQ_EOI 0x0020 +#define USBOTGSS_IRQSTATUS_RAW_0 0x0024 +#define USBOTGSS_IRQSTATUS_0 0x0028 +#define USBOTGSS_IRQENABLE_SET_0 0x002c +#define USBOTGSS_IRQENABLE_CLR_0 0x0030 +#define USBOTGSS_IRQSTATUS_RAW_1 0x0034 +#define USBOTGSS_IRQSTATUS_1 0x0038 +#define USBOTGSS_IRQENABLE_SET_1 0x003c +#define USBOTGSS_IRQENABLE_CLR_1 0x0040 +#define USBOTGSS_UTMI_OTG_CTRL 0x0080 +#define USBOTGSS_UTMI_OTG_STATUS 0x0084 +#define USBOTGSS_MMRAM_OFFSET 0x0100 +#define USBOTGSS_FLADJ 0x0104 +#define USBOTGSS_DEBUG_CFG 0x0108 +#define USBOTGSS_DEBUG_DATA 0x010c + +/* SYSCONFIG REGISTER */ +#define USBOTGSS_SYSCONFIG_DMADISABLE (1 << 16) +#define USBOTGSS_SYSCONFIG_STANDBYMODE(x) ((x) << 4) + +#define USBOTGSS_STANDBYMODE_FORCE_STANDBY 0 +#define USBOTGSS_STANDBYMODE_NO_STANDBY 1 +#define USBOTGSS_STANDBYMODE_SMART_STANDBY 2 +#define USBOTGSS_STANDBYMODE_SMART_WAKEUP 3 + +#define USBOTGSS_STANDBYMODE_MASK (0x03 << 4) + +#define USBOTGSS_SYSCONFIG_IDLEMODE(x) ((x) << 2) + +#define USBOTGSS_IDLEMODE_FORCE_IDLE 0 +#define USBOTGSS_IDLEMODE_NO_IDLE 1 +#define USBOTGSS_IDLEMODE_SMART_IDLE 2 +#define USBOTGSS_IDLEMODE_SMART_WAKEUP 3 + +#define USBOTGSS_IDLEMODE_MASK (0x03 << 2) + +/* IRQ_EOI REGISTER */ +#define USBOTGSS_IRQ_EOI_LINE_NUMBER (1 << 0) + +/* IRQS0 BITS */ +#define USBOTGSS_IRQO_COREIRQ_ST (1 << 0) + +/* IRQ1 BITS */ +#define USBOTGSS_IRQ1_DMADISABLECLR (1 << 17) +#define USBOTGSS_IRQ1_OEVT (1 << 16) +#define USBOTGSS_IRQ1_DRVVBUS_RISE (1 << 13) +#define USBOTGSS_IRQ1_CHRGVBUS_RISE (1 << 12) +#define USBOTGSS_IRQ1_DISCHRGVBUS_RISE (1 << 11) +#define USBOTGSS_IRQ1_IDPULLUP_RISE (1 << 8) +#define USBOTGSS_IRQ1_DRVVBUS_FALL (1 << 5) +#define USBOTGSS_IRQ1_CHRGVBUS_FALL (1 << 4) +#define USBOTGSS_IRQ1_DISCHRGVBUS_FALL (1 << 3) +#define USBOTGSS_IRQ1_IDPULLUP_FALL (1 << 0) + +/* UTMI_OTG_CTRL REGISTER */ +#define USBOTGSS_UTMI_OTG_CTRL_DRVVBUS (1 << 5) +#define USBOTGSS_UTMI_OTG_CTRL_CHRGVBUS (1 << 4) +#define USBOTGSS_UTMI_OTG_CTRL_DISCHRGVBUS (1 << 3) +#define USBOTGSS_UTMI_OTG_CTRL_IDPULLUP (1 << 0) + +/* UTMI_OTG_STATUS REGISTER */ +#define USBOTGSS_UTMI_OTG_STATUS_SW_MODE (1 << 31) +#define USBOTGSS_UTMI_OTG_STATUS_POWERPRESENT (1 << 9) +#define USBOTGSS_UTMI_OTG_STATUS_TXBITSTUFFENABLE (1 << 8) +#define USBOTGSS_UTMI_OTG_STATUS_IDDIG (1 << 4) +#define USBOTGSS_UTMI_OTG_STATUS_SESSEND (1 << 3) +#define USBOTGSS_UTMI_OTG_STATUS_SESSVALID (1 << 2) +#define USBOTGSS_UTMI_OTG_STATUS_VBUSVALID (1 << 1) + +struct dwc3_omap { + /* device lock */ + spinlock_t lock; + + struct platform_device *dwc3; + struct device *dev; + + int irq; + void __iomem *base; + + void *context; + u32 resource_size; + + u32 dma_status:1; +}; + +static irqreturn_t dwc3_omap_interrupt(int irq, void *_omap) +{ + struct dwc3_omap *omap = _omap; + u32 reg; + + spin_lock(&omap->lock); + + reg = dwc3_readl(omap->base, USBOTGSS_IRQSTATUS_1); + + if (reg & USBOTGSS_IRQ1_DMADISABLECLR) { + dev_dbg(omap->dev, "DMA Disable was Cleared\n"); + omap->dma_status = false; + } + + if (reg & USBOTGSS_IRQ1_OEVT) + dev_dbg(omap->dev, "OTG Event\n"); + + if (reg & USBOTGSS_IRQ1_DRVVBUS_RISE) + dev_dbg(omap->dev, "DRVVBUS Rise\n"); + + if (reg & USBOTGSS_IRQ1_CHRGVBUS_RISE) + dev_dbg(omap->dev, "CHRGVBUS Rise\n"); + + if (reg & USBOTGSS_IRQ1_DISCHRGVBUS_RISE) + dev_dbg(omap->dev, "DISCHRGVBUS Rise\n"); + + if (reg & USBOTGSS_IRQ1_IDPULLUP_RISE) + dev_dbg(omap->dev, "IDPULLUP Rise\n"); + + if (reg & USBOTGSS_IRQ1_DRVVBUS_FALL) + dev_dbg(omap->dev, "DRVVBUS Fall\n"); + + if (reg & USBOTGSS_IRQ1_CHRGVBUS_FALL) + dev_dbg(omap->dev, "CHRGVBUS Fall\n"); + + if (reg & USBOTGSS_IRQ1_DISCHRGVBUS_FALL) + dev_dbg(omap->dev, "DISCHRGVBUS Fall\n"); + + if (reg & USBOTGSS_IRQ1_IDPULLUP_FALL) + dev_dbg(omap->dev, "IDPULLUP Fall\n"); + + dwc3_writel(omap->base, USBOTGSS_IRQSTATUS_1, reg); + + reg = dwc3_readl(omap->base, USBOTGSS_IRQSTATUS_0); + dwc3_writel(omap->base, USBOTGSS_IRQSTATUS_0, reg); + + spin_unlock(&omap->lock); + + return IRQ_HANDLED; +} + +static int __devinit dwc3_omap_probe(struct platform_device *pdev) +{ + struct dwc3_omap_data *pdata = pdev->dev.platform_data; + struct platform_device *dwc3; + struct dwc3_omap *omap; + struct resource *res; + + int ret = -ENOMEM; + int irq; + + u32 reg; + + void __iomem *base; + void *context; + + omap = kzalloc(sizeof(*omap), GFP_KERNEL); + if (!omap) { + dev_err(&pdev->dev, "not enough memory\n"); + goto err0; + } + + platform_set_drvdata(pdev, omap); + + irq = platform_get_irq(pdev, 1); + if (irq < 0) { + dev_err(&pdev->dev, "missing IRQ resource\n"); + ret = -EINVAL; + goto err1; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!res) { + dev_err(&pdev->dev, "missing memory base resource\n"); + ret = -EINVAL; + goto err1; + } + + base = ioremap_nocache(res->start, resource_size(res)); + if (!base) { + dev_err(&pdev->dev, "ioremap failed\n"); + goto err1; + } + + dwc3 = platform_device_alloc("dwc3-omap", -1); + if (!dwc3) { + dev_err(&pdev->dev, "couldn't allocate dwc3 device\n"); + goto err2; + } + + context = kzalloc(resource_size(res), GFP_KERNEL); + if (!context) { + dev_err(&pdev->dev, "couldn't allocate dwc3 context memory\n"); + goto err3; + } + + spin_lock_init(&omap->lock); + dma_set_coherent_mask(&dwc3->dev, pdev->dev.coherent_dma_mask); + + dwc3->dev.parent = &pdev->dev; + dwc3->dev.dma_mask = pdev->dev.dma_mask; + dwc3->dev.dma_parms = pdev->dev.dma_parms; + omap->resource_size = resource_size(res); + omap->context = context; + omap->dev = &pdev->dev; + omap->irq = irq; + omap->base = base; + omap->dwc3 = dwc3; + + reg = dwc3_readl(omap->base, USBOTGSS_UTMI_OTG_STATUS); + + if (!pdata) { + dev_dbg(&pdev->dev, "missing platform data\n"); + } else { + switch (pdata->utmi_mode) { + case DWC3_OMAP_UTMI_MODE_SW: + reg |= USBOTGSS_UTMI_OTG_STATUS_SW_MODE; + break; + case DWC3_OMAP_UTMI_MODE_HW: + reg &= ~USBOTGSS_UTMI_OTG_STATUS_SW_MODE; + break; + default: + dev_dbg(&pdev->dev, "UNKNOWN utmi mode %d\n", + pdata->utmi_mode); + } + } + + dwc3_writel(omap->base, USBOTGSS_UTMI_OTG_STATUS, reg); + + /* check the DMA Status */ + reg = dwc3_readl(omap->base, USBOTGSS_SYSCONFIG); + omap->dma_status = !!(reg & USBOTGSS_SYSCONFIG_DMADISABLE); + + /* Set No-Idle and No-Standby */ + reg &= ~(USBOTGSS_STANDBYMODE_MASK + | USBOTGSS_IDLEMODE_MASK); + + reg |= (USBOTGSS_SYSCONFIG_STANDBYMODE(USBOTGSS_STANDBYMODE_NO_STANDBY) + | USBOTGSS_SYSCONFIG_IDLEMODE(USBOTGSS_IDLEMODE_NO_IDLE)); + + dwc3_writel(omap->base, USBOTGSS_SYSCONFIG, reg); + + ret = request_irq(omap->irq, dwc3_omap_interrupt, 0, + "dwc3-omap", omap); + if (ret) { + dev_err(&pdev->dev, "failed to request IRQ #%d --> %d\n", + omap->irq, ret); + goto err4; + } + + /* enable all IRQs */ + reg = USBOTGSS_IRQO_COREIRQ_ST; + dwc3_writel(omap->base, USBOTGSS_IRQENABLE_SET_0, reg); + + reg = (USBOTGSS_IRQ1_OEVT | + USBOTGSS_IRQ1_DRVVBUS_RISE | + USBOTGSS_IRQ1_CHRGVBUS_RISE | + USBOTGSS_IRQ1_DISCHRGVBUS_RISE | + USBOTGSS_IRQ1_IDPULLUP_RISE | + USBOTGSS_IRQ1_DRVVBUS_FALL | + USBOTGSS_IRQ1_CHRGVBUS_FALL | + USBOTGSS_IRQ1_DISCHRGVBUS_FALL | + USBOTGSS_IRQ1_IDPULLUP_FALL); + + dwc3_writel(omap->base, USBOTGSS_IRQENABLE_SET_1, reg); + + ret = platform_device_add_resources(dwc3, pdev->resource, + pdev->num_resources); + if (ret) { + dev_err(&pdev->dev, "couldn't add resources to dwc3 device\n"); + goto err5; + } + + ret = platform_device_add(dwc3); + if (ret) { + dev_err(&pdev->dev, "failed to register dwc3 device\n"); + goto err5; + } + + return 0; + +err5: + free_irq(omap->irq, omap); + +err4: + kfree(omap->context); + +err3: + platform_device_put(dwc3); + +err2: + iounmap(base); + +err1: + kfree(omap); + +err0: + return ret; +} + +static int __devexit dwc3_omap_remove(struct platform_device *pdev) +{ + struct dwc3_omap *omap = platform_get_drvdata(pdev); + + platform_device_unregister(omap->dwc3); + + free_irq(omap->irq, omap); + iounmap(omap->base); + + kfree(omap->context); + kfree(omap); + + return 0; +} + +static const struct of_device_id of_dwc3_matach[] = { + { + "ti,dwc3", + }, + { }, +}; +MODULE_DEVICE_TABLE(of, of_dwc3_matach); + +static struct platform_driver dwc3_omap_driver = { + .probe = dwc3_omap_probe, + .remove = __devexit_p(dwc3_omap_remove), + .driver = { + .name = "omap-dwc3", + .of_match_table = of_dwc3_matach, + }, +}; + +MODULE_AUTHOR("Felipe Balbi <balbi@ti.com>"); +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_DESCRIPTION("DesignWare USB3 OMAP Glue Layer"); + +static int __devinit dwc3_omap_init(void) +{ + return platform_driver_register(&dwc3_omap_driver); +} +module_init(dwc3_omap_init); + +static void __exit dwc3_omap_exit(void) +{ + platform_driver_unregister(&dwc3_omap_driver); +} +module_exit(dwc3_omap_exit); diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c new file mode 100644 index 000000000000..f77c00042685 --- /dev/null +++ b/drivers/usb/dwc3/dwc3-pci.c @@ -0,0 +1,219 @@ +/** + * dwc3-pci.c - PCI Specific glue layer + * + * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com + * + * Authors: Felipe Balbi <balbi@ti.com>, + * Sebastian Andrzej Siewior <bigeasy@linutronix.de> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The names of the above-listed copyright holders may not be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2, as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/pci.h> +#include <linux/platform_device.h> + +/* FIXME define these in <linux/pci_ids.h> */ +#define PCI_VENDOR_ID_SYNOPSYS 0x16c3 +#define PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3 0xabcd + +#define DWC3_PCI_DEVS_POSSIBLE 32 + +struct dwc3_pci { + struct device *dev; + struct platform_device *dwc3; +}; + +static DECLARE_BITMAP(dwc3_pci_devs, DWC3_PCI_DEVS_POSSIBLE); + +static int dwc3_pci_get_device_id(struct dwc3_pci *glue) +{ + int id; + +again: + id = find_first_zero_bit(dwc3_pci_devs, DWC3_PCI_DEVS_POSSIBLE); + if (id < DWC3_PCI_DEVS_POSSIBLE) { + int old; + + old = test_and_set_bit(id, dwc3_pci_devs); + if (old) + goto again; + } else { + dev_err(glue->dev, "no space for new device\n"); + id = -ENOMEM; + } + + return 0; +} + +static void dwc3_pci_put_device_id(struct dwc3_pci *glue, int id) +{ + int ret; + + if (id < 0) + return; + + ret = test_bit(id, dwc3_pci_devs); + WARN(!ret, "Device: %s\nID %d not in use\n", + dev_driver_string(glue->dev), id); + clear_bit(id, dwc3_pci_devs); +} + +static int __devinit dwc3_pci_probe(struct pci_dev *pci, + const struct pci_device_id *id) +{ + struct resource res[2]; + struct platform_device *dwc3; + struct dwc3_pci *glue; + int ret = -ENOMEM; + int devid; + + glue = kzalloc(sizeof(*glue), GFP_KERNEL); + if (!glue) { + dev_err(&pci->dev, "not enough memory\n"); + goto err0; + } + + glue->dev = &pci->dev; + + ret = pci_enable_device(pci); + if (ret) { + dev_err(&pci->dev, "failed to enable pci device\n"); + goto err1; + } + + pci_set_power_state(pci, PCI_D0); + pci_set_master(pci); + + devid = dwc3_pci_get_device_id(glue); + if (devid < 0) + goto err2; + + dwc3 = platform_device_alloc("dwc3-pci", devid); + if (!dwc3) { + dev_err(&pci->dev, "couldn't allocate dwc3 device\n"); + goto err3; + } + + memset(res, 0x00, sizeof(struct resource) * ARRAY_SIZE(res)); + + res[0].start = pci_resource_start(pci, 0); + res[0].end = pci_resource_end(pci, 0); + res[0].name = "dwc_usb3"; + res[0].flags = IORESOURCE_MEM; + + res[1].start = pci->irq; + res[1].name = "dwc_usb3"; + res[1].flags = IORESOURCE_IRQ; + + ret = platform_device_add_resources(dwc3, res, ARRAY_SIZE(res)); + if (ret) { + dev_err(&pci->dev, "couldn't add resources to dwc3 device\n"); + goto err4; + } + + pci_set_drvdata(pci, glue); + + dma_set_coherent_mask(&dwc3->dev, pci->dev.coherent_dma_mask); + + dwc3->dev.dma_mask = pci->dev.dma_mask; + dwc3->dev.dma_parms = pci->dev.dma_parms; + dwc3->dev.parent = &pci->dev; + glue->dwc3 = dwc3; + + ret = platform_device_add(dwc3); + if (ret) { + dev_err(&pci->dev, "failed to register dwc3 device\n"); + goto err4; + } + + return 0; + +err4: + pci_set_drvdata(pci, NULL); + platform_device_put(dwc3); + +err3: + dwc3_pci_put_device_id(glue, devid); + +err2: + pci_disable_device(pci); + +err1: + kfree(pci); + +err0: + return ret; +} + +static void __devexit dwc3_pci_remove(struct pci_dev *pci) +{ + struct dwc3_pci *glue = pci_get_drvdata(pci); + + dwc3_pci_put_device_id(glue, glue->dwc3->id); + platform_device_unregister(glue->dwc3); + pci_set_drvdata(pci, NULL); + pci_disable_device(pci); + kfree(glue); +} + +static DEFINE_PCI_DEVICE_TABLE(dwc3_pci_id_table) = { + { + PCI_DEVICE(PCI_VENDOR_ID_SYNOPSYS, + PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3), + }, + { } /* Terminating Entry */ +}; +MODULE_DEVICE_TABLE(pci, dwc3_pci_id_table); + +static struct pci_driver dwc3_pci_driver = { + .name = "pci-dwc3", + .id_table = dwc3_pci_id_table, + .probe = dwc3_pci_probe, + .remove = __devexit_p(dwc3_pci_remove), +}; + +MODULE_AUTHOR("Felipe Balbi <balbi@ti.com>"); +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_DESCRIPTION("DesignWare USB3 PCI Glue Layer"); + +static int __devinit dwc3_pci_init(void) +{ + return pci_register_driver(&dwc3_pci_driver); +} +module_init(dwc3_pci_init); + +static void __exit dwc3_pci_exit(void) +{ + pci_unregister_driver(&dwc3_pci_driver); +} +module_exit(dwc3_pci_exit); diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c new file mode 100644 index 000000000000..69a4e43ddf59 --- /dev/null +++ b/drivers/usb/dwc3/ep0.c @@ -0,0 +1,804 @@ +/** + * ep0.c - DesignWare USB3 DRD Controller Endpoint 0 Handling + * + * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com + * + * Authors: Felipe Balbi <balbi@ti.com>, + * Sebastian Andrzej Siewior <bigeasy@linutronix.de> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The names of the above-listed copyright holders may not be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2, as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/spinlock.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/list.h> +#include <linux/dma-mapping.h> + +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> + +#include "core.h" +#include "gadget.h" +#include "io.h" + +static void dwc3_ep0_inspect_setup(struct dwc3 *dwc, + const struct dwc3_event_depevt *event); + +static const char *dwc3_ep0_state_string(enum dwc3_ep0_state state) +{ + switch (state) { + case EP0_UNCONNECTED: + return "Unconnected"; + case EP0_SETUP_PHASE: + return "Setup Phase"; + case EP0_DATA_PHASE: + return "Data Phase"; + case EP0_STATUS_PHASE: + return "Status Phase"; + default: + return "UNKNOWN"; + } +} + +static int dwc3_ep0_start_trans(struct dwc3 *dwc, u8 epnum, dma_addr_t buf_dma, + u32 len, u32 type) +{ + struct dwc3_gadget_ep_cmd_params params; + struct dwc3_trb_hw *trb_hw; + struct dwc3_trb trb; + struct dwc3_ep *dep; + + int ret; + + dep = dwc->eps[epnum]; + if (dep->flags & DWC3_EP_BUSY) { + dev_vdbg(dwc->dev, "%s: still busy\n", dep->name); + return 0; + } + + trb_hw = dwc->ep0_trb; + memset(&trb, 0, sizeof(trb)); + + trb.trbctl = type; + trb.bplh = buf_dma; + trb.length = len; + + trb.hwo = 1; + trb.lst = 1; + trb.ioc = 1; + trb.isp_imi = 1; + + dwc3_trb_to_hw(&trb, trb_hw); + + memset(¶ms, 0, sizeof(params)); + params.param0 = upper_32_bits(dwc->ep0_trb_addr); + params.param1 = lower_32_bits(dwc->ep0_trb_addr); + + ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, + DWC3_DEPCMD_STARTTRANSFER, ¶ms); + if (ret < 0) { + dev_dbg(dwc->dev, "failed to send STARTTRANSFER command\n"); + return ret; + } + + dep->flags |= DWC3_EP_BUSY; + dep->res_trans_idx = dwc3_gadget_ep_get_transfer_index(dwc, + dep->number); + + dwc->ep0_next_event = DWC3_EP0_COMPLETE; + + return 0; +} + +static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep, + struct dwc3_request *req) +{ + int ret = 0; + + req->request.actual = 0; + req->request.status = -EINPROGRESS; + req->epnum = dep->number; + + list_add_tail(&req->list, &dep->request_list); + + /* + * Gadget driver might not be quick enough to queue a request + * before we get a Transfer Not Ready event on this endpoint. + * + * In that case, we will set DWC3_EP_PENDING_REQUEST. When that + * flag is set, it's telling us that as soon as Gadget queues the + * required request, we should kick the transfer here because the + * IRQ we were waiting for is long gone. + */ + if (dep->flags & DWC3_EP_PENDING_REQUEST) { + struct dwc3 *dwc = dep->dwc; + unsigned direction; + u32 type; + + direction = !!(dep->flags & DWC3_EP0_DIR_IN); + + if (dwc->ep0state == EP0_STATUS_PHASE) { + type = dwc->three_stage_setup + ? DWC3_TRBCTL_CONTROL_STATUS3 + : DWC3_TRBCTL_CONTROL_STATUS2; + } else if (dwc->ep0state == EP0_DATA_PHASE) { + type = DWC3_TRBCTL_CONTROL_DATA; + } else { + /* should never happen */ + WARN_ON(1); + return 0; + } + + ret = dwc3_ep0_start_trans(dwc, direction, + req->request.dma, req->request.length, type); + dep->flags &= ~(DWC3_EP_PENDING_REQUEST | + DWC3_EP0_DIR_IN); + } + + return ret; +} + +int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request, + gfp_t gfp_flags) +{ + struct dwc3_request *req = to_dwc3_request(request); + struct dwc3_ep *dep = to_dwc3_ep(ep); + struct dwc3 *dwc = dep->dwc; + + unsigned long flags; + + int ret; + + spin_lock_irqsave(&dwc->lock, flags); + if (!dep->desc) { + dev_dbg(dwc->dev, "trying to queue request %p to disabled %s\n", + request, dep->name); + ret = -ESHUTDOWN; + goto out; + } + + /* we share one TRB for ep0/1 */ + if (!list_empty(&dwc->eps[0]->request_list) || + !list_empty(&dwc->eps[1]->request_list) || + dwc->ep0_status_pending) { + ret = -EBUSY; + goto out; + } + + dev_vdbg(dwc->dev, "queueing request %p to %s length %d, state '%s'\n", + request, dep->name, request->length, + dwc3_ep0_state_string(dwc->ep0state)); + + ret = __dwc3_gadget_ep0_queue(dep, req); + +out: + spin_unlock_irqrestore(&dwc->lock, flags); + + return ret; +} + +static void dwc3_ep0_stall_and_restart(struct dwc3 *dwc) +{ + struct dwc3_ep *dep = dwc->eps[0]; + + /* stall is always issued on EP0 */ + __dwc3_gadget_ep_set_halt(dwc->eps[0], 1); + dwc->eps[0]->flags = DWC3_EP_ENABLED; + + if (!list_empty(&dep->request_list)) { + struct dwc3_request *req; + + req = next_request(&dep->request_list); + dwc3_gadget_giveback(dep, req, -ECONNRESET); + } + + dwc->ep0state = EP0_SETUP_PHASE; + dwc3_ep0_out_start(dwc); +} + +void dwc3_ep0_out_start(struct dwc3 *dwc) +{ + int ret; + + ret = dwc3_ep0_start_trans(dwc, 0, dwc->ctrl_req_addr, 8, + DWC3_TRBCTL_CONTROL_SETUP); + WARN_ON(ret < 0); +} + +static struct dwc3_ep *dwc3_wIndex_to_dep(struct dwc3 *dwc, __le16 wIndex_le) +{ + struct dwc3_ep *dep; + u32 windex = le16_to_cpu(wIndex_le); + u32 epnum; + + epnum = (windex & USB_ENDPOINT_NUMBER_MASK) << 1; + if ((windex & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) + epnum |= 1; + + dep = dwc->eps[epnum]; + if (dep->flags & DWC3_EP_ENABLED) + return dep; + + return NULL; +} + +static void dwc3_ep0_send_status_response(struct dwc3 *dwc) +{ + dwc3_ep0_start_trans(dwc, 1, dwc->setup_buf_addr, + dwc->ep0_usb_req.length, + DWC3_TRBCTL_CONTROL_DATA); +} + +/* + * ch 9.4.5 + */ +static int dwc3_ep0_handle_status(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) +{ + struct dwc3_ep *dep; + u32 recip; + u16 usb_status = 0; + __le16 *response_pkt; + + recip = ctrl->bRequestType & USB_RECIP_MASK; + switch (recip) { + case USB_RECIP_DEVICE: + /* + * We are self-powered. U1/U2/LTM will be set later + * once we handle this states. RemoteWakeup is 0 on SS + */ + usb_status |= dwc->is_selfpowered << USB_DEVICE_SELF_POWERED; + break; + + case USB_RECIP_INTERFACE: + /* + * Function Remote Wake Capable D0 + * Function Remote Wakeup D1 + */ + break; + + case USB_RECIP_ENDPOINT: + dep = dwc3_wIndex_to_dep(dwc, ctrl->wIndex); + if (!dep) + return -EINVAL; + + if (dep->flags & DWC3_EP_STALL) + usb_status = 1 << USB_ENDPOINT_HALT; + break; + default: + return -EINVAL; + }; + + response_pkt = (__le16 *) dwc->setup_buf; + *response_pkt = cpu_to_le16(usb_status); + dwc->ep0_usb_req.length = sizeof(*response_pkt); + dwc->ep0_status_pending = 1; + + return 0; +} + +static int dwc3_ep0_handle_feature(struct dwc3 *dwc, + struct usb_ctrlrequest *ctrl, int set) +{ + struct dwc3_ep *dep; + u32 recip; + u32 wValue; + u32 wIndex; + u32 reg; + int ret; + u32 mode; + + wValue = le16_to_cpu(ctrl->wValue); + wIndex = le16_to_cpu(ctrl->wIndex); + recip = ctrl->bRequestType & USB_RECIP_MASK; + switch (recip) { + case USB_RECIP_DEVICE: + + /* + * 9.4.1 says only only for SS, in AddressState only for + * default control pipe + */ + switch (wValue) { + case USB_DEVICE_U1_ENABLE: + case USB_DEVICE_U2_ENABLE: + case USB_DEVICE_LTM_ENABLE: + if (dwc->dev_state != DWC3_CONFIGURED_STATE) + return -EINVAL; + if (dwc->speed != DWC3_DSTS_SUPERSPEED) + return -EINVAL; + } + + /* XXX add U[12] & LTM */ + switch (wValue) { + case USB_DEVICE_REMOTE_WAKEUP: + break; + case USB_DEVICE_U1_ENABLE: + break; + case USB_DEVICE_U2_ENABLE: + break; + case USB_DEVICE_LTM_ENABLE: + break; + + case USB_DEVICE_TEST_MODE: + if ((wIndex & 0xff) != 0) + return -EINVAL; + if (!set) + return -EINVAL; + + mode = wIndex >> 8; + reg = dwc3_readl(dwc->regs, DWC3_DCTL); + reg &= ~DWC3_DCTL_TSTCTRL_MASK; + + switch (mode) { + case TEST_J: + case TEST_K: + case TEST_SE0_NAK: + case TEST_PACKET: + case TEST_FORCE_EN: + reg |= mode << 1; + break; + default: + return -EINVAL; + } + dwc3_writel(dwc->regs, DWC3_DCTL, reg); + break; + default: + return -EINVAL; + } + break; + + case USB_RECIP_INTERFACE: + switch (wValue) { + case USB_INTRF_FUNC_SUSPEND: + if (wIndex & USB_INTRF_FUNC_SUSPEND_LP) + /* XXX enable Low power suspend */ + ; + if (wIndex & USB_INTRF_FUNC_SUSPEND_RW) + /* XXX enable remote wakeup */ + ; + break; + default: + return -EINVAL; + } + break; + + case USB_RECIP_ENDPOINT: + switch (wValue) { + case USB_ENDPOINT_HALT: + + dep = dwc3_wIndex_to_dep(dwc, ctrl->wIndex); + if (!dep) + return -EINVAL; + ret = __dwc3_gadget_ep_set_halt(dep, set); + if (ret) + return -EINVAL; + break; + default: + return -EINVAL; + } + break; + + default: + return -EINVAL; + }; + + return 0; +} + +static int dwc3_ep0_set_address(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) +{ + u32 addr; + u32 reg; + + addr = le16_to_cpu(ctrl->wValue); + if (addr > 127) + return -EINVAL; + + reg = dwc3_readl(dwc->regs, DWC3_DCFG); + reg &= ~(DWC3_DCFG_DEVADDR_MASK); + reg |= DWC3_DCFG_DEVADDR(addr); + dwc3_writel(dwc->regs, DWC3_DCFG, reg); + + if (addr) + dwc->dev_state = DWC3_ADDRESS_STATE; + else + dwc->dev_state = DWC3_DEFAULT_STATE; + + return 0; +} + +static int dwc3_ep0_delegate_req(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) +{ + int ret; + + spin_unlock(&dwc->lock); + ret = dwc->gadget_driver->setup(&dwc->gadget, ctrl); + spin_lock(&dwc->lock); + return ret; +} + +static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) +{ + u32 cfg; + int ret; + + dwc->start_config_issued = false; + cfg = le16_to_cpu(ctrl->wValue); + + switch (dwc->dev_state) { + case DWC3_DEFAULT_STATE: + return -EINVAL; + break; + + case DWC3_ADDRESS_STATE: + ret = dwc3_ep0_delegate_req(dwc, ctrl); + /* if the cfg matches and the cfg is non zero */ + if (!ret && cfg) + dwc->dev_state = DWC3_CONFIGURED_STATE; + break; + + case DWC3_CONFIGURED_STATE: + ret = dwc3_ep0_delegate_req(dwc, ctrl); + if (!cfg) + dwc->dev_state = DWC3_ADDRESS_STATE; + break; + } + return 0; +} + +static int dwc3_ep0_std_request(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) +{ + int ret; + + switch (ctrl->bRequest) { + case USB_REQ_GET_STATUS: + dev_vdbg(dwc->dev, "USB_REQ_GET_STATUS\n"); + ret = dwc3_ep0_handle_status(dwc, ctrl); + break; + case USB_REQ_CLEAR_FEATURE: + dev_vdbg(dwc->dev, "USB_REQ_CLEAR_FEATURE\n"); + ret = dwc3_ep0_handle_feature(dwc, ctrl, 0); + break; + case USB_REQ_SET_FEATURE: + dev_vdbg(dwc->dev, "USB_REQ_SET_FEATURE\n"); + ret = dwc3_ep0_handle_feature(dwc, ctrl, 1); + break; + case USB_REQ_SET_ADDRESS: + dev_vdbg(dwc->dev, "USB_REQ_SET_ADDRESS\n"); + ret = dwc3_ep0_set_address(dwc, ctrl); + break; + case USB_REQ_SET_CONFIGURATION: + dev_vdbg(dwc->dev, "USB_REQ_SET_CONFIGURATION\n"); + ret = dwc3_ep0_set_config(dwc, ctrl); + break; + default: + dev_vdbg(dwc->dev, "Forwarding to gadget driver\n"); + ret = dwc3_ep0_delegate_req(dwc, ctrl); + break; + }; + + return ret; +} + +static void dwc3_ep0_inspect_setup(struct dwc3 *dwc, + const struct dwc3_event_depevt *event) +{ + struct usb_ctrlrequest *ctrl = dwc->ctrl_req; + int ret; + u32 len; + + if (!dwc->gadget_driver) + goto err; + + len = le16_to_cpu(ctrl->wLength); + if (!len) { + dwc->three_stage_setup = false; + dwc->ep0_expect_in = false; + dwc->ep0_next_event = DWC3_EP0_NRDY_STATUS; + } else { + dwc->three_stage_setup = true; + dwc->ep0_expect_in = !!(ctrl->bRequestType & USB_DIR_IN); + dwc->ep0_next_event = DWC3_EP0_NRDY_DATA; + } + + if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) + ret = dwc3_ep0_std_request(dwc, ctrl); + else + ret = dwc3_ep0_delegate_req(dwc, ctrl); + + if (ret >= 0) + return; + +err: + dwc3_ep0_stall_and_restart(dwc); +} + +static void dwc3_ep0_complete_data(struct dwc3 *dwc, + const struct dwc3_event_depevt *event) +{ + struct dwc3_request *r = NULL; + struct usb_request *ur; + struct dwc3_trb trb; + struct dwc3_ep *dep; + u32 transferred; + u8 epnum; + + epnum = event->endpoint_number; + dep = dwc->eps[epnum]; + + dwc->ep0_next_event = DWC3_EP0_NRDY_STATUS; + + if (!dwc->ep0_status_pending) { + r = next_request(&dwc->eps[0]->request_list); + ur = &r->request; + } else { + ur = &dwc->ep0_usb_req; + dwc->ep0_status_pending = 0; + } + + dwc3_trb_to_nat(dwc->ep0_trb, &trb); + + if (dwc->ep0_bounced) { + struct dwc3_ep *ep0 = dwc->eps[0]; + + transferred = min_t(u32, ur->length, + ep0->endpoint.maxpacket - trb.length); + memcpy(ur->buf, dwc->ep0_bounce, transferred); + dwc->ep0_bounced = false; + } else { + transferred = ur->length - trb.length; + ur->actual += transferred; + } + + if ((epnum & 1) && ur->actual < ur->length) { + /* for some reason we did not get everything out */ + + dwc3_ep0_stall_and_restart(dwc); + } else { + /* + * handle the case where we have to send a zero packet. This + * seems to be case when req.length > maxpacket. Could it be? + */ + if (r) + dwc3_gadget_giveback(dep, r, 0); + } +} + +static void dwc3_ep0_complete_req(struct dwc3 *dwc, + const struct dwc3_event_depevt *event) +{ + struct dwc3_request *r; + struct dwc3_ep *dep; + + dep = dwc->eps[0]; + + if (!list_empty(&dep->request_list)) { + r = next_request(&dep->request_list); + + dwc3_gadget_giveback(dep, r, 0); + } + + dwc->ep0state = EP0_SETUP_PHASE; + dwc3_ep0_out_start(dwc); +} + +static void dwc3_ep0_xfer_complete(struct dwc3 *dwc, + const struct dwc3_event_depevt *event) +{ + struct dwc3_ep *dep = dwc->eps[event->endpoint_number]; + + dep->flags &= ~DWC3_EP_BUSY; + + switch (dwc->ep0state) { + case EP0_SETUP_PHASE: + dev_vdbg(dwc->dev, "Inspecting Setup Bytes\n"); + dwc3_ep0_inspect_setup(dwc, event); + break; + + case EP0_DATA_PHASE: + dev_vdbg(dwc->dev, "Data Phase\n"); + dwc3_ep0_complete_data(dwc, event); + break; + + case EP0_STATUS_PHASE: + dev_vdbg(dwc->dev, "Status Phase\n"); + dwc3_ep0_complete_req(dwc, event); + break; + default: + WARN(true, "UNKNOWN ep0state %d\n", dwc->ep0state); + } +} + +static void dwc3_ep0_do_control_setup(struct dwc3 *dwc, + const struct dwc3_event_depevt *event) +{ + dwc->ep0state = EP0_SETUP_PHASE; + dwc3_ep0_out_start(dwc); +} + +static void dwc3_ep0_do_control_data(struct dwc3 *dwc, + const struct dwc3_event_depevt *event) +{ + struct dwc3_ep *dep; + struct dwc3_request *req; + int ret; + + dep = dwc->eps[0]; + dwc->ep0state = EP0_DATA_PHASE; + + if (dwc->ep0_status_pending) { + dwc3_ep0_send_status_response(dwc); + return; + } + + if (list_empty(&dep->request_list)) { + dev_vdbg(dwc->dev, "pending request for EP0 Data phase\n"); + dep->flags |= DWC3_EP_PENDING_REQUEST; + + if (event->endpoint_number) + dep->flags |= DWC3_EP0_DIR_IN; + return; + } + + req = next_request(&dep->request_list); + req->direction = !!event->endpoint_number; + + dwc->ep0state = EP0_DATA_PHASE; + if (req->request.length == 0) { + ret = dwc3_ep0_start_trans(dwc, event->endpoint_number, + dwc->ctrl_req_addr, 0, + DWC3_TRBCTL_CONTROL_DATA); + } else if ((req->request.length % dep->endpoint.maxpacket) + && (event->endpoint_number == 0)) { + dwc3_map_buffer_to_dma(req); + + WARN_ON(req->request.length > dep->endpoint.maxpacket); + + dwc->ep0_bounced = true; + + /* + * REVISIT in case request length is bigger than EP0 + * wMaxPacketSize, we will need two chained TRBs to handle + * the transfer. + */ + ret = dwc3_ep0_start_trans(dwc, event->endpoint_number, + dwc->ep0_bounce_addr, dep->endpoint.maxpacket, + DWC3_TRBCTL_CONTROL_DATA); + } else { + dwc3_map_buffer_to_dma(req); + + ret = dwc3_ep0_start_trans(dwc, event->endpoint_number, + req->request.dma, req->request.length, + DWC3_TRBCTL_CONTROL_DATA); + } + + WARN_ON(ret < 0); +} + +static void dwc3_ep0_do_control_status(struct dwc3 *dwc, + const struct dwc3_event_depevt *event) +{ + u32 type; + int ret; + + dwc->ep0state = EP0_STATUS_PHASE; + + type = dwc->three_stage_setup ? DWC3_TRBCTL_CONTROL_STATUS3 + : DWC3_TRBCTL_CONTROL_STATUS2; + + ret = dwc3_ep0_start_trans(dwc, event->endpoint_number, + dwc->ctrl_req_addr, 0, type); + + WARN_ON(ret < 0); +} + +static void dwc3_ep0_xfernotready(struct dwc3 *dwc, + const struct dwc3_event_depevt *event) +{ + switch (event->status) { + case DEPEVT_STATUS_CONTROL_SETUP: + dev_vdbg(dwc->dev, "Control Setup\n"); + dwc3_ep0_do_control_setup(dwc, event); + break; + + case DEPEVT_STATUS_CONTROL_DATA: + dev_vdbg(dwc->dev, "Control Data\n"); + + if (dwc->ep0_next_event != DWC3_EP0_NRDY_DATA) { + dev_vdbg(dwc->dev, "Expected %d got %d\n", + dwc->ep0_next_event, + DWC3_EP0_NRDY_DATA); + + dwc3_ep0_stall_and_restart(dwc); + return; + } + + /* + * One of the possible error cases is when Host _does_ + * request for Data Phase, but it does so on the wrong + * direction. + * + * Here, we already know ep0_next_event is DATA (see above), + * so we only need to check for direction. + */ + if (dwc->ep0_expect_in != event->endpoint_number) { + dev_vdbg(dwc->dev, "Wrong direction for Data phase\n"); + dwc3_ep0_stall_and_restart(dwc); + return; + } + + dwc3_ep0_do_control_data(dwc, event); + break; + + case DEPEVT_STATUS_CONTROL_STATUS: + dev_vdbg(dwc->dev, "Control Status\n"); + + if (dwc->ep0_next_event != DWC3_EP0_NRDY_STATUS) { + dev_vdbg(dwc->dev, "Expected %d got %d\n", + dwc->ep0_next_event, + DWC3_EP0_NRDY_STATUS); + + dwc3_ep0_stall_and_restart(dwc); + return; + } + dwc3_ep0_do_control_status(dwc, event); + } +} + +void dwc3_ep0_interrupt(struct dwc3 *dwc, + const const struct dwc3_event_depevt *event) +{ + u8 epnum = event->endpoint_number; + + dev_dbg(dwc->dev, "%s while ep%d%s in state '%s'\n", + dwc3_ep_event_string(event->endpoint_event), + epnum >> 1, (epnum & 1) ? "in" : "out", + dwc3_ep0_state_string(dwc->ep0state)); + + switch (event->endpoint_event) { + case DWC3_DEPEVT_XFERCOMPLETE: + dwc3_ep0_xfer_complete(dwc, event); + break; + + case DWC3_DEPEVT_XFERNOTREADY: + dwc3_ep0_xfernotready(dwc, event); + break; + + case DWC3_DEPEVT_XFERINPROGRESS: + case DWC3_DEPEVT_RXTXFIFOEVT: + case DWC3_DEPEVT_STREAMEVT: + case DWC3_DEPEVT_EPCMDCMPLT: + break; + } +} diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c new file mode 100644 index 000000000000..fa824cfdd2eb --- /dev/null +++ b/drivers/usb/dwc3/gadget.c @@ -0,0 +1,2104 @@ +/** + * gadget.c - DesignWare USB3 DRD Controller Gadget Framework Link + * + * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com + * + * Authors: Felipe Balbi <balbi@ti.com>, + * Sebastian Andrzej Siewior <bigeasy@linutronix.de> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The names of the above-listed copyright holders may not be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2, as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/spinlock.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/list.h> +#include <linux/dma-mapping.h> + +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> + +#include "core.h" +#include "gadget.h" +#include "io.h" + +#define DMA_ADDR_INVALID (~(dma_addr_t)0) + +void dwc3_map_buffer_to_dma(struct dwc3_request *req) +{ + struct dwc3 *dwc = req->dep->dwc; + + if (req->request.length == 0) { + /* req->request.dma = dwc->setup_buf_addr; */ + return; + } + + if (req->request.dma == DMA_ADDR_INVALID) { + req->request.dma = dma_map_single(dwc->dev, req->request.buf, + req->request.length, req->direction + ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + req->mapped = true; + } +} + +void dwc3_unmap_buffer_from_dma(struct dwc3_request *req) +{ + struct dwc3 *dwc = req->dep->dwc; + + if (req->request.length == 0) { + req->request.dma = DMA_ADDR_INVALID; + return; + } + + if (req->mapped) { + dma_unmap_single(dwc->dev, req->request.dma, + req->request.length, req->direction + ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + req->mapped = 0; + req->request.dma = DMA_ADDR_INVALID; + } +} + +void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req, + int status) +{ + struct dwc3 *dwc = dep->dwc; + + if (req->queued) { + dep->busy_slot++; + /* + * Skip LINK TRB. We can't use req->trb and check for + * DWC3_TRBCTL_LINK_TRB because it points the TRB we just + * completed (not the LINK TRB). + */ + if (((dep->busy_slot & DWC3_TRB_MASK) == DWC3_TRB_NUM - 1) && + usb_endpoint_xfer_isoc(dep->desc)) + dep->busy_slot++; + } + list_del(&req->list); + + if (req->request.status == -EINPROGRESS) + req->request.status = status; + + dwc3_unmap_buffer_from_dma(req); + + dev_dbg(dwc->dev, "request %p from %s completed %d/%d ===> %d\n", + req, dep->name, req->request.actual, + req->request.length, status); + + spin_unlock(&dwc->lock); + req->request.complete(&req->dep->endpoint, &req->request); + spin_lock(&dwc->lock); +} + +static const char *dwc3_gadget_ep_cmd_string(u8 cmd) +{ + switch (cmd) { + case DWC3_DEPCMD_DEPSTARTCFG: + return "Start New Configuration"; + case DWC3_DEPCMD_ENDTRANSFER: + return "End Transfer"; + case DWC3_DEPCMD_UPDATETRANSFER: + return "Update Transfer"; + case DWC3_DEPCMD_STARTTRANSFER: + return "Start Transfer"; + case DWC3_DEPCMD_CLEARSTALL: + return "Clear Stall"; + case DWC3_DEPCMD_SETSTALL: + return "Set Stall"; + case DWC3_DEPCMD_GETSEQNUMBER: + return "Get Data Sequence Number"; + case DWC3_DEPCMD_SETTRANSFRESOURCE: + return "Set Endpoint Transfer Resource"; + case DWC3_DEPCMD_SETEPCONFIG: + return "Set Endpoint Configuration"; + default: + return "UNKNOWN command"; + } +} + +int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep, + unsigned cmd, struct dwc3_gadget_ep_cmd_params *params) +{ + struct dwc3_ep *dep = dwc->eps[ep]; + u32 timeout = 500; + u32 reg; + + dev_vdbg(dwc->dev, "%s: cmd '%s' params %08x %08x %08x\n", + dep->name, + dwc3_gadget_ep_cmd_string(cmd), params->param0, + params->param1, params->param2); + + dwc3_writel(dwc->regs, DWC3_DEPCMDPAR0(ep), params->param0); + dwc3_writel(dwc->regs, DWC3_DEPCMDPAR1(ep), params->param1); + dwc3_writel(dwc->regs, DWC3_DEPCMDPAR2(ep), params->param2); + + dwc3_writel(dwc->regs, DWC3_DEPCMD(ep), cmd | DWC3_DEPCMD_CMDACT); + do { + reg = dwc3_readl(dwc->regs, DWC3_DEPCMD(ep)); + if (!(reg & DWC3_DEPCMD_CMDACT)) { + dev_vdbg(dwc->dev, "Command Complete --> %d\n", + DWC3_DEPCMD_STATUS(reg)); + return 0; + } + + /* + * We can't sleep here, because it is also called from + * interrupt context. + */ + timeout--; + if (!timeout) + return -ETIMEDOUT; + + udelay(1); + } while (1); +} + +static dma_addr_t dwc3_trb_dma_offset(struct dwc3_ep *dep, + struct dwc3_trb_hw *trb) +{ + u32 offset = (char *) trb - (char *) dep->trb_pool; + + return dep->trb_pool_dma + offset; +} + +static int dwc3_alloc_trb_pool(struct dwc3_ep *dep) +{ + struct dwc3 *dwc = dep->dwc; + + if (dep->trb_pool) + return 0; + + if (dep->number == 0 || dep->number == 1) + return 0; + + dep->trb_pool = dma_alloc_coherent(dwc->dev, + sizeof(struct dwc3_trb) * DWC3_TRB_NUM, + &dep->trb_pool_dma, GFP_KERNEL); + if (!dep->trb_pool) { + dev_err(dep->dwc->dev, "failed to allocate trb pool for %s\n", + dep->name); + return -ENOMEM; + } + + return 0; +} + +static void dwc3_free_trb_pool(struct dwc3_ep *dep) +{ + struct dwc3 *dwc = dep->dwc; + + dma_free_coherent(dwc->dev, sizeof(struct dwc3_trb) * DWC3_TRB_NUM, + dep->trb_pool, dep->trb_pool_dma); + + dep->trb_pool = NULL; + dep->trb_pool_dma = 0; +} + +static int dwc3_gadget_start_config(struct dwc3 *dwc, struct dwc3_ep *dep) +{ + struct dwc3_gadget_ep_cmd_params params; + u32 cmd; + + memset(¶ms, 0x00, sizeof(params)); + + if (dep->number != 1) { + cmd = DWC3_DEPCMD_DEPSTARTCFG; + /* XferRscIdx == 0 for ep0 and 2 for the remaining */ + if (dep->number > 1) { + if (dwc->start_config_issued) + return 0; + dwc->start_config_issued = true; + cmd |= DWC3_DEPCMD_PARAM(2); + } + + return dwc3_send_gadget_ep_cmd(dwc, 0, cmd, ¶ms); + } + + return 0; +} + +static int dwc3_gadget_set_ep_config(struct dwc3 *dwc, struct dwc3_ep *dep, + const struct usb_endpoint_descriptor *desc) +{ + struct dwc3_gadget_ep_cmd_params params; + + memset(¶ms, 0x00, sizeof(params)); + + params.param0 = DWC3_DEPCFG_EP_TYPE(usb_endpoint_type(desc)) + | DWC3_DEPCFG_MAX_PACKET_SIZE(usb_endpoint_maxp(desc)) + | DWC3_DEPCFG_BURST_SIZE(dep->endpoint.maxburst); + + params.param1 = DWC3_DEPCFG_XFER_COMPLETE_EN + | DWC3_DEPCFG_XFER_NOT_READY_EN; + + if (usb_endpoint_xfer_bulk(desc) && dep->endpoint.max_streams) { + params.param1 |= DWC3_DEPCFG_STREAM_CAPABLE + | DWC3_DEPCFG_STREAM_EVENT_EN; + dep->stream_capable = true; + } + + if (usb_endpoint_xfer_isoc(desc)) + params.param1 |= DWC3_DEPCFG_XFER_IN_PROGRESS_EN; + + /* + * We are doing 1:1 mapping for endpoints, meaning + * Physical Endpoints 2 maps to Logical Endpoint 2 and + * so on. We consider the direction bit as part of the physical + * endpoint number. So USB endpoint 0x81 is 0x03. + */ + params.param1 |= DWC3_DEPCFG_EP_NUMBER(dep->number); + + /* + * We must use the lower 16 TX FIFOs even though + * HW might have more + */ + if (dep->direction) + params.param0 |= DWC3_DEPCFG_FIFO_NUMBER(dep->number >> 1); + + if (desc->bInterval) { + params.param1 |= DWC3_DEPCFG_BINTERVAL_M1(desc->bInterval - 1); + dep->interval = 1 << (desc->bInterval - 1); + } + + return dwc3_send_gadget_ep_cmd(dwc, dep->number, + DWC3_DEPCMD_SETEPCONFIG, ¶ms); +} + +static int dwc3_gadget_set_xfer_resource(struct dwc3 *dwc, struct dwc3_ep *dep) +{ + struct dwc3_gadget_ep_cmd_params params; + + memset(¶ms, 0x00, sizeof(params)); + + params.param0 = DWC3_DEPXFERCFG_NUM_XFER_RES(1); + + return dwc3_send_gadget_ep_cmd(dwc, dep->number, + DWC3_DEPCMD_SETTRANSFRESOURCE, ¶ms); +} + +/** + * __dwc3_gadget_ep_enable - Initializes a HW endpoint + * @dep: endpoint to be initialized + * @desc: USB Endpoint Descriptor + * + * Caller should take care of locking + */ +static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, + const struct usb_endpoint_descriptor *desc) +{ + struct dwc3 *dwc = dep->dwc; + u32 reg; + int ret = -ENOMEM; + + if (!(dep->flags & DWC3_EP_ENABLED)) { + ret = dwc3_gadget_start_config(dwc, dep); + if (ret) + return ret; + } + + ret = dwc3_gadget_set_ep_config(dwc, dep, desc); + if (ret) + return ret; + + if (!(dep->flags & DWC3_EP_ENABLED)) { + struct dwc3_trb_hw *trb_st_hw; + struct dwc3_trb_hw *trb_link_hw; + struct dwc3_trb trb_link; + + ret = dwc3_gadget_set_xfer_resource(dwc, dep); + if (ret) + return ret; + + dep->desc = desc; + dep->type = usb_endpoint_type(desc); + dep->flags |= DWC3_EP_ENABLED; + + reg = dwc3_readl(dwc->regs, DWC3_DALEPENA); + reg |= DWC3_DALEPENA_EP(dep->number); + dwc3_writel(dwc->regs, DWC3_DALEPENA, reg); + + if (!usb_endpoint_xfer_isoc(desc)) + return 0; + + memset(&trb_link, 0, sizeof(trb_link)); + + /* Link TRB for ISOC. The HWO but is never reset */ + trb_st_hw = &dep->trb_pool[0]; + + trb_link.bplh = dwc3_trb_dma_offset(dep, trb_st_hw); + trb_link.trbctl = DWC3_TRBCTL_LINK_TRB; + trb_link.hwo = true; + + trb_link_hw = &dep->trb_pool[DWC3_TRB_NUM - 1]; + dwc3_trb_to_hw(&trb_link, trb_link_hw); + } + + return 0; +} + +static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum); +static void dwc3_remove_requests(struct dwc3 *dwc, struct dwc3_ep *dep) +{ + struct dwc3_request *req; + + if (!list_empty(&dep->req_queued)) + dwc3_stop_active_transfer(dwc, dep->number); + + while (!list_empty(&dep->request_list)) { + req = next_request(&dep->request_list); + + dwc3_gadget_giveback(dep, req, -ESHUTDOWN); + } +} + +/** + * __dwc3_gadget_ep_disable - Disables a HW endpoint + * @dep: the endpoint to disable + * + * This function also removes requests which are currently processed ny the + * hardware and those which are not yet scheduled. + * Caller should take care of locking. + */ +static int __dwc3_gadget_ep_disable(struct dwc3_ep *dep) +{ + struct dwc3 *dwc = dep->dwc; + u32 reg; + + dwc3_remove_requests(dwc, dep); + + reg = dwc3_readl(dwc->regs, DWC3_DALEPENA); + reg &= ~DWC3_DALEPENA_EP(dep->number); + dwc3_writel(dwc->regs, DWC3_DALEPENA, reg); + + dep->stream_capable = false; + dep->desc = NULL; + dep->type = 0; + dep->flags = 0; + + return 0; +} + +/* -------------------------------------------------------------------------- */ + +static int dwc3_gadget_ep0_enable(struct usb_ep *ep, + const struct usb_endpoint_descriptor *desc) +{ + return -EINVAL; +} + +static int dwc3_gadget_ep0_disable(struct usb_ep *ep) +{ + return -EINVAL; +} + +/* -------------------------------------------------------------------------- */ + +static int dwc3_gadget_ep_enable(struct usb_ep *ep, + const struct usb_endpoint_descriptor *desc) +{ + struct dwc3_ep *dep; + struct dwc3 *dwc; + unsigned long flags; + int ret; + + if (!ep || !desc || desc->bDescriptorType != USB_DT_ENDPOINT) { + pr_debug("dwc3: invalid parameters\n"); + return -EINVAL; + } + + if (!desc->wMaxPacketSize) { + pr_debug("dwc3: missing wMaxPacketSize\n"); + return -EINVAL; + } + + dep = to_dwc3_ep(ep); + dwc = dep->dwc; + + switch (usb_endpoint_type(desc)) { + case USB_ENDPOINT_XFER_CONTROL: + strncat(dep->name, "-control", sizeof(dep->name)); + break; + case USB_ENDPOINT_XFER_ISOC: + strncat(dep->name, "-isoc", sizeof(dep->name)); + break; + case USB_ENDPOINT_XFER_BULK: + strncat(dep->name, "-bulk", sizeof(dep->name)); + break; + case USB_ENDPOINT_XFER_INT: + strncat(dep->name, "-int", sizeof(dep->name)); + break; + default: + dev_err(dwc->dev, "invalid endpoint transfer type\n"); + } + + if (dep->flags & DWC3_EP_ENABLED) { + dev_WARN_ONCE(dwc->dev, true, "%s is already enabled\n", + dep->name); + return 0; + } + + dev_vdbg(dwc->dev, "Enabling %s\n", dep->name); + + spin_lock_irqsave(&dwc->lock, flags); + ret = __dwc3_gadget_ep_enable(dep, desc); + spin_unlock_irqrestore(&dwc->lock, flags); + + return ret; +} + +static int dwc3_gadget_ep_disable(struct usb_ep *ep) +{ + struct dwc3_ep *dep; + struct dwc3 *dwc; + unsigned long flags; + int ret; + + if (!ep) { + pr_debug("dwc3: invalid parameters\n"); + return -EINVAL; + } + + dep = to_dwc3_ep(ep); + dwc = dep->dwc; + + if (!(dep->flags & DWC3_EP_ENABLED)) { + dev_WARN_ONCE(dwc->dev, true, "%s is already disabled\n", + dep->name); + return 0; + } + + snprintf(dep->name, sizeof(dep->name), "ep%d%s", + dep->number >> 1, + (dep->number & 1) ? "in" : "out"); + + spin_lock_irqsave(&dwc->lock, flags); + ret = __dwc3_gadget_ep_disable(dep); + spin_unlock_irqrestore(&dwc->lock, flags); + + return ret; +} + +static struct usb_request *dwc3_gadget_ep_alloc_request(struct usb_ep *ep, + gfp_t gfp_flags) +{ + struct dwc3_request *req; + struct dwc3_ep *dep = to_dwc3_ep(ep); + struct dwc3 *dwc = dep->dwc; + + req = kzalloc(sizeof(*req), gfp_flags); + if (!req) { + dev_err(dwc->dev, "not enough memory\n"); + return NULL; + } + + req->epnum = dep->number; + req->dep = dep; + req->request.dma = DMA_ADDR_INVALID; + + return &req->request; +} + +static void dwc3_gadget_ep_free_request(struct usb_ep *ep, + struct usb_request *request) +{ + struct dwc3_request *req = to_dwc3_request(request); + + kfree(req); +} + +/* + * dwc3_prepare_trbs - setup TRBs from requests + * @dep: endpoint for which requests are being prepared + * @starting: true if the endpoint is idle and no requests are queued. + * + * The functions goes through the requests list and setups TRBs for the + * transfers. The functions returns once there are not more TRBs available or + * it run out of requests. + */ +static struct dwc3_request *dwc3_prepare_trbs(struct dwc3_ep *dep, + bool starting) +{ + struct dwc3_request *req, *n, *ret = NULL; + struct dwc3_trb_hw *trb_hw; + struct dwc3_trb trb; + u32 trbs_left; + + BUILD_BUG_ON_NOT_POWER_OF_2(DWC3_TRB_NUM); + + /* the first request must not be queued */ + trbs_left = (dep->busy_slot - dep->free_slot) & DWC3_TRB_MASK; + /* + * if busy & slot are equal than it is either full or empty. If we are + * starting to proceed requests then we are empty. Otherwise we ar + * full and don't do anything + */ + if (!trbs_left) { + if (!starting) + return NULL; + trbs_left = DWC3_TRB_NUM; + /* + * In case we start from scratch, we queue the ISOC requests + * starting from slot 1. This is done because we use ring + * buffer and have no LST bit to stop us. Instead, we place + * IOC bit TRB_NUM/4. We try to avoid to having an interrupt + * after the first request so we start at slot 1 and have + * 7 requests proceed before we hit the first IOC. + * Other transfer types don't use the ring buffer and are + * processed from the first TRB until the last one. Since we + * don't wrap around we have to start at the beginning. + */ + if (usb_endpoint_xfer_isoc(dep->desc)) { + dep->busy_slot = 1; + dep->free_slot = 1; + } else { + dep->busy_slot = 0; + dep->free_slot = 0; + } + } + + /* The last TRB is a link TRB, not used for xfer */ + if ((trbs_left <= 1) && usb_endpoint_xfer_isoc(dep->desc)) + return NULL; + + list_for_each_entry_safe(req, n, &dep->request_list, list) { + unsigned int last_one = 0; + unsigned int cur_slot; + + trb_hw = &dep->trb_pool[dep->free_slot & DWC3_TRB_MASK]; + cur_slot = dep->free_slot; + dep->free_slot++; + + /* Skip the LINK-TRB on ISOC */ + if (((cur_slot & DWC3_TRB_MASK) == DWC3_TRB_NUM - 1) && + usb_endpoint_xfer_isoc(dep->desc)) + continue; + + dwc3_gadget_move_request_queued(req); + memset(&trb, 0, sizeof(trb)); + trbs_left--; + + /* Is our TRB pool empty? */ + if (!trbs_left) + last_one = 1; + /* Is this the last request? */ + if (list_empty(&dep->request_list)) + last_one = 1; + + /* + * FIXME we shouldn't need to set LST bit always but we are + * facing some weird problem with the Hardware where it doesn't + * complete even though it has been previously started. + * + * While we're debugging the problem, as a workaround to + * multiple TRBs handling, use only one TRB at a time. + */ + last_one = 1; + + req->trb = trb_hw; + if (!ret) + ret = req; + + trb.bplh = req->request.dma; + + if (usb_endpoint_xfer_isoc(dep->desc)) { + trb.isp_imi = true; + trb.csp = true; + } else { + trb.lst = last_one; + } + + if (usb_endpoint_xfer_bulk(dep->desc) && dep->stream_capable) + trb.sid_sofn = req->request.stream_id; + + switch (usb_endpoint_type(dep->desc)) { + case USB_ENDPOINT_XFER_CONTROL: + trb.trbctl = DWC3_TRBCTL_CONTROL_SETUP; + break; + + case USB_ENDPOINT_XFER_ISOC: + trb.trbctl = DWC3_TRBCTL_ISOCHRONOUS_FIRST; + + /* IOC every DWC3_TRB_NUM / 4 so we can refill */ + if (!(cur_slot % (DWC3_TRB_NUM / 4))) + trb.ioc = last_one; + break; + + case USB_ENDPOINT_XFER_BULK: + case USB_ENDPOINT_XFER_INT: + trb.trbctl = DWC3_TRBCTL_NORMAL; + break; + default: + /* + * This is only possible with faulty memory because we + * checked it already :) + */ + BUG(); + } + + trb.length = req->request.length; + trb.hwo = true; + + dwc3_trb_to_hw(&trb, trb_hw); + req->trb_dma = dwc3_trb_dma_offset(dep, trb_hw); + + if (last_one) + break; + } + + return ret; +} + +static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param, + int start_new) +{ + struct dwc3_gadget_ep_cmd_params params; + struct dwc3_request *req; + struct dwc3 *dwc = dep->dwc; + int ret; + u32 cmd; + + if (start_new && (dep->flags & DWC3_EP_BUSY)) { + dev_vdbg(dwc->dev, "%s: endpoint busy\n", dep->name); + return -EBUSY; + } + dep->flags &= ~DWC3_EP_PENDING_REQUEST; + + /* + * If we are getting here after a short-out-packet we don't enqueue any + * new requests as we try to set the IOC bit only on the last request. + */ + if (start_new) { + if (list_empty(&dep->req_queued)) + dwc3_prepare_trbs(dep, start_new); + + /* req points to the first request which will be sent */ + req = next_request(&dep->req_queued); + } else { + /* + * req points to the first request where HWO changed + * from 0 to 1 + */ + req = dwc3_prepare_trbs(dep, start_new); + } + if (!req) { + dep->flags |= DWC3_EP_PENDING_REQUEST; + return 0; + } + + memset(¶ms, 0, sizeof(params)); + params.param0 = upper_32_bits(req->trb_dma); + params.param1 = lower_32_bits(req->trb_dma); + + if (start_new) + cmd = DWC3_DEPCMD_STARTTRANSFER; + else + cmd = DWC3_DEPCMD_UPDATETRANSFER; + + cmd |= DWC3_DEPCMD_PARAM(cmd_param); + ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, cmd, ¶ms); + if (ret < 0) { + dev_dbg(dwc->dev, "failed to send STARTTRANSFER command\n"); + + /* + * FIXME we need to iterate over the list of requests + * here and stop, unmap, free and del each of the linked + * requests instead of we do now. + */ + dwc3_unmap_buffer_from_dma(req); + list_del(&req->list); + return ret; + } + + dep->flags |= DWC3_EP_BUSY; + dep->res_trans_idx = dwc3_gadget_ep_get_transfer_index(dwc, + dep->number); + if (!dep->res_trans_idx) + printk_once(KERN_ERR "%s() res_trans_idx is invalid\n", __func__); + return 0; +} + +static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req) +{ + req->request.actual = 0; + req->request.status = -EINPROGRESS; + req->direction = dep->direction; + req->epnum = dep->number; + + /* + * We only add to our list of requests now and + * start consuming the list once we get XferNotReady + * IRQ. + * + * That way, we avoid doing anything that we don't need + * to do now and defer it until the point we receive a + * particular token from the Host side. + * + * This will also avoid Host cancelling URBs due to too + * many NACKs. + */ + dwc3_map_buffer_to_dma(req); + list_add_tail(&req->list, &dep->request_list); + + /* + * There is one special case: XferNotReady with + * empty list of requests. We need to kick the + * transfer here in that situation, otherwise + * we will be NAKing forever. + * + * If we get XferNotReady before gadget driver + * has a chance to queue a request, we will ACK + * the IRQ but won't be able to receive the data + * until the next request is queued. The following + * code is handling exactly that. + */ + if (dep->flags & DWC3_EP_PENDING_REQUEST) { + int ret; + int start_trans; + + start_trans = 1; + if (usb_endpoint_xfer_isoc(dep->endpoint.desc) && + dep->flags & DWC3_EP_BUSY) + start_trans = 0; + + ret = __dwc3_gadget_kick_transfer(dep, 0, start_trans); + if (ret && ret != -EBUSY) { + struct dwc3 *dwc = dep->dwc; + + dev_dbg(dwc->dev, "%s: failed to kick transfers\n", + dep->name); + } + }; + + return 0; +} + +static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request, + gfp_t gfp_flags) +{ + struct dwc3_request *req = to_dwc3_request(request); + struct dwc3_ep *dep = to_dwc3_ep(ep); + struct dwc3 *dwc = dep->dwc; + + unsigned long flags; + + int ret; + + if (!dep->desc) { + dev_dbg(dwc->dev, "trying to queue request %p to disabled %s\n", + request, ep->name); + return -ESHUTDOWN; + } + + dev_vdbg(dwc->dev, "queing request %p to %s length %d\n", + request, ep->name, request->length); + + spin_lock_irqsave(&dwc->lock, flags); + ret = __dwc3_gadget_ep_queue(dep, req); + spin_unlock_irqrestore(&dwc->lock, flags); + + return ret; +} + +static int dwc3_gadget_ep_dequeue(struct usb_ep *ep, + struct usb_request *request) +{ + struct dwc3_request *req = to_dwc3_request(request); + struct dwc3_request *r = NULL; + + struct dwc3_ep *dep = to_dwc3_ep(ep); + struct dwc3 *dwc = dep->dwc; + + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(&dwc->lock, flags); + + list_for_each_entry(r, &dep->request_list, list) { + if (r == req) + break; + } + + if (r != req) { + list_for_each_entry(r, &dep->req_queued, list) { + if (r == req) + break; + } + if (r == req) { + /* wait until it is processed */ + dwc3_stop_active_transfer(dwc, dep->number); + goto out0; + } + dev_err(dwc->dev, "request %p was not queued to %s\n", + request, ep->name); + ret = -EINVAL; + goto out0; + } + + /* giveback the request */ + dwc3_gadget_giveback(dep, req, -ECONNRESET); + +out0: + spin_unlock_irqrestore(&dwc->lock, flags); + + return ret; +} + +int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value) +{ + struct dwc3_gadget_ep_cmd_params params; + struct dwc3 *dwc = dep->dwc; + int ret; + + memset(¶ms, 0x00, sizeof(params)); + + if (value) { + if (dep->number == 0 || dep->number == 1) { + /* + * Whenever EP0 is stalled, we will restart + * the state machine, thus moving back to + * Setup Phase + */ + dwc->ep0state = EP0_SETUP_PHASE; + } + + ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, + DWC3_DEPCMD_SETSTALL, ¶ms); + if (ret) + dev_err(dwc->dev, "failed to %s STALL on %s\n", + value ? "set" : "clear", + dep->name); + else + dep->flags |= DWC3_EP_STALL; + } else { + if (dep->flags & DWC3_EP_WEDGE) + return 0; + + ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, + DWC3_DEPCMD_CLEARSTALL, ¶ms); + if (ret) + dev_err(dwc->dev, "failed to %s STALL on %s\n", + value ? "set" : "clear", + dep->name); + else + dep->flags &= ~DWC3_EP_STALL; + } + + return ret; +} + +static int dwc3_gadget_ep_set_halt(struct usb_ep *ep, int value) +{ + struct dwc3_ep *dep = to_dwc3_ep(ep); + struct dwc3 *dwc = dep->dwc; + + unsigned long flags; + + int ret; + + spin_lock_irqsave(&dwc->lock, flags); + + if (usb_endpoint_xfer_isoc(dep->desc)) { + dev_err(dwc->dev, "%s is of Isochronous type\n", dep->name); + ret = -EINVAL; + goto out; + } + + ret = __dwc3_gadget_ep_set_halt(dep, value); +out: + spin_unlock_irqrestore(&dwc->lock, flags); + + return ret; +} + +static int dwc3_gadget_ep_set_wedge(struct usb_ep *ep) +{ + struct dwc3_ep *dep = to_dwc3_ep(ep); + + dep->flags |= DWC3_EP_WEDGE; + + return dwc3_gadget_ep_set_halt(ep, 1); +} + +/* -------------------------------------------------------------------------- */ + +static struct usb_endpoint_descriptor dwc3_gadget_ep0_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bmAttributes = USB_ENDPOINT_XFER_CONTROL, +}; + +static const struct usb_ep_ops dwc3_gadget_ep0_ops = { + .enable = dwc3_gadget_ep0_enable, + .disable = dwc3_gadget_ep0_disable, + .alloc_request = dwc3_gadget_ep_alloc_request, + .free_request = dwc3_gadget_ep_free_request, + .queue = dwc3_gadget_ep0_queue, + .dequeue = dwc3_gadget_ep_dequeue, + .set_halt = dwc3_gadget_ep_set_halt, + .set_wedge = dwc3_gadget_ep_set_wedge, +}; + +static const struct usb_ep_ops dwc3_gadget_ep_ops = { + .enable = dwc3_gadget_ep_enable, + .disable = dwc3_gadget_ep_disable, + .alloc_request = dwc3_gadget_ep_alloc_request, + .free_request = dwc3_gadget_ep_free_request, + .queue = dwc3_gadget_ep_queue, + .dequeue = dwc3_gadget_ep_dequeue, + .set_halt = dwc3_gadget_ep_set_halt, + .set_wedge = dwc3_gadget_ep_set_wedge, +}; + +/* -------------------------------------------------------------------------- */ + +static int dwc3_gadget_get_frame(struct usb_gadget *g) +{ + struct dwc3 *dwc = gadget_to_dwc(g); + u32 reg; + + reg = dwc3_readl(dwc->regs, DWC3_DSTS); + return DWC3_DSTS_SOFFN(reg); +} + +static int dwc3_gadget_wakeup(struct usb_gadget *g) +{ + struct dwc3 *dwc = gadget_to_dwc(g); + + unsigned long timeout; + unsigned long flags; + + u32 reg; + + int ret = 0; + + u8 link_state; + u8 speed; + + spin_lock_irqsave(&dwc->lock, flags); + + /* + * According to the Databook Remote wakeup request should + * be issued only when the device is in early suspend state. + * + * We can check that via USB Link State bits in DSTS register. + */ + reg = dwc3_readl(dwc->regs, DWC3_DSTS); + + speed = reg & DWC3_DSTS_CONNECTSPD; + if (speed == DWC3_DSTS_SUPERSPEED) { + dev_dbg(dwc->dev, "no wakeup on SuperSpeed\n"); + ret = -EINVAL; + goto out; + } + + link_state = DWC3_DSTS_USBLNKST(reg); + + switch (link_state) { + case DWC3_LINK_STATE_RX_DET: /* in HS, means Early Suspend */ + case DWC3_LINK_STATE_U3: /* in HS, means SUSPEND */ + break; + default: + dev_dbg(dwc->dev, "can't wakeup from link state %d\n", + link_state); + ret = -EINVAL; + goto out; + } + + reg = dwc3_readl(dwc->regs, DWC3_DCTL); + + /* + * Switch link state to Recovery. In HS/FS/LS this means + * RemoteWakeup Request + */ + reg |= DWC3_DCTL_ULSTCHNG_RECOVERY; + dwc3_writel(dwc->regs, DWC3_DCTL, reg); + + /* wait for at least 2000us */ + usleep_range(2000, 2500); + + /* write zeroes to Link Change Request */ + reg &= ~DWC3_DCTL_ULSTCHNGREQ_MASK; + dwc3_writel(dwc->regs, DWC3_DCTL, reg); + + /* pool until Link State change to ON */ + timeout = jiffies + msecs_to_jiffies(100); + + while (!(time_after(jiffies, timeout))) { + reg = dwc3_readl(dwc->regs, DWC3_DSTS); + + /* in HS, means ON */ + if (DWC3_DSTS_USBLNKST(reg) == DWC3_LINK_STATE_U0) + break; + } + + if (DWC3_DSTS_USBLNKST(reg) != DWC3_LINK_STATE_U0) { + dev_err(dwc->dev, "failed to send remote wakeup\n"); + ret = -EINVAL; + } + +out: + spin_unlock_irqrestore(&dwc->lock, flags); + + return ret; +} + +static int dwc3_gadget_set_selfpowered(struct usb_gadget *g, + int is_selfpowered) +{ + struct dwc3 *dwc = gadget_to_dwc(g); + + dwc->is_selfpowered = !!is_selfpowered; + + return 0; +} + +static void dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on) +{ + u32 reg; + u32 timeout = 500; + + reg = dwc3_readl(dwc->regs, DWC3_DCTL); + if (is_on) + reg |= DWC3_DCTL_RUN_STOP; + else + reg &= ~DWC3_DCTL_RUN_STOP; + + dwc3_writel(dwc->regs, DWC3_DCTL, reg); + + do { + reg = dwc3_readl(dwc->regs, DWC3_DSTS); + if (is_on) { + if (!(reg & DWC3_DSTS_DEVCTRLHLT)) + break; + } else { + if (reg & DWC3_DSTS_DEVCTRLHLT) + break; + } + timeout--; + if (!timeout) + break; + udelay(1); + } while (1); + + dev_vdbg(dwc->dev, "gadget %s data soft-%s\n", + dwc->gadget_driver + ? dwc->gadget_driver->function : "no-function", + is_on ? "connect" : "disconnect"); +} + +static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on) +{ + struct dwc3 *dwc = gadget_to_dwc(g); + unsigned long flags; + + is_on = !!is_on; + + spin_lock_irqsave(&dwc->lock, flags); + dwc3_gadget_run_stop(dwc, is_on); + spin_unlock_irqrestore(&dwc->lock, flags); + + return 0; +} + +static int dwc3_gadget_start(struct usb_gadget *g, + struct usb_gadget_driver *driver) +{ + struct dwc3 *dwc = gadget_to_dwc(g); + struct dwc3_ep *dep; + unsigned long flags; + int ret = 0; + u32 reg; + + spin_lock_irqsave(&dwc->lock, flags); + + if (dwc->gadget_driver) { + dev_err(dwc->dev, "%s is already bound to %s\n", + dwc->gadget.name, + dwc->gadget_driver->driver.name); + ret = -EBUSY; + goto err0; + } + + dwc->gadget_driver = driver; + dwc->gadget.dev.driver = &driver->driver; + + reg = dwc3_readl(dwc->regs, DWC3_GCTL); + + reg &= ~DWC3_GCTL_SCALEDOWN(3); + reg &= ~DWC3_GCTL_PRTCAPDIR(DWC3_GCTL_PRTCAP_OTG); + reg &= ~DWC3_GCTL_DISSCRAMBLE; + reg |= DWC3_GCTL_PRTCAPDIR(DWC3_GCTL_PRTCAP_DEVICE); + + switch (DWC3_GHWPARAMS1_EN_PWROPT(dwc->hwparams.hwparams0)) { + case DWC3_GHWPARAMS1_EN_PWROPT_CLK: + reg &= ~DWC3_GCTL_DSBLCLKGTNG; + break; + default: + dev_dbg(dwc->dev, "No power optimization available\n"); + } + + /* + * WORKAROUND: DWC3 revisions <1.90a have a bug + * when The device fails to connect at SuperSpeed + * and falls back to high-speed mode which causes + * the device to enter in a Connect/Disconnect loop + */ + if (dwc->revision < DWC3_REVISION_190A) + reg |= DWC3_GCTL_U2RSTECN; + + dwc3_writel(dwc->regs, DWC3_GCTL, reg); + + reg = dwc3_readl(dwc->regs, DWC3_DCFG); + reg &= ~(DWC3_DCFG_SPEED_MASK); + reg |= DWC3_DCFG_SUPERSPEED; + dwc3_writel(dwc->regs, DWC3_DCFG, reg); + + dwc->start_config_issued = false; + + /* Start with SuperSpeed Default */ + dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512); + + dep = dwc->eps[0]; + ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc); + if (ret) { + dev_err(dwc->dev, "failed to enable %s\n", dep->name); + goto err0; + } + + dep = dwc->eps[1]; + ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc); + if (ret) { + dev_err(dwc->dev, "failed to enable %s\n", dep->name); + goto err1; + } + + /* begin to receive SETUP packets */ + dwc->ep0state = EP0_SETUP_PHASE; + dwc3_ep0_out_start(dwc); + + spin_unlock_irqrestore(&dwc->lock, flags); + + return 0; + +err1: + __dwc3_gadget_ep_disable(dwc->eps[0]); + +err0: + spin_unlock_irqrestore(&dwc->lock, flags); + + return ret; +} + +static int dwc3_gadget_stop(struct usb_gadget *g, + struct usb_gadget_driver *driver) +{ + struct dwc3 *dwc = gadget_to_dwc(g); + unsigned long flags; + + spin_lock_irqsave(&dwc->lock, flags); + + __dwc3_gadget_ep_disable(dwc->eps[0]); + __dwc3_gadget_ep_disable(dwc->eps[1]); + + dwc->gadget_driver = NULL; + dwc->gadget.dev.driver = NULL; + + spin_unlock_irqrestore(&dwc->lock, flags); + + return 0; +} +static const struct usb_gadget_ops dwc3_gadget_ops = { + .get_frame = dwc3_gadget_get_frame, + .wakeup = dwc3_gadget_wakeup, + .set_selfpowered = dwc3_gadget_set_selfpowered, + .pullup = dwc3_gadget_pullup, + .udc_start = dwc3_gadget_start, + .udc_stop = dwc3_gadget_stop, +}; + +/* -------------------------------------------------------------------------- */ + +static int __devinit dwc3_gadget_init_endpoints(struct dwc3 *dwc) +{ + struct dwc3_ep *dep; + u8 epnum; + + INIT_LIST_HEAD(&dwc->gadget.ep_list); + + for (epnum = 0; epnum < DWC3_ENDPOINTS_NUM; epnum++) { + dep = kzalloc(sizeof(*dep), GFP_KERNEL); + if (!dep) { + dev_err(dwc->dev, "can't allocate endpoint %d\n", + epnum); + return -ENOMEM; + } + + dep->dwc = dwc; + dep->number = epnum; + dwc->eps[epnum] = dep; + + snprintf(dep->name, sizeof(dep->name), "ep%d%s", epnum >> 1, + (epnum & 1) ? "in" : "out"); + dep->endpoint.name = dep->name; + dep->direction = (epnum & 1); + + if (epnum == 0 || epnum == 1) { + dep->endpoint.maxpacket = 512; + dep->endpoint.ops = &dwc3_gadget_ep0_ops; + if (!epnum) + dwc->gadget.ep0 = &dep->endpoint; + } else { + int ret; + + dep->endpoint.maxpacket = 1024; + dep->endpoint.ops = &dwc3_gadget_ep_ops; + list_add_tail(&dep->endpoint.ep_list, + &dwc->gadget.ep_list); + + ret = dwc3_alloc_trb_pool(dep); + if (ret) { + dev_err(dwc->dev, "%s: failed to allocate TRB pool\n", dep->name); + return ret; + } + } + INIT_LIST_HEAD(&dep->request_list); + INIT_LIST_HEAD(&dep->req_queued); + } + + return 0; +} + +static void dwc3_gadget_free_endpoints(struct dwc3 *dwc) +{ + struct dwc3_ep *dep; + u8 epnum; + + for (epnum = 0; epnum < DWC3_ENDPOINTS_NUM; epnum++) { + dep = dwc->eps[epnum]; + dwc3_free_trb_pool(dep); + + if (epnum != 0 && epnum != 1) + list_del(&dep->endpoint.ep_list); + + kfree(dep); + } +} + +static void dwc3_gadget_release(struct device *dev) +{ + dev_dbg(dev, "%s\n", __func__); +} + +/* -------------------------------------------------------------------------- */ +static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep, + const struct dwc3_event_depevt *event, int status) +{ + struct dwc3_request *req; + struct dwc3_trb trb; + unsigned int count; + unsigned int s_pkt = 0; + + do { + req = next_request(&dep->req_queued); + if (!req) + break; + + dwc3_trb_to_nat(req->trb, &trb); + + if (trb.hwo && status != -ESHUTDOWN) + /* + * We continue despite the error. There is not much we + * can do. If we don't clean in up we loop for ever. If + * we skip the TRB than it gets overwritten reused after + * a while since we use them in a ring buffer. a BUG() + * would help. Lets hope that if this occures, someone + * fixes the root cause instead of looking away :) + */ + dev_err(dwc->dev, "%s's TRB (%p) still owned by HW\n", + dep->name, req->trb); + count = trb.length; + + if (dep->direction) { + if (count) { + dev_err(dwc->dev, "incomplete IN transfer %s\n", + dep->name); + status = -ECONNRESET; + } + } else { + if (count && (event->status & DEPEVT_STATUS_SHORT)) + s_pkt = 1; + } + + /* + * We assume here we will always receive the entire data block + * which we should receive. Meaning, if we program RX to + * receive 4K but we receive only 2K, we assume that's all we + * should receive and we simply bounce the request back to the + * gadget driver for further processing. + */ + req->request.actual += req->request.length - count; + dwc3_gadget_giveback(dep, req, status); + if (s_pkt) + break; + if ((event->status & DEPEVT_STATUS_LST) && trb.lst) + break; + if ((event->status & DEPEVT_STATUS_IOC) && trb.ioc) + break; + } while (1); + + if ((event->status & DEPEVT_STATUS_IOC) && trb.ioc) + return 0; + return 1; +} + +static void dwc3_endpoint_transfer_complete(struct dwc3 *dwc, + struct dwc3_ep *dep, const struct dwc3_event_depevt *event, + int start_new) +{ + unsigned status = 0; + int clean_busy; + + if (event->status & DEPEVT_STATUS_BUSERR) + status = -ECONNRESET; + + clean_busy = dwc3_cleanup_done_reqs(dwc, dep, event, status); + if (clean_busy) { + dep->flags &= ~DWC3_EP_BUSY; + dep->res_trans_idx = 0; + } +} + +static void dwc3_gadget_start_isoc(struct dwc3 *dwc, + struct dwc3_ep *dep, const struct dwc3_event_depevt *event) +{ + u32 uf; + + if (list_empty(&dep->request_list)) { + dev_vdbg(dwc->dev, "ISOC ep %s run out for requests.\n", + dep->name); + return; + } + + if (event->parameters) { + u32 mask; + + mask = ~(dep->interval - 1); + uf = event->parameters & mask; + /* 4 micro frames in the future */ + uf += dep->interval * 4; + } else { + uf = 0; + } + + __dwc3_gadget_kick_transfer(dep, uf, 1); +} + +static void dwc3_process_ep_cmd_complete(struct dwc3_ep *dep, + const struct dwc3_event_depevt *event) +{ + struct dwc3 *dwc = dep->dwc; + struct dwc3_event_depevt mod_ev = *event; + + /* + * We were asked to remove one requests. It is possible that this + * request and a few other were started together and have the same + * transfer index. Since we stopped the complete endpoint we don't + * know how many requests were already completed (and not yet) + * reported and how could be done (later). We purge them all until + * the end of the list. + */ + mod_ev.status = DEPEVT_STATUS_LST; + dwc3_cleanup_done_reqs(dwc, dep, &mod_ev, -ESHUTDOWN); + dep->flags &= ~DWC3_EP_BUSY; + /* pending requets are ignored and are queued on XferNotReady */ +} + +static void dwc3_ep_cmd_compl(struct dwc3_ep *dep, + const struct dwc3_event_depevt *event) +{ + u32 param = event->parameters; + u32 cmd_type = (param >> 8) & ((1 << 5) - 1); + + switch (cmd_type) { + case DWC3_DEPCMD_ENDTRANSFER: + dwc3_process_ep_cmd_complete(dep, event); + break; + case DWC3_DEPCMD_STARTTRANSFER: + dep->res_trans_idx = param & 0x7f; + break; + default: + printk(KERN_ERR "%s() unknown /unexpected type: %d\n", + __func__, cmd_type); + break; + }; +} + +static void dwc3_endpoint_interrupt(struct dwc3 *dwc, + const struct dwc3_event_depevt *event) +{ + struct dwc3_ep *dep; + u8 epnum = event->endpoint_number; + + dep = dwc->eps[epnum]; + + dev_vdbg(dwc->dev, "%s: %s\n", dep->name, + dwc3_ep_event_string(event->endpoint_event)); + + if (epnum == 0 || epnum == 1) { + dwc3_ep0_interrupt(dwc, event); + return; + } + + switch (event->endpoint_event) { + case DWC3_DEPEVT_XFERCOMPLETE: + if (usb_endpoint_xfer_isoc(dep->desc)) { + dev_dbg(dwc->dev, "%s is an Isochronous endpoint\n", + dep->name); + return; + } + + dwc3_endpoint_transfer_complete(dwc, dep, event, 1); + break; + case DWC3_DEPEVT_XFERINPROGRESS: + if (!usb_endpoint_xfer_isoc(dep->desc)) { + dev_dbg(dwc->dev, "%s is not an Isochronous endpoint\n", + dep->name); + return; + } + + dwc3_endpoint_transfer_complete(dwc, dep, event, 0); + break; + case DWC3_DEPEVT_XFERNOTREADY: + if (usb_endpoint_xfer_isoc(dep->desc)) { + dwc3_gadget_start_isoc(dwc, dep, event); + } else { + int ret; + + dev_vdbg(dwc->dev, "%s: reason %s\n", + dep->name, event->status + ? "Transfer Active" + : "Transfer Not Active"); + + ret = __dwc3_gadget_kick_transfer(dep, 0, 1); + if (!ret || ret == -EBUSY) + return; + + dev_dbg(dwc->dev, "%s: failed to kick transfers\n", + dep->name); + } + + break; + case DWC3_DEPEVT_STREAMEVT: + if (!usb_endpoint_xfer_bulk(dep->desc)) { + dev_err(dwc->dev, "Stream event for non-Bulk %s\n", + dep->name); + return; + } + + switch (event->status) { + case DEPEVT_STREAMEVT_FOUND: + dev_vdbg(dwc->dev, "Stream %d found and started\n", + event->parameters); + + break; + case DEPEVT_STREAMEVT_NOTFOUND: + /* FALLTHROUGH */ + default: + dev_dbg(dwc->dev, "Couldn't find suitable stream\n"); + } + break; + case DWC3_DEPEVT_RXTXFIFOEVT: + dev_dbg(dwc->dev, "%s FIFO Overrun\n", dep->name); + break; + case DWC3_DEPEVT_EPCMDCMPLT: + dwc3_ep_cmd_compl(dep, event); + break; + } +} + +static void dwc3_disconnect_gadget(struct dwc3 *dwc) +{ + if (dwc->gadget_driver && dwc->gadget_driver->disconnect) { + spin_unlock(&dwc->lock); + dwc->gadget_driver->disconnect(&dwc->gadget); + spin_lock(&dwc->lock); + } +} + +static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum) +{ + struct dwc3_ep *dep; + struct dwc3_gadget_ep_cmd_params params; + u32 cmd; + int ret; + + dep = dwc->eps[epnum]; + + WARN_ON(!dep->res_trans_idx); + if (dep->res_trans_idx) { + cmd = DWC3_DEPCMD_ENDTRANSFER; + cmd |= DWC3_DEPCMD_HIPRI_FORCERM | DWC3_DEPCMD_CMDIOC; + cmd |= DWC3_DEPCMD_PARAM(dep->res_trans_idx); + memset(¶ms, 0, sizeof(params)); + ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, cmd, ¶ms); + WARN_ON_ONCE(ret); + dep->res_trans_idx = 0; + } +} + +static void dwc3_stop_active_transfers(struct dwc3 *dwc) +{ + u32 epnum; + + for (epnum = 2; epnum < DWC3_ENDPOINTS_NUM; epnum++) { + struct dwc3_ep *dep; + + dep = dwc->eps[epnum]; + if (!(dep->flags & DWC3_EP_ENABLED)) + continue; + + dwc3_remove_requests(dwc, dep); + } +} + +static void dwc3_clear_stall_all_ep(struct dwc3 *dwc) +{ + u32 epnum; + + for (epnum = 1; epnum < DWC3_ENDPOINTS_NUM; epnum++) { + struct dwc3_ep *dep; + struct dwc3_gadget_ep_cmd_params params; + int ret; + + dep = dwc->eps[epnum]; + + if (!(dep->flags & DWC3_EP_STALL)) + continue; + + dep->flags &= ~DWC3_EP_STALL; + + memset(¶ms, 0, sizeof(params)); + ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, + DWC3_DEPCMD_CLEARSTALL, ¶ms); + WARN_ON_ONCE(ret); + } +} + +static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc) +{ + dev_vdbg(dwc->dev, "%s\n", __func__); +#if 0 + XXX + U1/U2 is powersave optimization. Skip it for now. Anyway we need to + enable it before we can disable it. + + reg = dwc3_readl(dwc->regs, DWC3_DCTL); + reg &= ~DWC3_DCTL_INITU1ENA; + dwc3_writel(dwc->regs, DWC3_DCTL, reg); + + reg &= ~DWC3_DCTL_INITU2ENA; + dwc3_writel(dwc->regs, DWC3_DCTL, reg); +#endif + + dwc3_stop_active_transfers(dwc); + dwc3_disconnect_gadget(dwc); + dwc->start_config_issued = false; + + dwc->gadget.speed = USB_SPEED_UNKNOWN; +} + +static void dwc3_gadget_usb3_phy_power(struct dwc3 *dwc, int on) +{ + u32 reg; + + reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0)); + + if (on) + reg &= ~DWC3_GUSB3PIPECTL_SUSPHY; + else + reg |= DWC3_GUSB3PIPECTL_SUSPHY; + + dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg); +} + +static void dwc3_gadget_usb2_phy_power(struct dwc3 *dwc, int on) +{ + u32 reg; + + reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); + + if (on) + reg &= ~DWC3_GUSB2PHYCFG_SUSPHY; + else + reg |= DWC3_GUSB2PHYCFG_SUSPHY; + + dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); +} + +static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc) +{ + u32 reg; + + dev_vdbg(dwc->dev, "%s\n", __func__); + + /* Enable PHYs */ + dwc3_gadget_usb2_phy_power(dwc, true); + dwc3_gadget_usb3_phy_power(dwc, true); + + if (dwc->gadget.speed != USB_SPEED_UNKNOWN) + dwc3_disconnect_gadget(dwc); + + reg = dwc3_readl(dwc->regs, DWC3_DCTL); + reg &= ~DWC3_DCTL_TSTCTRL_MASK; + dwc3_writel(dwc->regs, DWC3_DCTL, reg); + + dwc3_stop_active_transfers(dwc); + dwc3_clear_stall_all_ep(dwc); + dwc->start_config_issued = false; + + /* Reset device address to zero */ + reg = dwc3_readl(dwc->regs, DWC3_DCFG); + reg &= ~(DWC3_DCFG_DEVADDR_MASK); + dwc3_writel(dwc->regs, DWC3_DCFG, reg); +} + +static void dwc3_update_ram_clk_sel(struct dwc3 *dwc, u32 speed) +{ + u32 reg; + u32 usb30_clock = DWC3_GCTL_CLK_BUS; + + /* + * We change the clock only at SS but I dunno why I would want to do + * this. Maybe it becomes part of the power saving plan. + */ + + if (speed != DWC3_DSTS_SUPERSPEED) + return; + + /* + * RAMClkSel is reset to 0 after USB reset, so it must be reprogrammed + * each time on Connect Done. + */ + if (!usb30_clock) + return; + + reg = dwc3_readl(dwc->regs, DWC3_GCTL); + reg |= DWC3_GCTL_RAMCLKSEL(usb30_clock); + dwc3_writel(dwc->regs, DWC3_GCTL, reg); +} + +static void dwc3_gadget_disable_phy(struct dwc3 *dwc, u8 speed) +{ + switch (speed) { + case USB_SPEED_SUPER: + dwc3_gadget_usb2_phy_power(dwc, false); + break; + case USB_SPEED_HIGH: + case USB_SPEED_FULL: + case USB_SPEED_LOW: + dwc3_gadget_usb3_phy_power(dwc, false); + break; + } +} + +static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc) +{ + struct dwc3_gadget_ep_cmd_params params; + struct dwc3_ep *dep; + int ret; + u32 reg; + u8 speed; + + dev_vdbg(dwc->dev, "%s\n", __func__); + + memset(¶ms, 0x00, sizeof(params)); + + reg = dwc3_readl(dwc->regs, DWC3_DSTS); + speed = reg & DWC3_DSTS_CONNECTSPD; + dwc->speed = speed; + + dwc3_update_ram_clk_sel(dwc, speed); + + switch (speed) { + case DWC3_DCFG_SUPERSPEED: + dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512); + dwc->gadget.ep0->maxpacket = 512; + dwc->gadget.speed = USB_SPEED_SUPER; + break; + case DWC3_DCFG_HIGHSPEED: + dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(64); + dwc->gadget.ep0->maxpacket = 64; + dwc->gadget.speed = USB_SPEED_HIGH; + break; + case DWC3_DCFG_FULLSPEED2: + case DWC3_DCFG_FULLSPEED1: + dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(64); + dwc->gadget.ep0->maxpacket = 64; + dwc->gadget.speed = USB_SPEED_FULL; + break; + case DWC3_DCFG_LOWSPEED: + dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(8); + dwc->gadget.ep0->maxpacket = 8; + dwc->gadget.speed = USB_SPEED_LOW; + break; + } + + /* Disable unneded PHY */ + dwc3_gadget_disable_phy(dwc, dwc->gadget.speed); + + dep = dwc->eps[0]; + ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc); + if (ret) { + dev_err(dwc->dev, "failed to enable %s\n", dep->name); + return; + } + + dep = dwc->eps[1]; + ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc); + if (ret) { + dev_err(dwc->dev, "failed to enable %s\n", dep->name); + return; + } + + /* + * Configure PHY via GUSB3PIPECTLn if required. + * + * Update GTXFIFOSIZn + * + * In both cases reset values should be sufficient. + */ +} + +static void dwc3_gadget_wakeup_interrupt(struct dwc3 *dwc) +{ + dev_vdbg(dwc->dev, "%s\n", __func__); + + /* + * TODO take core out of low power mode when that's + * implemented. + */ + + dwc->gadget_driver->resume(&dwc->gadget); +} + +static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc, + unsigned int evtinfo) +{ + /* The fith bit says SuperSpeed yes or no. */ + dwc->link_state = evtinfo & DWC3_LINK_STATE_MASK; + + dev_vdbg(dwc->dev, "%s link %d\n", __func__, dwc->link_state); +} + +static void dwc3_gadget_interrupt(struct dwc3 *dwc, + const struct dwc3_event_devt *event) +{ + switch (event->type) { + case DWC3_DEVICE_EVENT_DISCONNECT: + dwc3_gadget_disconnect_interrupt(dwc); + break; + case DWC3_DEVICE_EVENT_RESET: + dwc3_gadget_reset_interrupt(dwc); + break; + case DWC3_DEVICE_EVENT_CONNECT_DONE: + dwc3_gadget_conndone_interrupt(dwc); + break; + case DWC3_DEVICE_EVENT_WAKEUP: + dwc3_gadget_wakeup_interrupt(dwc); + break; + case DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE: + dwc3_gadget_linksts_change_interrupt(dwc, event->event_info); + break; + case DWC3_DEVICE_EVENT_EOPF: + dev_vdbg(dwc->dev, "End of Periodic Frame\n"); + break; + case DWC3_DEVICE_EVENT_SOF: + dev_vdbg(dwc->dev, "Start of Periodic Frame\n"); + break; + case DWC3_DEVICE_EVENT_ERRATIC_ERROR: + dev_vdbg(dwc->dev, "Erratic Error\n"); + break; + case DWC3_DEVICE_EVENT_CMD_CMPL: + dev_vdbg(dwc->dev, "Command Complete\n"); + break; + case DWC3_DEVICE_EVENT_OVERFLOW: + dev_vdbg(dwc->dev, "Overflow\n"); + break; + default: + dev_dbg(dwc->dev, "UNKNOWN IRQ %d\n", event->type); + } +} + +static void dwc3_process_event_entry(struct dwc3 *dwc, + const union dwc3_event *event) +{ + /* Endpoint IRQ, handle it and return early */ + if (event->type.is_devspec == 0) { + /* depevt */ + return dwc3_endpoint_interrupt(dwc, &event->depevt); + } + + switch (event->type.type) { + case DWC3_EVENT_TYPE_DEV: + dwc3_gadget_interrupt(dwc, &event->devt); + break; + /* REVISIT what to do with Carkit and I2C events ? */ + default: + dev_err(dwc->dev, "UNKNOWN IRQ type %d\n", event->raw); + } +} + +static irqreturn_t dwc3_process_event_buf(struct dwc3 *dwc, u32 buf) +{ + struct dwc3_event_buffer *evt; + int left; + u32 count; + + count = dwc3_readl(dwc->regs, DWC3_GEVNTCOUNT(buf)); + count &= DWC3_GEVNTCOUNT_MASK; + if (!count) + return IRQ_NONE; + + evt = dwc->ev_buffs[buf]; + left = count; + + while (left > 0) { + union dwc3_event event; + + memcpy(&event.raw, (evt->buf + evt->lpos), sizeof(event.raw)); + dwc3_process_event_entry(dwc, &event); + /* + * XXX we wrap around correctly to the next entry as almost all + * entries are 4 bytes in size. There is one entry which has 12 + * bytes which is a regular entry followed by 8 bytes data. ATM + * I don't know how things are organized if were get next to the + * a boundary so I worry about that once we try to handle that. + */ + evt->lpos = (evt->lpos + 4) % DWC3_EVENT_BUFFERS_SIZE; + left -= 4; + + dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(buf), 4); + } + + return IRQ_HANDLED; +} + +static irqreturn_t dwc3_interrupt(int irq, void *_dwc) +{ + struct dwc3 *dwc = _dwc; + int i; + irqreturn_t ret = IRQ_NONE; + + spin_lock(&dwc->lock); + + for (i = 0; i < DWC3_EVENT_BUFFERS_NUM; i++) { + irqreturn_t status; + + status = dwc3_process_event_buf(dwc, i); + if (status == IRQ_HANDLED) + ret = status; + } + + spin_unlock(&dwc->lock); + + return ret; +} + +/** + * dwc3_gadget_init - Initializes gadget related registers + * @dwc: Pointer to out controller context structure + * + * Returns 0 on success otherwise negative errno. + */ +int __devinit dwc3_gadget_init(struct dwc3 *dwc) +{ + u32 reg; + int ret; + int irq; + + dwc->ctrl_req = dma_alloc_coherent(dwc->dev, sizeof(*dwc->ctrl_req), + &dwc->ctrl_req_addr, GFP_KERNEL); + if (!dwc->ctrl_req) { + dev_err(dwc->dev, "failed to allocate ctrl request\n"); + ret = -ENOMEM; + goto err0; + } + + dwc->ep0_trb = dma_alloc_coherent(dwc->dev, sizeof(*dwc->ep0_trb), + &dwc->ep0_trb_addr, GFP_KERNEL); + if (!dwc->ep0_trb) { + dev_err(dwc->dev, "failed to allocate ep0 trb\n"); + ret = -ENOMEM; + goto err1; + } + + dwc->setup_buf = dma_alloc_coherent(dwc->dev, + sizeof(*dwc->setup_buf) * 2, + &dwc->setup_buf_addr, GFP_KERNEL); + if (!dwc->setup_buf) { + dev_err(dwc->dev, "failed to allocate setup buffer\n"); + ret = -ENOMEM; + goto err2; + } + + dwc->ep0_bounce = dma_alloc_coherent(dwc->dev, + 512, &dwc->ep0_bounce_addr, GFP_KERNEL); + if (!dwc->ep0_bounce) { + dev_err(dwc->dev, "failed to allocate ep0 bounce buffer\n"); + ret = -ENOMEM; + goto err3; + } + + dev_set_name(&dwc->gadget.dev, "gadget"); + + dwc->gadget.ops = &dwc3_gadget_ops; + dwc->gadget.is_dualspeed = true; + dwc->gadget.speed = USB_SPEED_UNKNOWN; + dwc->gadget.dev.parent = dwc->dev; + + dma_set_coherent_mask(&dwc->gadget.dev, dwc->dev->coherent_dma_mask); + + dwc->gadget.dev.dma_parms = dwc->dev->dma_parms; + dwc->gadget.dev.dma_mask = dwc->dev->dma_mask; + dwc->gadget.dev.release = dwc3_gadget_release; + dwc->gadget.name = "dwc3-gadget"; + + /* + * REVISIT: Here we should clear all pending IRQs to be + * sure we're starting from a well known location. + */ + + ret = dwc3_gadget_init_endpoints(dwc); + if (ret) + goto err4; + + irq = platform_get_irq(to_platform_device(dwc->dev), 0); + + ret = request_irq(irq, dwc3_interrupt, IRQF_SHARED, + "dwc3", dwc); + if (ret) { + dev_err(dwc->dev, "failed to request irq #%d --> %d\n", + irq, ret); + goto err5; + } + + /* Enable all but Start and End of Frame IRQs */ + reg = (DWC3_DEVTEN_VNDRDEVTSTRCVEDEN | + DWC3_DEVTEN_EVNTOVERFLOWEN | + DWC3_DEVTEN_CMDCMPLTEN | + DWC3_DEVTEN_ERRTICERREN | + DWC3_DEVTEN_WKUPEVTEN | + DWC3_DEVTEN_ULSTCNGEN | + DWC3_DEVTEN_CONNECTDONEEN | + DWC3_DEVTEN_USBRSTEN | + DWC3_DEVTEN_DISCONNEVTEN); + dwc3_writel(dwc->regs, DWC3_DEVTEN, reg); + + ret = device_register(&dwc->gadget.dev); + if (ret) { + dev_err(dwc->dev, "failed to register gadget device\n"); + put_device(&dwc->gadget.dev); + goto err6; + } + + ret = usb_add_gadget_udc(dwc->dev, &dwc->gadget); + if (ret) { + dev_err(dwc->dev, "failed to register udc\n"); + goto err7; + } + + return 0; + +err7: + device_unregister(&dwc->gadget.dev); + +err6: + dwc3_writel(dwc->regs, DWC3_DEVTEN, 0x00); + free_irq(irq, dwc); + +err5: + dwc3_gadget_free_endpoints(dwc); + +err4: + dma_free_coherent(dwc->dev, 512, dwc->ep0_bounce, + dwc->ep0_bounce_addr); + +err3: + dma_free_coherent(dwc->dev, sizeof(*dwc->setup_buf) * 2, + dwc->setup_buf, dwc->setup_buf_addr); + +err2: + dma_free_coherent(dwc->dev, sizeof(*dwc->ep0_trb), + dwc->ep0_trb, dwc->ep0_trb_addr); + +err1: + dma_free_coherent(dwc->dev, sizeof(*dwc->ctrl_req), + dwc->ctrl_req, dwc->ctrl_req_addr); + +err0: + return ret; +} + +void dwc3_gadget_exit(struct dwc3 *dwc) +{ + int irq; + int i; + + usb_del_gadget_udc(&dwc->gadget); + irq = platform_get_irq(to_platform_device(dwc->dev), 0); + + dwc3_writel(dwc->regs, DWC3_DEVTEN, 0x00); + free_irq(irq, dwc); + + for (i = 0; i < ARRAY_SIZE(dwc->eps); i++) + __dwc3_gadget_ep_disable(dwc->eps[i]); + + dwc3_gadget_free_endpoints(dwc); + + dma_free_coherent(dwc->dev, 512, dwc->ep0_bounce, + dwc->ep0_bounce_addr); + + dma_free_coherent(dwc->dev, sizeof(*dwc->setup_buf) * 2, + dwc->setup_buf, dwc->setup_buf_addr); + + dma_free_coherent(dwc->dev, sizeof(*dwc->ep0_trb), + dwc->ep0_trb, dwc->ep0_trb_addr); + + dma_free_coherent(dwc->dev, sizeof(*dwc->ctrl_req), + dwc->ctrl_req, dwc->ctrl_req_addr); + + device_unregister(&dwc->gadget.dev); +} diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h new file mode 100644 index 000000000000..71145a449d99 --- /dev/null +++ b/drivers/usb/dwc3/gadget.h @@ -0,0 +1,211 @@ +/** + * gadget.h - DesignWare USB3 DRD Gadget Header + * + * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com + * + * Authors: Felipe Balbi <balbi@ti.com>, + * Sebastian Andrzej Siewior <bigeasy@linutronix.de> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The names of the above-listed copyright holders may not be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2, as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __DRIVERS_USB_DWC3_GADGET_H +#define __DRIVERS_USB_DWC3_GADGET_H + +#include <linux/list.h> +#include <linux/usb/gadget.h> +#include "io.h" + +struct dwc3; +#define to_dwc3_ep(ep) (container_of(ep, struct dwc3_ep, endpoint)) +#define gadget_to_dwc(g) (container_of(g, struct dwc3, gadget)) + +/* DEPCFG parameter 1 */ +#define DWC3_DEPCFG_INT_NUM(n) ((n) << 0) +#define DWC3_DEPCFG_XFER_COMPLETE_EN (1 << 8) +#define DWC3_DEPCFG_XFER_IN_PROGRESS_EN (1 << 9) +#define DWC3_DEPCFG_XFER_NOT_READY_EN (1 << 10) +#define DWC3_DEPCFG_FIFO_ERROR_EN (1 << 11) +#define DWC3_DEPCFG_STREAM_EVENT_EN (1 << 13) +#define DWC3_DEPCFG_BINTERVAL_M1(n) ((n) << 16) +#define DWC3_DEPCFG_STREAM_CAPABLE (1 << 24) +#define DWC3_DEPCFG_EP_NUMBER(n) ((n) << 25) +#define DWC3_DEPCFG_BULK_BASED (1 << 30) +#define DWC3_DEPCFG_FIFO_BASED (1 << 31) + +/* DEPCFG parameter 0 */ +#define DWC3_DEPCFG_EP_TYPE(n) ((n) << 1) +#define DWC3_DEPCFG_MAX_PACKET_SIZE(n) ((n) << 3) +#define DWC3_DEPCFG_FIFO_NUMBER(n) ((n) << 17) +#define DWC3_DEPCFG_BURST_SIZE(n) ((n) << 22) +#define DWC3_DEPCFG_DATA_SEQ_NUM(n) ((n) << 26) +#define DWC3_DEPCFG_IGN_SEQ_NUM (1 << 31) + +/* DEPXFERCFG parameter 0 */ +#define DWC3_DEPXFERCFG_NUM_XFER_RES(n) ((n) & 0xffff) + +struct dwc3_gadget_ep_cmd_params { + u32 param2; + u32 param1; + u32 param0; +}; + +/* -------------------------------------------------------------------------- */ + +struct dwc3_request { + struct usb_request request; + struct list_head list; + struct dwc3_ep *dep; + + u8 epnum; + struct dwc3_trb_hw *trb; + dma_addr_t trb_dma; + + unsigned direction:1; + unsigned mapped:1; + unsigned queued:1; +}; +#define to_dwc3_request(r) (container_of(r, struct dwc3_request, request)) + +static inline struct dwc3_request *next_request(struct list_head *list) +{ + if (list_empty(list)) + return NULL; + + return list_first_entry(list, struct dwc3_request, list); +} + +static inline void dwc3_gadget_move_request_queued(struct dwc3_request *req) +{ + struct dwc3_ep *dep = req->dep; + + req->queued = true; + list_move_tail(&req->list, &dep->req_queued); +} + +#if defined(CONFIG_USB_GADGET_DWC3) || defined(CONFIG_USB_GADGET_DWC3_MODULE) +int dwc3_gadget_init(struct dwc3 *dwc); +void dwc3_gadget_exit(struct dwc3 *dwc); +#else +static inline int dwc3_gadget_init(struct dwc3 *dwc) { return 0; } +static inline void dwc3_gadget_exit(struct dwc3 *dwc) { } +static inline int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep, + unsigned cmd, struct dwc3_gadget_ep_cmd_params *params) +{ + return 0; +} +#endif + +void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req, + int status); + +void dwc3_ep0_interrupt(struct dwc3 *dwc, const struct dwc3_event_depevt *event); +void dwc3_ep0_out_start(struct dwc3 *dwc); +int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request, + gfp_t gfp_flags); +int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value); +int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep, + unsigned cmd, struct dwc3_gadget_ep_cmd_params *params); +void dwc3_map_buffer_to_dma(struct dwc3_request *req); +void dwc3_unmap_buffer_from_dma(struct dwc3_request *req); + +/** + * dwc3_gadget_ep_get_transfer_index - Gets transfer index from HW + * @dwc: DesignWare USB3 Pointer + * @number: DWC endpoint number + * + * Caller should take care of locking + */ +static inline u32 dwc3_gadget_ep_get_transfer_index(struct dwc3 *dwc, u8 number) +{ + u32 res_id; + + res_id = dwc3_readl(dwc->regs, DWC3_DEPCMD(number)); + + return DWC3_DEPCMD_GET_RSC_IDX(res_id); +} + +/** + * dwc3_gadget_event_string - returns event name + * @event: the event code + */ +static inline const char *dwc3_gadget_event_string(u8 event) +{ + switch (event) { + case DWC3_DEVICE_EVENT_DISCONNECT: + return "Disconnect"; + case DWC3_DEVICE_EVENT_RESET: + return "Reset"; + case DWC3_DEVICE_EVENT_CONNECT_DONE: + return "Connection Done"; + case DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE: + return "Link Status Change"; + case DWC3_DEVICE_EVENT_WAKEUP: + return "WakeUp"; + case DWC3_DEVICE_EVENT_EOPF: + return "End-Of-Frame"; + case DWC3_DEVICE_EVENT_SOF: + return "Start-Of-Frame"; + case DWC3_DEVICE_EVENT_ERRATIC_ERROR: + return "Erratic Error"; + case DWC3_DEVICE_EVENT_CMD_CMPL: + return "Command Complete"; + case DWC3_DEVICE_EVENT_OVERFLOW: + return "Overflow"; + } + + return "UNKNOWN"; +} + +/** + * dwc3_ep_event_string - returns event name + * @event: then event code + */ +static inline const char *dwc3_ep_event_string(u8 event) +{ + switch (event) { + case DWC3_DEPEVT_XFERCOMPLETE: + return "Transfer Complete"; + case DWC3_DEPEVT_XFERINPROGRESS: + return "Transfer In-Progress"; + case DWC3_DEPEVT_XFERNOTREADY: + return "Transfer Not Ready"; + case DWC3_DEPEVT_RXTXFIFOEVT: + return "FIFO"; + case DWC3_DEPEVT_STREAMEVT: + return "Stream"; + case DWC3_DEPEVT_EPCMDCMPLT: + return "Endpoint Command Complete"; + } + + return "UNKNOWN"; +} + +#endif /* __DRIVERS_USB_DWC3_GADGET_H */ diff --git a/drivers/usb/dwc3/io.h b/drivers/usb/dwc3/io.h new file mode 100644 index 000000000000..bc957db1ea4b --- /dev/null +++ b/drivers/usb/dwc3/io.h @@ -0,0 +1,54 @@ +/** + * io.h - DesignWare USB3 DRD IO Header + * + * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com + * + * Authors: Felipe Balbi <balbi@ti.com>, + * Sebastian Andrzej Siewior <bigeasy@linutronix.de> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The names of the above-listed copyright holders may not be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2, as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __DRIVERS_USB_DWC3_IO_H +#define __DRIVERS_USB_DWC3_IO_H + +#include <asm/io.h> + +static inline u32 dwc3_readl(void __iomem *base, u32 offset) +{ + return readl(base + offset); +} + +static inline void dwc3_writel(void __iomem *base, u32 offset, u32 value) +{ + writel(value, base + offset); +} + +#endif /* __DRIVERS_USB_DWC3_IO_H */ diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 5a084b9cfa3c..b21cd376c11a 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -96,6 +96,22 @@ config USB_GADGET_VBUS_DRAW This value will be used except for system-specific gadget drivers that have more specific information. +config USB_GADGET_STORAGE_NUM_BUFFERS + int "Number of storage pipeline buffers" + range 2 4 + default 2 + help + Usually 2 buffers are enough to establish a good buffering + pipeline. The number may be increased in order to compensate + for a bursty VFS behaviour. For instance there may be CPU wake up + latencies that makes the VFS to appear bursty in a system with + an CPU on-demand governor. Especially if DMA is doing IO to + offload the CPU. In this case the CPU will go into power + save often and spin up occasionally to move data within VFS. + If selecting USB_GADGET_DEBUG_FILES this value may be set by + a module parameter as well. + If unsure, say 2. + # # USB Peripheral Controller Support # @@ -255,12 +271,11 @@ config USB_S3C_HSOTG integrated into the S3C64XX series SoC. config USB_IMX - tristate "Freescale IMX USB Peripheral Controller" - depends on ARCH_MX1 + tristate "Freescale i.MX1 USB Peripheral Controller" + depends on ARCH_MXC help - Freescale's IMX series include an integrated full speed - USB 1.1 device controller. The controller in the IMX series - is register-compatible. + Freescale's i.MX1 includes an integrated full speed + USB 1.1 device controller. It has Six fixed-function endpoints, as well as endpoint zero (for control transfers). @@ -303,6 +318,18 @@ config USB_PXA_U2O PXA9xx Processor series include a high speed USB2.0 device controller, which support high speed and full speed USB peripheral. +config USB_GADGET_DWC3 + tristate "DesignWare USB3.0 (DRD) Controller" + depends on USB_DWC3 + select USB_GADGET_DUALSPEED + select USB_GADGET_SUPERSPEED + help + DesignWare USB3.0 controller is a SuperSpeed USB3.0 Controller + which can be configured for peripheral-only, host-only, hub-only + and Dual-Role operation. This Controller was first integrated into + the OMAP5 series of processors. More information about the OMAP5 + version of this controller, refer to http://www.ti.com/omap5. + # # Controllers available in both integrated and discrete versions # @@ -846,6 +873,16 @@ config USB_G_NOKIA It's only really useful for N900 hardware. If you're building a kernel for N900, say Y or M here. If unsure, say N. +config USB_G_ACM_MS + tristate "CDC Composite Device (ACM and mass storage)" + depends on BLOCK + help + This driver provides two functions in one configuration: + a mass storage, and a CDC ACM (serial port) link. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "g_acm_ms". + config USB_G_MULTI tristate "Multifunction Composite Gadget (EXPERIMENTAL)" depends on BLOCK && NET diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 9ba725af4a08..b54ac6190890 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -28,7 +28,7 @@ obj-$(CONFIG_USB_S3C_HSUDC) += s3c-hsudc.o obj-$(CONFIG_USB_LANGWELL) += langwell_udc.o obj-$(CONFIG_USB_EG20T) += pch_udc.o obj-$(CONFIG_USB_PXA_U2O) += mv_udc.o -mv_udc-y := mv_udc_core.o mv_udc_phy.o +mv_udc-y := mv_udc_core.o obj-$(CONFIG_USB_CI13XXX_MSM) += ci13xxx_msm.o obj-$(CONFIG_USB_FUSB300) += fusb300_udc.o @@ -51,6 +51,7 @@ g_dbgp-y := dbgp.o g_nokia-y := nokia.o g_webcam-y := webcam.o g_ncm-y := ncm.o +g_acm_ms-y := acm_ms.o obj-$(CONFIG_USB_ZERO) += g_zero.o obj-$(CONFIG_USB_AUDIO) += g_audio.o @@ -69,3 +70,4 @@ obj-$(CONFIG_USB_G_MULTI) += g_multi.o obj-$(CONFIG_USB_G_NOKIA) += g_nokia.o obj-$(CONFIG_USB_G_WEBCAM) += g_webcam.o obj-$(CONFIG_USB_G_NCM) += g_ncm.o +obj-$(CONFIG_USB_G_ACM_MS) += g_acm_ms.o diff --git a/drivers/usb/gadget/acm_ms.c b/drivers/usb/gadget/acm_ms.c new file mode 100644 index 000000000000..fdb7aec3bd0c --- /dev/null +++ b/drivers/usb/gadget/acm_ms.c @@ -0,0 +1,256 @@ +/* + * acm_ms.c -- Composite driver, with ACM and mass storage support + * + * Copyright (C) 2008 David Brownell + * Copyright (C) 2008 Nokia Corporation + * Author: David Brownell + * Modified: Klaus Schwarzkopf <schwarzkopf@sensortherm.de> + * + * Heavily based on multi.c and cdc2.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/kernel.h> +#include <linux/utsname.h> + +#include "u_serial.h" + +#define DRIVER_DESC "Composite Gadget (ACM + MS)" +#define DRIVER_VERSION "2011/10/10" + +/*-------------------------------------------------------------------------*/ + +/* + * DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!! + * Instead: allocate your own, using normal USB-IF procedures. + */ +#define ACM_MS_VENDOR_NUM 0x1d6b /* Linux Foundation */ +#define ACM_MS_PRODUCT_NUM 0x0106 /* Composite Gadget: ACM + MS*/ + +/*-------------------------------------------------------------------------*/ + +/* + * Kbuild is not very cooperative with respect to linking separately + * compiled library objects into one module. So for now we won't use + * separate compilation ... ensuring init/exit sections work to shrink + * the runtime footprint, and giving us at least some parts of what + * a "gcc --combine ... part1.c part2.c part3.c ... " build would. + */ + +#include "composite.c" +#include "usbstring.c" +#include "config.c" +#include "epautoconf.c" +#include "u_serial.c" +#include "f_acm.c" +#include "f_mass_storage.c" + +/*-------------------------------------------------------------------------*/ + +static struct usb_device_descriptor device_desc = { + .bLength = sizeof device_desc, + .bDescriptorType = USB_DT_DEVICE, + + .bcdUSB = cpu_to_le16(0x0200), + + .bDeviceClass = USB_CLASS_MISC /* 0xEF */, + .bDeviceSubClass = 2, + .bDeviceProtocol = 1, + + /* .bMaxPacketSize0 = f(hardware) */ + + /* Vendor and product id can be overridden by module parameters. */ + .idVendor = cpu_to_le16(ACM_MS_VENDOR_NUM), + .idProduct = cpu_to_le16(ACM_MS_PRODUCT_NUM), + /* .bcdDevice = f(hardware) */ + /* .iManufacturer = DYNAMIC */ + /* .iProduct = DYNAMIC */ + /* NO SERIAL NUMBER */ + /*.bNumConfigurations = DYNAMIC*/ +}; + +static struct usb_otg_descriptor otg_descriptor = { + .bLength = sizeof otg_descriptor, + .bDescriptorType = USB_DT_OTG, + + /* + * REVISIT SRP-only hardware is possible, although + * it would not be called "OTG" ... + */ + .bmAttributes = USB_OTG_SRP | USB_OTG_HNP, +}; + +static const struct usb_descriptor_header *otg_desc[] = { + (struct usb_descriptor_header *) &otg_descriptor, + NULL, +}; + + +/* string IDs are assigned dynamically */ + +#define STRING_MANUFACTURER_IDX 0 +#define STRING_PRODUCT_IDX 1 + +static char manufacturer[50]; + +static struct usb_string strings_dev[] = { + [STRING_MANUFACTURER_IDX].s = manufacturer, + [STRING_PRODUCT_IDX].s = DRIVER_DESC, + { } /* end of list */ +}; + +static struct usb_gadget_strings stringtab_dev = { + .language = 0x0409, /* en-us */ + .strings = strings_dev, +}; + +static struct usb_gadget_strings *dev_strings[] = { + &stringtab_dev, + NULL, +}; + +/****************************** Configurations ******************************/ + +static struct fsg_module_parameters fsg_mod_data = { .stall = 1 }; +FSG_MODULE_PARAMETERS(/* no prefix */, fsg_mod_data); + +static struct fsg_common fsg_common; + +/*-------------------------------------------------------------------------*/ + +/* + * We _always_ have both ACM and mass storage functions. + */ +static int __init acm_ms_do_config(struct usb_configuration *c) +{ + int status; + + if (gadget_is_otg(c->cdev->gadget)) { + c->descriptors = otg_desc; + c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; + } + + + status = acm_bind_config(c, 0); + if (status < 0) + return status; + + status = fsg_bind_config(c->cdev, c, &fsg_common); + if (status < 0) + return status; + + return 0; +} + +static struct usb_configuration acm_ms_config_driver = { + .label = DRIVER_DESC, + .bConfigurationValue = 1, + /* .iConfiguration = DYNAMIC */ + .bmAttributes = USB_CONFIG_ATT_SELFPOWER, +}; + +/*-------------------------------------------------------------------------*/ + +static int __init acm_ms_bind(struct usb_composite_dev *cdev) +{ + int gcnum; + struct usb_gadget *gadget = cdev->gadget; + int status; + void *retp; + + /* set up serial link layer */ + status = gserial_setup(cdev->gadget, 1); + if (status < 0) + return status; + + /* set up mass storage function */ + retp = fsg_common_from_params(&fsg_common, cdev, &fsg_mod_data); + if (IS_ERR(retp)) { + status = PTR_ERR(retp); + goto fail0; + } + + /* set bcdDevice */ + gcnum = usb_gadget_controller_number(gadget); + if (gcnum >= 0) { + device_desc.bcdDevice = cpu_to_le16(0x0300 | gcnum); + } else { + WARNING(cdev, "controller '%s' not recognized; trying %s\n", + gadget->name, + acm_ms_config_driver.label); + device_desc.bcdDevice = + cpu_to_le16(0x0300 | 0x0099); + } + + /* + * Allocate string descriptor numbers ... note that string + * contents can be overridden by the composite_dev glue. + */ + + /* device descriptor strings: manufacturer, product */ + snprintf(manufacturer, sizeof manufacturer, "%s %s with %s", + init_utsname()->sysname, init_utsname()->release, + gadget->name); + status = usb_string_id(cdev); + if (status < 0) + goto fail1; + strings_dev[STRING_MANUFACTURER_IDX].id = status; + device_desc.iManufacturer = status; + + status = usb_string_id(cdev); + if (status < 0) + goto fail1; + strings_dev[STRING_PRODUCT_IDX].id = status; + device_desc.iProduct = status; + + /* register our configuration */ + status = usb_add_config(cdev, &acm_ms_config_driver, acm_ms_do_config); + if (status < 0) + goto fail1; + + dev_info(&gadget->dev, "%s, version: " DRIVER_VERSION "\n", + DRIVER_DESC); + fsg_common_put(&fsg_common); + return 0; + + /* error recovery */ +fail1: + fsg_common_put(&fsg_common); +fail0: + gserial_cleanup(); + return status; +} + +static int __exit acm_ms_unbind(struct usb_composite_dev *cdev) +{ + gserial_cleanup(); + + return 0; +} + +static struct usb_composite_driver acm_ms_driver = { + .name = "g_acm_ms", + .dev = &device_desc, + .strings = dev_strings, + .unbind = __exit_p(acm_ms_unbind), +}; + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_AUTHOR("Klaus Schwarzkopf <schwarzkopf@sensortherm.de>"); +MODULE_LICENSE("GPL v2"); + +static int __init init(void) +{ + return usb_composite_probe(&acm_ms_driver, acm_ms_bind); +} +module_init(init); + +static void __exit cleanup(void) +{ + usb_composite_unregister(&acm_ms_driver); +} +module_exit(cleanup); diff --git a/drivers/usb/gadget/amd5536udc.c b/drivers/usb/gadget/amd5536udc.c index 70f2b376c86d..4730016d7cd4 100644 --- a/drivers/usb/gadget/amd5536udc.c +++ b/drivers/usb/gadget/amd5536udc.c @@ -8,15 +8,6 @@ * 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 */ /* @@ -354,7 +345,7 @@ udc_ep_enable(struct usb_ep *usbep, const struct usb_endpoint_descriptor *desc) writel(tmp, &dev->ep[ep->num].regs->ctl); /* set max packet size */ - maxpacket = le16_to_cpu(desc->wMaxPacketSize); + maxpacket = usb_endpoint_maxp(desc); tmp = readl(&dev->ep[ep->num].regs->bufout_maxpkt); tmp = AMD_ADDBITS(tmp, maxpacket, UDC_EP_MAX_PKT_SIZE); ep->ep.maxpacket = maxpacket; @@ -3014,13 +3005,8 @@ __acquires(dev->lock) /* link up all endpoints */ udc_setup_endpoints(dev); - if (dev->gadget.speed == USB_SPEED_HIGH) { - dev_info(&dev->pdev->dev, "Connect: speed = %s\n", - "high"); - } else if (dev->gadget.speed == USB_SPEED_FULL) { - dev_info(&dev->pdev->dev, "Connect: speed = %s\n", - "full"); - } + dev_info(&dev->pdev->dev, "Connect: %s\n", + usb_speed_string(dev->gadget.speed)); /* init ep 0 */ activate_control_endpoints(dev); diff --git a/drivers/usb/gadget/amd5536udc.h b/drivers/usb/gadget/amd5536udc.h index 1d1c7543468e..f87e29c65325 100644 --- a/drivers/usb/gadget/amd5536udc.h +++ b/drivers/usb/gadget/amd5536udc.h @@ -8,15 +8,6 @@ * 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 */ #ifndef AMD5536UDC_H diff --git a/drivers/usb/gadget/at91_udc.c b/drivers/usb/gadget/at91_udc.c index ddb118a76807..8efe0fa9228d 100644 --- a/drivers/usb/gadget/at91_udc.c +++ b/drivers/usb/gadget/at91_udc.c @@ -9,16 +9,6 @@ * 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. */ #undef VERBOSE_DEBUG @@ -460,7 +450,7 @@ static void nuke(struct at91_ep *ep, int status) { struct at91_request *req; - // terminer chaque requete dans la queue + /* terminate any request in the queue */ ep->stopped = 1; if (list_empty(&ep->queue)) return; @@ -487,7 +477,7 @@ static int at91_ep_enable(struct usb_ep *_ep, || !desc || ep->desc || _ep->name == ep0name || desc->bDescriptorType != USB_DT_ENDPOINT - || (maxpacket = le16_to_cpu(desc->wMaxPacketSize)) == 0 + || (maxpacket = usb_endpoint_maxp(desc)) == 0 || maxpacket > ep->maxpacket) { DBG("bad ep or descriptor\n"); return -EINVAL; @@ -788,7 +778,7 @@ static const struct usb_ep_ops at91_ep_ops = { .queue = at91_ep_queue, .dequeue = at91_ep_dequeue, .set_halt = at91_ep_set_halt, - // there's only imprecise fifo status reporting + /* there's only imprecise fifo status reporting */ }; /*-------------------------------------------------------------------------*/ @@ -846,7 +836,7 @@ static void udc_reinit(struct at91_udc *udc) ep->fifo_bank = 0; ep->ep.maxpacket = ep->maxpacket; ep->creg = (void __iomem *) udc->udp_baseaddr + AT91_UDP_CSR(i); - // initialiser une queue par endpoint + /* initialize one queue per endpoint */ INIT_LIST_HEAD(&ep->queue); } } @@ -952,7 +942,7 @@ static int at91_vbus_session(struct usb_gadget *gadget, int is_active) struct at91_udc *udc = to_udc(gadget); unsigned long flags; - // VDBG("vbus %s\n", is_active ? "on" : "off"); + /* VDBG("vbus %s\n", is_active ? "on" : "off"); */ spin_lock_irqsave(&udc->lock, flags); udc->vbus = (is_active != 0); if (udc->driver) @@ -1003,7 +993,7 @@ static const struct usb_gadget_ops at91_udc_ops = { * VBUS-powered devices may also also want to support bigger * power budgets after an appropriate SET_CONFIGURATION. */ - // .vbus_power = at91_vbus_power, + /* .vbus_power = at91_vbus_power, */ }; /*-------------------------------------------------------------------------*/ @@ -1072,7 +1062,7 @@ static void handle_setup(struct at91_udc *udc, struct at91_ep *ep, u32 csr) ep->is_in = 0; } } else { - // REVISIT this happens sometimes under load; why?? + /* REVISIT this happens sometimes under load; why?? */ ERR("SETUP len %d, csr %08x\n", rxcount, csr); status = -EINVAL; } @@ -1451,7 +1441,7 @@ static irqreturn_t at91_udc_irq (int irq, void *_udc) at91_udp_write(udc, AT91_UDP_IDR, AT91_UDP_RXSUSP); at91_udp_write(udc, AT91_UDP_IER, AT91_UDP_RXRSM); at91_udp_write(udc, AT91_UDP_ICR, AT91_UDP_RXSUSP); - // VDBG("bus suspend\n"); + /* VDBG("bus suspend\n"); */ if (udc->suspended) continue; udc->suspended = 1; @@ -1473,7 +1463,7 @@ static irqreturn_t at91_udc_irq (int irq, void *_udc) at91_udp_write(udc, AT91_UDP_IDR, AT91_UDP_RXRSM); at91_udp_write(udc, AT91_UDP_IER, AT91_UDP_RXSUSP); at91_udp_write(udc, AT91_UDP_ICR, AT91_UDP_RXRSM); - // VDBG("bus resume\n"); + /* VDBG("bus resume\n"); */ if (!udc->suspended) continue; udc->suspended = 0; @@ -1820,7 +1810,7 @@ static int __init at91udc_probe(struct platform_device *pdev) /* request UDC and maybe VBUS irqs */ udc->udp_irq = platform_get_irq(pdev, 0); retval = request_irq(udc->udp_irq, at91_udc_irq, - IRQF_DISABLED, driver_name, udc); + 0, driver_name, udc); if (retval < 0) { DBG("request irq %d failed\n", udc->udp_irq); goto fail1; @@ -1848,7 +1838,7 @@ static int __init at91udc_probe(struct platform_device *pdev) jiffies + VBUS_POLL_TIMEOUT); } else { if (request_irq(udc->board.vbus_pin, at91_vbus_irq, - IRQF_DISABLED, driver_name, udc)) { + 0, driver_name, udc)) { DBG("request vbus irq %d failed\n", udc->board.vbus_pin); retval = -EBUSY; diff --git a/drivers/usb/gadget/at91_udc.h b/drivers/usb/gadget/at91_udc.h index 108ca54f9092..3c0315b86ace 100644 --- a/drivers/usb/gadget/at91_udc.h +++ b/drivers/usb/gadget/at91_udc.h @@ -7,16 +7,6 @@ * 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. */ #ifndef AT91_UDC_H diff --git a/drivers/usb/gadget/atmel_usba_udc.c b/drivers/usb/gadget/atmel_usba_udc.c index 5b1665eb1bef..271a9d873608 100644 --- a/drivers/usb/gadget/atmel_usba_udc.c +++ b/drivers/usb/gadget/atmel_usba_udc.c @@ -527,7 +527,7 @@ usba_ep_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) DBG(DBG_GADGET, "%s: ep_enable: desc=%p\n", ep->ep.name, desc); - maxpacket = le16_to_cpu(desc->wMaxPacketSize) & 0x7ff; + maxpacket = usb_endpoint_maxp(desc) & 0x7ff; if (((desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK) != ep->index) || ep->index == 0 @@ -571,7 +571,7 @@ usba_ep_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) * Bits 11:12 specify number of _additional_ * transactions per microframe. */ - nr_trans = ((le16_to_cpu(desc->wMaxPacketSize) >> 11) & 3) + 1; + nr_trans = ((usb_endpoint_maxp(desc) >> 11) & 3) + 1; if (nr_trans > 3) return -EINVAL; @@ -1718,13 +1718,12 @@ static irqreturn_t usba_udc_irq(int irq, void *devid) spin_lock(&udc->lock); } - if (status & USBA_HIGH_SPEED) { - DBG(DBG_BUS, "High-speed bus reset detected\n"); + if (status & USBA_HIGH_SPEED) udc->gadget.speed = USB_SPEED_HIGH; - } else { - DBG(DBG_BUS, "Full-speed bus reset detected\n"); + else udc->gadget.speed = USB_SPEED_FULL; - } + DBG(DBG_BUS, "%s bus reset detected\n", + usb_speed_string(udc->gadget.speed)); ep0 = &usba_ep[0]; ep0->desc = &usba_ep0_desc; diff --git a/drivers/usb/gadget/cdc2.c b/drivers/usb/gadget/cdc2.c index b1c1afbb8750..672674c2fb3d 100644 --- a/drivers/usb/gadget/cdc2.c +++ b/drivers/usb/gadget/cdc2.c @@ -8,15 +8,6 @@ * 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 */ #include <linux/kernel.h> diff --git a/drivers/usb/gadget/ci13xxx_msm.c b/drivers/usb/gadget/ci13xxx_msm.c index 470981ad6f77..4eedfe557154 100644 --- a/drivers/usb/gadget/ci13xxx_msm.c +++ b/drivers/usb/gadget/ci13xxx_msm.c @@ -3,17 +3,6 @@ * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and * only version 2 as published by the Free Software Foundation. - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. - * */ #include <linux/module.h> diff --git a/drivers/usb/gadget/ci13xxx_udc.c b/drivers/usb/gadget/ci13xxx_udc.c index 1265a8502ea0..83428f56253b 100644 --- a/drivers/usb/gadget/ci13xxx_udc.c +++ b/drivers/usb/gadget/ci13xxx_udc.c @@ -2101,7 +2101,7 @@ static int ep_enable(struct usb_ep *ep, mEp->num = usb_endpoint_num(desc); mEp->type = usb_endpoint_type(desc); - mEp->ep.maxpacket = __constant_le16_to_cpu(desc->wMaxPacketSize); + mEp->ep.maxpacket = usb_endpoint_maxp(desc); dbg_event(_usb_addr(mEp), "ENABLE", 0); diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index aef47414f5d5..8a5529d214fb 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -7,15 +7,6 @@ * 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 */ /* #define VERBOSE_DEBUG */ @@ -164,7 +155,7 @@ int config_ep_by_speed(struct usb_gadget *g, ep_found: /* commit results */ - _ep->maxpacket = le16_to_cpu(chosen_desc->wMaxPacketSize); + _ep->maxpacket = usb_endpoint_maxp(chosen_desc); _ep->desc = chosen_desc; _ep->comp_desc = NULL; _ep->maxburst = 0; @@ -551,9 +542,9 @@ static int bos_desc(struct usb_composite_dev *cdev) if (cdev->gadget->ops->get_config_params) cdev->gadget->ops->get_config_params(&dcd_config_params); else { - dcd_config_params.bU1devExitLat = USB_DEFULT_U1_DEV_EXIT_LAT; + dcd_config_params.bU1devExitLat = USB_DEFAULT_U1_DEV_EXIT_LAT; dcd_config_params.bU2DevExitLat = - cpu_to_le16(USB_DEFULT_U2_DEV_EXIT_LAT); + cpu_to_le16(USB_DEFAULT_U2_DEV_EXIT_LAT); } ss_cap->bU1devExitLat = dcd_config_params.bU1devExitLat; ss_cap->bU2DevExitLat = dcd_config_params.bU2DevExitLat; @@ -626,25 +617,9 @@ static int set_config(struct usb_composite_dev *cdev, result = 0; } - INFO(cdev, "%s speed config #%d: %s\n", - ({ char *speed; - switch (gadget->speed) { - case USB_SPEED_LOW: - speed = "low"; - break; - case USB_SPEED_FULL: - speed = "full"; - break; - case USB_SPEED_HIGH: - speed = "high"; - break; - case USB_SPEED_SUPER: - speed = "super"; - break; - default: - speed = "?"; - break; - } ; speed; }), number, c ? c->label : "unconfigured"); + INFO(cdev, "%s config #%d: %s\n", + usb_speed_string(gadget->speed), + number, c ? c->label : "unconfigured"); if (!c) goto done; diff --git a/drivers/usb/gadget/config.c b/drivers/usb/gadget/config.c index b2c001334876..7542a72ce51a 100644 --- a/drivers/usb/gadget/config.c +++ b/drivers/usb/gadget/config.c @@ -7,15 +7,6 @@ * 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 */ #include <linux/errno.h> diff --git a/drivers/usb/gadget/dbgp.c b/drivers/usb/gadget/dbgp.c index 8beefdd36787..f855ecf7a637 100644 --- a/drivers/usb/gadget/dbgp.c +++ b/drivers/usb/gadget/dbgp.c @@ -4,7 +4,6 @@ * Copyright (C) 2010 Stephane Duverger * * Released under the GPLv2. - * */ /* verbose messages */ diff --git a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/dummy_hcd.c index e755a9d267fc..ab8f1b488d54 100644 --- a/drivers/usb/gadget/dummy_hcd.c +++ b/drivers/usb/gadget/dummy_hcd.c @@ -10,15 +10,6 @@ * 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 */ @@ -439,7 +430,7 @@ dummy_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) * maximum packet size. * For SS devices the wMaxPacketSize is limited by 1024. */ - max = le16_to_cpu(desc->wMaxPacketSize) & 0x7ff; + max = usb_endpoint_maxp(desc) & 0x7ff; /* drivers must not request bad settings, since lower levels * (hardware or its drivers) may not check. some endpoints @@ -1277,7 +1268,7 @@ static int periodic_bytes (struct dummy *dum, struct dummy_ep *ep) int tmp; /* high bandwidth mode */ - tmp = le16_to_cpu(ep->desc->wMaxPacketSize); + tmp = usb_endpoint_maxp(ep->desc); tmp = (tmp >> 11) & 0x03; tmp *= 8 /* applies to entire frame */; limit += limit * tmp; diff --git a/drivers/usb/gadget/epautoconf.c b/drivers/usb/gadget/epautoconf.c index 7a7e6b7e1fd6..596a0b464e61 100644 --- a/drivers/usb/gadget/epautoconf.c +++ b/drivers/usb/gadget/epautoconf.c @@ -7,16 +7,6 @@ * 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 - * */ #include <linux/kernel.h> @@ -158,7 +148,7 @@ ep_matches ( * where it's an output parameter representing the full speed limit. * the usb spec fixes high speed bulk maxpacket at 512 bytes. */ - max = 0x7ff & le16_to_cpu(desc->wMaxPacketSize); + max = 0x7ff & usb_endpoint_maxp(desc); switch (type) { case USB_ENDPOINT_XFER_INT: /* INT: limit 64 bytes full speed, 1024 high/super speed */ diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c index aafc84f33e26..0cd764d59351 100644 --- a/drivers/usb/gadget/ether.c +++ b/drivers/usb/gadget/ether.c @@ -9,15 +9,6 @@ * 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 */ /* #define VERBOSE_DEBUG */ diff --git a/drivers/usb/gadget/f_audio.c b/drivers/usb/gadget/f_audio.c index a9a4eade7e80..ec7ffcd0d0cd 100644 --- a/drivers/usb/gadget/f_audio.c +++ b/drivers/usb/gadget/f_audio.c @@ -460,7 +460,7 @@ static int audio_set_endpoint_req(struct usb_function *f, switch (ctrl->bRequest) { case UAC_SET_CUR: - value = 0; + value = len; break; case UAC_SET_MIN: @@ -499,7 +499,7 @@ static int audio_get_endpoint_req(struct usb_function *f, case UAC_GET_MIN: case UAC_GET_MAX: case UAC_GET_RES: - value = 3; + value = len; break; case UAC_GET_MEM: break; @@ -681,17 +681,18 @@ f_audio_bind(struct usb_configuration *c, struct usb_function *f) status = -ENOMEM; - /* supcard all relevant hardware speeds... we expect that when + /* copy descriptors, and track endpoint copies */ + f->descriptors = usb_copy_descriptors(f_audio_desc); + + /* + * support all relevant hardware speeds... we expect that when * hardware is dual speed, all bulk-capable endpoints work at * both speeds */ - - /* copy descriptors, and track endpoint copies */ if (gadget_is_dualspeed(c->cdev->gadget)) { c->highspeed = true; f->hs_descriptors = usb_copy_descriptors(f_audio_desc); - } else - f->descriptors = usb_copy_descriptors(f_audio_desc); + } return 0; diff --git a/drivers/usb/gadget/f_ecm.c b/drivers/usb/gadget/f_ecm.c index 3691a0cb9465..11c07cb7d337 100644 --- a/drivers/usb/gadget/f_ecm.c +++ b/drivers/usb/gadget/f_ecm.c @@ -8,15 +8,6 @@ * 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 */ /* #define VERBOSE_DEBUG */ diff --git a/drivers/usb/gadget/f_eem.c b/drivers/usb/gadget/f_eem.c index 046c6d0e6960..1a7b2dd7d408 100644 --- a/drivers/usb/gadget/f_eem.c +++ b/drivers/usb/gadget/f_eem.c @@ -9,15 +9,6 @@ * 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 */ #include <linux/kernel.h> diff --git a/drivers/usb/gadget/f_fs.c b/drivers/usb/gadget/f_fs.c index c161a9aaeb7e..6b1c20b6c9b2 100644 --- a/drivers/usb/gadget/f_fs.c +++ b/drivers/usb/gadget/f_fs.c @@ -12,15 +12,6 @@ * 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 */ diff --git a/drivers/usb/gadget/f_hid.c b/drivers/usb/gadget/f_hid.c index 83a266bdb40e..b2113420b806 100644 --- a/drivers/usb/gadget/f_hid.c +++ b/drivers/usb/gadget/f_hid.c @@ -7,15 +7,6 @@ * 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 */ #include <linux/kernel.h> diff --git a/drivers/usb/gadget/f_loopback.c b/drivers/usb/gadget/f_loopback.c index ca660d40b11a..6d87f288df4e 100644 --- a/drivers/usb/gadget/f_loopback.c +++ b/drivers/usb/gadget/f_loopback.c @@ -8,15 +8,6 @@ * 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 */ /* #define VERBOSE_DEBUG */ diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c index 5b9339582007..52583a235330 100644 --- a/drivers/usb/gadget/f_mass_storage.c +++ b/drivers/usb/gadget/f_mass_storage.c @@ -112,8 +112,7 @@ * is not loaded (an empty string as "filename" in the fsg_config * structure causes error). The CD-ROM emulation includes a single * data track and no audio tracks; hence there need be only one - * backing file per LUN. Note also that the CD-ROM block length is - * set to 512 rather than the more common value 2048. + * backing file per LUN. * * * MSF includes support for module parameters. If gadget using it @@ -363,7 +362,7 @@ struct fsg_common { struct fsg_buffhd *next_buffhd_to_fill; struct fsg_buffhd *next_buffhd_to_drain; - struct fsg_buffhd buffhds[FSG_NUM_BUFFERS]; + struct fsg_buffhd *buffhds; int cmnd_size; u8 cmnd[MAX_COMMAND_SIZE]; @@ -745,7 +744,6 @@ static int do_read(struct fsg_common *common) u32 amount_left; loff_t file_offset, file_offset_tmp; unsigned int amount; - unsigned int partial_page; ssize_t nread; /* @@ -771,7 +769,7 @@ static int do_read(struct fsg_common *common) curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; return -EINVAL; } - file_offset = ((loff_t) lba) << 9; + file_offset = ((loff_t) lba) << curlun->blkbits; /* Carry out the file reads */ amount_left = common->data_size_from_cmnd; @@ -784,18 +782,10 @@ static int do_read(struct fsg_common *common) * Try to read the remaining amount. * But don't read more than the buffer size. * And don't try to read past the end of the file. - * Finally, if we're not at a page boundary, don't read past - * the next page. - * If this means reading 0 then we were asked to read past - * the end of file. */ amount = min(amount_left, FSG_BUFLEN); amount = min((loff_t)amount, curlun->file_length - file_offset); - partial_page = file_offset & (PAGE_CACHE_SIZE - 1); - if (partial_page > 0) - amount = min(amount, (unsigned int)PAGE_CACHE_SIZE - - partial_page); /* Wait for the next buffer to become available */ bh = common->next_buffhd_to_fill; @@ -812,7 +802,8 @@ static int do_read(struct fsg_common *common) if (amount == 0) { curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; - curlun->sense_data_info = file_offset >> 9; + curlun->sense_data_info = + file_offset >> curlun->blkbits; curlun->info_valid = 1; bh->inreq->length = 0; bh->state = BUF_STATE_FULL; @@ -835,18 +826,25 @@ static int do_read(struct fsg_common *common) } else if (nread < amount) { LDBG(curlun, "partial file read: %d/%u\n", (int)nread, amount); - nread -= (nread & 511); /* Round down to a block */ + nread = round_down(nread, curlun->blksize); } file_offset += nread; amount_left -= nread; common->residue -= nread; + + /* + * Except at the end of the transfer, nread will be + * equal to the buffer size, which is divisible by the + * bulk-in maxpacket size. + */ bh->inreq->length = nread; bh->state = BUF_STATE_FULL; /* If an error occurred, report it and its position */ if (nread < amount) { curlun->sense_data = SS_UNRECOVERED_READ_ERROR; - curlun->sense_data_info = file_offset >> 9; + curlun->sense_data_info = + file_offset >> curlun->blkbits; curlun->info_valid = 1; break; } @@ -877,7 +875,6 @@ static int do_write(struct fsg_common *common) u32 amount_left_to_req, amount_left_to_write; loff_t usb_offset, file_offset, file_offset_tmp; unsigned int amount; - unsigned int partial_page; ssize_t nwritten; int rc; @@ -921,7 +918,7 @@ static int do_write(struct fsg_common *common) /* Carry out the file writes */ get_some_more = 1; - file_offset = usb_offset = ((loff_t) lba) << 9; + file_offset = usb_offset = ((loff_t) lba) << curlun->blkbits; amount_left_to_req = common->data_size_from_cmnd; amount_left_to_write = common->data_size_from_cmnd; @@ -933,41 +930,21 @@ static int do_write(struct fsg_common *common) /* * Figure out how much we want to get: - * Try to get the remaining amount. - * But don't get more than the buffer size. - * And don't try to go past the end of the file. - * If we're not at a page boundary, - * don't go past the next page. - * If this means getting 0, then we were asked - * to write past the end of file. - * Finally, round down to a block boundary. + * Try to get the remaining amount, + * but not more than the buffer size. */ amount = min(amount_left_to_req, FSG_BUFLEN); - amount = min((loff_t)amount, - curlun->file_length - usb_offset); - partial_page = usb_offset & (PAGE_CACHE_SIZE - 1); - if (partial_page > 0) - amount = min(amount, - (unsigned int)PAGE_CACHE_SIZE - partial_page); - - if (amount == 0) { + + /* Beyond the end of the backing file? */ + if (usb_offset >= curlun->file_length) { get_some_more = 0; curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; - curlun->sense_data_info = usb_offset >> 9; + curlun->sense_data_info = + usb_offset >> curlun->blkbits; curlun->info_valid = 1; continue; } - amount -= amount & 511; - if (amount == 0) { - - /* - * Why were we were asked to transfer a - * partial block? - */ - get_some_more = 0; - continue; - } /* Get the next buffer */ usb_offset += amount; @@ -977,12 +954,11 @@ static int do_write(struct fsg_common *common) get_some_more = 0; /* - * amount is always divisible by 512, hence by - * the bulk-out maxpacket size + * Except at the end of the transfer, amount will be + * equal to the buffer size, which is divisible by + * the bulk-out maxpacket size. */ - bh->outreq->length = amount; - bh->bulk_out_intended_length = amount; - bh->outreq->short_not_ok = 1; + set_bulk_out_req_length(common, bh, amount); if (!start_out_transfer(common, bh)) /* Dunno what to do if common->fsg is NULL */ return -EIO; @@ -1002,7 +978,8 @@ static int do_write(struct fsg_common *common) /* Did something go wrong with the transfer? */ if (bh->outreq->status != 0) { curlun->sense_data = SS_COMMUNICATION_FAILURE; - curlun->sense_data_info = file_offset >> 9; + curlun->sense_data_info = + file_offset >> curlun->blkbits; curlun->info_valid = 1; break; } @@ -1016,6 +993,16 @@ static int do_write(struct fsg_common *common) amount = curlun->file_length - file_offset; } + /* Don't accept excess data. The spec doesn't say + * what to do in this case. We'll ignore the error. + */ + amount = min(amount, bh->bulk_out_intended_length); + + /* Don't write a partial block */ + amount = round_down(amount, curlun->blksize); + if (amount == 0) + goto empty_write; + /* Perform the write */ file_offset_tmp = file_offset; nwritten = vfs_write(curlun->filp, @@ -1033,8 +1020,7 @@ static int do_write(struct fsg_common *common) } else if (nwritten < amount) { LDBG(curlun, "partial file write: %d/%u\n", (int)nwritten, amount); - nwritten -= (nwritten & 511); - /* Round down to a block */ + nwritten = round_down(nwritten, curlun->blksize); } file_offset += nwritten; amount_left_to_write -= nwritten; @@ -1043,13 +1029,15 @@ static int do_write(struct fsg_common *common) /* If an error occurred, report it and its position */ if (nwritten < amount) { curlun->sense_data = SS_WRITE_ERROR; - curlun->sense_data_info = file_offset >> 9; + curlun->sense_data_info = + file_offset >> curlun->blkbits; curlun->info_valid = 1; break; } + empty_write: /* Did the host decide to stop early? */ - if (bh->outreq->actual != bh->outreq->length) { + if (bh->outreq->actual < bh->bulk_out_intended_length) { common->short_packet_received = 1; break; } @@ -1129,8 +1117,8 @@ static int do_verify(struct fsg_common *common) return -EIO; /* No default reply */ /* Prepare to carry out the file verify */ - amount_left = verification_length << 9; - file_offset = ((loff_t) lba) << 9; + amount_left = verification_length << curlun->blkbits; + file_offset = ((loff_t) lba) << curlun->blkbits; /* Write out all the dirty buffers before invalidating them */ fsg_lun_fsync_sub(curlun); @@ -1148,8 +1136,6 @@ static int do_verify(struct fsg_common *common) * Try to read the remaining amount, but not more than * the buffer size. * And don't try to read past the end of the file. - * If this means reading 0 then we were asked to read - * past the end of file. */ amount = min(amount_left, FSG_BUFLEN); amount = min((loff_t)amount, @@ -1157,7 +1143,8 @@ static int do_verify(struct fsg_common *common) if (amount == 0) { curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; - curlun->sense_data_info = file_offset >> 9; + curlun->sense_data_info = + file_offset >> curlun->blkbits; curlun->info_valid = 1; break; } @@ -1179,11 +1166,12 @@ static int do_verify(struct fsg_common *common) } else if (nread < amount) { LDBG(curlun, "partial file verify: %d/%u\n", (int)nread, amount); - nread -= nread & 511; /* Round down to a sector */ + nread = round_down(nread, curlun->blksize); } if (nread == 0) { curlun->sense_data = SS_UNRECOVERED_READ_ERROR; - curlun->sense_data_info = file_offset >> 9; + curlun->sense_data_info = + file_offset >> curlun->blkbits; curlun->info_valid = 1; break; } @@ -1289,7 +1277,7 @@ static int do_read_capacity(struct fsg_common *common, struct fsg_buffhd *bh) put_unaligned_be32(curlun->num_sectors - 1, &buf[0]); /* Max logical block */ - put_unaligned_be32(512, &buf[4]); /* Block length */ + put_unaligned_be32(curlun->blksize, &buf[4]);/* Block length */ return 8; } @@ -1527,7 +1515,7 @@ static int do_read_format_capacities(struct fsg_common *common, put_unaligned_be32(curlun->num_sectors, &buf[0]); /* Number of blocks */ - put_unaligned_be32(512, &buf[4]); /* Block length */ + put_unaligned_be32(curlun->blksize, &buf[4]);/* Block length */ buf[4] = 0x02; /* Current capacity */ return 12; } @@ -1607,7 +1595,7 @@ static int throw_away_data(struct fsg_common *common) common->next_buffhd_to_drain = bh->next; /* A short packet or an error ends everything */ - if (bh->outreq->actual != bh->outreq->length || + if (bh->outreq->actual < bh->bulk_out_intended_length || bh->outreq->status != 0) { raise_exception(common, FSG_STATE_ABORT_BULK_OUT); @@ -1623,12 +1611,11 @@ static int throw_away_data(struct fsg_common *common) amount = min(common->usb_amount_left, FSG_BUFLEN); /* - * amount is always divisible by 512, hence by + * Except at the end of the transfer, amount will be + * equal to the buffer size, which is divisible by * the bulk-out maxpacket size. */ - bh->outreq->length = amount; - bh->bulk_out_intended_length = amount; - bh->outreq->short_not_ok = 1; + set_bulk_out_req_length(common, bh, amount); if (!start_out_transfer(common, bh)) /* Dunno what to do if common->fsg is NULL */ return -EIO; @@ -2022,7 +2009,8 @@ static int do_scsi_command(struct fsg_common *common) case READ_6: i = common->cmnd[4]; - common->data_size_from_cmnd = (i == 0 ? 256 : i) << 9; + common->data_size_from_cmnd = (i == 0 ? 256 : i) << + common->curlun->blkbits; reply = check_command(common, 6, DATA_DIR_TO_HOST, (7<<1) | (1<<4), 1, "READ(6)"); @@ -2032,7 +2020,8 @@ static int do_scsi_command(struct fsg_common *common) case READ_10: common->data_size_from_cmnd = - get_unaligned_be16(&common->cmnd[7]) << 9; + get_unaligned_be16(&common->cmnd[7]) << + common->curlun->blkbits; reply = check_command(common, 10, DATA_DIR_TO_HOST, (1<<1) | (0xf<<2) | (3<<7), 1, "READ(10)"); @@ -2042,7 +2031,8 @@ static int do_scsi_command(struct fsg_common *common) case READ_12: common->data_size_from_cmnd = - get_unaligned_be32(&common->cmnd[6]) << 9; + get_unaligned_be32(&common->cmnd[6]) << + common->curlun->blkbits; reply = check_command(common, 12, DATA_DIR_TO_HOST, (1<<1) | (0xf<<2) | (0xf<<6), 1, "READ(12)"); @@ -2142,7 +2132,8 @@ static int do_scsi_command(struct fsg_common *common) case WRITE_6: i = common->cmnd[4]; - common->data_size_from_cmnd = (i == 0 ? 256 : i) << 9; + common->data_size_from_cmnd = (i == 0 ? 256 : i) << + common->curlun->blkbits; reply = check_command(common, 6, DATA_DIR_FROM_HOST, (7<<1) | (1<<4), 1, "WRITE(6)"); @@ -2152,7 +2143,8 @@ static int do_scsi_command(struct fsg_common *common) case WRITE_10: common->data_size_from_cmnd = - get_unaligned_be16(&common->cmnd[7]) << 9; + get_unaligned_be16(&common->cmnd[7]) << + common->curlun->blkbits; reply = check_command(common, 10, DATA_DIR_FROM_HOST, (1<<1) | (0xf<<2) | (3<<7), 1, "WRITE(10)"); @@ -2162,7 +2154,8 @@ static int do_scsi_command(struct fsg_common *common) case WRITE_12: common->data_size_from_cmnd = - get_unaligned_be32(&common->cmnd[6]) << 9; + get_unaligned_be32(&common->cmnd[6]) << + common->curlun->blkbits; reply = check_command(common, 12, DATA_DIR_FROM_HOST, (1<<1) | (0xf<<2) | (0xf<<6), 1, "WRITE(12)"); @@ -2297,7 +2290,6 @@ static int get_next_command(struct fsg_common *common) /* Queue a request to read a Bulk-only CBW */ set_bulk_out_req_length(common, bh, USB_BULK_CB_WRAP_LEN); - bh->outreq->short_not_ok = 1; if (!start_out_transfer(common, bh)) /* Don't know what to do if common->fsg is NULL */ return -EIO; @@ -2348,7 +2340,7 @@ reset: if (common->fsg) { fsg = common->fsg; - for (i = 0; i < FSG_NUM_BUFFERS; ++i) { + for (i = 0; i < fsg_num_buffers; ++i) { struct fsg_buffhd *bh = &common->buffhds[i]; if (bh->inreq) { @@ -2401,12 +2393,11 @@ reset: goto reset; fsg->bulk_out->driver_data = common; fsg->bulk_out_enabled = 1; - common->bulk_out_maxpacket = - le16_to_cpu(fsg->bulk_out->desc->wMaxPacketSize); + common->bulk_out_maxpacket = usb_endpoint_maxp(fsg->bulk_out->desc); clear_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags); /* Allocate the requests */ - for (i = 0; i < FSG_NUM_BUFFERS; ++i) { + for (i = 0; i < fsg_num_buffers; ++i) { struct fsg_buffhd *bh = &common->buffhds[i]; rc = alloc_request(common, fsg->bulk_in, &bh->inreq); @@ -2475,7 +2466,7 @@ static void handle_exception(struct fsg_common *common) /* Cancel all the pending transfers */ if (likely(common->fsg)) { - for (i = 0; i < FSG_NUM_BUFFERS; ++i) { + for (i = 0; i < fsg_num_buffers; ++i) { bh = &common->buffhds[i]; if (bh->inreq_busy) usb_ep_dequeue(common->fsg->bulk_in, bh->inreq); @@ -2487,7 +2478,7 @@ static void handle_exception(struct fsg_common *common) /* Wait until everything is idle */ for (;;) { int num_active = 0; - for (i = 0; i < FSG_NUM_BUFFERS; ++i) { + for (i = 0; i < fsg_num_buffers; ++i) { bh = &common->buffhds[i]; num_active += bh->inreq_busy + bh->outreq_busy; } @@ -2510,7 +2501,7 @@ static void handle_exception(struct fsg_common *common) */ spin_lock_irq(&common->lock); - for (i = 0; i < FSG_NUM_BUFFERS; ++i) { + for (i = 0; i < fsg_num_buffers; ++i) { bh = &common->buffhds[i]; bh->state = BUF_STATE_EMPTY; } @@ -2719,6 +2710,10 @@ static struct fsg_common *fsg_common_init(struct fsg_common *common, int nluns, i, rc; char *pathbuf; + rc = fsg_num_buffers_validate(); + if (rc != 0) + return ERR_PTR(rc); + /* Find out how many LUNs there should be */ nluns = cfg->nluns; if (nluns < 1 || nluns > FSG_MAX_LUNS) { @@ -2737,6 +2732,14 @@ static struct fsg_common *fsg_common_init(struct fsg_common *common, common->free_storage_on_release = 0; } + common->buffhds = kcalloc(fsg_num_buffers, + sizeof *(common->buffhds), GFP_KERNEL); + if (!common->buffhds) { + if (common->free_storage_on_release) + kfree(common); + return ERR_PTR(-ENOMEM); + } + common->ops = cfg->ops; common->private_data = cfg->private_data; @@ -2814,7 +2817,7 @@ static struct fsg_common *fsg_common_init(struct fsg_common *common, /* Data buffers cyclic list */ bh = common->buffhds; - i = FSG_NUM_BUFFERS; + i = fsg_num_buffers; goto buffhds_first_it; do { bh->next = bh + 1; @@ -2940,12 +2943,13 @@ static void fsg_common_release(struct kref *ref) { struct fsg_buffhd *bh = common->buffhds; - unsigned i = FSG_NUM_BUFFERS; + unsigned i = fsg_num_buffers; do { kfree(bh->buf); } while (++bh, --i); } + kfree(common->buffhds); if (common->free_storage_on_release) kfree(common); } @@ -3019,6 +3023,28 @@ static int fsg_bind(struct usb_configuration *c, struct usb_function *f) } } + if (gadget_is_superspeed(gadget)) { + unsigned max_burst; + + /* Calculate bMaxBurst, we know packet size is 1024 */ + max_burst = min_t(unsigned, FSG_BUFLEN / 1024, 15); + + fsg_ss_bulk_in_desc.bEndpointAddress = + fsg_fs_bulk_in_desc.bEndpointAddress; + fsg_ss_bulk_in_comp_desc.bMaxBurst = max_burst; + + fsg_ss_bulk_out_desc.bEndpointAddress = + fsg_fs_bulk_out_desc.bEndpointAddress; + fsg_ss_bulk_out_comp_desc.bMaxBurst = max_burst; + + f->ss_descriptors = usb_copy_descriptors(fsg_ss_function); + if (unlikely(!f->ss_descriptors)) { + usb_free_descriptors(f->hs_descriptors); + usb_free_descriptors(f->descriptors); + return -ENOMEM; + } + } + return 0; autoconf_fail: diff --git a/drivers/usb/gadget/f_midi.c b/drivers/usb/gadget/f_midi.c new file mode 100644 index 000000000000..67b222908cf9 --- /dev/null +++ b/drivers/usb/gadget/f_midi.c @@ -0,0 +1,998 @@ +/* + * f_midi.c -- USB MIDI class function driver + * + * Copyright (C) 2006 Thumtronics Pty Ltd. + * Developed for Thumtronics by Grey Innovation + * Ben Williamson <ben.williamson@greyinnovation.com> + * + * Rewritten for the composite framework + * Copyright (C) 2011 Daniel Mack <zonque@gmail.com> + * + * Based on drivers/usb/gadget/f_audio.c, + * Copyright (C) 2008 Bryan Wu <cooloney@kernel.org> + * Copyright (C) 2008 Analog Devices, Inc + * + * and drivers/usb/gadget/midi.c, + * Copyright (C) 2006 Thumtronics Pty Ltd. + * Ben Williamson <ben.williamson@greyinnovation.com> + * + * Licensed under the GPL-2 or later. + */ + +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/utsname.h> +#include <linux/device.h> + +#include <sound/core.h> +#include <sound/initval.h> +#include <sound/rawmidi.h> + +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> +#include <linux/usb/audio.h> +#include <linux/usb/midi.h> + +MODULE_AUTHOR("Ben Williamson"); +MODULE_LICENSE("GPL v2"); + +static const char f_midi_shortname[] = "f_midi"; +static const char f_midi_longname[] = "MIDI Gadget"; + +/* + * We can only handle 16 cables on one single endpoint, as cable numbers are + * stored in 4-bit fields. And as the interface currently only holds one + * single endpoint, this is the maximum number of ports we can allow. + */ +#define MAX_PORTS 16 + +/* + * This is a gadget, and the IN/OUT naming is from the host's perspective. + * USB -> OUT endpoint -> rawmidi + * USB <- IN endpoint <- rawmidi + */ +struct gmidi_in_port { + struct f_midi *midi; + int active; + uint8_t cable; + uint8_t state; +#define STATE_UNKNOWN 0 +#define STATE_1PARAM 1 +#define STATE_2PARAM_1 2 +#define STATE_2PARAM_2 3 +#define STATE_SYSEX_0 4 +#define STATE_SYSEX_1 5 +#define STATE_SYSEX_2 6 + uint8_t data[2]; +}; + +struct f_midi { + struct usb_function func; + struct usb_gadget *gadget; + struct usb_ep *in_ep, *out_ep; + struct snd_card *card; + struct snd_rawmidi *rmidi; + + struct snd_rawmidi_substream *in_substream[MAX_PORTS]; + struct snd_rawmidi_substream *out_substream[MAX_PORTS]; + struct gmidi_in_port *in_port[MAX_PORTS]; + + unsigned long out_triggered; + struct tasklet_struct tasklet; + unsigned int in_ports; + unsigned int out_ports; + int index; + char *id; + unsigned int buflen, qlen; +}; + +static inline struct f_midi *func_to_midi(struct usb_function *f) +{ + return container_of(f, struct f_midi, func); +} + +static void f_midi_transmit(struct f_midi *midi, struct usb_request *req); + +DECLARE_UAC_AC_HEADER_DESCRIPTOR(1); +DECLARE_USB_MIDI_OUT_JACK_DESCRIPTOR(1); +DECLARE_USB_MIDI_OUT_JACK_DESCRIPTOR(16); +DECLARE_USB_MS_ENDPOINT_DESCRIPTOR(16); + +/* B.3.1 Standard AC Interface Descriptor */ +static struct usb_interface_descriptor ac_interface_desc __initdata = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + /* .bInterfaceNumber = DYNAMIC */ + /* .bNumEndpoints = DYNAMIC */ + .bInterfaceClass = USB_CLASS_AUDIO, + .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL, + /* .iInterface = DYNAMIC */ +}; + +/* B.3.2 Class-Specific AC Interface Descriptor */ +static struct uac1_ac_header_descriptor_1 ac_header_desc __initdata = { + .bLength = UAC_DT_AC_HEADER_SIZE(1), + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = USB_MS_HEADER, + .bcdADC = cpu_to_le16(0x0100), + .wTotalLength = cpu_to_le16(UAC_DT_AC_HEADER_SIZE(1)), + .bInCollection = 1, + /* .baInterfaceNr = DYNAMIC */ +}; + +/* B.4.1 Standard MS Interface Descriptor */ +static struct usb_interface_descriptor ms_interface_desc __initdata = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + /* .bInterfaceNumber = DYNAMIC */ + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_AUDIO, + .bInterfaceSubClass = USB_SUBCLASS_MIDISTREAMING, + /* .iInterface = DYNAMIC */ +}; + +/* B.4.2 Class-Specific MS Interface Descriptor */ +static struct usb_ms_header_descriptor ms_header_desc __initdata = { + .bLength = USB_DT_MS_HEADER_SIZE, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = USB_MS_HEADER, + .bcdMSC = cpu_to_le16(0x0100), + /* .wTotalLength = DYNAMIC */ +}; + +/* B.4.3 Embedded MIDI IN Jack Descriptor */ +static struct usb_midi_in_jack_descriptor jack_in_emb_desc = { + .bLength = USB_DT_MIDI_IN_SIZE, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = USB_MS_MIDI_IN_JACK, + .bJackType = USB_MS_EMBEDDED, + /* .bJackID = DYNAMIC */ +}; + +/* B.4.4 Embedded MIDI OUT Jack Descriptor */ +static struct usb_midi_out_jack_descriptor_16 jack_out_emb_desc = { + /* .bLength = DYNAMIC */ + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = USB_MS_MIDI_OUT_JACK, + .bJackType = USB_MS_EMBEDDED, + /* .bJackID = DYNAMIC */ + /* .bNrInputPins = DYNAMIC */ + /* .pins = DYNAMIC */ +}; + +/* B.5.1 Standard Bulk OUT Endpoint Descriptor */ +static struct usb_endpoint_descriptor bulk_out_desc = { + .bLength = USB_DT_ENDPOINT_AUDIO_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; + +/* B.5.2 Class-specific MS Bulk OUT Endpoint Descriptor */ +static struct usb_ms_endpoint_descriptor_16 ms_out_desc = { + /* .bLength = DYNAMIC */ + .bDescriptorType = USB_DT_CS_ENDPOINT, + .bDescriptorSubtype = USB_MS_GENERAL, + /* .bNumEmbMIDIJack = DYNAMIC */ + /* .baAssocJackID = DYNAMIC */ +}; + +/* B.6.1 Standard Bulk IN Endpoint Descriptor */ +static struct usb_endpoint_descriptor bulk_in_desc = { + .bLength = USB_DT_ENDPOINT_AUDIO_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; + +/* B.6.2 Class-specific MS Bulk IN Endpoint Descriptor */ +static struct usb_ms_endpoint_descriptor_16 ms_in_desc = { + /* .bLength = DYNAMIC */ + .bDescriptorType = USB_DT_CS_ENDPOINT, + .bDescriptorSubtype = USB_MS_GENERAL, + /* .bNumEmbMIDIJack = DYNAMIC */ + /* .baAssocJackID = DYNAMIC */ +}; + +/* string IDs are assigned dynamically */ + +#define STRING_FUNC_IDX 0 + +static struct usb_string midi_string_defs[] = { + [STRING_FUNC_IDX].s = "MIDI function", + { } /* end of list */ +}; + +static struct usb_gadget_strings midi_stringtab = { + .language = 0x0409, /* en-us */ + .strings = midi_string_defs, +}; + +static struct usb_gadget_strings *midi_strings[] = { + &midi_stringtab, + NULL, +}; + +static struct usb_request *alloc_ep_req(struct usb_ep *ep, unsigned length) +{ + struct usb_request *req; + + req = usb_ep_alloc_request(ep, GFP_ATOMIC); + if (req) { + req->length = length; + req->buf = kmalloc(length, GFP_ATOMIC); + if (!req->buf) { + usb_ep_free_request(ep, req); + req = NULL; + } + } + return req; +} + +static void free_ep_req(struct usb_ep *ep, struct usb_request *req) +{ + kfree(req->buf); + usb_ep_free_request(ep, req); +} + +static const uint8_t f_midi_cin_length[] = { + 0, 0, 2, 3, 3, 1, 2, 3, 3, 3, 3, 3, 2, 2, 3, 1 +}; + +/* + * Receives a chunk of MIDI data. + */ +static void f_midi_read_data(struct usb_ep *ep, int cable, + uint8_t *data, int length) +{ + struct f_midi *midi = ep->driver_data; + struct snd_rawmidi_substream *substream = midi->out_substream[cable]; + + if (!substream) + /* Nobody is listening - throw it on the floor. */ + return; + + if (!test_bit(cable, &midi->out_triggered)) + return; + + snd_rawmidi_receive(substream, data, length); +} + +static void f_midi_handle_out_data(struct usb_ep *ep, struct usb_request *req) +{ + unsigned int i; + u8 *buf = req->buf; + + for (i = 0; i + 3 < req->actual; i += 4) + if (buf[i] != 0) { + int cable = buf[i] >> 4; + int length = f_midi_cin_length[buf[i] & 0x0f]; + f_midi_read_data(ep, cable, &buf[i + 1], length); + } +} + +static void +f_midi_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct f_midi *midi = ep->driver_data; + struct usb_composite_dev *cdev = midi->func.config->cdev; + int status = req->status; + + switch (status) { + case 0: /* normal completion */ + if (ep == midi->out_ep) { + /* We received stuff. req is queued again, below */ + f_midi_handle_out_data(ep, req); + } else if (ep == midi->in_ep) { + /* Our transmit completed. See if there's more to go. + * f_midi_transmit eats req, don't queue it again. */ + f_midi_transmit(midi, req); + return; + } + break; + + /* this endpoint is normally active while we're configured */ + case -ECONNABORTED: /* hardware forced ep reset */ + case -ECONNRESET: /* request dequeued */ + case -ESHUTDOWN: /* disconnect from host */ + VDBG(cdev, "%s gone (%d), %d/%d\n", ep->name, status, + req->actual, req->length); + if (ep == midi->out_ep) + f_midi_handle_out_data(ep, req); + + free_ep_req(ep, req); + return; + + case -EOVERFLOW: /* buffer overrun on read means that + * we didn't provide a big enough buffer. + */ + default: + DBG(cdev, "%s complete --> %d, %d/%d\n", ep->name, + status, req->actual, req->length); + break; + case -EREMOTEIO: /* short read */ + break; + } + + status = usb_ep_queue(ep, req, GFP_ATOMIC); + if (status) { + ERROR(cdev, "kill %s: resubmit %d bytes --> %d\n", + ep->name, req->length, status); + usb_ep_set_halt(ep); + /* FIXME recover later ... somehow */ + } +} + +static int f_midi_start_ep(struct f_midi *midi, + struct usb_function *f, + struct usb_ep *ep) +{ + int err; + struct usb_composite_dev *cdev = f->config->cdev; + + if (ep->driver_data) + usb_ep_disable(ep); + + err = config_ep_by_speed(midi->gadget, f, ep); + if (err) { + ERROR(cdev, "can't configure %s: %d\n", ep->name, err); + return err; + } + + err = usb_ep_enable(ep); + if (err) { + ERROR(cdev, "can't start %s: %d\n", ep->name, err); + return err; + } + + ep->driver_data = midi; + + return 0; +} + +static int f_midi_set_alt(struct usb_function *f, unsigned intf, unsigned alt) +{ + struct f_midi *midi = func_to_midi(f); + struct usb_composite_dev *cdev = f->config->cdev; + unsigned i; + int err; + + err = f_midi_start_ep(midi, f, midi->in_ep); + if (err) + return err; + + err = f_midi_start_ep(midi, f, midi->out_ep); + if (err) + return err; + + if (midi->out_ep->driver_data) + usb_ep_disable(midi->out_ep); + + err = config_ep_by_speed(midi->gadget, f, midi->out_ep); + if (err) { + ERROR(cdev, "can't configure %s: %d\n", + midi->out_ep->name, err); + return err; + } + + err = usb_ep_enable(midi->out_ep); + if (err) { + ERROR(cdev, "can't start %s: %d\n", + midi->out_ep->name, err); + return err; + } + + midi->out_ep->driver_data = midi; + + /* allocate a bunch of read buffers and queue them all at once. */ + for (i = 0; i < midi->qlen && err == 0; i++) { + struct usb_request *req = + alloc_ep_req(midi->out_ep, midi->buflen); + if (req == NULL) + return -ENOMEM; + + req->complete = f_midi_complete; + err = usb_ep_queue(midi->out_ep, req, GFP_ATOMIC); + if (err) { + ERROR(midi, "%s queue req: %d\n", + midi->out_ep->name, err); + } + } + + return 0; +} + +static void f_midi_disable(struct usb_function *f) +{ + struct f_midi *midi = func_to_midi(f); + struct usb_composite_dev *cdev = f->config->cdev; + + DBG(cdev, "disable\n"); + + /* + * just disable endpoints, forcing completion of pending i/o. + * all our completion handlers free their requests in this case. + */ + usb_ep_disable(midi->in_ep); + usb_ep_disable(midi->out_ep); +} + +static void f_midi_unbind(struct usb_configuration *c, struct usb_function *f) +{ + struct usb_composite_dev *cdev = f->config->cdev; + struct f_midi *midi = func_to_midi(f); + struct snd_card *card; + + DBG(cdev, "unbind\n"); + + /* just to be sure */ + f_midi_disable(f); + + card = midi->card; + midi->card = NULL; + if (card) + snd_card_free(card); + + kfree(midi->id); + midi->id = NULL; + + usb_free_descriptors(f->descriptors); + kfree(midi); +} + +static int f_midi_snd_free(struct snd_device *device) +{ + return 0; +} + +static void f_midi_transmit_packet(struct usb_request *req, uint8_t p0, + uint8_t p1, uint8_t p2, uint8_t p3) +{ + unsigned length = req->length; + u8 *buf = (u8 *)req->buf + length; + + buf[0] = p0; + buf[1] = p1; + buf[2] = p2; + buf[3] = p3; + req->length = length + 4; +} + +/* + * Converts MIDI commands to USB MIDI packets. + */ +static void f_midi_transmit_byte(struct usb_request *req, + struct gmidi_in_port *port, uint8_t b) +{ + uint8_t p0 = port->cable << 4; + + if (b >= 0xf8) { + f_midi_transmit_packet(req, p0 | 0x0f, b, 0, 0); + } else if (b >= 0xf0) { + switch (b) { + case 0xf0: + port->data[0] = b; + port->state = STATE_SYSEX_1; + break; + case 0xf1: + case 0xf3: + port->data[0] = b; + port->state = STATE_1PARAM; + break; + case 0xf2: + port->data[0] = b; + port->state = STATE_2PARAM_1; + break; + case 0xf4: + case 0xf5: + port->state = STATE_UNKNOWN; + break; + case 0xf6: + f_midi_transmit_packet(req, p0 | 0x05, 0xf6, 0, 0); + port->state = STATE_UNKNOWN; + break; + case 0xf7: + switch (port->state) { + case STATE_SYSEX_0: + f_midi_transmit_packet(req, + p0 | 0x05, 0xf7, 0, 0); + break; + case STATE_SYSEX_1: + f_midi_transmit_packet(req, + p0 | 0x06, port->data[0], 0xf7, 0); + break; + case STATE_SYSEX_2: + f_midi_transmit_packet(req, + p0 | 0x07, port->data[0], + port->data[1], 0xf7); + break; + } + port->state = STATE_UNKNOWN; + break; + } + } else if (b >= 0x80) { + port->data[0] = b; + if (b >= 0xc0 && b <= 0xdf) + port->state = STATE_1PARAM; + else + port->state = STATE_2PARAM_1; + } else { /* b < 0x80 */ + switch (port->state) { + case STATE_1PARAM: + if (port->data[0] < 0xf0) { + p0 |= port->data[0] >> 4; + } else { + p0 |= 0x02; + port->state = STATE_UNKNOWN; + } + f_midi_transmit_packet(req, p0, port->data[0], b, 0); + break; + case STATE_2PARAM_1: + port->data[1] = b; + port->state = STATE_2PARAM_2; + break; + case STATE_2PARAM_2: + if (port->data[0] < 0xf0) { + p0 |= port->data[0] >> 4; + port->state = STATE_2PARAM_1; + } else { + p0 |= 0x03; + port->state = STATE_UNKNOWN; + } + f_midi_transmit_packet(req, + p0, port->data[0], port->data[1], b); + break; + case STATE_SYSEX_0: + port->data[0] = b; + port->state = STATE_SYSEX_1; + break; + case STATE_SYSEX_1: + port->data[1] = b; + port->state = STATE_SYSEX_2; + break; + case STATE_SYSEX_2: + f_midi_transmit_packet(req, + p0 | 0x04, port->data[0], port->data[1], b); + port->state = STATE_SYSEX_0; + break; + } + } +} + +static void f_midi_transmit(struct f_midi *midi, struct usb_request *req) +{ + struct usb_ep *ep = midi->in_ep; + int i; + + if (!ep) + return; + + if (!req) + req = alloc_ep_req(ep, midi->buflen); + + if (!req) { + ERROR(midi, "gmidi_transmit: alloc_ep_request failed\n"); + return; + } + req->length = 0; + req->complete = f_midi_complete; + + for (i = 0; i < MAX_PORTS; i++) { + struct gmidi_in_port *port = midi->in_port[i]; + struct snd_rawmidi_substream *substream = midi->in_substream[i]; + + if (!port || !port->active || !substream) + continue; + + while (req->length + 3 < midi->buflen) { + uint8_t b; + if (snd_rawmidi_transmit(substream, &b, 1) != 1) { + port->active = 0; + break; + } + f_midi_transmit_byte(req, port, b); + } + } + + if (req->length > 0) + usb_ep_queue(ep, req, GFP_ATOMIC); + else + free_ep_req(ep, req); +} + +static void f_midi_in_tasklet(unsigned long data) +{ + struct f_midi *midi = (struct f_midi *) data; + f_midi_transmit(midi, NULL); +} + +static int f_midi_in_open(struct snd_rawmidi_substream *substream) +{ + struct f_midi *midi = substream->rmidi->private_data; + + if (!midi->in_port[substream->number]) + return -EINVAL; + + VDBG(midi, "%s()\n", __func__); + midi->in_substream[substream->number] = substream; + midi->in_port[substream->number]->state = STATE_UNKNOWN; + return 0; +} + +static int f_midi_in_close(struct snd_rawmidi_substream *substream) +{ + struct f_midi *midi = substream->rmidi->private_data; + + VDBG(midi, "%s()\n", __func__); + return 0; +} + +static void f_midi_in_trigger(struct snd_rawmidi_substream *substream, int up) +{ + struct f_midi *midi = substream->rmidi->private_data; + + if (!midi->in_port[substream->number]) + return; + + VDBG(midi, "%s() %d\n", __func__, up); + midi->in_port[substream->number]->active = up; + if (up) + tasklet_hi_schedule(&midi->tasklet); +} + +static int f_midi_out_open(struct snd_rawmidi_substream *substream) +{ + struct f_midi *midi = substream->rmidi->private_data; + + if (substream->number >= MAX_PORTS) + return -EINVAL; + + VDBG(midi, "%s()\n", __func__); + midi->out_substream[substream->number] = substream; + return 0; +} + +static int f_midi_out_close(struct snd_rawmidi_substream *substream) +{ + struct f_midi *midi = substream->rmidi->private_data; + + VDBG(midi, "%s()\n", __func__); + return 0; +} + +static void f_midi_out_trigger(struct snd_rawmidi_substream *substream, int up) +{ + struct f_midi *midi = substream->rmidi->private_data; + + VDBG(midi, "%s()\n", __func__); + + if (up) + set_bit(substream->number, &midi->out_triggered); + else + clear_bit(substream->number, &midi->out_triggered); +} + +static struct snd_rawmidi_ops gmidi_in_ops = { + .open = f_midi_in_open, + .close = f_midi_in_close, + .trigger = f_midi_in_trigger, +}; + +static struct snd_rawmidi_ops gmidi_out_ops = { + .open = f_midi_out_open, + .close = f_midi_out_close, + .trigger = f_midi_out_trigger +}; + +/* register as a sound "card" */ +static int f_midi_register_card(struct f_midi *midi) +{ + struct snd_card *card; + struct snd_rawmidi *rmidi; + int err; + static struct snd_device_ops ops = { + .dev_free = f_midi_snd_free, + }; + + err = snd_card_create(midi->index, midi->id, THIS_MODULE, 0, &card); + if (err < 0) { + ERROR(midi, "snd_card_create() failed\n"); + goto fail; + } + midi->card = card; + + err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, midi, &ops); + if (err < 0) { + ERROR(midi, "snd_device_new() failed: error %d\n", err); + goto fail; + } + + strcpy(card->driver, f_midi_longname); + strcpy(card->longname, f_midi_longname); + strcpy(card->shortname, f_midi_shortname); + + /* Set up rawmidi */ + snd_component_add(card, "MIDI"); + err = snd_rawmidi_new(card, card->longname, 0, + midi->out_ports, midi->in_ports, &rmidi); + if (err < 0) { + ERROR(midi, "snd_rawmidi_new() failed: error %d\n", err); + goto fail; + } + midi->rmidi = rmidi; + strcpy(rmidi->name, card->shortname); + rmidi->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT | + SNDRV_RAWMIDI_INFO_INPUT | + SNDRV_RAWMIDI_INFO_DUPLEX; + rmidi->private_data = midi; + + /* + * Yes, rawmidi OUTPUT = USB IN, and rawmidi INPUT = USB OUT. + * It's an upside-down world being a gadget. + */ + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &gmidi_in_ops); + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &gmidi_out_ops); + + snd_card_set_dev(card, &midi->gadget->dev); + + /* register it - we're ready to go */ + err = snd_card_register(card); + if (err < 0) { + ERROR(midi, "snd_card_register() failed\n"); + goto fail; + } + + VDBG(midi, "%s() finished ok\n", __func__); + return 0; + +fail: + if (midi->card) { + snd_card_free(midi->card); + midi->card = NULL; + } + return err; +} + +/* MIDI function driver setup/binding */ + +static int __init +f_midi_bind(struct usb_configuration *c, struct usb_function *f) +{ + struct usb_descriptor_header *midi_function[(MAX_PORTS * 2) + 12]; + struct usb_midi_in_jack_descriptor jack_in_ext_desc[MAX_PORTS]; + struct usb_midi_out_jack_descriptor_1 jack_out_ext_desc[MAX_PORTS]; + struct usb_composite_dev *cdev = c->cdev; + struct f_midi *midi = func_to_midi(f); + int status, n, jack = 1, i = 0; + + /* maybe allocate device-global string ID */ + if (midi_string_defs[0].id == 0) { + status = usb_string_id(c->cdev); + if (status < 0) + goto fail; + midi_string_defs[0].id = status; + } + + /* We have two interfaces, AudioControl and MIDIStreaming */ + status = usb_interface_id(c, f); + if (status < 0) + goto fail; + ac_interface_desc.bInterfaceNumber = status; + + status = usb_interface_id(c, f); + if (status < 0) + goto fail; + ms_interface_desc.bInterfaceNumber = status; + ac_header_desc.baInterfaceNr[0] = status; + + status = -ENODEV; + + /* allocate instance-specific endpoints */ + midi->in_ep = usb_ep_autoconfig(cdev->gadget, &bulk_in_desc); + if (!midi->in_ep) + goto fail; + midi->in_ep->driver_data = cdev; /* claim */ + + midi->out_ep = usb_ep_autoconfig(cdev->gadget, &bulk_out_desc); + if (!midi->out_ep) + goto fail; + midi->out_ep->driver_data = cdev; /* claim */ + + /* + * construct the function's descriptor set. As the number of + * input and output MIDI ports is configurable, we have to do + * it that way. + */ + + /* add the headers - these are always the same */ + midi_function[i++] = (struct usb_descriptor_header *) &ac_interface_desc; + midi_function[i++] = (struct usb_descriptor_header *) &ac_header_desc; + midi_function[i++] = (struct usb_descriptor_header *) &ms_interface_desc; + + /* calculate the header's wTotalLength */ + n = USB_DT_MS_HEADER_SIZE + + (1 + midi->in_ports) * USB_DT_MIDI_IN_SIZE + + (1 + midi->out_ports) * USB_DT_MIDI_OUT_SIZE(1); + ms_header_desc.wTotalLength = cpu_to_le16(n); + + midi_function[i++] = (struct usb_descriptor_header *) &ms_header_desc; + + /* we have one embedded IN jack */ + jack_in_emb_desc.bJackID = jack++; + midi_function[i++] = (struct usb_descriptor_header *) &jack_in_emb_desc; + + /* and a dynamic amount of external IN jacks */ + for (n = 0; n < midi->in_ports; n++) { + struct usb_midi_in_jack_descriptor *ext = &jack_in_ext_desc[n]; + + ext->bLength = USB_DT_MIDI_IN_SIZE; + ext->bDescriptorType = USB_DT_CS_INTERFACE; + ext->bDescriptorSubtype = USB_MS_MIDI_IN_JACK; + ext->bJackType = USB_MS_EXTERNAL; + ext->bJackID = jack++; + ext->iJack = 0; + + midi_function[i++] = (struct usb_descriptor_header *) ext; + } + + /* one embedded OUT jack ... */ + jack_out_emb_desc.bLength = USB_DT_MIDI_OUT_SIZE(midi->in_ports); + jack_out_emb_desc.bJackID = jack++; + jack_out_emb_desc.bNrInputPins = midi->in_ports; + /* ... which referencess all external IN jacks */ + for (n = 0; n < midi->in_ports; n++) { + jack_out_emb_desc.pins[n].baSourceID = jack_in_ext_desc[n].bJackID; + jack_out_emb_desc.pins[n].baSourcePin = 1; + } + + midi_function[i++] = (struct usb_descriptor_header *) &jack_out_emb_desc; + + /* and multiple external OUT jacks ... */ + for (n = 0; n < midi->out_ports; n++) { + struct usb_midi_out_jack_descriptor_1 *ext = &jack_out_ext_desc[n]; + int m; + + ext->bLength = USB_DT_MIDI_OUT_SIZE(1); + ext->bDescriptorType = USB_DT_CS_INTERFACE; + ext->bDescriptorSubtype = USB_MS_MIDI_OUT_JACK; + ext->bJackType = USB_MS_EXTERNAL; + ext->bJackID = jack++; + ext->bNrInputPins = 1; + ext->iJack = 0; + /* ... which all reference the same embedded IN jack */ + for (m = 0; m < midi->out_ports; m++) { + ext->pins[m].baSourceID = jack_in_emb_desc.bJackID; + ext->pins[m].baSourcePin = 1; + } + + midi_function[i++] = (struct usb_descriptor_header *) ext; + } + + /* configure the endpoint descriptors ... */ + ms_out_desc.bLength = USB_DT_MS_ENDPOINT_SIZE(midi->in_ports); + ms_out_desc.bNumEmbMIDIJack = midi->in_ports; + for (n = 0; n < midi->in_ports; n++) + ms_out_desc.baAssocJackID[n] = jack_in_emb_desc.bJackID; + + ms_in_desc.bLength = USB_DT_MS_ENDPOINT_SIZE(midi->out_ports); + ms_in_desc.bNumEmbMIDIJack = midi->out_ports; + for (n = 0; n < midi->out_ports; n++) + ms_in_desc.baAssocJackID[n] = jack_out_emb_desc.bJackID; + + /* ... and add them to the list */ + midi_function[i++] = (struct usb_descriptor_header *) &bulk_out_desc; + midi_function[i++] = (struct usb_descriptor_header *) &ms_out_desc; + midi_function[i++] = (struct usb_descriptor_header *) &bulk_in_desc; + midi_function[i++] = (struct usb_descriptor_header *) &ms_in_desc; + midi_function[i++] = NULL; + + /* + * support all relevant hardware speeds... we expect that when + * hardware is dual speed, all bulk-capable endpoints work at + * both speeds + */ + /* copy descriptors, and track endpoint copies */ + if (gadget_is_dualspeed(c->cdev->gadget)) { + c->highspeed = true; + bulk_in_desc.wMaxPacketSize = cpu_to_le16(512); + bulk_out_desc.wMaxPacketSize = cpu_to_le16(512); + f->hs_descriptors = usb_copy_descriptors(midi_function); + } else { + f->descriptors = usb_copy_descriptors(midi_function); + } + + return 0; + +fail: + /* we might as well release our claims on endpoints */ + if (midi->out_ep) + midi->out_ep->driver_data = NULL; + if (midi->in_ep) + midi->in_ep->driver_data = NULL; + + ERROR(cdev, "%s: can't bind, err %d\n", f->name, status); + + return status; +} + +/** + * f_midi_bind_config - add USB MIDI function to a configuration + * @c: the configuration to supcard the USB audio function + * @index: the soundcard index to use for the ALSA device creation + * @id: the soundcard id to use for the ALSA device creation + * @buflen: the buffer length to use + * @qlen the number of read requests to pre-allocate + * Context: single threaded during gadget setup + * + * Returns zero on success, else negative errno. + */ +int __init f_midi_bind_config(struct usb_configuration *c, + int index, char *id, + unsigned int in_ports, + unsigned int out_ports, + unsigned int buflen, + unsigned int qlen) +{ + struct f_midi *midi; + int status, i; + + /* sanity check */ + if (in_ports > MAX_PORTS || out_ports > MAX_PORTS) + return -EINVAL; + + /* allocate and initialize one new instance */ + midi = kzalloc(sizeof *midi, GFP_KERNEL); + if (!midi) { + status = -ENOMEM; + goto fail; + } + + for (i = 0; i < in_ports; i++) { + struct gmidi_in_port *port = kzalloc(sizeof(*port), GFP_KERNEL); + if (!port) { + status = -ENOMEM; + goto setup_fail; + } + + port->midi = midi; + port->active = 0; + port->cable = i; + midi->in_port[i] = port; + } + + midi->gadget = c->cdev->gadget; + tasklet_init(&midi->tasklet, f_midi_in_tasklet, (unsigned long) midi); + + /* set up ALSA midi devices */ + midi->in_ports = in_ports; + midi->out_ports = out_ports; + status = f_midi_register_card(midi); + if (status < 0) + goto setup_fail; + + midi->func.name = "gmidi function"; + midi->func.strings = midi_strings; + midi->func.bind = f_midi_bind; + midi->func.unbind = f_midi_unbind; + midi->func.set_alt = f_midi_set_alt; + midi->func.disable = f_midi_disable; + + midi->id = kstrdup(id, GFP_KERNEL); + midi->index = index; + midi->buflen = buflen; + midi->qlen = qlen; + + status = usb_add_function(c, &midi->func); + if (status) + goto setup_fail; + + return 0; + +setup_fail: + for (--i; i >= 0; i--) + kfree(midi->in_port[i]); + kfree(midi); +fail: + return status; +} + diff --git a/drivers/usb/gadget/f_ncm.c b/drivers/usb/gadget/f_ncm.c index ae69ed7e6b99..aab8eded045b 100644 --- a/drivers/usb/gadget/f_ncm.c +++ b/drivers/usb/gadget/f_ncm.c @@ -13,15 +13,6 @@ * 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 */ #include <linux/kernel.h> diff --git a/drivers/usb/gadget/f_obex.c b/drivers/usb/gadget/f_obex.c index 394502abeb96..e3f74bf5da2d 100644 --- a/drivers/usb/gadget/f_obex.c +++ b/drivers/usb/gadget/f_obex.c @@ -10,15 +10,6 @@ * 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 */ /* #define VERBOSE_DEBUG */ diff --git a/drivers/usb/gadget/f_phonet.c b/drivers/usb/gadget/f_phonet.c index 8f3eab1af885..349077033338 100644 --- a/drivers/usb/gadget/f_phonet.c +++ b/drivers/usb/gadget/f_phonet.c @@ -8,16 +8,6 @@ * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. - * - * 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., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA */ #include <linux/mm.h> diff --git a/drivers/usb/gadget/f_rndis.c b/drivers/usb/gadget/f_rndis.c index 3ea4666be3d0..704d1d94f72a 100644 --- a/drivers/usb/gadget/f_rndis.c +++ b/drivers/usb/gadget/f_rndis.c @@ -11,15 +11,6 @@ * 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 */ /* #define VERBOSE_DEBUG */ diff --git a/drivers/usb/gadget/f_sourcesink.c b/drivers/usb/gadget/f_sourcesink.c index e18b4f520951..168906d2b5d4 100644 --- a/drivers/usb/gadget/f_sourcesink.c +++ b/drivers/usb/gadget/f_sourcesink.c @@ -8,15 +8,6 @@ * 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 */ /* #define VERBOSE_DEBUG */ diff --git a/drivers/usb/gadget/f_subset.c b/drivers/usb/gadget/f_subset.c index 3dc53754ab60..c1540648125a 100644 --- a/drivers/usb/gadget/f_subset.c +++ b/drivers/usb/gadget/f_subset.c @@ -8,15 +8,6 @@ * 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 */ #include <linux/slab.h> diff --git a/drivers/usb/gadget/f_uvc.c b/drivers/usb/gadget/f_uvc.c index 7a8b9aa4aea5..2022fe492148 100644 --- a/drivers/usb/gadget/f_uvc.c +++ b/drivers/usb/gadget/f_uvc.c @@ -8,7 +8,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * */ #include <linux/kernel.h> diff --git a/drivers/usb/gadget/f_uvc.h b/drivers/usb/gadget/f_uvc.h index e18a6636c283..abf832935134 100644 --- a/drivers/usb/gadget/f_uvc.h +++ b/drivers/usb/gadget/f_uvc.h @@ -8,7 +8,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * */ #ifndef _F_UVC_H_ diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c index 639e14a2fd15..3ac4f51cd0bb 100644 --- a/drivers/usb/gadget/file_storage.c +++ b/drivers/usb/gadget/file_storage.c @@ -69,8 +69,7 @@ * each LUN would be settable independently as a disk drive or a CD-ROM * drive, but currently all LUNs have to be the same type. The CD-ROM * emulation includes a single data track and no audio tracks; hence there - * need be only one backing file per LUN. Note also that the CD-ROM block - * length is set to 512 rather than the more common value 2048. + * need be only one backing file per LUN. * * Requirements are modest; only a bulk-in and a bulk-out endpoint are * needed (an interrupt-out endpoint is also needed for CBI). The memory @@ -461,7 +460,6 @@ struct fsg_dev { struct fsg_buffhd *next_buffhd_to_fill; struct fsg_buffhd *next_buffhd_to_drain; - struct fsg_buffhd buffhds[FSG_NUM_BUFFERS]; int thread_wakeup_needed; struct completion thread_notifier; @@ -488,6 +486,8 @@ struct fsg_dev { unsigned int nluns; struct fsg_lun *luns; struct fsg_lun *curlun; + /* Must be the last entry */ + struct fsg_buffhd buffhds[]; }; typedef void (*fsg_routine_t)(struct fsg_dev *); @@ -586,7 +586,19 @@ dev_qualifier = { .bNumConfigurations = 1, }; +static int populate_bos(struct fsg_dev *fsg, u8 *buf) +{ + memcpy(buf, &fsg_bos_desc, USB_DT_BOS_SIZE); + buf += USB_DT_BOS_SIZE; + + memcpy(buf, &fsg_ext_cap_desc, USB_DT_USB_EXT_CAP_SIZE); + buf += USB_DT_USB_EXT_CAP_SIZE; + memcpy(buf, &fsg_ss_cap_desc, USB_DT_USB_SS_CAP_SIZE); + + return USB_DT_BOS_SIZE + USB_DT_USB_SS_CAP_SIZE + + USB_DT_USB_EXT_CAP_SIZE; +} /* * Config descriptors must agree with the code that sets configurations @@ -935,7 +947,8 @@ static int standard_setup_req(struct fsg_dev *fsg, break; case USB_DT_DEVICE_QUALIFIER: VDBG(fsg, "get device qualifier\n"); - if (!gadget_is_dualspeed(fsg->gadget)) + if (!gadget_is_dualspeed(fsg->gadget) || + fsg->gadget->speed == USB_SPEED_SUPER) break; /* * Assume ep0 uses the same maxpacket value for both @@ -948,7 +961,8 @@ static int standard_setup_req(struct fsg_dev *fsg, case USB_DT_OTHER_SPEED_CONFIG: VDBG(fsg, "get other-speed config descriptor\n"); - if (!gadget_is_dualspeed(fsg->gadget)) + if (!gadget_is_dualspeed(fsg->gadget) || + fsg->gadget->speed == USB_SPEED_SUPER) break; goto get_config; case USB_DT_CONFIG: @@ -967,7 +981,15 @@ get_config: value = usb_gadget_get_string(&fsg_stringtab, w_value & 0xff, req->buf); break; + + case USB_DT_BOS: + VDBG(fsg, "get bos descriptor\n"); + + if (gadget_is_superspeed(fsg->gadget)) + value = populate_bos(fsg, req->buf); + break; } + break; /* One config, two speeds */ @@ -1136,7 +1158,6 @@ static int do_read(struct fsg_dev *fsg) u32 amount_left; loff_t file_offset, file_offset_tmp; unsigned int amount; - unsigned int partial_page; ssize_t nread; /* Get the starting Logical Block Address and check that it's @@ -1158,7 +1179,7 @@ static int do_read(struct fsg_dev *fsg) curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; return -EINVAL; } - file_offset = ((loff_t) lba) << 9; + file_offset = ((loff_t) lba) << curlun->blkbits; /* Carry out the file reads */ amount_left = fsg->data_size_from_cmnd; @@ -1171,17 +1192,10 @@ static int do_read(struct fsg_dev *fsg) * Try to read the remaining amount. * But don't read more than the buffer size. * And don't try to read past the end of the file. - * Finally, if we're not at a page boundary, don't read past - * the next page. - * If this means reading 0 then we were asked to read past - * the end of file. */ + */ amount = min((unsigned int) amount_left, mod_data.buflen); amount = min((loff_t) amount, curlun->file_length - file_offset); - partial_page = file_offset & (PAGE_CACHE_SIZE - 1); - if (partial_page > 0) - amount = min(amount, (unsigned int) PAGE_CACHE_SIZE - - partial_page); /* Wait for the next buffer to become available */ bh = fsg->next_buffhd_to_fill; @@ -1196,7 +1210,7 @@ static int do_read(struct fsg_dev *fsg) if (amount == 0) { curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; - curlun->sense_data_info = file_offset >> 9; + curlun->sense_data_info = file_offset >> curlun->blkbits; curlun->info_valid = 1; bh->inreq->length = 0; bh->state = BUF_STATE_FULL; @@ -1221,18 +1235,23 @@ static int do_read(struct fsg_dev *fsg) } else if (nread < amount) { LDBG(curlun, "partial file read: %d/%u\n", (int) nread, amount); - nread -= (nread & 511); // Round down to a block + nread = round_down(nread, curlun->blksize); } file_offset += nread; amount_left -= nread; fsg->residue -= nread; + + /* Except at the end of the transfer, nread will be + * equal to the buffer size, which is divisible by the + * bulk-in maxpacket size. + */ bh->inreq->length = nread; bh->state = BUF_STATE_FULL; /* If an error occurred, report it and its position */ if (nread < amount) { curlun->sense_data = SS_UNRECOVERED_READ_ERROR; - curlun->sense_data_info = file_offset >> 9; + curlun->sense_data_info = file_offset >> curlun->blkbits; curlun->info_valid = 1; break; } @@ -1262,7 +1281,6 @@ static int do_write(struct fsg_dev *fsg) u32 amount_left_to_req, amount_left_to_write; loff_t usb_offset, file_offset, file_offset_tmp; unsigned int amount; - unsigned int partial_page; ssize_t nwritten; int rc; @@ -1303,7 +1321,7 @@ static int do_write(struct fsg_dev *fsg) /* Carry out the file writes */ get_some_more = 1; - file_offset = usb_offset = ((loff_t) lba) << 9; + file_offset = usb_offset = ((loff_t) lba) << curlun->blkbits; amount_left_to_req = amount_left_to_write = fsg->data_size_from_cmnd; while (amount_left_to_write > 0) { @@ -1313,38 +1331,20 @@ static int do_write(struct fsg_dev *fsg) if (bh->state == BUF_STATE_EMPTY && get_some_more) { /* Figure out how much we want to get: - * Try to get the remaining amount. - * But don't get more than the buffer size. - * And don't try to go past the end of the file. - * If we're not at a page boundary, - * don't go past the next page. - * If this means getting 0, then we were asked - * to write past the end of file. - * Finally, round down to a block boundary. */ + * Try to get the remaining amount, + * but not more than the buffer size. + */ amount = min(amount_left_to_req, mod_data.buflen); - amount = min((loff_t) amount, curlun->file_length - - usb_offset); - partial_page = usb_offset & (PAGE_CACHE_SIZE - 1); - if (partial_page > 0) - amount = min(amount, - (unsigned int) PAGE_CACHE_SIZE - partial_page); - - if (amount == 0) { + + /* Beyond the end of the backing file? */ + if (usb_offset >= curlun->file_length) { get_some_more = 0; curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; - curlun->sense_data_info = usb_offset >> 9; + curlun->sense_data_info = usb_offset >> curlun->blkbits; curlun->info_valid = 1; continue; } - amount -= (amount & 511); - if (amount == 0) { - - /* Why were we were asked to transfer a - * partial block? */ - get_some_more = 0; - continue; - } /* Get the next buffer */ usb_offset += amount; @@ -1353,11 +1353,11 @@ static int do_write(struct fsg_dev *fsg) if (amount_left_to_req == 0) get_some_more = 0; - /* amount is always divisible by 512, hence by - * the bulk-out maxpacket size */ - bh->outreq->length = bh->bulk_out_intended_length = - amount; - bh->outreq->short_not_ok = 1; + /* Except at the end of the transfer, amount will be + * equal to the buffer size, which is divisible by + * the bulk-out maxpacket size. + */ + set_bulk_out_req_length(fsg, bh, amount); start_transfer(fsg, fsg->bulk_out, bh->outreq, &bh->outreq_busy, &bh->state); fsg->next_buffhd_to_fill = bh->next; @@ -1376,7 +1376,7 @@ static int do_write(struct fsg_dev *fsg) /* Did something go wrong with the transfer? */ if (bh->outreq->status != 0) { curlun->sense_data = SS_COMMUNICATION_FAILURE; - curlun->sense_data_info = file_offset >> 9; + curlun->sense_data_info = file_offset >> curlun->blkbits; curlun->info_valid = 1; break; } @@ -1390,6 +1390,16 @@ static int do_write(struct fsg_dev *fsg) amount = curlun->file_length - file_offset; } + /* Don't accept excess data. The spec doesn't say + * what to do in this case. We'll ignore the error. + */ + amount = min(amount, bh->bulk_out_intended_length); + + /* Don't write a partial block */ + amount = round_down(amount, curlun->blksize); + if (amount == 0) + goto empty_write; + /* Perform the write */ file_offset_tmp = file_offset; nwritten = vfs_write(curlun->filp, @@ -1408,8 +1418,7 @@ static int do_write(struct fsg_dev *fsg) } else if (nwritten < amount) { LDBG(curlun, "partial file write: %d/%u\n", (int) nwritten, amount); - nwritten -= (nwritten & 511); - // Round down to a block + nwritten = round_down(nwritten, curlun->blksize); } file_offset += nwritten; amount_left_to_write -= nwritten; @@ -1418,13 +1427,14 @@ static int do_write(struct fsg_dev *fsg) /* If an error occurred, report it and its position */ if (nwritten < amount) { curlun->sense_data = SS_WRITE_ERROR; - curlun->sense_data_info = file_offset >> 9; + curlun->sense_data_info = file_offset >> curlun->blkbits; curlun->info_valid = 1; break; } + empty_write: /* Did the host decide to stop early? */ - if (bh->outreq->actual != bh->outreq->length) { + if (bh->outreq->actual < bh->bulk_out_intended_length) { fsg->short_packet_received = 1; break; } @@ -1500,8 +1510,8 @@ static int do_verify(struct fsg_dev *fsg) return -EIO; // No default reply /* Prepare to carry out the file verify */ - amount_left = verification_length << 9; - file_offset = ((loff_t) lba) << 9; + amount_left = verification_length << curlun->blkbits; + file_offset = ((loff_t) lba) << curlun->blkbits; /* Write out all the dirty buffers before invalidating them */ fsg_lun_fsync_sub(curlun); @@ -1519,15 +1529,14 @@ static int do_verify(struct fsg_dev *fsg) * Try to read the remaining amount, but not more than * the buffer size. * And don't try to read past the end of the file. - * If this means reading 0 then we were asked to read - * past the end of file. */ + */ amount = min((unsigned int) amount_left, mod_data.buflen); amount = min((loff_t) amount, curlun->file_length - file_offset); if (amount == 0) { curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; - curlun->sense_data_info = file_offset >> 9; + curlun->sense_data_info = file_offset >> curlun->blkbits; curlun->info_valid = 1; break; } @@ -1550,11 +1559,11 @@ static int do_verify(struct fsg_dev *fsg) } else if (nread < amount) { LDBG(curlun, "partial file verify: %d/%u\n", (int) nread, amount); - nread -= (nread & 511); // Round down to a sector + nread = round_down(nread, curlun->blksize); } if (nread == 0) { curlun->sense_data = SS_UNRECOVERED_READ_ERROR; - curlun->sense_data_info = file_offset >> 9; + curlun->sense_data_info = file_offset >> curlun->blkbits; curlun->info_valid = 1; break; } @@ -1668,7 +1677,7 @@ static int do_read_capacity(struct fsg_dev *fsg, struct fsg_buffhd *bh) put_unaligned_be32(curlun->num_sectors - 1, &buf[0]); /* Max logical block */ - put_unaligned_be32(512, &buf[4]); /* Block length */ + put_unaligned_be32(curlun->blksize, &buf[4]); /* Block length */ return 8; } @@ -1890,7 +1899,7 @@ static int do_read_format_capacities(struct fsg_dev *fsg, put_unaligned_be32(curlun->num_sectors, &buf[0]); /* Number of blocks */ - put_unaligned_be32(512, &buf[4]); /* Block length */ + put_unaligned_be32(curlun->blksize, &buf[4]); /* Block length */ buf[4] = 0x02; /* Current capacity */ return 12; } @@ -1969,7 +1978,7 @@ static int throw_away_data(struct fsg_dev *fsg) fsg->next_buffhd_to_drain = bh->next; /* A short packet or an error ends everything */ - if (bh->outreq->actual != bh->outreq->length || + if (bh->outreq->actual < bh->bulk_out_intended_length || bh->outreq->status != 0) { raise_exception(fsg, FSG_STATE_ABORT_BULK_OUT); return -EINTR; @@ -1983,11 +1992,11 @@ static int throw_away_data(struct fsg_dev *fsg) amount = min(fsg->usb_amount_left, (u32) mod_data.buflen); - /* amount is always divisible by 512, hence by - * the bulk-out maxpacket size */ - bh->outreq->length = bh->bulk_out_intended_length = - amount; - bh->outreq->short_not_ok = 1; + /* Except at the end of the transfer, amount will be + * equal to the buffer size, which is divisible by + * the bulk-out maxpacket size. + */ + set_bulk_out_req_length(fsg, bh, amount); start_transfer(fsg, fsg->bulk_out, bh->outreq, &bh->outreq_busy, &bh->state); fsg->next_buffhd_to_fill = bh->next; @@ -2415,7 +2424,7 @@ static int do_scsi_command(struct fsg_dev *fsg) case READ_6: i = fsg->cmnd[4]; - fsg->data_size_from_cmnd = (i == 0 ? 256 : i) << 9; + fsg->data_size_from_cmnd = (i == 0 ? 256 : i) << fsg->curlun->blkbits; if ((reply = check_command(fsg, 6, DATA_DIR_TO_HOST, (7<<1) | (1<<4), 1, "READ(6)")) == 0) @@ -2424,7 +2433,7 @@ static int do_scsi_command(struct fsg_dev *fsg) case READ_10: fsg->data_size_from_cmnd = - get_unaligned_be16(&fsg->cmnd[7]) << 9; + get_unaligned_be16(&fsg->cmnd[7]) << fsg->curlun->blkbits; if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST, (1<<1) | (0xf<<2) | (3<<7), 1, "READ(10)")) == 0) @@ -2433,7 +2442,7 @@ static int do_scsi_command(struct fsg_dev *fsg) case READ_12: fsg->data_size_from_cmnd = - get_unaligned_be32(&fsg->cmnd[6]) << 9; + get_unaligned_be32(&fsg->cmnd[6]) << fsg->curlun->blkbits; if ((reply = check_command(fsg, 12, DATA_DIR_TO_HOST, (1<<1) | (0xf<<2) | (0xf<<6), 1, "READ(12)")) == 0) @@ -2519,7 +2528,7 @@ static int do_scsi_command(struct fsg_dev *fsg) case WRITE_6: i = fsg->cmnd[4]; - fsg->data_size_from_cmnd = (i == 0 ? 256 : i) << 9; + fsg->data_size_from_cmnd = (i == 0 ? 256 : i) << fsg->curlun->blkbits; if ((reply = check_command(fsg, 6, DATA_DIR_FROM_HOST, (7<<1) | (1<<4), 1, "WRITE(6)")) == 0) @@ -2528,7 +2537,7 @@ static int do_scsi_command(struct fsg_dev *fsg) case WRITE_10: fsg->data_size_from_cmnd = - get_unaligned_be16(&fsg->cmnd[7]) << 9; + get_unaligned_be16(&fsg->cmnd[7]) << fsg->curlun->blkbits; if ((reply = check_command(fsg, 10, DATA_DIR_FROM_HOST, (1<<1) | (0xf<<2) | (3<<7), 1, "WRITE(10)")) == 0) @@ -2537,7 +2546,7 @@ static int do_scsi_command(struct fsg_dev *fsg) case WRITE_12: fsg->data_size_from_cmnd = - get_unaligned_be32(&fsg->cmnd[6]) << 9; + get_unaligned_be32(&fsg->cmnd[6]) << fsg->curlun->blkbits; if ((reply = check_command(fsg, 12, DATA_DIR_FROM_HOST, (1<<1) | (0xf<<2) | (0xf<<6), 1, "WRITE(12)")) == 0) @@ -2666,7 +2675,6 @@ static int get_next_command(struct fsg_dev *fsg) /* Queue a request to read a Bulk-only CBW */ set_bulk_out_req_length(fsg, bh, USB_BULK_CB_WRAP_LEN); - bh->outreq->short_not_ok = 1; start_transfer(fsg, fsg->bulk_out, bh->outreq, &bh->outreq_busy, &bh->state); @@ -2752,7 +2760,7 @@ static int do_set_interface(struct fsg_dev *fsg, int altsetting) reset: /* Deallocate the requests */ - for (i = 0; i < FSG_NUM_BUFFERS; ++i) { + for (i = 0; i < fsg_num_buffers; ++i) { struct fsg_buffhd *bh = &fsg->buffhds[i]; if (bh->inreq) { @@ -2791,29 +2799,32 @@ reset: /* Enable the endpoints */ d = fsg_ep_desc(fsg->gadget, - &fsg_fs_bulk_in_desc, &fsg_hs_bulk_in_desc); + &fsg_fs_bulk_in_desc, &fsg_hs_bulk_in_desc, + &fsg_ss_bulk_in_desc); if ((rc = enable_endpoint(fsg, fsg->bulk_in, d)) != 0) goto reset; fsg->bulk_in_enabled = 1; d = fsg_ep_desc(fsg->gadget, - &fsg_fs_bulk_out_desc, &fsg_hs_bulk_out_desc); + &fsg_fs_bulk_out_desc, &fsg_hs_bulk_out_desc, + &fsg_ss_bulk_out_desc); if ((rc = enable_endpoint(fsg, fsg->bulk_out, d)) != 0) goto reset; fsg->bulk_out_enabled = 1; - fsg->bulk_out_maxpacket = le16_to_cpu(d->wMaxPacketSize); + fsg->bulk_out_maxpacket = usb_endpoint_maxp(d); clear_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags); if (transport_is_cbi()) { d = fsg_ep_desc(fsg->gadget, - &fsg_fs_intr_in_desc, &fsg_hs_intr_in_desc); + &fsg_fs_intr_in_desc, &fsg_hs_intr_in_desc, + &fsg_ss_intr_in_desc); if ((rc = enable_endpoint(fsg, fsg->intr_in, d)) != 0) goto reset; fsg->intr_in_enabled = 1; } /* Allocate the requests */ - for (i = 0; i < FSG_NUM_BUFFERS; ++i) { + for (i = 0; i < fsg_num_buffers; ++i) { struct fsg_buffhd *bh = &fsg->buffhds[i]; if ((rc = alloc_request(fsg, fsg->bulk_in, &bh->inreq)) != 0) @@ -2862,17 +2873,10 @@ static int do_set_config(struct fsg_dev *fsg, u8 new_config) fsg->config = new_config; if ((rc = do_set_interface(fsg, 0)) != 0) fsg->config = 0; // Reset on errors - else { - char *speed; - - switch (fsg->gadget->speed) { - case USB_SPEED_LOW: speed = "low"; break; - case USB_SPEED_FULL: speed = "full"; break; - case USB_SPEED_HIGH: speed = "high"; break; - default: speed = "?"; break; - } - INFO(fsg, "%s speed config #%d\n", speed, fsg->config); - } + else + INFO(fsg, "%s config #%d\n", + usb_speed_string(fsg->gadget->speed), + fsg->config); } return rc; } @@ -2909,7 +2913,7 @@ static void handle_exception(struct fsg_dev *fsg) /* Cancel all the pending transfers */ if (fsg->intreq_busy) usb_ep_dequeue(fsg->intr_in, fsg->intreq); - for (i = 0; i < FSG_NUM_BUFFERS; ++i) { + for (i = 0; i < fsg_num_buffers; ++i) { bh = &fsg->buffhds[i]; if (bh->inreq_busy) usb_ep_dequeue(fsg->bulk_in, bh->inreq); @@ -2920,7 +2924,7 @@ static void handle_exception(struct fsg_dev *fsg) /* Wait until everything is idle */ for (;;) { num_active = fsg->intreq_busy; - for (i = 0; i < FSG_NUM_BUFFERS; ++i) { + for (i = 0; i < fsg_num_buffers; ++i) { bh = &fsg->buffhds[i]; num_active += bh->inreq_busy + bh->outreq_busy; } @@ -2942,7 +2946,7 @@ static void handle_exception(struct fsg_dev *fsg) * state, and the exception. Then invoke the handler. */ spin_lock_irq(&fsg->lock); - for (i = 0; i < FSG_NUM_BUFFERS; ++i) { + for (i = 0; i < fsg_num_buffers; ++i) { bh = &fsg->buffhds[i]; bh->state = BUF_STATE_EMPTY; } @@ -3149,6 +3153,15 @@ static void /* __init_or_exit */ fsg_unbind(struct usb_gadget *gadget) DBG(fsg, "unbind\n"); clear_bit(REGISTERED, &fsg->atomic_bitflags); + /* If the thread isn't already dead, tell it to exit now */ + if (fsg->state != FSG_STATE_TERMINATED) { + raise_exception(fsg, FSG_STATE_EXIT); + wait_for_completion(&fsg->thread_notifier); + + /* The cleanup routine waits for this completion also */ + complete(&fsg->thread_notifier); + } + /* Unregister the sysfs attribute files and the LUNs */ for (i = 0; i < fsg->nluns; ++i) { curlun = &fsg->luns[i]; @@ -3162,17 +3175,8 @@ static void /* __init_or_exit */ fsg_unbind(struct usb_gadget *gadget) } } - /* If the thread isn't already dead, tell it to exit now */ - if (fsg->state != FSG_STATE_TERMINATED) { - raise_exception(fsg, FSG_STATE_EXIT); - wait_for_completion(&fsg->thread_notifier); - - /* The cleanup routine waits for this completion also */ - complete(&fsg->thread_notifier); - } - /* Free the data buffers */ - for (i = 0; i < FSG_NUM_BUFFERS; ++i) + for (i = 0; i < fsg_num_buffers; ++i) kfree(fsg->buffhds[i].buf); /* Free the request and buffer for endpoint 0 */ @@ -3445,6 +3449,24 @@ static int __init fsg_bind(struct usb_gadget *gadget) fsg_fs_intr_in_desc.bEndpointAddress; } + if (gadget_is_superspeed(gadget)) { + unsigned max_burst; + + fsg_ss_function[i + FSG_SS_FUNCTION_PRE_EP_ENTRIES] = NULL; + + /* Calculate bMaxBurst, we know packet size is 1024 */ + max_burst = min_t(unsigned, mod_data.buflen / 1024, 15); + + /* Assume endpoint addresses are the same for both speeds */ + fsg_ss_bulk_in_desc.bEndpointAddress = + fsg_fs_bulk_in_desc.bEndpointAddress; + fsg_ss_bulk_in_comp_desc.bMaxBurst = max_burst; + + fsg_ss_bulk_out_desc.bEndpointAddress = + fsg_fs_bulk_out_desc.bEndpointAddress; + fsg_ss_bulk_out_comp_desc.bMaxBurst = max_burst; + } + if (gadget_is_otg(gadget)) fsg_otg_desc.bmAttributes |= USB_OTG_HNP; @@ -3460,7 +3482,7 @@ static int __init fsg_bind(struct usb_gadget *gadget) req->complete = ep0_complete; /* Allocate the data buffers */ - for (i = 0; i < FSG_NUM_BUFFERS; ++i) { + for (i = 0; i < fsg_num_buffers; ++i) { struct fsg_buffhd *bh = &fsg->buffhds[i]; /* Allocate for the bulk-in endpoint. We assume that @@ -3471,7 +3493,7 @@ static int __init fsg_bind(struct usb_gadget *gadget) goto out; bh->next = bh + 1; } - fsg->buffhds[FSG_NUM_BUFFERS - 1].next = &fsg->buffhds[0]; + fsg->buffhds[fsg_num_buffers - 1].next = &fsg->buffhds[0]; /* This should reflect the actual gadget power source */ usb_gadget_set_selfpowered(gadget); @@ -3561,11 +3583,7 @@ static void fsg_resume(struct usb_gadget *gadget) /*-------------------------------------------------------------------------*/ static struct usb_gadget_driver fsg_driver = { -#ifdef CONFIG_USB_GADGET_DUALSPEED - .speed = USB_SPEED_HIGH, -#else - .speed = USB_SPEED_FULL, -#endif + .speed = USB_SPEED_SUPER, .function = (char *) fsg_string_product, .unbind = fsg_unbind, .disconnect = fsg_disconnect, @@ -3587,7 +3605,9 @@ static int __init fsg_alloc(void) { struct fsg_dev *fsg; - fsg = kzalloc(sizeof *fsg, GFP_KERNEL); + fsg = kzalloc(sizeof *fsg + + fsg_num_buffers * sizeof *(fsg->buffhds), GFP_KERNEL); + if (!fsg) return -ENOMEM; spin_lock_init(&fsg->lock); @@ -3605,6 +3625,10 @@ static int __init fsg_init(void) int rc; struct fsg_dev *fsg; + rc = fsg_num_buffers_validate(); + if (rc != 0) + return rc; + if ((rc = fsg_alloc()) != 0) return rc; fsg = the_fsg; diff --git a/drivers/usb/gadget/fsl_qe_udc.c b/drivers/usb/gadget/fsl_qe_udc.c index 3bf872e1ad39..2a03e4de11c1 100644 --- a/drivers/usb/gadget/fsl_qe_udc.c +++ b/drivers/usb/gadget/fsl_qe_udc.c @@ -540,7 +540,7 @@ static int qe_ep_init(struct qe_udc *udc, int reval = 0; u16 max = 0; - max = le16_to_cpu(desc->wMaxPacketSize); + max = usb_endpoint_maxp(desc); /* check the max package size validate for this endpoint */ /* Refer to USB2.0 spec table 9-13, diff --git a/drivers/usb/gadget/fsl_udc_core.c b/drivers/usb/gadget/fsl_udc_core.c index de24a4233c25..b2c44e1d5813 100644 --- a/drivers/usb/gadget/fsl_udc_core.c +++ b/drivers/usb/gadget/fsl_udc_core.c @@ -559,7 +559,7 @@ static int fsl_ep_enable(struct usb_ep *_ep, if (!udc->driver || (udc->gadget.speed == USB_SPEED_UNKNOWN)) return -ESHUTDOWN; - max = le16_to_cpu(desc->wMaxPacketSize); + max = usb_endpoint_maxp(desc); /* Disable automatic zlp generation. Driver is responsible to indicate * explicitly through req->req.zero. This is needed to enable multi-td @@ -1715,34 +1715,31 @@ static void dtd_complete_irq(struct fsl_udc *udc) } } +static inline enum usb_device_speed portscx_device_speed(u32 reg) +{ + switch (speed & PORTSCX_PORT_SPEED_MASK) { + case PORTSCX_PORT_SPEED_HIGH: + return USB_SPEED_HIGH; + case PORTSCX_PORT_SPEED_FULL: + return USB_SPEED_FULL; + case PORTSCX_PORT_SPEED_LOW: + return USB_SPEED_LOW; + default: + return USB_SPEED_UNKNOWN; + } +} + /* Process a port change interrupt */ static void port_change_irq(struct fsl_udc *udc) { - u32 speed; - if (udc->bus_reset) udc->bus_reset = 0; /* Bus resetting is finished */ - if (!(fsl_readl(&dr_regs->portsc1) & PORTSCX_PORT_RESET)) { + if (!(fsl_readl(&dr_regs->portsc1) & PORTSCX_PORT_RESET)) /* Get the speed */ - speed = (fsl_readl(&dr_regs->portsc1) - & PORTSCX_PORT_SPEED_MASK); - switch (speed) { - case PORTSCX_PORT_SPEED_HIGH: - udc->gadget.speed = USB_SPEED_HIGH; - break; - case PORTSCX_PORT_SPEED_FULL: - udc->gadget.speed = USB_SPEED_FULL; - break; - case PORTSCX_PORT_SPEED_LOW: - udc->gadget.speed = USB_SPEED_LOW; - break; - default: - udc->gadget.speed = USB_SPEED_UNKNOWN; - break; - } - } + udc->gadget.speed = + portscx_device_speed(fsl_readl(&dr_regs->portsc1)); /* Update USB state */ if (!udc->resume_state) @@ -2167,20 +2164,8 @@ static int fsl_proc_read(char *page, char **start, off_t off, int count, default: s = "None"; break; } - s;} ), ( { - char *s; - switch (tmp_reg & PORTSCX_PORT_SPEED_UNDEF) { - case PORTSCX_PORT_SPEED_FULL: - s = "Full Speed"; break; - case PORTSCX_PORT_SPEED_LOW: - s = "Low Speed"; break; - case PORTSCX_PORT_SPEED_HIGH: - s = "High Speed"; break; - default: - s = "Undefined"; break; - } - s; - } ), + s;} ), + usb_speed_string(portscx_device_speed(tmp_reg)), (tmp_reg & PORTSCX_PHY_LOW_POWER_SPD) ? "Normal PHY mode" : "Low power mode", (tmp_reg & PORTSCX_PORT_RESET) ? "In Reset" : diff --git a/drivers/usb/gadget/fusb300_udc.c b/drivers/usb/gadget/fusb300_udc.c index 4ec888f90002..e593f2849fa9 100644 --- a/drivers/usb/gadget/fusb300_udc.c +++ b/drivers/usb/gadget/fusb300_udc.c @@ -8,16 +8,6 @@ * 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; version 2 of the License. - * - * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * */ #include <linux/dma-mapping.h> #include <linux/err.h> @@ -220,7 +210,7 @@ static int config_ep(struct fusb300_ep *ep, info.type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; info.dir_in = (desc->bEndpointAddress & USB_ENDPOINT_DIR_MASK) ? 1 : 0; - info.maxpacket = le16_to_cpu(desc->wMaxPacketSize); + info.maxpacket = usb_endpoint_maxp(desc); info.epnum = desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; if ((info.type == USB_ENDPOINT_XFER_INT) || @@ -1479,7 +1469,7 @@ static int __init fusb300_probe(struct platform_device *pdev) fusb300->gadget.name = udc_name; fusb300->reg = reg; - ret = request_irq(ires->start, fusb300_irq, IRQF_DISABLED | IRQF_SHARED, + ret = request_irq(ires->start, fusb300_irq, IRQF_SHARED, udc_name, fusb300); if (ret < 0) { pr_err("request_irq error (%d)\n", ret); @@ -1487,7 +1477,7 @@ static int __init fusb300_probe(struct platform_device *pdev) } ret = request_irq(ires1->start, fusb300_irq, - IRQF_DISABLED | IRQF_SHARED, udc_name, fusb300); + IRQF_SHARED, udc_name, fusb300); if (ret < 0) { pr_err("request_irq1 error (%d)\n", ret); goto clean_up; diff --git a/drivers/usb/gadget/fusb300_udc.h b/drivers/usb/gadget/fusb300_udc.h index f51aa2ef1f90..92745bd03064 100644 --- a/drivers/usb/gadget/fusb300_udc.h +++ b/drivers/usb/gadget/fusb300_udc.h @@ -8,16 +8,6 @@ * 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; version 2 of the License. - * - * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * */ diff --git a/drivers/usb/gadget/g_ffs.c b/drivers/usb/gadget/g_ffs.c index 704c2800ac00..0519d77915ec 100644 --- a/drivers/usb/gadget/g_ffs.c +++ b/drivers/usb/gadget/g_ffs.c @@ -8,15 +8,6 @@ * 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 */ #define pr_fmt(fmt) "g_ffs: " fmt diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h index f3a83cd0ef50..a8855d0b7f3b 100644 --- a/drivers/usb/gadget/gadget_chips.h +++ b/drivers/usb/gadget/gadget_chips.h @@ -31,6 +31,7 @@ #define gadget_is_ci13xxx_msm(g) (!strcmp("ci13xxx_msm", (g)->name)) #define gadget_is_ci13xxx_pci(g) (!strcmp("ci13xxx_pci", (g)->name)) #define gadget_is_dummy(g) (!strcmp("dummy_udc", (g)->name)) +#define gadget_is_dwc3(g) (!strcmp("dwc3-gadget", (g)->name)) #define gadget_is_fsl_qe(g) (!strcmp("fsl_qe_udc", (g)->name)) #define gadget_is_fsl_usb2(g) (!strcmp("fsl-usb2-udc", (g)->name)) #define gadget_is_goku(g) (!strcmp("goku_udc", (g)->name)) @@ -115,6 +116,8 @@ static inline int usb_gadget_controller_number(struct usb_gadget *gadget) return 0x30; else if (gadget_is_net2272(gadget)) return 0x31; + else if (gadget_is_dwc3(gadget)) + return 0x32; return -ENOENT; } diff --git a/drivers/usb/gadget/gmidi.c b/drivers/usb/gadget/gmidi.c index 8b9220e128a7..8fcde37aa6d4 100644 --- a/drivers/usb/gadget/gmidi.c +++ b/drivers/usb/gadget/gmidi.c @@ -36,134 +36,43 @@ #include "gadget_chips.h" - -/* - * Kbuild is not very cooperative with respect to linking separately - * compiled library objects into one module. So for now we won't use - * separate compilation ... ensuring init/exit sections work to shrink - * the runtime footprint, and giving us at least some parts of what - * a "gcc --combine ... part1.c part2.c part3.c ... " build would. - */ +#include "composite.c" #include "usbstring.c" #include "config.c" #include "epautoconf.c" +#include "f_midi.c" /*-------------------------------------------------------------------------*/ - MODULE_AUTHOR("Ben Williamson"); MODULE_LICENSE("GPL v2"); -#define DRIVER_VERSION "25 Jul 2006" - static const char shortname[] = "g_midi"; static const char longname[] = "MIDI Gadget"; static int index = SNDRV_DEFAULT_IDX1; -static char *id = SNDRV_DEFAULT_STR1; - -module_param(index, int, 0444); +module_param(index, int, S_IRUGO); MODULE_PARM_DESC(index, "Index value for the USB MIDI Gadget adapter."); -module_param(id, charp, 0444); -MODULE_PARM_DESC(id, "ID string for the USB MIDI Gadget adapter."); - -/* Some systems will want different product identifiers published in the - * device descriptor, either numbers or strings or both. These string - * parameters are in UTF-8 (superset of ASCII's 7 bit characters). - */ - -static ushort idVendor; -module_param(idVendor, ushort, S_IRUGO); -MODULE_PARM_DESC(idVendor, "USB Vendor ID"); - -static ushort idProduct; -module_param(idProduct, ushort, S_IRUGO); -MODULE_PARM_DESC(idProduct, "USB Product ID"); - -static ushort bcdDevice; -module_param(bcdDevice, ushort, S_IRUGO); -MODULE_PARM_DESC(bcdDevice, "USB Device version (BCD)"); - -static char *iManufacturer; -module_param(iManufacturer, charp, S_IRUGO); -MODULE_PARM_DESC(iManufacturer, "USB Manufacturer string"); - -static char *iProduct; -module_param(iProduct, charp, S_IRUGO); -MODULE_PARM_DESC(iProduct, "USB Product string"); - -static char *iSerialNumber; -module_param(iSerialNumber, charp, S_IRUGO); -MODULE_PARM_DESC(iSerialNumber, "SerialNumber"); - -/* - * this version autoconfigures as much as possible, - * which is reasonable for most "bulk-only" drivers. - */ -static const char *EP_IN_NAME; -static const char *EP_OUT_NAME; +static char *id = SNDRV_DEFAULT_STR1; +module_param(id, charp, S_IRUGO); +MODULE_PARM_DESC(id, "ID string for the USB MIDI Gadget adapter."); -/* big enough to hold our biggest descriptor */ -#define USB_BUFSIZ 256 - - -/* This is a gadget, and the IN/OUT naming is from the host's perspective. - USB -> OUT endpoint -> rawmidi - USB <- IN endpoint <- rawmidi */ -struct gmidi_in_port { - struct gmidi_device* dev; - int active; - uint8_t cable; /* cable number << 4 */ - uint8_t state; -#define STATE_UNKNOWN 0 -#define STATE_1PARAM 1 -#define STATE_2PARAM_1 2 -#define STATE_2PARAM_2 3 -#define STATE_SYSEX_0 4 -#define STATE_SYSEX_1 5 -#define STATE_SYSEX_2 6 - uint8_t data[2]; -}; - -struct gmidi_device { - spinlock_t lock; - struct usb_gadget *gadget; - struct usb_request *req; /* for control responses */ - u8 config; - struct usb_ep *in_ep, *out_ep; - struct snd_card *card; - struct snd_rawmidi *rmidi; - struct snd_rawmidi_substream *in_substream; - struct snd_rawmidi_substream *out_substream; - - /* For the moment we only support one port in - each direction, but in_port is kept as a - separate struct so we can have more later. */ - struct gmidi_in_port in_port; - unsigned long out_triggered; - struct tasklet_struct tasklet; -}; - -static void gmidi_transmit(struct gmidi_device* dev, struct usb_request* req); - - -#define DBG(d, fmt, args...) \ - dev_dbg(&(d)->gadget->dev , fmt , ## args) -#define VDBG(d, fmt, args...) \ - dev_vdbg(&(d)->gadget->dev , fmt , ## args) -#define ERROR(d, fmt, args...) \ - dev_err(&(d)->gadget->dev , fmt , ## args) -#define INFO(d, fmt, args...) \ - dev_info(&(d)->gadget->dev , fmt , ## args) - - -static unsigned buflen = 256; -static unsigned qlen = 32; - +static unsigned int buflen = 256; module_param(buflen, uint, S_IRUGO); +MODULE_PARM_DESC(buflen, "MIDI buffer length"); + +static unsigned int qlen = 32; module_param(qlen, uint, S_IRUGO); +MODULE_PARM_DESC(qlen, "USB read request queue length"); +static unsigned int in_ports = 1; +module_param(in_ports, uint, S_IRUGO); +MODULE_PARM_DESC(in_ports, "Number of MIDI input ports"); + +static unsigned int out_ports = 1; +module_param(out_ports, uint, S_IRUGO); +MODULE_PARM_DESC(out_ports, "Number of MIDI output ports"); /* Thanks to Grey Innovation for donating this product ID. * @@ -173,1149 +82,124 @@ module_param(qlen, uint, S_IRUGO); #define DRIVER_VENDOR_NUM 0x17b3 /* Grey Innovation */ #define DRIVER_PRODUCT_NUM 0x0004 /* Linux-USB "MIDI Gadget" */ +/* string IDs are assigned dynamically */ -/* - * DESCRIPTORS ... most are static, but strings and (full) - * configuration descriptors are built on demand. - */ - -#define STRING_MANUFACTURER 25 -#define STRING_PRODUCT 42 -#define STRING_SERIAL 101 -#define STRING_MIDI_GADGET 250 +#define STRING_MANUFACTURER_IDX 0 +#define STRING_PRODUCT_IDX 1 +#define STRING_DESCRIPTION_IDX 2 -/* We only have the one configuration, it's number 1. */ -#define GMIDI_CONFIG 1 - -/* We have two interfaces- AudioControl and MIDIStreaming */ -#define GMIDI_AC_INTERFACE 0 -#define GMIDI_MS_INTERFACE 1 -#define GMIDI_NUM_INTERFACES 2 - -DECLARE_UAC_AC_HEADER_DESCRIPTOR(1); -DECLARE_USB_MIDI_OUT_JACK_DESCRIPTOR(1); -DECLARE_USB_MS_ENDPOINT_DESCRIPTOR(1); - -/* B.1 Device Descriptor */ static struct usb_device_descriptor device_desc = { .bLength = USB_DT_DEVICE_SIZE, .bDescriptorType = USB_DT_DEVICE, - .bcdUSB = cpu_to_le16(0x0200), + .bcdUSB = __constant_cpu_to_le16(0x0200), .bDeviceClass = USB_CLASS_PER_INTERFACE, - .idVendor = cpu_to_le16(DRIVER_VENDOR_NUM), - .idProduct = cpu_to_le16(DRIVER_PRODUCT_NUM), - .iManufacturer = STRING_MANUFACTURER, - .iProduct = STRING_PRODUCT, + .idVendor = __constant_cpu_to_le16(DRIVER_VENDOR_NUM), + .idProduct = __constant_cpu_to_le16(DRIVER_PRODUCT_NUM), + /* .iManufacturer = DYNAMIC */ + /* .iProduct = DYNAMIC */ .bNumConfigurations = 1, }; -/* B.2 Configuration Descriptor */ -static struct usb_config_descriptor config_desc = { - .bLength = USB_DT_CONFIG_SIZE, - .bDescriptorType = USB_DT_CONFIG, - /* compute wTotalLength on the fly */ - .bNumInterfaces = GMIDI_NUM_INTERFACES, - .bConfigurationValue = GMIDI_CONFIG, - .iConfiguration = STRING_MIDI_GADGET, - /* - * FIXME: When embedding this driver in a device, - * these need to be set to reflect the actual - * power properties of the device. Is it selfpowered? - */ - .bmAttributes = USB_CONFIG_ATT_ONE, - .bMaxPower = CONFIG_USB_GADGET_VBUS_DRAW / 2, -}; - -/* B.3.1 Standard AC Interface Descriptor */ -static const struct usb_interface_descriptor ac_interface_desc = { - .bLength = USB_DT_INTERFACE_SIZE, - .bDescriptorType = USB_DT_INTERFACE, - .bInterfaceNumber = GMIDI_AC_INTERFACE, - .bNumEndpoints = 0, - .bInterfaceClass = USB_CLASS_AUDIO, - .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL, - .iInterface = STRING_MIDI_GADGET, -}; - -/* B.3.2 Class-Specific AC Interface Descriptor */ -static const struct uac1_ac_header_descriptor_1 ac_header_desc = { - .bLength = UAC_DT_AC_HEADER_SIZE(1), - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubtype = USB_MS_HEADER, - .bcdADC = cpu_to_le16(0x0100), - .wTotalLength = cpu_to_le16(UAC_DT_AC_HEADER_SIZE(1)), - .bInCollection = 1, - .baInterfaceNr = { - [0] = GMIDI_MS_INTERFACE, - } -}; - -/* B.4.1 Standard MS Interface Descriptor */ -static const struct usb_interface_descriptor ms_interface_desc = { - .bLength = USB_DT_INTERFACE_SIZE, - .bDescriptorType = USB_DT_INTERFACE, - .bInterfaceNumber = GMIDI_MS_INTERFACE, - .bNumEndpoints = 2, - .bInterfaceClass = USB_CLASS_AUDIO, - .bInterfaceSubClass = USB_SUBCLASS_MIDISTREAMING, - .iInterface = STRING_MIDI_GADGET, +static struct usb_string strings_dev[] = { + [STRING_MANUFACTURER_IDX].s = "Grey Innovation", + [STRING_PRODUCT_IDX].s = "MIDI Gadget", + [STRING_DESCRIPTION_IDX].s = "MIDI", + { } /* end of list */ }; -/* B.4.2 Class-Specific MS Interface Descriptor */ -static const struct usb_ms_header_descriptor ms_header_desc = { - .bLength = USB_DT_MS_HEADER_SIZE, - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubtype = USB_MS_HEADER, - .bcdMSC = cpu_to_le16(0x0100), - .wTotalLength = cpu_to_le16(USB_DT_MS_HEADER_SIZE - + 2*USB_DT_MIDI_IN_SIZE - + 2*USB_DT_MIDI_OUT_SIZE(1)), -}; - -#define JACK_IN_EMB 1 -#define JACK_IN_EXT 2 -#define JACK_OUT_EMB 3 -#define JACK_OUT_EXT 4 - -/* B.4.3 MIDI IN Jack Descriptors */ -static const struct usb_midi_in_jack_descriptor jack_in_emb_desc = { - .bLength = USB_DT_MIDI_IN_SIZE, - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubtype = USB_MS_MIDI_IN_JACK, - .bJackType = USB_MS_EMBEDDED, - .bJackID = JACK_IN_EMB, -}; - -static const struct usb_midi_in_jack_descriptor jack_in_ext_desc = { - .bLength = USB_DT_MIDI_IN_SIZE, - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubtype = USB_MS_MIDI_IN_JACK, - .bJackType = USB_MS_EXTERNAL, - .bJackID = JACK_IN_EXT, -}; - -/* B.4.4 MIDI OUT Jack Descriptors */ -static const struct usb_midi_out_jack_descriptor_1 jack_out_emb_desc = { - .bLength = USB_DT_MIDI_OUT_SIZE(1), - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubtype = USB_MS_MIDI_OUT_JACK, - .bJackType = USB_MS_EMBEDDED, - .bJackID = JACK_OUT_EMB, - .bNrInputPins = 1, - .pins = { - [0] = { - .baSourceID = JACK_IN_EXT, - .baSourcePin = 1, - } - } -}; - -static const struct usb_midi_out_jack_descriptor_1 jack_out_ext_desc = { - .bLength = USB_DT_MIDI_OUT_SIZE(1), - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubtype = USB_MS_MIDI_OUT_JACK, - .bJackType = USB_MS_EXTERNAL, - .bJackID = JACK_OUT_EXT, - .bNrInputPins = 1, - .pins = { - [0] = { - .baSourceID = JACK_IN_EMB, - .baSourcePin = 1, - } - } -}; - -/* B.5.1 Standard Bulk OUT Endpoint Descriptor */ -static struct usb_endpoint_descriptor bulk_out_desc = { - .bLength = USB_DT_ENDPOINT_AUDIO_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = USB_DIR_OUT, - .bmAttributes = USB_ENDPOINT_XFER_BULK, -}; - -/* B.5.2 Class-specific MS Bulk OUT Endpoint Descriptor */ -static const struct usb_ms_endpoint_descriptor_1 ms_out_desc = { - .bLength = USB_DT_MS_ENDPOINT_SIZE(1), - .bDescriptorType = USB_DT_CS_ENDPOINT, - .bDescriptorSubtype = USB_MS_GENERAL, - .bNumEmbMIDIJack = 1, - .baAssocJackID = { - [0] = JACK_IN_EMB, - } -}; - -/* B.6.1 Standard Bulk IN Endpoint Descriptor */ -static struct usb_endpoint_descriptor bulk_in_desc = { - .bLength = USB_DT_ENDPOINT_AUDIO_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = USB_DIR_IN, - .bmAttributes = USB_ENDPOINT_XFER_BULK, -}; - -/* B.6.2 Class-specific MS Bulk IN Endpoint Descriptor */ -static const struct usb_ms_endpoint_descriptor_1 ms_in_desc = { - .bLength = USB_DT_MS_ENDPOINT_SIZE(1), - .bDescriptorType = USB_DT_CS_ENDPOINT, - .bDescriptorSubtype = USB_MS_GENERAL, - .bNumEmbMIDIJack = 1, - .baAssocJackID = { - [0] = JACK_OUT_EMB, - } -}; - -static const struct usb_descriptor_header *gmidi_function [] = { - (struct usb_descriptor_header *)&ac_interface_desc, - (struct usb_descriptor_header *)&ac_header_desc, - (struct usb_descriptor_header *)&ms_interface_desc, - - (struct usb_descriptor_header *)&ms_header_desc, - (struct usb_descriptor_header *)&jack_in_emb_desc, - (struct usb_descriptor_header *)&jack_in_ext_desc, - (struct usb_descriptor_header *)&jack_out_emb_desc, - (struct usb_descriptor_header *)&jack_out_ext_desc, - /* If you add more jacks, update ms_header_desc.wTotalLength */ - - (struct usb_descriptor_header *)&bulk_out_desc, - (struct usb_descriptor_header *)&ms_out_desc, - (struct usb_descriptor_header *)&bulk_in_desc, - (struct usb_descriptor_header *)&ms_in_desc, - NULL, -}; - -static char manufacturer[50]; -static char product_desc[40] = "MIDI Gadget"; -static char serial_number[20]; - -/* static strings, in UTF-8 */ -static struct usb_string strings [] = { - { STRING_MANUFACTURER, manufacturer, }, - { STRING_PRODUCT, product_desc, }, - { STRING_SERIAL, serial_number, }, - { STRING_MIDI_GADGET, longname, }, - { } /* end of list */ -}; - -static struct usb_gadget_strings stringtab = { +static struct usb_gadget_strings stringtab_dev = { .language = 0x0409, /* en-us */ - .strings = strings, + .strings = strings_dev, }; -static int config_buf(struct usb_gadget *gadget, - u8 *buf, u8 type, unsigned index) -{ - int len; - - /* only one configuration */ - if (index != 0) { - return -EINVAL; - } - len = usb_gadget_config_buf(&config_desc, - buf, USB_BUFSIZ, gmidi_function); - if (len < 0) { - return len; - } - ((struct usb_config_descriptor *)buf)->bDescriptorType = type; - return len; -} - -static struct usb_request *alloc_ep_req(struct usb_ep *ep, unsigned length) -{ - struct usb_request *req; - - req = usb_ep_alloc_request(ep, GFP_ATOMIC); - if (req) { - req->length = length; - req->buf = kmalloc(length, GFP_ATOMIC); - if (!req->buf) { - usb_ep_free_request(ep, req); - req = NULL; - } - } - return req; -} - -static void free_ep_req(struct usb_ep *ep, struct usb_request *req) -{ - kfree(req->buf); - usb_ep_free_request(ep, req); -} - -static const uint8_t gmidi_cin_length[] = { - 0, 0, 2, 3, 3, 1, 2, 3, 3, 3, 3, 3, 2, 2, 3, 1 +static struct usb_gadget_strings *dev_strings[] = { + &stringtab_dev, + NULL, }; -/* - * Receives a chunk of MIDI data. - */ -static void gmidi_read_data(struct usb_ep *ep, int cable, - uint8_t *data, int length) -{ - struct gmidi_device *dev = ep->driver_data; - /* cable is ignored, because for now we only have one. */ - - if (!dev->out_substream) { - /* Nobody is listening - throw it on the floor. */ - return; - } - if (!test_bit(dev->out_substream->number, &dev->out_triggered)) { - return; - } - snd_rawmidi_receive(dev->out_substream, data, length); -} - -static void gmidi_handle_out_data(struct usb_ep *ep, struct usb_request *req) -{ - unsigned i; - u8 *buf = req->buf; - - for (i = 0; i + 3 < req->actual; i += 4) { - if (buf[i] != 0) { - int cable = buf[i] >> 4; - int length = gmidi_cin_length[buf[i] & 0x0f]; - gmidi_read_data(ep, cable, &buf[i + 1], length); - } - } -} - -static void gmidi_complete(struct usb_ep *ep, struct usb_request *req) -{ - struct gmidi_device *dev = ep->driver_data; - int status = req->status; - - switch (status) { - case 0: /* normal completion */ - if (ep == dev->out_ep) { - /* we received stuff. - req is queued again, below */ - gmidi_handle_out_data(ep, req); - } else if (ep == dev->in_ep) { - /* our transmit completed. - see if there's more to go. - gmidi_transmit eats req, don't queue it again. */ - gmidi_transmit(dev, req); - return; - } - break; - - /* this endpoint is normally active while we're configured */ - case -ECONNABORTED: /* hardware forced ep reset */ - case -ECONNRESET: /* request dequeued */ - case -ESHUTDOWN: /* disconnect from host */ - VDBG(dev, "%s gone (%d), %d/%d\n", ep->name, status, - req->actual, req->length); - if (ep == dev->out_ep) { - gmidi_handle_out_data(ep, req); - } - free_ep_req(ep, req); - return; - - case -EOVERFLOW: /* buffer overrun on read means that - * we didn't provide a big enough - * buffer. - */ - default: - DBG(dev, "%s complete --> %d, %d/%d\n", ep->name, - status, req->actual, req->length); - break; - case -EREMOTEIO: /* short read */ - break; - } - - status = usb_ep_queue(ep, req, GFP_ATOMIC); - if (status) { - ERROR(dev, "kill %s: resubmit %d bytes --> %d\n", - ep->name, req->length, status); - usb_ep_set_halt(ep); - /* FIXME recover later ... somehow */ - } -} - -static int set_gmidi_config(struct gmidi_device *dev, gfp_t gfp_flags) -{ - int err = 0; - struct usb_request *req; - struct usb_ep *ep; - unsigned i; - - dev->in_ep->desc = &bulk_in_desc; - err = usb_ep_enable(dev->in_ep); - if (err) { - ERROR(dev, "can't start %s: %d\n", dev->in_ep->name, err); - goto fail; - } - dev->in_ep->driver_data = dev; - - dev->out_ep->desc = &bulk_out_desc; - err = usb_ep_enable(dev->out_ep); - if (err) { - ERROR(dev, "can't start %s: %d\n", dev->out_ep->name, err); - goto fail; - } - dev->out_ep->driver_data = dev; - - /* allocate a bunch of read buffers and queue them all at once. */ - ep = dev->out_ep; - for (i = 0; i < qlen && err == 0; i++) { - req = alloc_ep_req(ep, buflen); - if (req) { - req->complete = gmidi_complete; - err = usb_ep_queue(ep, req, GFP_ATOMIC); - if (err) { - DBG(dev, "%s queue req: %d\n", ep->name, err); - } - } else { - err = -ENOMEM; - } - } -fail: - /* caller is responsible for cleanup on error */ - return err; -} - - -static void gmidi_reset_config(struct gmidi_device *dev) -{ - if (dev->config == 0) { - return; - } - - DBG(dev, "reset config\n"); - - /* just disable endpoints, forcing completion of pending i/o. - * all our completion handlers free their requests in this case. - */ - usb_ep_disable(dev->in_ep); - usb_ep_disable(dev->out_ep); - dev->config = 0; -} - -/* change our operational config. this code must agree with the code - * that returns config descriptors, and altsetting code. - * - * it's also responsible for power management interactions. some - * configurations might not work with our current power sources. - * - * note that some device controller hardware will constrain what this - * code can do, perhaps by disallowing more than one configuration or - * by limiting configuration choices (like the pxa2xx). - */ -static int -gmidi_set_config(struct gmidi_device *dev, unsigned number, gfp_t gfp_flags) -{ - int result = 0; - struct usb_gadget *gadget = dev->gadget; - -#if 0 - /* FIXME */ - /* Hacking this bit out fixes a bug where on receipt of two - USB_REQ_SET_CONFIGURATION messages, we end up with no - buffered OUT requests waiting for data. This is clearly - hiding a bug elsewhere, because if the config didn't - change then we really shouldn't do anything. */ - /* Having said that, when we do "change" from config 1 - to config 1, we at least gmidi_reset_config() which - clears out any requests on endpoints, so it's not like - we leak or anything. */ - if (number == dev->config) { - return 0; - } -#endif - - gmidi_reset_config(dev); - - switch (number) { - case GMIDI_CONFIG: - result = set_gmidi_config(dev, gfp_flags); - break; - default: - result = -EINVAL; - /* FALL THROUGH */ - case 0: - return result; - } - - if (!result && (!dev->in_ep || !dev->out_ep)) { - result = -ENODEV; - } - if (result) { - gmidi_reset_config(dev); - } else { - char *speed; - - switch (gadget->speed) { - case USB_SPEED_LOW: speed = "low"; break; - case USB_SPEED_FULL: speed = "full"; break; - case USB_SPEED_HIGH: speed = "high"; break; - default: speed = "?"; break; - } - - dev->config = number; - INFO(dev, "%s speed\n", speed); - } - return result; -} - - -static void gmidi_setup_complete(struct usb_ep *ep, struct usb_request *req) -{ - if (req->status || req->actual != req->length) { - DBG((struct gmidi_device *) ep->driver_data, - "setup complete --> %d, %d/%d\n", - req->status, req->actual, req->length); - } -} - -/* - * The setup() callback implements all the ep0 functionality that's - * not handled lower down, in hardware or the hardware driver (like - * device and endpoint feature flags, and their status). It's all - * housekeeping for the gadget function we're implementing. Most of - * the work is in config-specific setup. - */ -static int gmidi_setup(struct usb_gadget *gadget, - const struct usb_ctrlrequest *ctrl) -{ - struct gmidi_device *dev = get_gadget_data(gadget); - struct usb_request *req = dev->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); - - /* usually this stores reply data in the pre-allocated ep0 buffer, - * but config change events will reconfigure hardware. - */ - req->zero = 0; - switch (ctrl->bRequest) { - - case USB_REQ_GET_DESCRIPTOR: - if (ctrl->bRequestType != USB_DIR_IN) { - goto unknown; - } - switch (w_value >> 8) { - - case USB_DT_DEVICE: - device_desc.bMaxPacketSize0 = gadget->ep0->maxpacket; - value = min(w_length, (u16) sizeof(device_desc)); - memcpy(req->buf, &device_desc, value); - break; - case USB_DT_CONFIG: - value = config_buf(gadget, req->buf, - w_value >> 8, - w_value & 0xff); - if (value >= 0) { - value = min(w_length, (u16)value); - } - break; - - case USB_DT_STRING: - /* wIndex == language code. - * this driver only handles one language, you can - * add string tables for other languages, using - * any UTF-8 characters - */ - value = usb_gadget_get_string(&stringtab, - w_value & 0xff, req->buf); - if (value >= 0) { - value = min(w_length, (u16)value); - } - break; - } - break; - - /* currently two configs, two speeds */ - case USB_REQ_SET_CONFIGURATION: - if (ctrl->bRequestType != 0) { - goto unknown; - } - if (gadget->a_hnp_support) { - DBG(dev, "HNP available\n"); - } else if (gadget->a_alt_hnp_support) { - DBG(dev, "HNP needs a different root port\n"); - } else { - VDBG(dev, "HNP inactive\n"); - } - spin_lock(&dev->lock); - value = gmidi_set_config(dev, w_value, GFP_ATOMIC); - spin_unlock(&dev->lock); - break; - case USB_REQ_GET_CONFIGURATION: - if (ctrl->bRequestType != USB_DIR_IN) { - goto unknown; - } - *(u8 *)req->buf = dev->config; - value = min(w_length, (u16)1); - break; - - /* until we add altsetting support, or other interfaces, - * only 0/0 are possible. pxa2xx only supports 0/0 (poorly) - * and already killed pending endpoint I/O. - */ - case USB_REQ_SET_INTERFACE: - if (ctrl->bRequestType != USB_RECIP_INTERFACE) { - goto unknown; - } - spin_lock(&dev->lock); - if (dev->config && w_index < GMIDI_NUM_INTERFACES - && w_value == 0) - { - u8 config = dev->config; - - /* resets interface configuration, forgets about - * previous transaction state (queued bufs, etc) - * and re-inits endpoint state (toggle etc) - * no response queued, just zero status == success. - * if we had more than one interface we couldn't - * use this "reset the config" shortcut. - */ - gmidi_reset_config(dev); - gmidi_set_config(dev, config, GFP_ATOMIC); - value = 0; - } - spin_unlock(&dev->lock); - break; - case USB_REQ_GET_INTERFACE: - if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE)) { - goto unknown; - } - if (!dev->config) { - break; - } - if (w_index >= GMIDI_NUM_INTERFACES) { - value = -EDOM; - break; - } - *(u8 *)req->buf = 0; - value = min(w_length, (u16)1); - break; - - default: -unknown: - VDBG(dev, "unknown 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 before status phase? */ - if (value >= 0) { - req->length = value; - req->zero = value < w_length; - value = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC); - if (value < 0) { - DBG(dev, "ep_queue --> %d\n", value); - req->status = 0; - gmidi_setup_complete(gadget->ep0, req); - } - } - - /* device either stalls (value < 0) or reports success */ - return value; -} - -static void gmidi_disconnect(struct usb_gadget *gadget) -{ - struct gmidi_device *dev = get_gadget_data(gadget); - unsigned long flags; - - spin_lock_irqsave(&dev->lock, flags); - gmidi_reset_config(dev); - - /* a more significant application might have some non-usb - * activities to quiesce here, saving resources like power - * or pushing the notification up a network stack. - */ - spin_unlock_irqrestore(&dev->lock, flags); - - /* next we may get setup() calls to enumerate new connections; - * or an unbind() during shutdown (including removing module). - */ -} - -static void /* __init_or_exit */ gmidi_unbind(struct usb_gadget *gadget) -{ - struct gmidi_device *dev = get_gadget_data(gadget); - struct snd_card *card; - - DBG(dev, "unbind\n"); - - card = dev->card; - dev->card = NULL; - if (card) { - snd_card_free(card); - } - - /* we've already been disconnected ... no i/o is active */ - if (dev->req) { - dev->req->length = USB_BUFSIZ; - free_ep_req(gadget->ep0, dev->req); - } - kfree(dev); - set_gadget_data(gadget, NULL); -} - -static int gmidi_snd_free(struct snd_device *device) +static int __exit midi_unbind(struct usb_composite_dev *dev) { return 0; } -static void gmidi_transmit_packet(struct usb_request *req, uint8_t p0, - uint8_t p1, uint8_t p2, uint8_t p3) -{ - unsigned length = req->length; - u8 *buf = (u8 *)req->buf + length; - - buf[0] = p0; - buf[1] = p1; - buf[2] = p2; - buf[3] = p3; - req->length = length + 4; -} - -/* - * Converts MIDI commands to USB MIDI packets. - */ -static void gmidi_transmit_byte(struct usb_request *req, - struct gmidi_in_port *port, uint8_t b) -{ - uint8_t p0 = port->cable; - - if (b >= 0xf8) { - gmidi_transmit_packet(req, p0 | 0x0f, b, 0, 0); - } else if (b >= 0xf0) { - switch (b) { - case 0xf0: - port->data[0] = b; - port->state = STATE_SYSEX_1; - break; - case 0xf1: - case 0xf3: - port->data[0] = b; - port->state = STATE_1PARAM; - break; - case 0xf2: - port->data[0] = b; - port->state = STATE_2PARAM_1; - break; - case 0xf4: - case 0xf5: - port->state = STATE_UNKNOWN; - break; - case 0xf6: - gmidi_transmit_packet(req, p0 | 0x05, 0xf6, 0, 0); - port->state = STATE_UNKNOWN; - break; - case 0xf7: - switch (port->state) { - case STATE_SYSEX_0: - gmidi_transmit_packet(req, - p0 | 0x05, 0xf7, 0, 0); - break; - case STATE_SYSEX_1: - gmidi_transmit_packet(req, - p0 | 0x06, port->data[0], 0xf7, 0); - break; - case STATE_SYSEX_2: - gmidi_transmit_packet(req, - p0 | 0x07, port->data[0], - port->data[1], 0xf7); - break; - } - port->state = STATE_UNKNOWN; - break; - } - } else if (b >= 0x80) { - port->data[0] = b; - if (b >= 0xc0 && b <= 0xdf) - port->state = STATE_1PARAM; - else - port->state = STATE_2PARAM_1; - } else { /* b < 0x80 */ - switch (port->state) { - case STATE_1PARAM: - if (port->data[0] < 0xf0) { - p0 |= port->data[0] >> 4; - } else { - p0 |= 0x02; - port->state = STATE_UNKNOWN; - } - gmidi_transmit_packet(req, p0, port->data[0], b, 0); - break; - case STATE_2PARAM_1: - port->data[1] = b; - port->state = STATE_2PARAM_2; - break; - case STATE_2PARAM_2: - if (port->data[0] < 0xf0) { - p0 |= port->data[0] >> 4; - port->state = STATE_2PARAM_1; - } else { - p0 |= 0x03; - port->state = STATE_UNKNOWN; - } - gmidi_transmit_packet(req, - p0, port->data[0], port->data[1], b); - break; - case STATE_SYSEX_0: - port->data[0] = b; - port->state = STATE_SYSEX_1; - break; - case STATE_SYSEX_1: - port->data[1] = b; - port->state = STATE_SYSEX_2; - break; - case STATE_SYSEX_2: - gmidi_transmit_packet(req, - p0 | 0x04, port->data[0], port->data[1], b); - port->state = STATE_SYSEX_0; - break; - } - } -} - -static void gmidi_transmit(struct gmidi_device *dev, struct usb_request *req) -{ - struct usb_ep *ep = dev->in_ep; - struct gmidi_in_port *port = &dev->in_port; - - if (!ep) { - return; - } - if (!req) { - req = alloc_ep_req(ep, buflen); - } - if (!req) { - ERROR(dev, "gmidi_transmit: alloc_ep_request failed\n"); - return; - } - req->length = 0; - req->complete = gmidi_complete; - - if (port->active) { - while (req->length + 3 < buflen) { - uint8_t b; - if (snd_rawmidi_transmit(dev->in_substream, &b, 1) - != 1) - { - port->active = 0; - break; - } - gmidi_transmit_byte(req, port, b); - } - } - if (req->length > 0) { - usb_ep_queue(ep, req, GFP_ATOMIC); - } else { - free_ep_req(ep, req); - } -} - -static void gmidi_in_tasklet(unsigned long data) -{ - struct gmidi_device *dev = (struct gmidi_device *)data; - - gmidi_transmit(dev, NULL); -} - -static int gmidi_in_open(struct snd_rawmidi_substream *substream) -{ - struct gmidi_device *dev = substream->rmidi->private_data; - - VDBG(dev, "gmidi_in_open\n"); - dev->in_substream = substream; - dev->in_port.state = STATE_UNKNOWN; - return 0; -} - -static int gmidi_in_close(struct snd_rawmidi_substream *substream) -{ - struct gmidi_device *dev = substream->rmidi->private_data; - - VDBG(dev, "gmidi_in_close\n"); - return 0; -} - -static void gmidi_in_trigger(struct snd_rawmidi_substream *substream, int up) -{ - struct gmidi_device *dev = substream->rmidi->private_data; - - VDBG(dev, "gmidi_in_trigger %d\n", up); - dev->in_port.active = up; - if (up) { - tasklet_hi_schedule(&dev->tasklet); - } -} - -static int gmidi_out_open(struct snd_rawmidi_substream *substream) -{ - struct gmidi_device *dev = substream->rmidi->private_data; - - VDBG(dev, "gmidi_out_open\n"); - dev->out_substream = substream; - return 0; -} - -static int gmidi_out_close(struct snd_rawmidi_substream *substream) -{ - struct gmidi_device *dev = substream->rmidi->private_data; - - VDBG(dev, "gmidi_out_close\n"); - return 0; -} - -static void gmidi_out_trigger(struct snd_rawmidi_substream *substream, int up) -{ - struct gmidi_device *dev = substream->rmidi->private_data; - - VDBG(dev, "gmidi_out_trigger %d\n", up); - if (up) { - set_bit(substream->number, &dev->out_triggered); - } else { - clear_bit(substream->number, &dev->out_triggered); - } -} - -static struct snd_rawmidi_ops gmidi_in_ops = { - .open = gmidi_in_open, - .close = gmidi_in_close, - .trigger = gmidi_in_trigger, +static struct usb_configuration midi_config = { + .label = "MIDI Gadget", + .bConfigurationValue = 1, + /* .iConfiguration = DYNAMIC */ + .bmAttributes = USB_CONFIG_ATT_ONE, + .bMaxPower = CONFIG_USB_GADGET_VBUS_DRAW / 2, }; -static struct snd_rawmidi_ops gmidi_out_ops = { - .open = gmidi_out_open, - .close = gmidi_out_close, - .trigger = gmidi_out_trigger -}; - -/* register as a sound "card" */ -static int gmidi_register_card(struct gmidi_device *dev) +static int __init midi_bind_config(struct usb_configuration *c) { - struct snd_card *card; - struct snd_rawmidi *rmidi; - int err; - int out_ports = 1; - int in_ports = 1; - static struct snd_device_ops ops = { - .dev_free = gmidi_snd_free, - }; - - err = snd_card_create(index, id, THIS_MODULE, 0, &card); - if (err < 0) { - ERROR(dev, "snd_card_create failed\n"); - goto fail; - } - dev->card = card; - - err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, dev, &ops); - if (err < 0) { - ERROR(dev, "snd_device_new failed: error %d\n", err); - goto fail; - } - - strcpy(card->driver, longname); - strcpy(card->longname, longname); - strcpy(card->shortname, shortname); - - /* Set up rawmidi */ - dev->in_port.dev = dev; - dev->in_port.active = 0; - snd_component_add(card, "MIDI"); - err = snd_rawmidi_new(card, "USB MIDI Gadget", 0, - out_ports, in_ports, &rmidi); - if (err < 0) { - ERROR(dev, "snd_rawmidi_new failed: error %d\n", err); - goto fail; - } - dev->rmidi = rmidi; - strcpy(rmidi->name, card->shortname); - rmidi->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT | - SNDRV_RAWMIDI_INFO_INPUT | - SNDRV_RAWMIDI_INFO_DUPLEX; - rmidi->private_data = dev; - - /* Yes, rawmidi OUTPUT = USB IN, and rawmidi INPUT = USB OUT. - It's an upside-down world being a gadget. */ - snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &gmidi_in_ops); - snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &gmidi_out_ops); - - snd_card_set_dev(card, &dev->gadget->dev); - - /* register it - we're ready to go */ - err = snd_card_register(card); - if (err < 0) { - ERROR(dev, "snd_card_register failed\n"); - goto fail; - } - - VDBG(dev, "gmidi_register_card finished ok\n"); - return 0; - -fail: - if (dev->card) { - snd_card_free(dev->card); - dev->card = NULL; - } - return err; + return f_midi_bind_config(c, index, id, + in_ports, out_ports, + buflen, qlen); } -/* - * Creates an output endpoint, and initializes output ports. - */ -static int __init gmidi_bind(struct usb_gadget *gadget) +static int __init midi_bind(struct usb_composite_dev *cdev) { - struct gmidi_device *dev; - struct usb_ep *in_ep, *out_ep; - int gcnum, err = 0; + struct usb_gadget *gadget = cdev->gadget; + int gcnum, status; - /* support optional vendor/distro customization */ - if (idVendor) { - if (!idProduct) { - pr_err("idVendor needs idProduct!\n"); - return -ENODEV; - } - device_desc.idVendor = cpu_to_le16(idVendor); - device_desc.idProduct = cpu_to_le16(idProduct); - if (bcdDevice) { - device_desc.bcdDevice = cpu_to_le16(bcdDevice); - } - } - if (iManufacturer) { - strlcpy(manufacturer, iManufacturer, sizeof(manufacturer)); - } else { - snprintf(manufacturer, sizeof(manufacturer), "%s %s with %s", - init_utsname()->sysname, init_utsname()->release, - gadget->name); - } - if (iProduct) { - strlcpy(product_desc, iProduct, sizeof(product_desc)); - } - if (iSerialNumber) { - device_desc.iSerialNumber = STRING_SERIAL, - strlcpy(serial_number, iSerialNumber, sizeof(serial_number)); - } + status = usb_string_id(cdev); + if (status < 0) + return status; + strings_dev[STRING_MANUFACTURER_IDX].id = status; + device_desc.iManufacturer = status; - /* Bulk-only drivers like this one SHOULD be able to - * autoconfigure on any sane usb controller driver, - * but there may also be important quirks to address. - */ - usb_ep_autoconfig_reset(gadget); - in_ep = usb_ep_autoconfig(gadget, &bulk_in_desc); - if (!in_ep) { -autoconf_fail: - pr_err("%s: can't autoconfigure on %s\n", - shortname, gadget->name); - return -ENODEV; - } - EP_IN_NAME = in_ep->name; - in_ep->driver_data = in_ep; /* claim */ + status = usb_string_id(cdev); + if (status < 0) + return status; + strings_dev[STRING_PRODUCT_IDX].id = status; + device_desc.iProduct = status; - out_ep = usb_ep_autoconfig(gadget, &bulk_out_desc); - if (!out_ep) { - goto autoconf_fail; - } - EP_OUT_NAME = out_ep->name; - out_ep->driver_data = out_ep; /* claim */ + /* config description */ + status = usb_string_id(cdev); + if (status < 0) + return status; + strings_dev[STRING_DESCRIPTION_IDX].id = status; + + midi_config.iConfiguration = status; gcnum = usb_gadget_controller_number(gadget); - if (gcnum >= 0) { - device_desc.bcdDevice = cpu_to_le16(0x0200 + gcnum); - } else { + if (gcnum < 0) { /* gmidi is so simple (no altsettings) that * it SHOULD NOT have problems with bulk-capable hardware. * so warn about unrecognized controllers, don't panic. */ pr_warning("%s: controller '%s' not recognized\n", - shortname, gadget->name); + __func__, gadget->name); device_desc.bcdDevice = cpu_to_le16(0x9999); + } else { + device_desc.bcdDevice = cpu_to_le16(0x0200 + gcnum); } + status = usb_add_config(cdev, &midi_config, midi_bind_config); + if (status < 0) + return status; - /* ok, we made sense of the hardware ... */ - dev = kzalloc(sizeof(*dev), GFP_KERNEL); - if (!dev) { - return -ENOMEM; - } - spin_lock_init(&dev->lock); - dev->gadget = gadget; - dev->in_ep = in_ep; - dev->out_ep = out_ep; - set_gadget_data(gadget, dev); - tasklet_init(&dev->tasklet, gmidi_in_tasklet, (unsigned long)dev); - - /* preallocate control response and buffer */ - dev->req = alloc_ep_req(gadget->ep0, USB_BUFSIZ); - if (!dev->req) { - err = -ENOMEM; - goto fail; - } - - dev->req->complete = gmidi_setup_complete; - - gadget->ep0->driver_data = dev; - - INFO(dev, "%s, version: " DRIVER_VERSION "\n", longname); - INFO(dev, "using %s, OUT %s IN %s\n", gadget->name, - EP_OUT_NAME, EP_IN_NAME); - - /* register as an ALSA sound card */ - err = gmidi_register_card(dev); - if (err < 0) { - goto fail; - } - - VDBG(dev, "gmidi_bind finished ok\n"); + pr_info("%s\n", longname); return 0; - -fail: - gmidi_unbind(gadget); - return err; -} - - -static void gmidi_suspend(struct usb_gadget *gadget) -{ - struct gmidi_device *dev = get_gadget_data(gadget); - - if (gadget->speed == USB_SPEED_UNKNOWN) { - return; - } - - DBG(dev, "suspend\n"); -} - -static void gmidi_resume(struct usb_gadget *gadget) -{ - struct gmidi_device *dev = get_gadget_data(gadget); - - DBG(dev, "resume\n"); } - -static struct usb_gadget_driver gmidi_driver = { - .speed = USB_SPEED_FULL, - .function = (char *)longname, - .unbind = gmidi_unbind, - - .setup = gmidi_setup, - .disconnect = gmidi_disconnect, - - .suspend = gmidi_suspend, - .resume = gmidi_resume, - - .driver = { - .name = (char *)shortname, - .owner = THIS_MODULE, - }, +static struct usb_composite_driver midi_driver = { + .name = (char *) longname, + .dev = &device_desc, + .strings = dev_strings, + .max_speed = USB_SPEED_HIGH, + .unbind = __exit_p(midi_unbind), }; -static int __init gmidi_init(void) +static int __init midi_init(void) { - return usb_gadget_probe_driver(&gmidi_driver, gmidi_bind); + return usb_composite_probe(&midi_driver, midi_bind); } -module_init(gmidi_init); +module_init(midi_init); -static void __exit gmidi_cleanup(void) +static void __exit midi_cleanup(void) { - usb_gadget_unregister_driver(&gmidi_driver); + usb_composite_unregister(&midi_driver); } -module_exit(gmidi_cleanup); +module_exit(midi_cleanup); diff --git a/drivers/usb/gadget/hid.c b/drivers/usb/gadget/hid.c index 9fb575034a0e..f888c3ede860 100644 --- a/drivers/usb/gadget/hid.c +++ b/drivers/usb/gadget/hid.c @@ -9,15 +9,6 @@ * 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 */ diff --git a/drivers/usb/gadget/imx_udc.c b/drivers/usb/gadget/imx_udc.c index 692fd9b2248b..2d978c0e7ced 100644 --- a/drivers/usb/gadget/imx_udc.c +++ b/drivers/usb/gadget/imx_udc.c @@ -689,7 +689,7 @@ static int imx_ep_enable(struct usb_ep *usb_ep, return -EINVAL; } - if (imx_ep->fifosize < le16_to_cpu(desc->wMaxPacketSize)) { + if (imx_ep->fifosize < usb_endpoint_maxp(desc)) { D_ERR(imx_usb->dev, "<%s> bad %s maxpacket\n", __func__, usb_ep->name); return -ERANGE; @@ -1478,7 +1478,7 @@ static int __init imx_udc_probe(struct platform_device *pdev) for (i = 0; i < IMX_USB_NB_EP + 1; i++) { ret = request_irq(imx_usb->usbd_int[i], intr_handler(i), - IRQF_DISABLED, driver_name, imx_usb); + 0, driver_name, imx_usb); if (ret) { dev_err(&pdev->dev, "can't get irq %i, err %d\n", imx_usb->usbd_int[i], ret); diff --git a/drivers/usb/gadget/imx_udc.h b/drivers/usb/gadget/imx_udc.h index 7136c242b4ec..d118fb777840 100644 --- a/drivers/usb/gadget/imx_udc.h +++ b/drivers/usb/gadget/imx_udc.h @@ -8,11 +8,6 @@ * 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. */ #ifndef __LINUX_USB_GADGET_IMX_H diff --git a/drivers/usb/gadget/inode.c b/drivers/usb/gadget/inode.c index 1b240990448f..a392ec0d2d51 100644 --- a/drivers/usb/gadget/inode.c +++ b/drivers/usb/gadget/inode.c @@ -8,15 +8,6 @@ * 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 */ diff --git a/drivers/usb/gadget/langwell_udc.c b/drivers/usb/gadget/langwell_udc.c index ff4d40d77c30..c9fa3bf5b377 100644 --- a/drivers/usb/gadget/langwell_udc.c +++ b/drivers/usb/gadget/langwell_udc.c @@ -5,16 +5,6 @@ * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope 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., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * */ @@ -60,9 +50,6 @@ static const char driver_name[] = "langwell_udc"; static const char driver_desc[] = DRIVER_DESC; -/* controller device global variable */ -static struct langwell_udc *the_controller; - /* for endpoint 0 operations */ static const struct usb_endpoint_descriptor langwell_ep0_desc = { @@ -283,7 +270,7 @@ static int langwell_ep_enable(struct usb_ep *_ep, if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) return -ESHUTDOWN; - max = le16_to_cpu(desc->wMaxPacketSize); + max = usb_endpoint_maxp(desc); /* * disable HW zero length termination select @@ -1321,9 +1308,12 @@ static int langwell_pullup(struct usb_gadget *_gadget, int is_on) return 0; } -static int langwell_start(struct usb_gadget_driver *driver, - int (*bind)(struct usb_gadget *)); -static int langwell_stop(struct usb_gadget_driver *driver); +static int langwell_start(struct usb_gadget *g, + struct usb_gadget_driver *driver); + +static int langwell_stop(struct usb_gadget *g, + struct usb_gadget_driver *driver); + /* device controller usb_gadget_ops structure */ static const struct usb_gadget_ops langwell_ops = { @@ -1345,8 +1335,8 @@ static const struct usb_gadget_ops langwell_ops = { /* D+ pullup, software-controlled connect/disconnect to USB host */ .pullup = langwell_pullup, - .start = langwell_start, - .stop = langwell_stop, + .udc_start = langwell_start, + .udc_stop = langwell_stop, }; @@ -1561,7 +1551,7 @@ static void stop_activity(struct langwell_udc *dev, static ssize_t show_function(struct device *_dev, struct device_attribute *attr, char *buf) { - struct langwell_udc *dev = the_controller; + struct langwell_udc *dev = dev_get_drvdata(_dev); if (!dev->driver || !dev->driver->function || strlen(dev->driver->function) > PAGE_SIZE) @@ -1572,11 +1562,25 @@ static ssize_t show_function(struct device *_dev, static DEVICE_ATTR(function, S_IRUGO, show_function, NULL); +static inline enum usb_device_speed lpm_device_speed(u32 reg) +{ + switch (LPM_PSPD(reg)) { + case LPM_SPEED_HIGH: + return USB_SPEED_HIGH; + case LPM_SPEED_FULL: + return USB_SPEED_FULL; + case LPM_SPEED_LOW: + return USB_SPEED_LOW; + default: + return USB_SPEED_UNKNOWN; + } +} + /* device "langwell_udc" sysfs attribute file */ static ssize_t show_langwell_udc(struct device *_dev, struct device_attribute *attr, char *buf) { - struct langwell_udc *dev = the_controller; + struct langwell_udc *dev = dev_get_drvdata(_dev); struct langwell_request *req; struct langwell_ep *ep = NULL; char *next; @@ -1700,20 +1704,7 @@ static ssize_t show_langwell_udc(struct device *_dev, "BmAttributes: %d\n\n", LPM_PTS(tmp_reg), (tmp_reg & LPM_STS) ? 1 : 0, - ({ - char *s; - switch (LPM_PSPD(tmp_reg)) { - case LPM_SPEED_FULL: - s = "Full Speed"; break; - case LPM_SPEED_LOW: - s = "Low Speed"; break; - case LPM_SPEED_HIGH: - s = "High Speed"; break; - default: - s = "Unknown Speed"; break; - } - s; - }), + usb_speed_string(lpm_device_speed(tmp_reg)), (tmp_reg & LPM_PFSC) ? "Force Full Speed" : "Not Force", (tmp_reg & LPM_PHCD) ? "Disabled" : "Enabled", LPM_BA(tmp_reg)); @@ -1821,7 +1812,7 @@ static DEVICE_ATTR(langwell_udc, S_IRUGO, show_langwell_udc, NULL); static ssize_t store_remote_wakeup(struct device *_dev, struct device_attribute *attr, const char *buf, size_t count) { - struct langwell_udc *dev = the_controller; + struct langwell_udc *dev = dev_get_drvdata(_dev); unsigned long flags; ssize_t rc = count; @@ -1857,21 +1848,15 @@ static DEVICE_ATTR(remote_wakeup, S_IWUSR, NULL, store_remote_wakeup); * the driver might get unbound. */ -static int langwell_start(struct usb_gadget_driver *driver, - int (*bind)(struct usb_gadget *)) +static int langwell_start(struct usb_gadget *g, + struct usb_gadget_driver *driver) { - struct langwell_udc *dev = the_controller; + struct langwell_udc *dev = gadget_to_langwell(g); unsigned long flags; int retval; - if (!dev) - return -ENODEV; - dev_dbg(&dev->pdev->dev, "---> %s()\n", __func__); - if (dev->driver) - return -EBUSY; - spin_lock_irqsave(&dev->lock, flags); /* hook up the driver ... */ @@ -1881,18 +1866,9 @@ static int langwell_start(struct usb_gadget_driver *driver, spin_unlock_irqrestore(&dev->lock, flags); - retval = bind(&dev->gadget); - if (retval) { - dev_dbg(&dev->pdev->dev, "bind to driver %s --> %d\n", - driver->driver.name, retval); - dev->driver = NULL; - dev->gadget.dev.driver = NULL; - return retval; - } - retval = device_create_file(&dev->pdev->dev, &dev_attr_function); if (retval) - goto err_unbind; + goto err; dev->usb_state = USB_STATE_ATTACHED; dev->ep0_state = WAIT_FOR_SETUP; @@ -1909,31 +1885,27 @@ static int langwell_start(struct usb_gadget_driver *driver, dev_info(&dev->pdev->dev, "register driver: %s\n", driver->driver.name); dev_dbg(&dev->pdev->dev, "<--- %s()\n", __func__); + return 0; -err_unbind: - driver->unbind(&dev->gadget); +err: dev->gadget.dev.driver = NULL; dev->driver = NULL; dev_dbg(&dev->pdev->dev, "<--- %s()\n", __func__); + return retval; } /* unregister gadget driver */ -static int langwell_stop(struct usb_gadget_driver *driver) +static int langwell_stop(struct usb_gadget *g, + struct usb_gadget_driver *driver) { - struct langwell_udc *dev = the_controller; + struct langwell_udc *dev = gadget_to_langwell(g); unsigned long flags; - if (!dev) - return -ENODEV; - dev_dbg(&dev->pdev->dev, "---> %s()\n", __func__); - if (unlikely(!driver || !driver->unbind)) - return -EINVAL; - /* exit PHY low power suspend */ if (dev->pdev->device != 0x0829) langwell_phy_low_power(dev, 0); @@ -1956,8 +1928,6 @@ static int langwell_stop(struct usb_gadget_driver *driver) stop_activity(dev, driver); spin_unlock_irqrestore(&dev->lock, flags); - /* unbind gadget driver */ - driver->unbind(&dev->gadget); dev->gadget.dev.driver = NULL; dev->driver = NULL; @@ -1966,6 +1936,7 @@ static int langwell_stop(struct usb_gadget_driver *driver) dev_info(&dev->pdev->dev, "unregistered driver '%s'\n", driver->driver.name); dev_dbg(&dev->pdev->dev, "<--- %s()\n", __func__); + return 0; } @@ -2657,12 +2628,10 @@ done: dev_vdbg(&dev->pdev->dev, "<--- %s()\n", __func__); } - /* port change detect interrupt handler */ static void handle_port_change(struct langwell_udc *dev) { u32 portsc1, devlc; - u32 speed; dev_vdbg(&dev->pdev->dev, "---> %s()\n", __func__); @@ -2677,24 +2646,9 @@ static void handle_port_change(struct langwell_udc *dev) /* bus reset is finished */ if (!(portsc1 & PORTS_PR)) { /* get the speed */ - speed = LPM_PSPD(devlc); - switch (speed) { - case LPM_SPEED_HIGH: - dev->gadget.speed = USB_SPEED_HIGH; - break; - case LPM_SPEED_FULL: - dev->gadget.speed = USB_SPEED_FULL; - break; - case LPM_SPEED_LOW: - dev->gadget.speed = USB_SPEED_LOW; - break; - default: - dev->gadget.speed = USB_SPEED_UNKNOWN; - break; - } - dev_vdbg(&dev->pdev->dev, - "speed = %d, dev->gadget.speed = %d\n", - speed, dev->gadget.speed); + dev->gadget.speed = lpm_device_speed(devlc); + dev_vdbg(&dev->pdev->dev, "dev->gadget.speed = %d\n", + dev->gadget.speed); } /* LPM L0 to L1 */ @@ -2999,7 +2953,7 @@ static irqreturn_t langwell_irq(int irq, void *_dev) /* release device structure */ static void gadget_release(struct device *_dev) { - struct langwell_udc *dev = the_controller; + struct langwell_udc *dev = dev_get_drvdata(_dev); dev_dbg(&dev->pdev->dev, "---> %s()\n", __func__); @@ -3057,7 +3011,7 @@ static void sram_deinit(struct langwell_udc *dev) /* tear down the binding between this driver and the pci device */ static void langwell_udc_remove(struct pci_dev *pdev) { - struct langwell_udc *dev = the_controller; + struct langwell_udc *dev = pci_get_drvdata(pdev); DECLARE_COMPLETION(done); @@ -3124,8 +3078,6 @@ static void langwell_udc_remove(struct pci_dev *pdev) /* free dev, wait for the release() finished */ wait_for_completion(&done); - - the_controller = NULL; } @@ -3144,11 +3096,6 @@ static int langwell_udc_probe(struct pci_dev *pdev, size_t size; int retval; - if (the_controller) { - dev_warn(&pdev->dev, "ignoring\n"); - return -EBUSY; - } - /* alloc, and start init */ dev = kzalloc(sizeof *dev, GFP_KERNEL); if (dev == NULL) { @@ -3368,8 +3315,6 @@ static int langwell_udc_probe(struct pci_dev *pdev, "After langwell_udc_probe(), print all registers:\n"); print_all_registers(dev); - the_controller = dev; - retval = device_register(&dev->gadget.dev); if (retval) goto error; @@ -3404,7 +3349,7 @@ error: /* device controller suspend */ static int langwell_udc_suspend(struct pci_dev *pdev, pm_message_t state) { - struct langwell_udc *dev = the_controller; + struct langwell_udc *dev = pci_get_drvdata(pdev); dev_dbg(&dev->pdev->dev, "---> %s()\n", __func__); @@ -3452,7 +3397,7 @@ static int langwell_udc_suspend(struct pci_dev *pdev, pm_message_t state) /* device controller resume */ static int langwell_udc_resume(struct pci_dev *pdev) { - struct langwell_udc *dev = the_controller; + struct langwell_udc *dev = pci_get_drvdata(pdev); size_t size; dev_dbg(&dev->pdev->dev, "---> %s()\n", __func__); @@ -3534,7 +3479,7 @@ static int langwell_udc_resume(struct pci_dev *pdev) /* pci driver shutdown */ static void langwell_udc_shutdown(struct pci_dev *pdev) { - struct langwell_udc *dev = the_controller; + struct langwell_udc *dev = pci_get_drvdata(pdev); u32 usbmode; dev_dbg(&dev->pdev->dev, "---> %s()\n", __func__); diff --git a/drivers/usb/gadget/langwell_udc.h b/drivers/usb/gadget/langwell_udc.h index f1d9c1bb04f3..ef79e242b7b0 100644 --- a/drivers/usb/gadget/langwell_udc.h +++ b/drivers/usb/gadget/langwell_udc.h @@ -5,16 +5,6 @@ * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope 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., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <linux/usb/langwell_udc.h> @@ -231,3 +221,5 @@ struct langwell_udc { u16 dev_status; }; +#define gadget_to_langwell(g) container_of((g), struct langwell_udc, gadget) + diff --git a/drivers/usb/gadget/m66592-udc.c b/drivers/usb/gadget/m66592-udc.c index 491f825ed5c9..91d0af2a24a8 100644 --- a/drivers/usb/gadget/m66592-udc.c +++ b/drivers/usb/gadget/m66592-udc.c @@ -8,16 +8,6 @@ * 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; version 2 of the License. - * - * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * */ #include <linux/module.h> @@ -370,7 +360,7 @@ static void m66592_ep_setting(struct m66592 *m66592, struct m66592_ep *ep, ep->pipectr = get_pipectr_addr(pipenum); ep->pipenum = pipenum; - ep->ep.maxpacket = le16_to_cpu(desc->wMaxPacketSize); + ep->ep.maxpacket = usb_endpoint_maxp(desc); m66592->pipenum2ep[pipenum] = ep; m66592->epaddr2ep[desc->bEndpointAddress&USB_ENDPOINT_NUMBER_MASK] = ep; INIT_LIST_HEAD(&ep->queue); @@ -447,7 +437,7 @@ static int alloc_pipe_config(struct m66592_ep *ep, ep->type = info.type; info.epnum = desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; - info.maxpacket = le16_to_cpu(desc->wMaxPacketSize); + info.maxpacket = usb_endpoint_maxp(desc); info.interval = desc->bInterval; if (desc->bEndpointAddress & USB_ENDPOINT_DIR_MASK) info.dir_in = 1; @@ -1674,7 +1664,7 @@ static int __init m66592_probe(struct platform_device *pdev) m66592->timer.data = (unsigned long)m66592; m66592->reg = reg; - ret = request_irq(ires->start, m66592_irq, IRQF_DISABLED | IRQF_SHARED, + ret = request_irq(ires->start, m66592_irq, IRQF_SHARED, udc_name, m66592); if (ret < 0) { pr_err("request_irq error (%d)\n", ret); diff --git a/drivers/usb/gadget/m66592-udc.h b/drivers/usb/gadget/m66592-udc.h index 7b93d579af37..9d9f7e39f037 100644 --- a/drivers/usb/gadget/m66592-udc.h +++ b/drivers/usb/gadget/m66592-udc.h @@ -8,16 +8,6 @@ * 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; version 2 of the License. - * - * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * */ #ifndef __M66592_UDC_H__ diff --git a/drivers/usb/gadget/mass_storage.c b/drivers/usb/gadget/mass_storage.c index d3eb27427c58..e24f72f82a47 100644 --- a/drivers/usb/gadget/mass_storage.c +++ b/drivers/usb/gadget/mass_storage.c @@ -10,15 +10,6 @@ * 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 */ @@ -169,7 +160,7 @@ static struct usb_composite_driver msg_driver = { .name = "g_mass_storage", .dev = &msg_device_desc, .iProduct = DRIVER_DESC, - .max_speed = USB_SPEED_HIGH, + .max_speed = USB_SPEED_SUPER, .needs_serial = 1, }; diff --git a/drivers/usb/gadget/multi.c b/drivers/usb/gadget/multi.c index 8c7b74717d85..7e7f515b8b19 100644 --- a/drivers/usb/gadget/multi.c +++ b/drivers/usb/gadget/multi.c @@ -10,15 +10,6 @@ * 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 */ diff --git a/drivers/usb/gadget/mv_udc.h b/drivers/usb/gadget/mv_udc.h index 65f1f7c3bd4e..daa75c12f336 100644 --- a/drivers/usb/gadget/mv_udc.h +++ b/drivers/usb/gadget/mv_udc.h @@ -1,3 +1,11 @@ +/* + * Copyright (C) 2011 Marvell International Ltd. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ #ifndef __MV_UDC_H #define __MV_UDC_H @@ -194,14 +202,25 @@ struct mv_udc { unsigned int ep0_dir; unsigned int dev_addr; + unsigned int test_mode; int errors; unsigned softconnect:1, vbus_active:1, remote_wakeup:1, softconnected:1, - force_fs:1; - struct clk *clk; + force_fs:1, + clock_gating:1, + active:1; + + struct work_struct vbus_work; + struct workqueue_struct *qwork; + + struct mv_usb_platform_data *pdata; + + /* some SOC has mutiple clock sources for USB*/ + unsigned int clknum; + struct clk *clk[0]; }; /* endpoint data structure */ @@ -225,6 +244,7 @@ struct mv_req { struct mv_dtd *dtd, *head, *tail; struct mv_ep *ep; struct list_head queue; + unsigned int test_mode; unsigned dtd_count; unsigned mapped:1; }; @@ -289,6 +309,4 @@ struct mv_dtd { struct mv_dtd *next_dtd_virt; }; -extern int mv_udc_phy_init(unsigned int base); - #endif diff --git a/drivers/usb/gadget/mv_udc_core.c b/drivers/usb/gadget/mv_udc_core.c index 0b3b8d0462db..892412103dd8 100644 --- a/drivers/usb/gadget/mv_udc_core.c +++ b/drivers/usb/gadget/mv_udc_core.c @@ -1,3 +1,14 @@ +/* + * Copyright (C) 2011 Marvell International Ltd. All rights reserved. + * Author: Chao Xie <chao.xie@marvell.com> + * Neil Zhang <zhangwm@marvell.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + #include <linux/module.h> #include <linux/pci.h> #include <linux/dma-mapping.h> @@ -22,6 +33,7 @@ #include <linux/irq.h> #include <linux/platform_device.h> #include <linux/clk.h> +#include <linux/platform_data/mv_usb.h> #include <asm/system.h> #include <asm/unaligned.h> @@ -45,6 +57,8 @@ #define LOOPS_USEC (1 << LOOPS_USEC_SHIFT) #define LOOPS(timeout) ((timeout) >> LOOPS_USEC_SHIFT) +static DECLARE_COMPLETION(release_done); + static const char driver_name[] = "mv_udc"; static const char driver_desc[] = DRIVER_DESC; @@ -53,6 +67,7 @@ static struct mv_udc *the_controller; int mv_usb_otgsc; static void nuke(struct mv_ep *ep, int status); +static void stop_activity(struct mv_udc *udc, struct usb_gadget_driver *driver); /* for endpoint 0 operations */ static const struct usb_endpoint_descriptor mv_ep0_desc = { @@ -82,14 +97,16 @@ static void ep0_reset(struct mv_udc *udc) (EP0_MAX_PKT_SIZE << EP_QUEUE_HEAD_MAX_PKT_LEN_POS) | EP_QUEUE_HEAD_IOS; + ep->dqh->next_dtd_ptr = EP_QUEUE_HEAD_NEXT_TERMINATE; + epctrlx = readl(&udc->op_regs->epctrlx[0]); if (i) { /* TX */ - epctrlx |= EPCTRL_TX_ENABLE | EPCTRL_TX_DATA_TOGGLE_RST + epctrlx |= EPCTRL_TX_ENABLE | (USB_ENDPOINT_XFER_CONTROL << EPCTRL_TX_EP_TYPE_SHIFT); } else { /* RX */ - epctrlx |= EPCTRL_RX_ENABLE | EPCTRL_RX_DATA_TOGGLE_RST + epctrlx |= EPCTRL_RX_ENABLE | (USB_ENDPOINT_XFER_CONTROL << EPCTRL_RX_EP_TYPE_SHIFT); } @@ -122,6 +139,7 @@ static int process_ep_req(struct mv_udc *udc, int index, int i, direction; int retval = 0; u32 errors; + u32 bit_pos; curr_dqh = &udc->ep_dqh[index]; direction = index % 2; @@ -139,10 +157,20 @@ static int process_ep_req(struct mv_udc *udc, int index, errors = curr_dtd->size_ioc_sts & DTD_ERROR_MASK; if (!errors) { - remaining_length += + remaining_length = (curr_dtd->size_ioc_sts & DTD_PACKET_SIZE) >> DTD_LENGTH_BIT_POS; actual -= remaining_length; + + if (remaining_length) { + if (direction) { + dev_dbg(&udc->dev->dev, + "TX dTD remains data\n"); + retval = -EPROTO; + break; + } else + break; + } } else { dev_info(&udc->dev->dev, "complete_tr error: ep=%d %s: error = 0x%x\n", @@ -164,6 +192,20 @@ static int process_ep_req(struct mv_udc *udc, int index, if (retval) return retval; + if (direction == EP_DIR_OUT) + bit_pos = 1 << curr_req->ep->ep_num; + else + bit_pos = 1 << (16 + curr_req->ep->ep_num); + + while ((curr_dqh->curr_dtd_ptr == curr_dtd->td_dma)) { + if (curr_dtd->dtd_next == EP_QUEUE_HEAD_NEXT_TERMINATE) { + while (readl(&udc->op_regs->epstatus) & bit_pos) + udelay(1); + break; + } + udelay(1); + } + curr_req->req.actual = actual; return 0; @@ -481,6 +523,7 @@ static int mv_ep_enable(struct usb_ep *_ep, u16 max = 0; u32 bit_pos, epctrlx, direction; unsigned char zlt = 0, ios = 0, mult = 0; + unsigned long flags; ep = container_of(_ep, struct mv_ep, ep); udc = ep->udc; @@ -493,7 +536,7 @@ static int mv_ep_enable(struct usb_ep *_ep, return -ESHUTDOWN; direction = ep_dir(ep); - max = le16_to_cpu(desc->wMaxPacketSize); + max = usb_endpoint_maxp(desc); /* * disable HW zero length termination select @@ -501,9 +544,6 @@ static int mv_ep_enable(struct usb_ep *_ep, */ zlt = 1; - /* Get the endpoint queue head address */ - dqh = (struct mv_dqh *)ep->dqh; - bit_pos = 1 << ((direction == EP_DIR_OUT ? 0 : 16) + ep->ep_num); /* Check if the Endpoint is Primed */ @@ -532,7 +572,7 @@ static int mv_ep_enable(struct usb_ep *_ep, case USB_ENDPOINT_XFER_ISOC: /* Calculate transactions needed for high bandwidth iso */ mult = (unsigned char)(1 + ((max >> 11) & 0x03)); - max = max & 0x8ff; /* bit 0~10 */ + max = max & 0x7ff; /* bit 0~10 */ /* 3 transactions at most */ if (mult > 3) goto en_done; @@ -540,6 +580,10 @@ static int mv_ep_enable(struct usb_ep *_ep, default: goto en_done; } + + spin_lock_irqsave(&udc->lock, flags); + /* Get the endpoint queue head address */ + dqh = ep->dqh; dqh->max_packet_length = (max << EP_QUEUE_HEAD_MAX_PKT_LEN_POS) | (mult << EP_QUEUE_HEAD_MULT_POS) | (zlt ? EP_QUEUE_HEAD_ZLT_SEL : 0) @@ -572,18 +616,20 @@ static int mv_ep_enable(struct usb_ep *_ep, */ epctrlx = readl(&udc->op_regs->epctrlx[ep->ep_num]); if ((epctrlx & EPCTRL_RX_ENABLE) == 0) { - epctrlx |= ((desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) + epctrlx |= (USB_ENDPOINT_XFER_BULK << EPCTRL_RX_EP_TYPE_SHIFT); writel(epctrlx, &udc->op_regs->epctrlx[ep->ep_num]); } epctrlx = readl(&udc->op_regs->epctrlx[ep->ep_num]); if ((epctrlx & EPCTRL_TX_ENABLE) == 0) { - epctrlx |= ((desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) + epctrlx |= (USB_ENDPOINT_XFER_BULK << EPCTRL_TX_EP_TYPE_SHIFT); writel(epctrlx, &udc->op_regs->epctrlx[ep->ep_num]); } + spin_unlock_irqrestore(&udc->lock, flags); + return 0; en_done: return -EINVAL; @@ -595,6 +641,7 @@ static int mv_ep_disable(struct usb_ep *_ep) struct mv_ep *ep; struct mv_dqh *dqh; u32 bit_pos, epctrlx, direction; + unsigned long flags; ep = container_of(_ep, struct mv_ep, ep); if ((_ep == NULL) || !ep->desc) @@ -605,6 +652,8 @@ static int mv_ep_disable(struct usb_ep *_ep) /* Get the endpoint queue head address */ dqh = ep->dqh; + spin_lock_irqsave(&udc->lock, flags); + direction = ep_dir(ep); bit_pos = 1 << ((direction == EP_DIR_OUT ? 0 : 16) + ep->ep_num); @@ -623,6 +672,9 @@ static int mv_ep_disable(struct usb_ep *_ep) ep->desc = NULL; ep->stopped = 1; + + spin_unlock_irqrestore(&udc->lock, flags); + return 0; } @@ -655,37 +707,28 @@ static void mv_ep_fifo_flush(struct usb_ep *_ep) { struct mv_udc *udc; u32 bit_pos, direction; - struct mv_ep *ep = container_of(_ep, struct mv_ep, ep); + struct mv_ep *ep; unsigned int loops; + if (!_ep) + return; + + ep = container_of(_ep, struct mv_ep, ep); + if (!ep->desc) + return; + udc = ep->udc; direction = ep_dir(ep); - bit_pos = 1 << ((direction == EP_DIR_OUT ? 0 : 16) + ep->ep_num); - /* - * Flushing will halt the pipe - * Write 1 to the Flush register - */ - writel(bit_pos, &udc->op_regs->epflush); - /* Wait until flushing completed */ - loops = LOOPS(FLUSH_TIMEOUT); - while (readl(&udc->op_regs->epflush) & bit_pos) { - /* - * ENDPTFLUSH bit should be cleared to indicate this - * operation is complete - */ - if (loops == 0) { - dev_err(&udc->dev->dev, - "TIMEOUT for ENDPTFLUSH=0x%x, bit_pos=0x%x\n", - (unsigned)readl(&udc->op_regs->epflush), - (unsigned)bit_pos); - return; - } - loops--; - udelay(LOOPS_USEC); - } + if (ep->ep_num == 0) + bit_pos = (1 << 16) | 1; + else if (direction == EP_DIR_OUT) + bit_pos = 1 << ep->ep_num; + else + bit_pos = 1 << (16 + ep->ep_num); + loops = LOOPS(EPSTATUS_TIMEOUT); - while (readl(&udc->op_regs->epstatus) & bit_pos) { + do { unsigned int inter_loops; if (loops == 0) { @@ -700,7 +743,7 @@ static void mv_ep_fifo_flush(struct usb_ep *_ep) /* Wait until flushing completed */ inter_loops = LOOPS(FLUSH_TIMEOUT); - while (readl(&udc->op_regs->epflush) & bit_pos) { + while (readl(&udc->op_regs->epflush)) { /* * ENDPTFLUSH bit should be cleared to indicate this * operation is complete @@ -717,7 +760,7 @@ static void mv_ep_fifo_flush(struct usb_ep *_ep) udelay(LOOPS_USEC); } loops--; - } + } while (readl(&udc->op_regs->epstatus) & bit_pos); } /* queues (submits) an I/O request to an endpoint */ @@ -987,6 +1030,22 @@ static struct usb_ep_ops mv_ep_ops = { .fifo_flush = mv_ep_fifo_flush, /* flush fifo */ }; +static void udc_clock_enable(struct mv_udc *udc) +{ + unsigned int i; + + for (i = 0; i < udc->clknum; i++) + clk_enable(udc->clk[i]); +} + +static void udc_clock_disable(struct mv_udc *udc) +{ + unsigned int i; + + for (i = 0; i < udc->clknum; i++) + clk_disable(udc->clk[i]); +} + static void udc_stop(struct mv_udc *udc) { u32 tmp; @@ -1075,6 +1134,40 @@ static int udc_reset(struct mv_udc *udc) return 0; } +static int mv_udc_enable(struct mv_udc *udc) +{ + int retval; + + if (udc->clock_gating == 0 || udc->active) + return 0; + + dev_dbg(&udc->dev->dev, "enable udc\n"); + udc_clock_enable(udc); + if (udc->pdata->phy_init) { + retval = udc->pdata->phy_init(udc->phy_regs); + if (retval) { + dev_err(&udc->dev->dev, + "init phy error %d\n", retval); + udc_clock_disable(udc); + return retval; + } + } + udc->active = 1; + + return 0; +} + +static void mv_udc_disable(struct mv_udc *udc) +{ + if (udc->clock_gating && udc->active) { + dev_dbg(&udc->dev->dev, "disable udc\n"); + if (udc->pdata->phy_deinit) + udc->pdata->phy_deinit(udc->phy_regs); + udc_clock_disable(udc); + udc->active = 0; + } +} + static int mv_udc_get_frame(struct usb_gadget *gadget) { struct mv_udc *udc; @@ -1110,22 +1203,68 @@ static int mv_udc_wakeup(struct usb_gadget *gadget) return 0; } +static int mv_udc_vbus_session(struct usb_gadget *gadget, int is_active) +{ + struct mv_udc *udc; + unsigned long flags; + int retval = 0; + + udc = container_of(gadget, struct mv_udc, gadget); + spin_lock_irqsave(&udc->lock, flags); + + dev_dbg(&udc->dev->dev, "%s: softconnect %d, vbus_active %d\n", + __func__, udc->softconnect, udc->vbus_active); + + udc->vbus_active = (is_active != 0); + if (udc->driver && udc->softconnect && udc->vbus_active) { + retval = mv_udc_enable(udc); + if (retval == 0) { + /* Clock is disabled, need re-init registers */ + udc_reset(udc); + ep0_reset(udc); + udc_start(udc); + } + } else if (udc->driver && udc->softconnect) { + /* stop all the transfer in queue*/ + stop_activity(udc, udc->driver); + udc_stop(udc); + mv_udc_disable(udc); + } + + spin_unlock_irqrestore(&udc->lock, flags); + return retval; +} + static int mv_udc_pullup(struct usb_gadget *gadget, int is_on) { struct mv_udc *udc; unsigned long flags; + int retval = 0; udc = container_of(gadget, struct mv_udc, gadget); spin_lock_irqsave(&udc->lock, flags); + dev_dbg(&udc->dev->dev, "%s: softconnect %d, vbus_active %d\n", + __func__, udc->softconnect, udc->vbus_active); + udc->softconnect = (is_on != 0); - if (udc->driver && udc->softconnect) - udc_start(udc); - else + if (udc->driver && udc->softconnect && udc->vbus_active) { + retval = mv_udc_enable(udc); + if (retval == 0) { + /* Clock is disabled, need re-init registers */ + udc_reset(udc); + ep0_reset(udc); + udc_start(udc); + } + } else if (udc->driver && udc->vbus_active) { + /* stop all the transfer in queue*/ + stop_activity(udc, udc->driver); udc_stop(udc); + mv_udc_disable(udc); + } spin_unlock_irqrestore(&udc->lock, flags); - return 0; + return retval; } static int mv_udc_start(struct usb_gadget_driver *driver, @@ -1140,17 +1279,15 @@ static const struct usb_gadget_ops mv_ops = { /* tries to wake up the host connected to this gadget */ .wakeup = mv_udc_wakeup, + /* notify controller that VBUS is powered or not */ + .vbus_session = mv_udc_vbus_session, + /* D+ pullup, software-controlled connect/disconnect to USB host */ .pullup = mv_udc_pullup, .start = mv_udc_start, .stop = mv_udc_stop, }; -static void mv_udc_testmode(struct mv_udc *udc, u16 index, bool enter) -{ - dev_info(&udc->dev->dev, "Test Mode is not support yet\n"); -} - static int eps_init(struct mv_udc *udc) { struct mv_ep *ep; @@ -1257,7 +1394,7 @@ static int mv_udc_start(struct usb_gadget_driver *driver, udc->usb_state = USB_STATE_ATTACHED; udc->ep0_state = WAIT_FOR_SETUP; - udc->ep0_dir = USB_DIR_OUT; + udc->ep0_dir = EP_DIR_OUT; spin_unlock_irqrestore(&udc->lock, flags); @@ -1269,9 +1406,13 @@ static int mv_udc_start(struct usb_gadget_driver *driver, udc->gadget.dev.driver = NULL; return retval; } - udc_reset(udc); - ep0_reset(udc); - udc_start(udc); + + /* pullup is always on */ + mv_udc_pullup(&udc->gadget, 1); + + /* When boot with cable attached, there will be no vbus irq occurred */ + if (udc->qwork) + queue_work(udc->qwork, &udc->vbus_work); return 0; } @@ -1284,13 +1425,16 @@ static int mv_udc_stop(struct usb_gadget_driver *driver) if (!udc) return -ENODEV; - udc_stop(udc); - spin_lock_irqsave(&udc->lock, flags); + mv_udc_enable(udc); + udc_stop(udc); + /* stop all usb activities */ udc->gadget.speed = USB_SPEED_UNKNOWN; stop_activity(udc, driver); + mv_udc_disable(udc); + spin_unlock_irqrestore(&udc->lock, flags); /* unbind gadget driver */ @@ -1301,6 +1445,31 @@ static int mv_udc_stop(struct usb_gadget_driver *driver) return 0; } +static void mv_set_ptc(struct mv_udc *udc, u32 mode) +{ + u32 portsc; + + portsc = readl(&udc->op_regs->portsc[0]); + portsc |= mode << 16; + writel(portsc, &udc->op_regs->portsc[0]); +} + +static void prime_status_complete(struct usb_ep *ep, struct usb_request *_req) +{ + struct mv_udc *udc = the_controller; + struct mv_req *req = container_of(_req, struct mv_req, req); + unsigned long flags; + + dev_info(&udc->dev->dev, "switch to test mode %d\n", req->test_mode); + + spin_lock_irqsave(&udc->lock, flags); + if (req->test_mode) { + mv_set_ptc(udc, req->test_mode); + req->test_mode = 0; + } + spin_unlock_irqrestore(&udc->lock, flags); +} + static int udc_prime_status(struct mv_udc *udc, u8 direction, u16 status, bool empty) { @@ -1310,6 +1479,7 @@ udc_prime_status(struct mv_udc *udc, u8 direction, u16 status, bool empty) ep = &udc->eps[0]; udc->ep0_dir = direction; + udc->ep0_state = WAIT_FOR_OUT_STATUS; req = udc->status_req; @@ -1323,9 +1493,21 @@ udc_prime_status(struct mv_udc *udc, u8 direction, u16 status, bool empty) req->ep = ep; req->req.status = -EINPROGRESS; req->req.actual = 0; - req->req.complete = NULL; + if (udc->test_mode) { + req->req.complete = prime_status_complete; + req->test_mode = udc->test_mode; + udc->test_mode = 0; + } else + req->req.complete = NULL; req->dtd_count = 0; + if (req->req.dma == DMA_ADDR_INVALID) { + req->req.dma = dma_map_single(ep->udc->gadget.dev.parent, + req->req.buf, req->req.length, + ep_dir(ep) ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + req->mapped = 1; + } + /* prime the data phase */ if (!req_to_dtd(req)) retval = queue_dtd(ep, req); @@ -1346,6 +1528,17 @@ out: return retval; } +static void mv_udc_testmode(struct mv_udc *udc, u16 index) +{ + if (index <= TEST_FORCE_EN) { + udc->test_mode = index; + if (udc_prime_status(udc, EP_DIR_IN, 0, true)) + ep0_stall(udc); + } else + dev_err(&udc->dev->dev, + "This test mode(%d) is not supported\n", index); +} + static void ch9setaddress(struct mv_udc *udc, struct usb_ctrlrequest *setup) { udc->dev_addr = (u8)setup->wValue; @@ -1360,7 +1553,7 @@ static void ch9setaddress(struct mv_udc *udc, struct usb_ctrlrequest *setup) static void ch9getstatus(struct mv_udc *udc, u8 ep_num, struct usb_ctrlrequest *setup) { - u16 status; + u16 status = 0; int retval; if ((setup->bRequestType & (USB_DIR_IN | USB_TYPE_MASK)) @@ -1388,6 +1581,8 @@ static void ch9getstatus(struct mv_udc *udc, u8 ep_num, retval = udc_prime_status(udc, EP_DIR_IN, status, false); if (retval) ep0_stall(udc); + else + udc->ep0_state = DATA_STATE_XMIT; } static void ch9clearfeature(struct mv_udc *udc, struct usb_ctrlrequest *setup) @@ -1402,9 +1597,6 @@ static void ch9clearfeature(struct mv_udc *udc, struct usb_ctrlrequest *setup) case USB_DEVICE_REMOTE_WAKEUP: udc->remote_wakeup = 0; break; - case USB_DEVICE_TEST_MODE: - mv_udc_testmode(udc, 0, false); - break; default: goto out; } @@ -1433,8 +1625,6 @@ static void ch9clearfeature(struct mv_udc *udc, struct usb_ctrlrequest *setup) if (udc_prime_status(udc, EP_DIR_IN, 0, true)) ep0_stall(udc); - else - udc->ep0_state = DATA_STATE_XMIT; out: return; } @@ -1452,16 +1642,16 @@ static void ch9setfeature(struct mv_udc *udc, struct usb_ctrlrequest *setup) break; case USB_DEVICE_TEST_MODE: if (setup->wIndex & 0xFF - && udc->gadget.speed != USB_SPEED_HIGH) - goto out; - if (udc->usb_state == USB_STATE_CONFIGURED - || udc->usb_state == USB_STATE_ADDRESS - || udc->usb_state == USB_STATE_DEFAULT) - mv_udc_testmode(udc, - setup->wIndex & 0xFF00, true); - else - goto out; - break; + || udc->gadget.speed != USB_SPEED_HIGH) + ep0_stall(udc); + + if (udc->usb_state != USB_STATE_CONFIGURED + && udc->usb_state != USB_STATE_ADDRESS + && udc->usb_state != USB_STATE_DEFAULT) + ep0_stall(udc); + + mv_udc_testmode(udc, (setup->wIndex >> 8)); + goto out; default: goto out; } @@ -1599,8 +1789,7 @@ static void get_setup_data(struct mv_udc *udc, u8 ep_num, u8 *buffer_ptr) dqh = &udc->ep_dqh[ep_num * 2 + EP_DIR_OUT]; /* Clear bit in ENDPTSETUPSTAT */ - temp = readl(&udc->op_regs->epsetupstat); - writel(temp | (1 << ep_num), &udc->op_regs->epsetupstat); + writel((1 << ep_num), &udc->op_regs->epsetupstat); /* while a hazard exists when setup package arrives */ do { @@ -1871,23 +2060,57 @@ static irqreturn_t mv_udc_irq(int irq, void *dev) return IRQ_HANDLED; } +static irqreturn_t mv_udc_vbus_irq(int irq, void *dev) +{ + struct mv_udc *udc = (struct mv_udc *)dev; + + /* polling VBUS and init phy may cause too much time*/ + if (udc->qwork) + queue_work(udc->qwork, &udc->vbus_work); + + return IRQ_HANDLED; +} + +static void mv_udc_vbus_work(struct work_struct *work) +{ + struct mv_udc *udc; + unsigned int vbus; + + udc = container_of(work, struct mv_udc, vbus_work); + if (!udc->pdata->vbus) + return; + + vbus = udc->pdata->vbus->poll(); + dev_info(&udc->dev->dev, "vbus is %d\n", vbus); + + if (vbus == VBUS_HIGH) + mv_udc_vbus_session(&udc->gadget, 1); + else if (vbus == VBUS_LOW) + mv_udc_vbus_session(&udc->gadget, 0); +} + /* release device structure */ static void gadget_release(struct device *_dev) { struct mv_udc *udc = the_controller; complete(udc->done); - kfree(udc); } -static int mv_udc_remove(struct platform_device *dev) +static int __devexit mv_udc_remove(struct platform_device *dev) { struct mv_udc *udc = the_controller; - DECLARE_COMPLETION(done); + int clk_i; usb_del_gadget_udc(&udc->gadget); - udc->done = &done; + if (udc->qwork) { + flush_workqueue(udc->qwork); + destroy_workqueue(udc->qwork); + } + + if (udc->pdata && udc->pdata->vbus && udc->clock_gating) + free_irq(udc->pdata->vbus->irq, &dev->dev); /* free memory allocated in probe */ if (udc->dtd_pool) @@ -1902,6 +2125,8 @@ static int mv_udc_remove(struct platform_device *dev) if (udc->irq) free_irq(udc->irq, &dev->dev); + mv_udc_disable(udc); + if (udc->cap_regs) iounmap(udc->cap_regs); udc->cap_regs = NULL; @@ -1915,45 +2140,62 @@ static int mv_udc_remove(struct platform_device *dev) kfree(udc->status_req); } + for (clk_i = 0; clk_i <= udc->clknum; clk_i++) + clk_put(udc->clk[clk_i]); + device_unregister(&udc->gadget.dev); /* free dev, wait for the release() finished */ - wait_for_completion(&done); + wait_for_completion(udc->done); + kfree(udc); the_controller = NULL; return 0; } -int mv_udc_probe(struct platform_device *dev) +static int __devinit mv_udc_probe(struct platform_device *dev) { + struct mv_usb_platform_data *pdata = dev->dev.platform_data; struct mv_udc *udc; int retval = 0; + int clk_i = 0; struct resource *r; size_t size; - udc = kzalloc(sizeof *udc, GFP_KERNEL); + if (pdata == NULL) { + dev_err(&dev->dev, "missing platform_data\n"); + return -ENODEV; + } + + size = sizeof(*udc) + sizeof(struct clk *) * pdata->clknum; + udc = kzalloc(size, GFP_KERNEL); if (udc == NULL) { dev_err(&dev->dev, "failed to allocate memory for udc\n"); - retval = -ENOMEM; - goto error; + return -ENOMEM; } + the_controller = udc; + udc->done = &release_done; + udc->pdata = dev->dev.platform_data; spin_lock_init(&udc->lock); udc->dev = dev; - udc->clk = clk_get(&dev->dev, "U2OCLK"); - if (IS_ERR(udc->clk)) { - retval = PTR_ERR(udc->clk); - goto error; + udc->clknum = pdata->clknum; + for (clk_i = 0; clk_i < udc->clknum; clk_i++) { + udc->clk[clk_i] = clk_get(&dev->dev, pdata->clkname[clk_i]); + if (IS_ERR(udc->clk[clk_i])) { + retval = PTR_ERR(udc->clk[clk_i]); + goto err_put_clk; + } } - r = platform_get_resource_byname(udc->dev, IORESOURCE_MEM, "u2o"); + r = platform_get_resource_byname(udc->dev, IORESOURCE_MEM, "capregs"); if (r == NULL) { dev_err(&dev->dev, "no I/O memory resource defined\n"); retval = -ENODEV; - goto error; + goto err_put_clk; } udc->cap_regs = (struct mv_cap_regs __iomem *) @@ -1961,29 +2203,31 @@ int mv_udc_probe(struct platform_device *dev) if (udc->cap_regs == NULL) { dev_err(&dev->dev, "failed to map I/O memory\n"); retval = -EBUSY; - goto error; + goto err_put_clk; } - r = platform_get_resource_byname(udc->dev, IORESOURCE_MEM, "u2ophy"); + r = platform_get_resource_byname(udc->dev, IORESOURCE_MEM, "phyregs"); if (r == NULL) { dev_err(&dev->dev, "no phy I/O memory resource defined\n"); retval = -ENODEV; - goto error; + goto err_iounmap_capreg; } udc->phy_regs = (unsigned int)ioremap(r->start, resource_size(r)); if (udc->phy_regs == 0) { dev_err(&dev->dev, "failed to map phy I/O memory\n"); retval = -EBUSY; - goto error; + goto err_iounmap_capreg; } /* we will acces controller register, so enable the clk */ - clk_enable(udc->clk); - retval = mv_udc_phy_init(udc->phy_regs); - if (retval) { - dev_err(&dev->dev, "phy initialization error %d\n", retval); - goto error; + udc_clock_enable(udc); + if (pdata->phy_init) { + retval = pdata->phy_init(udc->phy_regs); + if (retval) { + dev_err(&dev->dev, "phy init error %d\n", retval); + goto err_iounmap_phyreg; + } } udc->op_regs = (struct mv_op_regs __iomem *)((u32)udc->cap_regs @@ -1991,6 +2235,13 @@ int mv_udc_probe(struct platform_device *dev) & CAPLENGTH_MASK)); udc->max_eps = readl(&udc->cap_regs->dccparams) & DCCPARAMS_DEN_MASK; + /* + * some platform will use usb to download image, it may not disconnect + * usb gadget before loading kernel. So first stop udc here. + */ + udc_stop(udc); + writel(0xFFFFFFFF, &udc->op_regs->usbsts); + size = udc->max_eps * sizeof(struct mv_dqh) *2; size = (size + DQH_ALIGNMENT - 1) & ~(DQH_ALIGNMENT - 1); udc->ep_dqh = dma_alloc_coherent(&dev->dev, size, @@ -1999,7 +2250,7 @@ int mv_udc_probe(struct platform_device *dev) if (udc->ep_dqh == NULL) { dev_err(&dev->dev, "allocate dQH memory failed\n"); retval = -ENOMEM; - goto error; + goto err_disable_clock; } udc->ep_dqh_size = size; @@ -2012,7 +2263,7 @@ int mv_udc_probe(struct platform_device *dev) if (!udc->dtd_pool) { retval = -ENOMEM; - goto error; + goto err_free_dma; } size = udc->max_eps * sizeof(struct mv_ep) *2; @@ -2020,7 +2271,7 @@ int mv_udc_probe(struct platform_device *dev) if (udc->eps == NULL) { dev_err(&dev->dev, "allocate ep memory failed\n"); retval = -ENOMEM; - goto error; + goto err_destroy_dma; } /* initialize ep0 status request structure */ @@ -2028,13 +2279,13 @@ int mv_udc_probe(struct platform_device *dev) if (!udc->status_req) { dev_err(&dev->dev, "allocate status_req memory failed\n"); retval = -ENOMEM; - goto error; + goto err_free_eps; } INIT_LIST_HEAD(&udc->status_req->queue); /* allocate a small amount of memory to get valid address */ udc->status_req->req.buf = kzalloc(8, GFP_KERNEL); - udc->status_req->req.dma = virt_to_phys(udc->status_req->req.buf); + udc->status_req->req.dma = DMA_ADDR_INVALID; udc->resume_state = USB_STATE_NOTATTACHED; udc->usb_state = USB_STATE_POWERED; @@ -2045,15 +2296,15 @@ int mv_udc_probe(struct platform_device *dev) if (r == NULL) { dev_err(&dev->dev, "no IRQ resource defined\n"); retval = -ENODEV; - goto error; + goto err_free_status_req; } udc->irq = r->start; if (request_irq(udc->irq, mv_udc_irq, - IRQF_DISABLED | IRQF_SHARED, driver_name, udc)) { + IRQF_SHARED, driver_name, udc)) { dev_err(&dev->dev, "Request irq %d for UDC failed\n", udc->irq); retval = -ENODEV; - goto error; + goto err_free_status_req; } /* initialize gadget structure */ @@ -2072,18 +2323,82 @@ int mv_udc_probe(struct platform_device *dev) retval = device_register(&udc->gadget.dev); if (retval) - goto error; + goto err_free_irq; eps_init(udc); - the_controller = udc; + /* VBUS detect: we can disable/enable clock on demand.*/ + if (pdata->vbus) { + udc->clock_gating = 1; + retval = request_threaded_irq(pdata->vbus->irq, NULL, + mv_udc_vbus_irq, IRQF_ONESHOT, "vbus", udc); + if (retval) { + dev_info(&dev->dev, + "Can not request irq for VBUS, " + "disable clock gating\n"); + udc->clock_gating = 0; + } + + udc->qwork = create_singlethread_workqueue("mv_udc_queue"); + if (!udc->qwork) { + dev_err(&dev->dev, "cannot create workqueue\n"); + retval = -ENOMEM; + goto err_unregister; + } + + INIT_WORK(&udc->vbus_work, mv_udc_vbus_work); + } + + /* + * When clock gating is supported, we can disable clk and phy. + * If not, it means that VBUS detection is not supported, we + * have to enable vbus active all the time to let controller work. + */ + if (udc->clock_gating) { + if (udc->pdata->phy_deinit) + udc->pdata->phy_deinit(udc->phy_regs); + udc_clock_disable(udc); + } else + udc->vbus_active = 1; retval = usb_add_gadget_udc(&dev->dev, &udc->gadget); - if (!retval) - return retval; -error: - if (udc) - mv_udc_remove(udc->dev); + if (retval) + goto err_unregister; + + dev_info(&dev->dev, "successful probe UDC device %s clock gating.\n", + udc->clock_gating ? "with" : "without"); + + return 0; + +err_unregister: + if (udc->pdata && udc->pdata->vbus && udc->clock_gating) + free_irq(pdata->vbus->irq, &dev->dev); + device_unregister(&udc->gadget.dev); +err_free_irq: + free_irq(udc->irq, &dev->dev); +err_free_status_req: + kfree(udc->status_req->req.buf); + kfree(udc->status_req); +err_free_eps: + kfree(udc->eps); +err_destroy_dma: + dma_pool_destroy(udc->dtd_pool); +err_free_dma: + dma_free_coherent(&dev->dev, udc->ep_dqh_size, + udc->ep_dqh, udc->ep_dqh_dma); +err_disable_clock: + if (udc->pdata->phy_deinit) + udc->pdata->phy_deinit(udc->phy_regs); + udc_clock_disable(udc); +err_iounmap_phyreg: + iounmap((void *)udc->phy_regs); +err_iounmap_capreg: + iounmap(udc->cap_regs); +err_put_clk: + for (clk_i--; clk_i >= 0; clk_i--) + clk_put(udc->clk[clk_i]); + the_controller = NULL; + kfree(udc); return retval; } @@ -2102,11 +2417,16 @@ static int mv_udc_resume(struct device *_dev) struct mv_udc *udc = the_controller; int retval; - retval = mv_udc_phy_init(udc->phy_regs); - if (retval) { - dev_err(_dev, "phy initialization error %d\n", retval); - return retval; + if (udc->pdata->phy_init) { + retval = udc->pdata->phy_init(udc->phy_regs); + if (retval) { + dev_err(&udc->dev->dev, + "init phy error %d when resume back\n", + retval); + return retval; + } } + udc_reset(udc); ep0_reset(udc); udc_start(udc); @@ -2120,9 +2440,21 @@ static const struct dev_pm_ops mv_udc_pm_ops = { }; #endif +static void mv_udc_shutdown(struct platform_device *dev) +{ + struct mv_udc *udc = the_controller; + u32 mode; + + /* reset controller mode to IDLE */ + mode = readl(&udc->op_regs->usbmode); + mode &= ~3; + writel(mode, &udc->op_regs->usbmode); +} + static struct platform_driver udc_driver = { .probe = mv_udc_probe, .remove = __exit_p(mv_udc_remove), + .shutdown = mv_udc_shutdown, .driver = { .owner = THIS_MODULE, .name = "pxa-u2o", diff --git a/drivers/usb/gadget/mv_udc_phy.c b/drivers/usb/gadget/mv_udc_phy.c deleted file mode 100644 index d4dea97e38a5..000000000000 --- a/drivers/usb/gadget/mv_udc_phy.c +++ /dev/null @@ -1,214 +0,0 @@ -#include <linux/delay.h> -#include <linux/timer.h> -#include <linux/io.h> -#include <linux/errno.h> - -#include <mach/cputype.h> - -#ifdef CONFIG_ARCH_MMP - -#define UTMI_REVISION 0x0 -#define UTMI_CTRL 0x4 -#define UTMI_PLL 0x8 -#define UTMI_TX 0xc -#define UTMI_RX 0x10 -#define UTMI_IVREF 0x14 -#define UTMI_T0 0x18 -#define UTMI_T1 0x1c -#define UTMI_T2 0x20 -#define UTMI_T3 0x24 -#define UTMI_T4 0x28 -#define UTMI_T5 0x2c -#define UTMI_RESERVE 0x30 -#define UTMI_USB_INT 0x34 -#define UTMI_DBG_CTL 0x38 -#define UTMI_OTG_ADDON 0x3c - -/* For UTMICTRL Register */ -#define UTMI_CTRL_USB_CLK_EN (1 << 31) -/* pxa168 */ -#define UTMI_CTRL_SUSPEND_SET1 (1 << 30) -#define UTMI_CTRL_SUSPEND_SET2 (1 << 29) -#define UTMI_CTRL_RXBUF_PDWN (1 << 24) -#define UTMI_CTRL_TXBUF_PDWN (1 << 11) - -#define UTMI_CTRL_INPKT_DELAY_SHIFT 30 -#define UTMI_CTRL_INPKT_DELAY_SOF_SHIFT 28 -#define UTMI_CTRL_PU_REF_SHIFT 20 -#define UTMI_CTRL_ARC_PULLDN_SHIFT 12 -#define UTMI_CTRL_PLL_PWR_UP_SHIFT 1 -#define UTMI_CTRL_PWR_UP_SHIFT 0 -/* For UTMI_PLL Register */ -#define UTMI_PLL_CLK_BLK_EN_SHIFT 24 -#define UTMI_PLL_FBDIV_SHIFT 4 -#define UTMI_PLL_REFDIV_SHIFT 0 -#define UTMI_PLL_FBDIV_MASK 0x00000FF0 -#define UTMI_PLL_REFDIV_MASK 0x0000000F -#define UTMI_PLL_ICP_MASK 0x00007000 -#define UTMI_PLL_KVCO_MASK 0x00031000 -#define UTMI_PLL_PLLCALI12_SHIFT 29 -#define UTMI_PLL_PLLCALI12_MASK (0x3 << 29) -#define UTMI_PLL_PLLVDD18_SHIFT 27 -#define UTMI_PLL_PLLVDD18_MASK (0x3 << 27) -#define UTMI_PLL_PLLVDD12_SHIFT 25 -#define UTMI_PLL_PLLVDD12_MASK (0x3 << 25) -#define UTMI_PLL_KVCO_SHIFT 15 -#define UTMI_PLL_ICP_SHIFT 12 -/* For UTMI_TX Register */ -#define UTMI_TX_REG_EXT_FS_RCAL_SHIFT 27 -#define UTMI_TX_REG_EXT_FS_RCAL_MASK (0xf << 27) -#define UTMI_TX_REG_EXT_FS_RCAL_EN_MASK 26 -#define UTMI_TX_REG_EXT_FS_RCAL_EN (0x1 << 26) -#define UTMI_TX_LOW_VDD_EN_SHIFT 11 -#define UTMI_TX_IMPCAL_VTH_SHIFT 14 -#define UTMI_TX_IMPCAL_VTH_MASK (0x7 << 14) -#define UTMI_TX_CK60_PHSEL_SHIFT 17 -#define UTMI_TX_CK60_PHSEL_MASK (0xf << 17) -#define UTMI_TX_TXVDD12_SHIFT 22 -#define UTMI_TX_TXVDD12_MASK (0x3 << 22) -#define UTMI_TX_AMP_SHIFT 0 -#define UTMI_TX_AMP_MASK (0x7 << 0) -/* For UTMI_RX Register */ -#define UTMI_RX_SQ_THRESH_SHIFT 4 -#define UTMI_RX_SQ_THRESH_MASK (0xf << 4) -#define UTMI_REG_SQ_LENGTH_SHIFT 15 -#define UTMI_REG_SQ_LENGTH_MASK (0x3 << 15) - -#define REG_RCAL_START 0x00001000 -#define VCOCAL_START 0x00200000 -#define KVCO_EXT 0x00400000 -#define PLL_READY 0x00800000 -#define CLK_BLK_EN 0x01000000 -#endif - -static unsigned int u2o_read(unsigned int base, unsigned int offset) -{ - return readl(base + offset); -} - -static void u2o_set(unsigned int base, unsigned int offset, unsigned int value) -{ - unsigned int reg; - - reg = readl(base + offset); - reg |= value; - writel(reg, base + offset); - readl(base + offset); -} - -static void u2o_clear(unsigned int base, unsigned int offset, - unsigned int value) -{ - unsigned int reg; - - reg = readl(base + offset); - reg &= ~value; - writel(reg, base + offset); - readl(base + offset); -} - -static void u2o_write(unsigned int base, unsigned int offset, - unsigned int value) -{ - writel(value, base + offset); - readl(base + offset); -} - -#ifdef CONFIG_ARCH_MMP -int mv_udc_phy_init(unsigned int base) -{ - unsigned long timeout; - - /* Initialize the USB PHY power */ - if (cpu_is_pxa910()) { - u2o_set(base, UTMI_CTRL, (1 << UTMI_CTRL_INPKT_DELAY_SOF_SHIFT) - | (1 << UTMI_CTRL_PU_REF_SHIFT)); - } - - u2o_set(base, UTMI_CTRL, 1 << UTMI_CTRL_PLL_PWR_UP_SHIFT); - u2o_set(base, UTMI_CTRL, 1 << UTMI_CTRL_PWR_UP_SHIFT); - - /* UTMI_PLL settings */ - u2o_clear(base, UTMI_PLL, UTMI_PLL_PLLVDD18_MASK - | UTMI_PLL_PLLVDD12_MASK | UTMI_PLL_PLLCALI12_MASK - | UTMI_PLL_FBDIV_MASK | UTMI_PLL_REFDIV_MASK - | UTMI_PLL_ICP_MASK | UTMI_PLL_KVCO_MASK); - - u2o_set(base, UTMI_PLL, (0xee << UTMI_PLL_FBDIV_SHIFT) - | (0xb << UTMI_PLL_REFDIV_SHIFT) - | (3 << UTMI_PLL_PLLVDD18_SHIFT) - | (3 << UTMI_PLL_PLLVDD12_SHIFT) - | (3 << UTMI_PLL_PLLCALI12_SHIFT) - | (1 << UTMI_PLL_ICP_SHIFT) | (3 << UTMI_PLL_KVCO_SHIFT)); - - /* UTMI_TX */ - u2o_clear(base, UTMI_TX, UTMI_TX_REG_EXT_FS_RCAL_EN_MASK - | UTMI_TX_TXVDD12_MASK - | UTMI_TX_CK60_PHSEL_MASK | UTMI_TX_IMPCAL_VTH_MASK - | UTMI_TX_REG_EXT_FS_RCAL_MASK | UTMI_TX_AMP_MASK); - u2o_set(base, UTMI_TX, (3 << UTMI_TX_TXVDD12_SHIFT) - | (4 << UTMI_TX_CK60_PHSEL_SHIFT) - | (4 << UTMI_TX_IMPCAL_VTH_SHIFT) - | (8 << UTMI_TX_REG_EXT_FS_RCAL_SHIFT) - | (3 << UTMI_TX_AMP_SHIFT)); - - /* UTMI_RX */ - u2o_clear(base, UTMI_RX, UTMI_RX_SQ_THRESH_MASK - | UTMI_REG_SQ_LENGTH_MASK); - if (cpu_is_pxa168()) - u2o_set(base, UTMI_RX, (7 << UTMI_RX_SQ_THRESH_SHIFT) - | (2 << UTMI_REG_SQ_LENGTH_SHIFT)); - else - u2o_set(base, UTMI_RX, (0x7 << UTMI_RX_SQ_THRESH_SHIFT) - | (2 << UTMI_REG_SQ_LENGTH_SHIFT)); - - /* UTMI_IVREF */ - if (cpu_is_pxa168()) - /* - * fixing Microsoft Altair board interface with NEC hub issue - - * Set UTMI_IVREF from 0x4a3 to 0x4bf - */ - u2o_write(base, UTMI_IVREF, 0x4bf); - - /* calibrate */ - timeout = jiffies + 100; - while ((u2o_read(base, UTMI_PLL) & PLL_READY) == 0) { - if (time_after(jiffies, timeout)) - return -ETIME; - cpu_relax(); - } - - /* toggle VCOCAL_START bit of UTMI_PLL */ - udelay(200); - u2o_set(base, UTMI_PLL, VCOCAL_START); - udelay(40); - u2o_clear(base, UTMI_PLL, VCOCAL_START); - - /* toggle REG_RCAL_START bit of UTMI_TX */ - udelay(200); - u2o_set(base, UTMI_TX, REG_RCAL_START); - udelay(40); - u2o_clear(base, UTMI_TX, REG_RCAL_START); - udelay(200); - - /* make sure phy is ready */ - timeout = jiffies + 100; - while ((u2o_read(base, UTMI_PLL) & PLL_READY) == 0) { - if (time_after(jiffies, timeout)) - return -ETIME; - cpu_relax(); - } - - if (cpu_is_pxa168()) { - u2o_set(base, UTMI_RESERVE, 1 << 5); - /* Turn on UTMI PHY OTG extension */ - u2o_write(base, UTMI_OTG_ADDON, 1); - } - return 0; -} -#else -int mv_udc_phy_init(unsigned int base) -{ - return 0; -} -#endif diff --git a/drivers/usb/gadget/ncm.c b/drivers/usb/gadget/ncm.c index 62ee5087dcac..89530034dff1 100644 --- a/drivers/usb/gadget/ncm.c +++ b/drivers/usb/gadget/ncm.c @@ -14,15 +14,6 @@ * 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 */ /* #define DEBUG */ diff --git a/drivers/usb/gadget/ndis.h b/drivers/usb/gadget/ndis.h index df886cec5ef4..b0e52fc277b4 100644 --- a/drivers/usb/gadget/ndis.h +++ b/drivers/usb/gadget/ndis.h @@ -10,12 +10,6 @@ * * This source code is offered for use in the public domain. You may * use, modify or distribute it freely. - * - * This code is distributed in the hope that it will be useful but - * WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESS OR IMPLIED ARE HEREBY - * DISCLAIMED. This includes but is not limited to warranties of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * */ #ifndef _LINUX_NDIS_H diff --git a/drivers/usb/gadget/net2272.c b/drivers/usb/gadget/net2272.c index ab98ea926a11..d1b76368472f 100644 --- a/drivers/usb/gadget/net2272.c +++ b/drivers/usb/gadget/net2272.c @@ -204,7 +204,7 @@ net2272_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) return -ESHUTDOWN; - max = le16_to_cpu(desc->wMaxPacketSize) & 0x1fff; + max = usb_endpoint_maxp(desc) & 0x1fff; spin_lock_irqsave(&dev->lock, flags); _ep->maxpacket = max & 0x7fff; @@ -1172,17 +1172,18 @@ net2272_pullup(struct usb_gadget *_gadget, int is_on) return 0; } -static int net2272_start(struct usb_gadget_driver *driver, - int (*bind)(struct usb_gadget *)); -static int net2272_stop(struct usb_gadget_driver *driver); +static int net2272_start(struct usb_gadget *_gadget, + struct usb_gadget_driver *driver); +static int net2272_stop(struct usb_gadget *_gadget, + struct usb_gadget_driver *driver); static const struct usb_gadget_ops net2272_ops = { - .get_frame = net2272_get_frame, - .wakeup = net2272_wakeup, + .get_frame = net2272_get_frame, + .wakeup = net2272_wakeup, .set_selfpowered = net2272_set_selfpowered, - .pullup = net2272_pullup, - .start = net2272_start, - .stop = net2272_stop, + .pullup = net2272_pullup, + .udc_start = net2272_start, + .udc_stop = net2272_stop, }; /*---------------------------------------------------------------------------*/ @@ -1356,8 +1357,6 @@ net2272_set_fifo_mode(struct net2272 *dev, int mode) /*---------------------------------------------------------------------------*/ -static struct net2272 *the_controller; - static void net2272_usb_reset(struct net2272 *dev) { @@ -1453,20 +1452,17 @@ net2272_ep0_start(struct net2272 *dev) * disconnect is reported. then a host may connect again, or * the driver might get unbound. */ -static int net2272_start(struct usb_gadget_driver *driver, - int (*bind)(struct usb_gadget *)) +static int net2272_start(struct usb_gadget *_gadget, + struct usb_gadget_driver *driver) { - struct net2272 *dev = the_controller; - int ret; + struct net2272 *dev; unsigned i; - if (!driver || !bind || !driver->unbind || !driver->setup || + if (!driver || !driver->unbind || !driver->setup || driver->speed != USB_SPEED_HIGH) return -EINVAL; - if (!dev) - return -ENODEV; - if (dev->driver) - return -EBUSY; + + dev = container_of(_gadget, struct net2272, gadget); for (i = 0; i < 4; ++i) dev->ep[i].irqs = 0; @@ -1475,14 +1471,6 @@ static int net2272_start(struct usb_gadget_driver *driver, driver->driver.bus = NULL; dev->driver = driver; dev->gadget.dev.driver = &driver->driver; - ret = bind(&dev->gadget); - if (ret) { - dev_dbg(dev->dev, "bind to driver %s --> %d\n", - driver->driver.name, ret); - dev->driver = NULL; - dev->gadget.dev.driver = NULL; - return ret; - } /* ... then enable host detection and ep0; and we're ready * for set_configuration as well as eventual disconnect. @@ -1510,33 +1498,21 @@ stop_activity(struct net2272 *dev, struct usb_gadget_driver *driver) for (i = 0; i < 4; ++i) net2272_dequeue_all(&dev->ep[i]); - /* report disconnect; the driver is already quiesced */ - if (driver) { - spin_unlock(&dev->lock); - driver->disconnect(&dev->gadget); - spin_lock(&dev->lock); - - } net2272_usb_reinit(dev); } -static int net2272_stop(struct usb_gadget_driver *driver) +static int net2272_stop(struct usb_gadget *_gadget, + struct usb_gadget_driver *driver) { - struct net2272 *dev = the_controller; + struct net2272 *dev; unsigned long flags; - if (!dev) - return -ENODEV; - if (!driver || driver != dev->driver) - return -EINVAL; + dev = container_of(_gadget, struct net2272, gadget); spin_lock_irqsave(&dev->lock, flags); stop_activity(dev, driver); spin_unlock_irqrestore(&dev->lock, flags); - net2272_pullup(&dev->gadget, 0); - - driver->unbind(&dev->gadget); dev->gadget.dev.driver = NULL; dev->driver = NULL; @@ -1764,8 +1740,8 @@ net2272_handle_stat0_irqs(struct net2272 *dev, u8 stat) dev->gadget.speed = USB_SPEED_HIGH; else dev->gadget.speed = USB_SPEED_FULL; - dev_dbg(dev->dev, "%s speed\n", - (dev->gadget.speed == USB_SPEED_HIGH) ? "high" : "full"); + dev_dbg(dev->dev, "%s\n", + usb_speed_string(dev->gadget.speed)); } ep = &dev->ep[0]; @@ -2238,7 +2214,6 @@ net2272_remove(struct net2272 *dev) device_remove_file(dev->dev, &dev_attr_registers); dev_info(dev->dev, "unbind\n"); - the_controller = NULL; } static struct net2272 * __devinit @@ -2246,11 +2221,6 @@ net2272_probe_init(struct device *dev, unsigned int irq) { struct net2272 *ret; - if (the_controller) { - dev_warn(dev, "ignoring\n"); - return ERR_PTR(-EBUSY); - } - if (!irq) { dev_dbg(dev, "No IRQ!\n"); return ERR_PTR(-ENODEV); @@ -2307,8 +2277,6 @@ net2272_probe_fin(struct net2272 *dev, unsigned int irqflags) dma_mode_string()); dev_info(dev->dev, "version: %s\n", driver_vers); - the_controller = dev; - ret = device_register(&dev->gadget.dev); if (ret) goto err_irq; @@ -2684,8 +2652,6 @@ net2272_plat_probe(struct platform_device *pdev) dev_info(&pdev->dev, "running in 16-bit, %sbyte swap local bus mode\n", (net2272_read(dev, LOCCTL) & (1 << BYTE_SWAP)) ? "" : "no "); - the_controller = dev; - return 0; err_io: diff --git a/drivers/usb/gadget/net2280.c b/drivers/usb/gadget/net2280.c index a218a4de5dc9..7f1bc9a73cda 100644 --- a/drivers/usb/gadget/net2280.c +++ b/drivers/usb/gadget/net2280.c @@ -33,15 +33,6 @@ * 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 */ #undef DEBUG /* messages on error and most fault paths */ @@ -169,7 +160,7 @@ net2280_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) return -EDOM; /* sanity check ep-e/ep-f since their fifos are small */ - max = le16_to_cpu (desc->wMaxPacketSize) & 0x1fff; + max = usb_endpoint_maxp (desc) & 0x1fff; if (ep->num > 4 && max > 64) return -ERANGE; @@ -1410,17 +1401,18 @@ static int net2280_pullup(struct usb_gadget *_gadget, int is_on) return 0; } -static int net2280_start(struct usb_gadget_driver *driver, - int (*bind)(struct usb_gadget *)); -static int net2280_stop(struct usb_gadget_driver *driver); +static int net2280_start(struct usb_gadget *_gadget, + struct usb_gadget_driver *driver); +static int net2280_stop(struct usb_gadget *_gadget, + struct usb_gadget_driver *driver); static const struct usb_gadget_ops net2280_ops = { .get_frame = net2280_get_frame, .wakeup = net2280_wakeup, .set_selfpowered = net2280_set_selfpowered, .pullup = net2280_pullup, - .start = net2280_start, - .stop = net2280_stop, + .udc_start = net2280_start, + .udc_stop = net2280_stop, }; /*-------------------------------------------------------------------------*/ @@ -1640,7 +1632,7 @@ show_queues (struct device *_dev, struct device_attribute *attr, char *buf) default: val = "iso"; break; }; val; }), - le16_to_cpu (d->wMaxPacketSize) & 0x1fff, + usb_endpoint_maxp (d) & 0x1fff, ep->dma ? "dma" : "pio", ep->fifo_size ); } else /* ep0 should only have one transfer queued */ @@ -1753,8 +1745,6 @@ static void set_fifo_mode (struct net2280 *dev, int mode) * perhaps to bind specific drivers to specific devices. */ -static struct net2280 *the_controller; - static void usb_reset (struct net2280 *dev) { u32 tmp; @@ -1880,10 +1870,10 @@ static void ep0_start (struct net2280 *dev) * disconnect is reported. then a host may connect again, or * the driver might get unbound. */ -static int net2280_start(struct usb_gadget_driver *driver, - int (*bind)(struct usb_gadget *)) +static int net2280_start(struct usb_gadget *_gadget, + struct usb_gadget_driver *driver) { - struct net2280 *dev = the_controller; + struct net2280 *dev; int retval; unsigned i; @@ -1891,14 +1881,11 @@ static int net2280_start(struct usb_gadget_driver *driver, * (dev->usb->xcvrdiag & FORCE_FULL_SPEED_MODE) * "must not be used in normal operation" */ - if (!driver - || driver->speed != USB_SPEED_HIGH - || !bind || !driver->setup) + if (!driver || driver->speed != USB_SPEED_HIGH + || !driver->setup) return -EINVAL; - if (!dev) - return -ENODEV; - if (dev->driver) - return -EBUSY; + + dev = container_of (_gadget, struct net2280, gadget); for (i = 0; i < 7; i++) dev->ep [i].irqs = 0; @@ -1908,14 +1895,6 @@ static int net2280_start(struct usb_gadget_driver *driver, driver->driver.bus = NULL; dev->driver = driver; dev->gadget.dev.driver = &driver->driver; - retval = bind(&dev->gadget); - if (retval) { - DEBUG (dev, "bind to driver %s --> %d\n", - driver->driver.name, retval); - dev->driver = NULL; - dev->gadget.dev.driver = NULL; - return retval; - } retval = device_create_file (&dev->pdev->dev, &dev_attr_function); if (retval) goto err_unbind; @@ -1961,33 +1940,21 @@ stop_activity (struct net2280 *dev, struct usb_gadget_driver *driver) for (i = 0; i < 7; i++) nuke (&dev->ep [i]); - /* report disconnect; the driver is already quiesced */ - if (driver) { - spin_unlock (&dev->lock); - driver->disconnect (&dev->gadget); - spin_lock (&dev->lock); - } - usb_reinit (dev); } -static int net2280_stop(struct usb_gadget_driver *driver) +static int net2280_stop(struct usb_gadget *_gadget, + struct usb_gadget_driver *driver) { - struct net2280 *dev = the_controller; + struct net2280 *dev; unsigned long flags; - if (!dev) - return -ENODEV; - if (!driver || driver != dev->driver || !driver->unbind) - return -EINVAL; + dev = container_of (_gadget, struct net2280, gadget); spin_lock_irqsave (&dev->lock, flags); stop_activity (dev, driver); spin_unlock_irqrestore (&dev->lock, flags); - net2280_pullup (&dev->gadget, 0); - - driver->unbind (&dev->gadget); dev->gadget.dev.driver = NULL; dev->driver = NULL; @@ -2266,9 +2233,7 @@ static void handle_stat0_irqs (struct net2280 *dev, u32 stat) else dev->gadget.speed = USB_SPEED_FULL; net2280_led_speed (dev, dev->gadget.speed); - DEBUG (dev, "%s speed\n", - (dev->gadget.speed == USB_SPEED_HIGH) - ? "high" : "full"); + DEBUG(dev, "%s\n", usb_speed_string(dev->gadget.speed)); } ep = &dev->ep [0]; @@ -2709,8 +2674,6 @@ static void net2280_remove (struct pci_dev *pdev) pci_set_drvdata (pdev, NULL); INFO (dev, "unbind\n"); - - the_controller = NULL; } /* wrap this driver around the specified device, but @@ -2724,14 +2687,6 @@ static int net2280_probe (struct pci_dev *pdev, const struct pci_device_id *id) void __iomem *base = NULL; int retval, i; - /* if you want to support more than one controller in a system, - * usb_gadget_driver_{register,unregister}() must change. - */ - if (the_controller) { - dev_warn (&pdev->dev, "ignoring\n"); - return -EBUSY; - } - /* alloc, and start init */ dev = kzalloc (sizeof *dev, GFP_KERNEL); if (dev == NULL){ @@ -2858,8 +2813,6 @@ static int net2280_probe (struct pci_dev *pdev, const struct pci_device_id *id) use_dma ? (use_dma_chaining ? "chaining" : "enabled") : "disabled"); - the_controller = dev; - retval = device_register (&dev->gadget.dev); if (retval) goto done; retval = device_create_file (&pdev->dev, &dev_attr_registers); diff --git a/drivers/usb/gadget/net2280.h b/drivers/usb/gadget/net2280.h index c36852263d93..a844be0d683a 100644 --- a/drivers/usb/gadget/net2280.h +++ b/drivers/usb/gadget/net2280.h @@ -11,15 +11,6 @@ * 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 */ #include <linux/usb/net2280.h> diff --git a/drivers/usb/gadget/omap_udc.c b/drivers/usb/gadget/omap_udc.c index 740c7daed279..788989a10223 100644 --- a/drivers/usb/gadget/omap_udc.c +++ b/drivers/usb/gadget/omap_udc.c @@ -10,15 +10,6 @@ * 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 */ #undef DEBUG @@ -166,15 +157,14 @@ static int omap_ep_enable(struct usb_ep *_ep, if (!_ep || !desc || ep->desc || desc->bDescriptorType != USB_DT_ENDPOINT || ep->bEndpointAddress != desc->bEndpointAddress - || ep->maxpacket < le16_to_cpu - (desc->wMaxPacketSize)) { + || ep->maxpacket < usb_endpoint_maxp(desc)) { DBG("%s, bad ep or descriptor\n", __func__); return -EINVAL; } - maxp = le16_to_cpu (desc->wMaxPacketSize); + maxp = usb_endpoint_maxp(desc); if ((desc->bmAttributes == USB_ENDPOINT_XFER_BULK && maxp != ep->maxpacket) - || le16_to_cpu(desc->wMaxPacketSize) > ep->maxpacket + || usb_endpoint_maxp(desc) > ep->maxpacket || !desc->wMaxPacketSize) { DBG("%s, bad %s maxpacket\n", __func__, _ep->name); return -ERANGE; @@ -2968,7 +2958,7 @@ known: } #ifdef USE_ISO status = request_irq(pdev->resource[3].start, omap_udc_iso_irq, - IRQF_DISABLED, "omap_udc iso", udc); + 0, "omap_udc iso", udc); if (status != 0) { ERR("can't get irq %d, err %d\n", (int) pdev->resource[3].start, status); diff --git a/drivers/usb/gadget/pch_udc.c b/drivers/usb/gadget/pch_udc.c index f96615ab6b77..550d6dcdf104 100644 --- a/drivers/usb/gadget/pch_udc.c +++ b/drivers/usb/gadget/pch_udc.c @@ -4,15 +4,6 @@ * 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; version 2 of the License. - * - * 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. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/kernel.h> @@ -947,7 +938,7 @@ static void pch_udc_ep_enable(struct pch_udc_ep *ep, else buff_size = UDC_EPOUT_BUFF_SIZE; pch_udc_ep_set_bufsz(ep, buff_size, ep->in); - pch_udc_ep_set_maxpkt(ep, le16_to_cpu(desc->wMaxPacketSize)); + pch_udc_ep_set_maxpkt(ep, usb_endpoint_maxp(desc)); pch_udc_ep_set_nak(ep); pch_udc_ep_fifo_flush(ep, ep->in); /* Configure the endpoint */ @@ -957,7 +948,7 @@ static void pch_udc_ep_enable(struct pch_udc_ep *ep, (cfg->cur_cfg << UDC_CSR_NE_CFG_SHIFT) | (cfg->cur_intf << UDC_CSR_NE_INTF_SHIFT) | (cfg->cur_alt << UDC_CSR_NE_ALT_SHIFT) | - le16_to_cpu(desc->wMaxPacketSize) << UDC_CSR_NE_MAX_PKT_SHIFT; + usb_endpoint_maxp(desc) << UDC_CSR_NE_MAX_PKT_SHIFT; if (ep->in) pch_udc_write_csr(ep->dev, val, UDC_EPIN_IDX(ep->num)); @@ -1466,7 +1457,7 @@ static int pch_udc_pcd_ep_enable(struct usb_ep *usbep, ep->desc = desc; ep->halted = 0; pch_udc_ep_enable(ep, &ep->dev->cfg_data, desc); - ep->ep.maxpacket = le16_to_cpu(desc->wMaxPacketSize); + ep->ep.maxpacket = usb_endpoint_maxp(desc); pch_udc_enable_ep_interrupts(ep->dev, PCH_UDC_EPINT(ep->in, ep->num)); spin_unlock_irqrestore(&dev->lock, iflags); return 0; diff --git a/drivers/usb/gadget/printer.c b/drivers/usb/gadget/printer.c index a341dde6f9c3..65a8834f274b 100644 --- a/drivers/usb/gadget/printer.c +++ b/drivers/usb/gadget/printer.c @@ -8,15 +8,6 @@ * 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 */ #include <linux/module.h> @@ -971,23 +962,15 @@ printer_set_config(struct printer_dev *dev, unsigned number) usb_gadget_vbus_draw(dev->gadget, dev->gadget->is_otg ? 8 : 100); } else { - char *speed; unsigned power; power = 2 * config_desc.bMaxPower; usb_gadget_vbus_draw(dev->gadget, power); - switch (gadget->speed) { - case USB_SPEED_FULL: speed = "full"; break; -#ifdef CONFIG_USB_GADGET_DUALSPEED - case USB_SPEED_HIGH: speed = "high"; break; -#endif - default: speed = "?"; break; - } - dev->config = number; - INFO(dev, "%s speed config #%d: %d mA, %s\n", - speed, number, power, driver_desc); + INFO(dev, "%s config #%d: %d mA, %s\n", + usb_speed_string(gadget->speed), + number, power, driver_desc); } return result; } @@ -1611,7 +1594,7 @@ cleanup(void) if (status) ERROR(dev, "usb_gadget_unregister_driver %x\n", status); - unregister_chrdev_region(g_printer_devno, 2); + unregister_chrdev_region(g_printer_devno, 1); class_destroy(usb_gadget_class); mutex_unlock(&usb_printer_gadget.lock_printer_io); } diff --git a/drivers/usb/gadget/pxa25x_udc.c b/drivers/usb/gadget/pxa25x_udc.c index e4e59b4de25d..c090a7e3ecf8 100644 --- a/drivers/usb/gadget/pxa25x_udc.c +++ b/drivers/usb/gadget/pxa25x_udc.c @@ -11,16 +11,6 @@ * 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 - * */ /* #define VERBOSE_DEBUG */ @@ -232,8 +222,7 @@ static int pxa25x_ep_enable (struct usb_ep *_ep, if (!_ep || !desc || ep->desc || _ep->name == ep0name || desc->bDescriptorType != USB_DT_ENDPOINT || ep->bEndpointAddress != desc->bEndpointAddress - || ep->fifo_size < le16_to_cpu - (desc->wMaxPacketSize)) { + || ep->fifo_size < usb_endpoint_maxp (desc)) { DMSG("%s, bad ep or descriptor\n", __func__); return -EINVAL; } @@ -248,7 +237,7 @@ static int pxa25x_ep_enable (struct usb_ep *_ep, /* hardware _could_ do smaller, but driver doesn't */ if ((desc->bmAttributes == USB_ENDPOINT_XFER_BULK - && le16_to_cpu (desc->wMaxPacketSize) + && usb_endpoint_maxp (desc) != BULK_FIFO_SIZE) || !desc->wMaxPacketSize) { DMSG("%s, bad %s maxpacket\n", __func__, _ep->name); @@ -264,7 +253,7 @@ static int pxa25x_ep_enable (struct usb_ep *_ep, ep->desc = desc; ep->stopped = 0; ep->pio_irqs = 0; - ep->ep.maxpacket = le16_to_cpu (desc->wMaxPacketSize); + ep->ep.maxpacket = usb_endpoint_maxp (desc); /* flush fifo (mostly for OUT buffers) */ pxa25x_ep_fifo_flush (_ep); @@ -401,7 +390,7 @@ write_fifo (struct pxa25x_ep *ep, struct pxa25x_request *req) { unsigned max; - max = le16_to_cpu(ep->desc->wMaxPacketSize); + max = usb_endpoint_maxp(ep->desc); do { unsigned count; int is_last, is_short; @@ -671,8 +660,7 @@ pxa25x_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) * we can report per-packet status. that also helps with dma. */ if (unlikely (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC - && req->req.length > le16_to_cpu - (ep->desc->wMaxPacketSize))) + && req->req.length > usb_endpoint_maxp (ep->desc))) return -EMSGSIZE; DBG(DBG_NOISY, "%s queue req %p, len %d buf %p\n", @@ -1105,7 +1093,7 @@ udc_seq_show(struct seq_file *m, void *_d) tmp = *dev->ep [i].reg_udccs; seq_printf(m, "%s max %d %s udccs %02x irqs %lu\n", - ep->ep.name, le16_to_cpu(desc->wMaxPacketSize), + ep->ep.name, usb_endpoint_maxp(desc), "pio", tmp, ep->pio_irqs); /* TODO translate all five groups of udccs bits! */ @@ -2202,7 +2190,7 @@ static int __init pxa25x_udc_probe(struct platform_device *pdev) /* irq setup after old hardware state is cleaned up */ retval = request_irq(irq, pxa25x_udc_irq, - IRQF_DISABLED, driver_name, dev); + 0, driver_name, dev); if (retval != 0) { pr_err("%s: can't get irq %d, err %d\n", driver_name, irq, retval); @@ -2214,7 +2202,7 @@ static int __init pxa25x_udc_probe(struct platform_device *pdev) if (machine_is_lubbock()) { retval = request_irq(LUBBOCK_USB_DISC_IRQ, lubbock_vbus_irq, - IRQF_DISABLED | IRQF_SAMPLE_RANDOM, + IRQF_SAMPLE_RANDOM, driver_name, dev); if (retval != 0) { pr_err("%s: can't get irq %i, err %d\n", @@ -2223,7 +2211,7 @@ static int __init pxa25x_udc_probe(struct platform_device *pdev) } retval = request_irq(LUBBOCK_USB_IRQ, lubbock_vbus_irq, - IRQF_DISABLED | IRQF_SAMPLE_RANDOM, + IRQF_SAMPLE_RANDOM, driver_name, dev); if (retval != 0) { pr_err("%s: can't get irq %i, err %d\n", diff --git a/drivers/usb/gadget/pxa25x_udc.h b/drivers/usb/gadget/pxa25x_udc.h index f572c5617462..8eaf4e43726b 100644 --- a/drivers/usb/gadget/pxa25x_udc.h +++ b/drivers/usb/gadget/pxa25x_udc.h @@ -9,15 +9,6 @@ * 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 */ #ifndef __LINUX_USB_GADGET_PXA25X_H @@ -161,8 +152,6 @@ static struct pxa25x_udc *the_controller; #ifdef DEBUG -static int is_vbus_present(void); - static const char *state_name[] = { "EP0_IDLE", "EP0_IN_DATA_PHASE", "EP0_OUT_DATA_PHASE", @@ -214,8 +203,7 @@ dump_state(struct pxa25x_udc *dev) u32 tmp; unsigned i; - DMSG("%s %s, uicr %02X.%02X, usir %02X.%02x, ufnr %02X.%02X\n", - is_vbus_present() ? "host " : "disconnected", + DMSG("%s, uicr %02X.%02X, usir %02X.%02x, ufnr %02X.%02X\n", state_name[dev->ep0state], UICR1, UICR0, USIR1, USIR0, UFNRH, UFNRL); dump_udccr("udccr"); @@ -232,9 +220,6 @@ dump_state(struct pxa25x_udc *dev) } else DMSG("ep0 driver '%s'\n", dev->driver->driver.name); - if (!is_vbus_present()) - return; - dump_udccs0 ("udccs0"); DMSG("ep0 IN %lu/%lu, OUT %lu/%lu\n", dev->stats.write.bytes, dev->stats.write.ops, diff --git a/drivers/usb/gadget/pxa27x_udc.c b/drivers/usb/gadget/pxa27x_udc.c index 85b68c75dc9d..18b6b091f2a6 100644 --- a/drivers/usb/gadget/pxa27x_udc.c +++ b/drivers/usb/gadget/pxa27x_udc.c @@ -8,16 +8,6 @@ * 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 - * */ #include <linux/module.h> #include <linux/kernel.h> @@ -1439,7 +1429,7 @@ static int pxa_ep_enable(struct usb_ep *_ep, return -EINVAL; } - if (ep->fifo_size < le16_to_cpu(desc->wMaxPacketSize)) { + if (ep->fifo_size < usb_endpoint_maxp(desc)) { ep_err(ep, "bad maxpacket\n"); return -ERANGE; } diff --git a/drivers/usb/gadget/pxa27x_udc.h b/drivers/usb/gadget/pxa27x_udc.h index b01696eab068..7f4e8f424e80 100644 --- a/drivers/usb/gadget/pxa27x_udc.h +++ b/drivers/usb/gadget/pxa27x_udc.h @@ -9,15 +9,6 @@ * 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 */ #ifndef __LINUX_USB_GADGET_PXA27X_H diff --git a/drivers/usb/gadget/r8a66597-udc.c b/drivers/usb/gadget/r8a66597-udc.c index 50991e5bd5e8..68a826a1b866 100644 --- a/drivers/usb/gadget/r8a66597-udc.c +++ b/drivers/usb/gadget/r8a66597-udc.c @@ -8,16 +8,6 @@ * 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; version 2 of the License. - * - * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * */ #include <linux/module.h> @@ -28,13 +18,14 @@ #include <linux/clk.h> #include <linux/err.h> #include <linux/slab.h> +#include <linux/dma-mapping.h> #include <linux/usb/ch9.h> #include <linux/usb/gadget.h> #include "r8a66597-udc.h" -#define DRIVER_VERSION "2009-08-18" +#define DRIVER_VERSION "2011-09-26" static const char udc_name[] = "r8a66597_udc"; static const char *r8a66597_ep_name[] = { @@ -115,13 +106,15 @@ static inline u16 control_reg_get_pid(struct r8a66597 *r8a66597, u16 pipenum) u16 pid = 0; unsigned long offset; - if (pipenum == 0) + if (pipenum == 0) { pid = r8a66597_read(r8a66597, DCPCTR) & PID; - else if (pipenum < R8A66597_MAX_NUM_PIPE) { + } else if (pipenum < R8A66597_MAX_NUM_PIPE) { offset = get_pipectr_addr(pipenum); pid = r8a66597_read(r8a66597, offset) & PID; - } else - printk(KERN_ERR "unexpect pipe num (%d)\n", pipenum); + } else { + dev_err(r8a66597_to_dev(r8a66597), "unexpect pipe num (%d)\n", + pipenum); + } return pid; } @@ -131,13 +124,15 @@ static inline void control_reg_set_pid(struct r8a66597 *r8a66597, u16 pipenum, { unsigned long offset; - if (pipenum == 0) + if (pipenum == 0) { r8a66597_mdfy(r8a66597, pid, PID, DCPCTR); - else if (pipenum < R8A66597_MAX_NUM_PIPE) { + } else if (pipenum < R8A66597_MAX_NUM_PIPE) { offset = get_pipectr_addr(pipenum); r8a66597_mdfy(r8a66597, pid, PID, offset); - } else - printk(KERN_ERR "unexpect pipe num (%d)\n", pipenum); + } else { + dev_err(r8a66597_to_dev(r8a66597), "unexpect pipe num (%d)\n", + pipenum); + } } static inline void pipe_start(struct r8a66597 *r8a66597, u16 pipenum) @@ -160,13 +155,15 @@ static inline u16 control_reg_get(struct r8a66597 *r8a66597, u16 pipenum) u16 ret = 0; unsigned long offset; - if (pipenum == 0) + if (pipenum == 0) { ret = r8a66597_read(r8a66597, DCPCTR); - else if (pipenum < R8A66597_MAX_NUM_PIPE) { + } else if (pipenum < R8A66597_MAX_NUM_PIPE) { offset = get_pipectr_addr(pipenum); ret = r8a66597_read(r8a66597, offset); - } else - printk(KERN_ERR "unexpect pipe num (%d)\n", pipenum); + } else { + dev_err(r8a66597_to_dev(r8a66597), "unexpect pipe num (%d)\n", + pipenum); + } return ret; } @@ -177,13 +174,63 @@ static inline void control_reg_sqclr(struct r8a66597 *r8a66597, u16 pipenum) pipe_stop(r8a66597, pipenum); - if (pipenum == 0) + if (pipenum == 0) { r8a66597_bset(r8a66597, SQCLR, DCPCTR); - else if (pipenum < R8A66597_MAX_NUM_PIPE) { + } else if (pipenum < R8A66597_MAX_NUM_PIPE) { offset = get_pipectr_addr(pipenum); r8a66597_bset(r8a66597, SQCLR, offset); - } else - printk(KERN_ERR "unexpect pipe num(%d)\n", pipenum); + } else { + dev_err(r8a66597_to_dev(r8a66597), "unexpect pipe num (%d)\n", + pipenum); + } +} + +static void control_reg_sqset(struct r8a66597 *r8a66597, u16 pipenum) +{ + unsigned long offset; + + pipe_stop(r8a66597, pipenum); + + if (pipenum == 0) { + r8a66597_bset(r8a66597, SQSET, DCPCTR); + } else if (pipenum < R8A66597_MAX_NUM_PIPE) { + offset = get_pipectr_addr(pipenum); + r8a66597_bset(r8a66597, SQSET, offset); + } else { + dev_err(r8a66597_to_dev(r8a66597), + "unexpect pipe num(%d)\n", pipenum); + } +} + +static u16 control_reg_sqmon(struct r8a66597 *r8a66597, u16 pipenum) +{ + unsigned long offset; + + if (pipenum == 0) { + return r8a66597_read(r8a66597, DCPCTR) & SQMON; + } else if (pipenum < R8A66597_MAX_NUM_PIPE) { + offset = get_pipectr_addr(pipenum); + return r8a66597_read(r8a66597, offset) & SQMON; + } else { + dev_err(r8a66597_to_dev(r8a66597), + "unexpect pipe num(%d)\n", pipenum); + } + + return 0; +} + +static u16 save_usb_toggle(struct r8a66597 *r8a66597, u16 pipenum) +{ + return control_reg_sqmon(r8a66597, pipenum); +} + +static void restore_usb_toggle(struct r8a66597 *r8a66597, u16 pipenum, + u16 toggle) +{ + if (toggle) + control_reg_sqset(r8a66597, pipenum); + else + control_reg_sqclr(r8a66597, pipenum); } static inline int get_buffer_size(struct r8a66597 *r8a66597, u16 pipenum) @@ -222,18 +269,51 @@ static inline unsigned short mbw_value(struct r8a66597 *r8a66597) return MBW_16; } +static void r8a66597_change_curpipe(struct r8a66597 *r8a66597, u16 pipenum, + u16 isel, u16 fifosel) +{ + u16 tmp, mask, loop; + int i = 0; + + if (!pipenum) { + mask = ISEL | CURPIPE; + loop = isel; + } else { + mask = CURPIPE; + loop = pipenum; + } + r8a66597_mdfy(r8a66597, loop, mask, fifosel); + + do { + tmp = r8a66597_read(r8a66597, fifosel); + if (i++ > 1000000) { + dev_err(r8a66597_to_dev(r8a66597), + "r8a66597: register%x, loop %x " + "is timeout\n", fifosel, loop); + break; + } + ndelay(1); + } while ((tmp & mask) != loop); +} + static inline void pipe_change(struct r8a66597 *r8a66597, u16 pipenum) { struct r8a66597_ep *ep = r8a66597->pipenum2ep[pipenum]; if (ep->use_dma) - return; + r8a66597_bclr(r8a66597, DREQE, ep->fifosel); r8a66597_mdfy(r8a66597, pipenum, CURPIPE, ep->fifosel); ndelay(450); - r8a66597_bset(r8a66597, mbw_value(r8a66597), ep->fifosel); + if (r8a66597_is_sudmac(r8a66597) && ep->use_dma) + r8a66597_bclr(r8a66597, mbw_value(r8a66597), ep->fifosel); + else + r8a66597_bset(r8a66597, mbw_value(r8a66597), ep->fifosel); + + if (ep->use_dma) + r8a66597_bset(r8a66597, DREQE, ep->fifosel); } static int pipe_buffer_setting(struct r8a66597 *r8a66597, @@ -297,17 +377,18 @@ static void pipe_buffer_release(struct r8a66597 *r8a66597, if (info->pipe == 0) return; - if (is_bulk_pipe(info->pipe)) + if (is_bulk_pipe(info->pipe)) { r8a66597->bulk--; - else if (is_interrupt_pipe(info->pipe)) + } else if (is_interrupt_pipe(info->pipe)) { r8a66597->interrupt--; - else if (is_isoc_pipe(info->pipe)) { + } else if (is_isoc_pipe(info->pipe)) { r8a66597->isochronous--; if (info->type == R8A66597_BULK) r8a66597->bulk--; - } else - printk(KERN_ERR "ep_release: unexpect pipenum (%d)\n", - info->pipe); + } else { + dev_err(r8a66597_to_dev(r8a66597), + "ep_release: unexpect pipenum (%d)\n", info->pipe); + } } static void pipe_initialize(struct r8a66597_ep *ep) @@ -337,11 +418,17 @@ static void r8a66597_ep_setting(struct r8a66597 *r8a66597, ep->fifoaddr = CFIFO; ep->fifosel = CFIFOSEL; ep->fifoctr = CFIFOCTR; - ep->fifotrn = 0; ep->pipectr = get_pipectr_addr(pipenum); + if (is_bulk_pipe(pipenum) || is_isoc_pipe(pipenum)) { + ep->pipetre = get_pipetre_addr(pipenum); + ep->pipetrn = get_pipetrn_addr(pipenum); + } else { + ep->pipetre = 0; + ep->pipetrn = 0; + } ep->pipenum = pipenum; - ep->ep.maxpacket = le16_to_cpu(desc->wMaxPacketSize); + ep->ep.maxpacket = usb_endpoint_maxp(desc); r8a66597->pipenum2ep[pipenum] = ep; r8a66597->epaddr2ep[desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK] = ep; @@ -381,7 +468,8 @@ static int alloc_pipe_config(struct r8a66597_ep *ep, case USB_ENDPOINT_XFER_BULK: if (r8a66597->bulk >= R8A66597_MAX_NUM_BULK) { if (r8a66597->isochronous >= R8A66597_MAX_NUM_ISOC) { - printk(KERN_ERR "bulk pipe is insufficient\n"); + dev_err(r8a66597_to_dev(r8a66597), + "bulk pipe is insufficient\n"); return -ENODEV; } else { info.pipe = R8A66597_BASE_PIPENUM_ISOC @@ -397,7 +485,8 @@ static int alloc_pipe_config(struct r8a66597_ep *ep, break; case USB_ENDPOINT_XFER_INT: if (r8a66597->interrupt >= R8A66597_MAX_NUM_INT) { - printk(KERN_ERR "interrupt pipe is insufficient\n"); + dev_err(r8a66597_to_dev(r8a66597), + "interrupt pipe is insufficient\n"); return -ENODEV; } info.pipe = R8A66597_BASE_PIPENUM_INT + r8a66597->interrupt; @@ -406,7 +495,8 @@ static int alloc_pipe_config(struct r8a66597_ep *ep, break; case USB_ENDPOINT_XFER_ISOC: if (r8a66597->isochronous >= R8A66597_MAX_NUM_ISOC) { - printk(KERN_ERR "isochronous pipe is insufficient\n"); + dev_err(r8a66597_to_dev(r8a66597), + "isochronous pipe is insufficient\n"); return -ENODEV; } info.pipe = R8A66597_BASE_PIPENUM_ISOC + r8a66597->isochronous; @@ -414,13 +504,13 @@ static int alloc_pipe_config(struct r8a66597_ep *ep, counter = &r8a66597->isochronous; break; default: - printk(KERN_ERR "unexpect xfer type\n"); + dev_err(r8a66597_to_dev(r8a66597), "unexpect xfer type\n"); return -EINVAL; } ep->type = info.type; info.epnum = desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; - info.maxpacket = le16_to_cpu(desc->wMaxPacketSize); + info.maxpacket = usb_endpoint_maxp(desc); info.interval = desc->bInterval; if (desc->bEndpointAddress & USB_ENDPOINT_DIR_MASK) info.dir_in = 1; @@ -429,7 +519,8 @@ static int alloc_pipe_config(struct r8a66597_ep *ep, ret = pipe_buffer_setting(r8a66597, &info); if (ret < 0) { - printk(KERN_ERR "pipe_buffer_setting fail\n"); + dev_err(r8a66597_to_dev(r8a66597), + "pipe_buffer_setting fail\n"); return ret; } @@ -495,6 +586,124 @@ static void start_ep0_write(struct r8a66597_ep *ep, } } +static void disable_fifosel(struct r8a66597 *r8a66597, u16 pipenum, + u16 fifosel) +{ + u16 tmp; + + tmp = r8a66597_read(r8a66597, fifosel) & CURPIPE; + if (tmp == pipenum) + r8a66597_change_curpipe(r8a66597, 0, 0, fifosel); +} + +static void change_bfre_mode(struct r8a66597 *r8a66597, u16 pipenum, + int enable) +{ + struct r8a66597_ep *ep = r8a66597->pipenum2ep[pipenum]; + u16 tmp, toggle; + + /* check current BFRE bit */ + r8a66597_write(r8a66597, pipenum, PIPESEL); + tmp = r8a66597_read(r8a66597, PIPECFG) & R8A66597_BFRE; + if ((enable && tmp) || (!enable && !tmp)) + return; + + /* change BFRE bit */ + pipe_stop(r8a66597, pipenum); + disable_fifosel(r8a66597, pipenum, CFIFOSEL); + disable_fifosel(r8a66597, pipenum, D0FIFOSEL); + disable_fifosel(r8a66597, pipenum, D1FIFOSEL); + + toggle = save_usb_toggle(r8a66597, pipenum); + + r8a66597_write(r8a66597, pipenum, PIPESEL); + if (enable) + r8a66597_bset(r8a66597, R8A66597_BFRE, PIPECFG); + else + r8a66597_bclr(r8a66597, R8A66597_BFRE, PIPECFG); + + /* initialize for internal BFRE flag */ + r8a66597_bset(r8a66597, ACLRM, ep->pipectr); + r8a66597_bclr(r8a66597, ACLRM, ep->pipectr); + + restore_usb_toggle(r8a66597, pipenum, toggle); +} + +static int sudmac_alloc_channel(struct r8a66597 *r8a66597, + struct r8a66597_ep *ep, + struct r8a66597_request *req) +{ + struct r8a66597_dma *dma; + + if (!r8a66597_is_sudmac(r8a66597)) + return -ENODEV; + + /* Check transfer type */ + if (!is_bulk_pipe(ep->pipenum)) + return -EIO; + + if (r8a66597->dma.used) + return -EBUSY; + + /* set SUDMAC parameters */ + dma = &r8a66597->dma; + dma->used = 1; + if (ep->desc->bEndpointAddress & USB_DIR_IN) { + dma->dir = 1; + } else { + dma->dir = 0; + change_bfre_mode(r8a66597, ep->pipenum, 1); + } + + /* set r8a66597_ep paramters */ + ep->use_dma = 1; + ep->dma = dma; + ep->fifoaddr = D0FIFO; + ep->fifosel = D0FIFOSEL; + ep->fifoctr = D0FIFOCTR; + + /* dma mapping */ + req->req.dma = dma_map_single(r8a66597_to_dev(ep->r8a66597), + req->req.buf, req->req.length, + dma->dir ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + + return 0; +} + +static void sudmac_free_channel(struct r8a66597 *r8a66597, + struct r8a66597_ep *ep, + struct r8a66597_request *req) +{ + if (!r8a66597_is_sudmac(r8a66597)) + return; + + dma_unmap_single(r8a66597_to_dev(ep->r8a66597), + req->req.dma, req->req.length, + ep->dma->dir ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + + r8a66597_bclr(r8a66597, DREQE, ep->fifosel); + r8a66597_change_curpipe(r8a66597, 0, 0, ep->fifosel); + + ep->dma->used = 0; + ep->use_dma = 0; + ep->fifoaddr = CFIFO; + ep->fifosel = CFIFOSEL; + ep->fifoctr = CFIFOCTR; +} + +static void sudmac_start(struct r8a66597 *r8a66597, struct r8a66597_ep *ep, + struct r8a66597_request *req) +{ + BUG_ON(req->req.length == 0); + + r8a66597_sudmac_write(r8a66597, LBA_WAIT, CH0CFG); + r8a66597_sudmac_write(r8a66597, req->req.dma, CH0BA); + r8a66597_sudmac_write(r8a66597, req->req.length, CH0BBC); + r8a66597_sudmac_write(r8a66597, CH0ENDE, DINTCTRL); + + r8a66597_sudmac_write(r8a66597, DEN, CH0DEN); +} + static void start_packet_write(struct r8a66597_ep *ep, struct r8a66597_request *req) { @@ -505,11 +714,29 @@ static void start_packet_write(struct r8a66597_ep *ep, disable_irq_empty(r8a66597, ep->pipenum); pipe_start(r8a66597, ep->pipenum); - tmp = r8a66597_read(r8a66597, ep->fifoctr); - if (unlikely((tmp & FRDY) == 0)) - pipe_irq_enable(r8a66597, ep->pipenum); - else - irq_packet_write(ep, req); + if (req->req.length == 0) { + transfer_complete(ep, req, 0); + } else { + r8a66597_write(r8a66597, ~(1 << ep->pipenum), BRDYSTS); + if (sudmac_alloc_channel(r8a66597, ep, req) < 0) { + /* PIO mode */ + pipe_change(r8a66597, ep->pipenum); + disable_irq_empty(r8a66597, ep->pipenum); + pipe_start(r8a66597, ep->pipenum); + tmp = r8a66597_read(r8a66597, ep->fifoctr); + if (unlikely((tmp & FRDY) == 0)) + pipe_irq_enable(r8a66597, ep->pipenum); + else + irq_packet_write(ep, req); + } else { + /* DMA mode */ + pipe_change(r8a66597, ep->pipenum); + disable_irq_nrdy(r8a66597, ep->pipenum); + pipe_start(r8a66597, ep->pipenum); + enable_irq_nrdy(r8a66597, ep->pipenum); + sudmac_start(r8a66597, ep, req); + } + } } static void start_packet_read(struct r8a66597_ep *ep, @@ -524,17 +751,26 @@ static void start_packet_read(struct r8a66597_ep *ep, pipe_start(r8a66597, pipenum); pipe_irq_enable(r8a66597, pipenum); } else { - if (ep->use_dma) { - r8a66597_bset(r8a66597, TRCLR, ep->fifosel); - pipe_change(r8a66597, pipenum); - r8a66597_bset(r8a66597, TRENB, ep->fifosel); + pipe_stop(r8a66597, pipenum); + if (ep->pipetre) { + enable_irq_nrdy(r8a66597, pipenum); + r8a66597_write(r8a66597, TRCLR, ep->pipetre); r8a66597_write(r8a66597, - (req->req.length + ep->ep.maxpacket - 1) - / ep->ep.maxpacket, - ep->fifotrn); + DIV_ROUND_UP(req->req.length, ep->ep.maxpacket), + ep->pipetrn); + r8a66597_bset(r8a66597, TRENB, ep->pipetre); + } + + if (sudmac_alloc_channel(r8a66597, ep, req) < 0) { + /* PIO mode */ + change_bfre_mode(r8a66597, ep->pipenum, 0); + pipe_start(r8a66597, pipenum); /* trigger once */ + pipe_irq_enable(r8a66597, pipenum); + } else { + pipe_change(r8a66597, pipenum); + sudmac_start(r8a66597, ep, req); + pipe_start(r8a66597, pipenum); /* trigger once */ } - pipe_start(r8a66597, pipenum); /* trigger once */ - pipe_irq_enable(r8a66597, pipenum); } } @@ -564,7 +800,8 @@ static void start_ep0(struct r8a66597_ep *ep, struct r8a66597_request *req) control_end(ep->r8a66597, 0); break; default: - printk(KERN_ERR "start_ep0: unexpect ctsq(%x)\n", ctsq); + dev_err(r8a66597_to_dev(ep->r8a66597), + "start_ep0: unexpect ctsq(%x)\n", ctsq); break; } } @@ -690,6 +927,9 @@ __acquires(r8a66597->lock) if (!list_empty(&ep->queue)) restart = 1; + if (ep->use_dma) + sudmac_free_channel(ep->r8a66597, ep, req); + spin_unlock(&ep->r8a66597->lock); req->req.complete(&ep->ep, &req->req); spin_lock(&ep->r8a66597->lock); @@ -718,7 +958,8 @@ static void irq_ep0_write(struct r8a66597_ep *ep, struct r8a66597_request *req) do { tmp = r8a66597_read(r8a66597, ep->fifoctr); if (i++ > 100000) { - printk(KERN_ERR "pipe0 is busy. maybe cpu i/o bus" + dev_err(r8a66597_to_dev(r8a66597), + "pipe0 is busy. maybe cpu i/o bus " "conflict. please power off this controller."); return; } @@ -733,7 +974,7 @@ static void irq_ep0_write(struct r8a66597_ep *ep, struct r8a66597_request *req) /* write fifo */ if (req->req.buf) { if (size > 0) - r8a66597_write_fifo(r8a66597, ep->fifoaddr, buf, size); + r8a66597_write_fifo(r8a66597, ep, buf, size); if ((size == 0) || ((size % ep->ep.maxpacket) != 0)) r8a66597_bset(r8a66597, BVAL, ep->fifoctr); } @@ -769,7 +1010,8 @@ static void irq_packet_write(struct r8a66597_ep *ep, if (unlikely((tmp & FRDY) == 0)) { pipe_stop(r8a66597, pipenum); pipe_irq_disable(r8a66597, pipenum); - printk(KERN_ERR "write fifo not ready. pipnum=%d\n", pipenum); + dev_err(r8a66597_to_dev(r8a66597), + "write fifo not ready. pipnum=%d\n", pipenum); return; } @@ -780,7 +1022,7 @@ static void irq_packet_write(struct r8a66597_ep *ep, /* write fifo */ if (req->req.buf) { - r8a66597_write_fifo(r8a66597, ep->fifoaddr, buf, size); + r8a66597_write_fifo(r8a66597, ep, buf, size); if ((size == 0) || ((size % ep->ep.maxpacket) != 0) || ((bufsize != ep->ep.maxpacket) @@ -819,7 +1061,7 @@ static void irq_packet_read(struct r8a66597_ep *ep, req->req.status = -EPIPE; pipe_stop(r8a66597, pipenum); pipe_irq_disable(r8a66597, pipenum); - printk(KERN_ERR "read fifo not ready"); + dev_err(r8a66597_to_dev(r8a66597), "read fifo not ready"); return; } @@ -1095,7 +1337,7 @@ static void r8a66597_update_usb_speed(struct r8a66597 *r8a66597) break; default: r8a66597->gadget.speed = USB_SPEED_UNKNOWN; - printk(KERN_ERR "USB speed unknown\n"); + dev_err(r8a66597_to_dev(r8a66597), "USB speed unknown\n"); } } @@ -1158,11 +1400,71 @@ __acquires(r8a66597->lock) control_end(r8a66597, 0); break; default: - printk(KERN_ERR "ctrl_stage: unexpect ctsq(%x)\n", ctsq); + dev_err(r8a66597_to_dev(r8a66597), + "ctrl_stage: unexpect ctsq(%x)\n", ctsq); break; } } +static void sudmac_finish(struct r8a66597 *r8a66597, struct r8a66597_ep *ep) +{ + u16 pipenum; + struct r8a66597_request *req; + u32 len; + int i = 0; + + pipenum = ep->pipenum; + pipe_change(r8a66597, pipenum); + + while (!(r8a66597_read(r8a66597, ep->fifoctr) & FRDY)) { + udelay(1); + if (unlikely(i++ >= 10000)) { /* timeout = 10 msec */ + dev_err(r8a66597_to_dev(r8a66597), + "%s: FRDY was not set (%d)\n", + __func__, pipenum); + return; + } + } + + r8a66597_bset(r8a66597, BCLR, ep->fifoctr); + req = get_request_from_ep(ep); + + /* prepare parameters */ + len = r8a66597_sudmac_read(r8a66597, CH0CBC); + req->req.actual += len; + + /* clear */ + r8a66597_sudmac_write(r8a66597, CH0STCLR, DSTSCLR); + + /* check transfer finish */ + if ((!req->req.zero && (req->req.actual == req->req.length)) + || (len % ep->ep.maxpacket)) { + if (ep->dma->dir) { + disable_irq_ready(r8a66597, pipenum); + enable_irq_empty(r8a66597, pipenum); + } else { + /* Clear the interrupt flag for next transfer */ + r8a66597_write(r8a66597, ~(1 << pipenum), BRDYSTS); + transfer_complete(ep, req, 0); + } + } +} + +static void r8a66597_sudmac_irq(struct r8a66597 *r8a66597) +{ + u32 irqsts; + struct r8a66597_ep *ep; + u16 pipenum; + + irqsts = r8a66597_sudmac_read(r8a66597, DINTSTS); + if (irqsts & CH0ENDS) { + r8a66597_sudmac_write(r8a66597, CH0ENDC, DINTSTSCLR); + pipenum = (r8a66597_read(r8a66597, D0FIFOSEL) & CURPIPE); + ep = r8a66597->pipenum2ep[pipenum]; + sudmac_finish(r8a66597, ep); + } +} + static irqreturn_t r8a66597_irq(int irq, void *_r8a66597) { struct r8a66597 *r8a66597 = _r8a66597; @@ -1173,6 +1475,9 @@ static irqreturn_t r8a66597_irq(int irq, void *_r8a66597) u16 savepipe; u16 mask0; + if (r8a66597_is_sudmac(r8a66597)) + r8a66597_sudmac_irq(r8a66597); + spin_lock(&r8a66597->lock); intsts0 = r8a66597_read(r8a66597, INTSTS0); @@ -1433,23 +1738,18 @@ static struct usb_ep_ops r8a66597_ep_ops = { }; /*-------------------------------------------------------------------------*/ -static struct r8a66597 *the_controller; - -static int r8a66597_start(struct usb_gadget_driver *driver, - int (*bind)(struct usb_gadget *)) +static int r8a66597_start(struct usb_gadget *gadget, + struct usb_gadget_driver *driver) { - struct r8a66597 *r8a66597 = the_controller; + struct r8a66597 *r8a66597 = gadget_to_r8a66597(gadget); int retval; if (!driver || driver->speed != USB_SPEED_HIGH - || !bind || !driver->setup) return -EINVAL; if (!r8a66597) return -ENODEV; - if (r8a66597->driver) - return -EBUSY; /* hook up the driver */ driver->driver.bus = NULL; @@ -1458,14 +1758,8 @@ static int r8a66597_start(struct usb_gadget_driver *driver, retval = device_add(&r8a66597->gadget.dev); if (retval) { - printk(KERN_ERR "device_add error (%d)\n", retval); - goto error; - } - - retval = bind(&r8a66597->gadget); - if (retval) { - printk(KERN_ERR "bind to driver error (%d)\n", retval); - device_del(&r8a66597->gadget.dev); + dev_err(r8a66597_to_dev(r8a66597), "device_add error (%d)\n", + retval); goto error; } @@ -1489,23 +1783,17 @@ error: return retval; } -static int r8a66597_stop(struct usb_gadget_driver *driver) +static int r8a66597_stop(struct usb_gadget *gadget, + struct usb_gadget_driver *driver) { - struct r8a66597 *r8a66597 = the_controller; + struct r8a66597 *r8a66597 = gadget_to_r8a66597(gadget); unsigned long flags; - if (driver != r8a66597->driver || !driver->unbind) - return -EINVAL; - spin_lock_irqsave(&r8a66597->lock, flags); - if (r8a66597->gadget.speed != USB_SPEED_UNKNOWN) - r8a66597_usb_disconnect(r8a66597); r8a66597_bclr(r8a66597, VBSE, INTENB0); disable_controller(r8a66597); spin_unlock_irqrestore(&r8a66597->lock, flags); - driver->unbind(&r8a66597->gadget); - device_del(&r8a66597->gadget.dev); r8a66597->driver = NULL; return 0; @@ -1535,8 +1823,8 @@ static int r8a66597_pullup(struct usb_gadget *gadget, int is_on) static struct usb_gadget_ops r8a66597_gadget_ops = { .get_frame = r8a66597_get_frame, - .start = r8a66597_start, - .stop = r8a66597_stop, + .udc_start = r8a66597_start, + .udc_stop = r8a66597_stop, .pullup = r8a66597_pullup, }; @@ -1547,6 +1835,8 @@ static int __exit r8a66597_remove(struct platform_device *pdev) usb_del_gadget_udc(&r8a66597->gadget); del_timer_sync(&r8a66597->timer); iounmap(r8a66597->reg); + if (r8a66597->pdata->sudmac) + iounmap(r8a66597->sudmac_reg); free_irq(platform_get_irq(pdev, 0), r8a66597); r8a66597_free_request(&r8a66597->ep[0].ep, r8a66597->ep0_req); #ifdef CONFIG_HAVE_CLK @@ -1563,6 +1853,26 @@ static void nop_completion(struct usb_ep *ep, struct usb_request *r) { } +static int __init r8a66597_sudmac_ioremap(struct r8a66597 *r8a66597, + struct platform_device *pdev) +{ + struct resource *res; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sudmac"); + if (!res) { + dev_err(&pdev->dev, "platform_get_resource error(sudmac).\n"); + return -ENODEV; + } + + r8a66597->sudmac_reg = ioremap(res->start, resource_size(res)); + if (r8a66597->sudmac_reg == NULL) { + dev_err(&pdev->dev, "ioremap error(sudmac).\n"); + return -ENOMEM; + } + + return 0; +} + static int __init r8a66597_probe(struct platform_device *pdev) { #ifdef CONFIG_HAVE_CLK @@ -1579,7 +1889,7 @@ static int __init r8a66597_probe(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { ret = -ENODEV; - printk(KERN_ERR "platform_get_resource error.\n"); + dev_err(&pdev->dev, "platform_get_resource error.\n"); goto clean_up; } @@ -1589,14 +1899,14 @@ static int __init r8a66597_probe(struct platform_device *pdev) if (irq < 0) { ret = -ENODEV; - printk(KERN_ERR "platform_get_irq error.\n"); + dev_err(&pdev->dev, "platform_get_irq error.\n"); goto clean_up; } reg = ioremap(res->start, resource_size(res)); if (reg == NULL) { ret = -ENOMEM; - printk(KERN_ERR "ioremap error.\n"); + dev_err(&pdev->dev, "ioremap error.\n"); goto clean_up; } @@ -1604,7 +1914,7 @@ static int __init r8a66597_probe(struct platform_device *pdev) r8a66597 = kzalloc(sizeof(struct r8a66597), GFP_KERNEL); if (r8a66597 == NULL) { ret = -ENOMEM; - printk(KERN_ERR "kzalloc error\n"); + dev_err(&pdev->dev, "kzalloc error\n"); goto clean_up; } @@ -1640,13 +1950,18 @@ static int __init r8a66597_probe(struct platform_device *pdev) clk_enable(r8a66597->clk); } #endif + if (r8a66597->pdata->sudmac) { + ret = r8a66597_sudmac_ioremap(r8a66597, pdev); + if (ret < 0) + goto clean_up2; + } disable_controller(r8a66597); /* make sure controller is disabled */ - ret = request_irq(irq, r8a66597_irq, IRQF_DISABLED | IRQF_SHARED, + ret = request_irq(irq, r8a66597_irq, IRQF_SHARED, udc_name, r8a66597); if (ret < 0) { - printk(KERN_ERR "request_irq error (%d)\n", ret); + dev_err(&pdev->dev, "request_irq error (%d)\n", ret); goto clean_up2; } @@ -1672,13 +1987,10 @@ static int __init r8a66597_probe(struct platform_device *pdev) r8a66597->ep[0].fifoaddr = CFIFO; r8a66597->ep[0].fifosel = CFIFOSEL; r8a66597->ep[0].fifoctr = CFIFOCTR; - r8a66597->ep[0].fifotrn = 0; r8a66597->ep[0].pipectr = get_pipectr_addr(0); r8a66597->pipenum2ep[0] = &r8a66597->ep[0]; r8a66597->epaddr2ep[0] = &r8a66597->ep[0]; - the_controller = r8a66597; - r8a66597->ep0_req = r8a66597_alloc_request(&r8a66597->ep[0].ep, GFP_KERNEL); if (r8a66597->ep0_req == NULL) @@ -1705,6 +2017,8 @@ clean_up2: #endif clean_up: if (r8a66597) { + if (r8a66597->sudmac_reg) + iounmap(r8a66597->sudmac_reg); if (r8a66597->ep0_req) r8a66597_free_request(&r8a66597->ep[0].ep, r8a66597->ep0_req); diff --git a/drivers/usb/gadget/r8a66597-udc.h b/drivers/usb/gadget/r8a66597-udc.h index 503f766c23a7..8e3de61cd4b8 100644 --- a/drivers/usb/gadget/r8a66597-udc.h +++ b/drivers/usb/gadget/r8a66597-udc.h @@ -8,16 +8,6 @@ * 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; version 2 of the License. - * - * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * */ #ifndef __R8A66597_H__ @@ -53,6 +43,7 @@ ((pipenum >= R8A66597_BASE_PIPENUM_ISOC) && \ (pipenum < (R8A66597_BASE_PIPENUM_ISOC + R8A66597_MAX_NUM_ISOC))) +#define r8a66597_is_sudmac(r8a66597) (r8a66597->pdata->sudmac) struct r8a66597_pipe_info { u16 pipe; u16 epnum; @@ -70,6 +61,7 @@ struct r8a66597_request { struct r8a66597_ep { struct usb_ep ep; struct r8a66597 *r8a66597; + struct r8a66597_dma *dma; struct list_head queue; unsigned busy:1; @@ -85,13 +77,20 @@ struct r8a66597_ep { unsigned char fifoaddr; unsigned char fifosel; unsigned char fifoctr; - unsigned char fifotrn; unsigned char pipectr; + unsigned char pipetre; + unsigned char pipetrn; +}; + +struct r8a66597_dma { + unsigned used:1; + unsigned dir:1; /* 1 = IN(write), 0 = OUT(read) */ }; struct r8a66597 { spinlock_t lock; void __iomem *reg; + void __iomem *sudmac_reg; #ifdef CONFIG_HAVE_CLK struct clk *clk; @@ -104,6 +103,7 @@ struct r8a66597 { struct r8a66597_ep ep[R8A66597_MAX_NUM_PIPE]; struct r8a66597_ep *pipenum2ep[R8A66597_MAX_NUM_PIPE]; struct r8a66597_ep *epaddr2ep[16]; + struct r8a66597_dma dma; struct timer_list timer; struct usb_request *ep0_req; /* for internal request */ @@ -124,6 +124,7 @@ struct r8a66597 { #define gadget_to_r8a66597(_gadget) \ container_of(_gadget, struct r8a66597, gadget) #define r8a66597_to_gadget(r8a66597) (&r8a66597->gadget) +#define r8a66597_to_dev(r8a66597) (r8a66597->gadget.dev.parent) static inline u16 r8a66597_read(struct r8a66597 *r8a66597, unsigned long offset) { @@ -182,12 +183,27 @@ static inline void r8a66597_write(struct r8a66597 *r8a66597, u16 val, iowrite16(val, r8a66597->reg + offset); } +static inline void r8a66597_mdfy(struct r8a66597 *r8a66597, + u16 val, u16 pat, unsigned long offset) +{ + u16 tmp; + tmp = r8a66597_read(r8a66597, offset); + tmp = tmp & (~pat); + tmp = tmp | val; + r8a66597_write(r8a66597, tmp, offset); +} + +#define r8a66597_bclr(r8a66597, val, offset) \ + r8a66597_mdfy(r8a66597, 0, val, offset) +#define r8a66597_bset(r8a66597, val, offset) \ + r8a66597_mdfy(r8a66597, val, 0, offset) + static inline void r8a66597_write_fifo(struct r8a66597 *r8a66597, - unsigned long offset, + struct r8a66597_ep *ep, unsigned char *buf, int len) { - void __iomem *fifoaddr = r8a66597->reg + offset; + void __iomem *fifoaddr = r8a66597->reg + ep->fifoaddr; int adj = 0; int i; @@ -215,18 +231,12 @@ static inline void r8a66597_write_fifo(struct r8a66597 *r8a66597, adj = 0x01; /* 16-bit wide */ } + if (r8a66597->pdata->wr0_shorted_to_wr1) + r8a66597_bclr(r8a66597, MBW_16, ep->fifosel); for (i = 0; i < len; i++) iowrite8(buf[i], fifoaddr + adj - (i & adj)); -} - -static inline void r8a66597_mdfy(struct r8a66597 *r8a66597, - u16 val, u16 pat, unsigned long offset) -{ - u16 tmp; - tmp = r8a66597_read(r8a66597, offset); - tmp = tmp & (~pat); - tmp = tmp | val; - r8a66597_write(r8a66597, tmp, offset); + if (r8a66597->pdata->wr0_shorted_to_wr1) + r8a66597_bclr(r8a66597, MBW_16, ep->fifosel); } static inline u16 get_xtal_from_pdata(struct r8a66597_platdata *pdata) @@ -251,12 +261,21 @@ static inline u16 get_xtal_from_pdata(struct r8a66597_platdata *pdata) return clock; } -#define r8a66597_bclr(r8a66597, val, offset) \ - r8a66597_mdfy(r8a66597, 0, val, offset) -#define r8a66597_bset(r8a66597, val, offset) \ - r8a66597_mdfy(r8a66597, val, 0, offset) +static inline u32 r8a66597_sudmac_read(struct r8a66597 *r8a66597, + unsigned long offset) +{ + return ioread32(r8a66597->sudmac_reg + offset); +} + +static inline void r8a66597_sudmac_write(struct r8a66597 *r8a66597, u32 val, + unsigned long offset) +{ + iowrite32(val, r8a66597->sudmac_reg + offset); +} #define get_pipectr_addr(pipenum) (PIPE1CTR + (pipenum - 1) * 2) +#define get_pipetre_addr(pipenum) (PIPE1TRE + (pipenum - 1) * 4) +#define get_pipetrn_addr(pipenum) (PIPE1TRN + (pipenum - 1) * 4) #define enable_irq_ready(r8a66597, pipenum) \ enable_pipe_irq(r8a66597, pipenum, BRDYENB) diff --git a/drivers/usb/gadget/s3c-hsotg.c b/drivers/usb/gadget/s3c-hsotg.c index 8bdee67ce09a..a552453dc946 100644 --- a/drivers/usb/gadget/s3c-hsotg.c +++ b/drivers/usb/gadget/s3c-hsotg.c @@ -1951,30 +1951,26 @@ static void s3c_hsotg_irq_enumdone(struct s3c_hsotg *hsotg) case S3C_DSTS_EnumSpd_FS: case S3C_DSTS_EnumSpd_FS48: hsotg->gadget.speed = USB_SPEED_FULL; - dev_info(hsotg->dev, "new device is full-speed\n"); - ep0_mps = EP0_MPS_LIMIT; ep_mps = 64; break; case S3C_DSTS_EnumSpd_HS: - dev_info(hsotg->dev, "new device is high-speed\n"); hsotg->gadget.speed = USB_SPEED_HIGH; - ep0_mps = EP0_MPS_LIMIT; ep_mps = 512; break; case S3C_DSTS_EnumSpd_LS: hsotg->gadget.speed = USB_SPEED_LOW; - dev_info(hsotg->dev, "new device is low-speed\n"); - /* note, we don't actually support LS in this driver at the * moment, and the documentation seems to imply that it isn't * supported by the PHYs on some of the devices. */ break; } + dev_info(hsotg->dev, "new device is %s\n", + usb_speed_string(hsotg->gadget.speed)); /* we should now know the maximum packet size for an * endpoint, so set the endpoints to a default value. */ @@ -2297,7 +2293,7 @@ static int s3c_hsotg_ep_enable(struct usb_ep *ep, return -EINVAL; } - mps = le16_to_cpu(desc->wMaxPacketSize); + mps = usb_endpoint_maxp(desc); /* note, we handle this here instead of s3c_hsotg_set_ep_maxpacket */ diff --git a/drivers/usb/gadget/s3c-hsudc.c b/drivers/usb/gadget/s3c-hsudc.c index 3fa717c5f4bc..8d54f893cefe 100644 --- a/drivers/usb/gadget/s3c-hsudc.c +++ b/drivers/usb/gadget/s3c-hsudc.c @@ -26,6 +26,7 @@ #include <linux/clk.h> #include <linux/usb/ch9.h> #include <linux/usb/gadget.h> +#include <linux/usb/otg.h> #include <linux/prefetch.h> #include <mach/regs-s3c2443-clock.h> @@ -137,6 +138,7 @@ struct s3c_hsudc { struct usb_gadget_driver *driver; struct device *dev; struct s3c24xx_hsudc_platdata *pd; + struct otg_transceiver *transceiver; spinlock_t lock; void __iomem *regs; struct resource *mem_rsrc; @@ -759,11 +761,11 @@ static int s3c_hsudc_ep_enable(struct usb_ep *_ep, if (!_ep || !desc || hsep->desc || _ep->name == ep0name || desc->bDescriptorType != USB_DT_ENDPOINT || hsep->bEndpointAddress != desc->bEndpointAddress - || ep_maxpacket(hsep) < le16_to_cpu(desc->wMaxPacketSize)) + || ep_maxpacket(hsep) < usb_endpoint_maxp(desc)) return -EINVAL; if ((desc->bmAttributes == USB_ENDPOINT_XFER_BULK - && le16_to_cpu(desc->wMaxPacketSize) != ep_maxpacket(hsep)) + && usb_endpoint_maxp(desc) != ep_maxpacket(hsep)) || !desc->wMaxPacketSize) return -ERANGE; @@ -779,7 +781,7 @@ static int s3c_hsudc_ep_enable(struct usb_ep *_ep, hsep->stopped = hsep->wedge = 0; hsep->desc = desc; - hsep->ep.maxpacket = le16_to_cpu(desc->wMaxPacketSize); + hsep->ep.maxpacket = usb_endpoint_maxp(desc); s3c_hsudc_set_halt(_ep, 0); __set_bit(ep_index(hsep), hsudc->regs + S3C_EIER); @@ -1171,6 +1173,22 @@ static int s3c_hsudc_start(struct usb_gadget_driver *driver, return ret; } + /* connect to bus through transceiver */ + if (hsudc->transceiver) { + ret = otg_set_peripheral(hsudc->transceiver, &hsudc->gadget); + if (ret) { + dev_err(hsudc->dev, "%s: can't bind to transceiver\n", + hsudc->gadget.name); + driver->unbind(&hsudc->gadget); + + device_del(&hsudc->gadget.dev); + + hsudc->driver = NULL; + hsudc->gadget.dev.driver = NULL; + return ret; + } + } + enable_irq(hsudc->irq); dev_info(hsudc->dev, "bound driver %s\n", driver->driver.name); @@ -1201,6 +1219,9 @@ static int s3c_hsudc_stop(struct usb_gadget_driver *driver) s3c_hsudc_stop_activity(hsudc, driver); spin_unlock_irqrestore(&hsudc->lock, flags); + if (hsudc->transceiver) + (void) otg_set_peripheral(hsudc->transceiver, NULL); + driver->unbind(&hsudc->gadget); device_del(&hsudc->gadget.dev); disable_irq(hsudc->irq); @@ -1220,10 +1241,24 @@ static int s3c_hsudc_gadget_getframe(struct usb_gadget *gadget) return s3c_hsudc_read_frameno(to_hsudc(gadget)); } +static int s3c_hsudc_vbus_draw(struct usb_gadget *gadget, unsigned mA) +{ + struct s3c_hsudc *hsudc = the_controller; + + if (!hsudc) + return -ENODEV; + + if (hsudc->transceiver) + return otg_set_power(hsudc->transceiver, mA); + + return -EOPNOTSUPP; +} + static struct usb_gadget_ops s3c_hsudc_gadget_ops = { .get_frame = s3c_hsudc_gadget_getframe, .start = s3c_hsudc_start, .stop = s3c_hsudc_stop, + .vbus_draw = s3c_hsudc_vbus_draw, }; static int s3c_hsudc_probe(struct platform_device *pdev) @@ -1247,6 +1282,8 @@ static int s3c_hsudc_probe(struct platform_device *pdev) hsudc->dev = dev; hsudc->pd = pdev->dev.platform_data; + hsudc->transceiver = otg_get_transceiver(); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { dev_err(dev, "unable to obtain driver resource data\n"); @@ -1269,19 +1306,6 @@ static int s3c_hsudc_probe(struct platform_device *pdev) goto err_remap; } - ret = platform_get_irq(pdev, 0); - if (ret < 0) { - dev_err(dev, "unable to obtain IRQ number\n"); - goto err_irq; - } - hsudc->irq = ret; - - ret = request_irq(hsudc->irq, s3c_hsudc_irq, 0, driver_name, hsudc); - if (ret < 0) { - dev_err(dev, "irq request failed\n"); - goto err_irq; - } - spin_lock_init(&hsudc->lock); device_initialize(&hsudc->gadget.dev); @@ -1299,6 +1323,19 @@ static int s3c_hsudc_probe(struct platform_device *pdev) s3c_hsudc_setup_ep(hsudc); + ret = platform_get_irq(pdev, 0); + if (ret < 0) { + dev_err(dev, "unable to obtain IRQ number\n"); + goto err_irq; + } + hsudc->irq = ret; + + ret = request_irq(hsudc->irq, s3c_hsudc_irq, 0, driver_name, hsudc); + if (ret < 0) { + dev_err(dev, "irq request failed\n"); + goto err_irq; + } + hsudc->uclk = clk_get(&pdev->dev, "usb-device"); if (IS_ERR(hsudc->uclk)) { dev_err(dev, "failed to find usb-device clock source\n"); diff --git a/drivers/usb/gadget/s3c2410_udc.c b/drivers/usb/gadget/s3c2410_udc.c index 8d31848aab09..b8643771fa80 100644 --- a/drivers/usb/gadget/s3c2410_udc.c +++ b/drivers/usb/gadget/s3c2410_udc.c @@ -10,16 +10,6 @@ * 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 - * */ #include <linux/module.h> @@ -1082,7 +1072,7 @@ static int s3c2410_udc_ep_enable(struct usb_ep *_ep, if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) return -ESHUTDOWN; - max = le16_to_cpu(desc->wMaxPacketSize) & 0x1fff; + max = usb_endpoint_maxp(desc) & 0x1fff; local_irq_save (flags); _ep->maxpacket = max & 0x7ff; @@ -1903,7 +1893,7 @@ static int s3c2410_udc_probe(struct platform_device *pdev) /* irq setup after old hardware state is cleaned up */ retval = request_irq(IRQ_USBD, s3c2410_udc_irq, - IRQF_DISABLED, gadget_name, udc); + 0, gadget_name, udc); if (retval != 0) { dev_err(dev, "cannot get irq %i, err %d\n", IRQ_USBD, retval); @@ -1927,7 +1917,7 @@ static int s3c2410_udc_probe(struct platform_device *pdev) } retval = request_irq(irq, s3c2410_udc_vbus_irq, - IRQF_DISABLED | IRQF_TRIGGER_RISING + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_SHARED, gadget_name, udc); diff --git a/drivers/usb/gadget/s3c2410_udc.h b/drivers/usb/gadget/s3c2410_udc.h index 9e0bece4f241..a48f619cb1cc 100644 --- a/drivers/usb/gadget/s3c2410_udc.h +++ b/drivers/usb/gadget/s3c2410_udc.h @@ -9,16 +9,6 @@ * 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 - * */ #ifndef _S3C2410_UDC_H diff --git a/drivers/usb/gadget/storage_common.c b/drivers/usb/gadget/storage_common.c index d3dd227a2bfc..c7f291a331df 100644 --- a/drivers/usb/gadget/storage_common.c +++ b/drivers/usb/gadget/storage_common.c @@ -9,15 +9,6 @@ * 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 */ @@ -52,6 +43,12 @@ * characters rather then a pointer to void. */ +/* + * When USB_GADGET_DEBUG_FILES is defined the module param num_buffers + * sets the number of pipeline buffers (length of the fsg_buffhd array). + * The valid range of num_buffers is: num >= 2 && num <= 4. + */ + #include <linux/usb/storage.h> #include <scsi/scsi.h> @@ -247,6 +244,8 @@ struct fsg_lun { u32 sense_data_info; u32 unit_attention_data; + unsigned int blkbits; /* Bits of logical block size of bound block device */ + unsigned int blksize; /* logical block size of bound block device */ struct device dev; }; @@ -262,8 +261,31 @@ static struct fsg_lun *fsg_lun_from_dev(struct device *dev) #define EP0_BUFSIZE 256 #define DELAYED_STATUS (EP0_BUFSIZE + 999) /* An impossibly large value */ -/* Number of buffers we will use. 2 is enough for double-buffering */ -#define FSG_NUM_BUFFERS 2 +#ifdef CONFIG_USB_GADGET_DEBUG_FILES + +static unsigned int fsg_num_buffers = CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS; +module_param_named(num_buffers, fsg_num_buffers, uint, S_IRUGO); +MODULE_PARM_DESC(num_buffers, "Number of pipeline buffers"); + +#else + +/* + * Number of buffers we will use. + * 2 is usually enough for good buffering pipeline + */ +#define fsg_num_buffers CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS + +#endif /* CONFIG_USB_DEBUG */ + +/* check if fsg_num_buffers is within a valid range */ +static inline int fsg_num_buffers_validate(void) +{ + if (fsg_num_buffers >= 2 && fsg_num_buffers <= 4) + return 0; + pr_err("fsg_num_buffers %u is out of range (%d to %d)\n", + fsg_num_buffers, 2 ,4); + return -EINVAL; +} /* Default size of buffer length. */ #define FSG_BUFLEN ((u32)16384) @@ -493,12 +515,128 @@ static struct usb_descriptor_header *fsg_hs_function[] = { NULL, }; +static struct usb_endpoint_descriptor +fsg_ss_bulk_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + /* bEndpointAddress copied from fs_bulk_in_desc during fsg_bind() */ + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(1024), +}; + +static struct usb_ss_ep_comp_descriptor fsg_ss_bulk_in_comp_desc = { + .bLength = sizeof(fsg_ss_bulk_in_comp_desc), + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, + + /*.bMaxBurst = DYNAMIC, */ +}; + +static struct usb_endpoint_descriptor +fsg_ss_bulk_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + /* bEndpointAddress copied from fs_bulk_out_desc during fsg_bind() */ + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(1024), +}; + +static struct usb_ss_ep_comp_descriptor fsg_ss_bulk_out_comp_desc = { + .bLength = sizeof(fsg_ss_bulk_in_comp_desc), + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, + + /*.bMaxBurst = DYNAMIC, */ +}; + +#ifndef FSG_NO_INTR_EP + +static struct usb_endpoint_descriptor +fsg_ss_intr_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + /* bEndpointAddress copied from fs_intr_in_desc during fsg_bind() */ + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = cpu_to_le16(2), + .bInterval = 9, /* 2**(9-1) = 256 uframes -> 32 ms */ +}; + +static struct usb_ss_ep_comp_descriptor fsg_ss_intr_in_comp_desc = { + .bLength = sizeof(fsg_ss_bulk_in_comp_desc), + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, + + .wBytesPerInterval = cpu_to_le16(2), +}; + +#ifndef FSG_NO_OTG +# define FSG_SS_FUNCTION_PRE_EP_ENTRIES 2 +#else +# define FSG_SS_FUNCTION_PRE_EP_ENTRIES 1 +#endif + +#endif + +static __maybe_unused struct usb_ext_cap_descriptor fsg_ext_cap_desc = { + .bLength = USB_DT_USB_EXT_CAP_SIZE, + .bDescriptorType = USB_DT_DEVICE_CAPABILITY, + .bDevCapabilityType = USB_CAP_TYPE_EXT, + + .bmAttributes = cpu_to_le32(USB_LPM_SUPPORT), +}; + +static __maybe_unused struct usb_ss_cap_descriptor fsg_ss_cap_desc = { + .bLength = USB_DT_USB_SS_CAP_SIZE, + .bDescriptorType = USB_DT_DEVICE_CAPABILITY, + .bDevCapabilityType = USB_SS_CAP_TYPE, + + /* .bmAttributes = LTM is not supported yet */ + + .wSpeedSupported = cpu_to_le16(USB_LOW_SPEED_OPERATION + | USB_FULL_SPEED_OPERATION + | USB_HIGH_SPEED_OPERATION + | USB_5GBPS_OPERATION), + .bFunctionalitySupport = USB_LOW_SPEED_OPERATION, + .bU1devExitLat = USB_DEFAULT_U1_DEV_EXIT_LAT, + .bU2DevExitLat = USB_DEFAULT_U2_DEV_EXIT_LAT, +}; + +static __maybe_unused struct usb_bos_descriptor fsg_bos_desc = { + .bLength = USB_DT_BOS_SIZE, + .bDescriptorType = USB_DT_BOS, + + .wTotalLength = USB_DT_BOS_SIZE + + USB_DT_USB_EXT_CAP_SIZE + + USB_DT_USB_SS_CAP_SIZE, + + .bNumDeviceCaps = 2, +}; + +static struct usb_descriptor_header *fsg_ss_function[] = { +#ifndef FSG_NO_OTG + (struct usb_descriptor_header *) &fsg_otg_desc, +#endif + (struct usb_descriptor_header *) &fsg_intf_desc, + (struct usb_descriptor_header *) &fsg_ss_bulk_in_desc, + (struct usb_descriptor_header *) &fsg_ss_bulk_in_comp_desc, + (struct usb_descriptor_header *) &fsg_ss_bulk_out_desc, + (struct usb_descriptor_header *) &fsg_ss_bulk_out_comp_desc, +#ifndef FSG_NO_INTR_EP + (struct usb_descriptor_header *) &fsg_ss_intr_in_desc, + (struct usb_descriptor_header *) &fsg_ss_intr_in_comp_desc, +#endif + NULL, +}; + /* Maxpacket and other transfer characteristics vary by speed. */ static __maybe_unused struct usb_endpoint_descriptor * fsg_ep_desc(struct usb_gadget *g, struct usb_endpoint_descriptor *fs, - struct usb_endpoint_descriptor *hs) + struct usb_endpoint_descriptor *hs, + struct usb_endpoint_descriptor *ss) { - if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH) + if (gadget_is_superspeed(g) && g->speed == USB_SPEED_SUPER) + return ss; + else if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH) return hs; return fs; } @@ -580,13 +718,24 @@ static int fsg_lun_open(struct fsg_lun *curlun, const char *filename) rc = (int) size; goto out; } - num_sectors = size >> 9; /* File size in 512-byte blocks */ + + if (curlun->cdrom) { + curlun->blksize = 2048; + curlun->blkbits = 11; + } else if (inode->i_bdev) { + curlun->blksize = bdev_logical_block_size(inode->i_bdev); + curlun->blkbits = blksize_bits(curlun->blksize); + } else { + curlun->blksize = 512; + curlun->blkbits = 9; + } + + num_sectors = size >> curlun->blkbits; /* File size in logic-block-size blocks */ min_sectors = 1; if (curlun->cdrom) { - num_sectors &= ~3; /* Reduce to a multiple of 2048 */ - min_sectors = 300*4; /* Smallest track is 300 frames */ - if (num_sectors >= 256*60*75*4) { - num_sectors = (256*60*75 - 1) * 4; + min_sectors = 300; /* Smallest track is 300 frames */ + if (num_sectors >= 256*60*75) { + num_sectors = 256*60*75 - 1; LINFO(curlun, "file too big: %s\n", filename); LINFO(curlun, "using only first %d blocks\n", (int) num_sectors); diff --git a/drivers/usb/gadget/u_ether.c b/drivers/usb/gadget/u_ether.c index dfed4c1d96c0..29c854bbca44 100644 --- a/drivers/usb/gadget/u_ether.c +++ b/drivers/usb/gadget/u_ether.c @@ -9,15 +9,6 @@ * 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 */ /* #define VERBOSE_DEBUG */ diff --git a/drivers/usb/gadget/u_ether.h b/drivers/usb/gadget/u_ether.h index c966440ddd70..8012357e98aa 100644 --- a/drivers/usb/gadget/u_ether.h +++ b/drivers/usb/gadget/u_ether.h @@ -9,15 +9,6 @@ * 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 */ #ifndef __U_ETHER_H diff --git a/drivers/usb/gadget/u_serial.c b/drivers/usb/gadget/u_serial.c index a8aa46962d81..3a4a664bab44 100644 --- a/drivers/usb/gadget/u_serial.c +++ b/drivers/usb/gadget/u_serial.c @@ -552,9 +552,8 @@ recycle: /* Push from tty to ldisc; without low_latency set this is handled by * a workqueue, so we won't get callbacks and can hold port_lock */ - if (tty && do_push) { + if (tty && do_push) tty_flip_buffer_push(tty); - } /* We want our data queue to become empty ASAP, keeping data diff --git a/drivers/usb/gadget/udc-core.c b/drivers/usb/gadget/udc-core.c index 05ba47214361..022baeca7c94 100644 --- a/drivers/usb/gadget/udc-core.c +++ b/drivers/usb/gadget/udc-core.c @@ -356,7 +356,7 @@ static DEVICE_ATTR(srp, S_IWUSR, NULL, usb_udc_srp_store); static ssize_t usb_udc_softconn_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t n) { - struct usb_udc *udc = dev_get_drvdata(dev); + struct usb_udc *udc = container_of(dev, struct usb_udc, dev); if (sysfs_streq(buf, "connect")) { usb_gadget_connect(udc->gadget); @@ -375,23 +375,8 @@ static ssize_t usb_udc_speed_show(struct device *dev, struct device_attribute *attr, char *buf) { struct usb_udc *udc = container_of(dev, struct usb_udc, dev); - struct usb_gadget *gadget = udc->gadget; - - switch (gadget->speed) { - case USB_SPEED_LOW: - return snprintf(buf, PAGE_SIZE, "low-speed\n"); - case USB_SPEED_FULL: - return snprintf(buf, PAGE_SIZE, "full-speed\n"); - case USB_SPEED_HIGH: - return snprintf(buf, PAGE_SIZE, "high-speed\n"); - case USB_SPEED_WIRELESS: - return snprintf(buf, PAGE_SIZE, "wireless\n"); - case USB_SPEED_SUPER: - return snprintf(buf, PAGE_SIZE, "super-speed\n"); - case USB_SPEED_UNKNOWN: /* FALLTHROUGH */ - default: - return snprintf(buf, PAGE_SIZE, "UNKNOWN\n"); - } + return snprintf(buf, PAGE_SIZE, "%s\n", + usb_speed_string(udc->gadget->speed)); } static DEVICE_ATTR(speed, S_IRUSR, usb_udc_speed_show, NULL); diff --git a/drivers/usb/gadget/uvc.h b/drivers/usb/gadget/uvc.h index 81971fbc7ea5..bc78c606c12b 100644 --- a/drivers/usb/gadget/uvc.h +++ b/drivers/usb/gadget/uvc.h @@ -8,7 +8,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * */ #ifndef _UVC_GADGET_H_ diff --git a/drivers/usb/gadget/uvc_queue.c b/drivers/usb/gadget/uvc_queue.c index aa0ad34e0f1f..d776adb2da67 100644 --- a/drivers/usb/gadget/uvc_queue.c +++ b/drivers/usb/gadget/uvc_queue.c @@ -8,7 +8,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * */ #include <linux/kernel.h> diff --git a/drivers/usb/gadget/uvc_v4l2.c b/drivers/usb/gadget/uvc_v4l2.c index cfb58384394d..f6e083b50191 100644 --- a/drivers/usb/gadget/uvc_v4l2.c +++ b/drivers/usb/gadget/uvc_v4l2.c @@ -8,7 +8,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * */ #include <linux/kernel.h> diff --git a/drivers/usb/gadget/uvc_video.c b/drivers/usb/gadget/uvc_video.c index b08f35438d70..b0e53a8ea4f7 100644 --- a/drivers/usb/gadget/uvc_video.c +++ b/drivers/usb/gadget/uvc_video.c @@ -8,7 +8,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * */ #include <linux/kernel.h> diff --git a/drivers/usb/gadget/webcam.c b/drivers/usb/gadget/webcam.c index df6882de50bf..668fe128f2ef 100644 --- a/drivers/usb/gadget/webcam.c +++ b/drivers/usb/gadget/webcam.c @@ -8,8 +8,8 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * */ + #include <linux/kernel.h> #include <linux/device.h> #include <linux/usb/video.h> diff --git a/drivers/usb/gadget/zero.c b/drivers/usb/gadget/zero.c index 00e2fd2d4791..20697cc132d1 100644 --- a/drivers/usb/gadget/zero.c +++ b/drivers/usb/gadget/zero.c @@ -8,15 +8,6 @@ * 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 */ diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index ab085f12d570..060e0e2b1ae6 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -19,7 +19,7 @@ config USB_C67X00_HCD config USB_XHCI_HCD tristate "xHCI HCD (USB 3.0) support (EXPERIMENTAL)" - depends on USB && PCI && EXPERIMENTAL + depends on USB && USB_ARCH_HAS_XHCI && EXPERIMENTAL ---help--- The eXtensible Host Controller Interface (xHCI) is standard for USB 3.0 "SuperSpeed" host controller hardware. @@ -515,6 +515,19 @@ config USB_R8A66597_HCD To compile this driver as a module, choose M here: the module will be called r8a66597-hcd. +config USB_RENESAS_USBHS_HCD + tristate "Renesas USBHS HCD support" + depends on USB + depends on USB_RENESAS_USBHS + help + The Renesas USBHS is a USB 2.0 host and peripheral controller. + + Enable this option if your board has this chip, and you want + to use it as a host controller. If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called renesas-usbhs. + config USB_WHCI_HCD tristate "Wireless USB Host Controller Interface (WHCI) driver (EXPERIMENTAL)" depends on EXPERIMENTAL @@ -544,11 +557,11 @@ config USB_HWA_HCD will be called "hwa-hc". config USB_IMX21_HCD - tristate "iMX21 HCD support" - depends on USB && ARM && MACH_MX21 + tristate "i.MX21 HCD support" + depends on USB && ARM && ARCH_MXC help This driver enables support for the on-chip USB host in the - iMX21 processor. + i.MX21 processor. To compile this driver as a module, choose M here: the module will be called "imx21-hcd". @@ -578,3 +591,10 @@ config USB_OCTEON_OHCI config USB_OCTEON2_COMMON bool default y if USB_OCTEON_EHCI || USB_OCTEON_OHCI + +config USB_PXA168_EHCI + bool "Marvell PXA168 on-chip EHCI HCD support" + depends on USB_EHCI_HCD && ARCH_MMP + help + Enable support for Marvell PXA168 SoC's on-chip EHCI + host controller diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index 624a362f2fee..ed48a5d79e16 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -11,8 +11,9 @@ fhci-y += fhci-mem.o fhci-tds.o fhci-sched.o fhci-$(CONFIG_FHCI_DEBUG) += fhci-dbg.o -xhci-hcd-y := xhci.o xhci-mem.o xhci-pci.o +xhci-hcd-y := xhci.o xhci-mem.o xhci-hcd-y += xhci-ring.o xhci-hub.o xhci-dbg.o +xhci-hcd-$(CONFIG_PCI) += xhci-pci.o obj-$(CONFIG_USB_WHCI_HCD) += whci/ diff --git a/drivers/usb/host/ehci-ath79.c b/drivers/usb/host/ehci-ath79.c index 4d2e88d04dab..afb6743cf094 100644 --- a/drivers/usb/host/ehci-ath79.c +++ b/drivers/usb/host/ehci-ath79.c @@ -163,7 +163,7 @@ static int ehci_ath79_probe(struct platform_device *pdev) goto err_release_region; } - ret = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED); + ret = usb_add_hcd(hcd, irq, IRQF_SHARED); if (ret) goto err_iounmap; diff --git a/drivers/usb/host/ehci-au1xxx.c b/drivers/usb/host/ehci-au1xxx.c index 42ae57409908..65719e8d24e4 100644 --- a/drivers/usb/host/ehci-au1xxx.c +++ b/drivers/usb/host/ehci-au1xxx.c @@ -181,7 +181,7 @@ static int ehci_hcd_au1xxx_drv_probe(struct platform_device *pdev) ehci->hcs_params = readl(&ehci->caps->hcs_params); ret = usb_add_hcd(hcd, pdev->resource[1].start, - IRQF_DISABLED | IRQF_SHARED); + IRQF_SHARED); if (ret == 0) { platform_set_drvdata(pdev, hcd); return ret; @@ -293,7 +293,7 @@ static int ehci_hcd_au1xxx_drv_resume(struct device *dev) /* here we "know" root ports should always stay powered */ ehci_port_power(ehci, 1); - hcd->state = HC_STATE_SUSPENDED; + ehci->rh_state = EHCI_RH_SUSPENDED; return 0; } diff --git a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c index 40a844c1dbb4..d6d74d2e09f4 100644 --- a/drivers/usb/host/ehci-dbg.c +++ b/drivers/usb/host/ehci-dbg.c @@ -697,6 +697,19 @@ static ssize_t fill_periodic_buffer(struct debug_buffer *buf) } #undef DBG_SCHED_LIMIT +static const char *rh_state_string(struct ehci_hcd *ehci) +{ + switch (ehci->rh_state) { + case EHCI_RH_HALTED: + return "halted"; + case EHCI_RH_SUSPENDED: + return "suspended"; + case EHCI_RH_RUNNING: + return "running"; + } + return "?"; +} + static ssize_t fill_registers_buffer(struct debug_buffer *buf) { struct usb_hcd *hcd; @@ -730,11 +743,11 @@ static ssize_t fill_registers_buffer(struct debug_buffer *buf) temp = scnprintf (next, size, "bus %s, device %s\n" "%s\n" - "EHCI %x.%02x, hcd state %d\n", + "EHCI %x.%02x, rh state %s\n", hcd->self.controller->bus->name, dev_name(hcd->self.controller), hcd->product_desc, - i >> 8, i & 0x0ff, hcd->state); + i >> 8, i & 0x0ff, rh_state_string(ehci)); size -= temp; next += temp; @@ -808,7 +821,7 @@ static ssize_t fill_registers_buffer(struct debug_buffer *buf) next += temp; temp = scnprintf (next, size, "uframe %04x\n", - ehci_readl(ehci, &ehci->regs->frame_index)); + ehci_read_frame_index(ehci)); size -= temp; next += temp; diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c index 34a3140d1e5f..e90344a17631 100644 --- a/drivers/usb/host/ehci-fsl.c +++ b/drivers/usb/host/ehci-fsl.c @@ -134,7 +134,7 @@ static int usb_hcd_fsl_probe(const struct hc_driver *driver, /* Don't need to set host mode here. It will be done by tdi_reset() */ - retval = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED); + retval = usb_add_hcd(hcd, irq, IRQF_SHARED); if (retval != 0) goto err4; @@ -392,7 +392,7 @@ static int ehci_fsl_mpc512x_drv_suspend(struct device *dev) dev_dbg(dev, "suspending...\n"); - hcd->state = HC_STATE_SUSPENDED; + ehci->rh_state = EHCI_RH_SUSPENDED; dev->power.power_state = PMSG_SUSPEND; /* ignore non-host interrupts */ @@ -481,7 +481,7 @@ static int ehci_fsl_mpc512x_drv_resume(struct device *dev) ehci_writel(ehci, pdata->pm_portsc, &ehci->regs->port_status[0]); set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); - hcd->state = HC_STATE_RUNNING; + ehci->rh_state = EHCI_RH_RUNNING; dev->power.power_state = PMSG_ON; tmp = ehci_readl(ehci, &ehci->regs->command); diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index f72ae0b6ee7f..59e81615e09c 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -95,7 +95,7 @@ static const char hcd_name [] = "ehci_hcd"; #define EHCI_IO_JIFFIES (HZ/10) /* io watchdog > irq_thresh */ #define EHCI_ASYNC_JIFFIES (HZ/20) /* async idle timeout */ #define EHCI_SHRINK_JIFFIES (DIV_ROUND_UP(HZ, 200) + 1) - /* 200-ms async qh unlink delay */ + /* 5-ms async qh unlink delay */ /* Initial IRQ latency: faster than hw default */ static int log2_irq_thresh = 0; // 0 to 6 @@ -238,7 +238,7 @@ static int handshake_on_error_set_halt(struct ehci_hcd *ehci, void __iomem *ptr, error = handshake(ehci, ptr, mask, done, usec); if (error) { ehci_halt(ehci); - ehci_to_hcd(ehci)->state = HC_STATE_HALT; + ehci->rh_state = EHCI_RH_HALTED; ehci_err(ehci, "force halt; handshake %p %08x %08x -> %d\n", ptr, mask, done, error); } @@ -278,7 +278,7 @@ static int ehci_reset (struct ehci_hcd *ehci) command |= CMD_RESET; dbg_cmd (ehci, "reset", command); ehci_writel(ehci, command, &ehci->regs->command); - ehci_to_hcd(ehci)->state = HC_STATE_HALT; + ehci->rh_state = EHCI_RH_HALTED; ehci->next_statechange = jiffies; retval = handshake (ehci, &ehci->regs->command, CMD_RESET, 0, 250 * 1000); @@ -307,7 +307,7 @@ static void ehci_quiesce (struct ehci_hcd *ehci) u32 temp; #ifdef DEBUG - if (!HC_IS_RUNNING (ehci_to_hcd(ehci)->state)) + if (ehci->rh_state != EHCI_RH_RUNNING) BUG (); #endif @@ -356,7 +356,7 @@ static void ehci_iaa_watchdog(unsigned long param) */ if (ehci->reclaim && !timer_pending(&ehci->iaa_watchdog) - && HC_IS_RUNNING(ehci_to_hcd(ehci)->state)) { + && ehci->rh_state == EHCI_RH_RUNNING) { u32 cmd, status; /* If we get here, IAA is *REALLY* late. It's barely @@ -496,7 +496,7 @@ static void ehci_work (struct ehci_hcd *ehci) * misplace IRQs, and should let us run completely without IRQs. * such lossage has been observed on both VT6202 and VT8235. */ - if (HC_IS_RUNNING (ehci_to_hcd(ehci)->state) && + if (ehci->rh_state == EHCI_RH_RUNNING && (ehci->async->qh_next.ptr != NULL || ehci->periodic_sched != 0)) timer_action (ehci, TIMER_IO_WATCHDOG); @@ -516,7 +516,7 @@ static void ehci_stop (struct usb_hcd *hcd) del_timer_sync(&ehci->iaa_watchdog); spin_lock_irq(&ehci->lock); - if (HC_IS_RUNNING (hcd->state)) + if (ehci->rh_state == EHCI_RH_RUNNING) ehci_quiesce (ehci); ehci_silence_controller(ehci); @@ -741,7 +741,7 @@ static int ehci_run (struct usb_hcd *hcd) * be started before the port switching actions could complete. */ down_write(&ehci_cf_port_reset_rwsem); - hcd->state = HC_STATE_RUNNING; + ehci->rh_state = EHCI_RH_RUNNING; ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag); ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */ msleep(5); @@ -768,6 +768,35 @@ static int ehci_run (struct usb_hcd *hcd) return 0; } +static int __maybe_unused ehci_setup (struct usb_hcd *hcd) +{ + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + int retval; + + ehci->regs = (void __iomem *)ehci->caps + + HC_LENGTH(ehci, 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); + + ehci->sbrn = HCD_USB2; + + retval = ehci_halt(ehci); + if (retval) + return retval; + + /* data structure init */ + retval = ehci_init(hcd); + if (retval) + return retval; + + ehci_reset(ehci); + + return 0; +} + /*-------------------------------------------------------------------------*/ static irqreturn_t ehci_irq (struct usb_hcd *hcd) @@ -788,7 +817,7 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd) /* Shared IRQ? */ masked_status = status & INTR_MASK; - if (!masked_status || unlikely(hcd->state == HC_STATE_HALT)) { + if (!masked_status || unlikely(ehci->rh_state == EHCI_RH_HALTED)) { spin_unlock(&ehci->lock); return IRQ_NONE; } @@ -952,7 +981,7 @@ static int ehci_urb_enqueue ( static void unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh) { /* failfast */ - if (!HC_IS_RUNNING(ehci_to_hcd(ehci)->state) && ehci->reclaim) + if (ehci->rh_state != EHCI_RH_RUNNING && ehci->reclaim) end_unlink_async(ehci); /* If the QH isn't linked then there's nothing we can do @@ -1079,7 +1108,7 @@ rescan: goto idle_timeout; } - if (!HC_IS_RUNNING (hcd->state)) + if (ehci->rh_state != EHCI_RH_RUNNING) qh->qh_state = QH_STATE_IDLE; switch (qh->qh_state) { case QH_STATE_LINKED: @@ -1166,8 +1195,7 @@ ehci_endpoint_reset(struct usb_hcd *hcd, struct usb_host_endpoint *ep) static int ehci_get_frame (struct usb_hcd *hcd) { struct ehci_hcd *ehci = hcd_to_ehci (hcd); - return (ehci_readl(ehci, &ehci->regs->frame_index) >> 3) % - ehci->periodic_size; + return (ehci_read_frame_index(ehci) >> 3) % ehci->periodic_size; } /*-------------------------------------------------------------------------*/ @@ -1291,6 +1319,16 @@ MODULE_LICENSE ("GPL"); #define PLATFORM_DRIVER ehci_grlib_driver #endif +#ifdef CONFIG_USB_PXA168_EHCI +#include "ehci-pxa168.c" +#define PLATFORM_DRIVER ehci_pxa168_driver +#endif + +#ifdef CONFIG_NLM_XLR +#include "ehci-xls.c" +#define PLATFORM_DRIVER ehci_xls_driver +#endif + #if !defined(PCI_DRIVER) && !defined(PLATFORM_DRIVER) && \ !defined(PS3_SYSTEM_BUS_DRIVER) && !defined(OF_PLATFORM_DRIVER) && \ !defined(XILINX_OF_PLATFORM_DRIVER) diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index 4c32cb19b405..77bbb2357e47 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -236,10 +236,8 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) } /* stop schedules, clean any completed work */ - if (HC_IS_RUNNING(hcd->state)) { + if (ehci->rh_state == EHCI_RH_RUNNING) ehci_quiesce (ehci); - hcd->state = HC_STATE_QUIESCING; - } ehci->command = ehci_readl(ehci, &ehci->regs->command); ehci_work(ehci); @@ -313,7 +311,7 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) /* turn off now-idle HC */ ehci_halt (ehci); - hcd->state = HC_STATE_SUSPENDED; + ehci->rh_state = EHCI_RH_SUSPENDED; if (ehci->reclaim) end_unlink_async(ehci); @@ -382,6 +380,7 @@ static int ehci_bus_resume (struct usb_hcd *hcd) /* restore CMD_RUN, framelist size, and irq threshold */ ehci_writel(ehci, ehci->command, &ehci->regs->command); + ehci->rh_state = EHCI_RH_RUNNING; /* Some controller/firmware combinations need a delay during which * they set up the port statuses. See Bugzilla #8190. */ @@ -451,7 +450,6 @@ static int ehci_bus_resume (struct usb_hcd *hcd) } ehci->next_statechange = jiffies + msecs_to_jiffies(5); - hcd->state = HC_STATE_RUNNING; /* Now we can safely re-enable irqs */ ehci_writel(ehci, INTR_MASK, &ehci->regs->intr_enable); @@ -563,7 +561,7 @@ ehci_hub_status_data (struct usb_hcd *hcd, char *buf) u32 ppcd = 0; /* if !USB_SUSPEND, root hub timers won't get shut down ... */ - if (!HC_IS_RUNNING(hcd->state)) + if (ehci->rh_state != EHCI_RH_RUNNING) return 0; /* init status to no-changes */ diff --git a/drivers/usb/host/ehci-mxc.c b/drivers/usb/host/ehci-mxc.c index 555a73c864b5..55978fcfa4b2 100644 --- a/drivers/usb/host/ehci-mxc.c +++ b/drivers/usb/host/ehci-mxc.c @@ -236,7 +236,7 @@ static int ehci_mxc_drv_probe(struct platform_device *pdev) priv->hcd = hcd; platform_set_drvdata(pdev, priv); - ret = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED); + ret = usb_add_hcd(hcd, irq, IRQF_SHARED); if (ret) goto err_add; diff --git a/drivers/usb/host/ehci-octeon.c b/drivers/usb/host/ehci-octeon.c index c3ba3ed5f3a6..ba1f51361134 100644 --- a/drivers/usb/host/ehci-octeon.c +++ b/drivers/usb/host/ehci-octeon.c @@ -155,7 +155,7 @@ static int ehci_octeon_drv_probe(struct platform_device *pdev) /* cache this readonly data; minimize chip reads */ ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); - ret = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED); + ret = usb_add_hcd(hcd, irq, IRQF_SHARED); if (ret) { dev_dbg(&pdev->dev, "failed to add hcd with err %d\n", ret); goto err3; diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c index 45240321ca09..e39b0297bad1 100644 --- a/drivers/usb/host/ehci-omap.c +++ b/drivers/usb/host/ehci-omap.c @@ -228,7 +228,7 @@ static int ehci_hcd_omap_probe(struct platform_device *pdev) /* cache this readonly data; minimize chip reads */ omap_ehci->hcs_params = readl(&omap_ehci->caps->hcs_params); - ret = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED); + ret = usb_add_hcd(hcd, irq, IRQF_SHARED); if (ret) { dev_err(dev, "failed to add hcd with err %d\n", ret); goto err_add_hcd; diff --git a/drivers/usb/host/ehci-orion.c b/drivers/usb/host/ehci-orion.c index 395bdb0248d5..a68a2a5c4b83 100644 --- a/drivers/usb/host/ehci-orion.c +++ b/drivers/usb/host/ehci-orion.c @@ -277,7 +277,7 @@ static int __devinit ehci_orion_drv_probe(struct platform_device *pdev) printk(KERN_WARNING "Orion ehci -USB phy version isn't supported.\n"); } - err = usb_add_hcd(hcd, irq, IRQF_SHARED | IRQF_DISABLED); + err = usb_add_hcd(hcd, irq, IRQF_SHARED); if (err) goto err4; diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c index 1102ce65a3a9..f4b627d343ac 100644 --- a/drivers/usb/host/ehci-pci.c +++ b/drivers/usb/host/ehci-pci.c @@ -224,6 +224,11 @@ static int ehci_pci_setup(struct usb_hcd *hcd) pci_dev_put(p_smbus); } break; + case PCI_VENDOR_ID_NETMOS: + /* MosChip frame-index-register bug */ + ehci_info(ehci, "applying MosChip frame-index workaround\n"); + ehci->frame_index_bug = 1; + break; } /* optional debug port, normally in the first BAR */ @@ -439,7 +444,7 @@ static int ehci_pci_resume(struct usb_hcd *hcd, bool hibernated) /* here we "know" root ports should always stay powered */ ehci_port_power(ehci, 1); - hcd->state = HC_STATE_SUSPENDED; + ehci->rh_state = EHCI_RH_SUSPENDED; return 0; } #endif diff --git a/drivers/usb/host/ehci-ps3.c b/drivers/usb/host/ehci-ps3.c index 64626a777d61..2dc32da75cfc 100644 --- a/drivers/usb/host/ehci-ps3.c +++ b/drivers/usb/host/ehci-ps3.c @@ -167,7 +167,7 @@ static int __devinit ps3_ehci_probe(struct ps3_system_bus_device *dev) ps3_system_bus_set_drvdata(dev, hcd); - result = usb_add_hcd(hcd, virq, IRQF_DISABLED); + result = usb_add_hcd(hcd, virq, 0); if (result) { dev_dbg(&dev->core, "%s:%d: usb_add_hcd failed (%d)\n", diff --git a/drivers/usb/host/ehci-pxa168.c b/drivers/usb/host/ehci-pxa168.c new file mode 100644 index 000000000000..ac0c16e8f539 --- /dev/null +++ b/drivers/usb/host/ehci-pxa168.c @@ -0,0 +1,363 @@ +/* + * drivers/usb/host/ehci-pxa168.c + * + * Tanmay Upadhyay <tanmay.upadhyay@einfochips.com> + * + * Based on drivers/usb/host/ehci-orion.c + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/clk.h> +#include <mach/pxa168.h> + +#define USB_PHY_CTRL_REG 0x4 +#define USB_PHY_PLL_REG 0x8 +#define USB_PHY_TX_REG 0xc + +#define FBDIV_SHIFT 4 + +#define ICP_SHIFT 12 +#define ICP_15 2 +#define ICP_20 3 +#define ICP_25 4 + +#define KVCO_SHIFT 15 + +#define PLLCALI12_SHIFT 25 +#define CALI12_VDD 0 +#define CALI12_09 1 +#define CALI12_10 2 +#define CALI12_11 3 + +#define PLLVDD12_SHIFT 27 +#define VDD12_VDD 0 +#define VDD12_10 1 +#define VDD12_11 2 +#define VDD12_12 3 + +#define PLLVDD18_SHIFT 29 +#define VDD18_19 0 +#define VDD18_20 1 +#define VDD18_21 2 +#define VDD18_22 3 + + +#define PLL_READY (1 << 23) +#define VCOCAL_START (1 << 21) +#define REG_RCAL_START (1 << 12) + +struct pxa168_usb_drv_data { + struct ehci_hcd ehci; + struct clk *pxa168_usb_clk; + struct resource *usb_phy_res; + void __iomem *usb_phy_reg_base; +}; + +static int ehci_pxa168_setup(struct usb_hcd *hcd) +{ + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + int retval; + + ehci_reset(ehci); + retval = ehci_halt(ehci); + if (retval) + return retval; + + /* + * data structure init + */ + retval = ehci_init(hcd); + if (retval) + return retval; + + hcd->has_tt = 1; + + ehci_port_power(ehci, 0); + + return retval; +} + +static const struct hc_driver ehci_pxa168_hc_driver = { + .description = hcd_name, + .product_desc = "Marvell PXA168 EHCI", + .hcd_priv_size = sizeof(struct pxa168_usb_drv_data), + + /* + * generic hardware linkage + */ + .irq = ehci_irq, + .flags = HCD_MEMORY | HCD_USB2, + + /* + * basic lifecycle operations + */ + .reset = ehci_pxa168_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, + .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, + .relinquish_port = ehci_relinquish_port, + .port_handed_over = ehci_port_handed_over, + + .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, +}; + +static int pxa168_usb_phy_init(struct platform_device *pdev) +{ + struct resource *res; + void __iomem *usb_phy_reg_base; + struct pxa168_usb_pdata *pdata; + struct pxa168_usb_drv_data *drv_data; + struct usb_hcd *hcd = platform_get_drvdata(pdev); + unsigned long reg_val; + int pll_retry_cont = 10000, err = 0; + + drv_data = (struct pxa168_usb_drv_data *)hcd->hcd_priv; + pdata = (struct pxa168_usb_pdata *)pdev->dev.platform_data; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!res) { + dev_err(&pdev->dev, + "Found HC with no PHY register addr. Check %s setup!\n", + dev_name(&pdev->dev)); + return -ENODEV; + } + + if (!request_mem_region(res->start, resource_size(res), + ehci_pxa168_hc_driver.description)) { + dev_dbg(&pdev->dev, "controller already in use\n"); + return -EBUSY; + } + + usb_phy_reg_base = ioremap(res->start, resource_size(res)); + if (usb_phy_reg_base == NULL) { + dev_dbg(&pdev->dev, "error mapping memory\n"); + err = -EFAULT; + goto err1; + } + drv_data->usb_phy_reg_base = usb_phy_reg_base; + drv_data->usb_phy_res = res; + + /* If someone wants to init USB phy in board specific way */ + if (pdata && pdata->phy_init) + return pdata->phy_init(usb_phy_reg_base); + + /* Power up the PHY and PLL */ + writel(readl(usb_phy_reg_base + USB_PHY_CTRL_REG) | 0x3, + usb_phy_reg_base + USB_PHY_CTRL_REG); + + /* Configure PHY PLL */ + reg_val = readl(usb_phy_reg_base + USB_PHY_PLL_REG) & ~(0x7e03ffff); + reg_val |= (VDD18_22 << PLLVDD18_SHIFT | VDD12_12 << PLLVDD12_SHIFT | + CALI12_11 << PLLCALI12_SHIFT | 3 << KVCO_SHIFT | + ICP_15 << ICP_SHIFT | 0xee << FBDIV_SHIFT | 0xb); + writel(reg_val, usb_phy_reg_base + USB_PHY_PLL_REG); + + /* Make sure PHY PLL is ready */ + while (!(readl(usb_phy_reg_base + USB_PHY_PLL_REG) & PLL_READY)) { + if (!(pll_retry_cont--)) { + dev_dbg(&pdev->dev, "USB PHY PLL not ready\n"); + err = -EIO; + goto err2; + } + } + + /* Toggle VCOCAL_START bit of U2PLL for PLL calibration */ + udelay(200); + writel(readl(usb_phy_reg_base + USB_PHY_PLL_REG) | VCOCAL_START, + usb_phy_reg_base + USB_PHY_PLL_REG); + udelay(40); + writel(readl(usb_phy_reg_base + USB_PHY_PLL_REG) & ~VCOCAL_START, + usb_phy_reg_base + USB_PHY_PLL_REG); + + /* Toggle REG_RCAL_START bit of U2PTX for impedance calibration */ + udelay(400); + writel(readl(usb_phy_reg_base + USB_PHY_TX_REG) | REG_RCAL_START, + usb_phy_reg_base + USB_PHY_TX_REG); + udelay(40); + writel(readl(usb_phy_reg_base + USB_PHY_TX_REG) & ~REG_RCAL_START, + usb_phy_reg_base + USB_PHY_TX_REG); + + /* Make sure PHY PLL is ready again */ + pll_retry_cont = 0; + while (!(readl(usb_phy_reg_base + USB_PHY_PLL_REG) & PLL_READY)) { + if (!(pll_retry_cont--)) { + dev_dbg(&pdev->dev, "USB PHY PLL not ready\n"); + err = -EIO; + goto err2; + } + } + + return 0; +err2: + iounmap(usb_phy_reg_base); +err1: + release_mem_region(res->start, resource_size(res)); + return err; +} + +static int __devinit ehci_pxa168_drv_probe(struct platform_device *pdev) +{ + struct resource *res; + struct usb_hcd *hcd; + struct ehci_hcd *ehci; + struct pxa168_usb_drv_data *drv_data; + void __iomem *regs; + int irq, err = 0; + + if (usb_disabled()) + return -ENODEV; + + pr_debug("Initializing pxa168-SoC USB Host Controller\n"); + + irq = platform_get_irq(pdev, 0); + if (irq <= 0) { + dev_err(&pdev->dev, + "Found HC with no IRQ. Check %s setup!\n", + dev_name(&pdev->dev)); + err = -ENODEV; + goto err1; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, + "Found HC with no register addr. Check %s setup!\n", + dev_name(&pdev->dev)); + err = -ENODEV; + goto err1; + } + + if (!request_mem_region(res->start, resource_size(res), + ehci_pxa168_hc_driver.description)) { + dev_dbg(&pdev->dev, "controller already in use\n"); + err = -EBUSY; + goto err1; + } + + regs = ioremap(res->start, resource_size(res)); + if (regs == NULL) { + dev_dbg(&pdev->dev, "error mapping memory\n"); + err = -EFAULT; + goto err2; + } + + hcd = usb_create_hcd(&ehci_pxa168_hc_driver, + &pdev->dev, dev_name(&pdev->dev)); + if (!hcd) { + err = -ENOMEM; + goto err3; + } + + drv_data = (struct pxa168_usb_drv_data *)hcd->hcd_priv; + + /* Enable USB clock */ + drv_data->pxa168_usb_clk = clk_get(&pdev->dev, "PXA168-USBCLK"); + if (IS_ERR(drv_data->pxa168_usb_clk)) { + dev_err(&pdev->dev, "Couldn't get USB clock\n"); + err = PTR_ERR(drv_data->pxa168_usb_clk); + goto err4; + } + clk_enable(drv_data->pxa168_usb_clk); + + err = pxa168_usb_phy_init(pdev); + if (err) { + dev_err(&pdev->dev, "USB PHY initialization failed\n"); + goto err5; + } + + hcd->rsrc_start = res->start; + hcd->rsrc_len = resource_size(res); + hcd->regs = regs; + + ehci = hcd_to_ehci(hcd); + ehci->caps = hcd->regs + 0x100; + ehci->regs = hcd->regs + 0x100 + + HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase)); + ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); + hcd->has_tt = 1; + ehci->sbrn = 0x20; + + err = usb_add_hcd(hcd, irq, IRQF_SHARED | IRQF_DISABLED); + if (err) + goto err5; + + return 0; + +err5: + clk_disable(drv_data->pxa168_usb_clk); + clk_put(drv_data->pxa168_usb_clk); +err4: + usb_put_hcd(hcd); +err3: + iounmap(regs); +err2: + release_mem_region(res->start, resource_size(res)); +err1: + dev_err(&pdev->dev, "init %s fail, %d\n", + dev_name(&pdev->dev), err); + + return err; +} + +static int __exit ehci_pxa168_drv_remove(struct platform_device *pdev) +{ + struct usb_hcd *hcd = platform_get_drvdata(pdev); + struct pxa168_usb_drv_data *drv_data = + (struct pxa168_usb_drv_data *)hcd->hcd_priv; + + usb_remove_hcd(hcd); + + /* Power down PHY & PLL */ + writel(readl(drv_data->usb_phy_reg_base + USB_PHY_CTRL_REG) & (~0x3), + drv_data->usb_phy_reg_base + USB_PHY_CTRL_REG); + + clk_disable(drv_data->pxa168_usb_clk); + clk_put(drv_data->pxa168_usb_clk); + + iounmap(hcd->regs); + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); + + iounmap(drv_data->usb_phy_reg_base); + release_mem_region(drv_data->usb_phy_res->start, + resource_size(drv_data->usb_phy_res)); + + usb_put_hcd(hcd); + + return 0; +} + +MODULE_ALIAS("platform:pxa168-ehci"); + +static struct platform_driver ehci_pxa168_driver = { + .probe = ehci_pxa168_drv_probe, + .remove = __exit_p(ehci_pxa168_drv_remove), + .shutdown = usb_hcd_platform_shutdown, + .driver.name = "pxa168-ehci", +}; diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index 0917e3a32465..4e4066c35a09 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -111,8 +111,6 @@ qh_update (struct ehci_hcd *ehci, struct ehci_qh *qh, struct ehci_qtd *qtd) } } - /* HC must see latest qtd and qh data before we clear ACTIVE+HALT */ - wmb (); hw->hw_token &= cpu_to_hc32(ehci, QTD_TOGGLE | QTD_STS_PING); } @@ -153,7 +151,7 @@ static void ehci_clear_tt_buffer_complete(struct usb_hcd *hcd, spin_lock_irqsave(&ehci->lock, flags); qh->clearing_tt = 0; if (qh->qh_state == QH_STATE_IDLE && !list_empty(&qh->qtd_list) - && HC_IS_RUNNING(hcd->state)) + && ehci->rh_state == EHCI_RH_RUNNING) qh_link_async(ehci, qh); spin_unlock_irqrestore(&ehci->lock, flags); } @@ -425,7 +423,7 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh) /* stop scanning when we reach qtds the hc is using */ } else if (likely (!stopped - && HC_IS_RUNNING (ehci_to_hcd(ehci)->state))) { + && ehci->rh_state == EHCI_RH_RUNNING)) { break; /* scan the whole queue for unlinks whenever it stops */ @@ -433,7 +431,7 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh) stopped = 1; /* cancel everything if we halt, suspend, etc */ - if (!HC_IS_RUNNING(ehci_to_hcd(ehci)->state)) + if (ehci->rh_state != EHCI_RH_RUNNING) last_status = -ESHUTDOWN; /* this qtd is active; skip it unless a previous qtd @@ -724,7 +722,8 @@ qh_urb_transaction ( /* * control requests may need a terminating data "status" ack; - * bulk ones may need a terminating short packet (zero length). + * other OUT ones may need a terminating short packet + * (zero length). */ if (likely (urb->transfer_buffer_length != 0)) { int one_more = 0; @@ -733,7 +732,7 @@ qh_urb_transaction ( one_more = 1; token ^= 0x0100; /* "in" <--> "out" */ token |= QTD_TOGGLE; /* force DATA1 */ - } else if (usb_pipebulk (urb->pipe) + } else if (usb_pipeout(urb->pipe) && (urb->transfer_flags & URB_ZERO_PACKET) && !(urb->transfer_buffer_length % maxpacket)) { one_more = 1; @@ -977,9 +976,8 @@ static void qh_link_async (struct ehci_hcd *ehci, struct ehci_qh *qh) /* in case a clear of CMD_ASE didn't take yet */ (void)handshake(ehci, &ehci->regs->status, STS_ASS, 0, 150); - cmd |= CMD_ASE | CMD_RUN; + cmd |= CMD_ASE; ehci_writel(ehci, cmd, &ehci->regs->command); - ehci_to_hcd(ehci)->state = HC_STATE_RUNNING; /* posted write need not be known to HC yet ... */ } } @@ -1058,7 +1056,7 @@ static struct ehci_qh *qh_append_tds ( */ token = qtd->hw_token; qtd->hw_token = HALT_BIT(ehci); - wmb (); + dummy = qh->dummy; dma = dummy->qtd_dma; @@ -1168,14 +1166,13 @@ static void end_unlink_async (struct ehci_hcd *ehci) qh_completions (ehci, qh); - if (!list_empty (&qh->qtd_list) - && HC_IS_RUNNING (ehci_to_hcd(ehci)->state)) + if (!list_empty(&qh->qtd_list) && ehci->rh_state == EHCI_RH_RUNNING) { qh_link_async (ehci, qh); - else { + } else { /* it's not free to turn the async schedule on/off; leave it * active but idle for a while once it empties. */ - if (HC_IS_RUNNING (ehci_to_hcd(ehci)->state) + if (ehci->rh_state == EHCI_RH_RUNNING && ehci->async->qh_next.qh == NULL) timer_action (ehci, TIMER_ASYNC_OFF); } @@ -1211,7 +1208,7 @@ static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh) /* stop async schedule right now? */ if (unlikely (qh == ehci->async)) { /* can't get here without STS_ASS set */ - if (ehci_to_hcd(ehci)->state != HC_STATE_HALT + if (ehci->rh_state != EHCI_RH_HALTED && !ehci->reclaim) { /* ... and CMD_IAAD clear */ ehci_writel(ehci, cmd & ~CMD_ASE, @@ -1237,7 +1234,7 @@ static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh) wmb (); /* If the controller isn't running, we don't have to wait for it */ - if (unlikely(!HC_IS_RUNNING(ehci_to_hcd(ehci)->state))) { + if (unlikely(ehci->rh_state != EHCI_RH_RUNNING)) { /* if (unlikely (qh->reclaim != 0)) * this will recurse, probably not much */ @@ -1260,7 +1257,7 @@ static void scan_async (struct ehci_hcd *ehci) enum ehci_timer_action action = TIMER_IO_WATCHDOG; timer_action_done (ehci, TIMER_ASYNC_SHRINK); - stopped = !HC_IS_RUNNING(ehci_to_hcd(ehci)->state); + stopped = (ehci->rh_state != EHCI_RH_RUNNING); ehci->qh_scan_next = ehci->async->qh_next.qh; while (ehci->qh_scan_next) { diff --git a/drivers/usb/host/ehci-s5p.c b/drivers/usb/host/ehci-s5p.c index 9e77f1c8bdbd..024b65c4990d 100644 --- a/drivers/usb/host/ehci-s5p.c +++ b/drivers/usb/host/ehci-s5p.c @@ -136,7 +136,7 @@ static int __devinit s5p_ehci_probe(struct platform_device *pdev) /* cache this readonly data; minimize chip reads */ ehci->hcs_params = readl(&ehci->caps->hcs_params); - err = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED); + err = usb_add_hcd(hcd, irq, IRQF_SHARED); if (err) { dev_err(&pdev->dev, "Failed to add USB HCD\n"); goto fail; @@ -270,7 +270,7 @@ static int s5p_ehci_resume(struct device *dev) /* here we "know" root ports should always stay powered */ ehci_port_power(ehci, 1); - hcd->state = HC_STATE_SUSPENDED; + ehci->rh_state = EHCI_RH_SUSPENDED; return 0; } diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index 2abf8543f083..2e829fae6482 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -36,6 +36,27 @@ static int ehci_get_frame (struct usb_hcd *hcd); +#ifdef CONFIG_PCI + +static unsigned ehci_read_frame_index(struct ehci_hcd *ehci) +{ + unsigned uf; + + /* + * The MosChip MCS9990 controller updates its microframe counter + * a little before the frame counter, and occasionally we will read + * the invalid intermediate value. Avoid problems by checking the + * microframe number (the low-order 3 bits); if they are 0 then + * re-read the register to get the correct value. + */ + uf = ehci_readl(ehci, &ehci->regs->frame_index); + if (unlikely(ehci->frame_index_bug && ((uf & 7) == 0))) + uf = ehci_readl(ehci, &ehci->regs->frame_index); + return uf; +} + +#endif + /*-------------------------------------------------------------------------*/ /* @@ -479,10 +500,9 @@ static int enable_periodic (struct ehci_hcd *ehci) cmd = ehci_readl(ehci, &ehci->regs->command) | CMD_PSE; ehci_writel(ehci, cmd, &ehci->regs->command); /* posted write ... PSS happens later */ - ehci_to_hcd(ehci)->state = HC_STATE_RUNNING; /* make sure ehci_work scans these */ - ehci->next_uframe = ehci_readl(ehci, &ehci->regs->frame_index) + ehci->next_uframe = ehci_read_frame_index(ehci) % (ehci->periodic_size << 3); if (unlikely(ehci->broken_periodic)) ehci->last_periodic_enable = ktime_get_real(); @@ -677,7 +697,7 @@ static void intr_deschedule (struct ehci_hcd *ehci, struct ehci_qh *qh) /* reschedule QH iff another request is queued */ if (!list_empty(&qh->qtd_list) && - HC_IS_RUNNING(ehci_to_hcd(ehci)->state)) { + ehci->rh_state == EHCI_RH_RUNNING) { rc = qh_schedule(ehci, qh); /* An error here likely indicates handshake failure @@ -1409,7 +1429,7 @@ iso_stream_schedule ( goto fail; } - now = ehci_readl(ehci, &ehci->regs->frame_index) & (mod - 1); + now = ehci_read_frame_index(ehci) & (mod - 1); /* Typical case: reuse current schedule, stream is still active. * Hopefully there are no gaps from the host falling behind @@ -2275,8 +2295,8 @@ scan_periodic (struct ehci_hcd *ehci) * Touches as few pages as possible: cache-friendly. */ now_uframe = ehci->next_uframe; - if (HC_IS_RUNNING(ehci_to_hcd(ehci)->state)) { - clock = ehci_readl(ehci, &ehci->regs->frame_index); + if (ehci->rh_state == EHCI_RH_RUNNING) { + clock = ehci_read_frame_index(ehci); clock_frame = (clock >> 3) & (ehci->periodic_size - 1); } else { clock = now_uframe + mod - 1; @@ -2310,7 +2330,7 @@ restart: union ehci_shadow temp; int live; - live = HC_IS_RUNNING (ehci_to_hcd(ehci)->state); + live = (ehci->rh_state == EHCI_RH_RUNNING); switch (hc32_to_cpu(ehci, type)) { case Q_TYPE_QH: /* handle any completions */ @@ -2435,7 +2455,7 @@ restart: * We can't advance our scan without collecting the ISO * transfers that are still pending in this frame. */ - if (incomplete && HC_IS_RUNNING(ehci_to_hcd(ehci)->state)) { + if (incomplete && ehci->rh_state == EHCI_RH_RUNNING) { ehci->next_uframe = now_uframe; break; } @@ -2451,12 +2471,11 @@ restart: if (now_uframe == clock) { unsigned now; - if (!HC_IS_RUNNING (ehci_to_hcd(ehci)->state) + if (ehci->rh_state != EHCI_RH_RUNNING || ehci->periodic_sched == 0) break; ehci->next_uframe = now_uframe; - now = ehci_readl(ehci, &ehci->regs->frame_index) & - (mod - 1); + now = ehci_read_frame_index(ehci) & (mod - 1); if (now_uframe == now) break; diff --git a/drivers/usb/host/ehci-sh.c b/drivers/usb/host/ehci-sh.c index 86a95bb80a61..9d9cf47d80da 100644 --- a/drivers/usb/host/ehci-sh.c +++ b/drivers/usb/host/ehci-sh.c @@ -168,7 +168,7 @@ static int ehci_hcd_sh_probe(struct platform_device *pdev) clk_enable(priv->fclk); clk_enable(priv->iclk); - ret = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED); + ret = usb_add_hcd(hcd, irq, IRQF_SHARED); if (ret != 0) { dev_err(&pdev->dev, "Failed to add hcd"); goto fail_add_hcd; diff --git a/drivers/usb/host/ehci-spear.c b/drivers/usb/host/ehci-spear.c index dbf1e4ef3c17..b115b0b76e33 100644 --- a/drivers/usb/host/ehci-spear.c +++ b/drivers/usb/host/ehci-spear.c @@ -154,7 +154,7 @@ static int spear_ehci_hcd_drv_probe(struct platform_device *pdev) ehci->clk = usbh_clk; spear_start_ehci(ehci); - retval = usb_add_hcd(hcd, irq, IRQF_SHARED | IRQF_DISABLED); + retval = usb_add_hcd(hcd, irq, IRQF_SHARED); if (retval) goto fail_add_hcd; diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index 02b2bfd49a10..db9d1b4bfbdc 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -674,7 +674,7 @@ static int tegra_ehci_probe(struct platform_device *pdev) } #endif - err = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED); + err = usb_add_hcd(hcd, irq, IRQF_SHARED); if (err) { dev_err(&pdev->dev, "Failed to add USB HCD\n"); goto fail; diff --git a/drivers/usb/host/ehci-vt8500.c b/drivers/usb/host/ehci-vt8500.c index 47d749631bc7..54d1ab8aec49 100644 --- a/drivers/usb/host/ehci-vt8500.c +++ b/drivers/usb/host/ehci-vt8500.c @@ -133,7 +133,7 @@ static int vt8500_ehci_drv_probe(struct platform_device *pdev) ehci_port_power(ehci, 1); ret = usb_add_hcd(hcd, pdev->resource[1].start, - IRQF_DISABLED | IRQF_SHARED); + IRQF_SHARED); if (ret == 0) { platform_set_drvdata(pdev, hcd); return ret; diff --git a/drivers/usb/host/ehci-xls.c b/drivers/usb/host/ehci-xls.c new file mode 100644 index 000000000000..fe74bd676018 --- /dev/null +++ b/drivers/usb/host/ehci-xls.c @@ -0,0 +1,161 @@ +/* + * EHCI HCD for Netlogic XLS processors. + * + * (C) Copyright 2011 Netlogic Microsystems Inc. + * + * Based on various ehci-*.c drivers + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive for + * more details. + */ + +#include <linux/platform_device.h> + +static int ehci_xls_setup(struct usb_hcd *hcd) +{ + int retval; + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + + ehci->caps = hcd->regs; + ehci->regs = hcd->regs + + 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; + + ehci_reset(ehci); + + return retval; +} + +int ehci_xls_probe_internal(const struct hc_driver *driver, + struct platform_device *pdev) +{ + struct usb_hcd *hcd; + struct resource *res; + int retval, irq; + + /* Get our IRQ from an earlier registered Platform Resource */ + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "Found HC with no IRQ. Check %s setup!\n", + dev_name(&pdev->dev)); + return -ENODEV; + } + + /* Get our Memory Handle */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "Error: MMIO Handle %s setup!\n", + dev_name(&pdev->dev)); + return -ENODEV; + } + hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev)); + if (!hcd) { + retval = -ENOMEM; + goto err1; + } + + hcd->rsrc_start = res->start; + hcd->rsrc_len = res->end - res->start + 1; + + if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, + driver->description)) { + dev_dbg(&pdev->dev, "controller already in use\n"); + retval = -EBUSY; + goto err2; + } + hcd->regs = ioremap_nocache(hcd->rsrc_start, hcd->rsrc_len); + + if (hcd->regs == NULL) { + dev_dbg(&pdev->dev, "error mapping memory\n"); + retval = -EFAULT; + goto err3; + } + + retval = usb_add_hcd(hcd, irq, IRQF_SHARED); + if (retval != 0) + goto err4; + return retval; + +err4: + iounmap(hcd->regs); +err3: + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); +err2: + usb_put_hcd(hcd); +err1: + dev_err(&pdev->dev, "init %s fail, %d\n", dev_name(&pdev->dev), + retval); + return retval; +} + +static struct hc_driver ehci_xls_hc_driver = { + .description = hcd_name, + .product_desc = "XLS EHCI Host Controller", + .hcd_priv_size = sizeof(struct ehci_hcd), + .irq = ehci_irq, + .flags = HCD_USB2 | HCD_MEMORY, + .reset = ehci_xls_setup, + .start = ehci_run, + .stop = ehci_stop, + .shutdown = ehci_shutdown, + + .urb_enqueue = ehci_urb_enqueue, + .urb_dequeue = ehci_urb_dequeue, + .endpoint_disable = ehci_endpoint_disable, + .endpoint_reset = ehci_endpoint_reset, + + .get_frame_number = ehci_get_frame, + + .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, + + .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, +}; + +static int ehci_xls_probe(struct platform_device *pdev) +{ + if (usb_disabled()) + return -ENODEV; + + return ehci_xls_probe_internal(&ehci_xls_hc_driver, pdev); +} + +static int ehci_xls_remove(struct platform_device *pdev) +{ + struct usb_hcd *hcd = platform_get_drvdata(pdev); + + usb_remove_hcd(hcd); + iounmap(hcd->regs); + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); + usb_put_hcd(hcd); + return 0; +} + +MODULE_ALIAS("ehci-xls"); + +static struct platform_driver ehci_xls_driver = { + .probe = ehci_xls_probe, + .remove = ehci_xls_remove, + .shutdown = usb_hcd_platform_shutdown, + .driver = { + .name = "ehci-xls", + }, +}; diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index cc7d337ec355..0a5fda73b3f2 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -62,6 +62,12 @@ struct ehci_stats { #define EHCI_MAX_ROOT_PORTS 15 /* see HCS_N_PORTS */ +enum ehci_rh_state { + EHCI_RH_HALTED, + EHCI_RH_SUSPENDED, + EHCI_RH_RUNNING +}; + struct ehci_hcd { /* one per controller */ /* glue to PCI and HCD framework */ struct ehci_caps __iomem *caps; @@ -70,6 +76,7 @@ struct ehci_hcd { /* one per controller */ __u32 hcs_params; /* cached register copy */ spinlock_t lock; + enum ehci_rh_state rh_state; /* async schedule support */ struct ehci_qh *async; @@ -139,6 +146,7 @@ struct ehci_hcd { /* one per controller */ unsigned fs_i_thresh:1; /* Intel iso scheduling */ unsigned use_dummy_qh:1; /* AMD Frame List table quirk*/ unsigned has_synopsys_hc_bug:1; /* Synopsys HC */ + unsigned frame_index_bug:1; /* MosChip (AKA NetMos) */ /* required for usb32 quirk */ #define OHCI_CTRL_HCFS (3 << 6) @@ -740,6 +748,22 @@ static inline u32 hc32_to_cpup (const struct ehci_hcd *ehci, const __hc32 *x) /*-------------------------------------------------------------------------*/ +#ifdef CONFIG_PCI + +/* For working around the MosChip frame-index-register bug */ +static unsigned ehci_read_frame_index(struct ehci_hcd *ehci); + +#else + +static inline unsigned ehci_read_frame_index(struct ehci_hcd *ehci) +{ + return ehci_readl(ehci, &ehci->regs->frame_index); +} + +#endif + +/*-------------------------------------------------------------------------*/ + #ifndef DEBUG #define STUB_DEBUG_FILES #endif /* DEBUG */ diff --git a/drivers/usb/host/fhci-hcd.c b/drivers/usb/host/fhci-hcd.c index 572ea53b0226..4ed6d19f2a54 100644 --- a/drivers/usb/host/fhci-hcd.c +++ b/drivers/usb/host/fhci-hcd.c @@ -621,12 +621,15 @@ static int __devinit of_fhci_probe(struct platform_device *ofdev) goto err_pram; } - pram_addr = cpm_muram_alloc_fixed(iprop[2], FHCI_PRAM_SIZE); + pram_addr = cpm_muram_alloc(FHCI_PRAM_SIZE, 64); if (IS_ERR_VALUE(pram_addr)) { dev_err(dev, "failed to allocate usb pram\n"); ret = -ENOMEM; goto err_pram; } + + qe_issue_cmd(QE_ASSIGN_PAGE_TO_DEVICE, QE_CR_SUBBLOCK_USB, + QE_CR_PROTOCOL_UNSPECIFIED, pram_addr); fhci->pram = cpm_muram_addr(pram_addr); /* GPIOs and pins */ @@ -686,7 +689,7 @@ static int __devinit of_fhci_probe(struct platform_device *ofdev) } ret = request_irq(fhci->timer->irq, fhci_frame_limit_timer_irq, - IRQF_DISABLED, "qe timer (usb)", hcd); + 0, "qe timer (usb)", hcd); if (ret) { dev_err(dev, "failed to request timer irq"); goto err_timer_irq; @@ -745,7 +748,7 @@ static int __devinit of_fhci_probe(struct platform_device *ofdev) out_be16(&fhci->regs->usb_event, 0xffff); out_be16(&fhci->regs->usb_mask, 0); - ret = usb_add_hcd(hcd, usb_irq, IRQF_DISABLED); + ret = usb_add_hcd(hcd, usb_irq, 0); if (ret < 0) goto err_add_hcd; diff --git a/drivers/usb/host/fhci-sched.c b/drivers/usb/host/fhci-sched.c index a42ef380e917..2df851b4bc7c 100644 --- a/drivers/usb/host/fhci-sched.c +++ b/drivers/usb/host/fhci-sched.c @@ -1,7 +1,7 @@ /* * Freescale QUICC Engine USB Host Controller Driver * - * Copyright (c) Freescale Semicondutor, Inc. 2006. + * Copyright (c) Freescale Semicondutor, Inc. 2006, 2011. * Shlomi Gridish <gridish@freescale.com> * Jerry Huang <Chang-Ming.Huang@freescale.com> * Copyright (c) Logic Product Development, Inc. 2007 @@ -810,9 +810,11 @@ void fhci_queue_urb(struct fhci_hcd *fhci, struct urb *urb) ed->dev_addr = usb_pipedevice(urb->pipe); ed->max_pkt_size = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)); + /* setup stage */ td = fhci_td_fill(fhci, urb, urb_priv, ed, cnt++, FHCI_TA_SETUP, USB_TD_TOGGLE_DATA0, urb->setup_packet, 8, 0, 0, true); + /* data stage */ if (data_len > 0) { td = fhci_td_fill(fhci, urb, urb_priv, ed, cnt++, usb_pipeout(urb->pipe) ? FHCI_TA_OUT : @@ -820,9 +822,18 @@ void fhci_queue_urb(struct fhci_hcd *fhci, struct urb *urb) USB_TD_TOGGLE_DATA1, data, data_len, 0, 0, true); } - td = fhci_td_fill(fhci, urb, urb_priv, ed, cnt++, - usb_pipeout(urb->pipe) ? FHCI_TA_IN : FHCI_TA_OUT, - USB_TD_TOGGLE_DATA1, data, 0, 0, 0, true); + + /* status stage */ + if (data_len > 0) + td = fhci_td_fill(fhci, urb, urb_priv, ed, cnt++, + (usb_pipeout(urb->pipe) ? FHCI_TA_IN : + FHCI_TA_OUT), + USB_TD_TOGGLE_DATA1, data, 0, 0, 0, true); + else + td = fhci_td_fill(fhci, urb, urb_priv, ed, cnt++, + FHCI_TA_IN, + USB_TD_TOGGLE_DATA1, data, 0, 0, 0, true); + urb_state = US_CTRL_SETUP; break; case FHCI_TF_ISO: diff --git a/drivers/usb/host/imx21-hcd.c b/drivers/usb/host/imx21-hcd.c index af05718bdc73..2ee18cfa1efe 100644 --- a/drivers/usb/host/imx21-hcd.c +++ b/drivers/usb/host/imx21-hcd.c @@ -1891,7 +1891,7 @@ static int imx21_probe(struct platform_device *pdev) dev_info(imx21->dev, "Hardware HC revision: 0x%02X\n", (readl(imx21->regs + USBOTG_HWMODE) >> 16) & 0xFF); - ret = usb_add_hcd(hcd, irq, IRQF_DISABLED); + ret = usb_add_hcd(hcd, irq, 0); if (ret != 0) { dev_err(imx21->dev, "usb_add_hcd() returned %d\n", ret); goto failed_add_hcd; diff --git a/drivers/usb/host/isp116x-hcd.c b/drivers/usb/host/isp116x-hcd.c index baae4ccd16ac..d91e5f211a76 100644 --- a/drivers/usb/host/isp116x-hcd.c +++ b/drivers/usb/host/isp116x-hcd.c @@ -1639,7 +1639,7 @@ static int __devinit isp116x_probe(struct platform_device *pdev) goto err6; } - ret = usb_add_hcd(hcd, irq, irqflags | IRQF_DISABLED); + ret = usb_add_hcd(hcd, irq, irqflags); if (ret) goto err6; diff --git a/drivers/usb/host/isp1362-hcd.c b/drivers/usb/host/isp1362-hcd.c index 9c37dad3e816..e5fd8aa57af1 100644 --- a/drivers/usb/host/isp1362-hcd.c +++ b/drivers/usb/host/isp1362-hcd.c @@ -2358,7 +2358,7 @@ static int isp1362_hc_reset(struct usb_hcd *hcd) unsigned long flags; int clkrdy = 0; - pr_info("%s:\n", __func__); + pr_debug("%s:\n", __func__); if (isp1362_hcd->board && isp1362_hcd->board->reset) { isp1362_hcd->board->reset(hcd->self.controller, 1); @@ -2395,7 +2395,7 @@ static void isp1362_hc_stop(struct usb_hcd *hcd) unsigned long flags; u32 tmp; - pr_info("%s:\n", __func__); + pr_debug("%s:\n", __func__); del_timer_sync(&hcd->rh_timer); @@ -2523,7 +2523,7 @@ static int isp1362_hc_start(struct usb_hcd *hcd) u16 chipid; unsigned long flags; - pr_info("%s:\n", __func__); + pr_debug("%s:\n", __func__); spin_lock_irqsave(&isp1362_hcd->lock, flags); chipid = isp1362_read_reg16(isp1362_hcd, HCCHIPID); @@ -2773,7 +2773,7 @@ static int __devinit isp1362_probe(struct platform_device *pdev) if (irq_res->flags & IORESOURCE_IRQ_LOWLEVEL) irq_flags |= IRQF_TRIGGER_LOW; - retval = usb_add_hcd(hcd, irq, irq_flags | IRQF_DISABLED | IRQF_SHARED); + retval = usb_add_hcd(hcd, irq, irq_flags | IRQF_SHARED); if (retval != 0) goto err6; pr_info("%s, irq %d\n", hcd->product_desc, irq); diff --git a/drivers/usb/host/isp1760-hcd.c b/drivers/usb/host/isp1760-hcd.c index 840beda66dd9..27dfab80ed8f 100644 --- a/drivers/usb/host/isp1760-hcd.c +++ b/drivers/usb/host/isp1760-hcd.c @@ -21,8 +21,10 @@ #include <linux/uaccess.h> #include <linux/io.h> #include <linux/mm.h> +#include <linux/timer.h> #include <asm/unaligned.h> #include <asm/cacheflush.h> +#include <linux/gpio.h> #include "isp1760-hcd.h" @@ -39,7 +41,6 @@ struct isp1760_hcd { int int_done_map; struct memory_chunk memory_pool[BLOCKS]; struct list_head controlqhs, bulkqhs, interruptqhs; - int active_ptds; /* periodic schedule support */ #define DEFAULT_I_TDPS 1024 @@ -48,6 +49,8 @@ struct isp1760_hcd { unsigned long reset_done; unsigned long next_statechange; unsigned int devflags; + + int rst_gpio; }; static inline struct isp1760_hcd *hcd_to_priv(struct usb_hcd *hcd) @@ -114,6 +117,7 @@ struct isp1760_qh { u32 toggle; u32 ping; int slot; + int tt_buffer_dirty; /* See USB2.0 spec section 11.17.5 */ }; struct urb_listitem { @@ -432,6 +436,18 @@ static int isp1760_hc_setup(struct usb_hcd *hcd) int result; u32 scratch, hwmode; + /* low-level chip reset */ + if (gpio_is_valid(priv->rst_gpio)) { + unsigned int rst_lvl; + + rst_lvl = (priv->devflags & + ISP1760_FLAG_RESET_ACTIVE_HIGH) ? 1 : 0; + + gpio_set_value(priv->rst_gpio, rst_lvl); + mdelay(50); + gpio_set_value(priv->rst_gpio, !rst_lvl); + } + /* Setup HW Mode Control: This assumes a level active-low interrupt */ hwmode = HW_DATA_BUS_32BIT; @@ -489,10 +505,6 @@ static int isp1760_hc_setup(struct usb_hcd *hcd) 16 : 32, (priv->devflags & ISP1760_FLAG_ANALOG_OC) ? "analog" : "digital"); - /* This is weird: at the first plug-in of a device there seems to be - one packet queued that never gets returned? */ - priv->active_ptds = -1; - /* ATL reset */ reg_write32(hcd->regs, HC_HW_MODE_CTRL, hwmode | ALL_ATX_RESET); mdelay(10); @@ -514,83 +526,6 @@ static int isp1760_hc_setup(struct usb_hcd *hcd) return priv_init(hcd); } -static void isp1760_init_maps(struct usb_hcd *hcd) -{ - /*set last maps, for iso its only 1, else 32 tds bitmap*/ - reg_write32(hcd->regs, HC_ATL_PTD_LASTPTD_REG, 0x80000000); - reg_write32(hcd->regs, HC_INT_PTD_LASTPTD_REG, 0x80000000); - reg_write32(hcd->regs, HC_ISO_PTD_LASTPTD_REG, 0x00000001); - - reg_write32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG, 0xffffffff); - reg_write32(hcd->regs, HC_INT_PTD_SKIPMAP_REG, 0xffffffff); - reg_write32(hcd->regs, HC_ISO_PTD_SKIPMAP_REG, 0xffffffff); - - reg_write32(hcd->regs, HC_BUFFER_STATUS_REG, - ATL_BUF_FILL | INT_BUF_FILL); -} - -static void isp1760_enable_interrupts(struct usb_hcd *hcd) -{ - reg_write32(hcd->regs, HC_ATL_IRQ_MASK_AND_REG, 0); - reg_write32(hcd->regs, HC_ATL_IRQ_MASK_OR_REG, 0xffffffff); - reg_write32(hcd->regs, HC_INT_IRQ_MASK_AND_REG, 0); - reg_write32(hcd->regs, HC_INT_IRQ_MASK_OR_REG, 0xffffffff); - reg_write32(hcd->regs, HC_ISO_IRQ_MASK_AND_REG, 0); - reg_write32(hcd->regs, HC_ISO_IRQ_MASK_OR_REG, 0xffffffff); - /* step 23 passed */ -} - -static int isp1760_run(struct usb_hcd *hcd) -{ - int retval; - u32 temp; - u32 command; - u32 chipid; - - hcd->uses_new_polling = 1; - - hcd->state = HC_STATE_RUNNING; - isp1760_enable_interrupts(hcd); - temp = reg_read32(hcd->regs, HC_HW_MODE_CTRL); - reg_write32(hcd->regs, HC_HW_MODE_CTRL, temp | HW_GLOBAL_INTR_EN); - - command = reg_read32(hcd->regs, HC_USBCMD); - command &= ~(CMD_LRESET|CMD_RESET); - command |= CMD_RUN; - reg_write32(hcd->regs, HC_USBCMD, command); - - retval = handshake(hcd, HC_USBCMD, CMD_RUN, CMD_RUN, 250 * 1000); - if (retval) - return retval; - - /* - * XXX - * Spec says to write FLAG_CF as last config action, priv code grabs - * the semaphore while doing so. - */ - down_write(&ehci_cf_port_reset_rwsem); - reg_write32(hcd->regs, HC_CONFIGFLAG, FLAG_CF); - - retval = handshake(hcd, HC_CONFIGFLAG, FLAG_CF, FLAG_CF, 250 * 1000); - up_write(&ehci_cf_port_reset_rwsem); - if (retval) - return retval; - - chipid = reg_read32(hcd->regs, HC_CHIP_ID_REG); - dev_info(hcd->self.controller, "USB ISP %04x HW rev. %d started\n", - chipid & 0xffff, chipid >> 16); - - /* PTD Register Init Part 2, Step 28 */ - /* enable INTs */ - isp1760_init_maps(hcd); - - /* GRR this is run-once init(), being done every time the HC starts. - * So long as they're part of class devices, we can't do it init() - * since the class device isn't created that early. - */ - return 0; -} - static u32 base_to_chip(u32 base) { return ((base - 0x400) >> 3); @@ -813,28 +748,29 @@ static void start_bus_transfer(struct usb_hcd *hcd, u32 ptd_offset, int slot, WARN_ON(slots[slot].qh); WARN_ON(qtd->status != QTD_PAYLOAD_ALLOC); - slots[slot].qtd = qtd; - slots[slot].qh = qh; - qh->slot = slot; - qtd->status = QTD_XFER_STARTED; /* Set this before writing ptd, since - interrupt routine may preempt and expects this value. */ - ptd_write(hcd->regs, ptd_offset, slot, ptd); - priv->active_ptds++; - /* Make sure done map has not triggered from some unlinked transfer */ if (ptd_offset == ATL_PTD_OFFSET) { priv->atl_done_map |= reg_read32(hcd->regs, HC_ATL_PTD_DONEMAP_REG); - priv->atl_done_map &= ~(1 << qh->slot); + priv->atl_done_map &= ~(1 << slot); + } else { + priv->int_done_map |= reg_read32(hcd->regs, + HC_INT_PTD_DONEMAP_REG); + priv->int_done_map &= ~(1 << slot); + } + + qh->slot = slot; + qtd->status = QTD_XFER_STARTED; + slots[slot].timestamp = jiffies; + slots[slot].qtd = qtd; + slots[slot].qh = qh; + ptd_write(hcd->regs, ptd_offset, slot, ptd); + if (ptd_offset == ATL_PTD_OFFSET) { skip_map = reg_read32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG); skip_map &= ~(1 << qh->slot); reg_write32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG, skip_map); } else { - priv->int_done_map |= reg_read32(hcd->regs, - HC_INT_PTD_DONEMAP_REG); - priv->int_done_map &= ~(1 << qh->slot); - skip_map = reg_read32(hcd->regs, HC_INT_PTD_SKIPMAP_REG); skip_map &= ~(1 << qh->slot); reg_write32(hcd->regs, HC_INT_PTD_SKIPMAP_REG, skip_map); @@ -858,10 +794,7 @@ static void collect_qtds(struct usb_hcd *hcd, struct isp1760_qh *qh, if (qtd->status < QTD_XFER_COMPLETE) break; - if (list_is_last(&qtd->qtd_list, &qh->qtd_list)) - last_qtd = 1; - else - last_qtd = qtd->urb != qtd_next->urb; + last_qtd = last_qtd_of_urb(qtd, qh); if ((!last_qtd) && (qtd->status == QTD_RETIRE)) qtd_next->status = QTD_RETIRE; @@ -902,7 +835,7 @@ static void collect_qtds(struct usb_hcd *hcd, struct isp1760_qh *qh, urb_listitem = kmem_cache_zalloc(urb_listitem_cachep, GFP_ATOMIC); if (unlikely(!urb_listitem)) - break; + break; /* Try again on next call */ urb_listitem->urb = qtd->urb; list_add_tail(&urb_listitem->urb_list, urb_list); } @@ -928,6 +861,10 @@ static void enqueue_qtds(struct usb_hcd *hcd, struct isp1760_qh *qh) return; } + /* Make sure this endpoint's TT buffer is clean before queueing ptds */ + if (qh->tt_buffer_dirty) + return; + if (usb_pipeint(list_entry(qh->qtd_list.next, struct isp1760_qtd, qtd_list)->urb->pipe)) { ptd_offset = INT_PTD_OFFSET; @@ -1168,11 +1105,9 @@ static int check_atl_transfer(struct usb_hcd *hcd, struct ptd *ptd, return PTD_STATE_QTD_DONE; } -static irqreturn_t isp1760_irq(struct usb_hcd *hcd) +static void handle_done_ptds(struct usb_hcd *hcd) { struct isp1760_hcd *priv = hcd_to_priv(hcd); - u32 imask; - irqreturn_t irqret = IRQ_NONE; struct ptd ptd; struct isp1760_qh *qh; int slot; @@ -1181,27 +1116,14 @@ static irqreturn_t isp1760_irq(struct usb_hcd *hcd) u32 ptd_offset; struct isp1760_qtd *qtd; int modified; - static int last_active_ptds; - int int_skip_map, atl_skip_map; - - spin_lock(&priv->lock); - - if (!(hcd->state & HC_STATE_RUNNING)) - goto leave; - - imask = reg_read32(hcd->regs, HC_INTERRUPT_REG); - if (unlikely(!imask)) - goto leave; - reg_write32(hcd->regs, HC_INTERRUPT_REG, imask); /* Clear */ + int skip_map; - int_skip_map = reg_read32(hcd->regs, HC_INT_PTD_SKIPMAP_REG); - atl_skip_map = reg_read32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG); - priv->int_done_map |= reg_read32(hcd->regs, HC_INT_PTD_DONEMAP_REG); - priv->atl_done_map |= reg_read32(hcd->regs, HC_ATL_PTD_DONEMAP_REG); - priv->int_done_map &= ~int_skip_map; - priv->atl_done_map &= ~atl_skip_map; + skip_map = reg_read32(hcd->regs, HC_INT_PTD_SKIPMAP_REG); + priv->int_done_map &= ~skip_map; + skip_map = reg_read32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG); + priv->atl_done_map &= ~skip_map; - modified = priv->int_done_map | priv->atl_done_map; + modified = priv->int_done_map || priv->atl_done_map; while (priv->int_done_map || priv->atl_done_map) { if (priv->int_done_map) { @@ -1240,7 +1162,6 @@ static irqreturn_t isp1760_irq(struct usb_hcd *hcd) slots[slot].qtd = NULL; qh = slots[slot].qh; slots[slot].qh = NULL; - priv->active_ptds--; qh->slot = -1; WARN_ON(qtd->status != QTD_XFER_STARTED); @@ -1281,6 +1202,15 @@ static irqreturn_t isp1760_irq(struct usb_hcd *hcd) case PTD_STATE_URB_RETIRE: qtd->status = QTD_RETIRE; + if ((qtd->urb->dev->speed != USB_SPEED_HIGH) && + (qtd->urb->status != -EPIPE) && + (qtd->urb->status != -EREMOTEIO)) { + qh->tt_buffer_dirty = 1; + if (usb_hub_clear_tt_buffer(qtd->urb)) + /* Clear failed; let's hope things work + anyway */ + qh->tt_buffer_dirty = 0; + } qtd = NULL; qh->toggle = 0; qh->ping = 0; @@ -1311,22 +1241,28 @@ static irqreturn_t isp1760_irq(struct usb_hcd *hcd) if (modified) schedule_ptds(hcd); +} - /* ISP1760 Errata 2 explains that interrupts may be missed (or not - happen?) if two USB devices are running simultaneously. Perhaps - this happens when a PTD is finished during interrupt handling; - enable SOF interrupts if PTDs are still scheduled when exiting this - interrupt handler, just to be safe. */ +static irqreturn_t isp1760_irq(struct usb_hcd *hcd) +{ + struct isp1760_hcd *priv = hcd_to_priv(hcd); + u32 imask; + irqreturn_t irqret = IRQ_NONE; - if (priv->active_ptds != last_active_ptds) { - if (priv->active_ptds > 0) - reg_write32(hcd->regs, HC_INTERRUPT_ENABLE, - INTERRUPT_ENABLE_SOT_MASK); - else - reg_write32(hcd->regs, HC_INTERRUPT_ENABLE, - INTERRUPT_ENABLE_MASK); - last_active_ptds = priv->active_ptds; - } + spin_lock(&priv->lock); + + if (!(hcd->state & HC_STATE_RUNNING)) + goto leave; + + imask = reg_read32(hcd->regs, HC_INTERRUPT_REG); + if (unlikely(!imask)) + goto leave; + reg_write32(hcd->regs, HC_INTERRUPT_REG, imask); /* Clear */ + + priv->int_done_map |= reg_read32(hcd->regs, HC_INT_PTD_DONEMAP_REG); + priv->atl_done_map |= reg_read32(hcd->regs, HC_ATL_PTD_DONEMAP_REG); + + handle_done_ptds(hcd); irqret = IRQ_HANDLED; leave: @@ -1335,6 +1271,138 @@ leave: return irqret; } +/* + * Workaround for problem described in chip errata 2: + * + * Sometimes interrupts are not generated when ATL (not INT?) completion occurs. + * One solution suggested in the errata is to use SOF interrupts _instead_of_ + * ATL done interrupts (the "instead of" might be important since it seems + * enabling ATL interrupts also causes the chip to sometimes - rarely - "forget" + * to set the PTD's done bit in addition to not generating an interrupt!). + * + * So if we use SOF + ATL interrupts, we sometimes get stale PTDs since their + * done bit is not being set. This is bad - it blocks the endpoint until reboot. + * + * If we use SOF interrupts only, we get latency between ptd completion and the + * actual handling. This is very noticeable in testusb runs which takes several + * minutes longer without ATL interrupts. + * + * A better solution is to run the code below every SLOT_CHECK_PERIOD ms. If it + * finds active ATL slots which are older than SLOT_TIMEOUT ms, it checks the + * slot's ACTIVE and VALID bits. If these are not set, the ptd is considered + * completed and its done map bit is set. + * + * The values of SLOT_TIMEOUT and SLOT_CHECK_PERIOD have been arbitrarily chosen + * not to cause too much lag when this HW bug occurs, while still hopefully + * ensuring that the check does not falsely trigger. + */ +#define SLOT_TIMEOUT 300 +#define SLOT_CHECK_PERIOD 200 +static struct timer_list errata2_timer; + +void errata2_function(unsigned long data) +{ + struct usb_hcd *hcd = (struct usb_hcd *) data; + struct isp1760_hcd *priv = hcd_to_priv(hcd); + int slot; + struct ptd ptd; + unsigned long spinflags; + + spin_lock_irqsave(&priv->lock, spinflags); + + for (slot = 0; slot < 32; slot++) + if (priv->atl_slots[slot].qh && time_after(jiffies, + priv->atl_slots[slot].timestamp + + SLOT_TIMEOUT * HZ / 1000)) { + ptd_read(hcd->regs, ATL_PTD_OFFSET, slot, &ptd); + if (!FROM_DW0_VALID(ptd.dw0) && + !FROM_DW3_ACTIVE(ptd.dw3)) + priv->atl_done_map |= 1 << slot; + } + + if (priv->atl_done_map) + handle_done_ptds(hcd); + + spin_unlock_irqrestore(&priv->lock, spinflags); + + errata2_timer.expires = jiffies + SLOT_CHECK_PERIOD * HZ / 1000; + add_timer(&errata2_timer); +} + +static int isp1760_run(struct usb_hcd *hcd) +{ + int retval; + u32 temp; + u32 command; + u32 chipid; + + hcd->uses_new_polling = 1; + + hcd->state = HC_STATE_RUNNING; + + /* Set PTD interrupt AND & OR maps */ + reg_write32(hcd->regs, HC_ATL_IRQ_MASK_AND_REG, 0); + reg_write32(hcd->regs, HC_ATL_IRQ_MASK_OR_REG, 0xffffffff); + reg_write32(hcd->regs, HC_INT_IRQ_MASK_AND_REG, 0); + reg_write32(hcd->regs, HC_INT_IRQ_MASK_OR_REG, 0xffffffff); + reg_write32(hcd->regs, HC_ISO_IRQ_MASK_AND_REG, 0); + reg_write32(hcd->regs, HC_ISO_IRQ_MASK_OR_REG, 0xffffffff); + /* step 23 passed */ + + temp = reg_read32(hcd->regs, HC_HW_MODE_CTRL); + reg_write32(hcd->regs, HC_HW_MODE_CTRL, temp | HW_GLOBAL_INTR_EN); + + command = reg_read32(hcd->regs, HC_USBCMD); + command &= ~(CMD_LRESET|CMD_RESET); + command |= CMD_RUN; + reg_write32(hcd->regs, HC_USBCMD, command); + + retval = handshake(hcd, HC_USBCMD, CMD_RUN, CMD_RUN, 250 * 1000); + if (retval) + return retval; + + /* + * XXX + * Spec says to write FLAG_CF as last config action, priv code grabs + * the semaphore while doing so. + */ + down_write(&ehci_cf_port_reset_rwsem); + reg_write32(hcd->regs, HC_CONFIGFLAG, FLAG_CF); + + retval = handshake(hcd, HC_CONFIGFLAG, FLAG_CF, FLAG_CF, 250 * 1000); + up_write(&ehci_cf_port_reset_rwsem); + if (retval) + return retval; + + init_timer(&errata2_timer); + errata2_timer.function = errata2_function; + errata2_timer.data = (unsigned long) hcd; + errata2_timer.expires = jiffies + SLOT_CHECK_PERIOD * HZ / 1000; + add_timer(&errata2_timer); + + chipid = reg_read32(hcd->regs, HC_CHIP_ID_REG); + dev_info(hcd->self.controller, "USB ISP %04x HW rev. %d started\n", + chipid & 0xffff, chipid >> 16); + + /* PTD Register Init Part 2, Step 28 */ + + /* Setup registers controlling PTD checking */ + reg_write32(hcd->regs, HC_ATL_PTD_LASTPTD_REG, 0x80000000); + reg_write32(hcd->regs, HC_INT_PTD_LASTPTD_REG, 0x80000000); + reg_write32(hcd->regs, HC_ISO_PTD_LASTPTD_REG, 0x00000001); + reg_write32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG, 0xffffffff); + reg_write32(hcd->regs, HC_INT_PTD_SKIPMAP_REG, 0xffffffff); + reg_write32(hcd->regs, HC_ISO_PTD_SKIPMAP_REG, 0xffffffff); + reg_write32(hcd->regs, HC_BUFFER_STATUS_REG, + ATL_BUF_FILL | INT_BUF_FILL); + + /* GRR this is run-once init(), being done every time the HC starts. + * So long as they're part of class devices, we can't do it init() + * since the class device isn't created that early. + */ + return 0; +} + static int qtd_fill(struct isp1760_qtd *qtd, void *databuffer, size_t len) { qtd->data_buffer = databuffer; @@ -1503,7 +1571,6 @@ static int isp1760_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, packetize_urb(hcd, urb, &new_qtds, mem_flags); if (list_empty(&new_qtds)) return -ENOMEM; - urb->hcpriv = NULL; /* Used to signal unlink to interrupt handler */ retval = 0; spin_lock_irqsave(&priv->lock, spinflags); @@ -1531,6 +1598,7 @@ static int isp1760_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, qh = qh_alloc(GFP_ATOMIC); if (!qh) { retval = -ENOMEM; + usb_hcd_unlink_urb_from_ep(hcd, urb); goto out; } list_add_tail(&qh->qh_list, ep_queue); @@ -1570,7 +1638,41 @@ static void kill_transfer(struct usb_hcd *hcd, struct urb *urb, } qh->slot = -1; - priv->active_ptds--; +} + +/* + * Retire the qtds beginning at 'qtd' and belonging all to the same urb, killing + * any active transfer belonging to the urb in the process. + */ +static void dequeue_urb_from_qtd(struct usb_hcd *hcd, struct isp1760_qh *qh, + struct isp1760_qtd *qtd) +{ + struct urb *urb; + int urb_was_running; + + urb = qtd->urb; + urb_was_running = 0; + list_for_each_entry_from(qtd, &qh->qtd_list, qtd_list) { + if (qtd->urb != urb) + break; + + if (qtd->status >= QTD_XFER_STARTED) + urb_was_running = 1; + if (last_qtd_of_urb(qtd, qh) && + (qtd->status >= QTD_XFER_COMPLETE)) + urb_was_running = 0; + + if (qtd->status == QTD_XFER_STARTED) + kill_transfer(hcd, urb, qh); + qtd->status = QTD_RETIRE; + } + + if ((urb->dev->speed != USB_SPEED_HIGH) && urb_was_running) { + qh->tt_buffer_dirty = 1; + if (usb_hub_clear_tt_buffer(urb)) + /* Clear failed; let's hope things work anyway */ + qh->tt_buffer_dirty = 0; + } } static int isp1760_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, @@ -1595,9 +1697,8 @@ static int isp1760_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, list_for_each_entry(qtd, &qh->qtd_list, qtd_list) if (qtd->urb == urb) { - if (qtd->status == QTD_XFER_STARTED) - kill_transfer(hcd, urb, qh); - qtd->status = QTD_RETIRE; + dequeue_urb_from_qtd(hcd, qh, qtd); + break; } urb->status = status; @@ -1622,12 +1723,11 @@ static void isp1760_endpoint_disable(struct usb_hcd *hcd, if (!qh) goto out; - list_for_each_entry(qtd, &qh->qtd_list, qtd_list) { - if (qtd->status == QTD_XFER_STARTED) - kill_transfer(hcd, qtd->urb, qh); - qtd->status = QTD_RETIRE; - qtd->urb->status = -ECONNRESET; - } + list_for_each_entry(qtd, &qh->qtd_list, qtd_list) + if (qtd->status != QTD_RETIRE) { + dequeue_urb_from_qtd(hcd, qh, qtd); + qtd->urb->status = -ECONNRESET; + } ep->hcpriv = NULL; /* Cannot free qh here since it will be parsed by schedule_ptds() */ @@ -2021,6 +2121,8 @@ static void isp1760_stop(struct usb_hcd *hcd) struct isp1760_hcd *priv = hcd_to_priv(hcd); u32 temp; + del_timer(&errata2_timer); + isp1760_hub_control(hcd, ClearPortFeature, USB_PORT_FEAT_POWER, 1, NULL, 0); mdelay(20); @@ -2048,6 +2150,23 @@ static void isp1760_shutdown(struct usb_hcd *hcd) reg_write32(hcd->regs, HC_USBCMD, command); } +static void isp1760_clear_tt_buffer_complete(struct usb_hcd *hcd, + struct usb_host_endpoint *ep) +{ + struct isp1760_hcd *priv = hcd_to_priv(hcd); + struct isp1760_qh *qh = ep->hcpriv; + unsigned long spinflags; + + if (!qh) + return; + + spin_lock_irqsave(&priv->lock, spinflags); + qh->tt_buffer_dirty = 0; + schedule_ptds(hcd); + spin_unlock_irqrestore(&priv->lock, spinflags); +} + + static const struct hc_driver isp1760_hc_driver = { .description = "isp1760-hcd", .product_desc = "NXP ISP1760 USB Host Controller", @@ -2064,6 +2183,7 @@ static const struct hc_driver isp1760_hc_driver = { .get_frame_number = isp1760_get_frame, .hub_status_data = isp1760_hub_status_data, .hub_control = isp1760_hub_control, + .clear_tt_buffer_complete = isp1760_clear_tt_buffer_complete, }; int __init init_kmem_once(void) @@ -2102,6 +2222,7 @@ void deinit_kmem_cache(void) struct usb_hcd *isp1760_register(phys_addr_t res_start, resource_size_t res_len, int irq, unsigned long irqflags, + int rst_gpio, struct device *dev, const char *busname, unsigned int devflags) { @@ -2121,6 +2242,7 @@ struct usb_hcd *isp1760_register(phys_addr_t res_start, resource_size_t res_len, priv = hcd_to_priv(hcd); priv->devflags = devflags; + priv->rst_gpio = rst_gpio; init_memory(priv); hcd->regs = ioremap(res_start, res_len); if (!hcd->regs) { diff --git a/drivers/usb/host/isp1760-hcd.h b/drivers/usb/host/isp1760-hcd.h index 014a7dfadf91..33dc79ccaa6b 100644 --- a/drivers/usb/host/isp1760-hcd.h +++ b/drivers/usb/host/isp1760-hcd.h @@ -4,6 +4,7 @@ /* exports for if */ struct usb_hcd *isp1760_register(phys_addr_t res_start, resource_size_t res_len, int irq, unsigned long irqflags, + int rst_gpio, struct device *dev, const char *busname, unsigned int devflags); int init_kmem_once(void); @@ -73,7 +74,6 @@ void deinit_kmem_cache(void); #define HC_EOT_INT (1 << 3) #define HC_SOT_INT (1 << 1) #define INTERRUPT_ENABLE_MASK (HC_INTL_INT | HC_ATL_INT) -#define INTERRUPT_ENABLE_SOT_MASK (HC_SOT_INT) #define HC_ISO_IRQ_MASK_OR_REG 0x318 #define HC_INT_IRQ_MASK_OR_REG 0x31C @@ -107,6 +107,7 @@ struct ptd { struct slotinfo { struct isp1760_qh *qh; struct isp1760_qtd *qtd; + unsigned long timestamp; }; @@ -126,6 +127,7 @@ typedef void (packet_enqueue)(struct usb_hcd *hcd, struct isp1760_qh *qh, #define ISP1760_FLAG_ISP1761 0x00000040 /* Chip is ISP1761 */ #define ISP1760_FLAG_INTR_POL_HIGH 0x00000080 /* Interrupt polarity active high */ #define ISP1760_FLAG_INTR_EDGE_TRIG 0x00000100 /* Interrupt edge triggered */ +#define ISP1760_FLAG_RESET_ACTIVE_HIGH 0x80000000 /* RESET GPIO active high */ /* chip memory management */ struct memory_chunk { @@ -188,6 +190,7 @@ struct memory_chunk { #define DW3_BABBLE_BIT (1 << 29) #define DW3_HALT_BIT (1 << 30) #define DW3_ACTIVE_BIT (1 << 31) +#define FROM_DW3_ACTIVE(x) (((x) >> 31) & 0x01) #define INT_UNDERRUN (1 << 2) #define INT_BABBLE (1 << 1) diff --git a/drivers/usb/host/isp1760-if.c b/drivers/usb/host/isp1760-if.c index 7ee30056f373..2c7fc830c9e4 100644 --- a/drivers/usb/host/isp1760-if.c +++ b/drivers/usb/host/isp1760-if.c @@ -17,19 +17,28 @@ #include "isp1760-hcd.h" -#ifdef CONFIG_PPC_OF +#ifdef CONFIG_OF +#include <linux/slab.h> #include <linux/of.h> #include <linux/of_platform.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#include <linux/of_gpio.h> #endif #ifdef CONFIG_PCI #include <linux/pci.h> #endif -#ifdef CONFIG_PPC_OF +#ifdef CONFIG_OF +struct isp1760 { + struct usb_hcd *hcd; + int rst_gpio; +}; + static int of_isp1760_probe(struct platform_device *dev) { - struct usb_hcd *hcd; + struct isp1760 *drvdata; struct device_node *dp = dev->dev.of_node; struct resource *res; struct resource memory; @@ -39,6 +48,11 @@ static int of_isp1760_probe(struct platform_device *dev) int ret; const unsigned int *prop; unsigned int devflags = 0; + enum of_gpio_flags gpio_flags; + + drvdata = kzalloc(sizeof(*drvdata), GFP_KERNEL); + if (!drvdata) + return -ENOMEM; ret = of_address_to_resource(dp, 0, &memory); if (ret) @@ -78,32 +92,57 @@ static int of_isp1760_probe(struct platform_device *dev) if (of_get_property(dp, "dreq-polarity", NULL) != NULL) devflags |= ISP1760_FLAG_DREQ_POL_HIGH; - hcd = isp1760_register(memory.start, res_len, virq, - IRQF_SHARED | IRQF_DISABLED, &dev->dev, dev_name(&dev->dev), - devflags); - if (IS_ERR(hcd)) { - ret = PTR_ERR(hcd); - goto release_reg; + drvdata->rst_gpio = of_get_gpio_flags(dp, 0, &gpio_flags); + if (gpio_is_valid(drvdata->rst_gpio)) { + ret = gpio_request(drvdata->rst_gpio, dev_name(&dev->dev)); + if (!ret) { + if (!(gpio_flags & OF_GPIO_ACTIVE_LOW)) { + devflags |= ISP1760_FLAG_RESET_ACTIVE_HIGH; + gpio_direction_output(drvdata->rst_gpio, 0); + } else { + gpio_direction_output(drvdata->rst_gpio, 1); + } + } else { + drvdata->rst_gpio = ret; + } } - dev_set_drvdata(&dev->dev, hcd); + drvdata->hcd = isp1760_register(memory.start, res_len, virq, + IRQF_SHARED, drvdata->rst_gpio, + &dev->dev, dev_name(&dev->dev), + devflags); + if (IS_ERR(drvdata->hcd)) { + ret = PTR_ERR(drvdata->hcd); + goto free_gpio; + } + + dev_set_drvdata(&dev->dev, drvdata); return ret; +free_gpio: + if (gpio_is_valid(drvdata->rst_gpio)) + gpio_free(drvdata->rst_gpio); release_reg: release_mem_region(memory.start, res_len); + kfree(drvdata); return ret; } static int of_isp1760_remove(struct platform_device *dev) { - struct usb_hcd *hcd = dev_get_drvdata(&dev->dev); + struct isp1760 *drvdata = dev_get_drvdata(&dev->dev); dev_set_drvdata(&dev->dev, NULL); - usb_remove_hcd(hcd); - iounmap(hcd->regs); - release_mem_region(hcd->rsrc_start, hcd->rsrc_len); - usb_put_hcd(hcd); + usb_remove_hcd(drvdata->hcd); + iounmap(drvdata->hcd->regs); + release_mem_region(drvdata->hcd->rsrc_start, drvdata->hcd->rsrc_len); + usb_put_hcd(drvdata->hcd); + + if (gpio_is_valid(drvdata->rst_gpio)) + gpio_free(drvdata->rst_gpio); + + kfree(drvdata); return 0; } @@ -240,7 +279,7 @@ static int __devinit isp1761_pci_probe(struct pci_dev *dev, dev->dev.dma_mask = NULL; hcd = isp1760_register(pci_mem_phy0, memlength, dev->irq, - IRQF_SHARED | IRQF_DISABLED, &dev->dev, dev_name(&dev->dev), + IRQF_SHARED, -ENOENT, &dev->dev, dev_name(&dev->dev), devflags); if (IS_ERR(hcd)) { ret_status = -ENODEV; @@ -313,7 +352,7 @@ static int __devinit isp1760_plat_probe(struct platform_device *pdev) resource_size_t mem_size; struct isp1760_platform_data *priv = pdev->dev.platform_data; unsigned int devflags = 0; - unsigned long irqflags = IRQF_SHARED | IRQF_DISABLED; + unsigned long irqflags = IRQF_SHARED; mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!mem_res) { @@ -351,7 +390,8 @@ static int __devinit isp1760_plat_probe(struct platform_device *pdev) } hcd = isp1760_register(mem_res->start, mem_size, irq_res->start, - irqflags, &pdev->dev, dev_name(&pdev->dev), devflags); + irqflags, -ENOENT, + &pdev->dev, dev_name(&pdev->dev), devflags); if (IS_ERR(hcd)) { pr_warning("isp1760: Failed to register the HCD device\n"); ret = -ENODEV; @@ -396,7 +436,7 @@ static int __init isp1760_init(void) ret = platform_driver_register(&isp1760_plat_driver); if (!ret) any_ret = 0; -#ifdef CONFIG_PPC_OF +#ifdef CONFIG_OF ret = platform_driver_register(&isp1760_of_driver); if (!ret) any_ret = 0; @@ -416,7 +456,7 @@ module_init(isp1760_init); static void __exit isp1760_exit(void) { platform_driver_unregister(&isp1760_plat_driver); -#ifdef CONFIG_PPC_OF +#ifdef CONFIG_OF platform_driver_unregister(&isp1760_of_driver); #endif #ifdef CONFIG_PCI diff --git a/drivers/usb/host/ohci-ath79.c b/drivers/usb/host/ohci-ath79.c index c620c50f6770..18d574d6958b 100644 --- a/drivers/usb/host/ohci-ath79.c +++ b/drivers/usb/host/ohci-ath79.c @@ -111,7 +111,7 @@ static int ohci_ath79_probe(struct platform_device *pdev) ohci_hcd_init(hcd_to_ohci(hcd)); - ret = usb_add_hcd(hcd, irq, IRQF_DISABLED); + ret = usb_add_hcd(hcd, irq, 0); if (ret) goto err_stop_hcd; diff --git a/drivers/usb/host/ohci-au1xxx.c b/drivers/usb/host/ohci-au1xxx.c index 958d985f2951..6b7bc50dfeaa 100644 --- a/drivers/usb/host/ohci-au1xxx.c +++ b/drivers/usb/host/ohci-au1xxx.c @@ -218,7 +218,7 @@ static int ohci_hcd_au1xxx_drv_probe(struct platform_device *pdev) ohci_hcd_init(hcd_to_ohci(hcd)); ret = usb_add_hcd(hcd, pdev->resource[1].start, - IRQF_DISABLED | IRQF_SHARED); + IRQF_SHARED); if (ret == 0) { platform_set_drvdata(pdev, hcd); return ret; diff --git a/drivers/usb/host/ohci-da8xx.c b/drivers/usb/host/ohci-da8xx.c index 6aca2c4453f7..843509778a33 100644 --- a/drivers/usb/host/ohci-da8xx.c +++ b/drivers/usb/host/ohci-da8xx.c @@ -344,7 +344,7 @@ static int usb_hcd_da8xx_probe(const struct hc_driver *driver, error = -ENODEV; goto err4; } - error = usb_add_hcd(hcd, irq, IRQF_DISABLED); + error = usb_add_hcd(hcd, irq, 0); if (error) goto err4; diff --git a/drivers/usb/host/ohci-ep93xx.c b/drivers/usb/host/ohci-ep93xx.c index 4e681613e7ae..dc45d489d00e 100644 --- a/drivers/usb/host/ohci-ep93xx.c +++ b/drivers/usb/host/ohci-ep93xx.c @@ -81,7 +81,7 @@ static int usb_hcd_ep93xx_probe(const struct hc_driver *driver, ohci_hcd_init(hcd_to_ohci(hcd)); - retval = usb_add_hcd(hcd, pdev->resource[1].start, IRQF_DISABLED); + retval = usb_add_hcd(hcd, pdev->resource[1].start, 0); if (retval == 0) return retval; diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index f9cf3f04b742..34efd479e068 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -1114,6 +1114,11 @@ MODULE_LICENSE ("GPL"); #define PLATFORM_DRIVER ohci_hcd_ath79_driver #endif +#ifdef CONFIG_NLM_XLR +#include "ohci-xls.c" +#define PLATFORM_DRIVER ohci_xls_driver +#endif + #if !defined(PCI_DRIVER) && \ !defined(PLATFORM_DRIVER) && \ !defined(OMAP1_PLATFORM_DRIVER) && \ diff --git a/drivers/usb/host/ohci-hub.c b/drivers/usb/host/ohci-hub.c index 9154615292db..2f00040fc408 100644 --- a/drivers/usb/host/ohci-hub.c +++ b/drivers/usb/host/ohci-hub.c @@ -356,10 +356,7 @@ static void ohci_finish_controller_resume(struct usb_hcd *hcd) msleep(20); } - /* Does the root hub have a port wakeup pending? */ - if (ohci_readl(ohci, &ohci->regs->intrstatus) & - (OHCI_INTR_RD | OHCI_INTR_RHSC)) - usb_hcd_resume_root_hub(hcd); + usb_hcd_resume_root_hub(hcd); } /* Carry out polling-, autostop-, and autoresume-related state changes */ diff --git a/drivers/usb/host/ohci-octeon.c b/drivers/usb/host/ohci-octeon.c index d8b45647d1dc..d469bf9b9e54 100644 --- a/drivers/usb/host/ohci-octeon.c +++ b/drivers/usb/host/ohci-octeon.c @@ -164,7 +164,7 @@ static int ohci_octeon_drv_probe(struct platform_device *pdev) ohci_hcd_init(ohci); - ret = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED); + ret = usb_add_hcd(hcd, irq, IRQF_SHARED); if (ret) { dev_dbg(&pdev->dev, "failed to add hcd with err %d\n", ret); goto err3; diff --git a/drivers/usb/host/ohci-omap.c b/drivers/usb/host/ohci-omap.c index 5645f70b9214..e4b8782cc6e2 100644 --- a/drivers/usb/host/ohci-omap.c +++ b/drivers/usb/host/ohci-omap.c @@ -14,7 +14,7 @@ * This file is licenced under the GPL. */ -#include <linux/signal.h> /* IRQF_DISABLED */ +#include <linux/signal.h> #include <linux/jiffies.h> #include <linux/platform_device.h> #include <linux/clk.h> @@ -363,7 +363,7 @@ static int usb_hcd_omap_probe (const struct hc_driver *driver, retval = -ENXIO; goto err3; } - retval = usb_add_hcd(hcd, irq, IRQF_DISABLED); + retval = usb_add_hcd(hcd, irq, 0); if (retval) goto err3; diff --git a/drivers/usb/host/ohci-omap3.c b/drivers/usb/host/ohci-omap3.c index 6048f2f64f73..516ebc4d6cc2 100644 --- a/drivers/usb/host/ohci-omap3.c +++ b/drivers/usb/host/ohci-omap3.c @@ -149,7 +149,7 @@ static int __devinit ohci_hcd_omap3_probe(struct platform_device *pdev) res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ohci"); - if (!ret) { + if (!res) { dev_err(dev, "UHH OHCI get resource failed\n"); return -ENOMEM; } @@ -180,7 +180,7 @@ static int __devinit ohci_hcd_omap3_probe(struct platform_device *pdev) ohci_hcd_init(hcd_to_ohci(hcd)); - ret = usb_add_hcd(hcd, irq, IRQF_DISABLED); + ret = usb_add_hcd(hcd, irq, 0); if (ret) { dev_dbg(dev, "failed to add hcd with err %d\n", ret); goto err_add_hcd; diff --git a/drivers/usb/host/ohci-pnx4008.c b/drivers/usb/host/ohci-pnx4008.c index 653d6a60edb5..9ad8bee22c15 100644 --- a/drivers/usb/host/ohci-pnx4008.c +++ b/drivers/usb/host/ohci-pnx4008.c @@ -398,7 +398,7 @@ static int __devinit usb_hcd_pnx4008_probe(struct platform_device *pdev) ohci_hcd_init(ohci); dev_info(&pdev->dev, "at 0x%p, irq %d\n", hcd->regs, hcd->irq); - ret = usb_add_hcd(hcd, irq, IRQF_DISABLED); + ret = usb_add_hcd(hcd, irq, 0); if (ret == 0) return ret; diff --git a/drivers/usb/host/ohci-pnx8550.c b/drivers/usb/host/ohci-pnx8550.c index 28467e288a93..f13d08f94d6b 100644 --- a/drivers/usb/host/ohci-pnx8550.c +++ b/drivers/usb/host/ohci-pnx8550.c @@ -107,7 +107,7 @@ int usb_hcd_pnx8550_probe (const struct hc_driver *driver, ohci_hcd_init(hcd_to_ohci(hcd)); - retval = usb_add_hcd(hcd, dev->resource[1].start, IRQF_DISABLED); + retval = usb_add_hcd(hcd, dev->resource[1].start, 0); if (retval == 0) return retval; diff --git a/drivers/usb/host/ohci-ppc-of.c b/drivers/usb/host/ohci-ppc-of.c index 0c12f4e14dcd..d24cc89de16f 100644 --- a/drivers/usb/host/ohci-ppc-of.c +++ b/drivers/usb/host/ohci-ppc-of.c @@ -143,7 +143,7 @@ static int __devinit ohci_hcd_ppc_of_probe(struct platform_device *op) ohci_hcd_init(ohci); - rv = usb_add_hcd(hcd, irq, IRQF_DISABLED); + rv = usb_add_hcd(hcd, irq, 0); if (rv == 0) return 0; diff --git a/drivers/usb/host/ohci-ppc-soc.c b/drivers/usb/host/ohci-ppc-soc.c index c0f595c44487..1514b7067470 100644 --- a/drivers/usb/host/ohci-ppc-soc.c +++ b/drivers/usb/host/ohci-ppc-soc.c @@ -80,7 +80,7 @@ static int usb_hcd_ppc_soc_probe(const struct hc_driver *driver, #endif ohci_hcd_init(ohci); - retval = usb_add_hcd(hcd, irq, IRQF_DISABLED); + retval = usb_add_hcd(hcd, irq, 0); if (retval == 0) return retval; diff --git a/drivers/usb/host/ohci-ps3.c b/drivers/usb/host/ohci-ps3.c index 700950455f4d..6fd4fa1f19bb 100644 --- a/drivers/usb/host/ohci-ps3.c +++ b/drivers/usb/host/ohci-ps3.c @@ -164,7 +164,7 @@ static int __devinit ps3_ohci_probe(struct ps3_system_bus_device *dev) ps3_system_bus_set_drvdata(dev, hcd); - result = usb_add_hcd(hcd, virq, IRQF_DISABLED); + result = usb_add_hcd(hcd, virq, 0); if (result) { dev_dbg(&dev->core, "%s:%d: usb_add_hcd failed (%d)\n", diff --git a/drivers/usb/host/ohci-pxa27x.c b/drivers/usb/host/ohci-pxa27x.c index 80be5472783a..29dfefe1c726 100644 --- a/drivers/usb/host/ohci-pxa27x.c +++ b/drivers/usb/host/ohci-pxa27x.c @@ -359,7 +359,7 @@ int usb_hcd_pxa27x_probe (const struct hc_driver *driver, struct platform_device ohci_hcd_init(hcd_to_ohci(hcd)); - retval = usb_add_hcd(hcd, irq, IRQF_DISABLED); + retval = usb_add_hcd(hcd, irq, 0); if (retval == 0) return retval; diff --git a/drivers/usb/host/ohci-q.c b/drivers/usb/host/ohci-q.c index dd24fc115e48..15dc51ded61a 100644 --- a/drivers/usb/host/ohci-q.c +++ b/drivers/usb/host/ohci-q.c @@ -428,7 +428,7 @@ static struct ed *ed_get ( ed->type = usb_pipetype(pipe); info |= (ep->desc.bEndpointAddress & ~USB_DIR_IN) << 7; - info |= le16_to_cpu(ep->desc.wMaxPacketSize) << 16; + info |= usb_endpoint_maxp(&ep->desc) << 16; if (udev->speed == USB_SPEED_LOW) info |= ED_LOWSPEED; /* only control transfers store pids in tds */ @@ -444,7 +444,7 @@ static struct ed *ed_get ( ed->load = usb_calc_bus_time ( udev->speed, !is_out, ed->type == PIPE_ISOCHRONOUS, - le16_to_cpu(ep->desc.wMaxPacketSize)) + usb_endpoint_maxp(&ep->desc)) / 1000; } } diff --git a/drivers/usb/host/ohci-s3c2410.c b/drivers/usb/host/ohci-s3c2410.c index 7c9a4d55526b..a1877c47601e 100644 --- a/drivers/usb/host/ohci-s3c2410.c +++ b/drivers/usb/host/ohci-s3c2410.c @@ -384,7 +384,7 @@ static int usb_hcd_s3c2410_probe(const struct hc_driver *driver, ohci_hcd_init(hcd_to_ohci(hcd)); - retval = usb_add_hcd(hcd, dev->resource[1].start, IRQF_DISABLED); + retval = usb_add_hcd(hcd, dev->resource[1].start, 0); if (retval != 0) goto err_ioremap; diff --git a/drivers/usb/host/ohci-sa1111.c b/drivers/usb/host/ohci-sa1111.c index 4204d9720d23..4bde4f9821ba 100644 --- a/drivers/usb/host/ohci-sa1111.c +++ b/drivers/usb/host/ohci-sa1111.c @@ -143,7 +143,7 @@ int usb_hcd_sa1111_probe (const struct hc_driver *driver, sa1111_start_hc(dev); ohci_hcd_init(hcd_to_ohci(hcd)); - retval = usb_add_hcd(hcd, dev->irq[1], IRQF_DISABLED); + retval = usb_add_hcd(hcd, dev->irq[1], 0); if (retval == 0) return retval; diff --git a/drivers/usb/host/ohci-sh.c b/drivers/usb/host/ohci-sh.c index 14cecb52a9fe..afc4eb6bb9d0 100644 --- a/drivers/usb/host/ohci-sh.c +++ b/drivers/usb/host/ohci-sh.c @@ -109,7 +109,7 @@ static int ohci_hcd_sh_probe(struct platform_device *pdev) hcd->regs = (void __iomem *)res->start; hcd->rsrc_start = res->start; hcd->rsrc_len = resource_size(res); - ret = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED); + ret = usb_add_hcd(hcd, irq, IRQF_SHARED); if (ret != 0) { err("Failed to add hcd"); usb_put_hcd(hcd); diff --git a/drivers/usb/host/ohci-sm501.c b/drivers/usb/host/ohci-sm501.c index 78918ca0da23..968cea2b6d4e 100644 --- a/drivers/usb/host/ohci-sm501.c +++ b/drivers/usb/host/ohci-sm501.c @@ -165,7 +165,7 @@ static int ohci_hcd_sm501_drv_probe(struct platform_device *pdev) ohci_hcd_init(hcd_to_ohci(hcd)); - retval = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED); + retval = usb_add_hcd(hcd, irq, IRQF_SHARED); if (retval) goto err5; diff --git a/drivers/usb/host/ohci-spear.c b/drivers/usb/host/ohci-spear.c index 4fd4bea9ac7a..69874654f3b5 100644 --- a/drivers/usb/host/ohci-spear.c +++ b/drivers/usb/host/ohci-spear.c @@ -152,7 +152,7 @@ static int spear_ohci_hcd_drv_probe(struct platform_device *pdev) spear_start_ohci(ohci_p); ohci_hcd_init(hcd_to_ohci(hcd)); - retval = usb_add_hcd(hcd, platform_get_irq(pdev, 0), IRQF_DISABLED); + retval = usb_add_hcd(hcd, platform_get_irq(pdev, 0), 0); if (retval == 0) return retval; diff --git a/drivers/usb/host/ohci-ssb.c b/drivers/usb/host/ohci-ssb.c index c4aea3b8315e..5ba18595d6f7 100644 --- a/drivers/usb/host/ohci-ssb.c +++ b/drivers/usb/host/ohci-ssb.c @@ -169,7 +169,7 @@ static int ssb_ohci_attach(struct ssb_device *dev) hcd->regs = ioremap_nocache(hcd->rsrc_start, hcd->rsrc_len); if (!hcd->regs) goto err_put_hcd; - err = usb_add_hcd(hcd, dev->irq, IRQF_DISABLED | IRQF_SHARED); + err = usb_add_hcd(hcd, dev->irq, IRQF_SHARED); if (err) goto err_iounmap; diff --git a/drivers/usb/host/ohci-tmio.c b/drivers/usb/host/ohci-tmio.c index 57ad1271fc9b..06331d931171 100644 --- a/drivers/usb/host/ohci-tmio.c +++ b/drivers/usb/host/ohci-tmio.c @@ -244,7 +244,7 @@ static int __devinit ohci_hcd_tmio_drv_probe(struct platform_device *dev) ohci = hcd_to_ohci(hcd); ohci_hcd_init(ohci); - ret = usb_add_hcd(hcd, irq, IRQF_DISABLED); + ret = usb_add_hcd(hcd, irq, 0); if (ret) goto err_add_hcd; diff --git a/drivers/usb/host/ohci-xls.c b/drivers/usb/host/ohci-xls.c new file mode 100644 index 000000000000..a3a9c6f45b91 --- /dev/null +++ b/drivers/usb/host/ohci-xls.c @@ -0,0 +1,151 @@ +/* + * OHCI HCD for Netlogic XLS processors. + * + * (C) Copyright 2011 Netlogic Microsystems Inc. + * + * Based on ohci-au1xxx.c, and other Linux OHCI drivers. + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive for + * more details. + */ + +#include <linux/platform_device.h> +#include <linux/signal.h> + +static int ohci_xls_probe_internal(const struct hc_driver *driver, + struct platform_device *dev) +{ + struct resource *res; + struct usb_hcd *hcd; + int retval, irq; + + /* Get our IRQ from an earlier registered Platform Resource */ + irq = platform_get_irq(dev, 0); + if (irq < 0) { + dev_err(&dev->dev, "Found HC with no IRQ\n"); + return -ENODEV; + } + + /* Get our Memory Handle */ + res = platform_get_resource(dev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&dev->dev, "MMIO Handle incorrect!\n"); + return -ENODEV; + } + + hcd = usb_create_hcd(driver, &dev->dev, "XLS"); + if (!hcd) { + retval = -ENOMEM; + goto err1; + } + hcd->rsrc_start = res->start; + hcd->rsrc_len = res->end - res->start + 1; + + if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, + driver->description)) { + dev_dbg(&dev->dev, "Controller already in use\n"); + retval = -EBUSY; + goto err2; + } + + hcd->regs = ioremap_nocache(hcd->rsrc_start, hcd->rsrc_len); + if (hcd->regs == NULL) { + dev_dbg(&dev->dev, "error mapping memory\n"); + retval = -EFAULT; + goto err3; + } + + retval = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED); + if (retval != 0) + goto err4; + return retval; + +err4: + iounmap(hcd->regs); +err3: + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); +err2: + usb_put_hcd(hcd); +err1: + dev_err(&dev->dev, "init fail, %d\n", retval); + return retval; +} + +static int ohci_xls_reset(struct usb_hcd *hcd) +{ + struct ohci_hcd *ohci = hcd_to_ohci(hcd); + + ohci_hcd_init(ohci); + return ohci_init(ohci); +} + +static int __devinit ohci_xls_start(struct usb_hcd *hcd) +{ + struct ohci_hcd *ohci; + int ret; + + ohci = hcd_to_ohci(hcd); + ret = ohci_run(ohci); + if (ret < 0) { + err("can't start %s", hcd->self.bus_name); + ohci_stop(hcd); + return ret; + } + return 0; +} + +static struct hc_driver ohci_xls_hc_driver = { + .description = hcd_name, + .product_desc = "XLS OHCI Host Controller", + .hcd_priv_size = sizeof(struct ohci_hcd), + .irq = ohci_irq, + .flags = HCD_MEMORY | HCD_USB11, + .reset = ohci_xls_reset, + .start = ohci_xls_start, + .stop = ohci_stop, + .shutdown = ohci_shutdown, + .urb_enqueue = ohci_urb_enqueue, + .urb_dequeue = ohci_urb_dequeue, + .endpoint_disable = ohci_endpoint_disable, + .get_frame_number = ohci_get_frame, + .hub_status_data = ohci_hub_status_data, + .hub_control = ohci_hub_control, +#ifdef CONFIG_PM + .bus_suspend = ohci_bus_suspend, + .bus_resume = ohci_bus_resume, +#endif + .start_port_reset = ohci_start_port_reset, +}; + +static int ohci_xls_probe(struct platform_device *dev) +{ + int ret; + + pr_debug("In ohci_xls_probe"); + if (usb_disabled()) + return -ENODEV; + ret = ohci_xls_probe_internal(&ohci_xls_hc_driver, dev); + return ret; +} + +static int ohci_xls_remove(struct platform_device *dev) +{ + struct usb_hcd *hcd = platform_get_drvdata(dev); + + usb_remove_hcd(hcd); + iounmap(hcd->regs); + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); + usb_put_hcd(hcd); + return 0; +} + +static struct platform_driver ohci_xls_driver = { + .probe = ohci_xls_probe, + .remove = ohci_xls_remove, + .shutdown = usb_hcd_platform_shutdown, + .driver = { + .name = "ohci-xls-0", + .owner = THIS_MODULE, + }, +}; diff --git a/drivers/usb/host/r8a66597-hcd.c b/drivers/usb/host/r8a66597-hcd.c index 40a0d8b03ad7..e84ca1928dbf 100644 --- a/drivers/usb/host/r8a66597-hcd.c +++ b/drivers/usb/host/r8a66597-hcd.c @@ -959,7 +959,7 @@ static void init_pipe_info(struct r8a66597 *r8a66597, struct urb *urb, info.pipenum = get_empty_pipenum(r8a66597, ep); info.address = get_urb_to_r8a66597_addr(r8a66597, urb); info.epnum = usb_endpoint_num(ep); - info.maxpacket = le16_to_cpu(ep->wMaxPacketSize); + info.maxpacket = usb_endpoint_maxp(ep); info.type = get_r8a66597_type(usb_endpoint_type(ep)); info.bufnum = get_bufnum(info.pipenum); info.buf_bsize = get_buf_bsize(info.pipenum); @@ -2519,7 +2519,7 @@ static int __devinit r8a66597_probe(struct platform_device *pdev) hcd->rsrc_start = res->start; hcd->has_tt = 1; - ret = usb_add_hcd(hcd, irq, IRQF_DISABLED | irq_trigger); + ret = usb_add_hcd(hcd, irq, irq_trigger); if (ret != 0) { dev_err(&pdev->dev, "Failed to add hcd\n"); goto clean_up3; diff --git a/drivers/usb/host/sl811-hcd.c b/drivers/usb/host/sl811-hcd.c index 1a996245ab98..961d6638d8f9 100644 --- a/drivers/usb/host/sl811-hcd.c +++ b/drivers/usb/host/sl811-hcd.c @@ -1729,7 +1729,7 @@ sl811h_probe(struct platform_device *dev) * Use resource IRQ flags if set by platform device setup. */ irqflags |= IRQF_SHARED; - retval = usb_add_hcd(hcd, irq, IRQF_DISABLED | irqflags); + retval = usb_add_hcd(hcd, irq, irqflags); if (retval != 0) goto err6; diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c index fba99b120588..c8ae199cfbb8 100644 --- a/drivers/usb/host/uhci-hcd.c +++ b/drivers/usb/host/uhci-hcd.c @@ -294,50 +294,50 @@ __acquires(uhci->lock) * and that remote wakeups should be enabled. */ egsm_enable = USBCMD_EGSM; - uhci->RD_enable = 1; int_enable = USBINTR_RESUME; wakeup_enable = 1; - /* In auto-stop mode wakeups must always be detected, but - * Resume-Detect interrupts may be prohibited. (In the absence - * of CONFIG_PM, they are always disallowed.) + /* + * In auto-stop mode, we must be able to detect new connections. + * The user can force us to poll by disabling remote wakeup; + * otherwise we will use the EGSM/RD mechanism. */ if (auto_stop) { if (!device_may_wakeup(&rhdev->dev)) - int_enable = 0; + egsm_enable = int_enable = 0; + } - /* In bus-suspend mode wakeups may be disabled, but if they are - * allowed then so are Resume-Detect interrupts. - */ - } else { #ifdef CONFIG_PM + /* + * In bus-suspend mode, we use the wakeup setting specified + * for the root hub. + */ + else { if (!rhdev->do_remote_wakeup) wakeup_enable = 0; -#endif } +#endif - /* EGSM causes the root hub to echo a 'K' signal (resume) out any - * port which requests a remote wakeup. According to the USB spec, - * every hub is supposed to do this. But if we are ignoring - * remote-wakeup requests anyway then there's no point to it. - * We also shouldn't enable EGSM if it's broken. - */ - if (!wakeup_enable || global_suspend_mode_is_broken(uhci)) - egsm_enable = 0; - - /* If we're ignoring wakeup events then there's no reason to - * enable Resume-Detect interrupts. We also shouldn't enable - * them if they are broken or disallowed. + /* + * UHCI doesn't distinguish between wakeup requests from downstream + * devices and local connect/disconnect events. There's no way to + * enable one without the other; both are controlled by EGSM. Thus + * if wakeups are disallowed then EGSM must be turned off -- in which + * case remote wakeup requests from downstream during system sleep + * will be lost. + * + * In addition, if EGSM is broken then we can't use it. Likewise, + * if Resume-Detect interrupts are broken then we can't use them. * - * This logic may lead us to enabling RD but not EGSM. The UHCI - * spec foolishly says that RD works only when EGSM is on, but - * there's no harm in enabling it anyway -- perhaps some chips - * will implement it! + * Finally, neither EGSM nor RD is useful by itself. Without EGSM, + * the RD status bit will never get set. Without RD, the controller + * won't generate interrupts to tell the system about wakeup events. */ - if (!wakeup_enable || resume_detect_interrupts_are_broken(uhci) || - !int_enable) - uhci->RD_enable = int_enable = 0; + if (!wakeup_enable || global_suspend_mode_is_broken(uhci) || + resume_detect_interrupts_are_broken(uhci)) + egsm_enable = int_enable = 0; + uhci->RD_enable = !!int_enable; uhci_writew(uhci, int_enable, USBINTR); uhci_writew(uhci, egsm_enable | USBCMD_CF, USBCMD); mb(); @@ -364,10 +364,12 @@ __acquires(uhci->lock) uhci->rh_state = new_state; uhci->is_stopped = UHCI_IS_STOPPED; - /* If interrupts don't work and remote wakeup is enabled then - * the suspended root hub needs to be polled. + /* + * If remote wakeup is enabled but either EGSM or RD interrupts + * doesn't work, then we won't get an interrupt when a wakeup event + * occurs. Thus the suspended root hub needs to be polled. */ - if (!int_enable && wakeup_enable) + if (wakeup_enable && (!int_enable || !egsm_enable)) set_bit(HCD_FLAG_POLL_RH, &uhci_to_hcd(uhci)->flags); else clear_bit(HCD_FLAG_POLL_RH, &uhci_to_hcd(uhci)->flags); diff --git a/drivers/usb/host/uhci-q.c b/drivers/usb/host/uhci-q.c index 84ed28b34f93..f6ca80ee4cec 100644 --- a/drivers/usb/host/uhci-q.c +++ b/drivers/usb/host/uhci-q.c @@ -280,7 +280,7 @@ static struct uhci_qh *uhci_alloc_qh(struct uhci_hcd *uhci, qh->load = usb_calc_bus_time(udev->speed, usb_endpoint_dir_in(&hep->desc), qh->type == USB_ENDPOINT_XFER_ISOC, - le16_to_cpu(hep->desc.wMaxPacketSize)) + usb_endpoint_maxp(&hep->desc)) / 1000 + 1; } else { /* Skeleton QH */ @@ -792,7 +792,7 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb, { struct uhci_td *td; unsigned long destination, status; - int maxsze = le16_to_cpu(qh->hep->desc.wMaxPacketSize); + int maxsze = usb_endpoint_maxp(&qh->hep->desc); int len = urb->transfer_buffer_length; dma_addr_t data = urb->transfer_dma; __hc32 *plink; @@ -918,7 +918,7 @@ static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb, { struct uhci_td *td; unsigned long destination, status; - int maxsze = le16_to_cpu(qh->hep->desc.wMaxPacketSize); + int maxsze = usb_endpoint_maxp(&qh->hep->desc); int len = urb->transfer_buffer_length; int this_sg_len; dma_addr_t data; diff --git a/drivers/usb/host/xhci-ext-caps.h b/drivers/usb/host/xhci-ext-caps.h index ce5c9e51748e..c7f33123d4c0 100644 --- a/drivers/usb/host/xhci-ext-caps.h +++ b/drivers/usb/host/xhci-ext-caps.h @@ -65,6 +65,12 @@ /* bits 1:2, 5:12, and 17:19 need to be preserved; bits 21:28 should be zero */ #define XHCI_LEGACY_DISABLE_SMI ((0x3 << 1) + (0xff << 5) + (0x7 << 17)) +/* USB 2.0 xHCI 0.96 L1C capability - section 7.2.2.1.3.2 */ +#define XHCI_L1C (1 << 16) + +/* USB 2.0 xHCI 1.0 hardware LMP capability - section 7.2.2.1.3.2 */ +#define XHCI_HLC (1 << 19) + /* command register values to disable interrupts and halt the HC */ /* start/stop HC execution - do not write unless HC is halted*/ #define XHCI_CMD_RUN (1 << 0) diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 723f8231193d..431efe72b1f7 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -28,6 +28,25 @@ #define PORT_RWC_BITS (PORT_CSC | PORT_PEC | PORT_WRC | PORT_OCC | \ PORT_RC | PORT_PLC | PORT_PE) +/* usb 1.1 root hub device descriptor */ +static u8 usb_bos_descriptor [] = { + USB_DT_BOS_SIZE, /* __u8 bLength, 5 bytes */ + USB_DT_BOS, /* __u8 bDescriptorType */ + 0x0F, 0x00, /* __le16 wTotalLength, 15 bytes */ + 0x1, /* __u8 bNumDeviceCaps */ + /* First device capability */ + USB_DT_USB_SS_CAP_SIZE, /* __u8 bLength, 10 bytes */ + USB_DT_DEVICE_CAPABILITY, /* Device Capability */ + USB_SS_CAP_TYPE, /* bDevCapabilityType, SUPERSPEED_USB */ + 0x00, /* bmAttributes, LTM off by default */ + USB_5GBPS_OPERATION, 0x00, /* wSpeedsSupported, 5Gbps only */ + 0x03, /* bFunctionalitySupport, + USB 3.0 speed only */ + 0x00, /* bU1DevExitLat, set later. */ + 0x00, 0x00 /* __le16 bU2DevExitLat, set later. */ +}; + + static void xhci_common_hub_descriptor(struct xhci_hcd *xhci, struct usb_hub_descriptor *desc, int ports) { @@ -232,7 +251,7 @@ int xhci_find_slot_id_by_port(struct usb_hcd *hcd, struct xhci_hcd *xhci, continue; speed = xhci->devs[i]->udev->speed; if (((speed == USB_SPEED_SUPER) == (hcd->speed == HCD_USB3)) - && xhci->devs[i]->port == port) { + && xhci->devs[i]->fake_port == port) { slot_id = i; break; } @@ -392,13 +411,39 @@ static int xhci_get_ports(struct usb_hcd *hcd, __le32 __iomem ***port_array) return max_ports; } +void xhci_set_link_state(struct xhci_hcd *xhci, __le32 __iomem **port_array, + int port_id, u32 link_state) +{ + u32 temp; + + temp = xhci_readl(xhci, port_array[port_id]); + temp = xhci_port_state_to_neutral(temp); + temp &= ~PORT_PLS_MASK; + temp |= PORT_LINK_STROBE | link_state; + xhci_writel(xhci, temp, port_array[port_id]); +} + +/* Test and clear port RWC bit */ +void xhci_test_and_clear_bit(struct xhci_hcd *xhci, __le32 __iomem **port_array, + int port_id, u32 port_bit) +{ + u32 temp; + + temp = xhci_readl(xhci, port_array[port_id]); + if (temp & port_bit) { + temp = xhci_port_state_to_neutral(temp); + temp |= port_bit; + xhci_writel(xhci, temp, port_array[port_id]); + } +} + int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex, char *buf, u16 wLength) { struct xhci_hcd *xhci = hcd_to_xhci(hcd); int max_ports; unsigned long flags; - u32 temp, temp1, status; + u32 temp, status; int retval = 0; __le32 __iomem **port_array; int slot_id; @@ -429,6 +474,21 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, xhci_hub_descriptor(hcd, xhci, (struct usb_hub_descriptor *) buf); break; + case DeviceRequest | USB_REQ_GET_DESCRIPTOR: + if ((wValue & 0xff00) != (USB_DT_BOS << 8)) + goto error; + + if (hcd->speed != HCD_USB3) + goto error; + + memcpy(buf, &usb_bos_descriptor, + USB_DT_BOS_SIZE + USB_DT_USB_SS_CAP_SIZE); + temp = xhci_readl(xhci, &xhci->cap_regs->hcs_params3); + buf[12] = HCS_U1_LATENCY(temp); + put_unaligned_le16(HCS_U2_LATENCY(temp), &buf[13]); + + spin_unlock_irqrestore(&xhci->lock, flags); + return USB_DT_BOS_SIZE + USB_DT_USB_SS_CAP_SIZE; case GetPortStatus: if (!wIndex || wIndex > max_ports) goto error; @@ -472,11 +532,8 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, xhci_dbg(xhci, "Resume USB2 port %d\n", wIndex + 1); bus_state->resume_done[wIndex] = 0; - temp1 = xhci_port_state_to_neutral(temp); - temp1 &= ~PORT_PLS_MASK; - temp1 |= PORT_LINK_STROBE | XDEV_U0; - xhci_writel(xhci, temp1, port_array[wIndex]); - + xhci_set_link_state(xhci, port_array, wIndex, + XDEV_U0); xhci_dbg(xhci, "set port %d resume\n", wIndex + 1); slot_id = xhci_find_slot_id_by_port(hcd, xhci, @@ -551,10 +608,19 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, switch (wValue) { case USB_PORT_FEAT_SUSPEND: temp = xhci_readl(xhci, port_array[wIndex]); + if ((temp & PORT_PLS_MASK) != XDEV_U0) { + /* Resume the port to U0 first */ + xhci_set_link_state(xhci, port_array, wIndex, + XDEV_U0); + spin_unlock_irqrestore(&xhci->lock, flags); + msleep(10); + spin_lock_irqsave(&xhci->lock, flags); + } /* In spec software should not attempt to suspend * a port unless the port reports that it is in the * enabled (PED = ‘1’,PLS < ‘3’) state. */ + temp = xhci_readl(xhci, port_array[wIndex]); if ((temp & PORT_PE) == 0 || (temp & PORT_RESET) || (temp & PORT_PLS_MASK) >= XDEV_U3) { xhci_warn(xhci, "USB core suspending device " @@ -573,10 +639,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, xhci_stop_device(xhci, slot_id, 1); spin_lock_irqsave(&xhci->lock, flags); - temp = xhci_port_state_to_neutral(temp); - temp &= ~PORT_PLS_MASK; - temp |= PORT_LINK_STROBE | XDEV_U3; - xhci_writel(xhci, temp, port_array[wIndex]); + xhci_set_link_state(xhci, port_array, wIndex, XDEV_U3); spin_unlock_irqrestore(&xhci->lock, flags); msleep(10); /* wait device to enter */ @@ -610,10 +673,8 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, } } - temp = xhci_port_state_to_neutral(temp); - temp &= ~PORT_PLS_MASK; - temp |= PORT_LINK_STROBE | link_state; - xhci_writel(xhci, temp, port_array[wIndex]); + xhci_set_link_state(xhci, port_array, wIndex, + link_state); spin_unlock_irqrestore(&xhci->lock, flags); msleep(20); /* wait device to enter */ @@ -677,24 +738,13 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, if ((temp & PORT_PE) == 0) goto error; - temp = xhci_port_state_to_neutral(temp); - temp &= ~PORT_PLS_MASK; - temp |= PORT_LINK_STROBE | XDEV_RESUME; - xhci_writel(xhci, temp, - port_array[wIndex]); - - spin_unlock_irqrestore(&xhci->lock, - flags); + xhci_set_link_state(xhci, port_array, wIndex, + XDEV_RESUME); + spin_unlock_irqrestore(&xhci->lock, flags); msleep(20); spin_lock_irqsave(&xhci->lock, flags); - - temp = xhci_readl(xhci, - port_array[wIndex]); - temp = xhci_port_state_to_neutral(temp); - temp &= ~PORT_PLS_MASK; - temp |= PORT_LINK_STROBE | XDEV_U0; - xhci_writel(xhci, temp, - port_array[wIndex]); + xhci_set_link_state(xhci, port_array, wIndex, + XDEV_U0); } bus_state->port_c_suspend |= 1 << wIndex; @@ -910,25 +960,18 @@ int xhci_bus_resume(struct usb_hcd *hcd) if (test_bit(port_index, &bus_state->bus_suspended) && (temp & PORT_PLS_MASK)) { if (DEV_SUPERSPEED(temp)) { - temp = xhci_port_state_to_neutral(temp); - temp &= ~PORT_PLS_MASK; - temp |= PORT_LINK_STROBE | XDEV_U0; - xhci_writel(xhci, temp, port_array[port_index]); + xhci_set_link_state(xhci, port_array, + port_index, XDEV_U0); } else { - temp = xhci_port_state_to_neutral(temp); - temp &= ~PORT_PLS_MASK; - temp |= PORT_LINK_STROBE | XDEV_RESUME; - xhci_writel(xhci, temp, port_array[port_index]); + xhci_set_link_state(xhci, port_array, + port_index, XDEV_RESUME); spin_unlock_irqrestore(&xhci->lock, flags); msleep(20); spin_lock_irqsave(&xhci->lock, flags); - temp = xhci_readl(xhci, port_array[port_index]); - temp = xhci_port_state_to_neutral(temp); - temp &= ~PORT_PLS_MASK; - temp |= PORT_LINK_STROBE | XDEV_U0; - xhci_writel(xhci, temp, port_array[port_index]); + xhci_set_link_state(xhci, port_array, + port_index, XDEV_U0); } /* wait for the port to enter U0 and report port link * state change. @@ -938,12 +981,8 @@ int xhci_bus_resume(struct usb_hcd *hcd) spin_lock_irqsave(&xhci->lock, flags); /* Clear PLC */ - temp = xhci_readl(xhci, port_array[port_index]); - if (temp & PORT_PLC) { - temp = xhci_port_state_to_neutral(temp); - temp |= PORT_PLC; - xhci_writel(xhci, temp, port_array[port_index]); - } + xhci_test_and_clear_bit(xhci, port_array, port_index, + PORT_PLC); slot_id = xhci_find_slot_id_by_port(hcd, xhci, port_index + 1); diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index d446886b22b0..42a22b8e6922 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -61,8 +61,6 @@ static struct xhci_segment *xhci_segment_alloc(struct xhci_hcd *xhci, gfp_t flag static void xhci_segment_free(struct xhci_hcd *xhci, struct xhci_segment *seg) { - if (!seg) - return; if (seg->trbs) { xhci_dbg(xhci, "Freeing DMA segment at %p (virtual) 0x%llx (DMA)\n", seg->trbs, (unsigned long long)seg->dma); @@ -81,7 +79,7 @@ static void xhci_segment_free(struct xhci_hcd *xhci, struct xhci_segment *seg) * related flags, such as End TRB, Toggle Cycle, and no snoop. */ static void xhci_link_segments(struct xhci_hcd *xhci, struct xhci_segment *prev, - struct xhci_segment *next, bool link_trbs) + struct xhci_segment *next, bool link_trbs, bool isoc) { u32 val; @@ -97,7 +95,9 @@ static void xhci_link_segments(struct xhci_hcd *xhci, struct xhci_segment *prev, val &= ~TRB_TYPE_BITMASK; val |= TRB_TYPE(TRB_LINK); /* Always set the chain bit with 0.95 hardware */ - if (xhci_link_trb_quirk(xhci)) + /* Set chain bit for isoc rings on AMD 0.96 host */ + if (xhci_link_trb_quirk(xhci) || + (isoc && (xhci->quirks & XHCI_AMD_0x96_HOST))) val |= TRB_CHAIN; prev->trbs[TRBS_PER_SEGMENT-1].link.control = cpu_to_le32(val); } @@ -112,18 +112,20 @@ void xhci_ring_free(struct xhci_hcd *xhci, struct xhci_ring *ring) struct xhci_segment *seg; struct xhci_segment *first_seg; - if (!ring || !ring->first_seg) + if (!ring) return; - first_seg = ring->first_seg; - seg = first_seg->next; - xhci_dbg(xhci, "Freeing ring at %p\n", ring); - while (seg != first_seg) { - struct xhci_segment *next = seg->next; - xhci_segment_free(xhci, seg); - seg = next; + if (ring->first_seg) { + first_seg = ring->first_seg; + seg = first_seg->next; + xhci_dbg(xhci, "Freeing ring at %p\n", ring); + while (seg != first_seg) { + struct xhci_segment *next = seg->next; + xhci_segment_free(xhci, seg); + seg = next; + } + xhci_segment_free(xhci, first_seg); + ring->first_seg = NULL; } - xhci_segment_free(xhci, first_seg); - ring->first_seg = NULL; kfree(ring); } @@ -152,7 +154,7 @@ static void xhci_initialize_ring_info(struct xhci_ring *ring) * See section 4.9.1 and figures 15 and 16. */ static struct xhci_ring *xhci_ring_alloc(struct xhci_hcd *xhci, - unsigned int num_segs, bool link_trbs, gfp_t flags) + unsigned int num_segs, bool link_trbs, bool isoc, gfp_t flags) { struct xhci_ring *ring; struct xhci_segment *prev; @@ -178,12 +180,12 @@ static struct xhci_ring *xhci_ring_alloc(struct xhci_hcd *xhci, next = xhci_segment_alloc(xhci, flags); if (!next) goto fail; - xhci_link_segments(xhci, prev, next, link_trbs); + xhci_link_segments(xhci, prev, next, link_trbs, isoc); prev = next; num_segs--; } - xhci_link_segments(xhci, prev, ring->first_seg, link_trbs); + xhci_link_segments(xhci, prev, ring->first_seg, link_trbs, isoc); if (link_trbs) { /* See section 4.9.2.1 and 6.4.4.1 */ @@ -229,14 +231,14 @@ void xhci_free_or_cache_endpoint_ring(struct xhci_hcd *xhci, * pointers to the beginning of the ring. */ static void xhci_reinit_cached_ring(struct xhci_hcd *xhci, - struct xhci_ring *ring) + struct xhci_ring *ring, bool isoc) { struct xhci_segment *seg = ring->first_seg; do { memset(seg->trbs, 0, sizeof(union xhci_trb)*TRBS_PER_SEGMENT); /* All endpoint rings have link TRBs */ - xhci_link_segments(xhci, seg, seg->next, 1); + xhci_link_segments(xhci, seg, seg->next, 1, isoc); seg = seg->next; } while (seg != ring->first_seg); xhci_initialize_ring_info(ring); @@ -315,7 +317,7 @@ static void xhci_free_stream_ctx(struct xhci_hcd *xhci, struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller); if (num_stream_ctxs > MEDIUM_STREAM_ARRAY_SIZE) - pci_free_consistent(pdev, + dma_free_coherent(&pdev->dev, sizeof(struct xhci_stream_ctx)*num_stream_ctxs, stream_ctx, dma); else if (num_stream_ctxs <= SMALL_STREAM_ARRAY_SIZE) @@ -343,9 +345,9 @@ static struct xhci_stream_ctx *xhci_alloc_stream_ctx(struct xhci_hcd *xhci, struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller); if (num_stream_ctxs > MEDIUM_STREAM_ARRAY_SIZE) - return pci_alloc_consistent(pdev, + return dma_alloc_coherent(&pdev->dev, sizeof(struct xhci_stream_ctx)*num_stream_ctxs, - dma); + dma, mem_flags); else if (num_stream_ctxs <= SMALL_STREAM_ARRAY_SIZE) return dma_pool_alloc(xhci->small_streams_pool, mem_flags, dma); @@ -540,7 +542,7 @@ struct xhci_stream_info *xhci_alloc_stream_info(struct xhci_hcd *xhci, */ for (cur_stream = 1; cur_stream < num_streams; cur_stream++) { stream_info->stream_rings[cur_stream] = - xhci_ring_alloc(xhci, 1, true, mem_flags); + xhci_ring_alloc(xhci, 1, true, false, mem_flags); cur_ring = stream_info->stream_rings[cur_stream]; if (!cur_ring) goto cleanup_rings; @@ -687,11 +689,103 @@ static void xhci_init_endpoint_timer(struct xhci_hcd *xhci, ep->xhci = xhci; } -/* All the xhci_tds in the ring's TD list should be freed at this point */ +static void xhci_free_tt_info(struct xhci_hcd *xhci, + struct xhci_virt_device *virt_dev, + int slot_id) +{ + struct list_head *tt; + struct list_head *tt_list_head; + struct list_head *tt_next; + struct xhci_tt_bw_info *tt_info; + + /* If the device never made it past the Set Address stage, + * it may not have the real_port set correctly. + */ + if (virt_dev->real_port == 0 || + virt_dev->real_port > HCS_MAX_PORTS(xhci->hcs_params1)) { + xhci_dbg(xhci, "Bad real port.\n"); + return; + } + + tt_list_head = &(xhci->rh_bw[virt_dev->real_port - 1].tts); + if (list_empty(tt_list_head)) + return; + + list_for_each(tt, tt_list_head) { + tt_info = list_entry(tt, struct xhci_tt_bw_info, tt_list); + if (tt_info->slot_id == slot_id) + break; + } + /* Cautionary measure in case the hub was disconnected before we + * stored the TT information. + */ + if (tt_info->slot_id != slot_id) + return; + + tt_next = tt->next; + tt_info = list_entry(tt, struct xhci_tt_bw_info, + tt_list); + /* Multi-TT hubs will have more than one entry */ + do { + list_del(tt); + kfree(tt_info); + tt = tt_next; + if (list_empty(tt_list_head)) + break; + tt_next = tt->next; + tt_info = list_entry(tt, struct xhci_tt_bw_info, + tt_list); + } while (tt_info->slot_id == slot_id); +} + +int xhci_alloc_tt_info(struct xhci_hcd *xhci, + struct xhci_virt_device *virt_dev, + struct usb_device *hdev, + struct usb_tt *tt, gfp_t mem_flags) +{ + struct xhci_tt_bw_info *tt_info; + unsigned int num_ports; + int i, j; + + if (!tt->multi) + num_ports = 1; + else + num_ports = hdev->maxchild; + + for (i = 0; i < num_ports; i++, tt_info++) { + struct xhci_interval_bw_table *bw_table; + + tt_info = kzalloc(sizeof(*tt_info), mem_flags); + if (!tt_info) + goto free_tts; + INIT_LIST_HEAD(&tt_info->tt_list); + list_add(&tt_info->tt_list, + &xhci->rh_bw[virt_dev->real_port - 1].tts); + tt_info->slot_id = virt_dev->udev->slot_id; + if (tt->multi) + tt_info->ttport = i+1; + bw_table = &tt_info->bw_table; + for (j = 0; j < XHCI_MAX_INTERVAL; j++) + INIT_LIST_HEAD(&bw_table->interval_bw[j].endpoints); + } + return 0; + +free_tts: + xhci_free_tt_info(xhci, virt_dev, virt_dev->udev->slot_id); + return -ENOMEM; +} + + +/* All the xhci_tds in the ring's TD list should be freed at this point. + * Should be called with xhci->lock held if there is any chance the TT lists + * will be manipulated by the configure endpoint, allocate device, or update + * hub functions while this function is removing the TT entries from the list. + */ void xhci_free_virt_device(struct xhci_hcd *xhci, int slot_id) { struct xhci_virt_device *dev; int i; + int old_active_eps = 0; /* Slot ID 0 is reserved */ if (slot_id == 0 || !xhci->devs[slot_id]) @@ -702,13 +796,29 @@ void xhci_free_virt_device(struct xhci_hcd *xhci, int slot_id) if (!dev) return; + if (dev->tt_info) + old_active_eps = dev->tt_info->active_eps; + for (i = 0; i < 31; ++i) { if (dev->eps[i].ring) xhci_ring_free(xhci, dev->eps[i].ring); if (dev->eps[i].stream_info) xhci_free_stream_info(xhci, dev->eps[i].stream_info); + /* Endpoints on the TT/root port lists should have been removed + * when usb_disable_device() was called for the device. + * We can't drop them anyway, because the udev might have gone + * away by this point, and we can't tell what speed it was. + */ + if (!list_empty(&dev->eps[i].bw_endpoint_list)) + xhci_warn(xhci, "Slot %u endpoint %u " + "not removed from BW list!\n", + slot_id, i); } + /* If this is a hub, free the TT(s) from the TT list */ + xhci_free_tt_info(xhci, dev, slot_id); + /* If necessary, update the number of active TTs on this root port */ + xhci_update_tt_active_eps(xhci, dev, old_active_eps); if (dev->ring_cache) { for (i = 0; i < dev->num_rings_cached; i++) @@ -762,10 +872,11 @@ int xhci_alloc_virt_device(struct xhci_hcd *xhci, int slot_id, for (i = 0; i < 31; i++) { xhci_init_endpoint_timer(xhci, &dev->eps[i]); INIT_LIST_HEAD(&dev->eps[i].cancelled_td_list); + INIT_LIST_HEAD(&dev->eps[i].bw_endpoint_list); } /* Allocate endpoint 0 ring */ - dev->eps[0].ring = xhci_ring_alloc(xhci, 1, true, flags); + dev->eps[0].ring = xhci_ring_alloc(xhci, 1, true, false, flags); if (!dev->eps[0].ring) goto fail; @@ -921,9 +1032,40 @@ int xhci_setup_addressable_virt_dev(struct xhci_hcd *xhci, struct usb_device *ud for (top_dev = udev; top_dev->parent && top_dev->parent->parent; top_dev = top_dev->parent) /* Found device below root hub */; - dev->port = top_dev->portnum; + dev->fake_port = top_dev->portnum; + dev->real_port = port_num; xhci_dbg(xhci, "Set root hub portnum to %d\n", port_num); - xhci_dbg(xhci, "Set fake root hub portnum to %d\n", dev->port); + xhci_dbg(xhci, "Set fake root hub portnum to %d\n", dev->fake_port); + + /* Find the right bandwidth table that this device will be a part of. + * If this is a full speed device attached directly to a root port (or a + * decendent of one), it counts as a primary bandwidth domain, not a + * secondary bandwidth domain under a TT. An xhci_tt_info structure + * will never be created for the HS root hub. + */ + if (!udev->tt || !udev->tt->hub->parent) { + dev->bw_table = &xhci->rh_bw[port_num - 1].bw_table; + } else { + struct xhci_root_port_bw_info *rh_bw; + struct xhci_tt_bw_info *tt_bw; + + rh_bw = &xhci->rh_bw[port_num - 1]; + /* Find the right TT. */ + list_for_each_entry(tt_bw, &rh_bw->tts, tt_list) { + if (tt_bw->slot_id != udev->tt->hub->slot_id) + continue; + + if (!dev->udev->tt->multi || + (udev->tt->multi && + tt_bw->ttport == dev->udev->ttport)) { + dev->bw_table = &tt_bw->bw_table; + dev->tt_info = tt_bw; + break; + } + } + if (!dev->tt_info) + xhci_warn(xhci, "WARN: Didn't find a matching TT\n"); + } /* Is this a LS/FS device under an external HS hub? */ if (udev->tt && udev->tt->hub->parent) { @@ -1141,8 +1283,8 @@ static u32 xhci_get_max_esit_payload(struct xhci_hcd *xhci, if (udev->speed == USB_SPEED_SUPER) return le16_to_cpu(ep->ss_ep_comp.wBytesPerInterval); - max_packet = GET_MAX_PACKET(le16_to_cpu(ep->desc.wMaxPacketSize)); - max_burst = (le16_to_cpu(ep->desc.wMaxPacketSize) & 0x1800) >> 11; + max_packet = GET_MAX_PACKET(usb_endpoint_maxp(&ep->desc)); + max_burst = (usb_endpoint_maxp(&ep->desc) & 0x1800) >> 11; /* A 0 in max burst means 1 transfer per ESIT */ return max_packet * (max_burst + 1); } @@ -1175,10 +1317,10 @@ int xhci_endpoint_init(struct xhci_hcd *xhci, */ if (usb_endpoint_xfer_isoc(&ep->desc)) virt_dev->eps[ep_index].new_ring = - xhci_ring_alloc(xhci, 8, true, mem_flags); + xhci_ring_alloc(xhci, 8, true, true, mem_flags); else virt_dev->eps[ep_index].new_ring = - xhci_ring_alloc(xhci, 1, true, mem_flags); + xhci_ring_alloc(xhci, 1, true, false, mem_flags); if (!virt_dev->eps[ep_index].new_ring) { /* Attempt to use the ring cache */ if (virt_dev->num_rings_cached == 0) @@ -1187,7 +1329,8 @@ int xhci_endpoint_init(struct xhci_hcd *xhci, virt_dev->ring_cache[virt_dev->num_rings_cached]; virt_dev->ring_cache[virt_dev->num_rings_cached] = NULL; virt_dev->num_rings_cached--; - xhci_reinit_cached_ring(xhci, virt_dev->eps[ep_index].new_ring); + xhci_reinit_cached_ring(xhci, virt_dev->eps[ep_index].new_ring, + usb_endpoint_xfer_isoc(&ep->desc) ? true : false); } virt_dev->eps[ep_index].skip = false; ep_ring = virt_dev->eps[ep_index].new_ring; @@ -1211,7 +1354,7 @@ int xhci_endpoint_init(struct xhci_hcd *xhci, /* Set the max packet size and max burst */ switch (udev->speed) { case USB_SPEED_SUPER: - max_packet = le16_to_cpu(ep->desc.wMaxPacketSize); + max_packet = usb_endpoint_maxp(&ep->desc); ep_ctx->ep_info2 |= cpu_to_le32(MAX_PACKET(max_packet)); /* dig out max burst from ep companion desc */ max_packet = ep->ss_ep_comp.bMaxBurst; @@ -1223,14 +1366,14 @@ int xhci_endpoint_init(struct xhci_hcd *xhci, */ if (usb_endpoint_xfer_isoc(&ep->desc) || usb_endpoint_xfer_int(&ep->desc)) { - max_burst = (le16_to_cpu(ep->desc.wMaxPacketSize) + max_burst = (usb_endpoint_maxp(&ep->desc) & 0x1800) >> 11; ep_ctx->ep_info2 |= cpu_to_le32(MAX_BURST(max_burst)); } /* Fall through */ case USB_SPEED_FULL: case USB_SPEED_LOW: - max_packet = GET_MAX_PACKET(le16_to_cpu(ep->desc.wMaxPacketSize)); + max_packet = GET_MAX_PACKET(usb_endpoint_maxp(&ep->desc)); ep_ctx->ep_info2 |= cpu_to_le32(MAX_PACKET(max_packet)); break; default: @@ -1286,6 +1429,70 @@ void xhci_endpoint_zero(struct xhci_hcd *xhci, */ } +void xhci_clear_endpoint_bw_info(struct xhci_bw_info *bw_info) +{ + bw_info->ep_interval = 0; + bw_info->mult = 0; + bw_info->num_packets = 0; + bw_info->max_packet_size = 0; + bw_info->type = 0; + bw_info->max_esit_payload = 0; +} + +void xhci_update_bw_info(struct xhci_hcd *xhci, + struct xhci_container_ctx *in_ctx, + struct xhci_input_control_ctx *ctrl_ctx, + struct xhci_virt_device *virt_dev) +{ + struct xhci_bw_info *bw_info; + struct xhci_ep_ctx *ep_ctx; + unsigned int ep_type; + int i; + + for (i = 1; i < 31; ++i) { + bw_info = &virt_dev->eps[i].bw_info; + + /* We can't tell what endpoint type is being dropped, but + * unconditionally clearing the bandwidth info for non-periodic + * endpoints should be harmless because the info will never be + * set in the first place. + */ + if (!EP_IS_ADDED(ctrl_ctx, i) && EP_IS_DROPPED(ctrl_ctx, i)) { + /* Dropped endpoint */ + xhci_clear_endpoint_bw_info(bw_info); + continue; + } + + if (EP_IS_ADDED(ctrl_ctx, i)) { + ep_ctx = xhci_get_ep_ctx(xhci, in_ctx, i); + ep_type = CTX_TO_EP_TYPE(le32_to_cpu(ep_ctx->ep_info2)); + + /* Ignore non-periodic endpoints */ + if (ep_type != ISOC_OUT_EP && ep_type != INT_OUT_EP && + ep_type != ISOC_IN_EP && + ep_type != INT_IN_EP) + continue; + + /* Added or changed endpoint */ + bw_info->ep_interval = CTX_TO_EP_INTERVAL( + le32_to_cpu(ep_ctx->ep_info)); + /* Number of packets and mult are zero-based in the + * input context, but we want one-based for the + * interval table. + */ + bw_info->mult = CTX_TO_EP_MULT( + le32_to_cpu(ep_ctx->ep_info)) + 1; + bw_info->num_packets = CTX_TO_MAX_BURST( + le32_to_cpu(ep_ctx->ep_info2)) + 1; + bw_info->max_packet_size = MAX_PACKET_DECODED( + le32_to_cpu(ep_ctx->ep_info2)); + bw_info->type = ep_type; + bw_info->max_esit_payload = CTX_TO_MAX_ESIT_PAYLOAD( + le32_to_cpu(ep_ctx->tx_info)); + } + } +} + /* Copy output xhci_ep_ctx to the input xhci_ep_ctx copy. * Useful when you want to change one particular aspect of the endpoint and then * issue a configure endpoint command. @@ -1344,10 +1551,9 @@ static int scratchpad_alloc(struct xhci_hcd *xhci, gfp_t flags) if (!xhci->scratchpad) goto fail_sp; - xhci->scratchpad->sp_array = - pci_alloc_consistent(to_pci_dev(dev), + xhci->scratchpad->sp_array = dma_alloc_coherent(dev, num_sp * sizeof(u64), - &xhci->scratchpad->sp_dma); + &xhci->scratchpad->sp_dma, flags); if (!xhci->scratchpad->sp_array) goto fail_sp2; @@ -1364,8 +1570,8 @@ static int scratchpad_alloc(struct xhci_hcd *xhci, gfp_t flags) xhci->dcbaa->dev_context_ptrs[0] = cpu_to_le64(xhci->scratchpad->sp_dma); for (i = 0; i < num_sp; i++) { dma_addr_t dma; - void *buf = pci_alloc_consistent(to_pci_dev(dev), - xhci->page_size, &dma); + void *buf = dma_alloc_coherent(dev, xhci->page_size, &dma, + flags); if (!buf) goto fail_sp5; @@ -1378,7 +1584,7 @@ static int scratchpad_alloc(struct xhci_hcd *xhci, gfp_t flags) fail_sp5: for (i = i - 1; i >= 0; i--) { - pci_free_consistent(to_pci_dev(dev), xhci->page_size, + dma_free_coherent(dev, xhci->page_size, xhci->scratchpad->sp_buffers[i], xhci->scratchpad->sp_dma_buffers[i]); } @@ -1388,7 +1594,7 @@ static int scratchpad_alloc(struct xhci_hcd *xhci, gfp_t flags) kfree(xhci->scratchpad->sp_buffers); fail_sp3: - pci_free_consistent(to_pci_dev(dev), num_sp * sizeof(u64), + dma_free_coherent(dev, num_sp * sizeof(u64), xhci->scratchpad->sp_array, xhci->scratchpad->sp_dma); @@ -1412,13 +1618,13 @@ static void scratchpad_free(struct xhci_hcd *xhci) num_sp = HCS_MAX_SCRATCHPAD(xhci->hcs_params2); for (i = 0; i < num_sp; i++) { - pci_free_consistent(pdev, xhci->page_size, + dma_free_coherent(&pdev->dev, xhci->page_size, xhci->scratchpad->sp_buffers[i], xhci->scratchpad->sp_dma_buffers[i]); } kfree(xhci->scratchpad->sp_dma_buffers); kfree(xhci->scratchpad->sp_buffers); - pci_free_consistent(pdev, num_sp * sizeof(u64), + dma_free_coherent(&pdev->dev, num_sp * sizeof(u64), xhci->scratchpad->sp_array, xhci->scratchpad->sp_dma); kfree(xhci->scratchpad); @@ -1463,18 +1669,10 @@ struct xhci_command *xhci_alloc_command(struct xhci_hcd *xhci, void xhci_urb_free_priv(struct xhci_hcd *xhci, struct urb_priv *urb_priv) { - int last; - - if (!urb_priv) - return; - - last = urb_priv->length - 1; - if (last >= 0) { - int i; - for (i = 0; i <= last; i++) - kfree(urb_priv->td[i]); + if (urb_priv) { + kfree(urb_priv->td[0]); + kfree(urb_priv); } - kfree(urb_priv); } void xhci_free_command(struct xhci_hcd *xhci, @@ -1489,6 +1687,8 @@ void xhci_free_command(struct xhci_hcd *xhci, void xhci_mem_cleanup(struct xhci_hcd *xhci) { struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller); + struct dev_info *dev_info, *next; + unsigned long flags; int size; int i; @@ -1500,7 +1700,7 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci) } size = sizeof(struct xhci_erst_entry)*(xhci->erst.num_entries); if (xhci->erst.entries) - pci_free_consistent(pdev, size, + dma_free_coherent(&pdev->dev, size, xhci->erst.entries, xhci->erst.erst_dma_addr); xhci->erst.entries = NULL; xhci_dbg(xhci, "Freed ERST\n"); @@ -1540,17 +1740,25 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci) xhci_write_64(xhci, 0, &xhci->op_regs->dcbaa_ptr); if (xhci->dcbaa) - pci_free_consistent(pdev, sizeof(*xhci->dcbaa), + dma_free_coherent(&pdev->dev, sizeof(*xhci->dcbaa), xhci->dcbaa, xhci->dcbaa->dma); xhci->dcbaa = NULL; scratchpad_free(xhci); + spin_lock_irqsave(&xhci->lock, flags); + list_for_each_entry_safe(dev_info, next, &xhci->lpm_failed_devs, list) { + list_del(&dev_info->list); + kfree(dev_info); + } + spin_unlock_irqrestore(&xhci->lock, flags); + xhci->num_usb2_ports = 0; xhci->num_usb3_ports = 0; kfree(xhci->usb2_ports); kfree(xhci->usb3_ports); kfree(xhci->port_array); + kfree(xhci->rh_bw); xhci->page_size = 0; xhci->page_shift = 0; @@ -1762,6 +1970,23 @@ static void xhci_add_in_port(struct xhci_hcd *xhci, unsigned int num_ports, if (port_offset == 0 || (port_offset + port_count - 1) > num_ports) /* WTF? "Valid values are ‘1’ to MaxPorts" */ return; + + /* Check the host's USB2 LPM capability */ + if ((xhci->hci_version == 0x96) && (major_revision != 0x03) && + (temp & XHCI_L1C)) { + xhci_dbg(xhci, "xHCI 0.96: support USB2 software lpm\n"); + xhci->sw_lpm_support = 1; + } + + if ((xhci->hci_version >= 0x100) && (major_revision != 0x03)) { + xhci_dbg(xhci, "xHCI 1.0: support USB2 software lpm\n"); + xhci->sw_lpm_support = 1; + if (temp & XHCI_HLC) { + xhci_dbg(xhci, "xHCI 1.0: support USB2 hardware lpm\n"); + xhci->hw_lpm_support = 1; + } + } + port_offset--; for (i = port_offset; i < (port_offset + port_count); i++) { /* Duplicate entry. Ignore the port if the revisions differ. */ @@ -1806,7 +2031,7 @@ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags) __le32 __iomem *addr; u32 offset; unsigned int num_ports; - int i, port_index; + int i, j, port_index; addr = &xhci->cap_regs->hcc_params; offset = XHCI_HCC_EXT_CAPS(xhci_readl(xhci, addr)); @@ -1821,6 +2046,18 @@ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags) if (!xhci->port_array) return -ENOMEM; + xhci->rh_bw = kzalloc(sizeof(*xhci->rh_bw)*num_ports, flags); + if (!xhci->rh_bw) + return -ENOMEM; + for (i = 0; i < num_ports; i++) { + struct xhci_interval_bw_table *bw_table; + + INIT_LIST_HEAD(&xhci->rh_bw[i].tts); + bw_table = &xhci->rh_bw[i].bw_table; + for (j = 0; j < XHCI_MAX_INTERVAL; j++) + INIT_LIST_HEAD(&bw_table->interval_bw[j].endpoints); + } + /* * For whatever reason, the first capability offset is from the * capability register base, not from the HCCPARAMS register. @@ -1959,8 +2196,8 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) * Section 5.4.8 - doorbell array must be * "physically contiguous and 64-byte (cache line) aligned". */ - xhci->dcbaa = pci_alloc_consistent(to_pci_dev(dev), - sizeof(*xhci->dcbaa), &dma); + xhci->dcbaa = dma_alloc_coherent(dev, sizeof(*xhci->dcbaa), &dma, + GFP_KERNEL); if (!xhci->dcbaa) goto fail; memset(xhci->dcbaa, 0, sizeof *(xhci->dcbaa)); @@ -1994,14 +2231,14 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) dma_pool_create("xHCI 1KB stream ctx arrays", dev, MEDIUM_STREAM_ARRAY_SIZE, 16, 0); /* Any stream context array bigger than MEDIUM_STREAM_ARRAY_SIZE - * will be allocated with pci_alloc_consistent() + * will be allocated with dma_alloc_coherent() */ if (!xhci->small_streams_pool || !xhci->medium_streams_pool) goto fail; /* Set up the command ring to have one segments for now. */ - xhci->cmd_ring = xhci_ring_alloc(xhci, 1, true, flags); + xhci->cmd_ring = xhci_ring_alloc(xhci, 1, true, false, flags); if (!xhci->cmd_ring) goto fail; xhci_dbg(xhci, "Allocated command ring at %p\n", xhci->cmd_ring); @@ -2032,14 +2269,16 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) * the event ring segment table (ERST). Section 4.9.3. */ xhci_dbg(xhci, "// Allocating event ring\n"); - xhci->event_ring = xhci_ring_alloc(xhci, ERST_NUM_SEGS, false, flags); + xhci->event_ring = xhci_ring_alloc(xhci, ERST_NUM_SEGS, false, false, + flags); if (!xhci->event_ring) goto fail; if (xhci_check_trb_in_td_math(xhci, flags) < 0) goto fail; - xhci->erst.entries = pci_alloc_consistent(to_pci_dev(dev), - sizeof(struct xhci_erst_entry)*ERST_NUM_SEGS, &dma); + xhci->erst.entries = dma_alloc_coherent(dev, + sizeof(struct xhci_erst_entry) * ERST_NUM_SEGS, &dma, + GFP_KERNEL); if (!xhci->erst.entries) goto fail; xhci_dbg(xhci, "// Allocated event ring segment table at 0x%llx\n", @@ -2102,6 +2341,8 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) if (xhci_setup_port_arrays(xhci, flags)) goto fail; + INIT_LIST_HEAD(&xhci->lpm_failed_devs); + return 0; fail: diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index cb16de213f64..9f51f88cc0f5 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -51,61 +51,9 @@ static int xhci_pci_reinit(struct xhci_hcd *xhci, struct pci_dev *pdev) return 0; } -/* called during probe() after chip reset completes */ -static int xhci_pci_setup(struct usb_hcd *hcd) +static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci) { - struct xhci_hcd *xhci; - struct pci_dev *pdev = to_pci_dev(hcd->self.controller); - int retval; - u32 temp; - - hcd->self.sg_tablesize = TRBS_PER_SEGMENT - 2; - - if (usb_hcd_is_primary_hcd(hcd)) { - xhci = kzalloc(sizeof(struct xhci_hcd), GFP_KERNEL); - if (!xhci) - return -ENOMEM; - *((struct xhci_hcd **) hcd->hcd_priv) = xhci; - xhci->main_hcd = hcd; - /* Mark the first roothub as being USB 2.0. - * The xHCI driver will register the USB 3.0 roothub. - */ - hcd->speed = HCD_USB2; - hcd->self.root_hub->speed = USB_SPEED_HIGH; - /* - * USB 2.0 roothub under xHCI has an integrated TT, - * (rate matching hub) as opposed to having an OHCI/UHCI - * companion controller. - */ - hcd->has_tt = 1; - } else { - /* xHCI private pointer was set in xhci_pci_probe for the second - * registered roothub. - */ - xhci = hcd_to_xhci(hcd); - temp = xhci_readl(xhci, &xhci->cap_regs->hcc_params); - if (HCC_64BIT_ADDR(temp)) { - xhci_dbg(xhci, "Enabling 64-bit DMA addresses.\n"); - dma_set_mask(hcd->self.controller, DMA_BIT_MASK(64)); - } else { - dma_set_mask(hcd->self.controller, DMA_BIT_MASK(32)); - } - return 0; - } - - xhci->cap_regs = hcd->regs; - xhci->op_regs = hcd->regs + - HC_LENGTH(xhci_readl(xhci, &xhci->cap_regs->hc_capbase)); - xhci->run_regs = hcd->regs + - (xhci_readl(xhci, &xhci->cap_regs->run_regs_off) & RTSOFF_MASK); - /* Cache read-only capability registers */ - xhci->hcs_params1 = xhci_readl(xhci, &xhci->cap_regs->hcs_params1); - xhci->hcs_params2 = xhci_readl(xhci, &xhci->cap_regs->hcs_params2); - xhci->hcs_params3 = xhci_readl(xhci, &xhci->cap_regs->hcs_params3); - xhci->hcc_params = xhci_readl(xhci, &xhci->cap_regs->hc_capbase); - xhci->hci_version = HC_VERSION(xhci->hcc_params); - xhci->hcc_params = xhci_readl(xhci, &xhci->cap_regs->hcc_params); - xhci_print_registers(xhci); + struct pci_dev *pdev = to_pci_dev(dev); /* Look for vendor-specific quirks */ if (pdev->vendor == PCI_VENDOR_ID_FRESCO_LOGIC && @@ -128,6 +76,9 @@ static int xhci_pci_setup(struct usb_hcd *hcd) if (pdev->vendor == PCI_VENDOR_ID_NEC) xhci->quirks |= XHCI_NEC_HOST; + if (pdev->vendor == PCI_VENDOR_ID_AMD && xhci->hci_version == 0x96) + xhci->quirks |= XHCI_AMD_0x96_HOST; + /* AMD PLL quirk */ if (pdev->vendor == PCI_VENDOR_ID_AMD && usb_amd_find_chipset_info()) xhci->quirks |= XHCI_AMD_PLL_FIX; @@ -136,39 +87,29 @@ static int xhci_pci_setup(struct usb_hcd *hcd) xhci->quirks |= XHCI_SPURIOUS_SUCCESS; xhci->quirks |= XHCI_EP_LIMIT_QUIRK; xhci->limit_active_eps = 64; + xhci->quirks |= XHCI_SW_BW_CHECKING; } if (pdev->vendor == PCI_VENDOR_ID_ETRON && pdev->device == PCI_DEVICE_ID_ASROCK_P67) { xhci->quirks |= XHCI_RESET_ON_RESUME; xhci_dbg(xhci, "QUIRK: Resetting on resume\n"); } +} - /* Make sure the HC is halted. */ - retval = xhci_halt(xhci); - if (retval) - goto error; +/* called during probe() after chip reset completes */ +static int xhci_pci_setup(struct usb_hcd *hcd) +{ + struct xhci_hcd *xhci; + struct pci_dev *pdev = to_pci_dev(hcd->self.controller); + int retval; - xhci_dbg(xhci, "Resetting HCD\n"); - /* Reset the internal HC memory state and registers. */ - retval = xhci_reset(xhci); + retval = xhci_gen_setup(hcd, xhci_pci_quirks); if (retval) - goto error; - xhci_dbg(xhci, "Reset complete\n"); - - temp = xhci_readl(xhci, &xhci->cap_regs->hcc_params); - if (HCC_64BIT_ADDR(temp)) { - xhci_dbg(xhci, "Enabling 64-bit DMA addresses.\n"); - dma_set_mask(hcd->self.controller, DMA_BIT_MASK(64)); - } else { - dma_set_mask(hcd->self.controller, DMA_BIT_MASK(32)); - } + return retval; - xhci_dbg(xhci, "Calling HCD init\n"); - /* Initialize HCD and host controller data structures. */ - retval = xhci_init(hcd); - if (retval) - goto error; - xhci_dbg(xhci, "Called HCD init\n"); + xhci = hcd_to_xhci(hcd); + if (!usb_hcd_is_primary_hcd(hcd)) + return 0; pci_read_config_byte(pdev, XHCI_SBRN_OFFSET, &xhci->sbrn); xhci_dbg(xhci, "Got SBRN %u\n", (unsigned int) xhci->sbrn); @@ -178,7 +119,6 @@ static int xhci_pci_setup(struct usb_hcd *hcd) if (!retval) return retval; -error: kfree(xhci); return retval; } @@ -222,7 +162,7 @@ static int xhci_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) *((struct xhci_hcd **) xhci->shared_hcd->hcd_priv) = xhci; retval = usb_add_hcd(xhci->shared_hcd, dev->irq, - IRQF_DISABLED | IRQF_SHARED); + IRQF_SHARED); if (retval) goto put_usb3_hcd; /* Roothub already marked as USB 3.0 speed */ @@ -344,6 +284,11 @@ static const struct hc_driver xhci_pci_hc_driver = { .hub_status_data = xhci_hub_status_data, .bus_suspend = xhci_bus_suspend, .bus_resume = xhci_bus_resume, + /* + * call back when device connected and addressed + */ + .update_device = xhci_update_device, + .set_usb2_hw_lpm = xhci_set_usb2_hardware_lpm, }; /*-------------------------------------------------------------------------*/ @@ -375,12 +320,12 @@ static struct pci_driver xhci_pci_driver = { #endif }; -int xhci_register_pci(void) +int __init xhci_register_pci(void) { return pci_register_driver(&xhci_pci_driver); } -void xhci_unregister_pci(void) +void __exit xhci_unregister_pci(void) { pci_unregister_driver(&xhci_pci_driver); } diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 952e2ded61af..940321b3ec68 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -185,7 +185,7 @@ static void inc_deq(struct xhci_hcd *xhci, struct xhci_ring *ring, bool consumer * prepare_transfer()? */ static void inc_enq(struct xhci_hcd *xhci, struct xhci_ring *ring, - bool consumer, bool more_trbs_coming) + bool consumer, bool more_trbs_coming, bool isoc) { u32 chain; union xhci_trb *next; @@ -212,11 +212,13 @@ static void inc_enq(struct xhci_hcd *xhci, struct xhci_ring *ring, if (!chain && !more_trbs_coming) break; - /* If we're not dealing with 0.95 hardware, + /* If we're not dealing with 0.95 hardware or + * isoc rings on AMD 0.96 host, * carry over the chain bit of the previous TRB * (which may mean the chain bit is cleared). */ - if (!xhci_link_trb_quirk(xhci)) { + if (!(isoc && (xhci->quirks & XHCI_AMD_0x96_HOST)) + && !xhci_link_trb_quirk(xhci)) { next->link.control &= cpu_to_le32(~TRB_CHAIN); next->link.control |= @@ -1329,10 +1331,8 @@ static void handle_port_status(struct xhci_hcd *xhci, if (DEV_SUPERSPEED(temp)) { xhci_dbg(xhci, "resume SS port %d\n", port_id); - temp = xhci_port_state_to_neutral(temp); - temp &= ~PORT_PLS_MASK; - temp |= PORT_LINK_STROBE | XDEV_U0; - xhci_writel(xhci, temp, port_array[faked_port_index]); + xhci_set_link_state(xhci, port_array, faked_port_index, + XDEV_U0); slot_id = xhci_find_slot_id_by_port(hcd, xhci, faked_port_index); if (!slot_id) { @@ -1342,10 +1342,8 @@ static void handle_port_status(struct xhci_hcd *xhci, xhci_ring_device(xhci, slot_id); xhci_dbg(xhci, "resume SS port %d finished\n", port_id); /* Clear PORT_PLC */ - temp = xhci_readl(xhci, port_array[faked_port_index]); - temp = xhci_port_state_to_neutral(temp); - temp |= PORT_PLC; - xhci_writel(xhci, temp, port_array[faked_port_index]); + xhci_test_and_clear_bit(xhci, port_array, + faked_port_index, PORT_PLC); } else { xhci_dbg(xhci, "resume HS port %d\n", port_id); bus_state->resume_done[faked_port_index] = jiffies + @@ -1356,6 +1354,10 @@ static void handle_port_status(struct xhci_hcd *xhci, } } + if (hcd->speed != HCD_USB3) + xhci_test_and_clear_bit(xhci, port_array, faked_port_index, + PORT_PLC); + cleanup: /* Update event ring dequeue pointer before dropping the lock */ inc_deq(xhci, xhci->event_ring, true); @@ -2192,7 +2194,8 @@ cleanup: if ((urb->actual_length != urb->transfer_buffer_length && (urb->transfer_flags & URB_SHORT_NOT_OK)) || - status != 0) + (status != 0 && + !usb_endpoint_xfer_isoc(&urb->ep->desc))) xhci_dbg(xhci, "Giveback URB %p, len = %d, " "expected = %x, status = %d\n", urb, urb->actual_length, @@ -2409,7 +2412,7 @@ irqreturn_t xhci_msi_irq(int irq, struct usb_hcd *hcd) * prepare_transfer()? */ static void queue_trb(struct xhci_hcd *xhci, struct xhci_ring *ring, - bool consumer, bool more_trbs_coming, + bool consumer, bool more_trbs_coming, bool isoc, u32 field1, u32 field2, u32 field3, u32 field4) { struct xhci_generic_trb *trb; @@ -2419,7 +2422,7 @@ static void queue_trb(struct xhci_hcd *xhci, struct xhci_ring *ring, trb->field[1] = cpu_to_le32(field2); trb->field[2] = cpu_to_le32(field3); trb->field[3] = cpu_to_le32(field4); - inc_enq(xhci, ring, consumer, more_trbs_coming); + inc_enq(xhci, ring, consumer, more_trbs_coming, isoc); } /* @@ -2427,7 +2430,7 @@ static void queue_trb(struct xhci_hcd *xhci, struct xhci_ring *ring, * FIXME allocate segments if the ring is full. */ static int prepare_ring(struct xhci_hcd *xhci, struct xhci_ring *ep_ring, - u32 ep_state, unsigned int num_trbs, gfp_t mem_flags) + u32 ep_state, unsigned int num_trbs, bool isoc, gfp_t mem_flags) { /* Make sure the endpoint has been added to xHC schedule */ switch (ep_state) { @@ -2469,10 +2472,11 @@ static int prepare_ring(struct xhci_hcd *xhci, struct xhci_ring *ep_ring, next = ring->enqueue; while (last_trb(xhci, ring, ring->enq_seg, next)) { - /* If we're not dealing with 0.95 hardware, - * clear the chain bit. + /* If we're not dealing with 0.95 hardware or isoc rings + * on AMD 0.96 host, clear the chain bit. */ - if (!xhci_link_trb_quirk(xhci)) + if (!xhci_link_trb_quirk(xhci) && !(isoc && + (xhci->quirks & XHCI_AMD_0x96_HOST))) next->link.control &= cpu_to_le32(~TRB_CHAIN); else next->link.control |= cpu_to_le32(TRB_CHAIN); @@ -2505,6 +2509,7 @@ static int prepare_transfer(struct xhci_hcd *xhci, unsigned int num_trbs, struct urb *urb, unsigned int td_index, + bool isoc, gfp_t mem_flags) { int ret; @@ -2522,7 +2527,7 @@ static int prepare_transfer(struct xhci_hcd *xhci, ret = prepare_ring(xhci, ep_ring, le32_to_cpu(ep_ctx->ep_info) & EP_STATE_MASK, - num_trbs, mem_flags); + num_trbs, isoc, mem_flags); if (ret) return ret; @@ -2711,7 +2716,7 @@ static u32 xhci_v1_0_td_remainder(int running_total, int trb_buff_len, * running_total. */ packets_transferred = (running_total + trb_buff_len) / - le16_to_cpu(urb->ep->desc.wMaxPacketSize); + usb_endpoint_maxp(&urb->ep->desc); return xhci_td_remainder(total_packet_count - packets_transferred); } @@ -2741,11 +2746,11 @@ static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags, num_trbs = count_sg_trbs_needed(xhci, urb); num_sgs = urb->num_sgs; total_packet_count = roundup(urb->transfer_buffer_length, - le16_to_cpu(urb->ep->desc.wMaxPacketSize)); + usb_endpoint_maxp(&urb->ep->desc)); trb_buff_len = prepare_transfer(xhci, xhci->devs[slot_id], ep_index, urb->stream_id, - num_trbs, urb, 0, mem_flags); + num_trbs, urb, 0, false, mem_flags); if (trb_buff_len < 0) return trb_buff_len; @@ -2840,7 +2845,7 @@ static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags, more_trbs_coming = true; else more_trbs_coming = false; - queue_trb(xhci, ep_ring, false, more_trbs_coming, + queue_trb(xhci, ep_ring, false, more_trbs_coming, false, lower_32_bits(addr), upper_32_bits(addr), length_field, @@ -2931,7 +2936,7 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags, ret = prepare_transfer(xhci, xhci->devs[slot_id], ep_index, urb->stream_id, - num_trbs, urb, 0, mem_flags); + num_trbs, urb, 0, false, mem_flags); if (ret < 0) return ret; @@ -2948,7 +2953,7 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags, running_total = 0; total_packet_count = roundup(urb->transfer_buffer_length, - le16_to_cpu(urb->ep->desc.wMaxPacketSize)); + usb_endpoint_maxp(&urb->ep->desc)); /* How much data is in the first TRB? */ addr = (u64) urb->transfer_dma; trb_buff_len = TRB_MAX_BUFF_SIZE - @@ -3003,7 +3008,7 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags, more_trbs_coming = true; else more_trbs_coming = false; - queue_trb(xhci, ep_ring, false, more_trbs_coming, + queue_trb(xhci, ep_ring, false, more_trbs_coming, false, lower_32_bits(addr), upper_32_bits(addr), length_field, @@ -3063,7 +3068,7 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags, num_trbs++; ret = prepare_transfer(xhci, xhci->devs[slot_id], ep_index, urb->stream_id, - num_trbs, urb, 0, mem_flags); + num_trbs, urb, 0, false, mem_flags); if (ret < 0) return ret; @@ -3096,7 +3101,7 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags, } } - queue_trb(xhci, ep_ring, false, true, + queue_trb(xhci, ep_ring, false, true, false, setup->bRequestType | setup->bRequest << 8 | le16_to_cpu(setup->wValue) << 16, le16_to_cpu(setup->wIndex) | le16_to_cpu(setup->wLength) << 16, TRB_LEN(8) | TRB_INTR_TARGET(0), @@ -3116,7 +3121,7 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags, if (urb->transfer_buffer_length > 0) { if (setup->bRequestType & USB_DIR_IN) field |= TRB_DIR_IN; - queue_trb(xhci, ep_ring, false, true, + queue_trb(xhci, ep_ring, false, true, false, lower_32_bits(urb->transfer_dma), upper_32_bits(urb->transfer_dma), length_field, @@ -3132,7 +3137,7 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags, field = 0; else field = TRB_DIR_IN; - queue_trb(xhci, ep_ring, false, false, + queue_trb(xhci, ep_ring, false, false, false, 0, 0, TRB_INTR_TARGET(0), @@ -3269,7 +3274,7 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags, td_len = urb->iso_frame_desc[i].length; td_remain_len = td_len; total_packet_count = roundup(td_len, - le16_to_cpu(urb->ep->desc.wMaxPacketSize)); + usb_endpoint_maxp(&urb->ep->desc)); /* A zero-length transfer still involves at least one packet. */ if (total_packet_count == 0) total_packet_count++; @@ -3281,7 +3286,8 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags, trbs_per_td = count_isoc_trbs_needed(xhci, urb, i); ret = prepare_transfer(xhci, xhci->devs[slot_id], ep_index, - urb->stream_id, trbs_per_td, urb, i, mem_flags); + urb->stream_id, trbs_per_td, urb, i, true, + mem_flags); if (ret < 0) { if (i == 0) return ret; @@ -3351,7 +3357,7 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags, remainder | TRB_INTR_TARGET(0); - queue_trb(xhci, ep_ring, false, more_trbs_coming, + queue_trb(xhci, ep_ring, false, more_trbs_coming, true, lower_32_bits(addr), upper_32_bits(addr), length_field, @@ -3433,7 +3439,7 @@ int xhci_queue_isoc_tx_prepare(struct xhci_hcd *xhci, gfp_t mem_flags, * Do not insert any td of the urb to the ring if the check failed. */ ret = prepare_ring(xhci, ep_ring, le32_to_cpu(ep_ctx->ep_info) & EP_STATE_MASK, - num_trbs, mem_flags); + num_trbs, true, mem_flags); if (ret) return ret; @@ -3492,7 +3498,7 @@ static int queue_command(struct xhci_hcd *xhci, u32 field1, u32 field2, reserved_trbs++; ret = prepare_ring(xhci, xhci->cmd_ring, EP_STATE_RUNNING, - reserved_trbs, GFP_ATOMIC); + reserved_trbs, false, GFP_ATOMIC); if (ret < 0) { xhci_err(xhci, "ERR: No room for command on command ring\n"); if (command_must_succeed) @@ -3500,8 +3506,8 @@ static int queue_command(struct xhci_hcd *xhci, u32 field1, u32 field2, "unfailable commands failed.\n"); return ret; } - queue_trb(xhci, xhci->cmd_ring, false, false, field1, field2, field3, - field4 | xhci->cmd_ring->cycle_state); + queue_trb(xhci, xhci->cmd_ring, false, false, false, field1, field2, + field3, field4 | xhci->cmd_ring->cycle_state); return 0; } diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 3a0f695138f4..1ff95a0df576 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -175,28 +175,19 @@ int xhci_reset(struct xhci_hcd *xhci) return handshake(xhci, &xhci->op_regs->status, STS_CNR, 0, 250 * 1000); } -/* - * Free IRQs - * free all IRQs request - */ -static void xhci_free_irq(struct xhci_hcd *xhci) +#ifdef CONFIG_PCI +static int xhci_free_msi(struct xhci_hcd *xhci) { int i; - struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller); - /* return if using legacy interrupt */ - if (xhci_to_hcd(xhci)->irq >= 0) - return; - - if (xhci->msix_entries) { - for (i = 0; i < xhci->msix_count; i++) - if (xhci->msix_entries[i].vector) - free_irq(xhci->msix_entries[i].vector, - xhci_to_hcd(xhci)); - } else if (pdev->irq >= 0) - free_irq(pdev->irq, xhci_to_hcd(xhci)); + if (!xhci->msix_entries) + return -EINVAL; - return; + for (i = 0; i < xhci->msix_count; i++) + if (xhci->msix_entries[i].vector) + free_irq(xhci->msix_entries[i].vector, + xhci_to_hcd(xhci)); + return 0; } /* @@ -224,6 +215,28 @@ static int xhci_setup_msi(struct xhci_hcd *xhci) } /* + * Free IRQs + * free all IRQs request + */ +static void xhci_free_irq(struct xhci_hcd *xhci) +{ + struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller); + int ret; + + /* return if using legacy interrupt */ + if (xhci_to_hcd(xhci)->irq >= 0) + return; + + ret = xhci_free_msi(xhci); + if (!ret) + return; + if (pdev->irq >= 0) + free_irq(pdev->irq, xhci_to_hcd(xhci)); + + return; +} + +/* * Set up MSI-X */ static int xhci_setup_msix(struct xhci_hcd *xhci) @@ -302,6 +315,72 @@ static void xhci_cleanup_msix(struct xhci_hcd *xhci) return; } +static void xhci_msix_sync_irqs(struct xhci_hcd *xhci) +{ + int i; + + if (xhci->msix_entries) { + for (i = 0; i < xhci->msix_count; i++) + synchronize_irq(xhci->msix_entries[i].vector); + } +} + +static int xhci_try_enable_msi(struct usb_hcd *hcd) +{ + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller); + int ret; + + /* + * Some Fresco Logic host controllers advertise MSI, but fail to + * generate interrupts. Don't even try to enable MSI. + */ + if (xhci->quirks & XHCI_BROKEN_MSI) + return 0; + + /* unregister the legacy interrupt */ + if (hcd->irq) + free_irq(hcd->irq, hcd); + hcd->irq = -1; + + ret = xhci_setup_msix(xhci); + if (ret) + /* fall back to msi*/ + ret = xhci_setup_msi(xhci); + + if (!ret) + /* hcd->irq is -1, we have MSI */ + return 0; + + /* fall back to legacy interrupt*/ + ret = request_irq(pdev->irq, &usb_hcd_irq, IRQF_SHARED, + hcd->irq_descr, hcd); + if (ret) { + xhci_err(xhci, "request interrupt %d failed\n", + pdev->irq); + return ret; + } + hcd->irq = pdev->irq; + return 0; +} + +#else + +static int xhci_try_enable_msi(struct usb_hcd *hcd) +{ + return 0; +} + +static void xhci_cleanup_msix(struct xhci_hcd *xhci) +{ +} + +static void xhci_msix_sync_irqs(struct xhci_hcd *xhci) +{ +} + +#endif + /* * Initialize memory for HCD and xHC (one-time init). * @@ -316,7 +395,7 @@ int xhci_init(struct usb_hcd *hcd) xhci_dbg(xhci, "xhci_init\n"); spin_lock_init(&xhci->lock); - if (link_quirk) { + if (xhci->hci_version == 0x95 && link_quirk) { xhci_dbg(xhci, "QUIRK: Not clearing Link TRB chain bits.\n"); xhci->quirks |= XHCI_LINK_TRB_QUIRK; } else { @@ -413,9 +492,8 @@ int xhci_run(struct usb_hcd *hcd) { u32 temp; u64 temp_64; - u32 ret; + int ret; struct xhci_hcd *xhci = hcd_to_xhci(hcd); - struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller); /* Start the xHCI host controller running only after the USB 2.0 roothub * is setup. @@ -426,34 +504,10 @@ int xhci_run(struct usb_hcd *hcd) return xhci_run_finished(xhci); xhci_dbg(xhci, "xhci_run\n"); - /* unregister the legacy interrupt */ - if (hcd->irq) - free_irq(hcd->irq, hcd); - hcd->irq = -1; - - /* Some Fresco Logic host controllers advertise MSI, but fail to - * generate interrupts. Don't even try to enable MSI. - */ - if (xhci->quirks & XHCI_BROKEN_MSI) - goto legacy_irq; - ret = xhci_setup_msix(xhci); + ret = xhci_try_enable_msi(hcd); if (ret) - /* fall back to msi*/ - ret = xhci_setup_msi(xhci); - - if (ret) { -legacy_irq: - /* fall back to legacy interrupt*/ - ret = request_irq(pdev->irq, &usb_hcd_irq, IRQF_SHARED, - hcd->irq_descr, hcd); - if (ret) { - xhci_err(xhci, "request interrupt %d failed\n", - pdev->irq); - return ret; - } - hcd->irq = pdev->irq; - } + return ret; #ifdef CONFIG_USB_XHCI_HCD_DEBUGGING init_timer(&xhci->event_ring_timer); @@ -694,7 +748,6 @@ int xhci_suspend(struct xhci_hcd *xhci) int rc = 0; struct usb_hcd *hcd = xhci_to_hcd(xhci); u32 command; - int i; spin_lock_irq(&xhci->lock); clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); @@ -730,10 +783,7 @@ int xhci_suspend(struct xhci_hcd *xhci) /* step 5: remove core well power */ /* synchronize irq when using MSI-X */ - if (xhci->msix_entries) { - for (i = 0; i < xhci->msix_count; i++) - synchronize_irq(xhci->msix_entries[i].vector); - } + xhci_msix_sync_irqs(xhci); return rc; } @@ -945,8 +995,7 @@ static int xhci_check_args(struct usb_hcd *hcd, struct usb_device *udev, return -ENODEV; if (check_virt_dev) { - if (!udev->slot_id || !xhci->devs - || !xhci->devs[udev->slot_id]) { + if (!udev->slot_id || !xhci->devs[udev->slot_id]) { printk(KERN_DEBUG "xHCI %s called with unaddressed " "device\n", func); return -EINVAL; @@ -987,7 +1036,7 @@ static int xhci_check_maxpacket(struct xhci_hcd *xhci, unsigned int slot_id, out_ctx = xhci->devs[slot_id]->out_ctx; ep_ctx = xhci_get_ep_ctx(xhci, out_ctx, ep_index); hw_max_packet_size = MAX_PACKET_DECODED(le32_to_cpu(ep_ctx->ep_info2)); - max_packet_size = le16_to_cpu(urb->dev->ep0.desc.wMaxPacketSize); + max_packet_size = usb_endpoint_maxp(&urb->dev->ep0.desc); if (hw_max_packet_size != max_packet_size) { xhci_dbg(xhci, "Max Packet Size for ep 0 changed.\n"); xhci_dbg(xhci, "Max packet size in usb_device = %d\n", @@ -1035,6 +1084,7 @@ static int xhci_check_maxpacket(struct xhci_hcd *xhci, unsigned int slot_id, int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags) { struct xhci_hcd *xhci = hcd_to_xhci(hcd); + struct xhci_td *buffer; unsigned long flags; int ret = 0; unsigned int slot_id, ep_index; @@ -1065,13 +1115,15 @@ int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags) if (!urb_priv) return -ENOMEM; + buffer = kzalloc(size * sizeof(struct xhci_td), mem_flags); + if (!buffer) { + kfree(urb_priv); + return -ENOMEM; + } + for (i = 0; i < size; i++) { - urb_priv->td[i] = kzalloc(sizeof(struct xhci_td), mem_flags); - if (!urb_priv->td[i]) { - urb_priv->length = i; - xhci_urb_free_priv(xhci, urb_priv); - return -ENOMEM; - } + urb_priv->td[i] = buffer; + buffer++; } urb_priv->length = size; @@ -1747,6 +1799,564 @@ static void xhci_finish_resource_reservation(struct xhci_hcd *xhci, xhci->num_active_eps); } +unsigned int xhci_get_block_size(struct usb_device *udev) +{ + switch (udev->speed) { + case USB_SPEED_LOW: + case USB_SPEED_FULL: + return FS_BLOCK; + case USB_SPEED_HIGH: + return HS_BLOCK; + case USB_SPEED_SUPER: + return SS_BLOCK; + case USB_SPEED_UNKNOWN: + case USB_SPEED_WIRELESS: + default: + /* Should never happen */ + return 1; + } +} + +unsigned int xhci_get_largest_overhead(struct xhci_interval_bw *interval_bw) +{ + if (interval_bw->overhead[LS_OVERHEAD_TYPE]) + return LS_OVERHEAD; + if (interval_bw->overhead[FS_OVERHEAD_TYPE]) + return FS_OVERHEAD; + return HS_OVERHEAD; +} + +/* If we are changing a LS/FS device under a HS hub, + * make sure (if we are activating a new TT) that the HS bus has enough + * bandwidth for this new TT. + */ +static int xhci_check_tt_bw_table(struct xhci_hcd *xhci, + struct xhci_virt_device *virt_dev, + int old_active_eps) +{ + struct xhci_interval_bw_table *bw_table; + struct xhci_tt_bw_info *tt_info; + + /* Find the bandwidth table for the root port this TT is attached to. */ + bw_table = &xhci->rh_bw[virt_dev->real_port - 1].bw_table; + tt_info = virt_dev->tt_info; + /* If this TT already had active endpoints, the bandwidth for this TT + * has already been added. Removing all periodic endpoints (and thus + * making the TT enactive) will only decrease the bandwidth used. + */ + if (old_active_eps) + return 0; + if (old_active_eps == 0 && tt_info->active_eps != 0) { + if (bw_table->bw_used + TT_HS_OVERHEAD > HS_BW_LIMIT) + return -ENOMEM; + return 0; + } + /* Not sure why we would have no new active endpoints... + * + * Maybe because of an Evaluate Context change for a hub update or a + * control endpoint 0 max packet size change? + * FIXME: skip the bandwidth calculation in that case. + */ + return 0; +} + +static int xhci_check_ss_bw(struct xhci_hcd *xhci, + struct xhci_virt_device *virt_dev) +{ + unsigned int bw_reserved; + + bw_reserved = DIV_ROUND_UP(SS_BW_RESERVED*SS_BW_LIMIT_IN, 100); + if (virt_dev->bw_table->ss_bw_in > (SS_BW_LIMIT_IN - bw_reserved)) + return -ENOMEM; + + bw_reserved = DIV_ROUND_UP(SS_BW_RESERVED*SS_BW_LIMIT_OUT, 100); + if (virt_dev->bw_table->ss_bw_out > (SS_BW_LIMIT_OUT - bw_reserved)) + return -ENOMEM; + + return 0; +} + +/* + * This algorithm is a very conservative estimate of the worst-case scheduling + * scenario for any one interval. The hardware dynamically schedules the + * packets, so we can't tell which microframe could be the limiting factor in + * the bandwidth scheduling. This only takes into account periodic endpoints. + * + * Obviously, we can't solve an NP complete problem to find the minimum worst + * case scenario. Instead, we come up with an estimate that is no less than + * the worst case bandwidth used for any one microframe, but may be an + * over-estimate. + * + * We walk the requirements for each endpoint by interval, starting with the + * smallest interval, and place packets in the schedule where there is only one + * possible way to schedule packets for that interval. In order to simplify + * this algorithm, we record the largest max packet size for each interval, and + * assume all packets will be that size. + * + * For interval 0, we obviously must schedule all packets for each interval. + * The bandwidth for interval 0 is just the amount of data to be transmitted + * (the sum of all max ESIT payload sizes, plus any overhead per packet times + * the number of packets). + * + * For interval 1, we have two possible microframes to schedule those packets + * in. For this algorithm, if we can schedule the same number of packets for + * each possible scheduling opportunity (each microframe), we will do so. The + * remaining number of packets will be saved to be transmitted in the gaps in + * the next interval's scheduling sequence. + * + * As we move those remaining packets to be scheduled with interval 2 packets, + * we have to double the number of remaining packets to transmit. This is + * because the intervals are actually powers of 2, and we would be transmitting + * the previous interval's packets twice in this interval. We also have to be + * sure that when we look at the largest max packet size for this interval, we + * also look at the largest max packet size for the remaining packets and take + * the greater of the two. + * + * The algorithm continues to evenly distribute packets in each scheduling + * opportunity, and push the remaining packets out, until we get to the last + * interval. Then those packets and their associated overhead are just added + * to the bandwidth used. + */ +static int xhci_check_bw_table(struct xhci_hcd *xhci, + struct xhci_virt_device *virt_dev, + int old_active_eps) +{ + unsigned int bw_reserved; + unsigned int max_bandwidth; + unsigned int bw_used; + unsigned int block_size; + struct xhci_interval_bw_table *bw_table; + unsigned int packet_size = 0; + unsigned int overhead = 0; + unsigned int packets_transmitted = 0; + unsigned int packets_remaining = 0; + unsigned int i; + + if (virt_dev->udev->speed == USB_SPEED_SUPER) + return xhci_check_ss_bw(xhci, virt_dev); + + if (virt_dev->udev->speed == USB_SPEED_HIGH) { + max_bandwidth = HS_BW_LIMIT; + /* Convert percent of bus BW reserved to blocks reserved */ + bw_reserved = DIV_ROUND_UP(HS_BW_RESERVED * max_bandwidth, 100); + } else { + max_bandwidth = FS_BW_LIMIT; + bw_reserved = DIV_ROUND_UP(FS_BW_RESERVED * max_bandwidth, 100); + } + + bw_table = virt_dev->bw_table; + /* We need to translate the max packet size and max ESIT payloads into + * the units the hardware uses. + */ + block_size = xhci_get_block_size(virt_dev->udev); + + /* If we are manipulating a LS/FS device under a HS hub, double check + * that the HS bus has enough bandwidth if we are activing a new TT. + */ + if (virt_dev->tt_info) { + xhci_dbg(xhci, "Recalculating BW for rootport %u\n", + virt_dev->real_port); + if (xhci_check_tt_bw_table(xhci, virt_dev, old_active_eps)) { + xhci_warn(xhci, "Not enough bandwidth on HS bus for " + "newly activated TT.\n"); + return -ENOMEM; + } + xhci_dbg(xhci, "Recalculating BW for TT slot %u port %u\n", + virt_dev->tt_info->slot_id, + virt_dev->tt_info->ttport); + } else { + xhci_dbg(xhci, "Recalculating BW for rootport %u\n", + virt_dev->real_port); + } + + /* Add in how much bandwidth will be used for interval zero, or the + * rounded max ESIT payload + number of packets * largest overhead. + */ + bw_used = DIV_ROUND_UP(bw_table->interval0_esit_payload, block_size) + + bw_table->interval_bw[0].num_packets * + xhci_get_largest_overhead(&bw_table->interval_bw[0]); + + for (i = 1; i < XHCI_MAX_INTERVAL; i++) { + unsigned int bw_added; + unsigned int largest_mps; + unsigned int interval_overhead; + + /* + * How many packets could we transmit in this interval? + * If packets didn't fit in the previous interval, we will need + * to transmit that many packets twice within this interval. + */ + packets_remaining = 2 * packets_remaining + + bw_table->interval_bw[i].num_packets; + + /* Find the largest max packet size of this or the previous + * interval. + */ + if (list_empty(&bw_table->interval_bw[i].endpoints)) + largest_mps = 0; + else { + struct xhci_virt_ep *virt_ep; + struct list_head *ep_entry; + + ep_entry = bw_table->interval_bw[i].endpoints.next; + virt_ep = list_entry(ep_entry, + struct xhci_virt_ep, bw_endpoint_list); + /* Convert to blocks, rounding up */ + largest_mps = DIV_ROUND_UP( + virt_ep->bw_info.max_packet_size, + block_size); + } + if (largest_mps > packet_size) + packet_size = largest_mps; + + /* Use the larger overhead of this or the previous interval. */ + interval_overhead = xhci_get_largest_overhead( + &bw_table->interval_bw[i]); + if (interval_overhead > overhead) + overhead = interval_overhead; + + /* How many packets can we evenly distribute across + * (1 << (i + 1)) possible scheduling opportunities? + */ + packets_transmitted = packets_remaining >> (i + 1); + + /* Add in the bandwidth used for those scheduled packets */ + bw_added = packets_transmitted * (overhead + packet_size); + + /* How many packets do we have remaining to transmit? */ + packets_remaining = packets_remaining % (1 << (i + 1)); + + /* What largest max packet size should those packets have? */ + /* If we've transmitted all packets, don't carry over the + * largest packet size. + */ + if (packets_remaining == 0) { + packet_size = 0; + overhead = 0; + } else if (packets_transmitted > 0) { + /* Otherwise if we do have remaining packets, and we've + * scheduled some packets in this interval, take the + * largest max packet size from endpoints with this + * interval. + */ + packet_size = largest_mps; + overhead = interval_overhead; + } + /* Otherwise carry over packet_size and overhead from the last + * time we had a remainder. + */ + bw_used += bw_added; + if (bw_used > max_bandwidth) { + xhci_warn(xhci, "Not enough bandwidth. " + "Proposed: %u, Max: %u\n", + bw_used, max_bandwidth); + return -ENOMEM; + } + } + /* + * Ok, we know we have some packets left over after even-handedly + * scheduling interval 15. We don't know which microframes they will + * fit into, so we over-schedule and say they will be scheduled every + * microframe. + */ + if (packets_remaining > 0) + bw_used += overhead + packet_size; + + if (!virt_dev->tt_info && virt_dev->udev->speed == USB_SPEED_HIGH) { + unsigned int port_index = virt_dev->real_port - 1; + + /* OK, we're manipulating a HS device attached to a + * root port bandwidth domain. Include the number of active TTs + * in the bandwidth used. + */ + bw_used += TT_HS_OVERHEAD * + xhci->rh_bw[port_index].num_active_tts; + } + + xhci_dbg(xhci, "Final bandwidth: %u, Limit: %u, Reserved: %u, " + "Available: %u " "percent\n", + bw_used, max_bandwidth, bw_reserved, + (max_bandwidth - bw_used - bw_reserved) * 100 / + max_bandwidth); + + bw_used += bw_reserved; + if (bw_used > max_bandwidth) { + xhci_warn(xhci, "Not enough bandwidth. Proposed: %u, Max: %u\n", + bw_used, max_bandwidth); + return -ENOMEM; + } + + bw_table->bw_used = bw_used; + return 0; +} + +static bool xhci_is_async_ep(unsigned int ep_type) +{ + return (ep_type != ISOC_OUT_EP && ep_type != INT_OUT_EP && + ep_type != ISOC_IN_EP && + ep_type != INT_IN_EP); +} + +static bool xhci_is_sync_in_ep(unsigned int ep_type) +{ + return (ep_type == ISOC_IN_EP || ep_type != INT_IN_EP); +} + +static unsigned int xhci_get_ss_bw_consumed(struct xhci_bw_info *ep_bw) +{ + unsigned int mps = DIV_ROUND_UP(ep_bw->max_packet_size, SS_BLOCK); + + if (ep_bw->ep_interval == 0) + return SS_OVERHEAD_BURST + + (ep_bw->mult * ep_bw->num_packets * + (SS_OVERHEAD + mps)); + return DIV_ROUND_UP(ep_bw->mult * ep_bw->num_packets * + (SS_OVERHEAD + mps + SS_OVERHEAD_BURST), + 1 << ep_bw->ep_interval); + +} + +void xhci_drop_ep_from_interval_table(struct xhci_hcd *xhci, + struct xhci_bw_info *ep_bw, + struct xhci_interval_bw_table *bw_table, + struct usb_device *udev, + struct xhci_virt_ep *virt_ep, + struct xhci_tt_bw_info *tt_info) +{ + struct xhci_interval_bw *interval_bw; + int normalized_interval; + + if (xhci_is_async_ep(ep_bw->type)) + return; + + if (udev->speed == USB_SPEED_SUPER) { + if (xhci_is_sync_in_ep(ep_bw->type)) + xhci->devs[udev->slot_id]->bw_table->ss_bw_in -= + xhci_get_ss_bw_consumed(ep_bw); + else + xhci->devs[udev->slot_id]->bw_table->ss_bw_out -= + xhci_get_ss_bw_consumed(ep_bw); + return; + } + + /* SuperSpeed endpoints never get added to intervals in the table, so + * this check is only valid for HS/FS/LS devices. + */ + if (list_empty(&virt_ep->bw_endpoint_list)) + return; + /* For LS/FS devices, we need to translate the interval expressed in + * microframes to frames. + */ + if (udev->speed == USB_SPEED_HIGH) + normalized_interval = ep_bw->ep_interval; + else + normalized_interval = ep_bw->ep_interval - 3; + + if (normalized_interval == 0) + bw_table->interval0_esit_payload -= ep_bw->max_esit_payload; + interval_bw = &bw_table->interval_bw[normalized_interval]; + interval_bw->num_packets -= ep_bw->num_packets; + switch (udev->speed) { + case USB_SPEED_LOW: + interval_bw->overhead[LS_OVERHEAD_TYPE] -= 1; + break; + case USB_SPEED_FULL: + interval_bw->overhead[FS_OVERHEAD_TYPE] -= 1; + break; + case USB_SPEED_HIGH: + interval_bw->overhead[HS_OVERHEAD_TYPE] -= 1; + break; + case USB_SPEED_SUPER: + case USB_SPEED_UNKNOWN: + case USB_SPEED_WIRELESS: + /* Should never happen because only LS/FS/HS endpoints will get + * added to the endpoint list. + */ + return; + } + if (tt_info) + tt_info->active_eps -= 1; + list_del_init(&virt_ep->bw_endpoint_list); +} + +static void xhci_add_ep_to_interval_table(struct xhci_hcd *xhci, + struct xhci_bw_info *ep_bw, + struct xhci_interval_bw_table *bw_table, + struct usb_device *udev, + struct xhci_virt_ep *virt_ep, + struct xhci_tt_bw_info *tt_info) +{ + struct xhci_interval_bw *interval_bw; + struct xhci_virt_ep *smaller_ep; + int normalized_interval; + + if (xhci_is_async_ep(ep_bw->type)) + return; + + if (udev->speed == USB_SPEED_SUPER) { + if (xhci_is_sync_in_ep(ep_bw->type)) + xhci->devs[udev->slot_id]->bw_table->ss_bw_in += + xhci_get_ss_bw_consumed(ep_bw); + else + xhci->devs[udev->slot_id]->bw_table->ss_bw_out += + xhci_get_ss_bw_consumed(ep_bw); + return; + } + + /* For LS/FS devices, we need to translate the interval expressed in + * microframes to frames. + */ + if (udev->speed == USB_SPEED_HIGH) + normalized_interval = ep_bw->ep_interval; + else + normalized_interval = ep_bw->ep_interval - 3; + + if (normalized_interval == 0) + bw_table->interval0_esit_payload += ep_bw->max_esit_payload; + interval_bw = &bw_table->interval_bw[normalized_interval]; + interval_bw->num_packets += ep_bw->num_packets; + switch (udev->speed) { + case USB_SPEED_LOW: + interval_bw->overhead[LS_OVERHEAD_TYPE] += 1; + break; + case USB_SPEED_FULL: + interval_bw->overhead[FS_OVERHEAD_TYPE] += 1; + break; + case USB_SPEED_HIGH: + interval_bw->overhead[HS_OVERHEAD_TYPE] += 1; + break; + case USB_SPEED_SUPER: + case USB_SPEED_UNKNOWN: + case USB_SPEED_WIRELESS: + /* Should never happen because only LS/FS/HS endpoints will get + * added to the endpoint list. + */ + return; + } + + if (tt_info) + tt_info->active_eps += 1; + /* Insert the endpoint into the list, largest max packet size first. */ + list_for_each_entry(smaller_ep, &interval_bw->endpoints, + bw_endpoint_list) { + if (ep_bw->max_packet_size >= + smaller_ep->bw_info.max_packet_size) { + /* Add the new ep before the smaller endpoint */ + list_add_tail(&virt_ep->bw_endpoint_list, + &smaller_ep->bw_endpoint_list); + return; + } + } + /* Add the new endpoint at the end of the list. */ + list_add_tail(&virt_ep->bw_endpoint_list, + &interval_bw->endpoints); +} + +void xhci_update_tt_active_eps(struct xhci_hcd *xhci, + struct xhci_virt_device *virt_dev, + int old_active_eps) +{ + struct xhci_root_port_bw_info *rh_bw_info; + if (!virt_dev->tt_info) + return; + + rh_bw_info = &xhci->rh_bw[virt_dev->real_port - 1]; + if (old_active_eps == 0 && + virt_dev->tt_info->active_eps != 0) { + rh_bw_info->num_active_tts += 1; + rh_bw_info->bw_table.bw_used += TT_HS_OVERHEAD; + } else if (old_active_eps != 0 && + virt_dev->tt_info->active_eps == 0) { + rh_bw_info->num_active_tts -= 1; + rh_bw_info->bw_table.bw_used -= TT_HS_OVERHEAD; + } +} + +static int xhci_reserve_bandwidth(struct xhci_hcd *xhci, + struct xhci_virt_device *virt_dev, + struct xhci_container_ctx *in_ctx) +{ + struct xhci_bw_info ep_bw_info[31]; + int i; + struct xhci_input_control_ctx *ctrl_ctx; + int old_active_eps = 0; + + if (virt_dev->tt_info) + old_active_eps = virt_dev->tt_info->active_eps; + + ctrl_ctx = xhci_get_input_control_ctx(xhci, in_ctx); + + for (i = 0; i < 31; i++) { + if (!EP_IS_ADDED(ctrl_ctx, i) && !EP_IS_DROPPED(ctrl_ctx, i)) + continue; + + /* Make a copy of the BW info in case we need to revert this */ + memcpy(&ep_bw_info[i], &virt_dev->eps[i].bw_info, + sizeof(ep_bw_info[i])); + /* Drop the endpoint from the interval table if the endpoint is + * being dropped or changed. + */ + if (EP_IS_DROPPED(ctrl_ctx, i)) + xhci_drop_ep_from_interval_table(xhci, + &virt_dev->eps[i].bw_info, + virt_dev->bw_table, + virt_dev->udev, + &virt_dev->eps[i], + virt_dev->tt_info); + } + /* Overwrite the information stored in the endpoints' bw_info */ + xhci_update_bw_info(xhci, virt_dev->in_ctx, ctrl_ctx, virt_dev); + for (i = 0; i < 31; i++) { + /* Add any changed or added endpoints to the interval table */ + if (EP_IS_ADDED(ctrl_ctx, i)) + xhci_add_ep_to_interval_table(xhci, + &virt_dev->eps[i].bw_info, + virt_dev->bw_table, + virt_dev->udev, + &virt_dev->eps[i], + virt_dev->tt_info); + } + + if (!xhci_check_bw_table(xhci, virt_dev, old_active_eps)) { + /* Ok, this fits in the bandwidth we have. + * Update the number of active TTs. + */ + xhci_update_tt_active_eps(xhci, virt_dev, old_active_eps); + return 0; + } + + /* We don't have enough bandwidth for this, revert the stored info. */ + for (i = 0; i < 31; i++) { + if (!EP_IS_ADDED(ctrl_ctx, i) && !EP_IS_DROPPED(ctrl_ctx, i)) + continue; + + /* Drop the new copies of any added or changed endpoints from + * the interval table. + */ + if (EP_IS_ADDED(ctrl_ctx, i)) { + xhci_drop_ep_from_interval_table(xhci, + &virt_dev->eps[i].bw_info, + virt_dev->bw_table, + virt_dev->udev, + &virt_dev->eps[i], + virt_dev->tt_info); + } + /* Revert the endpoint back to its old information */ + memcpy(&virt_dev->eps[i].bw_info, &ep_bw_info[i], + sizeof(ep_bw_info[i])); + /* Add any changed or dropped endpoints back into the table */ + if (EP_IS_DROPPED(ctrl_ctx, i)) + xhci_add_ep_to_interval_table(xhci, + &virt_dev->eps[i].bw_info, + virt_dev->bw_table, + virt_dev->udev, + &virt_dev->eps[i], + virt_dev->tt_info); + } + return -ENOMEM; +} + + /* Issue a configure endpoint command or evaluate context command * and wait for it to finish. */ @@ -1765,17 +2375,30 @@ static int xhci_configure_endpoint(struct xhci_hcd *xhci, spin_lock_irqsave(&xhci->lock, flags); virt_dev = xhci->devs[udev->slot_id]; - if (command) { + + if (command) in_ctx = command->in_ctx; - if ((xhci->quirks & XHCI_EP_LIMIT_QUIRK) && - xhci_reserve_host_resources(xhci, in_ctx)) { - spin_unlock_irqrestore(&xhci->lock, flags); - xhci_warn(xhci, "Not enough host resources, " - "active endpoint contexts = %u\n", - xhci->num_active_eps); - return -ENOMEM; - } + else + in_ctx = virt_dev->in_ctx; + + if ((xhci->quirks & XHCI_EP_LIMIT_QUIRK) && + xhci_reserve_host_resources(xhci, in_ctx)) { + spin_unlock_irqrestore(&xhci->lock, flags); + xhci_warn(xhci, "Not enough host resources, " + "active endpoint contexts = %u\n", + xhci->num_active_eps); + return -ENOMEM; + } + if ((xhci->quirks & XHCI_SW_BW_CHECKING) && + xhci_reserve_bandwidth(xhci, virt_dev, in_ctx)) { + if ((xhci->quirks & XHCI_EP_LIMIT_QUIRK)) + xhci_free_host_resources(xhci, in_ctx); + spin_unlock_irqrestore(&xhci->lock, flags); + xhci_warn(xhci, "Not enough bandwidth\n"); + return -ENOMEM; + } + if (command) { cmd_completion = command->completion; cmd_status = &command->status; command->command_trb = xhci->cmd_ring->enqueue; @@ -1789,15 +2412,6 @@ static int xhci_configure_endpoint(struct xhci_hcd *xhci, list_add_tail(&command->cmd_list, &virt_dev->cmd_list); } else { - in_ctx = virt_dev->in_ctx; - if ((xhci->quirks & XHCI_EP_LIMIT_QUIRK) && - xhci_reserve_host_resources(xhci, in_ctx)) { - spin_unlock_irqrestore(&xhci->lock, flags); - xhci_warn(xhci, "Not enough host resources, " - "active endpoint contexts = %u\n", - xhci->num_active_eps); - return -ENOMEM; - } cmd_completion = &virt_dev->cmd_completion; cmd_status = &virt_dev->cmd_status; } @@ -1888,6 +2502,12 @@ int xhci_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev) ctrl_ctx->add_flags |= cpu_to_le32(SLOT_FLAG); ctrl_ctx->add_flags &= cpu_to_le32(~EP0_FLAG); ctrl_ctx->drop_flags &= cpu_to_le32(~(SLOT_FLAG | EP0_FLAG)); + + /* Don't issue the command if there's no endpoints to update. */ + if (ctrl_ctx->add_flags == cpu_to_le32(SLOT_FLAG) && + ctrl_ctx->drop_flags == 0) + return 0; + xhci_dbg(xhci, "New Input Control Context:\n"); slot_ctx = xhci_get_slot_ctx(xhci, virt_dev->in_ctx); xhci_dbg_ctx(xhci, virt_dev->in_ctx, @@ -2525,6 +3145,7 @@ int xhci_discover_or_reset_device(struct usb_hcd *hcd, struct usb_device *udev) int timeleft; int last_freed_endpoint; struct xhci_slot_ctx *slot_ctx; + int old_active_eps = 0; ret = xhci_check_args(hcd, udev, NULL, 0, false, __func__); if (ret <= 0) @@ -2666,7 +3287,18 @@ int xhci_discover_or_reset_device(struct usb_hcd *hcd, struct usb_device *udev) xhci_free_or_cache_endpoint_ring(xhci, virt_dev, i); last_freed_endpoint = i; } - } + if (!list_empty(&virt_dev->eps[i].bw_endpoint_list)) + xhci_drop_ep_from_interval_table(xhci, + &virt_dev->eps[i].bw_info, + virt_dev->bw_table, + udev, + &virt_dev->eps[i], + virt_dev->tt_info); + xhci_clear_endpoint_bw_info(&virt_dev->eps[i].bw_info); + } + /* If necessary, update the number of active TTs on this root port */ + xhci_update_tt_active_eps(xhci, virt_dev, old_active_eps); + xhci_dbg(xhci, "Output context after successful reset device cmd:\n"); xhci_dbg_ctx(xhci, virt_dev->out_ctx, last_freed_endpoint); ret = 0; @@ -2704,6 +3336,11 @@ void xhci_free_dev(struct usb_hcd *hcd, struct usb_device *udev) del_timer_sync(&virt_dev->eps[i].stop_cmd_timer); } + if (udev->usb2_hw_lpm_enabled) { + xhci_set_usb2_hardware_lpm(hcd, udev, 0); + udev->usb2_hw_lpm_enabled = 0; + } + 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); @@ -2889,7 +3526,7 @@ int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev) * command on a timeout. */ if (timeleft <= 0) { - xhci_warn(xhci, "%s while waiting for a slot\n", + xhci_warn(xhci, "%s while waiting for address device command\n", timeleft == 0 ? "Timeout" : "Signal"); /* FIXME cancel the address device command */ return -ETIME; @@ -2957,6 +3594,254 @@ int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev) return 0; } +#ifdef CONFIG_USB_SUSPEND + +/* BESL to HIRD Encoding array for USB2 LPM */ +static int xhci_besl_encoding[16] = {125, 150, 200, 300, 400, 500, 1000, 2000, + 3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000}; + +/* Calculate HIRD/BESL for USB2 PORTPMSC*/ +static int xhci_calculate_hird_besl(int u2del, bool use_besl) +{ + int hird; + + if (use_besl) { + for (hird = 0; hird < 16; hird++) { + if (xhci_besl_encoding[hird] >= u2del) + break; + } + } else { + if (u2del <= 50) + hird = 0; + else + hird = (u2del - 51) / 75 + 1; + + if (hird > 15) + hird = 15; + } + + return hird; +} + +static int xhci_usb2_software_lpm_test(struct usb_hcd *hcd, + struct usb_device *udev) +{ + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + struct dev_info *dev_info; + __le32 __iomem **port_array; + __le32 __iomem *addr, *pm_addr; + u32 temp, dev_id; + unsigned int port_num; + unsigned long flags; + int u2del, hird; + int ret; + + if (hcd->speed == HCD_USB3 || !xhci->sw_lpm_support || + !udev->lpm_capable) + return -EINVAL; + + /* we only support lpm for non-hub device connected to root hub yet */ + if (!udev->parent || udev->parent->parent || + udev->descriptor.bDeviceClass == USB_CLASS_HUB) + return -EINVAL; + + spin_lock_irqsave(&xhci->lock, flags); + + /* Look for devices in lpm_failed_devs list */ + dev_id = le16_to_cpu(udev->descriptor.idVendor) << 16 | + le16_to_cpu(udev->descriptor.idProduct); + list_for_each_entry(dev_info, &xhci->lpm_failed_devs, list) { + if (dev_info->dev_id == dev_id) { + ret = -EINVAL; + goto finish; + } + } + + port_array = xhci->usb2_ports; + port_num = udev->portnum - 1; + + if (port_num > HCS_MAX_PORTS(xhci->hcs_params1)) { + xhci_dbg(xhci, "invalid port number %d\n", udev->portnum); + ret = -EINVAL; + goto finish; + } + + /* + * Test USB 2.0 software LPM. + * FIXME: some xHCI 1.0 hosts may implement a new register to set up + * hardware-controlled USB 2.0 LPM. See section 5.4.11 and 4.23.5.1.1.1 + * in the June 2011 errata release. + */ + xhci_dbg(xhci, "test port %d software LPM\n", port_num); + /* + * Set L1 Device Slot and HIRD/BESL. + * Check device's USB 2.0 extension descriptor to determine whether + * HIRD or BESL shoule be used. See USB2.0 LPM errata. + */ + pm_addr = port_array[port_num] + 1; + u2del = HCS_U2_LATENCY(xhci->hcs_params3); + if (le32_to_cpu(udev->bos->ext_cap->bmAttributes) & (1 << 2)) + hird = xhci_calculate_hird_besl(u2del, 1); + else + hird = xhci_calculate_hird_besl(u2del, 0); + + temp = PORT_L1DS(udev->slot_id) | PORT_HIRD(hird); + xhci_writel(xhci, temp, pm_addr); + + /* Set port link state to U2(L1) */ + addr = port_array[port_num]; + xhci_set_link_state(xhci, port_array, port_num, XDEV_U2); + + /* wait for ACK */ + spin_unlock_irqrestore(&xhci->lock, flags); + msleep(10); + spin_lock_irqsave(&xhci->lock, flags); + + /* Check L1 Status */ + ret = handshake(xhci, pm_addr, PORT_L1S_MASK, PORT_L1S_SUCCESS, 125); + if (ret != -ETIMEDOUT) { + /* enter L1 successfully */ + temp = xhci_readl(xhci, addr); + xhci_dbg(xhci, "port %d entered L1 state, port status 0x%x\n", + port_num, temp); + ret = 0; + } else { + temp = xhci_readl(xhci, pm_addr); + xhci_dbg(xhci, "port %d software lpm failed, L1 status %d\n", + port_num, temp & PORT_L1S_MASK); + ret = -EINVAL; + } + + /* Resume the port */ + xhci_set_link_state(xhci, port_array, port_num, XDEV_U0); + + spin_unlock_irqrestore(&xhci->lock, flags); + msleep(10); + spin_lock_irqsave(&xhci->lock, flags); + + /* Clear PLC */ + xhci_test_and_clear_bit(xhci, port_array, port_num, PORT_PLC); + + /* Check PORTSC to make sure the device is in the right state */ + if (!ret) { + temp = xhci_readl(xhci, addr); + xhci_dbg(xhci, "resumed port %d status 0x%x\n", port_num, temp); + if (!(temp & PORT_CONNECT) || !(temp & PORT_PE) || + (temp & PORT_PLS_MASK) != XDEV_U0) { + xhci_dbg(xhci, "port L1 resume fail\n"); + ret = -EINVAL; + } + } + + if (ret) { + /* Insert dev to lpm_failed_devs list */ + xhci_warn(xhci, "device LPM test failed, may disconnect and " + "re-enumerate\n"); + dev_info = kzalloc(sizeof(struct dev_info), GFP_ATOMIC); + if (!dev_info) { + ret = -ENOMEM; + goto finish; + } + dev_info->dev_id = dev_id; + INIT_LIST_HEAD(&dev_info->list); + list_add(&dev_info->list, &xhci->lpm_failed_devs); + } else { + xhci_ring_device(xhci, udev->slot_id); + } + +finish: + spin_unlock_irqrestore(&xhci->lock, flags); + return ret; +} + +int xhci_set_usb2_hardware_lpm(struct usb_hcd *hcd, + struct usb_device *udev, int enable) +{ + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + __le32 __iomem **port_array; + __le32 __iomem *pm_addr; + u32 temp; + unsigned int port_num; + unsigned long flags; + int u2del, hird; + + if (hcd->speed == HCD_USB3 || !xhci->hw_lpm_support || + !udev->lpm_capable) + return -EPERM; + + if (!udev->parent || udev->parent->parent || + udev->descriptor.bDeviceClass == USB_CLASS_HUB) + return -EPERM; + + if (udev->usb2_hw_lpm_capable != 1) + return -EPERM; + + spin_lock_irqsave(&xhci->lock, flags); + + port_array = xhci->usb2_ports; + port_num = udev->portnum - 1; + pm_addr = port_array[port_num] + 1; + temp = xhci_readl(xhci, pm_addr); + + xhci_dbg(xhci, "%s port %d USB2 hardware LPM\n", + enable ? "enable" : "disable", port_num); + + u2del = HCS_U2_LATENCY(xhci->hcs_params3); + if (le32_to_cpu(udev->bos->ext_cap->bmAttributes) & (1 << 2)) + hird = xhci_calculate_hird_besl(u2del, 1); + else + hird = xhci_calculate_hird_besl(u2del, 0); + + if (enable) { + temp &= ~PORT_HIRD_MASK; + temp |= PORT_HIRD(hird) | PORT_RWE; + xhci_writel(xhci, temp, pm_addr); + temp = xhci_readl(xhci, pm_addr); + temp |= PORT_HLE; + xhci_writel(xhci, temp, pm_addr); + } else { + temp &= ~(PORT_HLE | PORT_RWE | PORT_HIRD_MASK); + xhci_writel(xhci, temp, pm_addr); + } + + spin_unlock_irqrestore(&xhci->lock, flags); + return 0; +} + +int xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev) +{ + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + int ret; + + ret = xhci_usb2_software_lpm_test(hcd, udev); + if (!ret) { + xhci_dbg(xhci, "software LPM test succeed\n"); + if (xhci->hw_lpm_support == 1) { + udev->usb2_hw_lpm_capable = 1; + ret = xhci_set_usb2_hardware_lpm(hcd, udev, 1); + if (!ret) + udev->usb2_hw_lpm_enabled = 1; + } + } + + return 0; +} + +#else + +int xhci_set_usb2_hardware_lpm(struct usb_hcd *hcd, + struct usb_device *udev, int enable) +{ + return 0; +} + +int xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev) +{ + return 0; +} + +#endif /* CONFIG_USB_SUSPEND */ + /* Once a hub descriptor is fetched for a device, we need to update the xHC's * internal data structures for the device. */ @@ -2988,6 +3873,14 @@ int xhci_update_hub_device(struct usb_hcd *hcd, struct usb_device *hdev, } spin_lock_irqsave(&xhci->lock, flags); + if (hdev->speed == USB_SPEED_HIGH && + xhci_alloc_tt_info(xhci, vdev, hdev, tt, GFP_ATOMIC)) { + xhci_dbg(xhci, "Could not allocate xHCI TT structure.\n"); + xhci_free_command(xhci, config_cmd); + spin_unlock_irqrestore(&xhci->lock, flags); + return -ENOMEM; + } + xhci_slot_copy(xhci, config_cmd->in_ctx, vdev->out_ctx); ctrl_ctx = xhci_get_input_control_ctx(xhci, config_cmd->in_ctx); ctrl_ctx->add_flags |= cpu_to_le32(SLOT_FLAG); @@ -3051,22 +3944,108 @@ int xhci_get_frame(struct usb_hcd *hcd) return xhci_readl(xhci, &xhci->run_regs->microframe_index) >> 3; } +int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks) +{ + struct xhci_hcd *xhci; + struct device *dev = hcd->self.controller; + int retval; + u32 temp; + + hcd->self.sg_tablesize = TRBS_PER_SEGMENT - 2; + + if (usb_hcd_is_primary_hcd(hcd)) { + xhci = kzalloc(sizeof(struct xhci_hcd), GFP_KERNEL); + if (!xhci) + return -ENOMEM; + *((struct xhci_hcd **) hcd->hcd_priv) = xhci; + xhci->main_hcd = hcd; + /* Mark the first roothub as being USB 2.0. + * The xHCI driver will register the USB 3.0 roothub. + */ + hcd->speed = HCD_USB2; + hcd->self.root_hub->speed = USB_SPEED_HIGH; + /* + * USB 2.0 roothub under xHCI has an integrated TT, + * (rate matching hub) as opposed to having an OHCI/UHCI + * companion controller. + */ + hcd->has_tt = 1; + } else { + /* xHCI private pointer was set in xhci_pci_probe for the second + * registered roothub. + */ + xhci = hcd_to_xhci(hcd); + temp = xhci_readl(xhci, &xhci->cap_regs->hcc_params); + if (HCC_64BIT_ADDR(temp)) { + xhci_dbg(xhci, "Enabling 64-bit DMA addresses.\n"); + dma_set_mask(hcd->self.controller, DMA_BIT_MASK(64)); + } else { + dma_set_mask(hcd->self.controller, DMA_BIT_MASK(32)); + } + return 0; + } + + xhci->cap_regs = hcd->regs; + xhci->op_regs = hcd->regs + + HC_LENGTH(xhci_readl(xhci, &xhci->cap_regs->hc_capbase)); + xhci->run_regs = hcd->regs + + (xhci_readl(xhci, &xhci->cap_regs->run_regs_off) & RTSOFF_MASK); + /* Cache read-only capability registers */ + xhci->hcs_params1 = xhci_readl(xhci, &xhci->cap_regs->hcs_params1); + xhci->hcs_params2 = xhci_readl(xhci, &xhci->cap_regs->hcs_params2); + xhci->hcs_params3 = xhci_readl(xhci, &xhci->cap_regs->hcs_params3); + xhci->hcc_params = xhci_readl(xhci, &xhci->cap_regs->hc_capbase); + xhci->hci_version = HC_VERSION(xhci->hcc_params); + xhci->hcc_params = xhci_readl(xhci, &xhci->cap_regs->hcc_params); + xhci_print_registers(xhci); + + get_quirks(dev, xhci); + + /* Make sure the HC is halted. */ + retval = xhci_halt(xhci); + if (retval) + goto error; + + xhci_dbg(xhci, "Resetting HCD\n"); + /* Reset the internal HC memory state and registers. */ + retval = xhci_reset(xhci); + if (retval) + goto error; + xhci_dbg(xhci, "Reset complete\n"); + + temp = xhci_readl(xhci, &xhci->cap_regs->hcc_params); + if (HCC_64BIT_ADDR(temp)) { + xhci_dbg(xhci, "Enabling 64-bit DMA addresses.\n"); + dma_set_mask(hcd->self.controller, DMA_BIT_MASK(64)); + } else { + dma_set_mask(hcd->self.controller, DMA_BIT_MASK(32)); + } + + xhci_dbg(xhci, "Calling HCD init\n"); + /* Initialize HCD and host controller data structures. */ + retval = xhci_init(hcd); + if (retval) + goto error; + xhci_dbg(xhci, "Called HCD init\n"); + return 0; +error: + kfree(xhci); + return retval; +} + MODULE_DESCRIPTION(DRIVER_DESC); MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_LICENSE("GPL"); static int __init xhci_hcd_init(void) { -#ifdef CONFIG_PCI - int retval = 0; + int retval; retval = xhci_register_pci(); - if (retval < 0) { printk(KERN_DEBUG "Problem registering PCI driver."); return retval; } -#endif /* * Check the compiler generated sizes of structures that must be laid * out in specific ways for hardware access. @@ -3091,8 +4070,6 @@ module_init(xhci_hcd_init); static void __exit xhci_hcd_cleanup(void) { -#ifdef CONFIG_PCI xhci_unregister_pci(); -#endif } module_exit(xhci_hcd_cleanup); diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index cae8e23308bf..3c8fbd2772ea 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -272,6 +272,7 @@ struct xhci_op_regs { */ #define PORT_PLS_MASK (0xf << 5) #define XDEV_U0 (0x0 << 5) +#define XDEV_U2 (0x2 << 5) #define XDEV_U3 (0x3 << 5) #define XDEV_RESUME (0xf << 5) /* true: port has power (see HCC_PPC) */ @@ -362,7 +363,13 @@ struct xhci_op_regs { /* Bits 24:31 for port testing */ /* USB2 Protocol PORTSPMSC */ -#define PORT_RWE (1 << 0x3) +#define PORT_L1S_MASK 7 +#define PORT_L1S_SUCCESS 1 +#define PORT_RWE (1 << 3) +#define PORT_HIRD(p) (((p) & 0xf) << 4) +#define PORT_HIRD_MASK (0xf << 4) +#define PORT_L1DS(p) (((p) & 0xff) << 8) +#define PORT_HLE (1 << 16) /** * struct xhci_intr_reg - Interrupt Register Set @@ -611,11 +618,13 @@ struct xhci_ep_ctx { #define EP_STATE_ERROR 4 /* Mult - Max number of burtst within an interval, in EP companion desc. */ #define EP_MULT(p) (((p) & 0x3) << 8) +#define CTX_TO_EP_MULT(p) (((p) >> 8) & 0x3) /* bits 10:14 are Max Primary Streams */ /* bit 15 is Linear Stream Array */ /* Interval - period between requests to an endpoint - 125u increments. */ #define EP_INTERVAL(p) (((p) & 0xff) << 16) #define EP_INTERVAL_TO_UFRAMES(p) (1 << (((p) >> 16) & 0xff)) +#define CTX_TO_EP_INTERVAL(p) (((p) >> 16) & 0xff) #define EP_MAXPSTREAMS_MASK (0x1f << 10) #define EP_MAXPSTREAMS(p) (((p) << 10) & EP_MAXPSTREAMS_MASK) /* Endpoint is set up with a Linear Stream Array (vs. Secondary Stream Array) */ @@ -640,6 +649,7 @@ struct xhci_ep_ctx { /* bit 6 reserved */ /* bit 7 is Host Initiate Disable - for disabling stream selection */ #define MAX_BURST(p) (((p)&0xff) << 8) +#define CTX_TO_MAX_BURST(p) (((p) >> 8) & 0xff) #define MAX_PACKET(p) (((p)&0xffff) << 16) #define MAX_PACKET_MASK (0xffff << 16) #define MAX_PACKET_DECODED(p) (((p) >> 16) & 0xffff) @@ -652,6 +662,7 @@ struct xhci_ep_ctx { /* tx_info bitmasks */ #define AVG_TRB_LENGTH_FOR_EP(p) ((p) & 0xffff) #define MAX_ESIT_PAYLOAD_FOR_EP(p) (((p) & 0xffff) << 16) +#define CTX_TO_MAX_ESIT_PAYLOAD(p) (((p) >> 16) & 0xffff) /* deq bitmasks */ #define EP_CTX_CYCLE_MASK (1 << 0) @@ -670,6 +681,11 @@ struct xhci_input_control_ctx { __le32 rsvd2[6]; }; +#define EP_IS_ADDED(ctrl_ctx, i) \ + (le32_to_cpu(ctrl_ctx->add_flags) & (1 << (i + 1))) +#define EP_IS_DROPPED(ctrl_ctx, i) \ + (le32_to_cpu(ctrl_ctx->drop_flags) & (1 << (i + 1))) + /* Represents everything that is needed to issue a command on the command ring. * It's useful to pre-allocate these for commands that cannot fail due to * out-of-memory errors, like freeing streams. @@ -731,6 +747,67 @@ struct xhci_stream_info { #define SMALL_STREAM_ARRAY_SIZE 256 #define MEDIUM_STREAM_ARRAY_SIZE 1024 +/* Some Intel xHCI host controllers need software to keep track of the bus + * bandwidth. Keep track of endpoint info here. Each root port is allocated + * the full bus bandwidth. We must also treat TTs (including each port under a + * multi-TT hub) as a separate bandwidth domain. The direct memory interface + * (DMI) also limits the total bandwidth (across all domains) that can be used. + */ +struct xhci_bw_info { + /* ep_interval is zero-based */ + unsigned int ep_interval; + /* mult and num_packets are one-based */ + unsigned int mult; + unsigned int num_packets; + unsigned int max_packet_size; + unsigned int max_esit_payload; + unsigned int type; +}; + +/* "Block" sizes in bytes the hardware uses for different device speeds. + * The logic in this part of the hardware limits the number of bits the hardware + * can use, so must represent bandwidth in a less precise manner to mimic what + * the scheduler hardware computes. + */ +#define FS_BLOCK 1 +#define HS_BLOCK 4 +#define SS_BLOCK 16 +#define DMI_BLOCK 32 + +/* Each device speed has a protocol overhead (CRC, bit stuffing, etc) associated + * with each byte transferred. SuperSpeed devices have an initial overhead to + * set up bursts. These are in blocks, see above. LS overhead has already been + * translated into FS blocks. + */ +#define DMI_OVERHEAD 8 +#define DMI_OVERHEAD_BURST 4 +#define SS_OVERHEAD 8 +#define SS_OVERHEAD_BURST 32 +#define HS_OVERHEAD 26 +#define FS_OVERHEAD 20 +#define LS_OVERHEAD 128 +/* The TTs need to claim roughly twice as much bandwidth (94 bytes per + * microframe ~= 24Mbps) of the HS bus as the devices can actually use because + * of overhead associated with split transfers crossing microframe boundaries. + * 31 blocks is pure protocol overhead. + */ +#define TT_HS_OVERHEAD (31 + 94) +#define TT_DMI_OVERHEAD (25 + 12) + +/* Bandwidth limits in blocks */ +#define FS_BW_LIMIT 1285 +#define TT_BW_LIMIT 1320 +#define HS_BW_LIMIT 1607 +#define SS_BW_LIMIT_IN 3906 +#define DMI_BW_LIMIT_IN 3906 +#define SS_BW_LIMIT_OUT 3906 +#define DMI_BW_LIMIT_OUT 3906 + +/* Percentage of bus bandwidth reserved for non-periodic transfers */ +#define FS_BW_RESERVED 10 +#define HS_BW_RESERVED 20 +#define SS_BW_RESERVED 10 + struct xhci_virt_ep { struct xhci_ring *ring; /* Related to endpoints that are configured to use stream IDs only */ @@ -772,8 +849,39 @@ struct xhci_virt_ep { * process the missed tds on the endpoint ring. */ bool skip; + /* Bandwidth checking storage */ + struct xhci_bw_info bw_info; + struct list_head bw_endpoint_list; +}; + +enum xhci_overhead_type { + LS_OVERHEAD_TYPE = 0, + FS_OVERHEAD_TYPE, + HS_OVERHEAD_TYPE, +}; + +struct xhci_interval_bw { + unsigned int num_packets; + /* Sorted by max packet size. + * Head of the list is the greatest max packet size. + */ + struct list_head endpoints; + /* How many endpoints of each speed are present. */ + unsigned int overhead[3]; +}; + +#define XHCI_MAX_INTERVAL 16 + +struct xhci_interval_bw_table { + unsigned int interval0_esit_payload; + struct xhci_interval_bw interval_bw[XHCI_MAX_INTERVAL]; + /* Includes reserved bandwidth for async endpoints */ + unsigned int bw_used; + unsigned int ss_bw_in; + unsigned int ss_bw_out; }; + struct xhci_virt_device { struct usb_device *udev; /* @@ -798,7 +906,32 @@ struct xhci_virt_device { /* Status of the last command issued for this device */ u32 cmd_status; struct list_head cmd_list; - u8 port; + u8 fake_port; + u8 real_port; + struct xhci_interval_bw_table *bw_table; + struct xhci_tt_bw_info *tt_info; +}; + +/* + * For each roothub, keep track of the bandwidth information for each periodic + * interval. + * + * If a high speed hub is attached to the roothub, each TT associated with that + * hub is a separate bandwidth domain. The interval information for the + * endpoints on the devices under that TT will appear in the TT structure. + */ +struct xhci_root_port_bw_info { + struct list_head tts; + unsigned int num_active_tts; + struct xhci_interval_bw_table bw_table; +}; + +struct xhci_tt_bw_info { + struct list_head tt_list; + int slot_id; + int ttport; + struct xhci_interval_bw_table bw_table; + int active_eps; }; @@ -1198,6 +1331,12 @@ struct s3_save { u64 erst_dequeue; }; +/* Use for lpm */ +struct dev_info { + u32 dev_id; + struct list_head list; +}; + struct xhci_bus_state { unsigned long bus_suspended; unsigned long next_statechange; @@ -1261,12 +1400,16 @@ struct xhci_hcd { struct xhci_erst erst; /* Scratchpad */ struct xhci_scratchpad *scratchpad; + /* Store LPM test failed devices' information */ + struct list_head lpm_failed_devs; /* slot enabling and address device helpers */ struct completion addr_dev; int slot_id; /* Internal mirror of the HW's dcbaa */ struct xhci_virt_device *devs[MAX_HC_SLOTS]; + /* For keeping track of bandwidth domains per roothub. */ + struct xhci_root_port_bw_info *rh_bw; /* DMA pools */ struct dma_pool *device_pool; @@ -1318,6 +1461,8 @@ struct xhci_hcd { #define XHCI_EP_LIMIT_QUIRK (1 << 5) #define XHCI_BROKEN_MSI (1 << 6) #define XHCI_RESET_ON_RESUME (1 << 7) +#define XHCI_SW_BW_CHECKING (1 << 8) +#define XHCI_AMD_0x96_HOST (1 << 9) unsigned int num_active_eps; unsigned int limit_active_eps; /* There are two roothubs to keep track of bus suspend info for */ @@ -1330,6 +1475,10 @@ struct xhci_hcd { /* Array of pointers to USB 2.0 PORTSC registers */ __le32 __iomem **usb2_ports; unsigned int num_usb2_ports; + /* support xHCI 0.96 spec USB2 software LPM */ + unsigned sw_lpm_support:1; + /* support xHCI 1.0 spec USB2 hardware LPM */ + unsigned hw_lpm_support:1; }; /* convert between an HCD pointer and the corresponding EHCI_HCD */ @@ -1401,9 +1550,7 @@ static inline void xhci_write_64(struct xhci_hcd *xhci, static inline int xhci_link_trb_quirk(struct xhci_hcd *xhci) { - u32 temp = xhci_readl(xhci, &xhci->cap_regs->hc_capbase); - return ((HC_VERSION(temp) == 0x95) && - (xhci->quirks & XHCI_LINK_TRB_QUIRK)); + return xhci->quirks & XHCI_LINK_TRB_QUIRK; } /* xHCI debugging */ @@ -1438,6 +1585,20 @@ unsigned int xhci_get_endpoint_flag(struct usb_endpoint_descriptor *desc); unsigned int xhci_get_endpoint_flag_from_index(unsigned int ep_index); unsigned int xhci_last_valid_endpoint(u32 added_ctxs); void xhci_endpoint_zero(struct xhci_hcd *xhci, struct xhci_virt_device *virt_dev, struct usb_host_endpoint *ep); +void xhci_drop_ep_from_interval_table(struct xhci_hcd *xhci, + struct xhci_bw_info *ep_bw, + struct xhci_interval_bw_table *bw_table, + struct usb_device *udev, + struct xhci_virt_ep *virt_ep, + struct xhci_tt_bw_info *tt_info); +void xhci_update_tt_active_eps(struct xhci_hcd *xhci, + struct xhci_virt_device *virt_dev, + int old_active_eps); +void xhci_clear_endpoint_bw_info(struct xhci_bw_info *bw_info); +void xhci_update_bw_info(struct xhci_hcd *xhci, + struct xhci_container_ctx *in_ctx, + struct xhci_input_control_ctx *ctrl_ctx, + struct xhci_virt_device *virt_dev); void xhci_endpoint_copy(struct xhci_hcd *xhci, struct xhci_container_ctx *in_ctx, struct xhci_container_ctx *out_ctx, @@ -1483,9 +1644,13 @@ void xhci_free_command(struct xhci_hcd *xhci, /* xHCI PCI glue */ int xhci_register_pci(void); void xhci_unregister_pci(void); +#else +static inline int xhci_register_pci(void) { return 0; } +static inline void xhci_unregister_pci(void) {} #endif /* xHCI host controller glue */ +typedef void (*xhci_get_quirks_t)(struct device *, struct xhci_hcd *); void xhci_quiesce(struct xhci_hcd *xhci); int xhci_halt(struct xhci_hcd *xhci); int xhci_reset(struct xhci_hcd *xhci); @@ -1493,6 +1658,7 @@ int xhci_init(struct usb_hcd *hcd); int xhci_run(struct usb_hcd *hcd); void xhci_stop(struct usb_hcd *hcd); void xhci_shutdown(struct usb_hcd *hcd); +int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks); #ifdef CONFIG_PM int xhci_suspend(struct xhci_hcd *xhci); @@ -1507,6 +1673,10 @@ irqreturn_t xhci_irq(struct usb_hcd *hcd); irqreturn_t xhci_msi_irq(int irq, struct usb_hcd *hcd); int xhci_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev); void xhci_free_dev(struct usb_hcd *hcd, struct usb_device *udev); +int xhci_alloc_tt_info(struct xhci_hcd *xhci, + struct xhci_virt_device *virt_dev, + struct usb_device *hdev, + struct usb_tt *tt, gfp_t mem_flags); int xhci_alloc_streams(struct usb_hcd *hcd, struct usb_device *udev, struct usb_host_endpoint **eps, unsigned int num_eps, unsigned int num_streams, gfp_t mem_flags); @@ -1514,6 +1684,9 @@ int xhci_free_streams(struct usb_hcd *hcd, struct usb_device *udev, struct usb_host_endpoint **eps, unsigned int num_eps, gfp_t mem_flags); int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev); +int xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev); +int xhci_set_usb2_hardware_lpm(struct usb_hcd *hcd, + struct usb_device *udev, int enable); int xhci_update_hub_device(struct usb_hcd *hcd, struct usb_device *hdev, struct usb_tt *tt, gfp_t mem_flags); int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags); @@ -1572,6 +1745,10 @@ void xhci_ring_ep_doorbell(struct xhci_hcd *xhci, unsigned int slot_id, unsigned int ep_index, unsigned int stream_id); /* xHCI roothub code */ +void xhci_set_link_state(struct xhci_hcd *xhci, __le32 __iomem **port_array, + int port_id, u32 link_state); +void xhci_test_and_clear_bit(struct xhci_hcd *xhci, __le32 __iomem **port_array, + int port_id, u32 port_bit); int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex, char *buf, u16 wLength); int xhci_hub_status_data(struct usb_hcd *hcd, char *buf); diff --git a/drivers/usb/misc/adutux.c b/drivers/usb/misc/adutux.c index a6afd15f6a46..fe858711651c 100644 --- a/drivers/usb/misc/adutux.c +++ b/drivers/usb/misc/adutux.c @@ -213,7 +213,7 @@ static void adu_interrupt_in_callback(struct urb *urb) if (urb->actual_length > 0 && dev->interrupt_in_buffer[0] != 0x00) { if (dev->read_buffer_length < - (4 * le16_to_cpu(dev->interrupt_in_endpoint->wMaxPacketSize)) - + (4 * usb_endpoint_maxp(dev->interrupt_in_endpoint)) - (urb->actual_length)) { memcpy (dev->read_buffer_primary + dev->read_buffer_length, @@ -315,7 +315,7 @@ static int adu_open(struct inode *inode, struct file *file) usb_rcvintpipe(dev->udev, dev->interrupt_in_endpoint->bEndpointAddress), dev->interrupt_in_buffer, - le16_to_cpu(dev->interrupt_in_endpoint->wMaxPacketSize), + usb_endpoint_maxp(dev->interrupt_in_endpoint), adu_interrupt_in_callback, dev, dev->interrupt_in_endpoint->bInterval); dev->read_urb_finished = 0; @@ -483,7 +483,7 @@ static ssize_t adu_read(struct file *file, __user char *buffer, size_t count, usb_rcvintpipe(dev->udev, dev->interrupt_in_endpoint->bEndpointAddress), dev->interrupt_in_buffer, - le16_to_cpu(dev->interrupt_in_endpoint->wMaxPacketSize), + usb_endpoint_maxp(dev->interrupt_in_endpoint), adu_interrupt_in_callback, dev, dev->interrupt_in_endpoint->bInterval); @@ -536,7 +536,7 @@ static ssize_t adu_read(struct file *file, __user char *buffer, size_t count, usb_rcvintpipe(dev->udev, dev->interrupt_in_endpoint->bEndpointAddress), dev->interrupt_in_buffer, - le16_to_cpu(dev->interrupt_in_endpoint->wMaxPacketSize), + usb_endpoint_maxp(dev->interrupt_in_endpoint), adu_interrupt_in_callback, dev, dev->interrupt_in_endpoint->bInterval); @@ -622,7 +622,7 @@ static ssize_t adu_write(struct file *file, const __user char *buffer, dbg(4," %s : sending, count = %Zd", __func__, count); /* write the data into interrupt_out_buffer from userspace */ - buffer_size = le16_to_cpu(dev->interrupt_out_endpoint->wMaxPacketSize); + buffer_size = usb_endpoint_maxp(dev->interrupt_out_endpoint); bytes_to_write = count > buffer_size ? buffer_size : count; dbg(4," %s : buffer_size = %Zd, count = %Zd, bytes_to_write = %Zd", __func__, buffer_size, count, bytes_to_write); @@ -752,8 +752,8 @@ static int adu_probe(struct usb_interface *interface, goto error; } - in_end_size = le16_to_cpu(dev->interrupt_in_endpoint->wMaxPacketSize); - out_end_size = le16_to_cpu(dev->interrupt_out_endpoint->wMaxPacketSize); + in_end_size = usb_endpoint_maxp(dev->interrupt_in_endpoint); + out_end_size = usb_endpoint_maxp(dev->interrupt_out_endpoint); dev->read_buffer_primary = kmalloc((4 * in_end_size), GFP_KERNEL); if (!dev->read_buffer_primary) { diff --git a/drivers/usb/misc/ftdi-elan.c b/drivers/usb/misc/ftdi-elan.c index 2f41089cd854..2dbe600fbc11 100644 --- a/drivers/usb/misc/ftdi-elan.c +++ b/drivers/usb/misc/ftdi-elan.c @@ -2777,7 +2777,7 @@ static int ftdi_elan_probe(struct usb_interface *interface, endpoint = &iface_desc->endpoint[i].desc; if (!ftdi->bulk_in_endpointAddr && usb_endpoint_is_bulk_in(endpoint)) { - buffer_size = le16_to_cpu(endpoint->wMaxPacketSize); + buffer_size = usb_endpoint_maxp(endpoint); ftdi->bulk_in_size = buffer_size; ftdi->bulk_in_endpointAddr = endpoint->bEndpointAddress; ftdi->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL); diff --git a/drivers/usb/misc/idmouse.c b/drivers/usb/misc/idmouse.c index c6184b4d1695..515b67fffab1 100644 --- a/drivers/usb/misc/idmouse.c +++ b/drivers/usb/misc/idmouse.c @@ -359,7 +359,7 @@ static int idmouse_probe(struct usb_interface *interface, endpoint = &iface_desc->endpoint[0].desc; if (!dev->bulk_in_endpointAddr && usb_endpoint_is_bulk_in(endpoint)) { /* we found a bulk in endpoint */ - dev->orig_bi_size = le16_to_cpu(endpoint->wMaxPacketSize); + dev->orig_bi_size = usb_endpoint_maxp(endpoint); dev->bulk_in_size = 0x200; /* works _much_ faster */ dev->bulk_in_endpointAddr = endpoint->bEndpointAddress; dev->bulk_in_buffer = diff --git a/drivers/usb/misc/iowarrior.c b/drivers/usb/misc/iowarrior.c index a2190b983f52..81457904d6ba 100644 --- a/drivers/usb/misc/iowarrior.c +++ b/drivers/usb/misc/iowarrior.c @@ -803,7 +803,7 @@ static int iowarrior_probe(struct usb_interface *interface, dev->int_out_endpoint = endpoint; } /* we have to check the report_size often, so remember it in the endianess suitable for our machine */ - dev->report_size = le16_to_cpu(dev->int_in_endpoint->wMaxPacketSize); + dev->report_size = usb_endpoint_maxp(dev->int_in_endpoint); if ((dev->interface->cur_altsetting->desc.bInterfaceNumber == 0) && (dev->product_id == USB_DEVICE_ID_CODEMERCS_IOW56)) /* IOWarrior56 has wMaxPacketSize different from report size */ diff --git a/drivers/usb/misc/ldusb.c b/drivers/usb/misc/ldusb.c index cb4096201e29..48c166f0d764 100644 --- a/drivers/usb/misc/ldusb.c +++ b/drivers/usb/misc/ldusb.c @@ -721,7 +721,7 @@ static int ld_usb_probe(struct usb_interface *intf, const struct usb_device_id * if (dev->interrupt_out_endpoint == NULL) dev_warn(&intf->dev, "Interrupt out endpoint not found (using control endpoint instead)\n"); - dev->interrupt_in_endpoint_size = le16_to_cpu(dev->interrupt_in_endpoint->wMaxPacketSize); + dev->interrupt_in_endpoint_size = usb_endpoint_maxp(dev->interrupt_in_endpoint); dev->ring_buffer = kmalloc(ring_buffer_size*(sizeof(size_t)+dev->interrupt_in_endpoint_size), GFP_KERNEL); if (!dev->ring_buffer) { dev_err(&intf->dev, "Couldn't allocate ring_buffer\n"); @@ -737,7 +737,7 @@ static int ld_usb_probe(struct usb_interface *intf, const struct usb_device_id * dev_err(&intf->dev, "Couldn't allocate interrupt_in_urb\n"); goto error; } - dev->interrupt_out_endpoint_size = dev->interrupt_out_endpoint ? le16_to_cpu(dev->interrupt_out_endpoint->wMaxPacketSize) : + dev->interrupt_out_endpoint_size = dev->interrupt_out_endpoint ? usb_endpoint_maxp(dev->interrupt_out_endpoint) : udev->descriptor.bMaxPacketSize0; dev->interrupt_out_buffer = kmalloc(write_buffer_size*dev->interrupt_out_endpoint_size, GFP_KERNEL); if (!dev->interrupt_out_buffer) { diff --git a/drivers/usb/misc/legousbtower.c b/drivers/usb/misc/legousbtower.c index 6482c6e2e6bd..a989356f693e 100644 --- a/drivers/usb/misc/legousbtower.c +++ b/drivers/usb/misc/legousbtower.c @@ -409,7 +409,7 @@ static int tower_open (struct inode *inode, struct file *file) dev->udev, usb_rcvintpipe(dev->udev, dev->interrupt_in_endpoint->bEndpointAddress), dev->interrupt_in_buffer, - le16_to_cpu(dev->interrupt_in_endpoint->wMaxPacketSize), + usb_endpoint_maxp(dev->interrupt_in_endpoint), tower_interrupt_in_callback, dev, dev->interrupt_in_interval); @@ -928,7 +928,7 @@ static int tower_probe (struct usb_interface *interface, const struct usb_device err("Couldn't allocate read_buffer"); goto error; } - dev->interrupt_in_buffer = kmalloc (le16_to_cpu(dev->interrupt_in_endpoint->wMaxPacketSize), GFP_KERNEL); + dev->interrupt_in_buffer = kmalloc (usb_endpoint_maxp(dev->interrupt_in_endpoint), GFP_KERNEL); if (!dev->interrupt_in_buffer) { err("Couldn't allocate interrupt_in_buffer"); goto error; diff --git a/drivers/usb/misc/usblcd.c b/drivers/usb/misc/usblcd.c index 51648154bb44..1871cdf10da3 100644 --- a/drivers/usb/misc/usblcd.c +++ b/drivers/usb/misc/usblcd.c @@ -18,7 +18,7 @@ #include <linux/slab.h> #include <linux/errno.h> #include <linux/mutex.h> -#include <asm/uaccess.h> +#include <linux/uaccess.h> #include <linux/usb.h> #define DRIVER_VERSION "USBLCD Driver Version 1.05" @@ -34,22 +34,29 @@ static const struct usb_device_id id_table[] = { { .idVendor = 0x10D2, .match_flags = USB_DEVICE_ID_MATCH_VENDOR, }, { }, }; -MODULE_DEVICE_TABLE (usb, id_table); +MODULE_DEVICE_TABLE(usb, id_table); static DEFINE_MUTEX(open_disc_mutex); struct usb_lcd { - struct usb_device * udev; /* init: probe_lcd */ - struct usb_interface * interface; /* the interface for this device */ - unsigned char * bulk_in_buffer; /* the buffer to receive data */ - size_t bulk_in_size; /* the size of the receive buffer */ - __u8 bulk_in_endpointAddr; /* the address of the bulk in endpoint */ - __u8 bulk_out_endpointAddr; /* the address of the bulk out endpoint */ + struct usb_device *udev; /* init: probe_lcd */ + struct usb_interface *interface; /* the interface for + this device */ + unsigned char *bulk_in_buffer; /* the buffer to receive + data */ + size_t bulk_in_size; /* the size of the + receive buffer */ + __u8 bulk_in_endpointAddr; /* the address of the + bulk in endpoint */ + __u8 bulk_out_endpointAddr; /* the address of the + bulk out endpoint */ struct kref kref; - struct semaphore limit_sem; /* to stop writes at full throttle from - * using up all RAM */ - struct usb_anchor submitted; /* URBs to wait for before suspend */ + struct semaphore limit_sem; /* to stop writes at + full throttle from + using up all RAM */ + struct usb_anchor submitted; /* URBs to wait for + before suspend */ }; #define to_lcd_dev(d) container_of(d, struct usb_lcd, kref) @@ -63,8 +70,8 @@ static void lcd_delete(struct kref *kref) struct usb_lcd *dev = to_lcd_dev(kref); usb_put_dev(dev->udev); - kfree (dev->bulk_in_buffer); - kfree (dev); + kfree(dev->bulk_in_buffer); + kfree(dev); } @@ -80,7 +87,7 @@ static int lcd_open(struct inode *inode, struct file *file) interface = usb_find_interface(&lcd_driver, subminor); if (!interface) { mutex_unlock(&lcd_mutex); - err ("USBLCD: %s - error, can't find device for minor %d", + err("USBLCD: %s - error, can't find device for minor %d", __func__, subminor); return -ENODEV; } @@ -126,7 +133,8 @@ static int lcd_release(struct inode *inode, struct file *file) return 0; } -static ssize_t lcd_read(struct file *file, char __user * buffer, size_t count, loff_t *ppos) +static ssize_t lcd_read(struct file *file, char __user * buffer, + size_t count, loff_t *ppos) { struct usb_lcd *dev; int retval = 0; @@ -135,8 +143,9 @@ static ssize_t lcd_read(struct file *file, char __user * buffer, size_t count, l dev = file->private_data; /* do a blocking bulk read to get data from the device */ - retval = usb_bulk_msg(dev->udev, - usb_rcvbulkpipe(dev->udev, dev->bulk_in_endpointAddr), + retval = usb_bulk_msg(dev->udev, + usb_rcvbulkpipe(dev->udev, + dev->bulk_in_endpointAddr), dev->bulk_in_buffer, min(dev->bulk_in_size, count), &bytes_read, 10000); @@ -161,23 +170,23 @@ static long lcd_ioctl(struct file *file, unsigned int cmd, unsigned long arg) dev = file->private_data; if (dev == NULL) return -ENODEV; - + switch (cmd) { case IOCTL_GET_HARD_VERSION: mutex_lock(&lcd_mutex); bcdDevice = le16_to_cpu((dev->udev)->descriptor.bcdDevice); - sprintf(buf,"%1d%1d.%1d%1d", + sprintf(buf, "%1d%1d.%1d%1d", (bcdDevice & 0xF000)>>12, (bcdDevice & 0xF00)>>8, (bcdDevice & 0xF0)>>4, (bcdDevice & 0xF)); mutex_unlock(&lcd_mutex); - if (copy_to_user((void __user *)arg,buf,strlen(buf))!=0) + if (copy_to_user((void __user *)arg, buf, strlen(buf)) != 0) return -EFAULT; break; case IOCTL_GET_DRV_VERSION: - sprintf(buf,DRIVER_VERSION); - if (copy_to_user((void __user *)arg,buf,strlen(buf))!=0) + sprintf(buf, DRIVER_VERSION); + if (copy_to_user((void __user *)arg, buf, strlen(buf)) != 0) return -EFAULT; break; default: @@ -199,7 +208,7 @@ static void lcd_write_bulk_callback(struct urb *urb) if (status && !(status == -ENOENT || status == -ECONNRESET || - status == -ESHUTDOWN)) { + status == -ESHUTDOWN)) { dbg("USBLCD: %s - nonzero write bulk status received: %d", __func__, status); } @@ -210,15 +219,16 @@ static void lcd_write_bulk_callback(struct urb *urb) up(&dev->limit_sem); } -static ssize_t lcd_write(struct file *file, const char __user * user_buffer, size_t count, loff_t *ppos) +static ssize_t lcd_write(struct file *file, const char __user * user_buffer, + size_t count, loff_t *ppos) { struct usb_lcd *dev; - int retval = 0, r; + int retval = 0, r; struct urb *urb = NULL; char *buf = NULL; - + dev = file->private_data; - + /* verify that we actually have some data to write */ if (count == 0) goto exit; @@ -233,34 +243,38 @@ static ssize_t lcd_write(struct file *file, const char __user * user_buffer, siz retval = -ENOMEM; goto err_no_buf; } - - buf = usb_alloc_coherent(dev->udev, count, GFP_KERNEL, &urb->transfer_dma); + + buf = usb_alloc_coherent(dev->udev, count, GFP_KERNEL, + &urb->transfer_dma); if (!buf) { retval = -ENOMEM; goto error; } - + if (copy_from_user(buf, user_buffer, count)) { retval = -EFAULT; goto error; } - + /* initialize the urb properly */ usb_fill_bulk_urb(urb, dev->udev, - usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr), + usb_sndbulkpipe(dev->udev, + dev->bulk_out_endpointAddr), buf, count, lcd_write_bulk_callback, dev); urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; usb_anchor_urb(urb, &dev->submitted); - + /* send the data out the bulk port */ retval = usb_submit_urb(urb, GFP_KERNEL); if (retval) { - err("USBLCD: %s - failed submitting write urb, error %d", __func__, retval); + err("USBLCD: %s - failed submitting write urb, error %d", + __func__, retval); goto error_unanchor; } - - /* release our reference to this urb, the USB core will eventually free it entirely */ + + /* release our reference to this urb, + the USB core will eventually free it entirely */ usb_free_urb(urb); exit: @@ -276,13 +290,13 @@ err_no_buf: } static const struct file_operations lcd_fops = { - .owner = THIS_MODULE, - .read = lcd_read, - .write = lcd_write, - .open = lcd_open, + .owner = THIS_MODULE, + .read = lcd_read, + .write = lcd_write, + .open = lcd_open, .unlocked_ioctl = lcd_ioctl, - .release = lcd_release, - .llseek = noop_llseek, + .release = lcd_release, + .llseek = noop_llseek, }; /* @@ -290,12 +304,13 @@ static const struct file_operations lcd_fops = { * and to have the device registered with the driver core */ static struct usb_class_driver lcd_class = { - .name = "lcd%d", - .fops = &lcd_fops, - .minor_base = USBLCD_MINOR, + .name = "lcd%d", + .fops = &lcd_fops, + .minor_base = USBLCD_MINOR, }; -static int lcd_probe(struct usb_interface *interface, const struct usb_device_id *id) +static int lcd_probe(struct usb_interface *interface, + const struct usb_device_id *id) { struct usb_lcd *dev = NULL; struct usb_host_interface *iface_desc; @@ -322,7 +337,7 @@ static int lcd_probe(struct usb_interface *interface, const struct usb_device_id retval = -ENODEV; goto error; } - + /* set up the endpoint information */ /* use only the first bulk-in and bulk-out endpoints */ iface_desc = interface->cur_altsetting; @@ -332,7 +347,7 @@ static int lcd_probe(struct usb_interface *interface, const struct usb_device_id if (!dev->bulk_in_endpointAddr && usb_endpoint_is_bulk_in(endpoint)) { /* we found a bulk in endpoint */ - buffer_size = le16_to_cpu(endpoint->wMaxPacketSize); + buffer_size = usb_endpoint_maxp(endpoint); dev->bulk_in_size = buffer_size; dev->bulk_in_endpointAddr = endpoint->bEndpointAddress; dev->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL); @@ -369,7 +384,7 @@ static int lcd_probe(struct usb_interface *interface, const struct usb_device_id dev_info(&interface->dev, "USBLCD Version %1d%1d.%1d%1d found " "at address %d\n", (i & 0xF000)>>12, (i & 0xF00)>>8, - (i & 0xF0)>>4,(i & 0xF), dev->udev->devnum); + (i & 0xF0)>>4, (i & 0xF), dev->udev->devnum); /* let the user know what node this device is now attached to */ dev_info(&interface->dev, "USB LCD device now attached to USBLCD-%d\n", @@ -401,7 +416,7 @@ static int lcd_suspend(struct usb_interface *intf, pm_message_t message) return 0; } -static int lcd_resume (struct usb_interface *intf) +static int lcd_resume(struct usb_interface *intf) { return 0; } @@ -409,16 +424,16 @@ static int lcd_resume (struct usb_interface *intf) static void lcd_disconnect(struct usb_interface *interface) { struct usb_lcd *dev; - int minor = interface->minor; + int minor = interface->minor; mutex_lock(&open_disc_mutex); - dev = usb_get_intfdata(interface); - usb_set_intfdata(interface, NULL); + dev = usb_get_intfdata(interface); + usb_set_intfdata(interface, NULL); mutex_unlock(&open_disc_mutex); - /* give back our minor */ - usb_deregister_dev(interface, &lcd_class); - + /* give back our minor */ + usb_deregister_dev(interface, &lcd_class); + /* decrement our usage count */ kref_put(&dev->kref, lcd_delete); @@ -438,7 +453,7 @@ static struct usb_driver lcd_driver = { static int __init usb_lcd_init(void) { int result; - + result = usb_register(&lcd_driver); if (result) err("usb_register failed. Error number %d", result); diff --git a/drivers/usb/misc/usbled.c b/drivers/usb/misc/usbled.c index 1616ad1793a4..43f84e50d514 100644 --- a/drivers/usb/misc/usbled.c +++ b/drivers/usb/misc/usbled.c @@ -33,10 +33,10 @@ static const struct usb_device_id id_table[] = { .driver_info = DREAM_CHEEKY_WEBMAIL_NOTIFIER }, { }, }; -MODULE_DEVICE_TABLE (usb, id_table); +MODULE_DEVICE_TABLE(usb, id_table); struct usb_led { - struct usb_device * udev; + struct usb_device *udev; unsigned char blue; unsigned char red; unsigned char green; @@ -113,14 +113,16 @@ static void change_color(struct usb_led *led) } #define show_set(value) \ -static ssize_t show_##value(struct device *dev, struct device_attribute *attr, char *buf) \ +static ssize_t show_##value(struct device *dev, struct device_attribute *attr,\ + char *buf) \ { \ struct usb_interface *intf = to_usb_interface(dev); \ struct usb_led *led = usb_get_intfdata(intf); \ \ return sprintf(buf, "%d\n", led->value); \ } \ -static ssize_t set_##value(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) \ +static ssize_t set_##value(struct device *dev, struct device_attribute *attr,\ + const char *buf, size_t count) \ { \ struct usb_interface *intf = to_usb_interface(dev); \ struct usb_led *led = usb_get_intfdata(intf); \ @@ -135,7 +137,8 @@ show_set(blue); show_set(red); show_set(green); -static int led_probe(struct usb_interface *interface, const struct usb_device_id *id) +static int led_probe(struct usb_interface *interface, + const struct usb_device_id *id) { struct usb_device *udev = interface_to_usbdev(interface); struct usb_led *dev = NULL; @@ -150,7 +153,7 @@ static int led_probe(struct usb_interface *interface, const struct usb_device_id dev->udev = usb_get_dev(udev); dev->type = id->driver_info; - usb_set_intfdata (interface, dev); + usb_set_intfdata(interface, dev); retval = device_create_file(&interface->dev, &dev_attr_blue); if (retval) @@ -194,7 +197,7 @@ error: device_remove_file(&interface->dev, &dev_attr_blue); device_remove_file(&interface->dev, &dev_attr_red); device_remove_file(&interface->dev, &dev_attr_green); - usb_set_intfdata (interface, NULL); + usb_set_intfdata(interface, NULL); usb_put_dev(dev->udev); kfree(dev); error_mem: @@ -205,14 +208,14 @@ static void led_disconnect(struct usb_interface *interface) { struct usb_led *dev; - dev = usb_get_intfdata (interface); + dev = usb_get_intfdata(interface); device_remove_file(&interface->dev, &dev_attr_blue); device_remove_file(&interface->dev, &dev_attr_red); device_remove_file(&interface->dev, &dev_attr_green); /* first remove the files, then set the pointer to NULL */ - usb_set_intfdata (interface, NULL); + usb_set_intfdata(interface, NULL); usb_put_dev(dev->udev); @@ -243,8 +246,8 @@ static void __exit usb_led_exit(void) usb_deregister(&led_driver); } -module_init (usb_led_init); -module_exit (usb_led_exit); +module_init(usb_led_init); +module_exit(usb_led_exit); MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); diff --git a/drivers/usb/misc/usbtest.c b/drivers/usb/misc/usbtest.c index bb10846affc3..bd6d00802eab 100644 --- a/drivers/usb/misc/usbtest.c +++ b/drivers/usb/misc/usbtest.c @@ -359,8 +359,10 @@ static int simple_io( urb->context = &completion; while (retval == 0 && iterations-- > 0) { init_completion(&completion); - if (usb_pipeout(urb->pipe)) + if (usb_pipeout(urb->pipe)) { simple_fill_buf(urb); + urb->transfer_flags |= URB_ZERO_PACKET; + } retval = usb_submit_urb(urb, GFP_KERNEL); if (retval != 0) break; @@ -1583,8 +1585,8 @@ static struct urb *iso_alloc_urb( if (bytes < 0 || !desc) return NULL; - maxp = 0x7ff & le16_to_cpu(desc->wMaxPacketSize); - maxp *= 1 + (0x3 & (le16_to_cpu(desc->wMaxPacketSize) >> 11)); + maxp = 0x7ff & usb_endpoint_maxp(desc); + maxp *= 1 + (0x3 & (usb_endpoint_maxp(desc) >> 11)); packets = DIV_ROUND_UP(bytes, maxp); urb = usb_alloc_urb(packets, GFP_KERNEL); @@ -1654,7 +1656,7 @@ test_iso_queue(struct usbtest_dev *dev, struct usbtest_param *param, "... iso period %d %sframes, wMaxPacket %04x\n", 1 << (desc->bInterval - 1), (udev->speed == USB_SPEED_HIGH) ? "micro" : "", - le16_to_cpu(desc->wMaxPacketSize)); + usb_endpoint_maxp(desc)); for (i = 0; i < param->sglen; i++) { urbs[i] = iso_alloc_urb(udev, pipe, desc, @@ -2298,25 +2300,8 @@ usbtest_probe(struct usb_interface *intf, const struct usb_device_id *id) usb_set_intfdata(intf, dev); dev_info(&intf->dev, "%s\n", info->name); - dev_info(&intf->dev, "%s speed {control%s%s%s%s%s} tests%s\n", - ({ char *tmp; - switch (udev->speed) { - case USB_SPEED_LOW: - tmp = "low"; - break; - case USB_SPEED_FULL: - tmp = "full"; - break; - case USB_SPEED_HIGH: - tmp = "high"; - break; - case USB_SPEED_SUPER: - tmp = "super"; - break; - default: - tmp = "unknown"; - break; - }; tmp; }), + dev_info(&intf->dev, "%s {control%s%s%s%s%s} tests%s\n", + usb_speed_string(udev->speed), info->ctrl_out ? " in/out" : "", rtest, wtest, irtest, iwtest, diff --git a/drivers/usb/mon/mon_bin.c b/drivers/usb/mon/mon_bin.c index a09dbd243eb3..a04b2ff9dd83 100644 --- a/drivers/usb/mon/mon_bin.c +++ b/drivers/usb/mon/mon_bin.c @@ -1101,7 +1101,7 @@ static long mon_bin_ioctl(struct file *file, unsigned int cmd, unsigned long arg nevents = mon_bin_queued(rp); sp = (struct mon_bin_stats __user *)arg; - if (put_user(rp->cnt_lost, &sp->dropped)) + if (put_user(ndropped, &sp->dropped)) return -EFAULT; if (put_user(nevents, &sp->queued)) return -EFAULT; diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c index fe8d14cac436..ae4a20acef6c 100644 --- a/drivers/usb/musb/musb_gadget.c +++ b/drivers/usb/musb/musb_gadget.c @@ -634,6 +634,7 @@ static void rxstate(struct musb *musb, struct musb_request *req) u16 len; u16 csr = musb_readw(epio, MUSB_RXCSR); struct musb_hw_ep *hw_ep = &musb->endpoints[epnum]; + u8 use_mode_1; if (hw_ep->is_shared_fifo) musb_ep = &hw_ep->ep_in; @@ -683,6 +684,18 @@ static void rxstate(struct musb *musb, struct musb_request *req) if (csr & MUSB_RXCSR_RXPKTRDY) { len = musb_readw(epio, MUSB_RXCOUNT); + + /* + * Enable Mode 1 on RX transfers only when short_not_ok flag + * is set. Currently short_not_ok flag is set only from + * file_storage and f_mass_storage drivers + */ + + if (request->short_not_ok && len == musb_ep->packet_sz) + use_mode_1 = 1; + else + use_mode_1 = 0; + if (request->actual < request->length) { #ifdef CONFIG_USB_INVENTRA_DMA if (is_buffer_mapped(req)) { @@ -714,37 +727,41 @@ static void rxstate(struct musb *musb, struct musb_request *req) * then becomes usable as a runtime "use mode 1" hint... */ - csr |= MUSB_RXCSR_DMAENAB; -#ifdef USE_MODE1 - csr |= MUSB_RXCSR_AUTOCLEAR; - /* csr |= MUSB_RXCSR_DMAMODE; */ - - /* this special sequence (enabling and then - * disabling MUSB_RXCSR_DMAMODE) is required - * to get DMAReq to activate - */ - musb_writew(epio, MUSB_RXCSR, - csr | MUSB_RXCSR_DMAMODE); -#else - if (!musb_ep->hb_mult && - musb_ep->hw_ep->rx_double_buffered) + /* Experimental: Mode1 works with mass storage use cases */ + if (use_mode_1) { csr |= MUSB_RXCSR_AUTOCLEAR; -#endif - musb_writew(epio, MUSB_RXCSR, csr); + musb_writew(epio, MUSB_RXCSR, csr); + csr |= MUSB_RXCSR_DMAENAB; + musb_writew(epio, MUSB_RXCSR, csr); + + /* + * this special sequence (enabling and then + * disabling MUSB_RXCSR_DMAMODE) is required + * to get DMAReq to activate + */ + musb_writew(epio, MUSB_RXCSR, + csr | MUSB_RXCSR_DMAMODE); + musb_writew(epio, MUSB_RXCSR, csr); + + } else { + if (!musb_ep->hb_mult && + musb_ep->hw_ep->rx_double_buffered) + csr |= MUSB_RXCSR_AUTOCLEAR; + csr |= MUSB_RXCSR_DMAENAB; + musb_writew(epio, MUSB_RXCSR, csr); + } if (request->actual < request->length) { int transfer_size = 0; -#ifdef USE_MODE1 - transfer_size = min(request->length - request->actual, - channel->max_len); -#else - transfer_size = min(request->length - request->actual, - (unsigned)len); -#endif - if (transfer_size <= musb_ep->packet_sz) - musb_ep->dma->desired_mode = 0; - else + if (use_mode_1) { + transfer_size = min(request->length - request->actual, + channel->max_len); musb_ep->dma->desired_mode = 1; + } else { + transfer_size = min(request->length - request->actual, + (unsigned)len); + musb_ep->dma->desired_mode = 0; + } use_dma = c->channel_program( channel, @@ -1020,7 +1037,7 @@ static int musb_gadget_enable(struct usb_ep *ep, goto fail; /* REVISIT this rules out high bandwidth periodic transfers */ - tmp = le16_to_cpu(desc->wMaxPacketSize); + tmp = usb_endpoint_maxp(desc); if (tmp & ~0x07ff) { int ok; diff --git a/drivers/usb/musb/musb_gadget_ep0.c b/drivers/usb/musb/musb_gadget_ep0.c index 9378b359c1f0..6a0d0467ec74 100644 --- a/drivers/usb/musb/musb_gadget_ep0.c +++ b/drivers/usb/musb/musb_gadget_ep0.c @@ -679,6 +679,14 @@ irqreturn_t musb_g_ep0_irq(struct musb *musb) musb_readb(mbase, MUSB_FADDR), decode_ep0stage(musb->ep0_state)); + if (csr & MUSB_CSR0_P_DATAEND) { + /* + * If DATAEND is set we should not call the callback, + * hence the status stage is not complete. + */ + return IRQ_HANDLED; + } + /* I sent a stall.. need to acknowledge it now.. */ if (csr & MUSB_CSR0_P_SENTSTALL) { musb_writew(regs, MUSB_CSR0, diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index 8b2473fa0f47..60ddba8066ea 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -1932,7 +1932,7 @@ static int musb_urb_enqueue( INIT_LIST_HEAD(&qh->ring); qh->is_ready = 1; - qh->maxpacket = le16_to_cpu(epd->wMaxPacketSize); + qh->maxpacket = usb_endpoint_maxp(epd); qh->type = usb_endpoint_type(epd); /* Bits 11 & 12 of wMaxPacketSize encode high bandwidth multiplier. diff --git a/drivers/usb/musb/musbhsdma.c b/drivers/usb/musb/musbhsdma.c index f70c5a577736..57a608584e16 100644 --- a/drivers/usb/musb/musbhsdma.c +++ b/drivers/usb/musb/musbhsdma.c @@ -408,7 +408,7 @@ dma_controller_create(struct musb *musb, void __iomem *base) controller->controller.channel_program = dma_channel_program; controller->controller.channel_abort = dma_channel_abort; - if (request_irq(irq, dma_controller_irq, IRQF_DISABLED, + if (request_irq(irq, dma_controller_irq, 0, dev_name(musb->controller), &controller->controller)) { dev_err(dev, "request_irq %d failed!\n", irq); dma_controller_destroy(&controller->controller); diff --git a/drivers/usb/otg/isp1301_omap.c b/drivers/usb/otg/isp1301_omap.c index ca9b690a7e40..8c86787c2f09 100644 --- a/drivers/usb/otg/isp1301_omap.c +++ b/drivers/usb/otg/isp1301_omap.c @@ -899,7 +899,7 @@ static int otg_bind(struct isp1301 *isp) if (otg_dev) status = request_irq(otg_dev->resource[1].start, omap_otg_irq, - IRQF_DISABLED, DRIVER_NAME, isp); + 0, DRIVER_NAME, isp); else status = -ENODEV; diff --git a/drivers/usb/otg/twl6030-usb.c b/drivers/usb/otg/twl6030-usb.c index b4d2c0972b3d..ed2b26cfe814 100644 --- a/drivers/usb/otg/twl6030-usb.c +++ b/drivers/usb/otg/twl6030-usb.c @@ -137,22 +137,6 @@ static inline u8 twl6030_readb(struct twl6030_usb *twl, u8 module, u8 address) return ret; } -/*-------------------------------------------------------------------------*/ -static int twl6030_set_phy_clk(struct otg_transceiver *x, int on) -{ - struct twl6030_usb *twl; - struct device *dev; - struct twl4030_usb_data *pdata; - - twl = xceiv_to_twl(x); - dev = twl->dev; - pdata = dev->platform_data; - - pdata->phy_set_clock(twl->dev, on); - - return 0; -} - static int twl6030_phy_init(struct otg_transceiver *x) { struct twl6030_usb *twl; diff --git a/drivers/usb/renesas_usbhs/Kconfig b/drivers/usb/renesas_usbhs/Kconfig index 286cbf1ca7de..6f4afa436381 100644 --- a/drivers/usb/renesas_usbhs/Kconfig +++ b/drivers/usb/renesas_usbhs/Kconfig @@ -4,7 +4,7 @@ config USB_RENESAS_USBHS tristate 'Renesas USBHS controller' - depends on SUPERH || ARCH_SHMOBILE + depends on USB && USB_GADGET default n help Renesas USBHS is a discrete USB host and peripheral controller chip diff --git a/drivers/usb/renesas_usbhs/Makefile b/drivers/usb/renesas_usbhs/Makefile index ce08345fa15a..bc8aef4311a1 100644 --- a/drivers/usb/renesas_usbhs/Makefile +++ b/drivers/usb/renesas_usbhs/Makefile @@ -6,4 +6,10 @@ obj-$(CONFIG_USB_RENESAS_USBHS) += renesas_usbhs.o renesas_usbhs-y := common.o mod.o pipe.o fifo.o -renesas_usbhs-$(CONFIG_USB_RENESAS_USBHS_UDC) += mod_gadget.o +ifneq ($(CONFIG_USB_RENESAS_USBHS_HCD),) + renesas_usbhs-y += mod_host.o +endif + +ifneq ($(CONFIG_USB_RENESAS_USBHS_UDC),) + renesas_usbhs-y += mod_gadget.o +endif diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c index d8239e5efa66..d2e2efaba658 100644 --- a/drivers/usb/renesas_usbhs/common.c +++ b/drivers/usb/renesas_usbhs/common.c @@ -61,8 +61,8 @@ */ #define usbhs_platform_call(priv, func, args...)\ (!(priv) ? -ENODEV : \ - !((priv)->pfunc->func) ? 0 : \ - (priv)->pfunc->func(args)) + !((priv)->pfunc.func) ? 0 : \ + (priv)->pfunc.func(args)) /* * common functions @@ -114,6 +114,10 @@ void usbhs_sys_host_ctrl(struct usbhs_priv *priv, int enable) { u16 mask = DCFM | DRPD | DPRPU; u16 val = DCFM | DRPD; + int has_otg = usbhs_get_dparam(priv, has_otg); + + if (has_otg) + usbhs_bset(priv, DVSTCTR, (EXTLP | PWEN), (EXTLP | PWEN)); /* * if enable @@ -147,19 +151,133 @@ int usbhs_frame_get_num(struct usbhs_priv *priv) } /* + * usb request functions + */ +void usbhs_usbreq_get_val(struct usbhs_priv *priv, struct usb_ctrlrequest *req) +{ + u16 val; + + val = usbhs_read(priv, USBREQ); + req->bRequest = (val >> 8) & 0xFF; + req->bRequestType = (val >> 0) & 0xFF; + + req->wValue = usbhs_read(priv, USBVAL); + req->wIndex = usbhs_read(priv, USBINDX); + req->wLength = usbhs_read(priv, USBLENG); +} + +void usbhs_usbreq_set_val(struct usbhs_priv *priv, struct usb_ctrlrequest *req) +{ + usbhs_write(priv, USBREQ, (req->bRequest << 8) | req->bRequestType); + usbhs_write(priv, USBVAL, req->wValue); + usbhs_write(priv, USBINDX, req->wIndex); + usbhs_write(priv, USBLENG, req->wLength); + + usbhs_bset(priv, DCPCTR, SUREQ, SUREQ); +} + +/* + * bus/vbus functions + */ +void usbhs_bus_send_sof_enable(struct usbhs_priv *priv) +{ + u16 status = usbhs_read(priv, DVSTCTR) & (USBRST | UACT); + + if (status != USBRST) { + struct device *dev = usbhs_priv_to_dev(priv); + dev_err(dev, "usbhs should be reset\n"); + } + + usbhs_bset(priv, DVSTCTR, (USBRST | UACT), UACT); +} + +void usbhs_bus_send_reset(struct usbhs_priv *priv) +{ + usbhs_bset(priv, DVSTCTR, (USBRST | UACT), USBRST); +} + +int usbhs_bus_get_speed(struct usbhs_priv *priv) +{ + u16 dvstctr = usbhs_read(priv, DVSTCTR); + + switch (RHST & dvstctr) { + case RHST_LOW_SPEED: + return USB_SPEED_LOW; + case RHST_FULL_SPEED: + return USB_SPEED_FULL; + case RHST_HIGH_SPEED: + return USB_SPEED_HIGH; + } + + return USB_SPEED_UNKNOWN; +} + +int usbhs_vbus_ctrl(struct usbhs_priv *priv, int enable) +{ + struct platform_device *pdev = usbhs_priv_to_pdev(priv); + + return usbhs_platform_call(priv, set_vbus, pdev, enable); +} + +static void usbhsc_bus_init(struct usbhs_priv *priv) +{ + usbhs_write(priv, DVSTCTR, 0); + + usbhs_vbus_ctrl(priv, 0); +} + +/* + * device configuration + */ +int usbhs_set_device_speed(struct usbhs_priv *priv, int devnum, + u16 upphub, u16 hubport, u16 speed) +{ + struct device *dev = usbhs_priv_to_dev(priv); + u16 usbspd = 0; + u32 reg = DEVADD0 + (2 * devnum); + + if (devnum > 10) { + dev_err(dev, "cannot set speed to unknown device %d\n", devnum); + return -EIO; + } + + if (upphub > 0xA) { + dev_err(dev, "unsupported hub number %d\n", upphub); + return -EIO; + } + + switch (speed) { + case USB_SPEED_LOW: + usbspd = USBSPD_SPEED_LOW; + break; + case USB_SPEED_FULL: + usbspd = USBSPD_SPEED_FULL; + break; + case USB_SPEED_HIGH: + usbspd = USBSPD_SPEED_HIGH; + break; + default: + dev_err(dev, "unsupported speed %d\n", speed); + return -EIO; + } + + usbhs_write(priv, reg, UPPHUB(upphub) | + HUBPORT(hubport)| + USBSPD(usbspd)); + + return 0; +} + +/* * local functions */ -static void usbhsc_bus_ctrl(struct usbhs_priv *priv, int enable) +static void usbhsc_set_buswait(struct usbhs_priv *priv) { int wait = usbhs_get_dparam(priv, buswait_bwait); - u16 data = 0; - if (enable) { - /* set bus wait if platform have */ - if (wait) - usbhs_bset(priv, BUSWAIT, 0x000F, wait); - } - usbhs_write(priv, DVSTCTR, data); + /* set bus wait if platform have */ + if (wait) + usbhs_bset(priv, BUSWAIT, 0x000F, wait); } /* @@ -191,10 +309,8 @@ static void usbhsc_power_ctrl(struct usbhs_priv *priv, int enable) /* USB on */ usbhs_sys_clock_ctrl(priv, enable); - usbhsc_bus_ctrl(priv, enable); } else { /* USB off */ - usbhsc_bus_ctrl(priv, enable); usbhs_sys_clock_ctrl(priv, enable); /* disable PM */ @@ -203,13 +319,10 @@ static void usbhsc_power_ctrl(struct usbhs_priv *priv, int enable) } /* - * notify hotplug + * hotplug */ -static void usbhsc_notify_hotplug(struct work_struct *work) +static void usbhsc_hotplug(struct usbhs_priv *priv) { - struct usbhs_priv *priv = container_of(work, - struct usbhs_priv, - notify_hotplug_work.work); struct platform_device *pdev = usbhs_priv_to_pdev(priv); struct usbhs_mod *mod = usbhs_mod_get_current(priv); int id; @@ -237,6 +350,10 @@ static void usbhsc_notify_hotplug(struct work_struct *work) if (usbhsc_flags_has(priv, USBHSF_RUNTIME_PWCTRL)) usbhsc_power_ctrl(priv, enable); + /* bus init */ + usbhsc_set_buswait(priv); + usbhsc_bus_init(priv); + /* module start */ usbhs_mod_call(priv, start, priv); @@ -246,6 +363,9 @@ static void usbhsc_notify_hotplug(struct work_struct *work) /* module stop */ usbhs_mod_call(priv, stop, priv); + /* bus init */ + usbhsc_bus_init(priv); + /* power off */ if (usbhsc_flags_has(priv, USBHSF_RUNTIME_PWCTRL)) usbhsc_power_ctrl(priv, enable); @@ -257,6 +377,17 @@ static void usbhsc_notify_hotplug(struct work_struct *work) } } +/* + * notify hotplug + */ +static void usbhsc_notify_hotplug(struct work_struct *work) +{ + struct usbhs_priv *priv = container_of(work, + struct usbhs_priv, + notify_hotplug_work.work); + usbhsc_hotplug(priv); +} + int usbhsc_drvcllbck_notify_hotplug(struct platform_device *pdev) { struct usbhs_priv *priv = usbhs_pdev_to_priv(pdev); @@ -315,24 +446,28 @@ static int __devinit usbhs_probe(struct platform_device *pdev) /* * care platform info */ - priv->pfunc = &info->platform_callback; - priv->dparam = &info->driver_param; + memcpy(&priv->pfunc, + &info->platform_callback, + sizeof(struct renesas_usbhs_platform_callback)); + memcpy(&priv->dparam, + &info->driver_param, + sizeof(struct renesas_usbhs_driver_param)); /* set driver callback functions for platform */ dfunc = &info->driver_callback; dfunc->notify_hotplug = usbhsc_drvcllbck_notify_hotplug; /* set default param if platform doesn't have */ - if (!priv->dparam->pipe_type) { - priv->dparam->pipe_type = usbhsc_default_pipe_type; - priv->dparam->pipe_size = ARRAY_SIZE(usbhsc_default_pipe_type); + if (!priv->dparam.pipe_type) { + priv->dparam.pipe_type = usbhsc_default_pipe_type; + priv->dparam.pipe_size = ARRAY_SIZE(usbhsc_default_pipe_type); } - if (!priv->dparam->pio_dma_border) - priv->dparam->pio_dma_border = 64; /* 64byte */ + if (!priv->dparam.pio_dma_border) + priv->dparam.pio_dma_border = 64; /* 64byte */ /* FIXME */ /* runtime power control ? */ - if (priv->pfunc->get_vbus) + if (priv->pfunc.get_vbus) usbhsc_flags_set(priv, USBHSF_RUNTIME_PWCTRL); /* @@ -443,9 +578,60 @@ static int __devexit usbhs_remove(struct platform_device *pdev) return 0; } +static int usbhsc_suspend(struct device *dev) +{ + struct usbhs_priv *priv = dev_get_drvdata(dev); + struct usbhs_mod *mod = usbhs_mod_get_current(priv); + + if (mod) { + usbhs_mod_call(priv, stop, priv); + usbhs_mod_change(priv, -1); + } + + if (mod || !usbhsc_flags_has(priv, USBHSF_RUNTIME_PWCTRL)) + usbhsc_power_ctrl(priv, 0); + + return 0; +} + +static int usbhsc_resume(struct device *dev) +{ + struct usbhs_priv *priv = dev_get_drvdata(dev); + struct platform_device *pdev = usbhs_priv_to_pdev(priv); + + usbhs_platform_call(priv, phy_reset, pdev); + + if (!usbhsc_flags_has(priv, USBHSF_RUNTIME_PWCTRL)) + usbhsc_power_ctrl(priv, 1); + + usbhsc_hotplug(priv); + + return 0; +} + +static int usbhsc_runtime_nop(struct device *dev) +{ + /* Runtime PM callback shared between ->runtime_suspend() + * and ->runtime_resume(). Simply returns success. + * + * This driver re-initializes all registers after + * pm_runtime_get_sync() anyway so there is no need + * to save and restore registers here. + */ + return 0; +} + +static const struct dev_pm_ops usbhsc_pm_ops = { + .suspend = usbhsc_suspend, + .resume = usbhsc_resume, + .runtime_suspend = usbhsc_runtime_nop, + .runtime_resume = usbhsc_runtime_nop, +}; + static struct platform_driver renesas_usbhs_driver = { .driver = { .name = "renesas_usbhs", + .pm = &usbhsc_pm_ops, }, .probe = usbhs_probe, .remove = __devexit_p(usbhs_remove), diff --git a/drivers/usb/renesas_usbhs/common.h b/drivers/usb/renesas_usbhs/common.h index b410463a1212..8729da5c3be6 100644 --- a/drivers/usb/renesas_usbhs/common.h +++ b/drivers/usb/renesas_usbhs/common.h @@ -90,6 +90,17 @@ struct usbhs_priv; #define PIPE9TRN 0x00BA #define PIPEATRE 0x00BC #define PIPEATRN 0x00BE +#define DEVADD0 0x00D0 /* Device address n configuration */ +#define DEVADD1 0x00D2 +#define DEVADD2 0x00D4 +#define DEVADD3 0x00D6 +#define DEVADD4 0x00D8 +#define DEVADD5 0x00DA +#define DEVADD6 0x00DC +#define DEVADD7 0x00DE +#define DEVADD8 0x00E0 +#define DEVADD9 0x00E2 +#define DEVADDA 0x00E4 /* SYSCFG */ #define SCKE (1 << 10) /* USB Module Clock Enable */ @@ -102,6 +113,8 @@ struct usbhs_priv; /* DVSTCTR */ #define EXTLP (1 << 10) /* Controls the EXTLP pin output state */ #define PWEN (1 << 9) /* Controls the PWEN pin output state */ +#define USBRST (1 << 6) /* Bus Reset Output */ +#define UACT (1 << 4) /* USB Bus Enable */ #define RHST (0x7) /* Reset Handshake */ #define RHST_LOW_SPEED 1 /* Low-speed connection */ #define RHST_FULL_SPEED 2 /* Full-speed connection */ @@ -159,6 +172,15 @@ struct usbhs_priv; #define NODATA_STATUS_STAGE 5 /* Control write NoData status stage */ #define SEQUENCE_ERROR 6 /* Control transfer sequence error */ +/* INTSTS1 */ +#define OVRCR (1 << 15) /* OVRCR Interrupt Status */ +#define BCHG (1 << 14) /* USB Bus Change Interrupt Status */ +#define DTCH (1 << 12) /* USB Disconnection Detect Interrupt Status */ +#define ATTCH (1 << 11) /* ATTCH Interrupt Status */ +#define EOFERR (1 << 6) /* EOF Error Detect Interrupt Status */ +#define SIGN (1 << 5) /* Setup Transaction Error Interrupt Status */ +#define SACK (1 << 4) /* Setup Transaction ACK Response Interrupt Status */ + /* PIPECFG */ /* DCPCFG */ #define TYPE_NONE (0 << 14) /* Transfer Type */ @@ -183,9 +205,11 @@ struct usbhs_priv; /* PIPEnCTR */ /* DCPCTR */ #define BSTS (1 << 15) /* Buffer Status */ +#define SUREQ (1 << 14) /* Sending SETUP Token */ #define CSSTS (1 << 12) /* CSSTS Status */ -#define SQCLR (1 << 8) /* Toggle Bit Clear */ #define ACLRM (1 << 9) /* Buffer Auto-Clear Mode */ +#define SQCLR (1 << 8) /* Toggle Bit Clear */ +#define SQSET (1 << 7) /* Toggle Bit Set */ #define PBUSY (1 << 5) /* Pipe Busy */ #define PID_MASK (0x3) /* Response PID */ #define PID_NAK 0 @@ -202,6 +226,14 @@ struct usbhs_priv; /* FRMNUM */ #define FRNM_MASK (0x7FF) +/* DEVADDn */ +#define UPPHUB(x) (((x) & 0xF) << 11) /* HUB Register */ +#define HUBPORT(x) (((x) & 0x7) << 8) /* HUB Port for Target Device */ +#define USBSPD(x) (((x) & 0x3) << 6) /* Device Transfer Rate */ +#define USBSPD_SPEED_LOW 0x1 +#define USBSPD_SPEED_FULL 0x2 +#define USBSPD_SPEED_HIGH 0x3 + /* * struct */ @@ -210,8 +242,8 @@ struct usbhs_priv { void __iomem *base; unsigned int irq; - struct renesas_usbhs_platform_callback *pfunc; - struct renesas_usbhs_driver_param *dparam; + struct renesas_usbhs_platform_callback pfunc; + struct renesas_usbhs_driver_param dparam; struct delayed_work notify_hotplug_work; struct platform_device *pdev; @@ -258,15 +290,35 @@ void usbhs_sys_host_ctrl(struct usbhs_priv *priv, int enable); void usbhs_sys_function_ctrl(struct usbhs_priv *priv, int enable); /* + * usb request + */ +void usbhs_usbreq_get_val(struct usbhs_priv *priv, struct usb_ctrlrequest *req); +void usbhs_usbreq_set_val(struct usbhs_priv *priv, struct usb_ctrlrequest *req); + +/* + * bus + */ +void usbhs_bus_send_sof_enable(struct usbhs_priv *priv); +void usbhs_bus_send_reset(struct usbhs_priv *priv); +int usbhs_bus_get_speed(struct usbhs_priv *priv); +int usbhs_vbus_ctrl(struct usbhs_priv *priv, int enable); + +/* * frame */ int usbhs_frame_get_num(struct usbhs_priv *priv); /* + * device config + */ +int usbhs_set_device_speed(struct usbhs_priv *priv, int devnum, u16 upphub, + u16 hubport, u16 speed); + +/* * data */ struct usbhs_priv *usbhs_pdev_to_priv(struct platform_device *pdev); -#define usbhs_get_dparam(priv, param) (priv->dparam->param) +#define usbhs_get_dparam(priv, param) (priv->dparam.param) #define usbhs_priv_to_pdev(priv) (priv->pdev) #define usbhs_priv_to_dev(priv) (&priv->pdev->dev) #define usbhs_priv_to_lock(priv) (&priv->lock) diff --git a/drivers/usb/renesas_usbhs/fifo.c b/drivers/usb/renesas_usbhs/fifo.c index a34430f55fb7..8da685e796d1 100644 --- a/drivers/usb/renesas_usbhs/fifo.c +++ b/drivers/usb/renesas_usbhs/fifo.c @@ -54,35 +54,45 @@ static struct usbhs_pkt_handle usbhsf_null_handler = { }; void usbhs_pkt_push(struct usbhs_pipe *pipe, struct usbhs_pkt *pkt, - struct usbhs_pkt_handle *handler, + void (*done)(struct usbhs_priv *priv, + struct usbhs_pkt *pkt), void *buf, int len, int zero) { struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe); struct device *dev = usbhs_priv_to_dev(priv); unsigned long flags; + if (!done) { + dev_err(dev, "no done function\n"); + return; + } + /******************** spin lock ********************/ usbhs_lock(priv, flags); - if (!handler) { + if (!pipe->handler) { dev_err(dev, "no handler function\n"); - handler = &usbhsf_null_handler; + pipe->handler = &usbhsf_null_handler; } list_del_init(&pkt->node); list_add_tail(&pkt->node, &pipe->list); + /* + * each pkt must hold own handler. + * because handler might be changed by its situation. + * dma handler -> pio handler. + */ pkt->pipe = pipe; pkt->buf = buf; - pkt->handler = handler; + pkt->handler = pipe->handler; pkt->length = len; pkt->zero = zero; pkt->actual = 0; + pkt->done = done; usbhs_unlock(priv, flags); /******************** spin unlock ******************/ - - usbhs_pkt_start(pipe); } static void __usbhsf_pkt_del(struct usbhs_pkt *pkt) @@ -118,10 +128,15 @@ struct usbhs_pkt *usbhs_pkt_pop(struct usbhs_pipe *pipe, struct usbhs_pkt *pkt) return pkt; } -int __usbhs_pkt_handler(struct usbhs_pipe *pipe, int type) +enum { + USBHSF_PKT_PREPARE, + USBHSF_PKT_TRY_RUN, + USBHSF_PKT_DMA_DONE, +}; + +static int usbhsf_pkt_handler(struct usbhs_pipe *pipe, int type) { struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe); - struct usbhs_pipe_info *info = usbhs_priv_to_pipeinfo(priv); struct usbhs_pkt *pkt; struct device *dev = usbhs_priv_to_dev(priv); int (*func)(struct usbhs_pkt *pkt, int *is_done); @@ -161,13 +176,18 @@ __usbhs_pkt_handler_end: /******************** spin unlock ******************/ if (is_done) { - info->done(pkt); + pkt->done(priv, pkt); usbhs_pkt_start(pipe); } return ret; } +void usbhs_pkt_start(struct usbhs_pipe *pipe) +{ + usbhsf_pkt_handler(pipe, USBHSF_PKT_PREPARE); +} + /* * irq enable/disable function */ @@ -276,9 +296,13 @@ static int usbhsf_fifo_select(struct usbhs_pipe *pipe, usbhsf_fifo_is_busy(fifo)) return -EBUSY; - if (usbhs_pipe_is_dcp(pipe)) + if (usbhs_pipe_is_dcp(pipe)) { base |= (1 == write) << 5; /* ISEL */ + if (usbhs_mod_is_host(priv)) + usbhs_dcp_dir_for_host(pipe, write); + } + /* "base" will be used below */ usbhs_write(priv, fifo->sel, base | MBW_32); @@ -297,6 +321,151 @@ static int usbhsf_fifo_select(struct usbhs_pipe *pipe, } /* + * DCP status stage + */ +static int usbhs_dcp_dir_switch_to_write(struct usbhs_pkt *pkt, int *is_done) +{ + struct usbhs_pipe *pipe = pkt->pipe; + struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe); + struct usbhs_fifo *fifo = usbhsf_get_cfifo(priv); /* CFIFO */ + struct device *dev = usbhs_priv_to_dev(priv); + int ret; + + usbhs_pipe_disable(pipe); + + ret = usbhsf_fifo_select(pipe, fifo, 1); + if (ret < 0) { + dev_err(dev, "%s() faile\n", __func__); + return ret; + } + + usbhs_pipe_sequence_data1(pipe); /* DATA1 */ + + usbhsf_fifo_clear(pipe, fifo); + usbhsf_send_terminator(pipe, fifo); + + usbhsf_fifo_unselect(pipe, fifo); + + usbhsf_tx_irq_ctrl(pipe, 1); + usbhs_pipe_enable(pipe); + + return ret; +} + +static int usbhs_dcp_dir_switch_to_read(struct usbhs_pkt *pkt, int *is_done) +{ + struct usbhs_pipe *pipe = pkt->pipe; + struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe); + struct usbhs_fifo *fifo = usbhsf_get_cfifo(priv); /* CFIFO */ + struct device *dev = usbhs_priv_to_dev(priv); + int ret; + + usbhs_pipe_disable(pipe); + + ret = usbhsf_fifo_select(pipe, fifo, 0); + if (ret < 0) { + dev_err(dev, "%s() fail\n", __func__); + return ret; + } + + usbhs_pipe_sequence_data1(pipe); /* DATA1 */ + usbhsf_fifo_clear(pipe, fifo); + + usbhsf_fifo_unselect(pipe, fifo); + + usbhsf_rx_irq_ctrl(pipe, 1); + usbhs_pipe_enable(pipe); + + return ret; + +} + +static int usbhs_dcp_dir_switch_done(struct usbhs_pkt *pkt, int *is_done) +{ + struct usbhs_pipe *pipe = pkt->pipe; + + if (pkt->handler == &usbhs_dcp_status_stage_in_handler) + usbhsf_tx_irq_ctrl(pipe, 0); + else + usbhsf_rx_irq_ctrl(pipe, 0); + + pkt->actual = pkt->length; + *is_done = 1; + + return 0; +} + +struct usbhs_pkt_handle usbhs_dcp_status_stage_in_handler = { + .prepare = usbhs_dcp_dir_switch_to_write, + .try_run = usbhs_dcp_dir_switch_done, +}; + +struct usbhs_pkt_handle usbhs_dcp_status_stage_out_handler = { + .prepare = usbhs_dcp_dir_switch_to_read, + .try_run = usbhs_dcp_dir_switch_done, +}; + +/* + * DCP data stage (push) + */ +static int usbhsf_dcp_data_stage_try_push(struct usbhs_pkt *pkt, int *is_done) +{ + struct usbhs_pipe *pipe = pkt->pipe; + + usbhs_pipe_sequence_data1(pipe); /* DATA1 */ + + /* + * change handler to PIO push + */ + pkt->handler = &usbhs_fifo_pio_push_handler; + + return pkt->handler->prepare(pkt, is_done); +} + +struct usbhs_pkt_handle usbhs_dcp_data_stage_out_handler = { + .prepare = usbhsf_dcp_data_stage_try_push, +}; + +/* + * DCP data stage (pop) + */ +static int usbhsf_dcp_data_stage_prepare_pop(struct usbhs_pkt *pkt, + int *is_done) +{ + struct usbhs_pipe *pipe = pkt->pipe; + struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe); + struct usbhs_fifo *fifo = usbhsf_get_cfifo(priv); + + if (usbhs_pipe_is_busy(pipe)) + return 0; + + /* + * prepare pop for DCP should + * - change DCP direction, + * - clear fifo + * - DATA1 + */ + usbhs_pipe_disable(pipe); + + usbhs_pipe_sequence_data1(pipe); /* DATA1 */ + + usbhsf_fifo_select(pipe, fifo, 0); + usbhsf_fifo_clear(pipe, fifo); + usbhsf_fifo_unselect(pipe, fifo); + + /* + * change handler to PIO pop + */ + pkt->handler = &usbhs_fifo_pio_pop_handler; + + return pkt->handler->prepare(pkt, is_done); +} + +struct usbhs_pkt_handle usbhs_dcp_data_stage_in_handler = { + .prepare = usbhsf_dcp_data_stage_prepare_pop, +}; + +/* * PIO push handler */ static int usbhsf_pio_try_push(struct usbhs_pkt *pkt, int *is_done) @@ -452,6 +621,20 @@ static int usbhsf_pio_try_pop(struct usbhs_pkt *pkt, int *is_done) total_len = len; /* + * update actual length first here to decide disable pipe. + * if this pipe keeps BUF status and all data were popped, + * then, next interrupt/token will be issued again + */ + pkt->actual += total_len; + + if ((pkt->actual == pkt->length) || /* receive all data */ + (total_len < maxp)) { /* short packet */ + *is_done = 1; + usbhsf_rx_irq_ctrl(pipe, 0); + usbhs_pipe_disable(pipe); /* disable pipe first */ + } + + /* * Buffer clear if Zero-Length packet * * see @@ -481,16 +664,7 @@ static int usbhsf_pio_try_pop(struct usbhs_pkt *pkt, int *is_done) buf[i] = (data >> ((i & 0x03) * 8)) & 0xff; } - pkt->actual += total_len; - usbhs_fifo_read_end: - if ((pkt->actual == pkt->length) || /* receive all data */ - (total_len < maxp)) { /* short packet */ - *is_done = 1; - usbhsf_rx_irq_ctrl(pipe, 0); - usbhs_pipe_disable(pipe); - } - dev_dbg(dev, " recv %d (%d/ %d/ %d/ %d)\n", usbhs_pipe_number(pipe), pkt->length, pkt->actual, *is_done, pkt->zero); @@ -646,7 +820,7 @@ static int usbhsf_dma_prepare_push(struct usbhs_pkt *pkt, int *is_done) if (len % 4) /* 32bit alignment */ goto usbhsf_pio_prepare_push; - if (((u32)pkt->buf + pkt->actual) & 0x7) /* 8byte alignment */ + if ((*(u32 *) pkt->buf + pkt->actual) & 0x7) /* 8byte alignment */ goto usbhsf_pio_prepare_push; /* get enable DMA fifo */ @@ -723,7 +897,7 @@ static int usbhsf_dma_try_pop(struct usbhs_pkt *pkt, int *is_done) if (!fifo) goto usbhsf_pio_prepare_pop; - if (((u32)pkt->buf + pkt->actual) & 0x7) /* 8byte alignment */ + if ((*(u32 *) pkt->buf + pkt->actual) & 0x7) /* 8byte alignment */ goto usbhsf_pio_prepare_pop; ret = usbhsf_fifo_select(pipe, fifo, 0); @@ -884,7 +1058,7 @@ static int usbhsf_irq_empty(struct usbhs_priv *priv, if (!(irq_state->bempsts & (1 << i))) continue; - ret = usbhs_pkt_run(pipe); + ret = usbhsf_pkt_handler(pipe, USBHSF_PKT_TRY_RUN); if (ret < 0) dev_err(dev, "irq_empty run_error %d : %d\n", i, ret); } @@ -914,7 +1088,7 @@ static int usbhsf_irq_ready(struct usbhs_priv *priv, if (!(irq_state->brdysts & (1 << i))) continue; - ret = usbhs_pkt_run(pipe); + ret = usbhsf_pkt_handler(pipe, USBHSF_PKT_TRY_RUN); if (ret < 0) dev_err(dev, "irq_ready run_error %d : %d\n", i, ret); } @@ -929,7 +1103,7 @@ static void usbhsf_dma_complete(void *arg) struct device *dev = usbhs_priv_to_dev(priv); int ret; - ret = usbhs_pkt_dmadone(pipe); + ret = usbhsf_pkt_handler(pipe, USBHSF_PKT_DMA_DONE); if (ret < 0) dev_err(dev, "dma_complete run_error %d : %d\n", usbhs_pipe_number(pipe), ret); diff --git a/drivers/usb/renesas_usbhs/fifo.h b/drivers/usb/renesas_usbhs/fifo.h index ed6d8e56c13c..32a7b246b28d 100644 --- a/drivers/usb/renesas_usbhs/fifo.h +++ b/drivers/usb/renesas_usbhs/fifo.h @@ -51,6 +51,8 @@ struct usbhs_pkt { struct list_head node; struct usbhs_pipe *pipe; struct usbhs_pkt_handle *handler; + void (*done)(struct usbhs_priv *priv, + struct usbhs_pkt *pkt); dma_addr_t dma; void *buf; int length; @@ -76,12 +78,6 @@ void usbhs_fifo_quit(struct usbhs_priv *priv); /* * packet info */ -enum { - USBHSF_PKT_PREPARE, - USBHSF_PKT_TRY_RUN, - USBHSF_PKT_DMA_DONE, -}; - extern struct usbhs_pkt_handle usbhs_fifo_pio_push_handler; extern struct usbhs_pkt_handle usbhs_fifo_pio_pop_handler; extern struct usbhs_pkt_handle usbhs_ctrl_stage_end_handler; @@ -89,16 +85,18 @@ extern struct usbhs_pkt_handle usbhs_ctrl_stage_end_handler; extern struct usbhs_pkt_handle usbhs_fifo_dma_push_handler; extern struct usbhs_pkt_handle usbhs_fifo_dma_pop_handler; +extern struct usbhs_pkt_handle usbhs_dcp_status_stage_in_handler; +extern struct usbhs_pkt_handle usbhs_dcp_status_stage_out_handler; + +extern struct usbhs_pkt_handle usbhs_dcp_data_stage_in_handler; +extern struct usbhs_pkt_handle usbhs_dcp_data_stage_out_handler; void usbhs_pkt_init(struct usbhs_pkt *pkt); void usbhs_pkt_push(struct usbhs_pipe *pipe, struct usbhs_pkt *pkt, - struct usbhs_pkt_handle *handler, + void (*done)(struct usbhs_priv *priv, + struct usbhs_pkt *pkt), void *buf, int len, int zero); struct usbhs_pkt *usbhs_pkt_pop(struct usbhs_pipe *pipe, struct usbhs_pkt *pkt); -int __usbhs_pkt_handler(struct usbhs_pipe *pipe, int type); - -#define usbhs_pkt_start(p) __usbhs_pkt_handler(p, USBHSF_PKT_PREPARE) -#define usbhs_pkt_run(p) __usbhs_pkt_handler(p, USBHSF_PKT_TRY_RUN) -#define usbhs_pkt_dmadone(p) __usbhs_pkt_handler(p, USBHSF_PKT_DMA_DONE) +void usbhs_pkt_start(struct usbhs_pipe *pipe); #endif /* RENESAS_USB_FIFO_H */ diff --git a/drivers/usb/renesas_usbhs/mod.c b/drivers/usb/renesas_usbhs/mod.c index a577f8f4064c..053f86d70009 100644 --- a/drivers/usb/renesas_usbhs/mod.c +++ b/drivers/usb/renesas_usbhs/mod.c @@ -58,7 +58,7 @@ void usbhs_mod_autonomy_mode(struct usbhs_priv *priv) struct usbhs_mod_info *info = usbhs_priv_to_modinfo(priv); info->irq_vbus = usbhsm_autonomy_irq_vbus; - priv->pfunc->get_vbus = usbhsm_autonomy_get_vbus; + priv->pfunc.get_vbus = usbhsm_autonomy_get_vbus; usbhs_irq_callback_update(priv, NULL); } @@ -93,8 +93,9 @@ struct usbhs_mod *usbhs_mod_get(struct usbhs_priv *priv, int id) return ret; } -int usbhs_mod_is_host(struct usbhs_priv *priv, struct usbhs_mod *mod) +int usbhs_mod_is_host(struct usbhs_priv *priv) { + struct usbhs_mod *mod = usbhs_mod_get_current(priv); struct usbhs_mod_info *info = usbhs_priv_to_modinfo(priv); if (!mod) @@ -139,13 +140,17 @@ int usbhs_mod_probe(struct usbhs_priv *priv) /* * install host/gadget driver */ - ret = usbhs_mod_gadget_probe(priv); + ret = usbhs_mod_host_probe(priv); if (ret < 0) return ret; + ret = usbhs_mod_gadget_probe(priv); + if (ret < 0) + goto mod_init_host_err; + /* irq settings */ ret = request_irq(priv->irq, usbhs_interrupt, - IRQF_DISABLED, dev_name(dev), priv); + 0, dev_name(dev), priv); if (ret) { dev_err(dev, "irq request err\n"); goto mod_init_gadget_err; @@ -155,12 +160,15 @@ int usbhs_mod_probe(struct usbhs_priv *priv) mod_init_gadget_err: usbhs_mod_gadget_remove(priv); +mod_init_host_err: + usbhs_mod_host_remove(priv); return ret; } void usbhs_mod_remove(struct usbhs_priv *priv) { + usbhs_mod_host_remove(priv); usbhs_mod_gadget_remove(priv); free_irq(priv->irq, priv); } @@ -168,20 +176,6 @@ void usbhs_mod_remove(struct usbhs_priv *priv) /* * status functions */ -int usbhs_status_get_usb_speed(struct usbhs_irq_state *irq_state) -{ - switch (irq_state->dvstctr & RHST) { - case RHST_LOW_SPEED: - return USB_SPEED_LOW; - case RHST_FULL_SPEED: - return USB_SPEED_FULL; - case RHST_HIGH_SPEED: - return USB_SPEED_HIGH; - } - - return USB_SPEED_UNKNOWN; -} - int usbhs_status_get_device_state(struct usbhs_irq_state *irq_state) { int state = irq_state->intsts0 & DVSQ_MASK; @@ -221,8 +215,6 @@ static void usbhs_status_get_each_irq(struct usbhs_priv *priv, state->intsts0 = usbhs_read(priv, INTSTS0); state->intsts1 = usbhs_read(priv, INTSTS1); - state->dvstctr = usbhs_read(priv, DVSTCTR); - /* mask */ if (mod) { state->brdysts = usbhs_read(priv, BRDYSTS); @@ -269,6 +261,8 @@ static irqreturn_t usbhs_interrupt(int irq, void *data) * see also * usbhs_irq_setting_update */ + + /* INTSTS0 */ if (irq_state.intsts0 & VBINT) usbhs_mod_info_call(priv, irq_vbus, priv, &irq_state); @@ -284,15 +278,38 @@ static irqreturn_t usbhs_interrupt(int irq, void *data) if (irq_state.intsts0 & BRDY) usbhs_mod_call(priv, irq_ready, priv, &irq_state); + /* INTSTS1 */ + if (irq_state.intsts1 & ATTCH) + usbhs_mod_call(priv, irq_attch, priv, &irq_state); + + if (irq_state.intsts1 & DTCH) + usbhs_mod_call(priv, irq_dtch, priv, &irq_state); + + if (irq_state.intsts1 & SIGN) + usbhs_mod_call(priv, irq_sign, priv, &irq_state); + + if (irq_state.intsts1 & SACK) + usbhs_mod_call(priv, irq_sack, priv, &irq_state); + return IRQ_HANDLED; } void usbhs_irq_callback_update(struct usbhs_priv *priv, struct usbhs_mod *mod) { u16 intenb0 = 0; + u16 intenb1 = 0; struct usbhs_mod_info *info = usbhs_priv_to_modinfo(priv); + /* + * BEMPENB/BRDYENB are picky. + * below method is required + * + * - clear INTSTS0 + * - update BEMPENB/BRDYENB + * - update INTSTS0 + */ usbhs_write(priv, INTENB0, 0); + usbhs_write(priv, INTENB1, 0); usbhs_write(priv, BEMPENB, 0); usbhs_write(priv, BRDYENB, 0); @@ -310,6 +327,9 @@ void usbhs_irq_callback_update(struct usbhs_priv *priv, struct usbhs_mod *mod) intenb0 |= VBSE; if (mod) { + /* + * INTSTS0 + */ if (mod->irq_ctrl_stage) intenb0 |= CTRE; @@ -322,7 +342,26 @@ void usbhs_irq_callback_update(struct usbhs_priv *priv, struct usbhs_mod *mod) usbhs_write(priv, BRDYENB, mod->irq_brdysts); intenb0 |= BRDYE; } + + /* + * INTSTS1 + */ + if (mod->irq_attch) + intenb1 |= ATTCHE; + + if (mod->irq_attch) + intenb1 |= DTCHE; + + if (mod->irq_sign) + intenb1 |= SIGNE; + + if (mod->irq_sack) + intenb1 |= SACKE; } - usbhs_write(priv, INTENB0, intenb0); + if (intenb0) + usbhs_write(priv, INTENB0, intenb0); + + if (intenb1) + usbhs_write(priv, INTENB1, intenb1); } diff --git a/drivers/usb/renesas_usbhs/mod.h b/drivers/usb/renesas_usbhs/mod.h index 5c845a28a21c..8ae3733031cd 100644 --- a/drivers/usb/renesas_usbhs/mod.h +++ b/drivers/usb/renesas_usbhs/mod.h @@ -30,7 +30,6 @@ struct usbhs_irq_state { u16 brdysts; u16 nrdysts; u16 bempsts; - u16 dvstctr; }; struct usbhs_mod { @@ -42,26 +41,48 @@ struct usbhs_mod { int (*start)(struct usbhs_priv *priv); int (*stop)(struct usbhs_priv *priv); - /* INTSTS0 :: DVST (DVSQ) */ + /* + * INTSTS0 + */ + + /* DVST (DVSQ) */ int (*irq_dev_state)(struct usbhs_priv *priv, struct usbhs_irq_state *irq_state); - /* INTSTS0 :: CTRT (CTSQ) */ + /* CTRT (CTSQ) */ int (*irq_ctrl_stage)(struct usbhs_priv *priv, struct usbhs_irq_state *irq_state); - /* INTSTS0 :: BEMP */ - /* BEMPSTS */ + /* BEMP / BEMPSTS */ int (*irq_empty)(struct usbhs_priv *priv, struct usbhs_irq_state *irq_state); u16 irq_bempsts; - /* INTSTS0 :: BRDY */ - /* BRDYSTS */ + /* BRDY / BRDYSTS */ int (*irq_ready)(struct usbhs_priv *priv, struct usbhs_irq_state *irq_state); u16 irq_brdysts; + /* + * INTSTS1 + */ + + /* ATTCHE */ + int (*irq_attch)(struct usbhs_priv *priv, + struct usbhs_irq_state *irq_state); + + /* DTCHE */ + int (*irq_dtch)(struct usbhs_priv *priv, + struct usbhs_irq_state *irq_state); + + /* SIGN */ + int (*irq_sign)(struct usbhs_priv *priv, + struct usbhs_irq_state *irq_state); + + /* SACK */ + int (*irq_sack)(struct usbhs_priv *priv, + struct usbhs_irq_state *irq_state); + struct usbhs_priv *priv; }; @@ -89,7 +110,7 @@ struct usbhs_mod_info { struct usbhs_mod *usbhs_mod_get(struct usbhs_priv *priv, int id); struct usbhs_mod *usbhs_mod_get_current(struct usbhs_priv *priv); void usbhs_mod_register(struct usbhs_priv *priv, struct usbhs_mod *usb, int id); -int usbhs_mod_is_host(struct usbhs_priv *priv, struct usbhs_mod *mod); +int usbhs_mod_is_host(struct usbhs_priv *priv); int usbhs_mod_change(struct usbhs_priv *priv, int id); int usbhs_mod_probe(struct usbhs_priv *priv); void usbhs_mod_remove(struct usbhs_priv *priv); @@ -99,7 +120,6 @@ void usbhs_mod_autonomy_mode(struct usbhs_priv *priv); /* * status functions */ -int usbhs_status_get_usb_speed(struct usbhs_irq_state *irq_state); int usbhs_status_get_device_state(struct usbhs_irq_state *irq_state); int usbhs_status_get_ctrl_stage(struct usbhs_irq_state *irq_state); @@ -119,9 +139,24 @@ void usbhs_irq_callback_update(struct usbhs_priv *priv, struct usbhs_mod *mod); }) /* - * gadget control + * host / gadget control */ -#ifdef CONFIG_USB_RENESAS_USBHS_UDC +#if defined(CONFIG_USB_RENESAS_USBHS_HCD) || \ + defined(CONFIG_USB_RENESAS_USBHS_HCD_MODULE) +extern int __devinit usbhs_mod_host_probe(struct usbhs_priv *priv); +extern int __devexit usbhs_mod_host_remove(struct usbhs_priv *priv); +#else +static inline int usbhs_mod_host_probe(struct usbhs_priv *priv) +{ + return 0; +} +static inline void usbhs_mod_host_remove(struct usbhs_priv *priv) +{ +} +#endif + +#if defined(CONFIG_USB_RENESAS_USBHS_UDC) || \ + defined(CONFIG_USB_RENESAS_USBHS_UDC_MODULE) extern int __devinit usbhs_mod_gadget_probe(struct usbhs_priv *priv); extern void __devexit usbhs_mod_gadget_remove(struct usbhs_priv *priv); #else diff --git a/drivers/usb/renesas_usbhs/mod_gadget.c b/drivers/usb/renesas_usbhs/mod_gadget.c index cb2d451d511e..4cc7ee0babc6 100644 --- a/drivers/usb/renesas_usbhs/mod_gadget.c +++ b/drivers/usb/renesas_usbhs/mod_gadget.c @@ -39,7 +39,6 @@ struct usbhsg_uep { char ep_name[EP_NAME_SIZE]; struct usbhsg_gpriv *gpriv; - struct usbhs_pkt_handle *handler; }; struct usbhsg_gpriv { @@ -128,25 +127,6 @@ LIST_HEAD(the_controller_link); /* * queue push/pop */ -static void usbhsg_queue_push(struct usbhsg_uep *uep, - struct usbhsg_request *ureq) -{ - struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep); - struct device *dev = usbhsg_gpriv_to_dev(gpriv); - struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep); - struct usbhs_pkt *pkt = usbhsg_ureq_to_pkt(ureq); - struct usb_request *req = &ureq->req; - - req->actual = 0; - req->status = -EINPROGRESS; - usbhs_pkt_push(pipe, pkt, uep->handler, - req->buf, req->length, req->zero); - - dev_dbg(dev, "pipe %d : queue push (%d)\n", - usbhs_pipe_number(pipe), - req->length); -} - static void usbhsg_queue_pop(struct usbhsg_uep *uep, struct usbhsg_request *ureq, int status) @@ -161,7 +141,7 @@ static void usbhsg_queue_pop(struct usbhsg_uep *uep, ureq->req.complete(&uep->ep, &ureq->req); } -static void usbhsg_queue_done(struct usbhs_pkt *pkt) +static void usbhsg_queue_done(struct usbhs_priv *priv, struct usbhs_pkt *pkt) { struct usbhs_pipe *pipe = pkt->pipe; struct usbhsg_uep *uep = usbhsg_pipe_to_uep(pipe); @@ -172,6 +152,26 @@ static void usbhsg_queue_done(struct usbhs_pkt *pkt) usbhsg_queue_pop(uep, ureq, 0); } +static void usbhsg_queue_push(struct usbhsg_uep *uep, + struct usbhsg_request *ureq) +{ + struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep); + struct device *dev = usbhsg_gpriv_to_dev(gpriv); + struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep); + struct usbhs_pkt *pkt = usbhsg_ureq_to_pkt(ureq); + struct usb_request *req = &ureq->req; + + req->actual = 0; + req->status = -EINPROGRESS; + usbhs_pkt_push(pipe, pkt, usbhsg_queue_done, + req->buf, req->length, req->zero); + usbhs_pkt_start(pipe); + + dev_dbg(dev, "pipe %d : queue push (%d)\n", + usbhs_pipe_number(pipe), + req->length); +} + /* * dma map/unmap */ @@ -265,7 +265,7 @@ static int usbhsg_recip_handler_std_clear_endpoint(struct usbhs_priv *priv, if (!usbhsg_status_has(gpriv, USBHSG_STATUS_WEDGE)) { usbhs_pipe_disable(pipe); - usbhs_pipe_clear_sequence(pipe); + usbhs_pipe_sequence_data0(pipe); usbhs_pipe_enable(pipe); } @@ -355,7 +355,7 @@ static int usbhsg_irq_dev_state(struct usbhs_priv *priv, struct usbhsg_gpriv *gpriv = usbhsg_priv_to_gpriv(priv); struct device *dev = usbhsg_gpriv_to_dev(gpriv); - gpriv->gadget.speed = usbhs_status_get_usb_speed(irq_state); + gpriv->gadget.speed = usbhs_bus_get_speed(priv); dev_dbg(dev, "state = %x : speed : %d\n", usbhs_status_get_device_state(irq_state), @@ -389,13 +389,13 @@ static int usbhsg_irq_ctrl_stage(struct usbhs_priv *priv, switch (stage) { case READ_DATA_STAGE: - dcp->handler = &usbhs_fifo_pio_push_handler; + pipe->handler = &usbhs_fifo_pio_push_handler; break; case WRITE_DATA_STAGE: - dcp->handler = &usbhs_fifo_pio_pop_handler; + pipe->handler = &usbhs_fifo_pio_pop_handler; break; case NODATA_STATUS_STAGE: - dcp->handler = &usbhs_ctrl_stage_end_handler; + pipe->handler = &usbhs_ctrl_stage_end_handler; break; default: return ret; @@ -479,24 +479,31 @@ static int usbhsg_ep_enable(struct usb_ep *ep, */ if (uep->pipe) { usbhs_pipe_clear(uep->pipe); - usbhs_pipe_clear_sequence(uep->pipe); + usbhs_pipe_sequence_data0(uep->pipe); return 0; } - pipe = usbhs_pipe_malloc(priv, desc); + pipe = usbhs_pipe_malloc(priv, + usb_endpoint_type(desc), + usb_endpoint_dir_in(desc)); if (pipe) { uep->pipe = pipe; pipe->mod_private = uep; + /* set epnum / maxp */ + usbhs_pipe_config_update(pipe, 0, + usb_endpoint_num(desc), + usb_endpoint_maxp(desc)); + /* * usbhs_fifo_dma_push/pop_handler try to * use dmaengine if possible. * It will use pio handler if impossible. */ if (usb_endpoint_dir_in(desc)) - uep->handler = &usbhs_fifo_dma_push_handler; + pipe->handler = &usbhs_fifo_dma_push_handler; else - uep->handler = &usbhs_fifo_dma_pop_handler; + pipe->handler = &usbhs_fifo_dma_pop_handler; ret = 0; } @@ -659,7 +666,6 @@ static int usbhsg_try_start(struct usbhs_priv *priv, u32 status) * pipe initialize and enable DCP */ usbhs_pipe_init(priv, - usbhsg_queue_done, usbhsg_dma_map_ctrl); usbhs_fifo_init(priv); usbhsg_uep_init(gpriv); @@ -667,6 +673,7 @@ static int usbhsg_try_start(struct usbhs_priv *priv, u32 status) /* dcp init */ dcp->pipe = usbhs_dcp_malloc(priv); dcp->pipe->mod_private = dcp; + usbhs_pipe_config_update(dcp->pipe, 0, 0, 64); /* * system config enble @@ -730,10 +737,6 @@ static int usbhsg_try_stop(struct usbhs_priv *priv, u32 status) usbhsg_pipe_disable(dcp); - if (gpriv->driver && - gpriv->driver->disconnect) - gpriv->driver->disconnect(&gpriv->gadget); - dev_dbg(dev, "stop gadget\n"); return 0; @@ -744,31 +747,19 @@ static int usbhsg_try_stop(struct usbhs_priv *priv, u32 status) * linux usb function * */ -static int usbhsg_gadget_start(struct usb_gadget_driver *driver, - int (*bind)(struct usb_gadget *)) +static int usbhsg_gadget_start(struct usb_gadget *gadget, + struct usb_gadget_driver *driver) { - struct usbhsg_gpriv *gpriv; + struct usbhsg_gpriv *gpriv = usbhsg_gadget_to_gpriv(gadget); struct usbhs_priv *priv; struct device *dev; int ret; - if (!bind || - !driver || + if (!driver || !driver->setup || driver->speed != USB_SPEED_HIGH) return -EINVAL; - /* - * find unused controller - */ - usbhsg_for_each_controller(gpriv) { - if (!gpriv->driver) - goto find_unused_controller; - } - return -ENODEV; - -find_unused_controller: - dev = usbhsg_gpriv_to_dev(gpriv); priv = usbhsg_gpriv_to_priv(gpriv); @@ -782,19 +773,8 @@ find_unused_controller: goto add_fail; } - ret = bind(&gpriv->gadget); - if (ret) { - dev_err(dev, "bind to driver %s error %d\n", - driver->driver.name, ret); - goto bind_fail; - } - - dev_dbg(dev, "bind %s\n", driver->driver.name); - return usbhsg_try_start(priv, USBHSG_STATUS_REGISTERD); -bind_fail: - device_del(&gpriv->gadget.dev); add_fail: gpriv->driver = NULL; gpriv->gadget.dev.driver = NULL; @@ -802,9 +782,10 @@ add_fail: return ret; } -static int usbhsg_gadget_stop(struct usb_gadget_driver *driver) +static int usbhsg_gadget_stop(struct usb_gadget *gadget, + struct usb_gadget_driver *driver) { - struct usbhsg_gpriv *gpriv; + struct usbhsg_gpriv *gpriv = usbhsg_gadget_to_gpriv(gadget); struct usbhs_priv *priv; struct device *dev; @@ -812,17 +793,6 @@ static int usbhsg_gadget_stop(struct usb_gadget_driver *driver) !driver->unbind) return -EINVAL; - /* - * find controller - */ - usbhsg_for_each_controller(gpriv) { - if (gpriv->driver == driver) - goto find_matching_controller; - } - return -ENODEV; - -find_matching_controller: - dev = usbhsg_gpriv_to_dev(gpriv); priv = usbhsg_gpriv_to_priv(gpriv); @@ -830,12 +800,6 @@ find_matching_controller: device_del(&gpriv->gadget.dev); gpriv->driver = NULL; - if (driver->disconnect) - driver->disconnect(&gpriv->gadget); - - driver->unbind(&gpriv->gadget); - dev_dbg(dev, "unbind %s\n", driver->driver.name); - return 0; } @@ -852,8 +816,8 @@ static int usbhsg_get_frame(struct usb_gadget *gadget) static struct usb_gadget_ops usbhsg_gadget_ops = { .get_frame = usbhsg_get_frame, - .start = usbhsg_gadget_start, - .stop = usbhsg_gadget_stop, + .udc_start = usbhsg_gadget_start, + .udc_stop = usbhsg_gadget_stop, }; static int usbhsg_start(struct usbhs_priv *priv) diff --git a/drivers/usb/renesas_usbhs/mod_host.c b/drivers/usb/renesas_usbhs/mod_host.c new file mode 100644 index 000000000000..1a7208a50afc --- /dev/null +++ b/drivers/usb/renesas_usbhs/mod_host.c @@ -0,0 +1,1312 @@ +/* + * Renesas USB driver + * + * Copyright (C) 2011 Renesas Solutions Corp. + * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ +#include <linux/io.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/usb.h> +#include <linux/usb/hcd.h> +#include "common.h" + +/* + *** HARDWARE LIMITATION *** + * + * 1) renesas_usbhs has a limited number of controllable devices. + * it can control only 9 devices in generally. + * see DEVADDn / DCPMAXP / PIPEMAXP. + * + * 2) renesas_usbhs pipe number is limited. + * the pipe will be re-used for each devices. + * so, software should control DATA0/1 sequence of each devices. + */ + + +/* + * image of mod_host + * + * +--------+ + * | udev 0 | --> it is used when set address + * +--------+ + * + * +--------+ pipes are reused for each uep. + * | udev 1 |-+- [uep 0 (dcp) ] --+ pipe will be switched when + * +--------+ | | target device was changed + * +- [uep 1 (bulk)] --|---+ +--------------+ + * | +--------------> | pipe0 (dcp) | + * +- [uep 2 (bulk)] --|---|---+ +--------------+ + * | | | | pipe1 (isoc) | + * +--------+ | | | +--------------+ + * | udev 2 |-+- [uep 0 (dcp) ] --+ +-- |------> | pipe2 (bulk) | + * +--------+ | | | | +--------------+ + * +- [uep 1 (int) ] --|-+ | +------> | pipe3 (bulk) | + * | | | | +--------------+ + * +--------+ | +-|---|------> | pipe4 (int) | + * | udev 3 |-+- [uep 0 (dcp) ] --+ | | +--------------+ + * +--------+ | | | | .... | + * +- [uep 1 (bulk)] ------+ | | .... | + * | | + * +- [uep 2 (bulk)]-----------+ + */ + + +/* + * struct + */ +struct usbhsh_pipe_info { + unsigned int usr_cnt; /* see usbhsh_endpoint_alloc() */ +}; + +struct usbhsh_request { + struct urb *urb; + struct usbhs_pkt pkt; + struct list_head ureq_link; /* see hpriv :: ureq_link_xxx */ +}; + +struct usbhsh_device { + struct usb_device *usbv; + struct list_head ep_list_head; /* list of usbhsh_ep */ +}; + +struct usbhsh_ep { + struct usbhs_pipe *pipe; + struct usbhsh_device *udev; /* attached udev */ + struct list_head ep_list; /* list to usbhsh_device */ + + int maxp; +}; + +#define USBHSH_DEVICE_MAX 10 /* see DEVADDn / DCPMAXP / PIPEMAXP */ +#define USBHSH_PORT_MAX 7 /* see DEVADDn :: HUBPORT */ +struct usbhsh_hpriv { + struct usbhs_mod mod; + struct usbhs_pipe *dcp; + + struct usbhsh_device udev[USBHSH_DEVICE_MAX]; + + struct usbhsh_pipe_info *pipe_info; + int pipe_size; + + u32 port_stat; /* USB_PORT_STAT_xxx */ + + struct completion *done; + + /* see usbhsh_req_alloc/free */ + struct list_head ureq_link_active; + struct list_head ureq_link_free; +}; + + +static const char usbhsh_hcd_name[] = "renesas_usbhs host"; + +/* + * macro + */ +#define usbhsh_priv_to_hpriv(priv) \ + container_of(usbhs_mod_get(priv, USBHS_HOST), struct usbhsh_hpriv, mod) + +#define __usbhsh_for_each_hpipe(start, pos, h, i) \ + for (i = start, pos = (h)->hpipe + i; \ + i < (h)->hpipe_size; \ + i++, pos = (h)->hpipe + i) + +#define usbhsh_for_each_hpipe(pos, hpriv, i) \ + __usbhsh_for_each_hpipe(1, pos, hpriv, i) + +#define usbhsh_for_each_hpipe_with_dcp(pos, hpriv, i) \ + __usbhsh_for_each_hpipe(0, pos, hpriv, i) + +#define __usbhsh_for_each_udev(start, pos, h, i) \ + for (i = start, pos = (h)->udev + i; \ + i < USBHSH_DEVICE_MAX; \ + i++, pos = (h)->udev + i) + +#define usbhsh_for_each_udev(pos, hpriv, i) \ + __usbhsh_for_each_udev(1, pos, hpriv, i) + +#define usbhsh_for_each_udev_with_dev0(pos, hpriv, i) \ + __usbhsh_for_each_udev(0, pos, hpriv, i) + +#define usbhsh_hcd_to_hpriv(h) (struct usbhsh_hpriv *)((h)->hcd_priv) +#define usbhsh_hcd_to_dev(h) ((h)->self.controller) + +#define usbhsh_hpriv_to_priv(h) ((h)->mod.priv) +#define usbhsh_hpriv_to_dcp(h) ((h)->dcp) +#define usbhsh_hpriv_to_hcd(h) \ + container_of((void *)h, struct usb_hcd, hcd_priv) + +#define usbhsh_ep_to_uep(u) ((u)->hcpriv) +#define usbhsh_uep_to_pipe(u) ((u)->pipe) +#define usbhsh_uep_to_udev(u) ((u)->udev) +#define usbhsh_urb_to_ureq(u) ((u)->hcpriv) +#define usbhsh_urb_to_usbv(u) ((u)->dev) + +#define usbhsh_usbv_to_udev(d) dev_get_drvdata(&(d)->dev) + +#define usbhsh_udev_to_usbv(h) ((h)->usbv) + +#define usbhsh_pipe_info(p) ((p)->mod_private) + +#define usbhsh_device_number(h, d) ((int)((d) - (h)->udev)) +#define usbhsh_device_nth(h, d) ((h)->udev + d) +#define usbhsh_device0(h) usbhsh_device_nth(h, 0) + +#define usbhsh_port_stat_init(h) ((h)->port_stat = 0) +#define usbhsh_port_stat_set(h, s) ((h)->port_stat |= (s)) +#define usbhsh_port_stat_clear(h, s) ((h)->port_stat &= ~(s)) +#define usbhsh_port_stat_get(h) ((h)->port_stat) + +#define usbhsh_pkt_to_req(p) \ + container_of((void *)p, struct usbhsh_request, pkt) + +/* + * req alloc/free + */ +static void usbhsh_req_list_init(struct usbhsh_hpriv *hpriv) +{ + INIT_LIST_HEAD(&hpriv->ureq_link_active); + INIT_LIST_HEAD(&hpriv->ureq_link_free); +} + +static void usbhsh_req_list_quit(struct usbhsh_hpriv *hpriv) +{ + struct usb_hcd *hcd = usbhsh_hpriv_to_hcd(hpriv); + struct device *dev = usbhsh_hcd_to_dev(hcd); + struct usbhsh_request *ureq, *next; + + /* kfree all active ureq */ + list_for_each_entry_safe(ureq, next, + &hpriv->ureq_link_active, + ureq_link) { + dev_err(dev, "active ureq (%p) is force freed\n", ureq); + kfree(ureq); + } + + /* kfree all free ureq */ + list_for_each_entry_safe(ureq, next, &hpriv->ureq_link_free, ureq_link) + kfree(ureq); +} + +static struct usbhsh_request *usbhsh_req_alloc(struct usbhsh_hpriv *hpriv, + struct urb *urb, + gfp_t mem_flags) +{ + struct usbhsh_request *ureq; + struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv); + struct device *dev = usbhs_priv_to_dev(priv); + + if (list_empty(&hpriv->ureq_link_free)) { + /* + * create new one if there is no free ureq + */ + ureq = kzalloc(sizeof(struct usbhsh_request), mem_flags); + if (ureq) + INIT_LIST_HEAD(&ureq->ureq_link); + } else { + /* + * reuse "free" ureq if exist + */ + ureq = list_entry(hpriv->ureq_link_free.next, + struct usbhsh_request, + ureq_link); + if (ureq) + list_del_init(&ureq->ureq_link); + } + + if (!ureq) { + dev_err(dev, "ureq alloc fail\n"); + return NULL; + } + + usbhs_pkt_init(&ureq->pkt); + + /* + * push it to "active" list + */ + list_add_tail(&ureq->ureq_link, &hpriv->ureq_link_active); + ureq->urb = urb; + + return ureq; +} + +static void usbhsh_req_free(struct usbhsh_hpriv *hpriv, + struct usbhsh_request *ureq) +{ + struct usbhs_pkt *pkt = &ureq->pkt; + + usbhs_pkt_init(pkt); + + /* + * removed from "active" list, + * and push it to "free" list + */ + ureq->urb = NULL; + list_del_init(&ureq->ureq_link); + list_add_tail(&ureq->ureq_link, &hpriv->ureq_link_free); +} + +/* + * device control + */ + +static int usbhsh_device_has_endpoint(struct usbhsh_device *udev) +{ + return !list_empty(&udev->ep_list_head); +} + +static struct usbhsh_device *usbhsh_device_alloc(struct usbhsh_hpriv *hpriv, + struct urb *urb) +{ + struct usbhsh_device *udev = NULL; + struct usb_hcd *hcd = usbhsh_hpriv_to_hcd(hpriv); + struct device *dev = usbhsh_hcd_to_dev(hcd); + struct usb_device *usbv = usbhsh_urb_to_usbv(urb); + struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv); + int i; + + /* + * device 0 + */ + if (0 == usb_pipedevice(urb->pipe)) { + udev = usbhsh_device0(hpriv); + goto usbhsh_device_find; + } + + /* + * find unused device + */ + usbhsh_for_each_udev(udev, hpriv, i) { + if (usbhsh_udev_to_usbv(udev)) + continue; + goto usbhsh_device_find; + } + + dev_err(dev, "no free usbhsh_device\n"); + + return NULL; + +usbhsh_device_find: + if (usbhsh_device_has_endpoint(udev)) + dev_warn(dev, "udev have old endpoint\n"); + + /* uep will be attached */ + INIT_LIST_HEAD(&udev->ep_list_head); + + /* + * usbhsh_usbv_to_udev() + * usbhsh_udev_to_usbv() + * will be enable + */ + dev_set_drvdata(&usbv->dev, udev); + udev->usbv = usbv; + + /* set device config */ + usbhs_set_device_speed(priv, + usbhsh_device_number(hpriv, udev), + usbhsh_device_number(hpriv, udev), + 0, /* FIXME no parent */ + usbv->speed); + + dev_dbg(dev, "%s [%d](%p)\n", __func__, + usbhsh_device_number(hpriv, udev), udev); + + return udev; +} + +static void usbhsh_device_free(struct usbhsh_hpriv *hpriv, + struct usbhsh_device *udev) +{ + struct usb_hcd *hcd = usbhsh_hpriv_to_hcd(hpriv); + struct device *dev = usbhsh_hcd_to_dev(hcd); + struct usb_device *usbv = usbhsh_udev_to_usbv(udev); + + dev_dbg(dev, "%s [%d](%p)\n", __func__, + usbhsh_device_number(hpriv, udev), udev); + + if (usbhsh_device_has_endpoint(udev)) + dev_warn(dev, "udev still have endpoint\n"); + + /* + * usbhsh_usbv_to_udev() + * usbhsh_udev_to_usbv() + * will be disable + */ + dev_set_drvdata(&usbv->dev, NULL); + udev->usbv = NULL; +} + +/* + * end-point control + */ +struct usbhsh_ep *usbhsh_endpoint_alloc(struct usbhsh_hpriv *hpriv, + struct usbhsh_device *udev, + struct usb_host_endpoint *ep, + gfp_t mem_flags) +{ + struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv); + struct usb_hcd *hcd = usbhsh_hpriv_to_hcd(hpriv); + struct usbhsh_ep *uep; + struct usbhsh_pipe_info *info; + struct usbhs_pipe *pipe, *best_pipe; + struct device *dev = usbhsh_hcd_to_dev(hcd); + struct usb_endpoint_descriptor *desc = &ep->desc; + int type, i; + unsigned int min_usr; + + uep = kzalloc(sizeof(struct usbhsh_ep), mem_flags); + if (!uep) { + dev_err(dev, "usbhsh_ep alloc fail\n"); + return NULL; + } + type = usb_endpoint_type(desc); + + /* + * find best pipe for endpoint + * see + * HARDWARE LIMITATION + */ + min_usr = ~0; + best_pipe = NULL; + usbhs_for_each_pipe_with_dcp(pipe, priv, i) { + if (!usbhs_pipe_type_is(pipe, type)) + continue; + + info = usbhsh_pipe_info(pipe); + + if (min_usr > info->usr_cnt) { + min_usr = info->usr_cnt; + best_pipe = pipe; + } + } + + if (unlikely(!best_pipe)) { + dev_err(dev, "couldn't find best pipe\n"); + kfree(uep); + return NULL; + } + + /* + * init uep + */ + uep->pipe = best_pipe; + uep->maxp = usb_endpoint_maxp(desc); + usbhsh_uep_to_udev(uep) = udev; + usbhsh_ep_to_uep(ep) = uep; + + /* + * update pipe user count + */ + info = usbhsh_pipe_info(best_pipe); + info->usr_cnt++; + + /* init this endpoint, and attach it to udev */ + INIT_LIST_HEAD(&uep->ep_list); + list_add_tail(&uep->ep_list, &udev->ep_list_head); + + /* + * usbhs_pipe_config_update() should be called after + * usbhs_device_config() + * see + * DCPMAXP/PIPEMAXP + */ + usbhs_pipe_config_update(uep->pipe, + usbhsh_device_number(hpriv, udev), + usb_endpoint_num(desc), + uep->maxp); + + dev_dbg(dev, "%s [%d-%s](%p)\n", __func__, + usbhsh_device_number(hpriv, udev), + usbhs_pipe_name(pipe), uep); + + return uep; +} + +void usbhsh_endpoint_free(struct usbhsh_hpriv *hpriv, + struct usb_host_endpoint *ep) +{ + struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv); + struct device *dev = usbhs_priv_to_dev(priv); + struct usbhsh_ep *uep = usbhsh_ep_to_uep(ep); + struct usbhsh_pipe_info *info; + + if (!uep) + return; + + dev_dbg(dev, "%s [%d-%s](%p)\n", __func__, + usbhsh_device_number(hpriv, usbhsh_uep_to_udev(uep)), + usbhs_pipe_name(uep->pipe), uep); + + info = usbhsh_pipe_info(uep->pipe); + info->usr_cnt--; + + /* remove this endpoint from udev */ + list_del_init(&uep->ep_list); + + usbhsh_uep_to_udev(uep) = NULL; + usbhsh_ep_to_uep(ep) = NULL; + + kfree(uep); +} + +/* + * queue push/pop + */ +static void usbhsh_queue_done(struct usbhs_priv *priv, struct usbhs_pkt *pkt) +{ + struct usbhsh_request *ureq = usbhsh_pkt_to_req(pkt); + struct usbhsh_hpriv *hpriv = usbhsh_priv_to_hpriv(priv); + struct usb_hcd *hcd = usbhsh_hpriv_to_hcd(hpriv); + struct urb *urb = ureq->urb; + struct device *dev = usbhs_priv_to_dev(priv); + + dev_dbg(dev, "%s\n", __func__); + + if (!urb) { + dev_warn(dev, "pkt doesn't have urb\n"); + return; + } + + urb->actual_length = pkt->actual; + usbhsh_req_free(hpriv, ureq); + usbhsh_urb_to_ureq(urb) = NULL; + + usb_hcd_unlink_urb_from_ep(hcd, urb); + usb_hcd_giveback_urb(hcd, urb, 0); +} + +static int usbhsh_queue_push(struct usb_hcd *hcd, + struct usbhs_pipe *pipe, + struct urb *urb) +{ + struct usbhsh_request *ureq = usbhsh_urb_to_ureq(urb); + struct usbhs_pkt *pkt = &ureq->pkt; + struct device *dev = usbhsh_hcd_to_dev(hcd); + void *buf; + int len; + + if (usb_pipeisoc(urb->pipe)) { + dev_err(dev, "pipe iso is not supported now\n"); + return -EIO; + } + + if (usb_pipein(urb->pipe)) + pipe->handler = &usbhs_fifo_pio_pop_handler; + else + pipe->handler = &usbhs_fifo_pio_push_handler; + + buf = (void *)(urb->transfer_buffer + urb->actual_length); + len = urb->transfer_buffer_length - urb->actual_length; + + dev_dbg(dev, "%s\n", __func__); + usbhs_pkt_push(pipe, pkt, usbhsh_queue_done, + buf, len, (urb->transfer_flags & URB_ZERO_PACKET)); + usbhs_pkt_start(pipe); + + return 0; +} + +/* + * DCP setup stage + */ +static int usbhsh_is_request_address(struct urb *urb) +{ + struct usb_ctrlrequest *cmd; + + cmd = (struct usb_ctrlrequest *)urb->setup_packet; + + if ((DeviceOutRequest == cmd->bRequestType << 8) && + (USB_REQ_SET_ADDRESS == cmd->bRequest)) + return 1; + else + return 0; +} + +static void usbhsh_setup_stage_packet_push(struct usbhsh_hpriv *hpriv, + struct urb *urb, + struct usbhs_pipe *pipe) +{ + struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv); + struct usb_ctrlrequest req; + struct device *dev = usbhs_priv_to_dev(priv); + + /* + * wait setup packet ACK + * see + * usbhsh_irq_setup_ack() + * usbhsh_irq_setup_err() + */ + DECLARE_COMPLETION(done); + hpriv->done = &done; + + /* copy original request */ + memcpy(&req, urb->setup_packet, sizeof(struct usb_ctrlrequest)); + + /* + * renesas_usbhs can not use original usb address. + * see HARDWARE LIMITATION. + * modify usb address here. + */ + if (usbhsh_is_request_address(urb)) { + /* FIXME */ + req.wValue = 1; + dev_dbg(dev, "create new address - %d\n", req.wValue); + } + + /* set request */ + usbhs_usbreq_set_val(priv, &req); + + /* + * wait setup packet ACK + */ + wait_for_completion(&done); + hpriv->done = NULL; + + dev_dbg(dev, "%s done\n", __func__); +} + +/* + * DCP data stage + */ +static void usbhsh_data_stage_packet_done(struct usbhs_priv *priv, + struct usbhs_pkt *pkt) +{ + struct usbhsh_request *ureq = usbhsh_pkt_to_req(pkt); + struct usbhsh_hpriv *hpriv = usbhsh_priv_to_hpriv(priv); + struct urb *urb = ureq->urb; + + /* this ureq was connected to urb when usbhsh_urb_enqueue() */ + + usbhsh_req_free(hpriv, ureq); + usbhsh_urb_to_ureq(urb) = NULL; +} + +static void usbhsh_data_stage_packet_push(struct usbhsh_hpriv *hpriv, + struct urb *urb, + struct usbhs_pipe *pipe) +{ + struct usbhsh_request *ureq; + struct usbhs_pkt *pkt; + + /* + * FIXME + * + * data stage uses ureq which is connected to urb + * see usbhsh_urb_enqueue() :: alloc new request. + * it will be freed in usbhsh_data_stage_packet_done() + */ + ureq = usbhsh_urb_to_ureq(urb); + pkt = &ureq->pkt; + + if (usb_pipein(urb->pipe)) + pipe->handler = &usbhs_dcp_data_stage_in_handler; + else + pipe->handler = &usbhs_dcp_data_stage_out_handler; + + usbhs_pkt_push(pipe, pkt, + usbhsh_data_stage_packet_done, + urb->transfer_buffer, + urb->transfer_buffer_length, + (urb->transfer_flags & URB_ZERO_PACKET)); +} + +/* + * DCP status stage + */ +static void usbhsh_status_stage_packet_push(struct usbhsh_hpriv *hpriv, + struct urb *urb, + struct usbhs_pipe *pipe) +{ + struct usbhsh_request *ureq; + struct usbhs_pkt *pkt; + + /* + * FIXME + * + * status stage uses allocated ureq. + * it will be freed on usbhsh_queue_done() + */ + ureq = usbhsh_req_alloc(hpriv, urb, GFP_KERNEL); + pkt = &ureq->pkt; + + if (usb_pipein(urb->pipe)) + pipe->handler = &usbhs_dcp_status_stage_in_handler; + else + pipe->handler = &usbhs_dcp_status_stage_out_handler; + + usbhs_pkt_push(pipe, pkt, + usbhsh_queue_done, + NULL, + urb->transfer_buffer_length, + 0); +} + +static int usbhsh_dcp_queue_push(struct usb_hcd *hcd, + struct usbhsh_hpriv *hpriv, + struct usbhs_pipe *pipe, + struct urb *urb) +{ + struct device *dev = usbhsh_hcd_to_dev(hcd); + + dev_dbg(dev, "%s\n", __func__); + + /* + * setup stage + * + * usbhsh_send_setup_stage_packet() wait SACK/SIGN + */ + usbhsh_setup_stage_packet_push(hpriv, urb, pipe); + + /* + * data stage + * + * It is pushed only when urb has buffer. + */ + if (urb->transfer_buffer_length) + usbhsh_data_stage_packet_push(hpriv, urb, pipe); + + /* + * status stage + */ + usbhsh_status_stage_packet_push(hpriv, urb, pipe); + + /* + * start pushed packets + */ + usbhs_pkt_start(pipe); + + return 0; +} + +/* + * dma map functions + */ +static int usbhsh_dma_map_ctrl(struct usbhs_pkt *pkt, int map) +{ + return 0; +} + +/* + * for hc_driver + */ +static int usbhsh_host_start(struct usb_hcd *hcd) +{ + return 0; +} + +static void usbhsh_host_stop(struct usb_hcd *hcd) +{ +} + +static int usbhsh_urb_enqueue(struct usb_hcd *hcd, + struct urb *urb, + gfp_t mem_flags) +{ + struct usbhsh_hpriv *hpriv = usbhsh_hcd_to_hpriv(hcd); + struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv); + struct device *dev = usbhs_priv_to_dev(priv); + struct usb_device *usbv = usbhsh_urb_to_usbv(urb); + struct usb_host_endpoint *ep = urb->ep; + struct usbhsh_request *ureq; + struct usbhsh_device *udev, *new_udev = NULL; + struct usbhs_pipe *pipe; + struct usbhsh_ep *uep; + + int ret; + + dev_dbg(dev, "%s (%s)\n", + __func__, usb_pipein(urb->pipe) ? "in" : "out"); + + ret = usb_hcd_link_urb_to_ep(hcd, urb); + if (ret) + goto usbhsh_urb_enqueue_error_not_linked; + + /* + * get udev + */ + udev = usbhsh_usbv_to_udev(usbv); + if (!udev) { + new_udev = usbhsh_device_alloc(hpriv, urb); + if (!new_udev) + goto usbhsh_urb_enqueue_error_not_linked; + + udev = new_udev; + } + + /* + * get uep + */ + uep = usbhsh_ep_to_uep(ep); + if (!uep) { + uep = usbhsh_endpoint_alloc(hpriv, udev, ep, mem_flags); + if (!uep) + goto usbhsh_urb_enqueue_error_free_device; + } + pipe = usbhsh_uep_to_pipe(uep); + + /* + * alloc new request + */ + ureq = usbhsh_req_alloc(hpriv, urb, mem_flags); + if (unlikely(!ureq)) { + ret = -ENOMEM; + goto usbhsh_urb_enqueue_error_free_endpoint; + } + usbhsh_urb_to_ureq(urb) = ureq; + + /* + * push packet + */ + if (usb_pipecontrol(urb->pipe)) + usbhsh_dcp_queue_push(hcd, hpriv, pipe, urb); + else + usbhsh_queue_push(hcd, pipe, urb); + + return 0; + +usbhsh_urb_enqueue_error_free_endpoint: + usbhsh_endpoint_free(hpriv, ep); +usbhsh_urb_enqueue_error_free_device: + if (new_udev) + usbhsh_device_free(hpriv, new_udev); +usbhsh_urb_enqueue_error_not_linked: + + dev_dbg(dev, "%s error\n", __func__); + + return ret; +} + +static int usbhsh_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) +{ + struct usbhsh_hpriv *hpriv = usbhsh_hcd_to_hpriv(hcd); + struct usbhsh_request *ureq = usbhsh_urb_to_ureq(urb); + + if (ureq) { + usbhsh_req_free(hpriv, ureq); + usbhsh_urb_to_ureq(urb) = NULL; + } + + return 0; +} + +static void usbhsh_endpoint_disable(struct usb_hcd *hcd, + struct usb_host_endpoint *ep) +{ + struct usbhsh_ep *uep = usbhsh_ep_to_uep(ep); + struct usbhsh_device *udev; + struct usbhsh_hpriv *hpriv; + + /* + * this function might be called manytimes by same hcd/ep + * in-endpoitn == out-endpoint if ep == dcp. + */ + if (!uep) + return; + + udev = usbhsh_uep_to_udev(uep); + hpriv = usbhsh_hcd_to_hpriv(hcd); + + usbhsh_endpoint_free(hpriv, ep); + ep->hcpriv = NULL; + + /* + * if there is no endpoint, + * free device + */ + if (!usbhsh_device_has_endpoint(udev)) + usbhsh_device_free(hpriv, udev); +} + +static int usbhsh_hub_status_data(struct usb_hcd *hcd, char *buf) +{ + struct usbhsh_hpriv *hpriv = usbhsh_hcd_to_hpriv(hcd); + struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv); + struct device *dev = usbhs_priv_to_dev(priv); + int roothub_id = 1; /* only 1 root hub */ + + /* + * does port stat was changed ? + * check USB_PORT_STAT_C_xxx << 16 + */ + if (usbhsh_port_stat_get(hpriv) & 0xFFFF0000) + *buf = (1 << roothub_id); + else + *buf = 0; + + dev_dbg(dev, "%s (%02x)\n", __func__, *buf); + + return !!(*buf); +} + +static int __usbhsh_hub_hub_feature(struct usbhsh_hpriv *hpriv, + u16 typeReq, u16 wValue, + u16 wIndex, char *buf, u16 wLength) +{ + struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv); + struct device *dev = usbhs_priv_to_dev(priv); + + switch (wValue) { + case C_HUB_OVER_CURRENT: + case C_HUB_LOCAL_POWER: + dev_dbg(dev, "%s :: C_HUB_xx\n", __func__); + return 0; + } + + return -EPIPE; +} + +static int __usbhsh_hub_port_feature(struct usbhsh_hpriv *hpriv, + u16 typeReq, u16 wValue, + u16 wIndex, char *buf, u16 wLength) +{ + struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv); + struct device *dev = usbhs_priv_to_dev(priv); + int enable = (typeReq == SetPortFeature); + int speed, i, timeout = 128; + int roothub_id = 1; /* only 1 root hub */ + + /* common error */ + if (wIndex > roothub_id || wLength != 0) + return -EPIPE; + + /* check wValue */ + switch (wValue) { + case USB_PORT_FEAT_POWER: + usbhs_vbus_ctrl(priv, enable); + dev_dbg(dev, "%s :: USB_PORT_FEAT_POWER\n", __func__); + break; + + case USB_PORT_FEAT_ENABLE: + case USB_PORT_FEAT_SUSPEND: + case USB_PORT_FEAT_C_ENABLE: + case USB_PORT_FEAT_C_SUSPEND: + case USB_PORT_FEAT_C_CONNECTION: + case USB_PORT_FEAT_C_OVER_CURRENT: + case USB_PORT_FEAT_C_RESET: + dev_dbg(dev, "%s :: USB_PORT_FEAT_xxx\n", __func__); + break; + + case USB_PORT_FEAT_RESET: + if (!enable) + break; + + usbhsh_port_stat_clear(hpriv, + USB_PORT_STAT_HIGH_SPEED | + USB_PORT_STAT_LOW_SPEED); + + usbhs_bus_send_reset(priv); + msleep(20); + usbhs_bus_send_sof_enable(priv); + + for (i = 0; i < timeout ; i++) { + switch (usbhs_bus_get_speed(priv)) { + case USB_SPEED_LOW: + speed = USB_PORT_STAT_LOW_SPEED; + goto got_usb_bus_speed; + case USB_SPEED_HIGH: + speed = USB_PORT_STAT_HIGH_SPEED; + goto got_usb_bus_speed; + case USB_SPEED_FULL: + speed = 0; + goto got_usb_bus_speed; + } + + msleep(20); + } + return -EPIPE; + +got_usb_bus_speed: + usbhsh_port_stat_set(hpriv, speed); + usbhsh_port_stat_set(hpriv, USB_PORT_STAT_ENABLE); + + dev_dbg(dev, "%s :: USB_PORT_FEAT_RESET (speed = %d)\n", + __func__, speed); + + /* status change is not needed */ + return 0; + + default: + return -EPIPE; + } + + /* set/clear status */ + if (enable) + usbhsh_port_stat_set(hpriv, (1 << wValue)); + else + usbhsh_port_stat_clear(hpriv, (1 << wValue)); + + return 0; +} + +static int __usbhsh_hub_get_status(struct usbhsh_hpriv *hpriv, + u16 typeReq, u16 wValue, + u16 wIndex, char *buf, u16 wLength) +{ + struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv); + struct usb_hub_descriptor *desc = (struct usb_hub_descriptor *)buf; + struct device *dev = usbhs_priv_to_dev(priv); + int roothub_id = 1; /* only 1 root hub */ + + switch (typeReq) { + case GetHubStatus: + dev_dbg(dev, "%s :: GetHubStatus\n", __func__); + + *buf = 0x00; + break; + + case GetPortStatus: + if (wIndex != roothub_id) + return -EPIPE; + + dev_dbg(dev, "%s :: GetPortStatus\n", __func__); + *(__le32 *)buf = cpu_to_le32(usbhsh_port_stat_get(hpriv)); + break; + + case GetHubDescriptor: + desc->bDescriptorType = 0x29; + desc->bHubContrCurrent = 0; + desc->bNbrPorts = roothub_id; + desc->bDescLength = 9; + desc->bPwrOn2PwrGood = 0; + desc->wHubCharacteristics = cpu_to_le16(0x0011); + desc->u.hs.DeviceRemovable[0] = (roothub_id << 1); + desc->u.hs.DeviceRemovable[1] = ~0; + dev_dbg(dev, "%s :: GetHubDescriptor\n", __func__); + break; + } + + return 0; +} + +static int usbhsh_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, + u16 wIndex, char *buf, u16 wLength) +{ + struct usbhsh_hpriv *hpriv = usbhsh_hcd_to_hpriv(hcd); + struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv); + struct device *dev = usbhs_priv_to_dev(priv); + int ret = -EPIPE; + + switch (typeReq) { + + /* Hub Feature */ + case ClearHubFeature: + case SetHubFeature: + ret = __usbhsh_hub_hub_feature(hpriv, typeReq, + wValue, wIndex, buf, wLength); + break; + + /* Port Feature */ + case SetPortFeature: + case ClearPortFeature: + ret = __usbhsh_hub_port_feature(hpriv, typeReq, + wValue, wIndex, buf, wLength); + break; + + /* Get status */ + case GetHubStatus: + case GetPortStatus: + case GetHubDescriptor: + ret = __usbhsh_hub_get_status(hpriv, typeReq, + wValue, wIndex, buf, wLength); + break; + } + + dev_dbg(dev, "typeReq = %x, ret = %d, port_stat = %x\n", + typeReq, ret, usbhsh_port_stat_get(hpriv)); + + return ret; +} + +static struct hc_driver usbhsh_driver = { + .description = usbhsh_hcd_name, + .hcd_priv_size = sizeof(struct usbhsh_hpriv), + + /* + * generic hardware linkage + */ + .flags = HCD_USB2, + + .start = usbhsh_host_start, + .stop = usbhsh_host_stop, + + /* + * managing i/o requests and associated device resources + */ + .urb_enqueue = usbhsh_urb_enqueue, + .urb_dequeue = usbhsh_urb_dequeue, + .endpoint_disable = usbhsh_endpoint_disable, + + /* + * root hub + */ + .hub_status_data = usbhsh_hub_status_data, + .hub_control = usbhsh_hub_control, +}; + +/* + * interrupt functions + */ +static int usbhsh_irq_attch(struct usbhs_priv *priv, + struct usbhs_irq_state *irq_state) +{ + struct usbhsh_hpriv *hpriv = usbhsh_priv_to_hpriv(priv); + struct device *dev = usbhs_priv_to_dev(priv); + + dev_dbg(dev, "device attached\n"); + + usbhsh_port_stat_set(hpriv, USB_PORT_STAT_CONNECTION); + usbhsh_port_stat_set(hpriv, USB_PORT_STAT_C_CONNECTION << 16); + + return 0; +} + +static int usbhsh_irq_dtch(struct usbhs_priv *priv, + struct usbhs_irq_state *irq_state) +{ + struct usbhsh_hpriv *hpriv = usbhsh_priv_to_hpriv(priv); + struct device *dev = usbhs_priv_to_dev(priv); + + dev_dbg(dev, "device detached\n"); + + usbhsh_port_stat_clear(hpriv, USB_PORT_STAT_CONNECTION); + usbhsh_port_stat_set(hpriv, USB_PORT_STAT_C_CONNECTION << 16); + + return 0; +} + +static int usbhsh_irq_setup_ack(struct usbhs_priv *priv, + struct usbhs_irq_state *irq_state) +{ + struct usbhsh_hpriv *hpriv = usbhsh_priv_to_hpriv(priv); + struct device *dev = usbhs_priv_to_dev(priv); + + dev_dbg(dev, "setup packet OK\n"); + + if (unlikely(!hpriv->done)) + dev_err(dev, "setup ack happen without necessary data\n"); + else + complete(hpriv->done); /* see usbhsh_urb_enqueue() */ + + return 0; +} + +static int usbhsh_irq_setup_err(struct usbhs_priv *priv, + struct usbhs_irq_state *irq_state) +{ + struct usbhsh_hpriv *hpriv = usbhsh_priv_to_hpriv(priv); + struct device *dev = usbhs_priv_to_dev(priv); + + dev_dbg(dev, "setup packet Err\n"); + + if (unlikely(!hpriv->done)) + dev_err(dev, "setup err happen without necessary data\n"); + else + complete(hpriv->done); /* see usbhsh_urb_enqueue() */ + + return 0; +} + +/* + * module start/stop + */ +static void usbhsh_pipe_init_for_host(struct usbhs_priv *priv) +{ + struct usbhsh_hpriv *hpriv = usbhsh_priv_to_hpriv(priv); + struct usbhsh_pipe_info *pipe_info = hpriv->pipe_info; + struct usbhs_pipe *pipe; + u32 *pipe_type = usbhs_get_dparam(priv, pipe_type); + int pipe_size = usbhs_get_dparam(priv, pipe_size); + int old_type, dir_in, i; + + /* init all pipe */ + old_type = USB_ENDPOINT_XFER_CONTROL; + for (i = 0; i < pipe_size; i++) { + pipe_info[i].usr_cnt = 0; + + /* + * data "output" will be finished as soon as possible, + * but there is no guaranty at data "input" case. + * + * "input" needs "standby" pipe. + * So, "input" direction pipe > "output" direction pipe + * is good idea. + * + * 1st USB_ENDPOINT_XFER_xxx will be output direction, + * and the other will be input direction here. + * + * ex) + * ... + * USB_ENDPOINT_XFER_ISOC -> dir out + * USB_ENDPOINT_XFER_ISOC -> dir in + * USB_ENDPOINT_XFER_BULK -> dir out + * USB_ENDPOINT_XFER_BULK -> dir in + * USB_ENDPOINT_XFER_BULK -> dir in + * ... + */ + dir_in = (pipe_type[i] == old_type); + old_type = pipe_type[i]; + + if (USB_ENDPOINT_XFER_CONTROL == pipe_type[i]) { + pipe = usbhs_dcp_malloc(priv); + usbhsh_hpriv_to_dcp(hpriv) = pipe; + } else { + pipe = usbhs_pipe_malloc(priv, + pipe_type[i], + dir_in); + } + + pipe->mod_private = pipe_info + i; + } +} + +static int usbhsh_start(struct usbhs_priv *priv) +{ + struct usbhsh_hpriv *hpriv = usbhsh_priv_to_hpriv(priv); + struct usb_hcd *hcd = usbhsh_hpriv_to_hcd(hpriv); + struct usbhs_mod *mod = usbhs_mod_get_current(priv); + struct device *dev = usbhs_priv_to_dev(priv); + int ret; + + /* add hcd */ + ret = usb_add_hcd(hcd, 0, 0); + if (ret < 0) + return 0; + + /* + * pipe initialize and enable DCP + */ + usbhs_pipe_init(priv, + usbhsh_dma_map_ctrl); + usbhs_fifo_init(priv); + usbhsh_pipe_init_for_host(priv); + + /* + * system config enble + * - HI speed + * - host + * - usb module + */ + usbhs_sys_hispeed_ctrl(priv, 1); + usbhs_sys_host_ctrl(priv, 1); + usbhs_sys_usb_ctrl(priv, 1); + + /* + * enable irq callback + */ + mod->irq_attch = usbhsh_irq_attch; + mod->irq_dtch = usbhsh_irq_dtch; + mod->irq_sack = usbhsh_irq_setup_ack; + mod->irq_sign = usbhsh_irq_setup_err; + usbhs_irq_callback_update(priv, mod); + + dev_dbg(dev, "start host\n"); + + return ret; +} + +static int usbhsh_stop(struct usbhs_priv *priv) +{ + struct usbhsh_hpriv *hpriv = usbhsh_priv_to_hpriv(priv); + struct usb_hcd *hcd = usbhsh_hpriv_to_hcd(hpriv); + struct device *dev = usbhs_priv_to_dev(priv); + + usb_remove_hcd(hcd); + + /* disable sys */ + usbhs_sys_hispeed_ctrl(priv, 0); + usbhs_sys_host_ctrl(priv, 0); + usbhs_sys_usb_ctrl(priv, 0); + + dev_dbg(dev, "quit host\n"); + + return 0; +} + +int __devinit usbhs_mod_host_probe(struct usbhs_priv *priv) +{ + struct usbhsh_hpriv *hpriv; + struct usb_hcd *hcd; + struct usbhsh_pipe_info *pipe_info; + struct usbhsh_device *udev; + struct device *dev = usbhs_priv_to_dev(priv); + int pipe_size = usbhs_get_dparam(priv, pipe_size); + int i; + + /* initialize hcd */ + hcd = usb_create_hcd(&usbhsh_driver, dev, usbhsh_hcd_name); + if (!hcd) { + dev_err(dev, "Failed to create hcd\n"); + return -ENOMEM; + } + + pipe_info = kzalloc(sizeof(*pipe_info) * pipe_size, GFP_KERNEL); + if (!pipe_info) { + dev_err(dev, "Could not allocate pipe_info\n"); + goto usbhs_mod_host_probe_err; + } + + /* + * CAUTION + * + * There is no guarantee that it is possible to access usb module here. + * Don't accesses to it. + * The accesse will be enable after "usbhsh_start" + */ + + hpriv = usbhsh_hcd_to_hpriv(hcd); + + /* + * register itself + */ + usbhs_mod_register(priv, &hpriv->mod, USBHS_HOST); + + /* init hpriv */ + hpriv->mod.name = "host"; + hpriv->mod.start = usbhsh_start; + hpriv->mod.stop = usbhsh_stop; + hpriv->pipe_info = pipe_info; + hpriv->pipe_size = pipe_size; + hpriv->done = NULL; + usbhsh_req_list_init(hpriv); + usbhsh_port_stat_init(hpriv); + + /* init all device */ + usbhsh_for_each_udev_with_dev0(udev, hpriv, i) { + udev->usbv = NULL; + INIT_LIST_HEAD(&udev->ep_list_head); + } + + dev_info(dev, "host probed\n"); + + return 0; + +usbhs_mod_host_probe_err: + usb_put_hcd(hcd); + + return -ENOMEM; +} + +int __devexit usbhs_mod_host_remove(struct usbhs_priv *priv) +{ + struct usbhsh_hpriv *hpriv = usbhsh_priv_to_hpriv(priv); + struct usb_hcd *hcd = usbhsh_hpriv_to_hcd(hpriv); + + usbhsh_req_list_quit(hpriv); + + usb_put_hcd(hcd); + + return 0; +} diff --git a/drivers/usb/renesas_usbhs/pipe.c b/drivers/usb/renesas_usbhs/pipe.c index 1b14cae45704..c74389ce2177 100644 --- a/drivers/usb/renesas_usbhs/pipe.c +++ b/drivers/usb/renesas_usbhs/pipe.c @@ -29,9 +29,6 @@ #define usbhsp_flags_has(p, f) ((p)->flags & USBHS_PIPE_FLAGS_##f) #define usbhsp_flags_init(p) do {(p)->flags = 0; } while (0) -#define usbhsp_type(p) ((p)->pipe_type) -#define usbhsp_type_is(p, t) ((p)->pipe_type == t) - /* * for debug */ @@ -42,28 +39,9 @@ static char *usbhsp_pipe_name[] = { [USB_ENDPOINT_XFER_ISOC] = "ISO", }; -/* - * usb request functions - */ -void usbhs_usbreq_get_val(struct usbhs_priv *priv, struct usb_ctrlrequest *req) +char *usbhs_pipe_name(struct usbhs_pipe *pipe) { - u16 val; - - val = usbhs_read(priv, USBREQ); - req->bRequest = (val >> 8) & 0xFF; - req->bRequestType = (val >> 0) & 0xFF; - - req->wValue = usbhs_read(priv, USBVAL); - req->wIndex = usbhs_read(priv, USBINDX); - req->wLength = usbhs_read(priv, USBLENG); -} - -void usbhs_usbreq_set_val(struct usbhs_priv *priv, struct usb_ctrlrequest *req) -{ - usbhs_write(priv, USBREQ, (req->bRequest << 8) | req->bRequestType); - usbhs_write(priv, USBVAL, req->wValue); - usbhs_write(priv, USBINDX, req->wIndex); - usbhs_write(priv, USBLENG, req->wLength); + return usbhsp_pipe_name[usbhs_pipe_type(pipe)]; } /* @@ -106,17 +84,6 @@ static void __usbhsp_pipe_xxx_set(struct usbhs_pipe *pipe, usbhs_bset(priv, pipe_reg, mask, val); } -static u16 __usbhsp_pipe_xxx_get(struct usbhs_pipe *pipe, - u16 dcp_reg, u16 pipe_reg) -{ - struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe); - - if (usbhs_pipe_is_dcp(pipe)) - return usbhs_read(priv, dcp_reg); - else - return usbhs_read(priv, pipe_reg); -} - /* * DCPCFG/PIPECFG functions */ @@ -144,11 +111,6 @@ static void usbhsp_pipe_maxp_set(struct usbhs_pipe *pipe, u16 mask, u16 val) __usbhsp_pipe_xxx_set(pipe, DCPMAXP, PIPEMAXP, mask, val); } -static u16 usbhsp_pipe_maxp_get(struct usbhs_pipe *pipe) -{ - return __usbhsp_pipe_xxx_get(pipe, DCPMAXP, PIPEMAXP); -} - /* * pipe control functions */ @@ -303,16 +265,16 @@ static int usbhsp_possible_double_buffer(struct usbhs_pipe *pipe) /* * only ISO / BULK pipe can use double buffer */ - if (usbhsp_type_is(pipe, USB_ENDPOINT_XFER_BULK) || - usbhsp_type_is(pipe, USB_ENDPOINT_XFER_ISOC)) + if (usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_BULK) || + usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_ISOC)) return 1; return 0; } static u16 usbhsp_setup_pipecfg(struct usbhs_pipe *pipe, - const struct usb_endpoint_descriptor *desc, - int is_host) + int is_host, + int dir_in) { u16 type = 0; u16 bfre = 0; @@ -341,40 +303,40 @@ static u16 usbhsp_setup_pipecfg(struct usbhs_pipe *pipe, */ /* TYPE */ - type = type_array[usbhsp_type(pipe)]; + type = type_array[usbhs_pipe_type(pipe)]; /* BFRE */ - if (usbhsp_type_is(pipe, USB_ENDPOINT_XFER_ISOC) || - usbhsp_type_is(pipe, USB_ENDPOINT_XFER_BULK)) + if (usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_ISOC) || + usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_BULK)) bfre = 0; /* FIXME */ /* DBLB */ - if (usbhsp_type_is(pipe, USB_ENDPOINT_XFER_ISOC) || - usbhsp_type_is(pipe, USB_ENDPOINT_XFER_BULK)) + if (usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_ISOC) || + usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_BULK)) dblb = (is_double) ? DBLB : 0; /* CNTMD */ - if (usbhsp_type_is(pipe, USB_ENDPOINT_XFER_BULK)) + if (usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_BULK)) cntmd = 0; /* FIXME */ /* DIR */ - if (usb_endpoint_dir_in(desc)) + if (dir_in) usbhsp_flags_set(pipe, IS_DIR_HOST); - if ((is_host && usb_endpoint_dir_out(desc)) || - (!is_host && usb_endpoint_dir_in(desc))) + if ((is_host && !dir_in) || + (!is_host && dir_in)) dir |= DIR_OUT; if (!dir) usbhsp_flags_set(pipe, IS_DIR_IN); /* SHTNAK */ - if (usbhsp_type_is(pipe, USB_ENDPOINT_XFER_BULK) && + if (usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_BULK) && !dir) shtnak = SHTNAK; /* EPNUM */ - epnum = 0xF & usb_endpoint_num(desc); + epnum = 0; /* see usbhs_pipe_config_update() */ return type | bfre | @@ -385,19 +347,7 @@ static u16 usbhsp_setup_pipecfg(struct usbhs_pipe *pipe, epnum; } -static u16 usbhsp_setup_pipemaxp(struct usbhs_pipe *pipe, - const struct usb_endpoint_descriptor *desc, - int is_host) -{ - /* host should set DEVSEL */ - - /* reutn MXPS */ - return PIPE_MAXP_MASK & le16_to_cpu(desc->wMaxPacketSize); -} - -static u16 usbhsp_setup_pipebuff(struct usbhs_pipe *pipe, - const struct usb_endpoint_descriptor *desc, - int is_host) +static u16 usbhsp_setup_pipebuff(struct usbhs_pipe *pipe) { struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe); struct usbhs_pipe_info *info = usbhs_priv_to_pipeinfo(priv); @@ -441,9 +391,9 @@ static u16 usbhsp_setup_pipebuff(struct usbhs_pipe *pipe, * INT : 64 byte * ISOC: 512 byte */ - if (usbhsp_type_is(pipe, USB_ENDPOINT_XFER_CONTROL)) + if (usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_CONTROL)) buff_size = 256; - else if (usbhsp_type_is(pipe, USB_ENDPOINT_XFER_INT)) + else if (usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_INT)) buff_size = 64; else buff_size = 512; @@ -453,7 +403,7 @@ static u16 usbhsp_setup_pipebuff(struct usbhs_pipe *pipe, /* BUFNMB has been reserved for INT pipe * see above */ - if (usbhsp_type_is(pipe, USB_ENDPOINT_XFER_INT)) { + if (usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_INT)) { bufnmb = pipe_num - 2; } else { bufnmb = info->bufnmb_last; @@ -473,16 +423,42 @@ static u16 usbhsp_setup_pipebuff(struct usbhs_pipe *pipe, (0xff & bufnmb) << 0; } +void usbhs_pipe_config_update(struct usbhs_pipe *pipe, u16 devsel, + u16 epnum, u16 maxp) +{ + if (devsel > 0xA) { + struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe); + struct device *dev = usbhs_priv_to_dev(priv); + + dev_err(dev, "devsel error %d\n", devsel); + + devsel = 0; + } + + usbhsp_pipe_barrier(pipe); + + pipe->maxp = maxp; + + usbhsp_pipe_select(pipe); + usbhsp_pipe_maxp_set(pipe, 0xFFFF, + (devsel << 12) | + maxp); + + if (!usbhs_pipe_is_dcp(pipe)) + usbhsp_pipe_cfg_set(pipe, 0x000F, epnum); +} + /* * pipe control */ int usbhs_pipe_get_maxpacket(struct usbhs_pipe *pipe) { - u16 mask = usbhs_pipe_is_dcp(pipe) ? DCP_MAXP_MASK : PIPE_MAXP_MASK; - - usbhsp_pipe_select(pipe); - - return (int)(usbhsp_pipe_maxp_get(pipe) & mask); + /* + * see + * usbhs_pipe_config_update() + * usbhs_dcp_malloc() + */ + return pipe->maxp; } int usbhs_pipe_is_dir_in(struct usbhs_pipe *pipe) @@ -495,9 +471,12 @@ int usbhs_pipe_is_dir_host(struct usbhs_pipe *pipe) return usbhsp_flags_has(pipe, IS_DIR_HOST); } -void usbhs_pipe_clear_sequence(struct usbhs_pipe *pipe) +void usbhs_pipe_data_sequence(struct usbhs_pipe *pipe, int data) { - usbhsp_pipectrl_set(pipe, SQCLR, SQCLR); + u16 mask = (SQCLR | SQSET); + u16 val = (data) ? SQSET : SQCLR; + + usbhsp_pipectrl_set(pipe, mask, val); } void usbhs_pipe_clear(struct usbhs_pipe *pipe) @@ -516,7 +495,7 @@ static struct usbhs_pipe *usbhsp_get_pipe(struct usbhs_priv *priv, u32 type) */ pipe = NULL; usbhs_for_each_pipe_with_dcp(pos, priv, i) { - if (!usbhsp_type_is(pos, type)) + if (!usbhs_pipe_type_is(pos, type)) continue; if (usbhsp_flags_has(pos, IS_USED)) continue; @@ -538,19 +517,12 @@ static struct usbhs_pipe *usbhsp_get_pipe(struct usbhs_priv *priv, u32 type) } void usbhs_pipe_init(struct usbhs_priv *priv, - void (*done)(struct usbhs_pkt *pkt), int (*dma_map_ctrl)(struct usbhs_pkt *pkt, int map)) { struct usbhs_pipe_info *info = usbhs_priv_to_pipeinfo(priv); - struct device *dev = usbhs_priv_to_dev(priv); struct usbhs_pipe *pipe; int i; - if (!done) { - dev_err(dev, "no done function\n"); - return; - } - /* * FIXME * @@ -565,7 +537,7 @@ void usbhs_pipe_init(struct usbhs_priv *priv, */ info->bufnmb_last = 4; usbhs_for_each_pipe_with_dcp(pipe, priv, i) { - if (usbhsp_type_is(pipe, USB_ENDPOINT_XFER_INT)) + if (usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_INT)) info->bufnmb_last++; usbhsp_flags_init(pipe); @@ -577,24 +549,23 @@ void usbhs_pipe_init(struct usbhs_priv *priv, usbhs_pipe_clear(pipe); } - info->done = done; info->dma_map_ctrl = dma_map_ctrl; } struct usbhs_pipe *usbhs_pipe_malloc(struct usbhs_priv *priv, - const struct usb_endpoint_descriptor *desc) + int endpoint_type, + int dir_in) { struct device *dev = usbhs_priv_to_dev(priv); - struct usbhs_mod *mod = usbhs_mod_get_current(priv); struct usbhs_pipe *pipe; - int is_host = usbhs_mod_is_host(priv, mod); + int is_host = usbhs_mod_is_host(priv); int ret; - u16 pipecfg, pipebuf, pipemaxp; + u16 pipecfg, pipebuf; - pipe = usbhsp_get_pipe(priv, usb_endpoint_type(desc)); + pipe = usbhsp_get_pipe(priv, endpoint_type); if (!pipe) { dev_err(dev, "can't get pipe (%s)\n", - usbhsp_pipe_name[usb_endpoint_type(desc)]); + usbhsp_pipe_name[endpoint_type]); return NULL; } @@ -609,22 +580,25 @@ struct usbhs_pipe *usbhs_pipe_malloc(struct usbhs_priv *priv, return NULL; } - pipecfg = usbhsp_setup_pipecfg(pipe, desc, is_host); - pipebuf = usbhsp_setup_pipebuff(pipe, desc, is_host); - pipemaxp = usbhsp_setup_pipemaxp(pipe, desc, is_host); + pipecfg = usbhsp_setup_pipecfg(pipe, is_host, dir_in); + pipebuf = usbhsp_setup_pipebuff(pipe); usbhsp_pipe_select(pipe); usbhsp_pipe_cfg_set(pipe, 0xFFFF, pipecfg); usbhsp_pipe_buf_set(pipe, 0xFFFF, pipebuf); - usbhsp_pipe_maxp_set(pipe, 0xFFFF, pipemaxp); - usbhs_pipe_clear_sequence(pipe); + usbhs_pipe_sequence_data0(pipe); dev_dbg(dev, "enable pipe %d : %s (%s)\n", usbhs_pipe_number(pipe), - usbhsp_pipe_name[usb_endpoint_type(desc)], + usbhs_pipe_name(pipe), usbhs_pipe_is_dir_in(pipe) ? "in" : "out"); + /* + * epnum / maxp are still not set to this pipe. + * call usbhs_pipe_config_update() after this function !! + */ + return pipe; } @@ -651,25 +625,31 @@ struct usbhs_pipe *usbhs_dcp_malloc(struct usbhs_priv *priv) if (!pipe) return NULL; + INIT_LIST_HEAD(&pipe->list); + /* - * dcpcfg : default - * dcpmaxp : default - * pipebuf : nothing to do + * call usbhs_pipe_config_update() after this function !! */ - usbhsp_pipe_select(pipe); - usbhs_pipe_clear_sequence(pipe); - INIT_LIST_HEAD(&pipe->list); - return pipe; } void usbhs_dcp_control_transfer_done(struct usbhs_pipe *pipe) { + struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe); + WARN_ON(!usbhs_pipe_is_dcp(pipe)); usbhs_pipe_enable(pipe); - usbhsp_pipectrl_set(pipe, CCPL, CCPL); + + if (!usbhs_mod_is_host(priv)) /* funconly */ + usbhsp_pipectrl_set(pipe, CCPL, CCPL); +} + +void usbhs_dcp_dir_for_host(struct usbhs_pipe *pipe, int dir_out) +{ + usbhsp_pipe_cfg_set(pipe, DIR_OUT, + dir_out ? DIR_OUT : 0); } /* @@ -703,7 +683,9 @@ int usbhs_pipe_probe(struct usbhs_priv *priv) */ usbhs_for_each_pipe_with_dcp(pipe, priv, i) { pipe->priv = priv; - usbhsp_type(pipe) = pipe_type[i] & USB_ENDPOINT_XFERTYPE_MASK; + + usbhs_pipe_type(pipe) = + pipe_type[i] & USB_ENDPOINT_XFERTYPE_MASK; dev_dbg(dev, "pipe %x\t: %s\n", i, usbhsp_pipe_name[pipe_type[i]]); diff --git a/drivers/usb/renesas_usbhs/pipe.h b/drivers/usb/renesas_usbhs/pipe.h index 41534cb0e734..6334fc644cc0 100644 --- a/drivers/usb/renesas_usbhs/pipe.h +++ b/drivers/usb/renesas_usbhs/pipe.h @@ -30,11 +30,15 @@ struct usbhs_pipe { struct usbhs_fifo *fifo; struct list_head list; + int maxp; + u32 flags; #define USBHS_PIPE_FLAGS_IS_USED (1 << 0) #define USBHS_PIPE_FLAGS_IS_DIR_IN (1 << 1) #define USBHS_PIPE_FLAGS_IS_DIR_HOST (1 << 2) + struct usbhs_pkt_handle *handler; + void *mod_private; }; @@ -43,7 +47,6 @@ struct usbhs_pipe_info { int size; /* array size of "pipe" */ int bufnmb_last; /* FIXME : driver needs good allocator */ - void (*done)(struct usbhs_pkt *pkt); int (*dma_map_ctrl)(struct usbhs_pkt *pkt, int map); }; @@ -67,32 +70,30 @@ struct usbhs_pipe_info { #define usbhs_priv_to_pipeinfo(pr) (&(pr)->pipe_info) /* - * usb request - */ -void usbhs_usbreq_get_val(struct usbhs_priv *priv, struct usb_ctrlrequest *req); -void usbhs_usbreq_set_val(struct usbhs_priv *priv, struct usb_ctrlrequest *req); - -/* * pipe control */ +char *usbhs_pipe_name(struct usbhs_pipe *pipe); struct usbhs_pipe -*usbhs_pipe_malloc(struct usbhs_priv *priv, - const struct usb_endpoint_descriptor *desc); +*usbhs_pipe_malloc(struct usbhs_priv *priv, int endpoint_type, int dir_in); int usbhs_pipe_probe(struct usbhs_priv *priv); void usbhs_pipe_remove(struct usbhs_priv *priv); int usbhs_pipe_is_dir_in(struct usbhs_pipe *pipe); int usbhs_pipe_is_dir_host(struct usbhs_pipe *pipe); void usbhs_pipe_init(struct usbhs_priv *priv, - void (*done)(struct usbhs_pkt *pkt), int (*dma_map_ctrl)(struct usbhs_pkt *pkt, int map)); int usbhs_pipe_get_maxpacket(struct usbhs_pipe *pipe); -void usbhs_pipe_clear_sequence(struct usbhs_pipe *pipe); void usbhs_pipe_clear(struct usbhs_pipe *pipe); int usbhs_pipe_is_accessible(struct usbhs_pipe *pipe); void usbhs_pipe_enable(struct usbhs_pipe *pipe); void usbhs_pipe_disable(struct usbhs_pipe *pipe); void usbhs_pipe_stall(struct usbhs_pipe *pipe); void usbhs_pipe_select_fifo(struct usbhs_pipe *pipe, struct usbhs_fifo *fifo); +void usbhs_pipe_config_update(struct usbhs_pipe *pipe, u16 devsel, + u16 epnum, u16 maxp); + +#define usbhs_pipe_sequence_data0(pipe) usbhs_pipe_data_sequence(pipe, 0) +#define usbhs_pipe_sequence_data1(pipe) usbhs_pipe_data_sequence(pipe, 1) +void usbhs_pipe_data_sequence(struct usbhs_pipe *pipe, int data); #define usbhs_pipe_to_priv(p) ((p)->priv) #define usbhs_pipe_number(p) (int)((p) - (p)->priv->pipe_info.pipe) @@ -100,10 +101,14 @@ void usbhs_pipe_select_fifo(struct usbhs_pipe *pipe, struct usbhs_fifo *fifo); #define usbhs_pipe_to_fifo(p) ((p)->fifo) #define usbhs_pipe_is_busy(p) usbhs_pipe_to_fifo(p) +#define usbhs_pipe_type(p) ((p)->pipe_type) +#define usbhs_pipe_type_is(p, t) ((p)->pipe_type == t) + /* * dcp control */ struct usbhs_pipe *usbhs_dcp_malloc(struct usbhs_priv *priv); void usbhs_dcp_control_transfer_done(struct usbhs_pipe *pipe); +void usbhs_dcp_dir_for_host(struct usbhs_pipe *pipe, int dir_out); #endif /* RENESAS_USB_PIPE_H */ diff --git a/drivers/usb/serial/Kconfig b/drivers/usb/serial/Kconfig index b71e309116a3..677f577c0243 100644 --- a/drivers/usb/serial/Kconfig +++ b/drivers/usb/serial/Kconfig @@ -252,6 +252,7 @@ config USB_SERIAL_GARMIN config USB_SERIAL_IPW tristate "USB IPWireless (3G UMTS TDD) Driver" + select USB_SERIAL_WWAN help Say Y here if you want to use a IPWireless USB modem such as the ones supplied by Axity3G/Sentech South Africa. diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 5fc13e717911..8fe034d2d3e7 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -73,6 +73,7 @@ struct ftdi_private { */ int flags; /* some ASYNC_xxxx flags are supported */ unsigned long last_dtr_rts; /* saved modem control outputs */ + struct async_icount icount; wait_queue_head_t delta_msr_wait; /* Used for TIOCMIWAIT */ char prev_status, diff_status; /* Used for TIOCMIWAIT */ char transmit_empty; /* If transmitter is empty or not */ @@ -207,6 +208,8 @@ static struct usb_device_id id_table_combined [] = { { USB_DEVICE(FTDI_VID, FTDI_XF_640_PID) }, { USB_DEVICE(FTDI_VID, FTDI_XF_642_PID) }, { USB_DEVICE(FTDI_VID, FTDI_DSS20_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_URBAN_0_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_URBAN_1_PID) }, { USB_DEVICE(FTDI_NF_RIC_VID, FTDI_NF_RIC_PID) }, { USB_DEVICE(FTDI_VID, FTDI_VNHCPCUSB_D_PID) }, { USB_DEVICE(FTDI_VID, FTDI_MTXORB_0_PID) }, @@ -745,6 +748,8 @@ static struct usb_device_id id_table_combined [] = { .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, { USB_DEVICE(FTDI_VID, LMI_LM3S_EVAL_BOARD_PID), .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, + { USB_DEVICE(FTDI_VID, LMI_LM3S_ICDI_BOARD_PID), + .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, { USB_DEVICE(FTDI_VID, FTDI_TURTELIZER_PID), .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, { USB_DEVICE(RATOC_VENDOR_ID, RATOC_PRODUCT_ID_USB60F) }, @@ -885,6 +890,8 @@ static void ftdi_set_termios(struct tty_struct *tty, static int ftdi_tiocmget(struct tty_struct *tty); static int ftdi_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear); +static int ftdi_get_icount(struct tty_struct *tty, + struct serial_icounter_struct *icount); static int ftdi_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg); static void ftdi_break_ctl(struct tty_struct *tty, int break_state); @@ -919,6 +926,7 @@ static struct usb_serial_driver ftdi_sio_device = { .prepare_write_buffer = ftdi_prepare_write_buffer, .tiocmget = ftdi_tiocmget, .tiocmset = ftdi_tiocmset, + .get_icount = ftdi_get_icount, .ioctl = ftdi_ioctl, .set_termios = ftdi_set_termios, .break_ctl = ftdi_break_ctl, @@ -1486,7 +1494,7 @@ static void ftdi_set_max_packet_size(struct usb_serial_port *port) } /* set max packet size based on descriptor */ - priv->max_packet_size = le16_to_cpu(ep_desc->wMaxPacketSize); + priv->max_packet_size = usb_endpoint_maxp(ep_desc); dev_info(&udev->dev, "Setting MaxPacketSize %d\n", priv->max_packet_size); } @@ -1646,6 +1654,7 @@ static int ftdi_sio_port_probe(struct usb_serial_port *port) kref_init(&priv->kref); mutex_init(&priv->cfg_lock); + memset(&priv->icount, 0x00, sizeof(priv->icount)); init_waitqueue_head(&priv->delta_msr_wait); priv->flags = ASYNC_LOW_LATENCY; @@ -1909,6 +1918,7 @@ static int ftdi_prepare_write_buffer(struct usb_serial_port *port, c = kfifo_out(&port->write_fifo, &buffer[i + 1], len); if (!c) break; + priv->icount.tx += c; buffer[i] = (c << 2) + 1; count += c + 1; } @@ -1916,6 +1926,7 @@ static int ftdi_prepare_write_buffer(struct usb_serial_port *port, } else { count = kfifo_out_locked(&port->write_fifo, dest, size, &port->lock); + priv->icount.tx += count; } return count; @@ -1943,6 +1954,14 @@ static int ftdi_process_packet(struct tty_struct *tty, N.B. packet may be processed more than once, but differences are only processed once. */ status = packet[0] & FTDI_STATUS_B0_MASK; + if (status & FTDI_RS0_CTS) + priv->icount.cts++; + if (status & FTDI_RS0_DSR) + priv->icount.dsr++; + if (status & FTDI_RS0_RI) + priv->icount.rng++; + if (status & FTDI_RS0_RLSD) + priv->icount.dcd++; if (status != priv->prev_status) { priv->diff_status |= status ^ priv->prev_status; wake_up_interruptible(&priv->delta_msr_wait); @@ -1955,15 +1974,20 @@ static int ftdi_process_packet(struct tty_struct *tty, * over framing errors */ if (packet[1] & FTDI_RS_BI) { flag = TTY_BREAK; + priv->icount.brk++; usb_serial_handle_break(port); } else if (packet[1] & FTDI_RS_PE) { flag = TTY_PARITY; + priv->icount.parity++; } else if (packet[1] & FTDI_RS_FE) { flag = TTY_FRAME; + priv->icount.frame++; } /* Overrun is special, not associated with a char */ - if (packet[1] & FTDI_RS_OE) + if (packet[1] & FTDI_RS_OE) { + priv->icount.overrun++; tty_insert_flip_char(tty, 0, TTY_OVERRUN); + } } /* save if the transmitter is empty or not */ @@ -1975,6 +1999,7 @@ static int ftdi_process_packet(struct tty_struct *tty, len -= 2; if (!len) return 0; /* status only */ + priv->icount.rx += len; ch = packet + 2; if (port->port.console && port->sysrq) { @@ -2277,11 +2302,34 @@ static int ftdi_tiocmset(struct tty_struct *tty, return update_mctrl(port, set, clear); } +static int ftdi_get_icount(struct tty_struct *tty, + struct serial_icounter_struct *icount) +{ + struct usb_serial_port *port = tty->driver_data; + struct ftdi_private *priv = usb_get_serial_port_data(port); + struct async_icount *ic = &priv->icount; + + icount->cts = ic->cts; + icount->dsr = ic->dsr; + icount->rng = ic->rng; + icount->dcd = ic->dcd; + icount->tx = ic->tx; + icount->rx = ic->rx; + icount->frame = ic->frame; + icount->parity = ic->parity; + icount->overrun = ic->overrun; + icount->brk = ic->brk; + icount->buf_overrun = ic->buf_overrun; + return 0; +} + static int ftdi_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { struct usb_serial_port *port = tty->driver_data; struct ftdi_private *priv = usb_get_serial_port_data(port); + struct async_icount cnow; + struct async_icount cprev; dbg("%s cmd 0x%04x", __func__, cmd); @@ -2301,41 +2349,30 @@ static int ftdi_ioctl(struct tty_struct *tty, * - mask passed in arg for lines of interest * (use |'ed TIOCM_RNG/DSR/CD/CTS for masking) * Caller should use TIOCGICOUNT to see which one it was. - * (except that the driver doesn't support it !) * * This code is borrowed from linux/drivers/char/serial.c */ case TIOCMIWAIT: - while (priv != NULL) { + cprev = priv->icount; + while (1) { interruptible_sleep_on(&priv->delta_msr_wait); /* see if a signal did it */ if (signal_pending(current)) return -ERESTARTSYS; - else { - char diff = priv->diff_status; - - if (diff == 0) - return -EIO; /* no change => error */ - - /* Consume all events */ - priv->diff_status = 0; - - /* Return 0 if caller wanted to know about - these bits */ - if (((arg & TIOCM_RNG) && (diff & FTDI_RS0_RI)) || - ((arg & TIOCM_DSR) && (diff & FTDI_RS0_DSR)) || - ((arg & TIOCM_CD) && (diff & FTDI_RS0_RLSD)) || - ((arg & TIOCM_CTS) && (diff & FTDI_RS0_CTS))) { - return 0; - } - /* - * Otherwise caller can't care less about what - * happened,and so we continue to wait for more - * events. - */ + cnow = priv->icount; + if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && + cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) + return -EIO; /* no change => error */ + if (((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) || + ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) || + ((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) || + ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts))) { + return 0; } + cprev = cnow; } - return 0; + /* not reached */ + break; case TIOCSERGETLSR: return get_lsr_info(port, (struct serial_struct __user *)arg); break; diff --git a/drivers/usb/serial/ftdi_sio_ids.h b/drivers/usb/serial/ftdi_sio_ids.h index bf5227ad3ef7..571fa96b49c7 100644 --- a/drivers/usb/serial/ftdi_sio_ids.h +++ b/drivers/usb/serial/ftdi_sio_ids.h @@ -54,6 +54,7 @@ /* FTDI 2332C Dual channel device, side A=245 FIFO (JTAG), Side B=RS232 UART */ #define LMI_LM3S_DEVEL_BOARD_PID 0xbcd8 #define LMI_LM3S_EVAL_BOARD_PID 0xbcd9 +#define LMI_LM3S_ICDI_BOARD_PID 0xbcda #define FTDI_TURTELIZER_PID 0xBDC8 /* JTAG/RS-232 adapter by egnite GmbH */ @@ -420,9 +421,11 @@ #define PROTEGO_SPECIAL_4 0xFC73 /* special/unknown device */ /* - * DSS-20 Sync Station for Sony Ericsson P800 + * Sony Ericsson product ids */ -#define FTDI_DSS20_PID 0xFC82 +#define FTDI_DSS20_PID 0xFC82 /* DSS-20 Sync Station for Sony Ericsson P800 */ +#define FTDI_URBAN_0_PID 0xFC8A /* Sony Ericsson Urban, uart #0 */ +#define FTDI_URBAN_1_PID 0xFC8B /* Sony Ericsson Urban, uart #1 */ /* www.irtrans.de device */ #define FTDI_IRTRANS_PID 0xFC60 /* Product Id */ diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c index abf095be5753..2ee807523f53 100644 --- a/drivers/usb/serial/io_edgeport.c +++ b/drivers/usb/serial/io_edgeport.c @@ -3042,7 +3042,7 @@ static int edge_startup(struct usb_serial *serial) endpoint = &serial->interface->altsetting[0]. endpoint[i].desc; - buffer_size = le16_to_cpu(endpoint->wMaxPacketSize); + buffer_size = usb_endpoint_maxp(endpoint); if (!interrupt_in_found && (usb_endpoint_is_int_in(endpoint))) { /* we found a interrupt in endpoint */ @@ -3107,7 +3107,7 @@ static int edge_startup(struct usb_serial *serial) usb_rcvbulkpipe(dev, endpoint->bEndpointAddress), edge_serial->bulk_in_buffer, - le16_to_cpu(endpoint->wMaxPacketSize), + usb_endpoint_maxp(endpoint), edge_bulk_in_callback, edge_serial); bulk_in_found = true; diff --git a/drivers/usb/serial/ipw.c b/drivers/usb/serial/ipw.c index ca77e88836bd..5170799d6e94 100644 --- a/drivers/usb/serial/ipw.c +++ b/drivers/usb/serial/ipw.c @@ -47,6 +47,7 @@ #include <linux/usb.h> #include <linux/usb/serial.h> #include <linux/uaccess.h> +#include "usb-wwan.h" /* * Version Information @@ -185,7 +186,7 @@ static int ipw_open(struct tty_struct *tty, struct usb_serial_port *port) /*--2: Start reading from the device */ dbg("%s: setting up bulk read callback", __func__); - usb_serial_generic_open(tty, port); + usb_wwan_open(tty, port); /*--3: Tell the modem to open the floodgates on the rx bulk channel */ dbg("%s:asking modem for RxRead (RXBULK_ON)", __func__); @@ -219,6 +220,29 @@ static int ipw_open(struct tty_struct *tty, struct usb_serial_port *port) return 0; } +/* fake probe - only to allocate data structures */ +static int ipw_probe(struct usb_serial *serial, const struct usb_device_id *id) +{ + struct usb_wwan_intf_private *data; + + data = kzalloc(sizeof(struct usb_wwan_intf_private), GFP_KERNEL); + if (!data) + return -ENOMEM; + + spin_lock_init(&data->susp_lock); + usb_set_serial_data(serial, data); + return 0; +} + +static void ipw_release(struct usb_serial *serial) +{ + struct usb_wwan_intf_private *data = usb_get_serial_data(serial); + + usb_wwan_release(serial); + usb_set_serial_data(serial, NULL); + kfree(data); +} + static void ipw_dtr_rts(struct usb_serial_port *port, int on) { struct usb_device *dev = port->serial->dev; @@ -285,7 +309,7 @@ static void ipw_close(struct usb_serial_port *port) dev_err(&port->dev, "Disabling bulk RxRead failed (error = %d)\n", result); - usb_serial_generic_close(port); + usb_wwan_close(port); } static struct usb_serial_driver ipw_device = { @@ -297,9 +321,14 @@ static struct usb_serial_driver ipw_device = { .usb_driver = &usb_ipw_driver, .id_table = usb_ipw_ids, .num_ports = 1, + .disconnect = usb_wwan_disconnect, .open = ipw_open, .close = ipw_close, + .probe = ipw_probe, + .attach = usb_wwan_startup, + .release = ipw_release, .dtr_rts = ipw_dtr_rts, + .write = usb_wwan_write, }; diff --git a/drivers/usb/serial/opticon.c b/drivers/usb/serial/opticon.c index 96423f3c8ef3..c248a9147439 100644 --- a/drivers/usb/serial/opticon.c +++ b/drivers/usb/serial/opticon.c @@ -523,7 +523,7 @@ static int opticon_startup(struct usb_serial *serial) goto error; } - priv->buffer_size = le16_to_cpu(endpoint->wMaxPacketSize) * 2; + priv->buffer_size = usb_endpoint_maxp(endpoint) * 2; priv->bulk_in_buffer = kmalloc(priv->buffer_size, GFP_KERNEL); if (!priv->bulk_in_buffer) { dev_err(&priv->udev->dev, "out of memory\n"); diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index fe22e90bc879..89ae1f65e1b1 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -475,31 +475,54 @@ enum option_blacklist_reason { OPTION_BLACKLIST_RESERVED_IF = 2 }; +#define MAX_BL_NUM 8 struct option_blacklist_info { - const u32 infolen; /* number of interface numbers on blacklist */ - const u8 *ifaceinfo; /* pointer to the array holding the numbers */ - enum option_blacklist_reason reason; + /* bitfield of interface numbers for OPTION_BLACKLIST_SENDSETUP */ + const unsigned long sendsetup; + /* bitfield of interface numbers for OPTION_BLACKLIST_RESERVED_IF */ + const unsigned long reserved; }; -static const u8 four_g_w14_no_sendsetup[] = { 0, 1 }; static const struct option_blacklist_info four_g_w14_blacklist = { - .infolen = ARRAY_SIZE(four_g_w14_no_sendsetup), - .ifaceinfo = four_g_w14_no_sendsetup, - .reason = OPTION_BLACKLIST_SENDSETUP + .sendsetup = BIT(0) | BIT(1), }; -static const u8 alcatel_x200_no_sendsetup[] = { 0, 1 }; static const struct option_blacklist_info alcatel_x200_blacklist = { - .infolen = ARRAY_SIZE(alcatel_x200_no_sendsetup), - .ifaceinfo = alcatel_x200_no_sendsetup, - .reason = OPTION_BLACKLIST_SENDSETUP + .sendsetup = BIT(0) | BIT(1), +}; + +static const struct option_blacklist_info zte_0037_blacklist = { + .sendsetup = BIT(0) | BIT(1), }; -static const u8 zte_k3765_z_no_sendsetup[] = { 0, 1, 2 }; static const struct option_blacklist_info zte_k3765_z_blacklist = { - .infolen = ARRAY_SIZE(zte_k3765_z_no_sendsetup), - .ifaceinfo = zte_k3765_z_no_sendsetup, - .reason = OPTION_BLACKLIST_SENDSETUP + .sendsetup = BIT(0) | BIT(1) | BIT(2), + .reserved = BIT(4), +}; + +static const struct option_blacklist_info huawei_cdc12_blacklist = { + .reserved = BIT(1) | BIT(2), +}; + +static const struct option_blacklist_info net_intf1_blacklist = { + .reserved = BIT(1), +}; + +static const struct option_blacklist_info net_intf3_blacklist = { + .reserved = BIT(3), +}; + +static const struct option_blacklist_info net_intf4_blacklist = { + .reserved = BIT(4), +}; + +static const struct option_blacklist_info net_intf5_blacklist = { + .reserved = BIT(5), +}; + +static const struct option_blacklist_info zte_mf626_blacklist = { + .sendsetup = BIT(0) | BIT(1), + .reserved = BIT(4), }; static const struct usb_device_id option_ids[] = { @@ -599,12 +622,15 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E143D, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E143E, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E143F, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K4505, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K3765, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K4505, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t) &huawei_cdc12_blacklist }, + { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K3765, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t) &huawei_cdc12_blacklist }, { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_ETS1220, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E14AC, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K3806, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K4605, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K4605, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t) &huawei_cdc12_blacklist }, { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K3770, 0xff, 0x02, 0x31) }, { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K3770, 0xff, 0x02, 0x32) }, { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K3771, 0xff, 0x02, 0x31) }, @@ -705,7 +731,8 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_UC864E) }, { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_UC864G) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_MF622, 0xff, 0xff, 0xff) }, /* ZTE WCDMA products */ - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0002, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0002, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&net_intf1_blacklist }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0003, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0004, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0005, 0xff, 0xff, 0xff) }, @@ -720,51 +747,62 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x000f, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0010, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0011, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0012, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0012, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&net_intf1_blacklist }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0013, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0014, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_MF628, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0016, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0017, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0017, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&net_intf3_blacklist }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0018, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0019, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0020, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0021, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0021, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0022, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0023, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0024, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0025, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0025, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&net_intf1_blacklist }, /* { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0026, 0xff, 0xff, 0xff) }, */ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0028, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0029, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0030, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_MF626, 0xff, - 0xff, 0xff), .driver_info = (kernel_ulong_t)&four_g_w14_blacklist }, + 0xff, 0xff), .driver_info = (kernel_ulong_t)&zte_mf626_blacklist }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0032, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0033, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0034, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0037, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0037, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&zte_0037_blacklist }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0038, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0039, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0040, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0042, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0042, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0043, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0044, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0048, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0049, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0049, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&net_intf5_blacklist }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0050, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0051, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0052, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0052, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, /* { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0053, 0xff, 0xff, 0xff) }, */ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0054, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0055, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0055, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&net_intf1_blacklist }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0056, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0057, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0058, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0058, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0059, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0061, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0062, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0063, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0063, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0064, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0065, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0066, 0xff, 0xff, 0xff) }, @@ -779,11 +817,13 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0083, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0086, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0087, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0104, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0104, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0105, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0106, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0108, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0113, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0113, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&net_intf5_blacklist }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0117, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0118, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0121, 0xff, 0xff, 0xff) }, @@ -1214,10 +1254,35 @@ static void __exit option_exit(void) module_init(option_init); module_exit(option_exit); +static bool is_blacklisted(const u8 ifnum, enum option_blacklist_reason reason, + const struct option_blacklist_info *blacklist) +{ + unsigned long num; + const unsigned long *intf_list; + + if (blacklist) { + if (reason == OPTION_BLACKLIST_SENDSETUP) + intf_list = &blacklist->sendsetup; + else if (reason == OPTION_BLACKLIST_RESERVED_IF) + intf_list = &blacklist->reserved; + else { + BUG_ON(reason); + return false; + } + + for_each_set_bit(num, intf_list, MAX_BL_NUM + 1) { + if (num == ifnum) + return true; + } + } + return false; +} + static int option_probe(struct usb_serial *serial, const struct usb_device_id *id) { struct usb_wwan_intf_private *data; + /* D-Link DWM 652 still exposes CD-Rom emulation interface in modem mode */ if (serial->dev->descriptor.idVendor == DLINK_VENDOR_ID && serial->dev->descriptor.idProduct == DLINK_PRODUCT_DWM_652 && @@ -1230,14 +1295,14 @@ static int option_probe(struct usb_serial *serial, serial->interface->cur_altsetting->desc.bInterfaceClass != 0xff) return -ENODEV; - /* Don't bind network interfaces on Huawei K3765, K4505 & K4605 */ - if (serial->dev->descriptor.idVendor == HUAWEI_VENDOR_ID && - (serial->dev->descriptor.idProduct == HUAWEI_PRODUCT_K3765 || - serial->dev->descriptor.idProduct == HUAWEI_PRODUCT_K4505 || - serial->dev->descriptor.idProduct == HUAWEI_PRODUCT_K4605) && - (serial->interface->cur_altsetting->desc.bInterfaceNumber == 1 || - serial->interface->cur_altsetting->desc.bInterfaceNumber == 2)) - return -ENODEV; + /* Don't bind reserved interfaces (like network ones) which often have + * the same class/subclass/protocol as the serial interfaces. Look at + * the Windows driver .INF files for reserved interface numbers. + */ + if (is_blacklisted( + serial->interface->cur_altsetting->desc.bInterfaceNumber, + OPTION_BLACKLIST_RESERVED_IF, + (const struct option_blacklist_info *) id->driver_info)) /* Don't bind network interface on Samsung GT-B3730, it is handled by a separate module */ if (serial->dev->descriptor.idVendor == SAMSUNG_VENDOR_ID && @@ -1246,7 +1311,6 @@ static int option_probe(struct usb_serial *serial, return -ENODEV; data = serial->private = kzalloc(sizeof(struct usb_wwan_intf_private), GFP_KERNEL); - if (!data) return -ENOMEM; data->send_setup = option_send_setup; @@ -1255,23 +1319,6 @@ static int option_probe(struct usb_serial *serial, return 0; } -static enum option_blacklist_reason is_blacklisted(const u8 ifnum, - const struct option_blacklist_info *blacklist) -{ - const u8 *info; - int i; - - if (blacklist) { - info = blacklist->ifaceinfo; - - for (i = 0; i < blacklist->infolen; i++) { - if (info[i] == ifnum) - return blacklist->reason; - } - } - return OPTION_BLACKLIST_NONE; -} - static void option_instat_callback(struct urb *urb) { int err; @@ -1343,9 +1390,8 @@ static int option_send_setup(struct usb_serial_port *port) int val = 0; dbg("%s", __func__); - if (is_blacklisted(ifNum, - (struct option_blacklist_info *) intfdata->private) - == OPTION_BLACKLIST_SENDSETUP) { + if (is_blacklisted(ifNum, OPTION_BLACKLIST_SENDSETUP, + (struct option_blacklist_info *) intfdata->private)) { dbg("No send_setup on blacklisted interface #%d\n", ifNum); return -EIO; } diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index 1d33260de014..9083d1e616b4 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -92,6 +92,7 @@ static const struct usb_device_id id_table[] = { { USB_DEVICE(SANWA_VENDOR_ID, SANWA_PRODUCT_ID) }, { USB_DEVICE(ADLINK_VENDOR_ID, ADLINK_ND6530_PRODUCT_ID) }, { USB_DEVICE(WINCHIPHEAD_VENDOR_ID, WINCHIPHEAD_USBSER_PRODUCT_ID) }, + { USB_DEVICE(SMART_VENDOR_ID, SMART_PRODUCT_ID) }, { } /* Terminating entry */ }; @@ -360,9 +361,6 @@ static void pl2303_set_termios(struct tty_struct *tty, tmp >>= 2; buf[1] <<= 1; } - if (tmp > 256) { - tmp %= 256; - } buf[0] = tmp; } } diff --git a/drivers/usb/serial/pl2303.h b/drivers/usb/serial/pl2303.h index ca0d237683b3..3d10d7f02072 100644 --- a/drivers/usb/serial/pl2303.h +++ b/drivers/usb/serial/pl2303.h @@ -148,3 +148,8 @@ /* WinChipHead USB->RS 232 adapter */ #define WINCHIPHEAD_VENDOR_ID 0x4348 #define WINCHIPHEAD_USBSER_PRODUCT_ID 0x5523 + +/* SMART USB Serial Adapter */ +#define SMART_VENDOR_ID 0x0b8c +#define SMART_PRODUCT_ID 0x2303 + diff --git a/drivers/usb/serial/qcserial.c b/drivers/usb/serial/qcserial.c index aeccc7f0a93c..b9bb24729c99 100644 --- a/drivers/usb/serial/qcserial.c +++ b/drivers/usb/serial/qcserial.c @@ -28,6 +28,7 @@ static const struct usb_device_id id_table[] = { {USB_DEVICE(0x05c6, 0x9212)}, /* Acer Gobi Modem Device */ {USB_DEVICE(0x03f0, 0x1f1d)}, /* HP un2400 Gobi Modem Device */ {USB_DEVICE(0x03f0, 0x201d)}, /* HP un2400 Gobi QDL Device */ + {USB_DEVICE(0x03f0, 0x371d)}, /* HP un2430 Mobile Broadband Module */ {USB_DEVICE(0x04da, 0x250d)}, /* Panasonic Gobi Modem device */ {USB_DEVICE(0x04da, 0x250c)}, /* Panasonic Gobi QDL device */ {USB_DEVICE(0x413c, 0x8172)}, /* Dell Gobi Modem device */ @@ -84,6 +85,7 @@ static const struct usb_device_id id_table[] = { {USB_DEVICE(0x16d8, 0x8002)}, /* CMDTech Gobi 2000 Modem device (VU922) */ {USB_DEVICE(0x05c6, 0x9204)}, /* Gobi 2000 QDL device */ {USB_DEVICE(0x05c6, 0x9205)}, /* Gobi 2000 Modem device */ + {USB_DEVICE(0x1199, 0x9013)}, /* Sierra Wireless Gobi 3000 Modem device (MC8355) */ { } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, id_table); diff --git a/drivers/usb/serial/symbolserial.c b/drivers/usb/serial/symbolserial.c index d9457bd4fe10..7096f799b071 100644 --- a/drivers/usb/serial/symbolserial.c +++ b/drivers/usb/serial/symbolserial.c @@ -226,7 +226,7 @@ static int symbol_startup(struct usb_serial *serial) goto error; } - priv->buffer_size = le16_to_cpu(endpoint->wMaxPacketSize) * 2; + priv->buffer_size = usb_endpoint_maxp(endpoint) * 2; priv->int_buffer = kmalloc(priv->buffer_size, GFP_KERNEL); if (!priv->int_buffer) { dev_err(&priv->udev->dev, "out of memory\n"); diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 1c031309ab25..cc274fdf2627 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -912,7 +912,7 @@ int usb_serial_probe(struct usb_interface *interface, goto probe_error; } buffer_size = max_t(int, serial->type->bulk_in_size, - le16_to_cpu(endpoint->wMaxPacketSize)); + usb_endpoint_maxp(endpoint)); port->bulk_in_size = buffer_size; port->bulk_in_endpointAddress = endpoint->bEndpointAddress; port->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL); @@ -942,7 +942,7 @@ int usb_serial_probe(struct usb_interface *interface, goto probe_error; buffer_size = serial->type->bulk_out_size; if (!buffer_size) - buffer_size = le16_to_cpu(endpoint->wMaxPacketSize); + buffer_size = usb_endpoint_maxp(endpoint); port->bulk_out_size = buffer_size; port->bulk_out_endpointAddress = endpoint->bEndpointAddress; port->bulk_out_buffer = kmalloc(buffer_size, GFP_KERNEL); @@ -990,7 +990,7 @@ int usb_serial_probe(struct usb_interface *interface, "No free urbs available\n"); goto probe_error; } - buffer_size = le16_to_cpu(endpoint->wMaxPacketSize); + buffer_size = usb_endpoint_maxp(endpoint); port->interrupt_in_endpointAddress = endpoint->bEndpointAddress; port->interrupt_in_buffer = kmalloc(buffer_size, @@ -1021,7 +1021,7 @@ int usb_serial_probe(struct usb_interface *interface, "No free urbs available\n"); goto probe_error; } - buffer_size = le16_to_cpu(endpoint->wMaxPacketSize); + buffer_size = usb_endpoint_maxp(endpoint); port->interrupt_out_size = buffer_size; port->interrupt_out_endpointAddress = endpoint->bEndpointAddress; diff --git a/drivers/usb/storage/Kconfig b/drivers/usb/storage/Kconfig index bedc4b9f2ac4..fe2d803a6347 100644 --- a/drivers/usb/storage/Kconfig +++ b/drivers/usb/storage/Kconfig @@ -42,7 +42,7 @@ config USB_STORAGE_REALTEK config REALTEK_AUTOPM bool "Realtek Card Reader autosuspend support" - depends on USB_STORAGE_REALTEK && CONFIG_PM_RUNTIME + depends on USB_STORAGE_REALTEK && PM_RUNTIME default y config USB_STORAGE_DATAFAB diff --git a/drivers/usb/storage/realtek_cr.c b/drivers/usb/storage/realtek_cr.c index 6fd13068481b..0ce5f79197e7 100644 --- a/drivers/usb/storage/realtek_cr.c +++ b/drivers/usb/storage/realtek_cr.c @@ -292,6 +292,52 @@ static int rts51x_bulk_transport(struct us_data *us, u8 lun, return USB_STOR_TRANSPORT_ERROR; } +static int rts51x_bulk_transport_special(struct us_data *us, u8 lun, + u8 *cmd, int cmd_len, u8 *buf, int buf_len, + enum dma_data_direction dir, int *act_len) +{ + struct bulk_cb_wrap *bcb = (struct bulk_cb_wrap *) us->iobuf; + struct bulk_cs_wrap *bcs = (struct bulk_cs_wrap *) us->iobuf; + int result; + unsigned int cswlen; + unsigned int cbwlen = US_BULK_CB_WRAP_LEN; + + /* set up the command wrapper */ + bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN); + bcb->DataTransferLength = cpu_to_le32(buf_len); + bcb->Flags = (dir == DMA_FROM_DEVICE) ? 1 << 7 : 0; + bcb->Tag = ++us->tag; + bcb->Lun = lun; + bcb->Length = cmd_len; + + /* copy the command payload */ + memset(bcb->CDB, 0, sizeof(bcb->CDB)); + memcpy(bcb->CDB, cmd, bcb->Length); + + /* send it to out endpoint */ + result = usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe, + bcb, cbwlen, NULL); + if (result != USB_STOR_XFER_GOOD) + return USB_STOR_TRANSPORT_ERROR; + + /* DATA STAGE */ + /* send/receive data payload, if there is any */ + + if (buf && buf_len) { + unsigned int pipe = (dir == DMA_FROM_DEVICE) ? + us->recv_bulk_pipe : us->send_bulk_pipe; + result = usb_stor_bulk_transfer_buf(us, pipe, + buf, buf_len, NULL); + if (result == USB_STOR_XFER_ERROR) + return USB_STOR_TRANSPORT_ERROR; + } + + /* get CSW for device status */ + result = usb_bulk_msg(us->pusb_dev, us->recv_bulk_pipe, bcs, + US_BULK_CS_WRAP_LEN, &cswlen, 250); + return result; +} + /* Determine what the maximum LUN supported is */ static int rts51x_get_max_lun(struct us_data *us) { @@ -319,6 +365,11 @@ static int rts51x_read_mem(struct us_data *us, u16 addr, u8 *data, u16 len) { int retval; u8 cmnd[12] = { 0 }; + u8 *buf; + + buf = kmalloc(len, GFP_NOIO); + if (buf == NULL) + return USB_STOR_TRANSPORT_ERROR; US_DEBUGP("%s, addr = 0x%x, len = %d\n", __func__, addr, len); @@ -330,10 +381,14 @@ static int rts51x_read_mem(struct us_data *us, u16 addr, u8 *data, u16 len) cmnd[5] = (u8) len; retval = rts51x_bulk_transport(us, 0, cmnd, 12, - data, len, DMA_FROM_DEVICE, NULL); - if (retval != USB_STOR_TRANSPORT_GOOD) + buf, len, DMA_FROM_DEVICE, NULL); + if (retval != USB_STOR_TRANSPORT_GOOD) { + kfree(buf); return -EIO; + } + memcpy(data, buf, len); + kfree(buf); return 0; } @@ -341,6 +396,12 @@ static int rts51x_write_mem(struct us_data *us, u16 addr, u8 *data, u16 len) { int retval; u8 cmnd[12] = { 0 }; + u8 *buf; + + buf = kmalloc(len, GFP_NOIO); + if (buf == NULL) + return USB_STOR_TRANSPORT_ERROR; + memcpy(buf, data, len); US_DEBUGP("%s, addr = 0x%x, len = %d\n", __func__, addr, len); @@ -352,7 +413,8 @@ static int rts51x_write_mem(struct us_data *us, u16 addr, u8 *data, u16 len) cmnd[5] = (u8) len; retval = rts51x_bulk_transport(us, 0, cmnd, 12, - data, len, DMA_TO_DEVICE, NULL); + buf, len, DMA_TO_DEVICE, NULL); + kfree(buf); if (retval != USB_STOR_TRANSPORT_GOOD) return -EIO; @@ -364,6 +426,11 @@ static int rts51x_read_status(struct us_data *us, { int retval; u8 cmnd[12] = { 0 }; + u8 *buf; + + buf = kmalloc(len, GFP_NOIO); + if (buf == NULL) + return USB_STOR_TRANSPORT_ERROR; US_DEBUGP("%s, lun = %d\n", __func__, lun); @@ -371,10 +438,14 @@ static int rts51x_read_status(struct us_data *us, cmnd[1] = 0x09; retval = rts51x_bulk_transport(us, lun, cmnd, 12, - status, len, DMA_FROM_DEVICE, actlen); - if (retval != USB_STOR_TRANSPORT_GOOD) + buf, len, DMA_FROM_DEVICE, actlen); + if (retval != USB_STOR_TRANSPORT_GOOD) { + kfree(buf); return -EIO; + } + memcpy(status, buf, len); + kfree(buf); return 0; } @@ -433,6 +504,29 @@ static int enable_oscillator(struct us_data *us) return 0; } +static int __do_config_autodelink(struct us_data *us, u8 *data, u16 len) +{ + int retval; + u16 addr = 0xFE47; + u8 cmnd[12] = {0}; + + US_DEBUGP("%s, addr = 0x%x, len = %d\n", __FUNCTION__, addr, len); + + cmnd[0] = 0xF0; + cmnd[1] = 0x0E; + cmnd[2] = (u8)(addr >> 8); + cmnd[3] = (u8)addr; + cmnd[4] = (u8)(len >> 8); + cmnd[5] = (u8)len; + + retval = rts51x_bulk_transport_special(us, 0, cmnd, 12, data, len, DMA_TO_DEVICE, NULL); + if (retval != USB_STOR_TRANSPORT_GOOD) { + return -EIO; + } + + return 0; +} + static int do_config_autodelink(struct us_data *us, int enable, int force) { int retval; @@ -453,7 +547,8 @@ static int do_config_autodelink(struct us_data *us, int enable, int force) US_DEBUGP("In %s,set 0xfe47 to 0x%x\n", __func__, value); - retval = rts51x_write_mem(us, 0xFE47, &value, 1); + /* retval = rts51x_write_mem(us, 0xFE47, &value, 1); */ + retval = __do_config_autodelink(us, &value, 1); if (retval < 0) return -EIO; @@ -485,7 +580,8 @@ static int config_autodelink_after_power_on(struct us_data *us) SET_BIT(value, 7); - retval = rts51x_write_mem(us, 0xFE47, &value, 1); + /* retval = rts51x_write_mem(us, 0xFE47, &value, 1); */ + retval = __do_config_autodelink(us, &value, 1); if (retval < 0) return -EIO; @@ -506,7 +602,8 @@ static int config_autodelink_after_power_on(struct us_data *us) CLR_BIT(value, 7); } - retval = rts51x_write_mem(us, 0xFE47, &value, 1); + /* retval = rts51x_write_mem(us, 0xFE47, &value, 1); */ + retval = __do_config_autodelink(us, &value, 1); if (retval < 0) return -EIO; @@ -583,7 +680,8 @@ static int config_autodelink_before_power_down(struct us_data *us) if (CHECK_ID(chip, 0x0138, 0x3882)) SET_BIT(value, 2); - retval = rts51x_write_mem(us, 0xFE47, &value, 1); + /* retval = rts51x_write_mem(us, 0xFE47, &value, 1); */ + retval = __do_config_autodelink(us, &value, 1); if (retval < 0) return -EIO; } diff --git a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c index e8ae21b2d387..ff32390d61e5 100644 --- a/drivers/usb/storage/transport.c +++ b/drivers/usb/storage/transport.c @@ -691,6 +691,9 @@ void usb_stor_invoke_transport(struct scsi_cmnd *srb, struct us_data *us) int temp_result; struct scsi_eh_save ses; int sense_size = US_SENSE_SIZE; + struct scsi_sense_hdr sshdr; + const u8 *scdd; + u8 fm_ili; /* device supports and needs bigger sense buffer */ if (us->fflags & US_FL_SANE_SENSE) @@ -774,32 +777,30 @@ Retry_Sense: srb->sense_buffer[7] = (US_SENSE_SIZE - 8); } + scsi_normalize_sense(srb->sense_buffer, SCSI_SENSE_BUFFERSIZE, + &sshdr); + US_DEBUGP("-- Result from auto-sense is %d\n", temp_result); US_DEBUGP("-- code: 0x%x, key: 0x%x, ASC: 0x%x, ASCQ: 0x%x\n", - srb->sense_buffer[0], - srb->sense_buffer[2] & 0xf, - srb->sense_buffer[12], - srb->sense_buffer[13]); + sshdr.response_code, sshdr.sense_key, + sshdr.asc, sshdr.ascq); #ifdef CONFIG_USB_STORAGE_DEBUG - usb_stor_show_sense( - srb->sense_buffer[2] & 0xf, - srb->sense_buffer[12], - srb->sense_buffer[13]); + usb_stor_show_sense(sshdr.sense_key, sshdr.asc, sshdr.ascq); #endif /* set the result so the higher layers expect this data */ srb->result = SAM_STAT_CHECK_CONDITION; + scdd = scsi_sense_desc_find(srb->sense_buffer, + SCSI_SENSE_BUFFERSIZE, 4); + fm_ili = (scdd ? scdd[3] : srb->sense_buffer[2]) & 0xA0; + /* We often get empty sense data. This could indicate that * everything worked or that there was an unspecified * problem. We have to decide which. */ - if ( /* Filemark 0, ignore EOM, ILI 0, no sense */ - (srb->sense_buffer[2] & 0xaf) == 0 && - /* No ASC or ASCQ */ - srb->sense_buffer[12] == 0 && - srb->sense_buffer[13] == 0) { - + if (sshdr.sense_key == 0 && sshdr.asc == 0 && sshdr.ascq == 0 && + fm_ili == 0) { /* If things are really okay, then let's show that. * Zero out the sense buffer so the higher layers * won't realize we did an unsolicited auto-sense. @@ -814,7 +815,10 @@ Retry_Sense: */ } else { srb->result = DID_ERROR << 16; - srb->sense_buffer[2] = HARDWARE_ERROR; + if ((sshdr.response_code & 0x72) == 0x72) + srb->sense_buffer[1] = HARDWARE_ERROR; + else + srb->sense_buffer[2] = HARDWARE_ERROR; } } } diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c index 0ca095820f3e..c325e69415a1 100644 --- a/drivers/usb/storage/usb.c +++ b/drivers/usb/storage/usb.c @@ -831,12 +831,22 @@ static int usb_stor_scan_thread(void * __us) dev_dbg(dev, "device found\n"); - set_freezable(); - /* Wait for the timeout to expire or for a disconnect */ + set_freezable_with_signal(); + /* + * Wait for the timeout to expire or for a disconnect + * + * We can't freeze in this thread or we risk causing khubd to + * fail to freeze, but we can't be non-freezable either. Nor can + * khubd freeze while waiting for scanning to complete as it may + * hold the device lock, causing a hang when suspending devices. + * So we request a fake signal when freezing and use + * interruptible sleep to kick us out of our wait early when + * freezing happens. + */ if (delay_use > 0) { dev_dbg(dev, "waiting for device to settle " "before scanning\n"); - wait_event_freezable_timeout(us->delay_wait, + wait_event_interruptible_timeout(us->delay_wait, test_bit(US_FLIDX_DONT_SCAN, &us->dflags), delay_use * HZ); } diff --git a/drivers/usb/usb-common.c b/drivers/usb/usb-common.c new file mode 100644 index 000000000000..d29503e954ab --- /dev/null +++ b/drivers/usb/usb-common.c @@ -0,0 +1,35 @@ +/* + * Provides code common for host and device side USB. + * + * 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, version 2. + * + * If either host side (ie. CONFIG_USB=y) or device side USB stack + * (ie. CONFIG_USB_GADGET=y) is compiled in the kernel, this module is + * compiled-in as well. Otherwise, if either of the two stacks is + * compiled as module, this file is compiled as module as well. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/usb/ch9.h> + +const char *usb_speed_string(enum usb_device_speed speed) +{ + static const char *const names[] = { + [USB_SPEED_UNKNOWN] = "UNKNOWN", + [USB_SPEED_LOW] = "low-speed", + [USB_SPEED_FULL] = "full-speed", + [USB_SPEED_HIGH] = "high-speed", + [USB_SPEED_WIRELESS] = "wireless", + [USB_SPEED_SUPER] = "super-speed", + }; + + if (speed < 0 || speed >= ARRAY_SIZE(names)) + speed = USB_SPEED_UNKNOWN; + return names[speed]; +} +EXPORT_SYMBOL_GPL(usb_speed_string); + +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/usb-skeleton.c b/drivers/usb/usb-skeleton.c index e24ce3123071..32d6fc953904 100644 --- a/drivers/usb/usb-skeleton.c +++ b/drivers/usb/usb-skeleton.c @@ -555,7 +555,7 @@ static int skel_probe(struct usb_interface *interface, if (!dev->bulk_in_endpointAddr && usb_endpoint_is_bulk_in(endpoint)) { /* we found a bulk in endpoint */ - buffer_size = le16_to_cpu(endpoint->wMaxPacketSize); + buffer_size = usb_endpoint_maxp(endpoint); dev->bulk_in_size = buffer_size; dev->bulk_in_endpointAddr = endpoint->bEndpointAddress; dev->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL); diff --git a/drivers/usb/wusbcore/wa-hc.c b/drivers/usb/wusbcore/wa-hc.c index 59a748a0e5da..0d1863c9edde 100644 --- a/drivers/usb/wusbcore/wa-hc.c +++ b/drivers/usb/wusbcore/wa-hc.c @@ -43,7 +43,7 @@ int wa_create(struct wahc *wa, struct usb_interface *iface) /* Fill up Data Transfer EP pointers */ wa->dti_epd = &iface->cur_altsetting->endpoint[1].desc; wa->dto_epd = &iface->cur_altsetting->endpoint[2].desc; - wa->xfer_result_size = le16_to_cpu(wa->dti_epd->wMaxPacketSize); + wa->xfer_result_size = usb_endpoint_maxp(wa->dti_epd); wa->xfer_result = kmalloc(wa->xfer_result_size, GFP_KERNEL); if (wa->xfer_result == NULL) goto error_xfer_result_alloc; diff --git a/include/linux/platform_data/dwc3-omap.h b/include/linux/platform_data/dwc3-omap.h new file mode 100644 index 000000000000..ada401244e0b --- /dev/null +++ b/include/linux/platform_data/dwc3-omap.h @@ -0,0 +1,47 @@ +/** + * dwc3-omap.h - OMAP Specific Glue layer, header. + * + * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com + * All rights reserved. + * + * Author: Felipe Balbi <balbi@ti.com> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The names of the above-listed copyright holders may not be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2, as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +enum dwc3_omap_utmi_mode { + DWC3_OMAP_UTMI_MODE_UNKNOWN = 0, + DWC3_OMAP_UTMI_MODE_HW, + DWC3_OMAP_UTMI_MODE_SW, +}; + +struct dwc3_omap_data { + enum dwc3_omap_utmi_mode utmi_mode; +}; diff --git a/include/linux/platform_data/mv_usb.h b/include/linux/platform_data/mv_usb.h new file mode 100644 index 000000000000..e9d9149ddf38 --- /dev/null +++ b/include/linux/platform_data/mv_usb.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2011 Marvell International Ltd. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#ifndef __MV_PLATFORM_USB_H +#define __MV_PLATFORM_USB_H + +enum pxa_ehci_type { + EHCI_UNDEFINED = 0, + PXA_U2OEHCI, /* pxa 168, 9xx */ + PXA_SPH, /* pxa 168, 9xx SPH */ + MMP3_HSIC, /* mmp3 hsic */ + MMP3_FSIC, /* mmp3 fsic */ +}; + +enum { + MV_USB_MODE_OTG, + MV_USB_MODE_HOST, +}; + +enum { + VBUS_LOW = 0, + VBUS_HIGH = 1 << 0, +}; + +struct mv_usb_addon_irq { + unsigned int irq; + int (*poll)(void); +}; + +struct mv_usb_platform_data { + unsigned int clknum; + char **clkname; + struct mv_usb_addon_irq *id; /* Only valid for OTG. ID pin change*/ + struct mv_usb_addon_irq *vbus; /* valid for OTG/UDC. VBUS change*/ + + /* only valid for HCD. OTG or Host only*/ + unsigned int mode; + + int (*phy_init)(unsigned int regbase); + void (*phy_deinit)(unsigned int regbase); + int (*set_vbus)(unsigned int vbus); +}; + +#endif diff --git a/include/linux/sched.h b/include/linux/sched.h index 41d0237fd449..600eb0a6f89a 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -2165,7 +2165,8 @@ extern int force_sigsegv(int, struct task_struct *); extern int force_sig_info(int, struct siginfo *, struct task_struct *); extern int __kill_pgrp_info(int sig, struct siginfo *info, struct pid *pgrp); extern int kill_pid_info(int sig, struct siginfo *info, struct pid *pid); -extern int kill_pid_info_as_uid(int, struct siginfo *, struct pid *, uid_t, uid_t, u32); +extern int kill_pid_info_as_cred(int, struct siginfo *, struct pid *, + const struct cred *, u32); extern int kill_pgrp(struct pid *pid, int sig, int priv); extern int kill_pid(struct pid *pid, int sig, int priv); extern int kill_proc_info(int, struct siginfo *, pid_t); diff --git a/include/linux/usb.h b/include/linux/usb.h index 73c7df489607..6f49a1b39fa6 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -292,6 +292,16 @@ struct usb_host_config { int extralen; }; +/* USB2.0 and USB3.0 device BOS descriptor set */ +struct usb_host_bos { + struct usb_bos_descriptor *desc; + + /* wireless cap descriptor is handled by wusb */ + struct usb_ext_cap_descriptor *ext_cap; + struct usb_ss_cap_descriptor *ss_cap; + struct usb_ss_container_id_descriptor *ss_id; +}; + int __usb_get_extra_descriptor(char *buffer, unsigned size, unsigned char type, void **ptr); #define usb_get_extra_descriptor(ifpoint, type, ptr) \ @@ -381,6 +391,7 @@ struct usb_tt; * @ep0: endpoint 0 data (default control pipe) * @dev: generic device interface * @descriptor: USB device descriptor + * @bos: USB device BOS descriptor set * @config: all of the device's configs * @actconfig: the active configuration * @ep_in: array of IN endpoints @@ -399,6 +410,9 @@ struct usb_tt; * FIXME -- complete doc * @authenticated: Crypto authentication passed * @wusb: device is Wireless USB + * @lpm_capable: device supports LPM + * @usb2_hw_lpm_capable: device can perform USB2 hardware LPM + * @usb2_hw_lpm_enabled: USB2 hardware LPM enabled * @string_langid: language ID for strings * @product: iProduct string, if present (static) * @manufacturer: iManufacturer string, if present (static) @@ -442,6 +456,7 @@ struct usb_device { struct device dev; struct usb_device_descriptor descriptor; + struct usb_host_bos *bos; struct usb_host_config *config; struct usb_host_config *actconfig; @@ -460,6 +475,9 @@ struct usb_device { unsigned authorized:1; unsigned authenticated:1; unsigned wusb:1; + unsigned lpm_capable:1; + unsigned usb2_hw_lpm_capable:1; + unsigned usb2_hw_lpm_enabled:1; int string_langid; /* static strings from the device */ @@ -1574,7 +1592,7 @@ usb_maxpacket(struct usb_device *udev, int pipe, int is_out) return 0; /* NOTE: only 0x07ff bits are for packet size... */ - return le16_to_cpu(ep->desc.wMaxPacketSize); + return usb_endpoint_maxp(&ep->desc); } /* ----------------------------------------------------------------------- */ diff --git a/include/linux/usb/ch9.h b/include/linux/usb/ch9.h index 0fd3fbdd8283..f32a64e57f97 100644 --- a/include/linux/usb/ch9.h +++ b/include/linux/usb/ch9.h @@ -34,6 +34,7 @@ #define __LINUX_USB_CH9_H #include <linux/types.h> /* __u8 etc */ +#include <asm/byteorder.h> /* le16_to_cpu */ /*-------------------------------------------------------------------------*/ @@ -143,6 +144,11 @@ #define USB_INTRF_FUNC_SUSPEND 0 /* function suspend */ #define USB_INTR_FUNC_SUSPEND_OPT_MASK 0xFF00 +/* + * Suspend Options, Table 9-7 USB 3.0 spec + */ +#define USB_INTRF_FUNC_SUSPEND_LP (1 << (8 + 0)) +#define USB_INTRF_FUNC_SUSPEND_RW (1 << (8 + 1)) #define USB_ENDPOINT_HALT 0 /* IN/OUT will STALL */ @@ -570,6 +576,17 @@ static inline int usb_endpoint_is_isoc_out( return usb_endpoint_xfer_isoc(epd) && usb_endpoint_dir_out(epd); } +/** + * usb_endpoint_maxp - get endpoint's max packet size + * @epd: endpoint to be checked + * + * Returns @epd's max packet + */ +static inline int usb_endpoint_maxp(const struct usb_endpoint_descriptor *epd) +{ + return le16_to_cpu(epd->wMaxPacketSize); +} + /*-------------------------------------------------------------------------*/ /* USB_DT_SS_ENDPOINT_COMP: SuperSpeed Endpoint Companion descriptor */ @@ -851,6 +868,18 @@ enum usb_device_speed { USB_SPEED_SUPER, /* usb 3.0 */ }; +#ifdef __KERNEL__ + +/** + * usb_speed_string() - Returns human readable-name of the speed. + * @speed: The speed to return human-readable name for. If it's not + * any of the speeds defined in usb_device_speed enum, string for + * USB_SPEED_UNKNOWN will be returned. + */ +extern const char *usb_speed_string(enum usb_device_speed speed); + +#endif + enum usb_device_state { /* NOTATTACHED isn't in the USB spec, and this state acts * the same as ATTACHED ... but it's clearer this way. diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h index 087f4b931833..1d3a67523ffc 100644 --- a/include/linux/usb/gadget.h +++ b/include/linux/usb/gadget.h @@ -437,9 +437,9 @@ static inline void usb_ep_fifo_flush(struct usb_ep *ep) struct usb_dcd_config_params { __u8 bU1devExitLat; /* U1 Device exit Latency */ -#define USB_DEFULT_U1_DEV_EXIT_LAT 0x01 /* Less then 1 microsec */ +#define USB_DEFAULT_U1_DEV_EXIT_LAT 0x01 /* Less then 1 microsec */ __le16 bU2DevExitLat; /* U2 Device exit Latency */ -#define USB_DEFULT_U2_DEV_EXIT_LAT 0x1F4 /* Less then 500 microsec */ +#define USB_DEFAULT_U2_DEV_EXIT_LAT 0x1F4 /* Less then 500 microsec */ }; diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h index 0097136ba45d..03354d557b79 100644 --- a/include/linux/usb/hcd.h +++ b/include/linux/usb/hcd.h @@ -178,7 +178,7 @@ struct usb_hcd { * this structure. */ unsigned long hcd_priv[0] - __attribute__ ((aligned(sizeof(unsigned long)))); + __attribute__ ((aligned(sizeof(s64)))); }; /* 2.4 does this a bit differently ... */ @@ -343,6 +343,7 @@ struct hc_driver { * address is set */ int (*update_device)(struct usb_hcd *, struct usb_device *); + int (*set_usb2_hw_lpm)(struct usb_hcd *, struct usb_device *, int); }; extern int usb_hcd_link_urb_to_ep(struct usb_hcd *hcd, struct urb *urb); diff --git a/include/linux/usb/r8a66597.h b/include/linux/usb/r8a66597.h index b6b8660d0c68..55805f9dcf21 100644 --- a/include/linux/usb/r8a66597.h +++ b/include/linux/usb/r8a66597.h @@ -48,6 +48,9 @@ struct r8a66597_platdata { /* (external controller only) set one = WR0_N shorted to WR1_N */ unsigned wr0_shorted_to_wr1:1; + + /* set one = using SUDMAC */ + unsigned sudmac:1; }; /* Register definitions */ @@ -417,5 +420,62 @@ struct r8a66597_platdata { #define USBSPD 0x00C0 #define RTPORT 0x0001 +/* SUDMAC registers */ +#define CH0CFG 0x00 +#define CH1CFG 0x04 +#define CH0BA 0x10 +#define CH1BA 0x14 +#define CH0BBC 0x18 +#define CH1BBC 0x1C +#define CH0CA 0x20 +#define CH1CA 0x24 +#define CH0CBC 0x28 +#define CH1CBC 0x2C +#define CH0DEN 0x30 +#define CH1DEN 0x34 +#define DSTSCLR 0x38 +#define DBUFCTRL 0x3C +#define DINTCTRL 0x40 +#define DINTSTS 0x44 +#define DINTSTSCLR 0x48 +#define CH0SHCTRL 0x50 +#define CH1SHCTRL 0x54 + +/* SUDMAC Configuration Registers */ +#define SENDBUFM 0x1000 /* b12: Transmit Buffer Mode */ +#define RCVENDM 0x0100 /* b8: Receive Data Transfer End Mode */ +#define LBA_WAIT 0x0030 /* b5-4: Local Bus Access Wait */ + +/* DMA Enable Registers */ +#define DEN 0x0001 /* b1: DMA Transfer Enable */ + +/* DMA Status Clear Register */ +#define CH1STCLR 0x0002 /* b2: Ch1 DMA Status Clear */ +#define CH0STCLR 0x0001 /* b1: Ch0 DMA Status Clear */ + +/* DMA Buffer Control Register */ +#define CH1BUFW 0x0200 /* b9: Ch1 DMA Buffer Data Transfer Enable */ +#define CH0BUFW 0x0100 /* b8: Ch0 DMA Buffer Data Transfer Enable */ +#define CH1BUFS 0x0002 /* b2: Ch1 DMA Buffer Data Status */ +#define CH0BUFS 0x0001 /* b1: Ch0 DMA Buffer Data Status */ + +/* DMA Interrupt Control Register */ +#define CH1ERRE 0x0200 /* b9: Ch1 SHwy Res Err Detect Int Enable */ +#define CH0ERRE 0x0100 /* b8: Ch0 SHwy Res Err Detect Int Enable */ +#define CH1ENDE 0x0002 /* b2: Ch1 DMA Transfer End Int Enable */ +#define CH0ENDE 0x0001 /* b1: Ch0 DMA Transfer End Int Enable */ + +/* DMA Interrupt Status Register */ +#define CH1ERRS 0x0200 /* b9: Ch1 SHwy Res Err Detect Int Status */ +#define CH0ERRS 0x0100 /* b8: Ch0 SHwy Res Err Detect Int Status */ +#define CH1ENDS 0x0002 /* b2: Ch1 DMA Transfer End Int Status */ +#define CH0ENDS 0x0001 /* b1: Ch0 DMA Transfer End Int Status */ + +/* DMA Interrupt Status Clear Register */ +#define CH1ERRC 0x0200 /* b9: Ch1 SHwy Res Err Detect Int Stat Clear */ +#define CH0ERRC 0x0100 /* b8: Ch0 SHwy Res Err Detect Int Stat Clear */ +#define CH1ENDC 0x0002 /* b2: Ch1 DMA Transfer End Int Stat Clear */ +#define CH0ENDC 0x0001 /* b1: Ch0 DMA Transfer End Int Stat Clear */ + #endif /* __LINUX_USB_R8A66597_H */ diff --git a/include/linux/usb/renesas_usbhs.h b/include/linux/usb/renesas_usbhs.h index 8977431259c6..e5a40c318548 100644 --- a/include/linux/usb/renesas_usbhs.h +++ b/include/linux/usb/renesas_usbhs.h @@ -82,6 +82,13 @@ struct renesas_usbhs_platform_callback { * get VBUS status function. */ int (*get_vbus)(struct platform_device *pdev); + + /* + * option: + * + * VBUS control is needed for Host + */ + int (*set_vbus)(struct platform_device *pdev, int enable); }; /* @@ -101,6 +108,8 @@ struct renesas_usbhs_driver_param { * option: * * for BUSWAIT :: BWAIT + * see + * renesas_usbhs/common.c :: usbhsc_set_buswait() * */ int buswait_bwait; @@ -127,6 +136,11 @@ struct renesas_usbhs_driver_param { * pio <--> dma border. */ int pio_dma_border; /* default is 64byte */ + + /* + * option: + */ + u32 has_otg:1; /* for controlling PWEN/EXTLP */ }; /* diff --git a/kernel/signal.c b/kernel/signal.c index 291c9700be75..d252be2d3de5 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -1344,13 +1344,24 @@ int kill_proc_info(int sig, struct siginfo *info, pid_t pid) return error; } +static int kill_as_cred_perm(const struct cred *cred, + struct task_struct *target) +{ + const struct cred *pcred = __task_cred(target); + if (cred->user_ns != pcred->user_ns) + return 0; + if (cred->euid != pcred->suid && cred->euid != pcred->uid && + cred->uid != pcred->suid && cred->uid != pcred->uid) + return 0; + return 1; +} + /* like kill_pid_info(), but doesn't use uid/euid of "current" */ -int kill_pid_info_as_uid(int sig, struct siginfo *info, struct pid *pid, - uid_t uid, uid_t euid, u32 secid) +int kill_pid_info_as_cred(int sig, struct siginfo *info, struct pid *pid, + const struct cred *cred, u32 secid) { int ret = -EINVAL; struct task_struct *p; - const struct cred *pcred; unsigned long flags; if (!valid_signal(sig)) @@ -1362,10 +1373,7 @@ int kill_pid_info_as_uid(int sig, struct siginfo *info, struct pid *pid, ret = -ESRCH; goto out_unlock; } - pcred = __task_cred(p); - if (si_fromuser(info) && - euid != pcred->suid && euid != pcred->uid && - uid != pcred->suid && uid != pcred->uid) { + if (si_fromuser(info) && !kill_as_cred_perm(cred, p)) { ret = -EPERM; goto out_unlock; } @@ -1384,7 +1392,7 @@ out_unlock: rcu_read_unlock(); return ret; } -EXPORT_SYMBOL_GPL(kill_pid_info_as_uid); +EXPORT_SYMBOL_GPL(kill_pid_info_as_cred); /* * kill_something_info() interprets pid in interesting ways just like kill(2). |