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:
parent
568d422e9c
commit
54e4026b64
@ -156,7 +156,7 @@ config USB_ATMEL_USBA
|
|||||||
|
|
||||||
config USB_GADGET_FSL_USB2
|
config USB_GADGET_FSL_USB2
|
||||||
boolean "Freescale Highspeed USB DR Peripheral Controller"
|
boolean "Freescale Highspeed USB DR Peripheral Controller"
|
||||||
depends on FSL_SOC
|
depends on FSL_SOC || ARCH_MXC
|
||||||
select USB_GADGET_DUALSPEED
|
select USB_GADGET_DUALSPEED
|
||||||
help
|
help
|
||||||
Some of Freescale PowerPC processors have a High Speed
|
Some of Freescale PowerPC processors have a High Speed
|
||||||
|
@ -18,6 +18,10 @@ obj-$(CONFIG_USB_S3C2410) += s3c2410_udc.o
|
|||||||
obj-$(CONFIG_USB_AT91) += at91_udc.o
|
obj-$(CONFIG_USB_AT91) += at91_udc.o
|
||||||
obj-$(CONFIG_USB_ATMEL_USBA) += atmel_usba_udc.o
|
obj-$(CONFIG_USB_ATMEL_USBA) += atmel_usba_udc.o
|
||||||
obj-$(CONFIG_USB_FSL_USB2) += fsl_usb2_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_M66592) += m66592-udc.o
|
||||||
obj-$(CONFIG_USB_FSL_QE) += fsl_qe_udc.o
|
obj-$(CONFIG_USB_FSL_QE) += fsl_qe_udc.o
|
||||||
obj-$(CONFIG_USB_CI13XXX) += ci13xxx_udc.o
|
obj-$(CONFIG_USB_CI13XXX) += ci13xxx_udc.o
|
||||||
|
95
drivers/usb/gadget/fsl_mx3_udc.c
Normal file
95
drivers/usb/gadget/fsl_mx3_udc.c
Normal 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);
|
||||||
|
}
|
@ -38,6 +38,7 @@
|
|||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/fsl_devices.h>
|
#include <linux/fsl_devices.h>
|
||||||
#include <linux/dmapool.h>
|
#include <linux/dmapool.h>
|
||||||
|
#include <linux/delay.h>
|
||||||
|
|
||||||
#include <asm/byteorder.h>
|
#include <asm/byteorder.h>
|
||||||
#include <asm/io.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 const char driver_desc[] = DRIVER_DESC;
|
||||||
|
|
||||||
static struct usb_dr_device *dr_regs;
|
static struct usb_dr_device *dr_regs;
|
||||||
|
#ifndef CONFIG_ARCH_MXC
|
||||||
static struct usb_sys_interface *usb_sys_regs;
|
static struct usb_sys_interface *usb_sys_regs;
|
||||||
|
#endif
|
||||||
|
|
||||||
/* it is initialized in probe() */
|
/* it is initialized in probe() */
|
||||||
static struct fsl_udc *udc_controller = NULL;
|
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)
|
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;
|
unsigned long timeout;
|
||||||
#define FSL_UDC_RESET_TIMEOUT 1000
|
#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 */
|
/* Stop and reset the usb controller */
|
||||||
tmp = fsl_readl(&dr_regs->usbcmd);
|
tmp = fsl_readl(&dr_regs->usbcmd);
|
||||||
tmp &= ~USB_CMD_RUN_STOP;
|
tmp &= ~USB_CMD_RUN_STOP;
|
||||||
@ -215,31 +242,12 @@ static int dr_controller_setup(struct fsl_udc *udc)
|
|||||||
udc->ep_qh, (int)tmp,
|
udc->ep_qh, (int)tmp,
|
||||||
fsl_readl(&dr_regs->endpointlistaddr));
|
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 */
|
/* Config control enable i/o output, cpu endian register */
|
||||||
|
#ifndef CONFIG_ARCH_MXC
|
||||||
ctrl = __raw_readl(&usb_sys_regs->control);
|
ctrl = __raw_readl(&usb_sys_regs->control);
|
||||||
ctrl |= USB_CTRL_IOENB;
|
ctrl |= USB_CTRL_IOENB;
|
||||||
__raw_writel(ctrl, &usb_sys_regs->control);
|
__raw_writel(ctrl, &usb_sys_regs->control);
|
||||||
|
#endif
|
||||||
|
|
||||||
#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE)
|
#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE)
|
||||||
/* Turn on cache snooping hardware, since some PowerPC platforms
|
/* 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;
|
size -= t;
|
||||||
next += t;
|
next += t;
|
||||||
|
|
||||||
|
#ifndef CONFIG_ARCH_MXC
|
||||||
tmp_reg = usb_sys_regs->snoop1;
|
tmp_reg = usb_sys_regs->snoop1;
|
||||||
t = scnprintf(next, size, "Snoop1 Reg : = [0x%x]\n\n", tmp_reg);
|
t = scnprintf(next, size, "Snoop1 Reg : = [0x%x]\n\n", tmp_reg);
|
||||||
size -= t;
|
size -= t;
|
||||||
@ -2053,6 +2062,7 @@ static int fsl_proc_read(char *page, char **start, off_t off, int count,
|
|||||||
tmp_reg);
|
tmp_reg);
|
||||||
size -= t;
|
size -= t;
|
||||||
next += t;
|
next += t;
|
||||||
|
#endif
|
||||||
|
|
||||||
/* ------fsl_udc, fsl_ep, fsl_request structure information ----- */
|
/* ------fsl_udc, fsl_ep, fsl_request structure information ----- */
|
||||||
ep = &udc->eps[0];
|
ep = &udc->eps[0];
|
||||||
@ -2263,14 +2273,21 @@ static int __init fsl_udc_probe(struct platform_device *pdev)
|
|||||||
goto err_kfree;
|
goto err_kfree;
|
||||||
}
|
}
|
||||||
|
|
||||||
dr_regs = ioremap(res->start, res->end - res->start + 1);
|
dr_regs = ioremap(res->start, resource_size(res));
|
||||||
if (!dr_regs) {
|
if (!dr_regs) {
|
||||||
ret = -ENOMEM;
|
ret = -ENOMEM;
|
||||||
goto err_release_mem_region;
|
goto err_release_mem_region;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef CONFIG_ARCH_MXC
|
||||||
usb_sys_regs = (struct usb_sys_interface *)
|
usb_sys_regs = (struct usb_sys_interface *)
|
||||||
((u32)dr_regs + USB_DR_SYS_OFFSET);
|
((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 */
|
/* Read Device Controller Capability Parameters register */
|
||||||
dccparams = fsl_readl(&dr_regs->dccparams);
|
dccparams = fsl_readl(&dr_regs->dccparams);
|
||||||
@ -2308,6 +2325,8 @@ static int __init fsl_udc_probe(struct platform_device *pdev)
|
|||||||
* leave usbintr reg untouched */
|
* leave usbintr reg untouched */
|
||||||
dr_controller_setup(udc_controller);
|
dr_controller_setup(udc_controller);
|
||||||
|
|
||||||
|
fsl_udc_clk_finalize(pdev);
|
||||||
|
|
||||||
/* Setup gadget structure */
|
/* Setup gadget structure */
|
||||||
udc_controller->gadget.ops = &fsl_gadget_ops;
|
udc_controller->gadget.ops = &fsl_gadget_ops;
|
||||||
udc_controller->gadget.is_dualspeed = 1;
|
udc_controller->gadget.is_dualspeed = 1;
|
||||||
@ -2362,6 +2381,8 @@ err_unregister:
|
|||||||
err_free_irq:
|
err_free_irq:
|
||||||
free_irq(udc_controller->irq, udc_controller);
|
free_irq(udc_controller->irq, udc_controller);
|
||||||
err_iounmap:
|
err_iounmap:
|
||||||
|
fsl_udc_clk_release();
|
||||||
|
err_iounmap_noclk:
|
||||||
iounmap(dr_regs);
|
iounmap(dr_regs);
|
||||||
err_release_mem_region:
|
err_release_mem_region:
|
||||||
release_mem_region(res->start, res->end - res->start + 1);
|
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;
|
return -ENODEV;
|
||||||
udc_controller->done = &done;
|
udc_controller->done = &done;
|
||||||
|
|
||||||
|
fsl_udc_clk_release();
|
||||||
|
|
||||||
/* DR has been stopped in usb_gadget_unregister_driver() */
|
/* DR has been stopped in usb_gadget_unregister_driver() */
|
||||||
remove_proc_file();
|
remove_proc_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))
|
* 2 + ((windex & USB_DIR_IN) ? 1 : 0))
|
||||||
#define get_pipe_by_ep(EP) (ep_index(EP) * 2 + ep_is_in(EP))
|
#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
|
#endif
|
||||||
|
Loading…
Reference in New Issue
Block a user