mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-23 09:19:51 +00:00
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
5 changed files with 164 additions and 24 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
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/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 @@ static int __init fsl_udc_probe(struct platform_device *pdev)
|
|||
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();
|
||||
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue