Re: [PATCH 7/8] Add EHCI support for MX27 and MX31 based boards

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Thu, Jun 25, 2009 at 02:11:56PM +0200, Daniel Mack wrote:
> The Freescale MX27 and MX31 SoCs have a EHCI controller onboard.
> The controller is capable of USB on the go. This patch adds
> a driver to support all three of them.
> 
> Users have to pass details about serial interface configuration in the
> platform data.
> 
> The USB OTG core used here is the ARC core, so the driver should
> be renamed and probably be merged with ehci-fsl.c eventually.
> 
> Signed-off-by: Sascha Hauer <s.hauer@xxxxxxxxxxxxxx>
> Signed-off-by: Daniel Mack <daniel@xxxxxxxx>
> ---
>  arch/arm/plat-mxc/include/mach/mxc_ehci.h |   35 +++
>  drivers/usb/Kconfig                       |    1 +
>  drivers/usb/host/Kconfig                  |    7 +
>  drivers/usb/host/ehci-hcd.c               |    5 +
>  drivers/usb/host/ehci-mxc.c               |  335 +++++++++++++++++++++++++++++
>  5 files changed, 383 insertions(+), 0 deletions(-)
>  create mode 100644 arch/arm/plat-mxc/include/mach/mxc_ehci.h
>  create mode 100644 drivers/usb/host/ehci-mxc.c
> 
> diff --git a/arch/arm/plat-mxc/include/mach/mxc_ehci.h b/arch/arm/plat-mxc/include/mach/mxc_ehci.h
> new file mode 100644
> index 0000000..625e32b
> --- /dev/null
> +++ b/arch/arm/plat-mxc/include/mach/mxc_ehci.h
> @@ -0,0 +1,35 @@
> +#ifndef __INCLUDE_ASM_ARCH_MXC_EHCI_H
> +#define __INCLUDE_ASM_ARCH_MXC_EHCI_H
> +
> +/* values for portsc field */
> +#define MXC_EHCI_PHY_LOW_POWER_SUSPEND	(1 << 23)
> +#define MXC_EHCI_FORCE_FS		(1 << 24)
> +#define MXC_EHCI_UTMI_8BIT		(0 << 28)
> +#define MXC_EHCI_UTMI_16BIT		(1 << 28)
> +#define MXC_EHCI_SERIAL			(1 << 29)
> +#define MXC_EHCI_MODE_UTMI		(0 << 30)
> +#define MXC_EHCI_MODE_PHILIPS		(1 << 30)
> +#define MXC_EHCI_MODE_ULPI		(2 << 30)
> +#define MXC_EHCI_MODE_SERIAL		(3 << 30)
> +
> +/* values for flags field */
> +#define MXC_EHCI_INTERFACE_DIFF_UNI	(0 << 0)
> +#define MXC_EHCI_INTERFACE_DIFF_BI	(1 << 0)
> +#define MXC_EHCI_INTERFACE_SINGLE_UNI	(2 << 0)
> +#define MXC_EHCI_INTERFACE_SINGLE_BI	(3 << 0)
> +#define MXC_EHCI_INTERFACE_MASK		(0xf)
> +
> +#define MXC_EHCI_POWER_PINS_ENABLED	(1 << 5)
> +#define MXC_EHCI_TTL_ENABLED		(1 << 6)

I just had a look into the i.MX35 datasheet. The i.MX35 has only 2 host
cores and so the lower 16 bits of the USBCONTROL register are free for
other uses. Freescale put bits there which the platform code probably
wants to set. Maybe it's better to let the platform code set this
register. How about something like

mx31_set_usbcontrol(unsigned int val)
{
	clk_get() / clk_enable(),...

	writel();

	clk_put() / clk_disable()
}

which the platform can call during startup.

We would get rid of the problem with different base addresses aswell.

