Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 54e4026b authored by Guennadi Liakhovetski's avatar Guennadi Liakhovetski Committed by Greg Kroah-Hartman
Browse files

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: default avatarGuennadi Liakhovetski <lg@denx.de>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 568d422e
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -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
+4 −0
Original line number Diff line number Diff line
@@ -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 −0
Original line number Diff line number Diff line
/*
 * 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);
}
+46 −23
Original line number Diff line number Diff line
@@ -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();

+18 −0
Original line number Diff line number Diff line
@@ -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