This driver will handle i.MX233/i.MX28 and I hope soon i.MX6Q. I tried to keep this separate from the MXC EHCI to avoid further polution of the MXC EHCI, though eventually these two might be merged. NOTE: I still haven't figured out how to enable/disable the disconnection detector, it can't be enabled all the time, so I toggle PHY stuff from this driver, which I doubt is correct. Signed-off-by: Marek Vasut <marex@xxxxxxx> Cc: Chen Peter-B29397 <B29397@xxxxxxxxxxxxx> Cc: Detlev Zundel <dzu@xxxxxxx> Cc: Fabio Estevam <festevam@xxxxxxxxx> Cc: Li Frank-B20596 <B20596@xxxxxxxxxxxxx> Cc: Lin Tony-B19295 <B19295@xxxxxxxxxxxxx> Cc: Linux USB <linux-usb@xxxxxxxxxxxxxxx> Cc: Sascha Hauer <s.hauer@xxxxxxxxxxxxxx> Cc: Shawn Guo <shawn.guo@xxxxxxxxxxxxx> Cc: Shawn Guo <shawn.guo@xxxxxxxxxx> Cc: Stefano Babic <sbabic@xxxxxxx> Cc: Subodh Nijsure <snijsure@xxxxxxxxxxxx> Cc: Tony Lin <tony.lin@xxxxxxxxxxxxx> Cc: Wolfgang Denk <wd@xxxxxxx> --- drivers/usb/host/Kconfig | 7 ++ drivers/usb/host/ehci-hcd.c | 5 + drivers/usb/host/ehci-mxs.c | 254 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 266 insertions(+) create mode 100644 drivers/usb/host/ehci-mxs.c diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index f788eb8..85ed593 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -148,6 +148,13 @@ config USB_EHCI_MXC ---help--- Variation of ARC USB block used in some Freescale chips. +config USB_EHCI_MXS + bool "Support for Freescale i.MX28 on-chip EHCI USB controller" + depends on USB_EHCI_HCD && ARCH_MXS + select USB_EHCI_ROOT_HUB_TT + ---help--- + Enable USB support for i.MX28. + config USB_EHCI_HCD_OMAP bool "EHCI support for OMAP3 and later chips" depends on USB_EHCI_HCD && ARCH_OMAP diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 806cc95..d0df8be 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -1263,6 +1263,11 @@ MODULE_LICENSE ("GPL"); #define PLATFORM_DRIVER ehci_mxc_driver #endif +#ifdef CONFIG_USB_EHCI_MXS +#include "ehci-mxs.c" +#define PLATFORM_DRIVER ehci_mxs_driver +#endif + #ifdef CONFIG_USB_EHCI_SH #include "ehci-sh.c" #define PLATFORM_DRIVER ehci_hcd_sh_driver diff --git a/drivers/usb/host/ehci-mxs.c b/drivers/usb/host/ehci-mxs.c new file mode 100644 index 0000000..47e6502 --- /dev/null +++ b/drivers/usb/host/ehci-mxs.c @@ -0,0 +1,254 @@ +/* + * Freescale i.MX28 EHCI driver + * + * Copyright (c) 2012 Marek Vasut <marex@xxxxxxx> + * on behalf of DENX Software Engineering GmbH + * + * Based on MXC EHCI driver: + * + * Copyright (c) 2009 Daniel Mack <daniel@xxxxxxxx> + * Copyright (c) 2008 Sascha Hauer <s.hauer@xxxxxxxxxxxxxx>, Pengutronix + * + * 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/otg.h> +#include <linux/usb/ulpi.h> +#include <linux/slab.h> +#include <linux/fsl_devices.h> + +#include <mach/hardware.h> +#include <mach/devices-common.h> +#include <mach/mx28.h> + +#include <asm/mach-types.h> + +#define MXC_EHCI_MODE_ULPI (2 << 30) + +/* Called during probe() after chip reset completes */ +static int ehci_mxs_setup(struct usb_hcd *hcd) +{ + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + int retval; + + 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); + + hcd->has_tt = 1; + + retval = ehci_halt(ehci); + if (retval) + return retval; + + /* Data structure init. */ + retval = ehci_init(hcd); + if (retval) + return retval; + + ehci->sbrn = 0x20; + + ehci_reset(ehci); + + ehci_port_power(ehci, 0); + + return 0; +} + +static irqreturn_t mxs_ehci_irq(struct usb_hcd *hcd) +{ + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + struct usb_phy *phy = usb_get_transceiver(); + uint32_t status; + static uint32_t discon; + + if (phy && phy->otg && phy->otg->set_vbus) { + status = ehci_readl(ehci, &ehci->regs->status); + status = !!(status & STS_PCD); + if (status != discon) { + discon = status; + phy->otg->set_vbus(phy->otg, status); + } + } + + return ehci_irq(hcd); +} + +static const struct hc_driver ehci_mxs_hc_driver = { + .description = hcd_name, + .product_desc = "Freescale i.MX28 On-Chip EHCI Host Controller", + .hcd_priv_size = sizeof(struct ehci_hcd), + + /* + * Generic hardware linkage + */ + .irq = mxs_ehci_irq, + .flags = HCD_USB2 | HCD_MEMORY, + + /* + * Basic lifecycle operations + */ + .reset = ehci_mxs_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, + .endpoint_reset = ehci_endpoint_reset, + + /* + * 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, + + .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, +}; + +static int ehci_mxs_drv_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct imx_usb_data *data = pdev->dev.platform_data; + struct usb_hcd *hcd; + struct ehci_hcd *ehci; + struct usb_phy *phy; + int ret; + + dev_info(dev, "Initializing i.MX28 USB Controller\n"); + + if (!data) { + dev_err(dev, "USB Host platform data missing!\n"); + return -ENODEV; + } + + /* Create HCD controller instance. */ + hcd = usb_create_hcd(&ehci_mxs_hc_driver, dev, dev_name(dev)); + if (!hcd) { + dev_err(dev, "Failed to create HCD instance!\n"); + return -ENOMEM; + } + + hcd->rsrc_start = data->mem_res->start; + hcd->rsrc_len = resource_size(data->mem_res); + hcd->regs = data->mem; + + clk_enable(data->clk); + + /* Wait for the controller to stabilize. */ + mdelay(10); + + /* Initialize the transceiver */ +#ifdef CONFIG_USB_OTG_UTILS + phy = usb_get_transceiver(); + if (!phy) { + dev_err(dev, "Unable to find transceiver.\n"); + ret = -ENODEV; + goto err_phy; + } + + ret = usb_phy_init(phy); + if (ret < 0) { + dev_err(dev, "Unable init transceiver\n"); + ret = -ENODEV; + goto err_phy_init; + } +#else + dev_info(dev, "USB_MXS_PHY must have CONFIG_USB_OTG_UTILS enabled\n"); + goto err_phy; +#endif + + ehci = hcd_to_ehci(hcd); + + /* EHCI registers start at offset 0x100 */ + ehci->caps = hcd->regs + 0x100; + ehci->regs = hcd->regs + 0x100 + + HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase)); + + /* Set up the PORTSCx register */ + ehci_writel(ehci, MXC_EHCI_MODE_ULPI, &ehci->regs->port_status[0]); + + platform_set_drvdata(pdev, hcd); + + ret = usb_add_hcd(hcd, data->irq, IRQF_SHARED); + if (ret) + goto err_phy_init; + + return 0; + +err_phy_init: +#ifdef CONFIG_USB_OTG_UTILS + if (phy) + usb_put_transceiver(phy); +#endif +err_phy: + clk_disable(data->clk); + usb_put_hcd(hcd); + return ret; +} + +static int __exit ehci_mxs_drv_remove(struct platform_device *pdev) +{ + struct imx_usb_data *data = pdev->dev.platform_data; + struct usb_hcd *hcd = platform_get_drvdata(pdev); + struct usb_phy *phy = usb_get_transceiver(); + + if (phy) + usb_phy_shutdown(phy); + + usb_remove_hcd(hcd); + usb_put_hcd(hcd); + platform_set_drvdata(pdev, NULL); + clk_disable(data->clk); + + return 0; +} + +static void ehci_mxs_drv_shutdown(struct platform_device *pdev) +{ + struct usb_hcd *hcd = platform_get_drvdata(pdev); + + if (hcd->driver->shutdown) + hcd->driver->shutdown(hcd); +} + +static struct platform_driver ehci_mxs_driver = { + .probe = ehci_mxs_drv_probe, + .remove = __exit_p(ehci_mxs_drv_remove), + .shutdown = ehci_mxs_drv_shutdown, + .driver = { + .name = "mxs-ehci", + }, +}; + +MODULE_ALIAS("platform:mxs-ehci"); -- 1.7.9.5 -- 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