USB: fsl_udc_core: support device mode of MPC5121E DR USB Controller
authorAnatolij Gustschin <agust@denx.de>
Mon, 18 Apr 2011 20:01:58 +0000 (22:01 +0200)
committerGreg Kroah-Hartman <gregkh@suse.de>
Mon, 2 May 2011 23:59:37 +0000 (16:59 -0700)
Extend the FSL UDC driver to support MPC5121E DR USB Controller
operation in device mode. Add MPC5121E specific init/uninit
at probe and remove and isolate system interface register accesses
when running on MPC5121E SoC, as these registers are not available
on this platform. This patch relies on previous patch for supporting
big endian registers and descriptors access in the FSL UDC driver.
Additionally support endpoint FIFO status operation by providing
appropriate callback in endpoint ops structure.

Also flush cache for the req buffer used for GetStatus reply.
Without this, the correct reply to an endpoint GetStatus
is written to 'req', but doesn't make it out to the USB bus
since the buffer hasn't been flushed. This would cause the
USBCV Halt Endpoint test to fail (according to changelog in
Freescale LTIB driver code).

Signed-off-by: Anatolij Gustschin <agust@denx.de>
Cc: Li Yang <leoli@freescale.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
drivers/usb/gadget/fsl_udc_core.c
drivers/usb/gadget/fsl_usb2_udc.h

index b101b7e..28b3a9f 100644 (file)
@@ -6,7 +6,7 @@
  *
  * Description:
  * Freescale high-speed USB SOC DR module device controller driver.
- * This can be found on MPC8349E/MPC8313E cpus.
+ * This can be found on MPC8349E/MPC8313E/MPC5121E cpus.
  * The driver is previously named as mpc_udc.  Based on bare board
  * code from Dave Liu and Shlomi Gridish.
  *
@@ -45,6 +45,7 @@
 #include <asm/system.h>
 #include <asm/unaligned.h>
 #include <asm/dma.h>
+#include <asm/cacheflush.h>
 
 #include "fsl_usb2_udc.h"
 
@@ -278,9 +279,12 @@ static int dr_controller_setup(struct fsl_udc *udc)
 
        /* Set the controller as device mode */
        tmp = fsl_readl(&dr_regs->usbmode);
+       tmp &= ~USB_MODE_CTRL_MODE_MASK;        /* clear mode bits */
        tmp |= USB_MODE_CTRL_MODE_DEVICE;
        /* Disable Setup Lockout */
        tmp |= USB_MODE_SETUP_LOCK_OFF;
+       if (udc->pdata->es)
+               tmp |= USB_MODE_ES;
        fsl_writel(tmp, &dr_regs->usbmode);
 
        /* Clear the setup status */
@@ -296,20 +300,24 @@ static int dr_controller_setup(struct fsl_udc *udc)
 
        /* Config control enable i/o output, cpu endian register */
 #ifndef CONFIG_ARCH_MXC
-       ctrl = __raw_readl(&usb_sys_regs->control);
-       ctrl |= USB_CTRL_IOENB;
-       __raw_writel(ctrl, &usb_sys_regs->control);
+       if (udc->pdata->have_sysif_regs) {
+               ctrl = __raw_readl(&usb_sys_regs->control);
+               ctrl |= USB_CTRL_IOENB;
+               __raw_writel(ctrl, &usb_sys_regs->control);
+       }
 #endif
 
 #if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE)
        /* Turn on cache snooping hardware, since some PowerPC platforms
         * wholly rely on hardware to deal with cache coherent. */
 
-       /* Setup Snooping for all the 4GB space */
-       tmp = SNOOP_SIZE_2GB;   /* starts from 0x0, size 2G */
-       __raw_writel(tmp, &usb_sys_regs->snoop1);
-       tmp |= 0x80000000;      /* starts from 0x8000000, size 2G */
-       __raw_writel(tmp, &usb_sys_regs->snoop2);
+       if (udc->pdata->have_sysif_regs) {
+               /* Setup Snooping for all the 4GB space */
+               tmp = SNOOP_SIZE_2GB;   /* starts from 0x0, size 2G */
+               __raw_writel(tmp, &usb_sys_regs->snoop1);
+               tmp |= 0x80000000;      /* starts from 0x8000000, size 2G */
+               __raw_writel(tmp, &usb_sys_regs->snoop2);
+       }
 #endif
 
        return 0;
@@ -1014,6 +1022,36 @@ out:
        return status;
 }
 
