USB: gadget: Add i.MX3x support to the fsl_usb2_udc driver
authorGuennadi Liakhovetski <lg@denx.de>
Wed, 15 Apr 2009 12:25:33 +0000 (14:25 +0200)
committerGreg Kroah-Hartman <gregkh@suse.de>
Tue, 16 Jun 2009 04:44:47 +0000 (21:44 -0700)
This patch adds support for i.MX3x (only tested with i.MX31 so far) ARM
SoCs to the fsl_usb2_udc driver. It also moves PHY configuration before
controller reset, because otherwise an ULPI PHY doesn't get a reset and
doesn't function after a reboot. The problem with longer control transfers
is still not fixed. The patch renames the fsl_usb2_udc.c file to
fsl_udc_core.c to preserve the same module name for user-space
backwards compatibility.

Signed-off-by: Guennadi Liakhovetski <lg@denx.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
drivers/usb/gadget/Kconfig
drivers/usb/gadget/Makefile
drivers/usb/gadget/fsl_mx3_udc.c [new file with mode: 0644]
drivers/usb/gadget/fsl_udc_core.c [moved from drivers/usb/gadget/fsl_usb2_udc.c with 99% similarity]
drivers/usb/gadget/fsl_usb2_udc.h

index 772dd3b..5de9b4f 100644 (file)
@@ -156,7 +156,7 @@ config USB_ATMEL_USBA
 
 config USB_GADGET_FSL_USB2
        boolean "Freescale Highspeed USB DR Peripheral Controller"
-       depends on FSL_SOC
+       depends on FSL_SOC || ARCH_MXC
        select USB_GADGET_DUALSPEED
        help
           Some of Freescale PowerPC processors have a High Speed
index 39a51d7..c3fe063 100644 (file)
@@ -18,6 +18,10 @@ obj-$(CONFIG_USB_S3C2410)    += s3c2410_udc.o
 obj-$(CONFIG_USB_AT91)         += at91_udc.o
 obj-$(CONFIG_USB_ATMEL_USBA)   += atmel_usba_udc.o
 obj-$(CONFIG_USB_FSL_USB2)     += fsl_usb2_udc.o
+fsl_usb2_udc-objs              := fsl_udc_core.o
+ifeq ($(CONFIG_ARCH_MXC),y)
+fsl_usb2_udc-objs              += fsl_mx3_udc.o
+endif
 obj-$(CONFIG_USB_M66592)       += m66592-udc.o
 obj-$(CONFIG_USB_FSL_QE)       += fsl_qe_udc.o
 obj-$(CONFIG_USB_CI13XXX)      += ci13xxx_udc.o
