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>
This commit is contained in:
Alexander Shishkin
2012-05-11 17:25:47 +03:00
committed by Greg Kroah-Hartman
parent e443b33362
commit 5f36e231e9
7 changed files with 330 additions and 120 deletions

View File

@@ -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);
kfree(udc->hw_bank.regmap);
kfree(udc);
/* 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;
return 0;
}