summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorGuennadi Liakhovetski <lg@denx.de>2009-04-15 14:25:33 +0200
committerGreg Kroah-Hartman <gregkh@suse.de>2009-06-15 21:44:47 -0700
commit54e4026b64a970303349b952866641a7804ef594 (patch)
treea6410500a7218dfe8380e617dad58faf6572a221 /drivers
parent568d422e9cf52b7b26d2e026ae1617971f62b560 (diff)
USB: gadget: Add i.MX3x support to the fsl_usb2_udc driver
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>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/usb/gadget/Kconfig2
-rw-r--r--drivers/usb/gadget/Makefile4
-rw-r--r--drivers/usb/gadget/fsl_mx3_udc.c95
-rw-r--r--drivers/usb/gadget/fsl_udc_core.c (renamed from drivers/usb/gadget/fsl_usb2_udc.c)69
-rw-r--r--drivers/usb/gadget/fsl_usb2_udc.h18
5 files changed, 164 insertions, 24 deletions
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index 772dd3b9bab9..5de9b4f21713 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -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
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index 39a51d746cb7..c3fe06396b74 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -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
index 000000000000..4bc2bf3d602e
--- /dev/null
+++ b/drivers/usb/gadget/fsl_mx3_udc.c
@@ -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);
+}
diff --git a/drivers/usb/gadget/fsl_usb2_udc.c b/drivers/usb/gadget/fsl_udc_core.c
index 9d7b95d4e3d2..42a74b8a0bb8 100644
--- a/drivers/usb/gadget/fsl_usb2_udc.c
+++ b/drivers/usb/gadget/fsl_udc_core.c
@@ -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();
diff --git a/drivers/usb/gadget/fsl_usb2_udc.h b/drivers/usb/gadget/fsl_usb2_udc.h
index e63ef12645f5..20aeceed48c7 100644
--- a/drivers/usb/gadget/fsl_usb2_udc.h
+++ b/drivers/usb/gadget/fsl_usb2_udc.h
@@ -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