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>
This commit is contained in:
Guennadi Liakhovetski 2009-04-15 14:25:33 +02:00 committed by Greg Kroah-Hartman
parent 568d422e9c
commit 54e4026b64
5 changed files with 164 additions and 24 deletions

View 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

View 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

View file

@ -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);
}

View 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();

View 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