summary refs log tree commit diff
path: root/drivers/usb/chipidea/udc.c
diff options
context:
space:
mode:
authorAlexander Shishkin <alexander.shishkin@linux.intel.com>2012-05-11 17:25:47 +0300
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2012-05-11 16:52:10 -0700
commit5f36e231e9dbffb5264612e5b5817ab574a5e5db (patch)
treea71027cded532334d3d51cbf737925240d34e7df /drivers/usb/chipidea/udc.c
parente443b333629f82ca0da91a05ca638050943bbedd (diff)
downloadlinux-5f36e231e9dbffb5264612e5b5817ab574a5e5db.tar.gz
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.c80
1 files changed, 39 insertions, 41 deletions
diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c
index 6866ef085397..9133a59450f4 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;
 }