diff --git a/drivers/usb/gadget/fsl_mx3_udc.c b/drivers/usb/gadget/fsl_mx3_udc.c
new file mode 100644 (file)
index 0000000..4bc2bf3
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2009
+ * Guennadi Liakhovetski, DENX Software Engineering, <lg@denx.de>
+ *
+ * Description:
+ * Helper routines for i.MX3x SoCs from Freescale, needed by the fsl_usb2_udc.c
+ * driver to function correctly on these systems.
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/fsl_devices.h>
+#include <linux/platform_device.h>
+
+static struct clk *mxc_ahb_clk;
+static struct clk *mxc_usb_clk;
+
+int fsl_udc_clk_init(struct platform_device *pdev)
+{
+       struct fsl_usb2_platform_data *pdata;
+       unsigned long freq;
+       int ret;
+
+       pdata = pdev->dev.platform_data;
+
+       mxc_ahb_clk = clk_get(&pdev->dev, "usb_ahb");
+       if (IS_ERR(mxc_ahb_clk))
+               return PTR_ERR(mxc_ahb_clk);
+
+       ret = clk_enable(mxc_ahb_clk);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "clk_enable(\"usb_ahb\") failed\n");
+               goto eenahb;
+       }
+
+       /* make sure USB_CLK is running at 60 MHz +/- 1000 Hz */
+       mxc_usb_clk = clk_get(&pdev->dev, "usb");
+       if (IS_ERR(mxc_usb_clk)) {
+               dev_err(&pdev->dev, "clk_get(\"usb\") failed\n");
+               ret = PTR_ERR(mxc_usb_clk);
+               goto egusb;
+       }
+
+       freq = clk_get_rate(mxc_usb_clk);
+       if (pdata->phy_mode != FSL_USB2_PHY_ULPI &&
+           (freq < 59999000 || freq > 60001000)) {
+               dev_err(&pdev->dev, "USB_CLK=%lu, should be 60MHz\n", freq);
+               goto eclkrate;
+       }
+
+       ret = clk_enable(mxc_usb_clk);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "clk_enable(\"usb_clk\") failed\n");
+               goto eenusb;
+       }
+
+       return 0;
+
+eenusb:
+eclkrate:
+       clk_put(mxc_usb_clk);
+       mxc_usb_clk = NULL;
+egusb:
+       clk_disable(mxc_ahb_clk);
+eenahb:
+       clk_put(mxc_ahb_clk);
+       return ret;
+}
+
+void fsl_udc_clk_finalize(struct platform_device *pdev)
+{
+       struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data;
+
+       /* ULPI transceivers don't need usbpll */
+       if (pdata->phy_mode == FSL_USB2_PHY_ULPI) {
+               clk_disable(mxc_usb_clk);
+               clk_put(mxc_usb_clk);
+               mxc_usb_clk = NULL;
+       }
+}
+
+void fsl_udc_clk_release(void)
+{
+       if (mxc_usb_clk) {
+               clk_disable(mxc_usb_clk);
+               clk_put(mxc_usb_clk);
+       }
+       clk_disable(mxc_ahb_clk);
+       clk_put(mxc_ahb_clk);
+}
similarity index 99%
rename from drivers/usb/gadget/fsl_usb2_udc.c
rename to drivers/usb/gadget/fsl_udc_core.c
index 9d7b95d..42a74b8 100644 (file)
@@ -38,6 +38,7 @@
 #include <linux/platform_device.h>
 #include <linux/fsl_devices.h>
 #include <linux/dmapool.h>
+#include <linux/delay.h>
 
 #include <asm/byteorder.h>
 #include <asm/io.h>
@@ -57,7 +58,9 @@ static const char driver_name[] = "fsl-usb2-udc";
 static const char driver_desc[] = DRIVER_DESC;
 
 static struct usb_dr_device *dr_regs;
+#ifndef CONFIG_ARCH_MXC
 static struct usb_sys_interface *usb_sys_regs;
+#endif
 
 /* it is initialized in probe()  */
 static struct fsl_udc *udc_controller = NULL;
@@ -174,10 +177,34 @@ static void nuke(struct fsl_ep *ep, int status)
 
 static int dr_controller_setup(struct fsl_udc *udc)
 {
-       unsigned int tmp = 0, portctrl = 0, ctrl = 0;
+       unsigned int tmp, portctrl;
+#ifndef CONFIG_ARCH_MXC
+       unsigned int ctrl;
+#endif
        unsigned long timeout;
 #define FSL_UDC_RESET_TIMEOUT 1000
 
+       /* Config PHY interface */
+       portctrl = fsl_readl(&dr_regs->portsc1);
+       portctrl &= ~(PORTSCX_PHY_TYPE_SEL | PORTSCX_PORT_WIDTH);
+       switch (udc->phy_mode) {
+       case FSL_USB2_PHY_ULPI:
+               portctrl |= PORTSCX_PTS_ULPI;
+               break;
+       case FSL_USB2_PHY_UTMI_WIDE:
+               portctrl |= PORTSCX_PTW_16BIT;
+               /* fall through */
+       case FSL_USB2_PHY_UTMI:
+               portctrl |= PORTSCX_PTS_UTMI;
+               break;
+       case FSL_USB2_PHY_SERIAL:
+               portctrl |= PORTSCX_PTS_FSLS;
+               break;
+       default:
+               return -EINVAL;
+       }
+       fsl_writel(portctrl, &dr_regs->portsc1);
+
        /* Stop and reset the usb controller */
        tmp = fsl_readl(&dr_regs->usbcmd);
        tmp &= ~USB_CMD_RUN_STOP;
@@ -215,31 +242,12 @@ static int dr_controller_setup(struct fsl_udc *udc)
                udc->ep_qh, (int)tmp,
                fsl_readl(&dr_regs->endpointlistaddr));
 
