diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2011-01-07 13:16:28 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-01-07 13:16:28 -0800 |
commit | 3e5b08cbbf78bedd316904ab0cf3b27119433ee5 (patch) | |
tree | 0365745c1b7441c1868551c024410c829c3accc6 /drivers/usb/gadget | |
parent | da40d036fd716f0efb2917076220814b1e927ae1 (diff) | |
parent | 2af10844eb6ed104f9505bf3a7ba3ceb02264f31 (diff) |
Merge branch 'usb-next' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb-2.6
* 'usb-next' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb-2.6: (144 commits)
USB: add support for Dream Cheeky DL100B Webmail Notifier (1d34:0004)
USB: serial: ftdi_sio: add support for TIOCSERGETLSR
USB: ehci-mxc: Setup portsc register prior to accessing OTG viewport
USB: atmel_usba_udc: fix freeing irq in usba_udc_remove()
usb: ehci-omap: fix tll channel enable mask
usb: ohci-omap3: fix trivial typo
USB: gadget: ci13xxx: don't assume that PAGE_SIZE is 4096
USB: gadget: ci13xxx: fix complete() callback for no_interrupt rq's
USB: gadget: update ci13xxx to work with g_ether
USB: gadgets: ci13xxx: fix probing of compiled-in gadget drivers
Revert "USB: musb: pm: don't rely fully on clock support"
Revert "USB: musb: blackfin: pm: make it work"
USB: uas: Use GFP_NOIO instead of GFP_KERNEL in I/O submission path
USB: uas: Ensure we only bind to a UAS interface
USB: uas: Rename sense pipe and sense urb to status pipe and status urb
USB: uas: Use kzalloc instead of kmalloc
USB: uas: Fix up the Sense IU
usb: musb: core: kill unneeded #include's
DA8xx: assign name to MUSB IRQ resource
usb: gadget: g_ncm added
...
Manually fix up trivial conflicts in USB Kconfig changes in:
arch/arm/mach-omap2/Kconfig
arch/sh/Kconfig
drivers/usb/Kconfig
drivers/usb/host/ehci-hcd.c
and annoying chip clock data conflicts in:
arch/arm/mach-omap2/clock3xxx_data.c
arch/arm/mach-omap2/clock44xx_data.c
Diffstat (limited to 'drivers/usb/gadget')
28 files changed, 8604 insertions, 878 deletions
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 607d0db4a98..1dc9739277b 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -338,6 +338,19 @@ config USB_S3C2410_DEBUG boolean "S3C2410 udc debug messages" depends on USB_GADGET_S3C2410 +config USB_GADGET_PXA_U2O + boolean "PXA9xx Processor USB2.0 controller" + select USB_GADGET_DUALSPEED + help + PXA9xx Processor series include a high speed USB2.0 device + controller, which support high speed and full speed USB peripheral. + +config USB_PXA_U2O + tristate + depends on USB_GADGET_PXA_U2O + default USB_GADGET + select USB_GADGET_SELECTED + # # Controllers available in both integrated and discrete versions # @@ -414,8 +427,8 @@ config USB_FSL_QE default USB_GADGET select USB_GADGET_SELECTED -config USB_GADGET_CI13XXX - boolean "MIPS USB CI13xxx" +config USB_GADGET_CI13XXX_PCI + boolean "MIPS USB CI13xxx PCI UDC" depends on PCI select USB_GADGET_DUALSPEED help @@ -426,9 +439,9 @@ config USB_GADGET_CI13XXX dynamically linked module called "ci13xxx_udc" and force all gadget drivers to also be dynamically linked. -config USB_CI13XXX +config USB_CI13XXX_PCI tristate - depends on USB_GADGET_CI13XXX + depends on USB_GADGET_CI13XXX_PCI default USB_GADGET select USB_GADGET_SELECTED @@ -495,6 +508,49 @@ config USB_LANGWELL default USB_GADGET select USB_GADGET_SELECTED +config USB_GADGET_EG20T + boolean "Intel EG20T(Topcliff) USB Device controller" + depends on PCI + select USB_GADGET_DUALSPEED + help + This is a USB device driver for EG20T PCH. + EG20T PCH is the platform controller hub that is used in Intel's + general embedded platform. EG20T PCH has USB device interface. + Using this interface, it is able to access system devices connected + to USB device. + This driver enables USB device function. + USB device is a USB peripheral controller which + supports both full and high speed USB 2.0 data transfers. + This driver supports both control transfer and bulk transfer modes. + This driver dose not support interrupt transfer or isochronous + transfer modes. + +config USB_EG20T + tristate + depends on USB_GADGET_EG20T + default USB_GADGET + select USB_GADGET_SELECTED + +config USB_GADGET_CI13XXX_MSM + boolean "MIPS USB CI13xxx for MSM" + depends on ARCH_MSM + select USB_GADGET_DUALSPEED + select USB_MSM_OTG_72K + help + MSM SoC has chipidea USB controller. This driver uses + ci13xxx_udc core. + This driver depends on OTG driver for PHY initialization, + clock management, powering up VBUS, and power management. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "ci13xxx_msm" and force all + gadget drivers to also be dynamically linked. + +config USB_CI13XXX_MSM + tristate + depends on USB_GADGET_CI13XXX_MSM + default USB_GADGET + select USB_GADGET_SELECTED # # LAST -- dummy/emulated controller @@ -685,6 +741,19 @@ config USB_ETH_EEM If you say "y" here, the Ethernet gadget driver will use the EEM protocol rather than ECM. If unsure, say "n". +config USB_G_NCM + tristate "Network Control Model (NCM) support" + depends on NET + select CRC32 + help + This driver implements USB CDC NCM subclass standard. NCM is + an advanced protocol for Ethernet encapsulation, allows grouping + of several ethernet frames into one USB transfer and diffferent + alignment possibilities. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "g_ncm". + config USB_GADGETFS tristate "Gadget Filesystem (EXPERIMENTAL)" depends on EXPERIMENTAL diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 5780db42417..55f5e8ae592 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -21,9 +21,13 @@ fsl_usb2_udc-$(CONFIG_ARCH_MXC) += fsl_mxc_udc.o obj-$(CONFIG_USB_M66592) += m66592-udc.o obj-$(CONFIG_USB_R8A66597) += r8a66597-udc.o obj-$(CONFIG_USB_FSL_QE) += fsl_qe_udc.o -obj-$(CONFIG_USB_CI13XXX) += ci13xxx_udc.o +obj-$(CONFIG_USB_CI13XXX_PCI) += ci13xxx_pci.o obj-$(CONFIG_USB_S3C_HSOTG) += s3c-hsotg.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 +obj-$(CONFIG_USB_CI13XXX_MSM) += ci13xxx_msm.o # # USB gadget drivers @@ -43,6 +47,7 @@ g_hid-y := hid.o g_dbgp-y := dbgp.o g_nokia-y := nokia.o g_webcam-y := webcam.o +g_ncm-y := ncm.o obj-$(CONFIG_USB_ZERO) += g_zero.o obj-$(CONFIG_USB_AUDIO) += g_audio.o @@ -60,3 +65,4 @@ obj-$(CONFIG_USB_G_DBGP) += g_dbgp.o 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 diff --git a/drivers/usb/gadget/amd5536udc.c b/drivers/usb/gadget/amd5536udc.c index 9034e034472..f8dd7269d79 100644 --- a/drivers/usb/gadget/amd5536udc.c +++ b/drivers/usb/gadget/amd5536udc.c @@ -3359,7 +3359,6 @@ static int udc_probe(struct udc *dev) dev_set_name(&dev->gadget.dev, "gadget"); dev->gadget.dev.release = gadget_release; dev->gadget.name = name; - dev->gadget.name = name; dev->gadget.is_dualspeed = 1; /* init registers, interrupts, ... */ diff --git a/drivers/usb/gadget/atmel_usba_udc.c b/drivers/usb/gadget/atmel_usba_udc.c index 717ff653fa2..e7c65a4408f 100644 --- a/drivers/usb/gadget/atmel_usba_udc.c +++ b/drivers/usb/gadget/atmel_usba_udc.c @@ -2057,8 +2057,10 @@ static int __exit usba_udc_remove(struct platform_device *pdev) usba_ep_cleanup_debugfs(&usba_ep[i]); usba_cleanup_debugfs(udc); - if (gpio_is_valid(udc->vbus_pin)) + if (gpio_is_valid(udc->vbus_pin)) { + free_irq(gpio_to_irq(udc->vbus_pin), udc); gpio_free(udc->vbus_pin); + } free_irq(udc->irq, udc); kfree(usba_ep); diff --git a/drivers/usb/gadget/ci13xxx_msm.c b/drivers/usb/gadget/ci13xxx_msm.c new file mode 100644 index 00000000000..139ac941959 --- /dev/null +++ b/drivers/usb/gadget/ci13xxx_msm.c @@ -0,0 +1,134 @@ +/* Copyright (c) 2010, Code Aurora Forum. 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 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> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/usb/msm_hsusb_hw.h> +#include <linux/usb/ulpi.h> + +#include "ci13xxx_udc.c" + +#define MSM_USB_BASE (udc->regs) + +static irqreturn_t msm_udc_irq(int irq, void *data) +{ + return udc_irq(); +} + +static void ci13xxx_msm_notify_event(struct ci13xxx *udc, unsigned event) +{ + struct device *dev = udc->gadget.dev.parent; + int val; + + switch (event) { + case CI13XXX_CONTROLLER_RESET_EVENT: + dev_dbg(dev, "CI13XXX_CONTROLLER_RESET_EVENT received\n"); + writel(0, USB_AHBBURST); + writel(0, USB_AHBMODE); + break; + case CI13XXX_CONTROLLER_STOPPED_EVENT: + dev_dbg(dev, "CI13XXX_CONTROLLER_STOPPED_EVENT received\n"); + /* + * Put the transceiver in non-driving mode. Otherwise host + * may not detect soft-disconnection. + */ + val = otg_io_read(udc->transceiver, ULPI_FUNC_CTRL); + val &= ~ULPI_FUNC_CTRL_OPMODE_MASK; + val |= ULPI_FUNC_CTRL_OPMODE_NONDRIVING; + otg_io_write(udc->transceiver, val, ULPI_FUNC_CTRL); + break; + default: + dev_dbg(dev, "unknown ci13xxx_udc event\n"); + break; + } +} + +static struct ci13xxx_udc_driver ci13xxx_msm_udc_driver = { + .name = "ci13xxx_msm", + .flags = CI13XXX_REGS_SHARED | + CI13XXX_REQUIRE_TRANSCEIVER | + CI13XXX_PULLUP_ON_VBUS | + CI13XXX_DISABLE_STREAMING, + + .notify_event = ci13xxx_msm_notify_event, +}; + +static int ci13xxx_msm_probe(struct platform_device *pdev) +{ + struct resource *res; + void __iomem *regs; + int irq; + int ret; + + dev_dbg(&pdev->dev, "ci13xxx_msm_probe\n"); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "failed to get platform resource mem\n"); + return -ENXIO; + } + + regs = ioremap(res->start, resource_size(res)); + if (!regs) { + dev_err(&pdev->dev, "ioremap failed\n"); + return -ENOMEM; + } + + ret = udc_probe(&ci13xxx_msm_udc_driver, &pdev->dev, regs); + if (ret < 0) { + dev_err(&pdev->dev, "udc_probe failed\n"); + goto iounmap; + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "IRQ not found\n"); + ret = -ENXIO; + goto udc_remove; + } + + ret = request_irq(irq, msm_udc_irq, IRQF_SHARED, pdev->name, pdev); + if (ret < 0) { + dev_err(&pdev->dev, "request_irq failed\n"); + goto udc_remove; + } + + pm_runtime_no_callbacks(&pdev->dev); + pm_runtime_enable(&pdev->dev); + + return 0; + +udc_remove: + udc_remove(); +iounmap: + iounmap(regs); + + return ret; +} + +static struct platform_driver ci13xxx_msm_driver = { + .probe = ci13xxx_msm_probe, + .driver = { .name = "msm_hsusb", }, +}; + +static int __init ci13xxx_msm_init(void) +{ + return platform_driver_register(&ci13xxx_msm_driver); +} +module_init(ci13xxx_msm_init); diff --git a/drivers/usb/gadget/ci13xxx_pci.c b/drivers/usb/gadget/ci13xxx_pci.c new file mode 100644 index 00000000000..883ab5e832d --- /dev/null +++ b/drivers/usb/gadget/ci13xxx_pci.c @@ -0,0 +1,176 @@ +/* + * ci13xxx_pci.c - MIPS USB IP core family device controller + * + * Copyright (C) 2008 Chipidea - MIPS Technologies, Inc. All rights reserved. + * + * Author: David Lopo + * + * 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. + */ + +#include <linux/module.h> +#include <linux/pci.h> + +#include "ci13xxx_udc.c" + +/* driver name */ +#define UDC_DRIVER_NAME "ci13xxx_pci" + +/****************************************************************************** + * PCI block + *****************************************************************************/ +/** + * ci13xxx_pci_irq: interrut handler + * @irq: irq number + * @pdev: USB Device Controller interrupt source + * + * This function returns IRQ_HANDLED if the IRQ has been handled + * This is an ISR don't trace, use attribute interface instead + */ +static irqreturn_t ci13xxx_pci_irq(int irq, void *pdev) +{ + if (irq == 0) { + dev_err(&((struct pci_dev *)pdev)->dev, "Invalid IRQ0 usage!"); + return IRQ_HANDLED; + } + return udc_irq(); +} + +static struct ci13xxx_udc_driver ci13xxx_pci_udc_driver = { + .name = UDC_DRIVER_NAME, +}; + +/** + * ci13xxx_pci_probe: PCI probe + * @pdev: USB device controller being probed + * @id: PCI hotplug ID connecting controller to UDC framework + * + * This function returns an error code + * Allocates basic PCI resources for this USB device controller, and then + * invokes the udc_probe() method to start the UDC associated with it + */ +static int __devinit ci13xxx_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + void __iomem *regs = NULL; + int retval = 0; + + if (id == NULL) + return -EINVAL; + + retval = pci_enable_device(pdev); + if (retval) + goto done; + + if (!pdev->irq) { + dev_err(&pdev->dev, "No IRQ, check BIOS/PCI setup!"); + retval = -ENODEV; + goto disable_device; + } + + retval = pci_request_regions(pdev, UDC_DRIVER_NAME); + if (retval) + goto disable_device; + + /* BAR 0 holds all the registers */ + regs = pci_iomap(pdev, 0, 0); + if (!regs) { + dev_err(&pdev->dev, "Error mapping memory!"); + retval = -EFAULT; + goto release_regions; + } + pci_set_drvdata(pdev, (__force void *)regs); + + pci_set_master(pdev); + pci_try_set_mwi(pdev); + + retval = udc_probe(&ci13xxx_pci_udc_driver, &pdev->dev, regs); + if (retval) + goto iounmap; + + /* our device does not have MSI capability */ + + retval = request_irq(pdev->irq, ci13xxx_pci_irq, IRQF_SHARED, + UDC_DRIVER_NAME, pdev); + if (retval) + goto gadget_remove; + + return 0; + + gadget_remove: + udc_remove(); + iounmap: + pci_iounmap(pdev, regs); + release_regions: + pci_release_regions(pdev); + disable_device: + pci_disable_device(pdev); + done: + return retval; +} + +/** + * ci13xxx_pci_remove: PCI remove + * @pdev: USB Device Controller being removed + * + * Reverses the effect of ci13xxx_pci_probe(), + * first invoking the udc_remove() and then releases + * all PCI resources allocated for this USB device controller + */ +static void __devexit ci13xxx_pci_remove(struct pci_dev *pdev) +{ + free_irq(pdev->irq, pdev); + udc_remove(); + pci_iounmap(pdev, (__force void __iomem *)pci_get_drvdata(pdev)); + pci_release_regions(pdev); + pci_disable_device(pdev); +} + +/** + * PCI device table + * PCI device structure + * + * Check "pci.h" for details + */ +static DEFINE_PCI_DEVICE_TABLE(ci13xxx_pci_id_table) = { + { PCI_DEVICE(0x153F, 0x1004) }, + { PCI_DEVICE(0x153F, 0x1006) }, + { 0, 0, 0, 0, 0, 0, 0 /* end: all zeroes */ } +}; +MODULE_DEVICE_TABLE(pci, ci13xxx_pci_id_table); + +static struct pci_driver ci13xxx_pci_driver = { + .name = UDC_DRIVER_NAME, + .id_table = ci13xxx_pci_id_table, + .probe = ci13xxx_pci_probe, + .remove = __devexit_p(ci13xxx_pci_remove), +}; + +/** + * ci13xxx_pci_init: module init + * + * Driver load + */ +static int __init ci13xxx_pci_init(void) +{ + return pci_register_driver(&ci13xxx_pci_driver); +} +module_init(ci13xxx_pci_init); + +/** + * ci13xxx_pci_exit: module exit + * + * Driver unload + */ +static void __exit ci13xxx_pci_exit(void) +{ + pci_unregister_driver(&ci13xxx_pci_driver); +} +module_exit(ci13xxx_pci_exit); + +MODULE_AUTHOR("MIPS - David Lopo <dlopo@chipidea.mips.com>"); +MODULE_DESCRIPTION("MIPS CI13XXX USB Peripheral Controller"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("June 2008"); diff --git a/drivers/usb/gadget/ci13xxx_udc.c b/drivers/usb/gadget/ci13xxx_udc.c index 98b36fc88c7..31656a2b4ab 100644 --- a/drivers/usb/gadget/ci13xxx_udc.c +++ b/drivers/usb/gadget/ci13xxx_udc.c @@ -22,7 +22,6 @@ * - ENDPT: endpoint operations (Gadget API) * - GADGET: gadget operations (Gadget API) * - BUS: bus glue code, bus abstraction layer - * - PCI: PCI core interface and PCI resources (interrupts, memory...) * * Compile Options * - CONFIG_USB_GADGET_DEBUG_FILES: enable debug facilities @@ -60,11 +59,11 @@ #include <linux/io.h> #include <linux/irq.h> #include <linux/kernel.h> -#include <linux/module.h> -#include <linux/pci.h> #include <linux/slab.h> +#include <linux/pm_runtime.h> #include <linux/usb/ch9.h> #include <linux/usb/gadget.h> +#include <linux/usb/otg.h> #include "ci13xxx_udc.h" @@ -75,9 +74,6 @@ /* ctrl register bank access */ static DEFINE_SPINLOCK(udc_lock); -/* driver name */ -#define UDC_DRIVER_NAME "ci13xxx_udc" - /* control endpoint description */ static const struct usb_endpoint_descriptor ctrl_endpt_desc = { @@ -132,6 +128,9 @@ static struct { size_t size; /* bank size */ } hw_bank; +/* MSM specific */ +#define ABS_AHBBURST (0x0090UL) +#define ABS_AHBMODE (0x0098UL) /* UDC register map */ #define ABS_CAPLENGTH (0x100UL) #define ABS_HCCPARAMS (0x108UL) @@ -248,13 +247,7 @@ static u32 hw_ctest_and_write(u32 addr, u32 mask, u32 data) return (reg & mask) >> ffs_nr(mask); } -/** - * hw_device_reset: resets chip (execute without interruption) - * @base: register base address - * - * This function returns an error code - */ -static int hw_device_reset(void __iomem *base) +static int hw_device_init(void __iomem *base) { u32 reg; @@ -271,6 +264,28 @@ static int hw_device_reset(void __iomem *base) hw_bank.size += CAP_LAST; hw_bank.size /= sizeof(u32); + reg = hw_aread(ABS_DCCPARAMS, DCCPARAMS_DEN) >> ffs_nr(DCCPARAMS_DEN); + if (reg == 0 || reg > ENDPT_MAX) + return -ENODEV; + + hw_ep_max = reg; /* cache hw ENDPT_MAX */ + + /* setup lock mode ? */ + + /* ENDPTSETUPSTAT is '0' by default */ + + /* HCSPARAMS.bf.ppc SHOULD BE zero for device */ + + return 0; +} +/** + * hw_device_reset: resets chip (execute without interruption) + * @base: register base address + * + * This function returns an error code + */ +static int hw_device_reset(struct ci13xxx *udc) +{ /* should flush & stop before reset */ hw_cwrite(CAP_ENDPTFLUSH, ~0, ~0); hw_cwrite(CAP_USBCMD, USBCMD_RS, 0); @@ -279,6 +294,14 @@ static int hw_device_reset(void __iomem *base) while (hw_cread(CAP_USBCMD, USBCMD_RST)) udelay(10); /* not RTOS friendly */ + + if (udc->udc_driver->notify_event) + udc->udc_driver->notify_event(udc, + CI13XXX_CONTROLLER_RESET_EVENT); + + if (udc->udc_driver->flags && CI13XXX_DISABLE_STREAMING) + hw_cwrite(CAP_USBMODE, USBMODE_SDIS, USBMODE_SDIS); + /* USBMODE should be configured step by step */ hw_cwrite(CAP_USBMODE, USBMODE_CM, USBMODE_CM_IDLE); hw_cwrite(CAP_USBMODE, USBMODE_CM, USBMODE_CM_DEVICE); @@ -290,18 +313,6 @@ static int hw_device_reset(void __iomem *base) return -ENODEV; } - reg = hw_aread(ABS_DCCPARAMS, DCCPARAMS_DEN) >> ffs_nr(DCCPARAMS_DEN); - if (reg == 0 || reg > ENDPT_MAX) - return -ENODEV; - - hw_ep_max = reg; /* cache hw ENDPT_MAX */ - - /* setup lock mode ? */ - - /* ENDPTSETUPSTAT is '0' by default */ - - /* HCSPARAMS.bf.ppc SHOULD BE zero for device */ - return 0; } @@ -1449,7 +1460,7 @@ static int _hardware_enqueue(struct ci13xxx_ep *mEp, struct ci13xxx_req *mReq) mReq->ptr->page[0] = mReq->req.dma; for (i = 1; i < 5; i++) mReq->ptr->page[i] = - (mReq->req.dma + i * PAGE_SIZE) & ~TD_RESERVED_MASK; + (mReq->req.dma + i * CI13XXX_PAGE_SIZE) & ~TD_RESERVED_MASK; /* * QH configuration @@ -1540,7 +1551,7 @@ __acquires(mEp->lock) list_del_init(&mReq->queue); mReq->req.status = -ESHUTDOWN; - if (!mReq->req.no_interrupt && mReq->req.complete != NULL) { + if (mReq->req.complete != NULL) { spin_unlock(mEp->lock); mReq->req.complete(&mEp->ep, &mReq->req); spin_lock(mEp->lock); @@ -1557,8 +1568,6 @@ __acquires(mEp->lock) * Caller must hold lock */ static int _gadget_stop_activity(struct usb_gadget *gadget) -__releases(udc->lock) -__acquires(udc->lock) { struct usb_ep *ep; struct ci13xxx *udc = container_of(gadget, struct ci13xxx, gadget); @@ -1570,8 +1579,6 @@ __acquires(udc->lock) if (gadget == NULL) return -EINVAL; - spin_unlock(udc->lock); - /* flush all endpoints */ gadget_for_each_ep(ep, gadget) { usb_ep_fifo_flush(ep); @@ -1591,8 +1598,6 @@ __acquires(udc->lock) mEp->status = NULL; } - spin_lock(udc->lock); - return 0; } @@ -1621,6 +1626,7 @@ __acquires(udc->lock) dbg_event(0xFF, "BUS RST", 0); + spin_unlock(udc->lock); retval = _gadget_stop_activity(&udc->gadget); if (retval) goto done; @@ -1629,10 +1635,9 @@ __acquires(udc->lock) if (retval) goto done; - spin_unlock(udc->lock); retval = usb_ep_enable(&mEp->ep, &ctrl_endpt_desc); if (!retval) { - mEp->status = usb_ep_alloc_request(&mEp->ep, GFP_KERNEL); + mEp->status = usb_ep_alloc_request(&mEp->ep, GFP_ATOMIC); if (mEp->status == NULL) { usb_ep_disable(&mEp->ep); retval = -ENOMEM; @@ -1789,18 +1794,20 @@ __acquires(mEp->lock) dbg_done(_usb_addr(mEp), mReq->ptr->token, retval); - if (!mReq->req.no_interrupt && mReq->req.complete != NULL) { + if (!list_empty(&mEp->qh[mEp->dir].queue)) { + struct ci13xxx_req* mReqEnq; + + mReqEnq = list_entry(mEp->qh[mEp->dir].queue.next, + struct ci13xxx_req, queue); + _hardware_enqueue(mEp, mReqEnq); + } + + if (mReq->req.complete != NULL) { spin_unlock(mEp->lock); mReq->req.complete(&mEp->ep, &mReq->req); spin_lock(mEp->lock); } - if (!list_empty(&mEp->qh[mEp->dir].queue)) { - mReq = list_entry(mEp->qh[mEp->dir].queue.next, - struct ci13xxx_req, queue); - _hardware_enqueue(mEp, mReq); - } - done: return retval; } @@ -2061,7 +2068,6 @@ static struct usb_request *ep_alloc_request(struct usb_ep *ep, gfp_t gfp_flags) { struct ci13xxx_ep *mEp = container_of(ep, struct ci13xxx_ep, ep); struct ci13xxx_req *mReq = NULL; - unsigned long flags; trace("%p, %i", ep, gfp_flags); @@ -2070,8 +2076,6 @@ static struct usb_request *ep_alloc_request(struct usb_ep *ep, gfp_t gfp_flags) return NULL; } - spin_lock_irqsave(mEp->lock, flags); - mReq = kzalloc(sizeof(struct ci13xxx_req), gfp_flags); if (mReq != NULL) { INIT_LIST_HEAD(&mReq->queue); @@ -2086,8 +2090,6 @@ static struct usb_request *ep_alloc_request(struct usb_ep *ep, gfp_t gfp_flags) dbg_event(_usb_addr(mEp), "ALLOC", mReq == NULL); - spin_unlock_irqrestore(mEp->lock, flags); - return (mReq == NULL) ? NULL : &mReq->req; } @@ -2157,8 +2159,8 @@ static int ep_queue(struct usb_ep *ep, struct usb_request *req, goto done; } - if (req->length > (4 * PAGE_SIZE)) { - req->length = (4 * PAGE_SIZE); + if (req->length > (4 * CI13XXX_PAGE_SIZE)) { + req->length = (4 * CI13XXX_PAGE_SIZE); retval = -EMSGSIZE; warn("request length truncated"); } @@ -2170,8 +2172,10 @@ static int ep_queue(struct usb_ep *ep, struct usb_request *req, mReq->req.actual = 0; list_add_tail(&mReq->queue, &mEp->qh[mEp->dir].queue); - retval = _hardware_enqueue(mEp, mReq); - if (retval == -EALREADY || retval == -EBUSY) { + if (list_is_singular(&mEp->qh[mEp->dir].queue)) + retval = _hardware_enqueue(mEp, mReq); + + if (retval == -EALREADY) { dbg_event(_usb_addr(mEp), "QUEUE", retval); retval = 0; } @@ -2209,7 +2213,7 @@ static int ep_dequeue(struct usb_ep *ep, struct usb_request *req) list_del_init(&mReq->queue); req->status = -ECONNRESET; - if (!mReq->req.no_interrupt && mReq->req.complete != NULL) { + if (mReq->req.complete != NULL) { spin_unlock(mEp->lock); mReq->req.complete(&mEp->ep, &mReq->req); spin_lock(mEp->lock); @@ -2332,12 +2336,47 @@ static const struct usb_ep_ops usb_ep_ops = { /****************************************************************************** * GADGET block *****************************************************************************/ +static int ci13xxx_vbus_session(struct usb_gadget *_gadget, int is_active) +{ + struct ci13xxx *udc = container_of(_gadget, struct ci13xxx, gadget); + unsigned long flags; + int gadget_ready = 0; + + if (!(udc->udc_driver->flags & CI13XXX_PULLUP_ON_VBUS)) + return -EOPNOTSUPP; + + spin_lock_irqsave(udc->lock, flags); + udc->vbus_active = is_active; + if (udc->driver) + gadget_ready = 1; + spin_unlock_irqrestore(udc->lock, flags); + + if (gadget_ready) { + if (is_active) { + pm_runtime_get_sync(&_gadget->dev); + hw_device_reset(udc); + hw_device_state(udc->ci13xxx_ep[0].qh[RX].dma); + } else { + hw_device_state(0); + if (udc->udc_driver->notify_event) + udc->udc_driver->notify_event(udc, + CI13XXX_CONTROLLER_STOPPED_EVENT); + _gadget_stop_activity(&udc->gadget); + pm_runtime_put_sync(&_gadget->dev); + } + } + + return 0; +} + /** * Device operations part of the API to the USB controller hardware, * which don't involve endpoints (or i/o) * Check "usb_gadget.h" for details */ -static const struct usb_gadget_ops usb_gadget_ops; +static const struct usb_gadget_ops usb_gadget_ops = { + .vbus_session = ci13xxx_vbus_session, +}; /** * usb_gadget_probe_driver: register a gadget driver @@ -2358,7 +2397,6 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver, if (driver == NULL || bind == NULL || - driver->unbind == NULL || driver->setup == NULL || driver->disconnect == NULL || driver->suspend == NULL || @@ -2372,13 +2410,13 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver, /* alloc resources */ udc->qh_pool = dma_pool_create("ci13xxx_qh", &udc->gadget.dev, sizeof(struct ci13xxx_qh), - 64, PAGE_SIZE); + 64, CI13XXX_PAGE_SIZE); if (udc->qh_pool == NULL) return -ENOMEM; udc->td_pool = dma_pool_create("ci13xxx_td", &udc->gadget.dev, sizeof(struct ci13xxx_td), - 64, PAGE_SIZE); + 64, CI13XXX_PAGE_SIZE); if (udc->td_pool == NULL) { dma_pool_destroy(udc->qh_pool); udc->qh_pool = NULL; @@ -2390,7 +2428,6 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver, info("hw_ep_max = %d", hw_ep_max); |