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]

 



Hi Daniel,

I have another comment about the patch, please see below.

Daniel Mack wrote:

commit 04bcb5f8471aa181bcd5a4edd2357fd366281a3f
Author: Daniel Mack <daniel@xxxxxxxx>
Date:   Thu Jun 25 13:07:54 2009 +0200

    Add EHCI support for MX27 and MX31 based boards

    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>

diff --git a/arch/arm/plat-mxc/Makefile b/arch/arm/plat-mxc/Makefile
index 545412f..20a9dba 100644
--- a/arch/arm/plat-mxc/Makefile
+++ b/arch/arm/plat-mxc/Makefile
@@ -10,3 +10,5 @@ obj-$(CONFIG_ARCH_MX2) += iomux-mx1-mx2.o dma-mx1-mx2.o
 obj-$(CONFIG_ARCH_MXC_IOMUX_V3) += iomux-v3.o
 obj-$(CONFIG_MXC_PWM)  += pwm.o
 obj-$(CONFIG_MXC_ULPI) += ulpi.o
+obj-$(CONFIG_USB_EHCI_MXC)     += ehci.o
+
diff --git a/arch/arm/plat-mxc/ehci.c b/arch/arm/plat-mxc/ehci.c
new file mode 100644
index 0000000..2539454
--- /dev/null
+++ b/arch/arm/plat-mxc/ehci.c
@@ -0,0 +1,89 @@
+/*
+ * 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 <mach/hardware.h>
+#include <mach/mxc_ehci.h>
+#include <asm/io.h>
+
+#define USBCTRL_OTGBASE_OFFSET 0x600
+
+#define MX31_OTG_SIC_SHIFT     29
+#define MX31_OTG_SIC_MASK      (0xf << MX31_OTG_SIC_SHIFT)
+#define MX31_OTG_PM_BIT                (1 << 24)
+
+#define MX31_H2_SIC_SHIFT      21
+#define MX31_H2_SIC_MASK       (0xf << MX31_H2_SIC_SHIFT)
+#define MX31_H2_PM_BIT         (1 << 16)
+#define MX31_H2_DT_BIT         (1 << 5)
+
+#define MX31_H1_SIC_SHIFT      13
+#define MX31_H1_SIC_MASK       (0xf << MX31_H1_SIC_SHIFT)
+#define MX31_H1_PM_BIT         (1 << 8)
+#define MX31_H1_DT_BIT         (1 << 4)
+
+int mxc_set_usbcontrol(int port, unsigned int flags)
+{
+       unsigned int v;
+
+       if (cpu_is_mx31()) {
+               v = readl(IO_ADDRESS(OTG_BASE_ADDR + USBCTRL_OTGBASE_OFFSET));
+
+               switch (port) {
+               case 0: /* OTG port */
+                       v &= ~(MX31_OTG_SIC_MASK | MX31_OTG_PM_BIT);
+                       v |= (flags & MXC_EHCI_INTERFACE_MASK)
+                                       << MX31_OTG_SIC_SHIFT;
+                       if (flags & MXC_EHCI_POWER_PINS_ENABLED)
+                               v |= MX31_OTG_PM_BIT;
+
+                       break;
+               case 1: /* H1 port */
+                       v &= ~(MX31_H1_SIC_MASK | MX31_H1_PM_BIT);
+                       v |= (flags & MXC_EHCI_INTERFACE_MASK)
+                                               << MX31_H1_SIC_SHIFT;
+                       if (flags & MXC_EHCI_POWER_PINS_ENABLED)
+                               v |= MX31_H1_PM_BIT;
+
+                       if (!(flags & MXC_EHCI_TTL_ENABLED))
+                               v |= MX31_H1_DT_BIT;
+
+                       break;
+               case 2: /* H2 port */
+                       v &= ~(MX31_H2_SIC_MASK | MX31_H2_PM_BIT);
+                       v |= (flags & MXC_EHCI_INTERFACE_MASK)
+                                               << MX31_H2_SIC_SHIFT;
+                       if (!(flags & MXC_EHCI_POWER_PINS_ENABLED))
+                               v |= MX31_H2_PM_BIT;
+
+                       if (!(flags & MXC_EHCI_TTL_ENABLED))
+                               v |= MX31_H2_DT_BIT;
+
+                       break;
+               }
+
+               writel(v, IO_ADDRESS(OTG_BASE_ADDR + USBCTRL_OTGBASE_OFFSET));
+               return 0;
+       }
+
+       printk(KERN_WARNING
+               "%s() unable to setup USBCONTROL for this CPU\n", __func__);
+       return -EINVAL;
+}
+
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..7259edc
--- /dev/null
+++ b/arch/arm/plat-mxc/include/mach/mxc_ehci.h
@@ -0,0 +1,37 @@
+#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)
+
+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;
+};
+
+extern int mxc_set_usbcontrol(int port, unsigned int flags);
+
+#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..303d6f7
--- /dev/null
+++ b/drivers/usb/host/ehci-mxc.c
@@ -0,0 +1,286 @@
+/*
+ * 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
+
+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. */
+       ret = mxc_set_usbcontrol(pdev->id, pdata->flags);
+       if (ret < 0)
+               goto err_init;
+
+       /* 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;

Shouldn't we call pdata->exit (as you do it correctly in one error path for drv_probe) in drv_remove too ? I would say yes, because for instance I claim gpios in pdata->init that are freed by pdata->exit. So when I have unloaded the ehci-hcd module, the reloading fails (gpio not available).

I can provide a patch, but since this is trvial. Tell me if you want it.

+
+       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",
+       },
+};
--
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


Best Regards

Val

--
Valentin Longchamp, PhD Student, EPFL-STI-LSRO1
valentin.longchamp@xxxxxxx, Phone: +41216937827
http://people.epfl.ch/valentin.longchamp
MEA3485, Station 9, CH-1015 Lausanne
--
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