diff options
Diffstat (limited to 'drivers/usb/gadget/ci13xxx_udc.c')
-rw-r--r-- | drivers/usb/gadget/ci13xxx_udc.c | 384 |
1 files changed, 165 insertions, 219 deletions
diff --git a/drivers/usb/gadget/ci13xxx_udc.c b/drivers/usb/gadget/ci13xxx_udc.c index 98b36fc88c77..f200e472e476 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; } @@ -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; @@ -2061,7 +2066,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 +2074,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 +2088,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; } @@ -2332,12 +2332,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 @@ -2390,7 +2425,6 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver, info("hw_ep_max = %d", hw_ep_max); udc->driver = driver; - udc->gadget.ops = NULL; udc->gadget.dev.driver = NULL; retval = 0; @@ -2410,9 +2444,11 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver, /* this allocation cannot be random */ for (k = RX; k <= TX; k++) { INIT_LIST_HEAD(&mEp->qh[k].queue); + spin_unlock_irqrestore(udc->lock, flags); mEp->qh[k].ptr = dma_pool_alloc(udc->qh_pool, GFP_KERNEL, &mEp->qh[k].dma); + spin_lock_irqsave(udc->lock, flags); if (mEp->qh[k].ptr == NULL) retval = -ENOMEM; else @@ -2429,7 +2465,6 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver, /* bind gadget */ driver->driver.bus = NULL; - udc->gadget.ops = &usb_gadget_ops; udc->gadget.dev.driver = &driver->driver; spin_unlock_irqrestore(udc->lock, flags); @@ -2437,12 +2472,24 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver, spin_lock_irqsave(udc->lock, flags); if (retval) { - udc->gadget.ops = NULL; udc->gadget.dev.driver = NULL; goto done; } + pm_runtime_get_sync(&udc->gadget.dev); + if (udc->udc_driver->flags & CI13XXX_PULLUP_ON_VBUS) { + if (udc->vbus_active) { + if (udc->udc_driver->flags & CI13XXX_REGS_SHARED) + hw_device_reset(udc); + } else { + pm_runtime_put_sync(&udc->gadget.dev); + goto done; + } + } + retval = hw_device_state(udc->ci13xxx_ep[0].qh[RX].dma); + if (retval) + pm_runtime_put_sync(&udc->gadget.dev); done: spin_unlock_irqrestore(udc->lock, flags); @@ -2475,19 +2522,22 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) spin_lock_irqsave(udc->lock, flags); - hw_device_state(0); - - /* unbind gadget */ - if (udc->gadget.ops != NULL) { + if (!(udc->udc_driver->flags & CI13XXX_PULLUP_ON_VBUS) || + udc->vbus_active) { + 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(&udc->gadget.dev); + } - spin_unlock_irqrestore(udc->lock, flags); - driver->unbind(&udc->gadget); /* MAY SLEEP */ - spin_lock_irqsave(udc->lock, flags); + /* unbind gadget */ + spin_unlock_irqrestore(udc->lock, flags); + driver->unbind(&udc->gadget); /* MAY SLEEP */ + spin_lock_irqsave(udc->lock, flags); - udc->gadget.ops = NULL; - udc->gadget.dev.driver = NULL; - } + udc->gadget.dev.driver = NULL; /* free resources */ for (i = 0; i < hw_ep_max; i++) { @@ -2544,6 +2594,14 @@ static irqreturn_t udc_irq(void) } spin_lock(udc->lock); + + if (udc->udc_driver->flags & CI13XXX_REGS_SHARED) { + if (hw_cread(CAP_USBMODE, USBMODE_CM) != + USBMODE_CM_DEVICE) { + spin_unlock(udc->lock); + return IRQ_NONE; + } + } intr = hw_test_and_clear_intr_active(); if (intr) { isr_statistics.hndl.buf[isr_statistics.hndl.idx++] = intr; @@ -2602,14 +2660,16 @@ static void udc_release(struct device *dev) * No interrupts active, the IRQ has not been requested yet * Kernel assumes 32-bit DMA operations by default, no need to dma_set_mask */ -static int udc_probe(struct device *dev, void __iomem *regs, const char *name) +static int udc_probe(struct ci13xxx_udc_driver *driver, struct device *dev, + void __iomem *regs) { struct ci13xxx *udc; int retval = 0; trace("%p, %p, %p", dev, regs, name); - if (dev == NULL || regs == NULL || name == NULL) + if (dev == NULL || regs == NULL || driver == NULL || + driver->name == NULL) return -EINVAL; udc = kzalloc(sizeof(struct ci13xxx), GFP_KERNEL); @@ -2617,42 +2677,77 @@ static int udc_probe(struct device *dev, void __iomem *regs, const char *name) return -ENOMEM; udc->lock = &udc_lock; + udc->regs = regs; + udc->udc_driver = driver; - retval = hw_device_reset(regs); - if (retval) - goto done; - - udc->gadget.ops = NULL; + udc->gadget.ops = &usb_gadget_ops; udc->gadget.speed = USB_SPEED_UNKNOWN; udc->gadget.is_dualspeed = 1; udc->gadget.is_otg = 0; - udc->gadget.name = name; + udc->gadget.name = driver->name; INIT_LIST_HEAD(&udc->gadget.ep_list); udc->gadget.ep0 = NULL; dev_set_name(&udc->gadget.dev, "gadget"); udc->gadget.dev.dma_mask = dev->dma_mask; + udc->gadget.dev.coherent_dma_mask = dev->coherent_dma_mask; udc->gadget.dev.parent = dev; udc->gadget.dev.release = udc_release; + retval = hw_device_init(regs); + if (retval < 0) + goto free_udc; + + udc->transceiver = otg_get_transceiver(); + + if (udc->udc_driver->flags & CI13XXX_REQUIRE_TRANSCEIVER) { + if (udc->transceiver == NULL) { + retval = -ENODEV; + goto free_udc; + } + } + + if (!(udc->udc_driver->flags & CI13XXX_REGS_SHARED)) { + retval = hw_device_reset(udc); + if (retval) + goto put_transceiver; + } + retval = device_register(&udc->gadget.dev); - if (retval) - goto done; + if (retval) { + put_device(&udc->gadget.dev); + goto put_transceiver; + } #ifdef CONFIG_USB_GADGET_DEBUG_FILES retval = dbg_create_files(&udc->gadget.dev); #endif - if (retval) { - device_unregister(&udc->gadget.dev); - goto done; + if (retval) + goto unreg_device; + + if (udc->transceiver) { + retval = otg_set_peripheral(udc->transceiver, &udc->gadget); + if (retval) + goto remove_dbg; } + pm_runtime_no_callbacks(&udc->gadget.dev); + pm_runtime_enable(&udc->gadget.dev); _udc = udc; return retval; - done: err("error = %i", retval); +remove_dbg: +#ifdef CONFIG_USB_GADGET_DEBUG_FILES + dbg_remove_files(&udc->gadget.dev); +#endif +unreg_device: + device_unregister(&udc->gadget.dev); +put_transceiver: + if (udc->transceiver) + otg_put_transceiver(udc->transceiver); +free_udc: kfree(udc); _udc = NULL; return retval; @@ -2672,6 +2767,10 @@ static void udc_remove(void) return; } + if (udc->transceiver) { + otg_set_peripheral(udc->transceiver, &udc->gadget); + otg_put_transceiver(udc->transceiver); + } #ifdef CONFIG_USB_GADGET_DEBUG_FILES dbg_remove_files(&udc->gadget.dev); #endif @@ -2680,156 +2779,3 @@ static void udc_remove(void) kfree(udc); _udc = NULL; } - -/****************************************************************************** - * 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(); -} - -/** - * 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(&pdev->dev, regs, UDC_DRIVER_NAME); - 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"); |