USB: gadget: Introduce ci13xxx_udc_driver struct
authorPavankumar Kondeti <pkondeti@codeaurora.org>
Tue, 7 Dec 2010 12:24:02 +0000 (17:54 +0530)
committerGreg Kroah-Hartman <gregkh@suse.de>
Fri, 10 Dec 2010 22:23:33 +0000 (14:23 -0800)
Introduces ci13xxx_udc_driver struct for bus glue drivers to hint
ci13xxx_udc core about their special requirements.  The flags include
avoiding hardware register access when controller is not in peripheral
mode, enabling pull-up upon VBUS, disabling streaming mode and dependency
on transceiver driver.

Initialize gadget_ops in udc_probe so that transceiver can notify VBUS
presence even when no gadget driver is bounded.

A notify_event callback is embedded in the same struct. This patch implements
two events called CONTROLLER_RESET_EVENT and CONTROLLER_STOPPED_EVENT to
notify the bus glue driver after resetting and stopping the controller for
performing SoC specific quirks.

Signed-off-by: Pavankumar Kondeti <pkondeti@codeaurora.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
drivers/usb/gadget/ci13xxx_pci.c
drivers/usb/gadget/ci13xxx_udc.c
drivers/usb/gadget/ci13xxx_udc.h

index 7a0f153..883ab5e 100644 (file)
@@ -38,6 +38,10 @@ static irqreturn_t ci13xxx_pci_irq(int irq, void *pdev)
        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
@@ -82,7 +86,7 @@ static int __devinit ci13xxx_pci_probe(struct pci_dev *pdev,
        pci_set_master(pdev);
        pci_try_set_mwi(pdev);
 
-       retval = udc_probe(&pdev->dev, regs, UDC_DRIVER_NAME);
+       retval = udc_probe(&ci13xxx_pci_udc_driver, &pdev->dev, regs);
        if (retval)
                goto iounmap;
 
index 956fa64..c10d1ae 100644 (file)
@@ -62,6 +62,7 @@
 #include <linux/slab.h>
 #include <linux/usb/ch9.h>
 #include <linux/usb/gadget.h>
+#include <linux/usb/otg.h>
 
 #include "ci13xxx_udc.h"
 
@@ -126,6 +127,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)
@@ -242,13 +246,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;
 
@@ -265,6 +263,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);
@@ -273,6 +293,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);
@@ -284,18 +312,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;
 }
 
@@ -1551,8 +1567,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);
@@ -1564,8 +1578,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);
@@ -1585,8 +1597,6 @@ __acquires(udc->lock)
                mEp->status = NULL;
        }
 
-       spin_lock(udc->lock);
-
        return 0;
 }
 
@@ -1615,6 +1625,7 @@ __acquires(udc->lock)
 
        dbg_event(0xFF, "BUS RST", 0);
 
+       spin_unlock(udc->lock);
        retval = _gadget_stop_activity(&udc->gadget);
        if (retval)
                goto done;
@@ -1623,7 +1634,6 @@ __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_ATOMIC);
@@ -2321,12 +2331,45 @@ 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) {
+                       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);
+               }
+       }
+
+       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
@@ -2379,7 +2422,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;
@@ -2420,7 +2462,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);
@@ -2428,11 +2469,19 @@ 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;
        }
 
+       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 {
+                       goto done;
+               }
+       }
+
        retval = hw_device_state(udc->ci13xxx_ep[0].qh[RX].dma);
 
  done:
@@ -2466,19 +2515,21 @@ 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);
+       }
 
-               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++) {
@@ -2535,6 +2586,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;
@@ -2593,14 +2652,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);
@@ -2608,16 +2669,14 @@ 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;
@@ -2628,23 +2687,57 @@ static int udc_probe(struct device *dev, void __iomem *regs, const char *name)
        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;
        }
 
        _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;
@@ -2664,6 +2757,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
index 4026e9c..4fd1931 100644 (file)
@@ -97,9 +97,24 @@ struct ci13xxx_ep {
        struct dma_pool                       *td_pool;
 };
 
+struct ci13xxx;
+struct ci13xxx_udc_driver {
+       const char      *name;
+       unsigned long    flags;
+#define CI13XXX_REGS_SHARED            BIT(0)
+#define CI13XXX_REQUIRE_TRANSCEIVER    BIT(1)
+#define CI13XXX_PULLUP_ON_VBUS         BIT(2)
+#define CI13XXX_DISABLE_STREAMING      BIT(3)
+
+#define CI13XXX_CONTROLLER_RESET_EVENT         0
+#define CI13XXX_CONTROLLER_STOPPED_EVENT       1
+       void    (*notify_event) (struct ci13xxx *udc, unsigned event);
+};
+
 /* CI13XXX UDC descriptor & global resources */
 struct ci13xxx {
        spinlock_t                *lock;      /* ctrl register bank access */
+       void __iomem              *regs;      /* registers address space */
 
        struct dma_pool           *qh_pool;   /* DMA pool for queue heads */
        struct dma_pool           *td_pool;   /* DMA pool for transfer descs */
@@ -108,6 +123,9 @@ struct ci13xxx {
        struct ci13xxx_ep          ci13xxx_ep[ENDPT_MAX]; /* extended endpts */
 
        struct usb_gadget_driver  *driver;     /* 3rd party gadget driver */
+       struct ci13xxx_udc_driver *udc_driver; /* device controller driver */
+       int                        vbus_active; /* is VBUS active */
+       struct otg_transceiver    *transceiver; /* Transceiver struct */
 };
 
 /******************************************************************************
@@ -157,6 +175,7 @@ struct ci13xxx {
 #define    USBMODE_CM_DEVICE  (0x02UL <<  0)
 #define    USBMODE_CM_HOST    (0x03UL <<  0)
 #define USBMODE_SLOM          BIT(3)
+#define USBMODE_SDIS          BIT(4)
 
 /* ENDPTCTRL */
 #define ENDPTCTRL_RXS         BIT(0)