+static int fsl_ep_fifo_status(struct usb_ep *_ep)
+{
+       struct fsl_ep *ep;
+       struct fsl_udc *udc;
+       int size = 0;
+       u32 bitmask;
+       struct ep_queue_head *d_qh;
+
+       ep = container_of(_ep, struct fsl_ep, ep);
+       if (!_ep || (!ep->desc && ep_index(ep) != 0))
+               return -ENODEV;
+
+       udc = (struct fsl_udc *)ep->udc;
+
+       if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN)
+               return -ESHUTDOWN;
+
+       d_qh = &ep->udc->ep_qh[ep_index(ep) * 2 + ep_is_in(ep)];
+
+       bitmask = (ep_is_in(ep)) ? (1 << (ep_index(ep) + 16)) :
+           (1 << (ep_index(ep)));
+
+       if (fsl_readl(&dr_regs->endptstatus) & bitmask)
+               size = (d_qh->size_ioc_int_sts & DTD_PACKET_SIZE)
+                   >> DTD_LENGTH_BIT_POS;
+
+       pr_debug("%s %u\n", __func__, size);
+       return size;
+}
+
 static void fsl_ep_fifo_flush(struct usb_ep *_ep)
 {
        struct fsl_ep *ep;
@@ -1066,6 +1104,7 @@ static struct usb_ep_ops fsl_ep_ops = {
        .dequeue = fsl_ep_dequeue,
 
        .set_halt = fsl_ep_set_halt,
+       .fifo_status = fsl_ep_fifo_status,
        .fifo_flush = fsl_ep_fifo_flush,        /* flush fifo */
 };
 
@@ -1280,6 +1319,10 @@ static void ch9getstatus(struct fsl_udc *udc, u8 request_type, u16 value,
        req = udc->status_req;
        /* Fill in the reqest structure */
        *((u16 *) req->req.buf) = cpu_to_le16(tmp);
+
+       /* flush cache for the req buffer */
+       flush_dcache_range((u32)req->req.buf, (u32)req->req.buf + 8);
+
        req->ep = ep;
        req->req.length = 2;
        req->req.status = -EINPROGRESS;
@@ -1332,6 +1375,7 @@ static void setup_received_irq(struct fsl_udc *udc,
                /* Status phase from udc */
        {
                int rc = -EOPNOTSUPP;
+               u16 ptc = 0;
 
                if ((setup->bRequestType & (USB_RECIP_MASK | USB_TYPE_MASK))
                                == (USB_RECIP_ENDPOINT | USB_TYPE_STANDARD)) {
@@ -1353,17 +1397,19 @@ static void setup_received_irq(struct fsl_udc *udc,
                                | USB_TYPE_STANDARD)) {
                        /* Note: The driver has not include OTG support yet.
                         * This will be set when OTG support is added */
-                       if (!gadget_is_otg(&udc->gadget))
-                               break;
-                       else if (setup->bRequest == USB_DEVICE_B_HNP_ENABLE)
-                               udc->gadget.b_hnp_enable = 1;
-                       else if (setup->bRequest == USB_DEVICE_A_HNP_SUPPORT)
-                               udc->gadget.a_hnp_support = 1;
-                       else if (setup->bRequest ==
-                                       USB_DEVICE_A_ALT_HNP_SUPPORT)
-                               udc->gadget.a_alt_hnp_support = 1;
-                       else
-                               break;
+                       if (wValue == USB_DEVICE_TEST_MODE)
+                               ptc = wIndex >> 8;
+                       else if (gadget_is_otg(&udc->gadget)) {
+                               if (setup->bRequest ==
+                                   USB_DEVICE_B_HNP_ENABLE)
+                                       udc->gadget.b_hnp_enable = 1;
+                               else if (setup->bRequest ==
+                                        USB_DEVICE_A_HNP_SUPPORT)
+                                       udc->gadget.a_hnp_support = 1;
+                               else if (setup->bRequest ==
+                                        USB_DEVICE_A_ALT_HNP_SUPPORT)
+                                       udc->gadget.a_alt_hnp_support = 1;
+                       }
                        rc = 0;
                } else
                        break;
@@ -1372,6 +1418,15 @@ static void setup_received_irq(struct fsl_udc *udc,
                        if (ep0_prime_status(udc, EP_DIR_IN))
                                ep0stall(udc);
                }
+               if (ptc) {
+                       u32 tmp;
+
+                       mdelay(10);
+                       tmp = fsl_readl(&dr_regs->portsc1) | (ptc << 16);
+                       fsl_writel(tmp, &dr_regs->portsc1);
+                       printk(KERN_INFO "udc: switch to test mode %d.\n", ptc);
+               }
+
                return;
        }
 
@@ -2106,16 +2161,18 @@ static int fsl_proc_read(char *page, char **start, off_t off, int count,
        next += t;
 
 #ifndef CONFIG_ARCH_MXC
-       tmp_reg = usb_sys_regs->snoop1;
-       t = scnprintf(next, size, "Snoop1 Reg : = [0x%x]\n\n", tmp_reg);
-       size -= t;
-       next += t;
+       if (udc->pdata->have_sysif_regs) {
+               tmp_reg = usb_sys_regs->snoop1;
+               t = scnprintf(next, size, "Snoop1 Reg : = [0x%x]\n\n", tmp_reg);
+               size -= t;
+               next += t;
 
-       tmp_reg = usb_sys_regs->control;
-       t = scnprintf(next, size, "General Control Reg : = [0x%x]\n\n",
-                       tmp_reg);
-       size -= t;
-       next += t;
+               tmp_reg = usb_sys_regs->control;
+               t = scnprintf(next, size, "General Control Reg : = [0x%x]\n\n",
+                               tmp_reg);
+               size -= t;
+               next += t;
+       }
 #endif
 
        /* ------fsl_udc, fsl_ep, fsl_request structure information ----- */
@@ -2336,6 +2393,17 @@ static int __init fsl_udc_probe(struct platform_device *pdev)
                goto err_release_mem_region;
        }
 
+       pdata->regs = (void *)dr_regs;
+
+       /*
+        * do platform specific init: check the clock, grab/config pins, etc.
+        */
+       if (pdata->init && pdata->init(pdev)) {
+               ret = -ENODEV;
+               goto err_iounmap_noclk;
+       }
+
+       /* Set accessors only after pdata->init() ! */
        if (pdata->big_endian_mmio) {
                _fsl_readl = _fsl_readl_be;
                _fsl_writel = _fsl_writel_be;
@@ -2345,8 +2413,9 @@ static int __init fsl_udc_probe(struct platform_device *pdev)
        }
 
 #ifndef CONFIG_ARCH_MXC
-       usb_sys_regs = (struct usb_sys_interface *)
-                       ((u32)dr_regs + USB_DR_SYS_OFFSET);
+       if (pdata->have_sysif_regs)
+               usb_sys_regs = (struct usb_sys_interface *)
+                               ((u32)dr_regs + USB_DR_SYS_OFFSET);
 #endif
 
        /* Initialize USB clocks */
@@ -2446,6 +2515,8 @@ err_unregister:
 err_free_irq:
        free_irq(udc_controller->irq, udc_controller);
 err_iounmap:
+       if (pdata->exit)
+               pdata->exit(pdev);
        fsl_udc_clk_release();
 err_iounmap_noclk:
        iounmap(dr_regs);
@@ -2463,6 +2534,7 @@ err_kfree:
 static int __exit fsl_udc_remove(struct platform_device *pdev)
 {
        struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data;
 
        DECLARE_COMPLETION(done);
 
@@ -2489,6 +2561,13 @@ static int __exit fsl_udc_remove(struct platform_device *pdev)
        /* free udc --wait for the release() finished */
        wait_for_completion(&done);
 
+       /*
+        * do platform specific un-initialization:
+        * release iomux pins, etc.
+        */
+       if (pdata->exit)
+               pdata->exit(pdev);
+
        return 0;
 }
 
index 24efba9..5647cc2 100644 (file)
@@ -275,7 +275,9 @@ struct usb_sys_interface {
 #define  USB_MODE_CTRL_MODE_IDLE              0x00000000
 #define  USB_MODE_CTRL_MODE_DEVICE            0x00000002
 #define  USB_MODE_CTRL_MODE_HOST              0x00000003
+#define  USB_MODE_CTRL_MODE_MASK              0x00000003
 #define  USB_MODE_CTRL_MODE_RSV               0x00000001
+#define  USB_MODE_ES                          0x00000004 /* Endian Select */
 #define  USB_MODE_SETUP_LOCK_OFF              0x00000008
 #define  USB_MODE_STREAM_DISABLE              0x00000010
 /* Endpoint Flush Register */