> +
> +struct mxc_usbh_platform_data {
> +	int (*init)(struct platform_device *pdev);
> +	int (*exit)(struct platform_device *pdev);
> +
> +	unsigned int	 portsc;
> +	unsigned int	 flags;
> +	struct usb_xcvr *xcvr;
> +};
> +
> +#endif /* __INCLUDE_ASM_ARCH_MXC_EHCI_H */
> +
> diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
> index dcd49f1..3ffaed5 100644
> --- a/drivers/usb/Kconfig
> +++ b/drivers/usb/Kconfig
> @@ -58,6 +58,7 @@ config USB_ARCH_HAS_EHCI
>  	default y if PPC_83xx
>  	default y if SOC_AU1200
>  	default y if ARCH_IXP4XX
> +	default y if ARCH_MXC
>  	default PCI
>  
>  # ARM SA1111 chips have a non-PCI based "OHCI-compatible" USB host interface.
> diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
> index 1576a05..59a5ebd 100644
> --- a/drivers/usb/host/Kconfig
> +++ b/drivers/usb/host/Kconfig
> @@ -105,6 +105,13 @@ config USB_EHCI_FSL
>  	---help---
>  	  Variation of ARC USB block used in some Freescale chips.
>  
> +config USB_EHCI_MXC
> +	bool "Support for Freescale on-chip EHCI USB controller"
> +	depends on USB_EHCI_HCD && ARCH_MXC
> +	select USB_EHCI_ROOT_HUB_TT
> +	---help---
> +	  Variation of ARC USB block used in some Freescale chips.
> +
>  config USB_EHCI_HCD_PPC_OF
>  	bool "EHCI support for PPC USB controller on OF platform bus"
>  	depends on USB_EHCI_HCD && PPC_OF
> diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
> index 2b72473..dc88ac9 100644
> --- a/drivers/usb/host/ehci-hcd.c
> +++ b/drivers/usb/host/ehci-hcd.c
> @@ -1092,6 +1092,11 @@ MODULE_LICENSE ("GPL");
>  #define	PLATFORM_DRIVER		ehci_fsl_driver
>  #endif
>  
> +#ifdef CONFIG_USB_EHCI_MXC
> +#include "ehci-mxc.c"
> +#define PLATFORM_DRIVER		ehci_mxc_driver
> +#endif
> +
>  #ifdef CONFIG_SOC_AU1200
>  #include "ehci-au1xxx.c"
>  #define	PLATFORM_DRIVER		ehci_hcd_au1xxx_driver
> diff --git a/drivers/usb/host/ehci-mxc.c b/drivers/usb/host/ehci-mxc.c
> new file mode 100644
> index 0000000..36b0924
> --- /dev/null
> +++ b/drivers/usb/host/ehci-mxc.c
> @@ -0,0 +1,335 @@
> +/*
> + * Copyright (c) 2008 Sascha Hauer <s.hauer@xxxxxxxxxxxxxx>, Pengutronix
> + * Copyright (c) 2009 Daniel Mack <daniel@xxxxxxxx>
> + *
> + * 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.
> + *
> + * This program is distributed in the hope that it will be useful, but
> + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
> + * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
> + * for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software Foundation,
> + * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
> + */
> +
> +#include <linux/platform_device.h>
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/usb/xcvr.h>
> +
> +#include <mach/mxc_ehci.h>
> +
> +#define ULPI_VIEWPORT_OFFSET	0x170
> +#define PORTSC_OFFSET		0x184
> +#define USBMODE_OFFSET		0x1a8
> +#define USBMODE_CM_HOST		3
> +#define USBCTRL_OTGBASE_OFFSET	0x600
> +
> +#define OTG_SIC_SHIFT		29
> +#define OTG_SIC_MASK		(0xf << OTG_SIC_SHIFT)
> +#define OTG_PM_BIT		(1 << 24)
> +
> +#define H2_SIC_SHIFT		21
> +#define H2_SIC_MASK		(0xf << H2_SIC_SHIFT)
> +#define H2_PM_BIT		(1 << 16)
> +#define H2_DT_BIT		(1 << 5)
> +
> +#define H1_SIC_SHIFT		13
> +#define H1_SIC_MASK		(0xf << H1_SIC_SHIFT)
> +#define H1_PM_BIT		(1 << 8)
> +#define H1_DT_BIT		(1 << 4)
> +
> +
> +struct ehci_mxc_priv {
> +	struct clk *usbclk, *ahbclk;
> +	struct usb_hcd *hcd;
> +};
> +
> +/* called during probe() after chip reset completes */
> +static int ehci_mxc_setup(struct usb_hcd *hcd)
> +{
> +	struct ehci_hcd *ehci = hcd_to_ehci(hcd);
> +	int retval;
> +
> +	/* EHCI registers start at offset 0x100 */
> +	ehci->caps = hcd->regs + 0x100;
> +	ehci->regs = hcd->regs + 0x100 +
> +	    HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase));
> +	dbg_hcs_params(ehci, "reset");
> +	dbg_hcc_params(ehci, "reset");
> +
> +	/* cache this readonly data; minimize chip reads */
> +	ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
> +
> +	retval = ehci_halt(ehci);
> +	if (retval)
> +		return retval;
> +
> +	/* data structure init */
> +	retval = ehci_init(hcd);
> +	if (retval)
> +		return retval;
> +
> +	hcd->has_tt = 1;
> +
> +	ehci->sbrn = 0x20;
> +
> +	ehci_reset(ehci);
> +
> +	ehci_port_power(ehci, 0);
> +	return 0;
> +}
> +
> +static const struct hc_driver ehci_mxc_hc_driver = {
> +	.description = hcd_name,
> +	.product_desc = "Freescale On-Chip EHCI Host Controller",
> +	.hcd_priv_size = sizeof(struct ehci_hcd),
> +
> +	/*
> +	 * generic hardware linkage
> +	 */
> +	.irq = ehci_irq,
> +	.flags = HCD_USB2 | HCD_MEMORY,
> +
> +	/*
> +	 * basic lifecycle operations
> +	 */
> +	.reset = ehci_mxc_setup,
> +	.start = ehci_run,
> +	.stop = ehci_stop,
> +	.shutdown = ehci_shutdown,
> +
> +	/*
> +	 * managing i/o requests and associated device resources
> +	 */
> +	.urb_enqueue = ehci_urb_enqueue,
> +	.urb_dequeue = ehci_urb_dequeue,
> +	.endpoint_disable = ehci_endpoint_disable,
> +
> +	/*
> +	 * scheduling support
> +	 */
> +	.get_frame_number = ehci_get_frame,
> +
> +	/*
> +	 * root hub support
> +	 */
> +	.hub_status_data = ehci_hub_status_data,
> +	.hub_control = ehci_hub_control,
> +	.bus_suspend = ehci_bus_suspend,
> +	.bus_resume = ehci_bus_resume,
> +	.relinquish_port = ehci_relinquish_port,
> +	.port_handed_over = ehci_port_handed_over,
> +};
> +
> +static int ehci_mxc_drv_probe(struct platform_device *pdev)
> +{
> +	struct mxc_usbh_platform_data *pdata = pdev->dev.platform_data;
> +	struct usb_hcd *hcd;
> +	struct resource *res;
> +	int irq, ret, temp;
> +	struct ehci_mxc_priv *priv;
> +	struct device *dev = &pdev->dev;
> +
> +	dev_info(&pdev->dev, "initializing i.MX USB Controller\n");
> +
> +	if (!pdata) {
> +		dev_err(dev, "No platform data given, bailing out.\n");
> +		return -EINVAL;
> +	}
> +
> +	irq = platform_get_irq(pdev, 0);
> +
> +	hcd = usb_create_hcd(&ehci_mxc_hc_driver, dev, dev_name(dev));
> +	if (!hcd)
> +		return -ENOMEM;
> +
> +	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
> +	if (!priv) {
> +		ret = -ENOMEM;
> +		goto err_alloc;
> +	}
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (!res) {
> +		dev_err(dev, "Found HC with no register addr. Check setup!\n");
> +		ret = -ENODEV;
> +		goto err_get_resource;
> +	}
> +
> +	hcd->rsrc_start = res->start;
> +	hcd->rsrc_len = resource_size(res);
> +
> +	if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
> +		dev_dbg(dev, "controller already in use\n");
> +		ret = -EBUSY;
> +		goto err_request_mem;
> +	}
> +
> +	hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
> +	if (!hcd->regs) {
> +		dev_err(dev, "error mapping memory\n");
> +		ret = -EFAULT;
> +		goto err_ioremap;
> +	}
> +
> +	/* enable clocks */
> +	priv->usbclk = clk_get(dev, "usb");
> +	if (IS_ERR(priv->usbclk)) {
> +		ret = PTR_ERR(priv->usbclk);
> +		goto err_clk;
> +	}
> +	clk_enable(priv->usbclk);
> +
> +	if (!cpu_is_mx35()) {
> +		priv->ahbclk = clk_get(dev, "usb_ahb");
> +		if (IS_ERR(priv->ahbclk)) {
> +			ret = PTR_ERR(priv->ahbclk);
> +			goto err_clk_ahb;
> +		}
> +		clk_enable(priv->ahbclk);
> +	}
> +
> +	/* set USBMODE to host mode */
> +	temp = readl(hcd->regs + USBMODE_OFFSET);
> +	writel(temp | USBMODE_CM_HOST, hcd->regs + USBMODE_OFFSET);
> +
> +	/* set up the PORTSCx register */
> +	writel(pdata->portsc, hcd->regs + PORTSC_OFFSET);
> +	mdelay(10);
> +
> +	/* setup USBCONTROL. Bits for all 3 ports are MUXed into that
> +	 * single register, hence we have to access memory outside the
> +	 * resource window */
> +	temp = readl(IO_ADDRESS(OTG_BASE_ADDR + USBCTRL_OTGBASE_OFFSET));
> +	switch (pdev->id) {
> +	case 0:	/* OTG port */
> +		temp &= ~(OTG_SIC_MASK | OTG_PM_BIT);
> +		temp |= (pdata->flags & MXC_EHCI_INTERFACE_MASK)
> +					<< OTG_SIC_SHIFT;
> +		if (pdata->flags & MXC_EHCI_POWER_PINS_ENABLED)
> +			temp |= OTG_PM_BIT;
> +
> +		break;
> +	case 1:	/* H1 port */
> +		temp &= ~(H1_SIC_MASK | H1_PM_BIT);
> +		temp |= (pdata->flags & MXC_EHCI_INTERFACE_MASK)
> +					<< H1_SIC_SHIFT;
> +		if (pdata->flags & MXC_EHCI_POWER_PINS_ENABLED)
> +			temp |= H1_PM_BIT;
> +
> +		if (!(pdata->flags & MXC_EHCI_TTL_ENABLED))
> +			temp |= H1_DT_BIT;
> +
> +		break;
> +	case 2:	/* H2 port */
> +		temp &= ~(H2_SIC_MASK | H2_PM_BIT);
> +		temp |= (pdata->flags & MXC_EHCI_INTERFACE_MASK)
> +					<< H2_SIC_SHIFT;
> +		if (!(pdata->flags & MXC_EHCI_POWER_PINS_ENABLED))
> +			temp |= H2_PM_BIT;
> +
> +		if (!(pdata->flags & MXC_EHCI_TTL_ENABLED))
> +			temp |= H2_DT_BIT;
> +
> +		break;
> +	}
> +	writel(temp, IO_ADDRESS(OTG_BASE_ADDR + USBCTRL_OTGBASE_OFFSET));
> +
> +	/* call platform specific init function */
> +	if (pdata->init) {
> +		ret = pdata->init(pdev);
> +		if (ret) {
> +			dev_err(dev, "platform init failed\n");
> +			goto err_init;
> +		}
> +	}
> +
> +	/* Initialize the transceiver */
> +	if (pdata->xcvr) {
> +		pdata->xcvr->access_priv = hcd->regs + ULPI_VIEWPORT_OFFSET;
> +		if (usb_xcvr_init(pdata->xcvr) != 0)
> +			dev_err(dev, "unable to init transceiver\n");
> +		else if (usb_xcvr_set_vbus(pdata->xcvr, 1) != 0)
> +			dev_err(dev, "unable to enable vbus on transceiver\n");
> +	}
> +
> +	priv->hcd = hcd;
> +	platform_set_drvdata(pdev, priv);
> +
> +	ret = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED);
> +	if (ret)
> +		goto err_add;
> +
> +	return 0;
> +
> +err_add:
> +	if (pdata && pdata->exit)
> +		pdata->exit(pdev);
> +err_init:
> +	if (priv->ahbclk) {
> +		clk_disable(priv->ahbclk);
> +		clk_put(priv->ahbclk);
> +	}
> +err_clk_ahb:
> +	clk_disable(priv->usbclk);
> +	clk_put(priv->usbclk);
> +err_clk:
> +	iounmap(hcd->regs);
> +err_ioremap:
> +	release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
> +err_request_mem:
> +err_get_resource:
> +	kfree(priv);
> +err_alloc:
> +	usb_put_hcd(hcd);
> +	return ret;
> +}
> +
> +static int __exit ehci_mxc_drv_remove(struct platform_device *pdev)
> +{
> +	struct ehci_mxc_priv *priv = platform_get_drvdata(pdev);
> +	struct usb_hcd *hcd = priv->hcd;
> +
> +	usb_remove_hcd(hcd);
> +	iounmap(hcd->regs);
> +	release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
> +	usb_put_hcd(hcd);
> +	platform_set_drvdata(pdev, NULL);
> +
> +	clk_disable(priv->usbclk);
> +	clk_put(priv->usbclk);
> +	if (priv->ahbclk) {
> +		clk_disable(priv->ahbclk);
> +		clk_put(priv->ahbclk);
> +	}
> +
> +	kfree(priv);
> +
> +	return 0;
> +}
> +
> +static void ehci_mxc_drv_shutdown(struct platform_device *pdev)
> +{
> +	struct ehci_mxc_priv *priv = platform_get_drvdata(pdev);
> +	struct usb_hcd *hcd = priv->hcd;
> +
> +	if (hcd->driver->shutdown)
> +		hcd->driver->shutdown(hcd);
> +}
> +
> +MODULE_ALIAS("platform:mxc-ehci");
> +
> +static struct platform_driver ehci_mxc_driver = {
> +	.probe = ehci_mxc_drv_probe,
> +	.remove = __exit_p(ehci_mxc_drv_remove),
> +	.shutdown = ehci_mxc_drv_shutdown,
> +	.driver = {
> +		   .name = "mxc-ehci",
> +	},
> +};
> -- 
> 1.6.3.1
> 
> 

-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux