]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - drivers/usb/gadget/ci13xxx_udc.c
Merge tag 'v2.6.38' of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
[mv-sheeva.git] / drivers / usb / gadget / ci13xxx_udc.c
index 98b36fc88c77d298bf4f8efb4c9dc2f773f1931a..a1c67ae1572a2b8abf57ed9688d0a917a413aab7 100644 (file)
@@ -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
 #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"
 
 /* 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 = {
+ctrl_endpt_out_desc = {
        .bLength         = USB_DT_ENDPOINT_SIZE,
        .bDescriptorType = USB_DT_ENDPOINT,
 
+       .bEndpointAddress = USB_DIR_OUT,
+       .bmAttributes    = USB_ENDPOINT_XFER_CONTROL,
+       .wMaxPacketSize  = cpu_to_le16(CTRL_PAYLOAD_MAX),
+};
+
+static const struct usb_endpoint_descriptor
+ctrl_endpt_in_desc = {
+       .bLength         = USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType = USB_DT_ENDPOINT,
+
+       .bEndpointAddress = USB_DIR_IN,
        .bmAttributes    = USB_ENDPOINT_XFER_CONTROL,
        .wMaxPacketSize  = cpu_to_le16(CTRL_PAYLOAD_MAX),
 };
@@ -132,6 +139,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 +258,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 +275,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);
+       hw_ep_max = reg * 2;   /* cache hw ENDPT_MAX */
+
+       if (hw_ep_max == 0 || hw_ep_max > ENDPT_MAX)
+               return -ENODEV;
+
+       /* 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 +305,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 +324,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;
 }
 
@@ -1186,16 +1208,17 @@ static ssize_t show_qheads(struct device *dev, struct device_attribute *attr,
        }
 
        spin_lock_irqsave(udc->lock, flags);
-       for (i = 0; i < hw_ep_max; i++) {
-               struct ci13xxx_ep *mEp = &udc->ci13xxx_ep[i];
+       for (i = 0; i < hw_ep_max/2; i++) {
+               struct ci13xxx_ep *mEpRx = &udc->ci13xxx_ep[i];
+               struct ci13xxx_ep *mEpTx = &udc->ci13xxx_ep[i + hw_ep_max/2];
                n += scnprintf(buf + n, PAGE_SIZE - n,
                               "EP=%02i: RX=%08X TX=%08X\n",
-                              i, (u32)mEp->qh[RX].dma, (u32)mEp->qh[TX].dma);
+                              i, (u32)mEpRx->qh.dma, (u32)mEpTx->qh.dma);
                for (j = 0; j < (sizeof(struct ci13xxx_qh)/sizeof(u32)); j++) {
                        n += scnprintf(buf + n, PAGE_SIZE - n,
                                       " %04X:    %08X    %08X\n", j,
-                                      *((u32 *)mEp->qh[RX].ptr + j),
-                                      *((u32 *)mEp->qh[TX].ptr + j));
+                                      *((u32 *)mEpRx->qh.ptr + j),
+                                      *((u32 *)mEpTx->qh.ptr + j));
                }
        }
        spin_unlock_irqrestore(udc->lock, flags);
@@ -1282,7 +1305,7 @@ static ssize_t show_requests(struct device *dev, struct device_attribute *attr,
        unsigned long flags;
        struct list_head   *ptr = NULL;
        struct ci13xxx_req *req = NULL;
-       unsigned i, j, k, n = 0, qSize = sizeof(struct ci13xxx_td)/sizeof(u32);
+       unsigned i, j, n = 0, qSize = sizeof(struct ci13xxx_td)/sizeof(u32);
 
        dbg_trace("[%s] %p\n", __func__, buf);
        if (attr == NULL || buf == NULL) {
@@ -1292,22 +1315,20 @@ static ssize_t show_requests(struct device *dev, struct device_attribute *attr,
 
        spin_lock_irqsave(udc->lock, flags);
        for (i = 0; i < hw_ep_max; i++)
-               for (k = RX; k <= TX; k++)
-                       list_for_each(ptr, &udc->ci13xxx_ep[i].qh[k].queue)
-                       {
-                               req = list_entry(ptr,
-                                                struct ci13xxx_req, queue);
+               list_for_each(ptr, &udc->ci13xxx_ep[i].qh.queue)
+               {
+                       req = list_entry(ptr, struct ci13xxx_req, queue);
 
+                       n += scnprintf(buf + n, PAGE_SIZE - n,
+                                       "EP=%02i: TD=%08X %s\n",
+                                       i % hw_ep_max/2, (u32)req->dma,
+                                       ((i < hw_ep_max/2) ? "RX" : "TX"));
+
+                       for (j = 0; j < qSize; j++)
                                n += scnprintf(buf + n, PAGE_SIZE - n,
-                                              "EP=%02i: TD=%08X %s\n",
-                                              i, (u32)req->dma,
-                                              ((k == RX) ? "RX" : "TX"));
-
-                               for (j = 0; j < qSize; j++)
-                                       n += scnprintf(buf + n, PAGE_SIZE - n,
-                                                      " %04X:    %08X\n", j,
-                                                      *((u32 *)req->ptr + j));
-                       }
+                                               " %04X:    %08X\n", j,
+                                               *((u32 *)req->ptr + j));
+               }
        spin_unlock_irqrestore(udc->lock, flags);
 
        return n;
@@ -1449,19 +1470,19 @@ static int _hardware_enqueue(struct ci13xxx_ep *mEp, struct ci13xxx_req *mReq)
        mReq->ptr->page[0]  = mReq->req.dma;
        for (i = 1; i < 5; i++)
                mReq->ptr->page[i] =
-                       (mReq->req.dma + i * PAGE_SIZE) & ~TD_RESERVED_MASK;
+                       (mReq->req.dma + i * CI13XXX_PAGE_SIZE) & ~TD_RESERVED_MASK;
 
        /*
         *  QH configuration
         *  At this point it's guaranteed exclusive access to qhead
         *  (endpt is not primed) so it's no need to use tripwire
         */
-       mEp->qh[mEp->dir].ptr->td.next   = mReq->dma;    /* TERMINATE = 0 */
-       mEp->qh[mEp->dir].ptr->td.token &= ~TD_STATUS;   /* clear status */
+       mEp->qh.ptr->td.next   = mReq->dma;    /* TERMINATE = 0 */
+       mEp->qh.ptr->td.token &= ~TD_STATUS;   /* clear status */
        if (mReq->req.zero == 0)
-               mEp->qh[mEp->dir].ptr->cap |=  QH_ZLT;
+               mEp->qh.ptr->cap |=  QH_ZLT;
        else
-               mEp->qh[mEp->dir].ptr->cap &= ~QH_ZLT;
+               mEp->qh.ptr->cap &= ~QH_ZLT;
 
        wmb();   /* synchronize before ep prime */
 
@@ -1531,16 +1552,16 @@ __acquires(mEp->lock)
 
        hw_ep_flush(mEp->num, mEp->dir);
 
-       while (!list_empty(&mEp->qh[mEp->dir].queue)) {
+       while (!list_empty(&mEp->qh.queue)) {
 
                /* pop oldest request */
                struct ci13xxx_req *mReq = \
-                       list_entry(mEp->qh[mEp->dir].queue.next,
+                       list_entry(mEp->qh.queue.next,
                                   struct ci13xxx_req, queue);
                list_del_init(&mReq->queue);
                mReq->req.status = -ESHUTDOWN;
 
-               if (!mReq->req.no_interrupt && mReq->req.complete != NULL) {
+               if (mReq->req.complete != NULL) {
                        spin_unlock(mEp->lock);
                        mReq->req.complete(&mEp->ep, &mReq->req);
                        spin_lock(mEp->lock);
@@ -1557,26 +1578,21 @@ __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);
-       struct ci13xxx_ep *mEp = container_of(gadget->ep0,
-                                             struct ci13xxx_ep, ep);
 
        trace("%p", gadget);
 
        if (gadget == NULL)
                return -EINVAL;
 
-       spin_unlock(udc->lock);
-
        /* flush all endpoints */
        gadget_for_each_ep(ep, gadget) {
                usb_ep_fifo_flush(ep);
        }
-       usb_ep_fifo_flush(gadget->ep0);
+       usb_ep_fifo_flush(&udc->ep0out.ep);
+       usb_ep_fifo_flush(&udc->ep0in.ep);
 
        udc->driver->disconnect(gadget);
 
@@ -1584,15 +1600,14 @@ __acquires(udc->lock)
        gadget_for_each_ep(ep, gadget) {
                usb_ep_disable(ep);
        }
-       usb_ep_disable(gadget->ep0);
+       usb_ep_disable(&udc->ep0out.ep);
+       usb_ep_disable(&udc->ep0in.ep);
 
-       if (mEp->status != NULL) {
-               usb_ep_free_request(gadget->ep0, mEp->status);
-               mEp->status = NULL;
+       if (udc->status != NULL) {
+               usb_ep_free_request(&udc->ep0in.ep, udc->status);
+               udc->status = NULL;
        }
 
-       spin_lock(udc->lock);
-
        return 0;
 }
 
@@ -1609,7 +1624,6 @@ static void isr_reset_handler(struct ci13xxx *udc)
 __releases(udc->lock)
 __acquires(udc->lock)
 {
-       struct ci13xxx_ep *mEp = &udc->ci13xxx_ep[0];
        int retval;
 
        trace("%p", udc);
@@ -1621,6 +1635,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,12 +1644,15 @@ __acquires(udc->lock)
        if (retval)
                goto done;
 
-       spin_unlock(udc->lock);
-       retval = usb_ep_enable(&mEp->ep, &ctrl_endpt_desc);
+       retval = usb_ep_enable(&udc->ep0out.ep, &ctrl_endpt_out_desc);
+       if (retval)
+               goto done;
+
+       retval = usb_ep_enable(&udc->ep0in.ep, &ctrl_endpt_in_desc);
        if (!retval) {
-               mEp->status = usb_ep_alloc_request(&mEp->ep, GFP_KERNEL);
-               if (mEp->status == NULL) {
-                       usb_ep_disable(&mEp->ep);
+               udc->status = usb_ep_alloc_request(&udc->ep0in.ep, GFP_ATOMIC);
+               if (udc->status == NULL) {
+                       usb_ep_disable(&udc->ep0out.ep);
                        retval = -ENOMEM;
                }
        }
@@ -1667,16 +1685,17 @@ static void isr_get_status_complete(struct usb_ep *ep, struct usb_request *req)
 
 /**
  * isr_get_status_response: get_status request response
- * @ep:    endpoint
+ * @udc: udc struct
  * @setup: setup request packet
  *
  * This function returns an error code
  */
-static int isr_get_status_response(struct ci13xxx_ep *mEp,
+static int isr_get_status_response(struct ci13xxx *udc,
                                   struct usb_ctrlrequest *setup)
 __releases(mEp->lock)
 __acquires(mEp->lock)
 {
+       struct ci13xxx_ep *mEp = &udc->ep0in;
        struct usb_request *req = NULL;
        gfp_t gfp_flags = GFP_ATOMIC;
        int dir, num, retval;
@@ -1731,27 +1750,23 @@ __acquires(mEp->lock)
 
 /**
  * isr_setup_status_phase: queues the status phase of a setup transation
- * @mEp: endpoint
+ * @udc: udc struct
  *
  * This function returns an error code
  */
-static int isr_setup_status_phase(struct ci13xxx_ep *mEp)
+static int isr_setup_status_phase(struct ci13xxx *udc)
 __releases(mEp->lock)
 __acquires(mEp->lock)
 {
        int retval;
+       struct ci13xxx_ep *mEp;
 
-       trace("%p", mEp);
-
-       /* mEp is always valid & configured */
-
-       if (mEp->type == USB_ENDPOINT_XFER_CONTROL)
-               mEp->dir = (mEp->dir == TX) ? RX : TX;
+       trace("%p", udc);
 
-       mEp->status->no_interrupt = 1;
+       mEp = (udc->ep0_dir == TX) ? &udc->ep0out : &udc->ep0in;
 
        spin_unlock(mEp->lock);
-       retval = usb_ep_queue(&mEp->ep, mEp->status, GFP_ATOMIC);
+       retval = usb_ep_queue(&mEp->ep, udc->status, GFP_ATOMIC);
        spin_lock(mEp->lock);
 
        return retval;
@@ -1773,11 +1788,11 @@ __acquires(mEp->lock)
 
        trace("%p", mEp);
 
-       if (list_empty(&mEp->qh[mEp->dir].queue))
+       if (list_empty(&mEp->qh.queue))
                return -EINVAL;
 
        /* pop oldest request */
-       mReq = list_entry(mEp->qh[mEp->dir].queue.next,
+       mReq = list_entry(mEp->qh.queue.next,
                          struct ci13xxx_req, queue);
        list_del_init(&mReq->queue);
 
@@ -1789,18 +1804,20 @@ __acquires(mEp->lock)
 
        dbg_done(_usb_addr(mEp), mReq->ptr->token, retval);
 
-       if (!mReq->req.no_interrupt && mReq->req.complete != NULL) {
+       if (!list_empty(&mEp->qh.queue)) {
+               struct ci13xxx_req* mReqEnq;
+
+               mReqEnq = list_entry(mEp->qh.queue.next,
+                                 struct ci13xxx_req, queue);
+               _hardware_enqueue(mEp, mReqEnq);
+       }
+
+       if (mReq->req.complete != NULL) {
                spin_unlock(mEp->lock);
                mReq->req.complete(&mEp->ep, &mReq->req);
                spin_lock(mEp->lock);
        }
 
-       if (!list_empty(&mEp->qh[mEp->dir].queue)) {
-               mReq = list_entry(mEp->qh[mEp->dir].queue.next,
-                                 struct ci13xxx_req, queue);
-               _hardware_enqueue(mEp, mReq);
-       }
-
  done:
        return retval;
 }
@@ -1829,16 +1846,14 @@ __acquires(udc->lock)
                int type, num, err = -EINVAL;
                struct usb_ctrlrequest req;
 
-
                if (mEp->desc == NULL)
                        continue;   /* not configured */
 
-               if ((mEp->dir == RX && hw_test_and_clear_complete(i)) ||
-                   (mEp->dir == TX && hw_test_and_clear_complete(i + 16))) {
+               if (hw_test_and_clear_complete(i)) {
                        err = isr_tr_complete_low(mEp);
                        if (mEp->type == USB_ENDPOINT_XFER_CONTROL) {
                                if (err > 0)   /* needs status phase */
-                                       err = isr_setup_status_phase(mEp);
+                                       err = isr_setup_status_phase(udc);
                                if (err < 0) {
                                        dbg_event(_usb_addr(mEp),
                                                  "ERROR", err);
@@ -1859,15 +1874,22 @@ __acquires(udc->lock)
                        continue;
                }
 
+               /*
+                * Flush data and handshake transactions of previous
+                * setup packet.
+                */
+               _ep_nuke(&udc->ep0out);
+               _ep_nuke(&udc->ep0in);
+
                /* read_setup_packet */
                do {
                        hw_test_and_set_setup_guard();
-                       memcpy(&req, &mEp->qh[RX].ptr->setup, sizeof(req));
+                       memcpy(&req, &mEp->qh.ptr->setup, sizeof(req));
                } while (!hw_test_and_clear_setup_guard());
 
                type = req.bRequestType;
 
-               mEp->dir = (type & USB_DIR_IN) ? TX : RX;
+               udc->ep0_dir = (type & USB_DIR_IN) ? TX : RX;
 
                dbg_setup(_usb_addr(mEp), &req);
 
@@ -1888,7 +1910,7 @@ __acquires(udc->lock)
                                if (err)
                                        break;
                        }
-                       err = isr_setup_status_phase(mEp);
+                       err = isr_setup_status_phase(udc);
                        break;
                case USB_REQ_GET_STATUS:
                        if (type != (USB_DIR_IN|USB_RECIP_DEVICE)   &&
@@ -1898,7 +1920,7 @@ __acquires(udc->lock)
                        if (le16_to_cpu(req.wLength) != 2 ||
                            le16_to_cpu(req.wValue)  != 0)
                                break;
-                       err = isr_get_status_response(mEp, &req);
+                       err = isr_get_status_response(udc, &req);
                        break;
                case USB_REQ_SET_ADDRESS:
                        if (type != (USB_DIR_OUT|USB_RECIP_DEVICE))
@@ -1909,7 +1931,7 @@ __acquires(udc->lock)
                        err = hw_usb_set_address((u8)le16_to_cpu(req.wValue));
                        if (err)
                                break;
-                       err = isr_setup_status_phase(mEp);
+                       err = isr_setup_status_phase(udc);
                        break;
                case USB_REQ_SET_FEATURE:
                        if (type != (USB_DIR_OUT|USB_RECIP_ENDPOINT) &&
@@ -1925,12 +1947,12 @@ __acquires(udc->lock)
                        spin_lock(udc->lock);
                        if (err)
                                break;
-                       err = isr_setup_status_phase(mEp);
+                       err = isr_setup_status_phase(udc);
                        break;
                default:
 delegate:
                        if (req.wLength == 0)   /* no data phase */
-                               mEp->dir = TX;
+                               udc->ep0_dir = TX;
 
                        spin_unlock(udc->lock);
                        err = udc->driver->setup(&udc->gadget, &req);
@@ -1961,7 +1983,7 @@ static int ep_enable(struct usb_ep *ep,
                     const struct usb_endpoint_descriptor *desc)
 {
        struct ci13xxx_ep *mEp = container_of(ep, struct ci13xxx_ep, ep);
-       int direction, retval = 0;
+       int retval = 0;
        unsigned long flags;
 
        trace("%p, %p", ep, desc);
@@ -1975,7 +1997,7 @@ static int ep_enable(struct usb_ep *ep,
 
        mEp->desc = desc;
 
-       if (!list_empty(&mEp->qh[mEp->dir].queue))
+       if (!list_empty(&mEp->qh.queue))
                warn("enabling a non-empty endpoint!");
 
        mEp->dir  = usb_endpoint_dir_in(desc) ? TX : RX;
@@ -1984,29 +2006,22 @@ static int ep_enable(struct usb_ep *ep,
 
        mEp->ep.maxpacket = __constant_le16_to_cpu(desc->wMaxPacketSize);
 
-       direction = mEp->dir;
-       do {
-               dbg_event(_usb_addr(mEp), "ENABLE", 0);
+       dbg_event(_usb_addr(mEp), "ENABLE", 0);
 
-               mEp->qh[mEp->dir].ptr->cap = 0;
+       mEp->qh.ptr->cap = 0;
 
-               if (mEp->type == USB_ENDPOINT_XFER_CONTROL)
-                       mEp->qh[mEp->dir].ptr->cap |=  QH_IOS;
-               else if (mEp->type == USB_ENDPOINT_XFER_ISOC)
-                       mEp->qh[mEp->dir].ptr->cap &= ~QH_MULT;
-               else
-                       mEp->qh[mEp->dir].ptr->cap &= ~QH_ZLT;
-
-               mEp->qh[mEp->dir].ptr->cap |=
-                       (mEp->ep.maxpacket << ffs_nr(QH_MAX_PKT)) & QH_MAX_PKT;
-               mEp->qh[mEp->dir].ptr->td.next |= TD_TERMINATE;   /* needed? */
+       if (mEp->type == USB_ENDPOINT_XFER_CONTROL)
+               mEp->qh.ptr->cap |=  QH_IOS;
+       else if (mEp->type == USB_ENDPOINT_XFER_ISOC)
+               mEp->qh.ptr->cap &= ~QH_MULT;
+       else
+               mEp->qh.ptr->cap &= ~QH_ZLT;
 
-               retval |= hw_ep_enable(mEp->num, mEp->dir, mEp->type);
+       mEp->qh.ptr->cap |=
+               (mEp->ep.maxpacket << ffs_nr(QH_MAX_PKT)) & QH_MAX_PKT;
+       mEp->qh.ptr->td.next |= TD_TERMINATE;   /* needed? */
 
-               if (mEp->type == USB_ENDPOINT_XFER_CONTROL)
-                       mEp->dir = (mEp->dir == TX) ? RX : TX;
-
-       } while (mEp->dir != direction);
+       retval |= hw_ep_enable(mEp->num, mEp->dir, mEp->type);
 
        spin_unlock_irqrestore(mEp->lock, flags);
        return retval;
@@ -2061,7 +2076,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 +2084,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 +2098,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;
 }
 
@@ -2144,7 +2154,7 @@ static int ep_queue(struct usb_ep *ep, struct usb_request *req,
        spin_lock_irqsave(mEp->lock, flags);
 
        if (mEp->type == USB_ENDPOINT_XFER_CONTROL &&
-           !list_empty(&mEp->qh[mEp->dir].queue)) {
+           !list_empty(&mEp->qh.queue)) {
                _ep_nuke(mEp);
                retval = -EOVERFLOW;
                warn("endpoint ctrl %X nuked", _usb_addr(mEp));
@@ -2157,8 +2167,8 @@ static int ep_queue(struct usb_ep *ep, struct usb_request *req,
                goto done;
        }
 
-       if (req->length > (4 * PAGE_SIZE)) {
-               req->length = (4 * PAGE_SIZE);
+       if (req->length > (4 * CI13XXX_PAGE_SIZE)) {
+               req->length = (4 * CI13XXX_PAGE_SIZE);
                retval = -EMSGSIZE;
                warn("request length truncated");
        }
@@ -2168,10 +2178,12 @@ static int ep_queue(struct usb_ep *ep, struct usb_request *req,
        /* push request */
        mReq->req.status = -EINPROGRESS;
        mReq->req.actual = 0;
-       list_add_tail(&mReq->queue, &mEp->qh[mEp->dir].queue);
+       list_add_tail(&mReq->queue, &mEp->qh.queue);
+
+       if (list_is_singular(&mEp->qh.queue))
+               retval = _hardware_enqueue(mEp, mReq);
 
-       retval = _hardware_enqueue(mEp, mReq);
-       if (retval == -EALREADY || retval == -EBUSY) {
+       if (retval == -EALREADY) {
                dbg_event(_usb_addr(mEp), "QUEUE", retval);
                retval = 0;
        }
@@ -2195,7 +2207,7 @@ static int ep_dequeue(struct usb_ep *ep, struct usb_request *req)
        trace("%p, %p", ep, req);
 
        if (ep == NULL || req == NULL || mEp->desc == NULL ||
-           list_empty(&mReq->queue)  || list_empty(&mEp->qh[mEp->dir].queue))
+           list_empty(&mReq->queue)  || list_empty(&mEp->qh.queue))
                return -EINVAL;
 
        spin_lock_irqsave(mEp->lock, flags);
@@ -2209,7 +2221,7 @@ static int ep_dequeue(struct usb_ep *ep, struct usb_request *req)
        list_del_init(&mReq->queue);
        req->status = -ECONNRESET;
 
-       if (!mReq->req.no_interrupt && mReq->req.complete != NULL) {
+       if (mReq->req.complete != NULL) {
                spin_unlock(mEp->lock);
                mReq->req.complete(&mEp->ep, &mReq->req);
                spin_lock(mEp->lock);
@@ -2240,7 +2252,7 @@ static int ep_set_halt(struct usb_ep *ep, int value)
 #ifndef STALL_IN
        /* g_file_storage MS compliant but g_zero fails chapter 9 compliance */
        if (value && mEp->type == USB_ENDPOINT_XFER_BULK && mEp->dir == TX &&
-           !list_empty(&mEp->qh[mEp->dir].queue)) {
+           !list_empty(&mEp->qh.queue)) {
                spin_unlock_irqrestore(mEp->lock, flags);
                return -EAGAIN;
        }
@@ -2332,12 +2344,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->ep0out.qh.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
@@ -2351,14 +2398,14 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver,
                int (*bind)(struct usb_gadget *))
 {
        struct ci13xxx *udc = _udc;
-       unsigned long i, k, flags;
+       unsigned long flags;
+       int i, j;
        int retval = -ENOMEM;
 
        trace("%p", driver);
 
        if (driver             == NULL ||
            bind               == NULL ||
-           driver->unbind     == NULL ||
            driver->setup      == NULL ||
            driver->disconnect == NULL ||
            driver->suspend    == NULL ||
@@ -2372,13 +2419,13 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver,
        /* alloc resources */
        udc->qh_pool = dma_pool_create("ci13xxx_qh", &udc->gadget.dev,
                                       sizeof(struct ci13xxx_qh),
-                                      64, PAGE_SIZE);
+                                      64, CI13XXX_PAGE_SIZE);
        if (udc->qh_pool == NULL)
                return -ENOMEM;
 
        udc->td_pool = dma_pool_create("ci13xxx_td", &udc->gadget.dev,
                                       sizeof(struct ci13xxx_td),
-                                      64, PAGE_SIZE);
+                                      64, CI13XXX_PAGE_SIZE);
        if (udc->td_pool == NULL) {
                dma_pool_destroy(udc->qh_pool);
                udc->qh_pool = NULL;
@@ -2389,47 +2436,48 @@ 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;
-       for (i = 0; i < hw_ep_max; i++) {
-               struct ci13xxx_ep *mEp = &udc->ci13xxx_ep[i];
-
-               scnprintf(mEp->name, sizeof(mEp->name), "ep%i", (int)i);
-
-               mEp->lock         = udc->lock;
-               mEp->device       = &udc->gadget.dev;
-               mEp->td_pool      = udc->td_pool;
-
-               mEp->ep.name      = mEp->name;
-               mEp->ep.ops       = &usb_ep_ops;
-               mEp->ep.maxpacket = CTRL_PAYLOAD_MAX;
-
-               /* this allocation cannot be random */
-               for (k = RX; k <= TX; k++) {
-                       INIT_LIST_HEAD(&mEp->qh[k].queue);
-                       mEp->qh[k].ptr = dma_pool_alloc(udc->qh_pool,
-                                                       GFP_KERNEL,
-                                                       &mEp->qh[k].dma);
-                       if (mEp->qh[k].ptr == NULL)
+       for (i = 0; i < hw_ep_max/2; i++) {
+               for (j = RX; j <= TX; j++) {
+                       int k = i + j * hw_ep_max/2;
+                       struct ci13xxx_ep *mEp = &udc->ci13xxx_ep[k];
+
+                       scnprintf(mEp->name, sizeof(mEp->name), "ep%i%s", i,
+                                       (j == TX)  ? "in" : "out");
+
+                       mEp->lock         = udc->lock;
+                       mEp->device       = &udc->gadget.dev;
+                       mEp->td_pool      = udc->td_pool;
+
+                       mEp->ep.name      = mEp->name;
+                       mEp->ep.ops       = &usb_ep_ops;
+                       mEp->ep.maxpacket = CTRL_PAYLOAD_MAX;
+
+                       INIT_LIST_HEAD(&mEp->qh.queue);
+                       spin_unlock_irqrestore(udc->lock, flags);
+                       mEp->qh.ptr = dma_pool_alloc(udc->qh_pool, GFP_KERNEL,
+                                       &mEp->qh.dma);
+                       spin_lock_irqsave(udc->lock, flags);
+                       if (mEp->qh.ptr == NULL)
                                retval = -ENOMEM;
                        else
-                               memset(mEp->qh[k].ptr, 0,
-                                      sizeof(*mEp->qh[k].ptr));
-               }
-               if (i == 0)
-                       udc->gadget.ep0 = &mEp->ep;
-               else
+                               memset(mEp->qh.ptr, 0, sizeof(*mEp->qh.ptr));
+
+                       /* skip ep0 out and in endpoints */
+                       if (i == 0)
+                               continue;
+
                        list_add_tail(&mEp->ep.ep_list, &udc->gadget.ep_list);
+               }
        }
        if (retval)
                goto done;
 
+       udc->gadget.ep0 = &udc->ep0in.ep;
        /* 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,17 +2485,28 @@ 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;
        }
 
-       retval = hw_device_state(udc->ci13xxx_ep[0].qh[RX].dma);
+       udc->driver = driver;
+       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->ep0out.qh.dma);
+       if (retval)
+               pm_runtime_put_sync(&udc->gadget.dev);
 
  done:
        spin_unlock_irqrestore(udc->lock, flags);
-       if (retval)
-               usb_gadget_unregister_driver(driver);
        return retval;
 }
 EXPORT_SYMBOL(usb_gadget_probe_driver);
@@ -2460,7 +2519,7 @@ EXPORT_SYMBOL(usb_gadget_probe_driver);
 int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
 {
        struct ci13xxx *udc = _udc;
-       unsigned long i, k, flags;
+       unsigned long i, flags;
 
        trace("%p", driver);
 
@@ -2475,35 +2534,35 @@ 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++) {
                struct ci13xxx_ep *mEp = &udc->ci13xxx_ep[i];
 
-               if (i == 0)
-                       udc->gadget.ep0 = NULL;
-               else if (!list_empty(&mEp->ep.ep_list))
+               if (!list_empty(&mEp->ep.ep_list))
                        list_del_init(&mEp->ep.ep_list);
 
-               for (k = RX; k <= TX; k++)
-                       if (mEp->qh[k].ptr != NULL)
-                               dma_pool_free(udc->qh_pool,
-                                             mEp->qh[k].ptr, mEp->qh[k].dma);
+               if (mEp->qh.ptr != NULL)
+                       dma_pool_free(udc->qh_pool, mEp->qh.ptr, mEp->qh.dma);
        }
 
+       udc->gadget.ep0 = NULL;
        udc->driver = NULL;
 
        spin_unlock_irqrestore(udc->lock, flags);
@@ -2544,6 +2603,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 +2669,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 +2686,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 +2776,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 +2788,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");