diff options
author | Alexander Shishkin <alexander.shishkin@linux.intel.com> | 2012-05-11 17:25:47 +0300 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2012-05-11 16:52:10 -0700 |
commit | 5f36e231e9dbffb5264612e5b5817ab574a5e5db (patch) | |
tree | a71027cded532334d3d51cbf737925240d34e7df /drivers/usb/chipidea/udc.c | |
parent | e443b333629f82ca0da91a05ca638050943bbedd (diff) |
usb: chipidea: add support for roles
Add some generic code for roles and implement simple role switching
based on ID pin state and/or a sysfs file. At this, we also rename
the device to ci_hdrc, which is what it is.
The "manual" switch is made into a sysfs file and not debugfs, because
it might be useful even in non-debug context. For some boards, like
sheevaplug, it seems to be the only way to switch roles without modifying
the hardware, since the ID pin is always grounded.
Signed-off-by: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/usb/chipidea/udc.c')
-rw-r--r-- | drivers/usb/chipidea/udc.c | 80 |
1 files changed, 39 insertions, 41 deletions
diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index 6866ef08539..9133a59450f 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -1592,14 +1592,13 @@ static int ci13xxx_stop(struct usb_gadget *gadget, * BUS block *****************************************************************************/ /** - * udc_irq: global interrupt handler + * udc_irq: udc interrupt handler * * This function returns IRQ_HANDLED if the IRQ has been handled * It locks access to registers */ -irqreturn_t udc_irq(int irq, void *data) +static irqreturn_t udc_irq(struct ci13xxx *udc) { - struct ci13xxx *udc = data; irqreturn_t retval; u32 intr; @@ -1666,38 +1665,24 @@ static void udc_release(struct device *dev) } /** - * udc_probe: parent probe must call this to initialize UDC - * @dev: parent device - * @regs: registers base address - * @name: driver name - * - * This function returns an error code - * No interrupts active, the IRQ has not been requested yet - * Kernel assumes 32-bit DMA operations by default, no need to dma_set_mask + * udc_start: initialize gadget role + * @udc: chipidea controller */ -int udc_probe(struct ci13xxx_udc_driver *driver, struct device *dev, - void __iomem *regs, struct ci13xxx **_udc) +static int udc_start(struct ci13xxx *udc) { - struct ci13xxx *udc; + struct device *dev = udc->dev; int retval = 0; - if (dev == NULL || regs == NULL || driver == NULL || - driver->name == NULL) + if (!udc) return -EINVAL; - udc = kzalloc(sizeof(struct ci13xxx), GFP_KERNEL); - if (udc == NULL) - return -ENOMEM; - spin_lock_init(&udc->lock); - udc->regs = regs; - udc->udc_driver = driver; udc->gadget.ops = &usb_gadget_ops; udc->gadget.speed = USB_SPEED_UNKNOWN; udc->gadget.max_speed = USB_SPEED_HIGH; udc->gadget.is_otg = 0; - udc->gadget.name = driver->name; + udc->gadget.name = udc->udc_driver->name; INIT_LIST_HEAD(&udc->gadget.ep_list); @@ -1707,16 +1692,12 @@ int udc_probe(struct ci13xxx_udc_driver *driver, struct device *dev, udc->gadget.dev.parent = dev; udc->gadget.dev.release = udc_release; - udc->dev = dev; - /* alloc resources */ udc->qh_pool = dma_pool_create("ci13xxx_qh", dev, sizeof(struct ci13xxx_qh), 64, CI13XXX_PAGE_SIZE); - if (udc->qh_pool == NULL) { - retval = -ENOMEM; - goto free_udc; - } + if (udc->qh_pool == NULL) + return -ENOMEM; udc->td_pool = dma_pool_create("ci13xxx_td", dev, sizeof(struct ci13xxx_td), @@ -1726,10 +1707,6 @@ int udc_probe(struct ci13xxx_udc_driver *driver, struct device *dev, goto free_qh_pool; } - retval = hw_device_init(udc, regs, driver->capoffset); - if (retval < 0) - goto free_pools; - retval = init_eps(udc); if (retval) goto free_pools; @@ -1775,7 +1752,6 @@ int udc_probe(struct ci13xxx_udc_driver *driver, struct device *dev, pm_runtime_no_callbacks(&udc->gadget.dev); pm_runtime_enable(&udc->gadget.dev); - *_udc = udc; return retval; remove_trans: @@ -1796,9 +1772,6 @@ free_pools: dma_pool_destroy(udc->td_pool); free_qh_pool: dma_pool_destroy(udc->qh_pool); -free_udc: - kfree(udc); - *_udc = NULL; return retval; } @@ -1807,7 +1780,7 @@ free_udc: * * No interrupts active, the IRQ has been released */ -void udc_remove(struct ci13xxx *udc) +static void udc_stop(struct ci13xxx *udc) { int i; @@ -1826,12 +1799,37 @@ void udc_remove(struct ci13xxx *udc) dma_pool_destroy(udc->qh_pool); if (udc->transceiver) { - otg_set_peripheral(udc->transceiver->otg, &udc->gadget); + otg_set_peripheral(udc->transceiver->otg, NULL); usb_put_transceiver(udc->transceiver); } dbg_remove_files(&udc->gadget.dev); device_unregister(&udc->gadget.dev); + /* my kobject is dynamic, I swear! */ + memset(&udc->gadget, 0, sizeof(udc->gadget)); +} + +/** + * ci_hdrc_gadget_init - initialize device related bits + * ci: the controller + * + * This function enables the gadget role, if the device is "device capable". + */ +int ci_hdrc_gadget_init(struct ci13xxx *ci) +{ + struct ci_role_driver *rdrv; + + if (!hw_read(ci, CAP_DCCPARAMS, DCCPARAMS_DC)) + return -ENXIO; + + rdrv = devm_kzalloc(ci->dev, sizeof(struct ci_role_driver), GFP_KERNEL); + if (!rdrv) + return -ENOMEM; + + rdrv->start = udc_start; + rdrv->stop = udc_stop; + rdrv->irq = udc_irq; + rdrv->name = "gadget"; + ci->roles[CI_ROLE_GADGET] = rdrv; - kfree(udc->hw_bank.regmap); - kfree(udc); + return 0; } |