-       /* Config PHY interface */
-       portctrl = fsl_readl(&dr_regs->portsc1);
-       portctrl &= ~(PORTSCX_PHY_TYPE_SEL | PORTSCX_PORT_WIDTH);
-       switch (udc->phy_mode) {
-       case FSL_USB2_PHY_ULPI:
-               portctrl |= PORTSCX_PTS_ULPI;
-               break;
-       case FSL_USB2_PHY_UTMI_WIDE:
-               portctrl |= PORTSCX_PTW_16BIT;
-               /* fall through */
-       case FSL_USB2_PHY_UTMI:
-               portctrl |= PORTSCX_PTS_UTMI;
-               break;
-       case FSL_USB2_PHY_SERIAL:
-               portctrl |= PORTSCX_PTS_FSLS;
-               break;
-       default:
-               return -EINVAL;
-       }
-       fsl_writel(portctrl, &dr_regs->portsc1);
-
        /* 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);
+#endif
 
 #if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE)
        /* Turn on cache snooping hardware, since some PowerPC platforms
@@ -2043,6 +2051,7 @@ static int fsl_proc_read(char *page, char **start, off_t off, int count,
        size -= t;
        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;
@@ -2053,6 +2062,7 @@ static int fsl_proc_read(char *page, char **start, off_t off, int count,
                        tmp_reg);
        size -= t;
        next += t;
+#endif
 
        /* ------fsl_udc, fsl_ep, fsl_request structure information ----- */
        ep = &udc->eps[0];
@@ -2263,14 +2273,21 @@ static int __init fsl_udc_probe(struct platform_device *pdev)
                goto err_kfree;
        }
 
-       dr_regs = ioremap(res->start, res->end - res->start + 1);
+       dr_regs = ioremap(res->start, resource_size(res));
        if (!dr_regs) {
                ret = -ENOMEM;
                goto err_release_mem_region;
        }
 
+#ifndef CONFIG_ARCH_MXC
        usb_sys_regs = (struct usb_sys_interface *)
                        ((u32)dr_regs + USB_DR_SYS_OFFSET);
+#endif
+
+       /* Initialize USB clocks */
+       ret = fsl_udc_clk_init(pdev);
+       if (ret < 0)
+               goto err_iounmap_noclk;
 
        /* Read Device Controller Capability Parameters register */
        dccparams = fsl_readl(&dr_regs->dccparams);
@@ -2308,6 +2325,8 @@ static int __init fsl_udc_probe(struct platform_device *pdev)
         * leave usbintr reg untouched */
        dr_controller_setup(udc_controller);
 
+       fsl_udc_clk_finalize(pdev);
+
        /* Setup gadget structure */
        udc_controller->gadget.ops = &fsl_gadget_ops;
        udc_controller->gadget.is_dualspeed = 1;
@@ -2362,6 +2381,8 @@ err_unregister:
 err_free_irq:
        free_irq(udc_controller->irq, udc_controller);
 err_iounmap:
+       fsl_udc_clk_release();
+err_iounmap_noclk:
        iounmap(dr_regs);
 err_release_mem_region:
        release_mem_region(res->start, res->end - res->start + 1);
@@ -2384,6 +2405,8 @@ static int __exit fsl_udc_remove(struct platform_device *pdev)
                return -ENODEV;
        udc_controller->done = &done;
 
+       fsl_udc_clk_release();
+
        /* DR has been stopped in usb_gadget_unregister_driver() */
        remove_proc_file();
 
index e63ef12..20aecee 100644 (file)
@@ -563,4 +563,22 @@ static void dump_msg(const char *label, const u8 * buf, unsigned int length)
                                        * 2 + ((windex & USB_DIR_IN) ? 1 : 0))
 #define get_pipe_by_ep(EP)     (ep_index(EP) * 2 + ep_is_in(EP))
 
+struct platform_device;
+#ifdef CONFIG_ARCH_MXC
+int fsl_udc_clk_init(struct platform_device *pdev);
+void fsl_udc_clk_finalize(struct platform_device *pdev);
+void fsl_udc_clk_release(void);
+#else
+static inline int fsl_udc_clk_init(struct platform_device *pdev)
+{
+       return 0;
+}
+static inline void fsl_udc_clk_finalize(struct platform_device *pdev)
+{
+}
+static inline void fsl_udc_clk_release(void)
+{
+}
+#endif
+
 #endif