]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - drivers/usb/host/ehci-mxc.c
Merge tag 'v2.6.38' of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
[mv-sheeva.git] / drivers / usb / host / ehci-mxc.c
index bce85055019a190d3d835fd0386d8a8cd7f4e746..c8e360d7d9759dbde4eff329781c252d5d27e806 100644 (file)
 #include <linux/clk.h>
 #include <linux/delay.h>
 #include <linux/usb/otg.h>
+#include <linux/usb/ulpi.h>
 #include <linux/slab.h>
 
 #include <mach/mxc_ehci.h>
 
+#include <asm/mach-types.h>
+
 #define ULPI_VIEWPORT_OFFSET   0x170
 
 struct ehci_mxc_priv {
-       struct clk *usbclk, *ahbclk;
+       struct clk *usbclk, *ahbclk, *phy1clk;
        struct usb_hcd *hcd;
 };
 
@@ -36,14 +39,8 @@ struct ehci_mxc_priv {
 static int ehci_mxc_setup(struct usb_hcd *hcd)
 {
        struct ehci_hcd *ehci = hcd_to_ehci(hcd);
-       struct device *dev = hcd->self.controller;
-       struct mxc_usbh_platform_data *pdata = dev_get_platdata(dev);
        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");
 
@@ -65,12 +62,6 @@ static int ehci_mxc_setup(struct usb_hcd *hcd)
 
        ehci_reset(ehci);
 
-       /* set up the PORTSCx register */
-       ehci_writel(ehci, pdata->portsc, &ehci->regs->port_status[0]);
-
-       /* is this really needed? */
-       msleep(10);
-
        ehci_port_power(ehci, 0);
        return 0;
 }
@@ -100,6 +91,7 @@ static const struct hc_driver ehci_mxc_hc_driver = {
        .urb_enqueue = ehci_urb_enqueue,
        .urb_dequeue = ehci_urb_dequeue,
        .endpoint_disable = ehci_endpoint_disable,
+       .endpoint_reset = ehci_endpoint_reset,
 
        /*
         * scheduling support
@@ -115,6 +107,8 @@ static const struct hc_driver ehci_mxc_hc_driver = {
        .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_mxc_drv_probe(struct platform_device *pdev)
@@ -123,8 +117,10 @@ static int ehci_mxc_drv_probe(struct platform_device *pdev)
        struct usb_hcd *hcd;
        struct resource *res;
        int irq, ret;
+       unsigned int flags;
        struct ehci_mxc_priv *priv;
        struct device *dev = &pdev->dev;
+       struct ehci_hcd *ehci;
 
        dev_info(&pdev->dev, "initializing i.MX USB Controller\n");
 
@@ -168,17 +164,6 @@ static int ehci_mxc_drv_probe(struct platform_device *pdev)
                goto err_ioremap;
        }
 
-       /* call platform specific init function */
-       if (pdata->init) {
-               ret = pdata->init(pdev);
-               if (ret) {
-                       dev_err(dev, "platform init failed\n");
-                       goto err_init;
-               }
-               /* platforms need some time to settle changed IO settings */
-               mdelay(10);
-       }
-
        /* enable clocks */
        priv->usbclk = clk_get(dev, "usb");
        if (IS_ERR(priv->usbclk)) {
@@ -196,11 +181,46 @@ static int ehci_mxc_drv_probe(struct platform_device *pdev)
                clk_enable(priv->ahbclk);
        }
 
+       /* "dr" device has its own clock on i.MX51 */
+       if (cpu_is_mx51() && (pdev->id == 0)) {
+               priv->phy1clk = clk_get(dev, "usb_phy1");
+               if (IS_ERR(priv->phy1clk)) {
+                       ret = PTR_ERR(priv->phy1clk);
+                       goto err_clk_phy;
+               }
+               clk_enable(priv->phy1clk);
+       }
+
+
+       /* call platform specific init function */
+       if (pdata->init) {
+               ret = pdata->init(pdev);
+               if (ret) {
+                       dev_err(dev, "platform init failed\n");
+                       goto err_init;
+               }
+               /* platforms need some time to settle changed IO settings */
+               mdelay(10);
+       }
+
        /* setup specific usb hw */
        ret = mxc_initialize_usb_hw(pdev->id, pdata->flags);
        if (ret < 0)
                goto err_init;
 
+       ehci = hcd_to_ehci(hcd);
+
+       /* 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));
+
+       /* set up the PORTSCx register */
+       ehci_writel(ehci, pdata->portsc, &ehci->regs->port_status[0]);
+
+       /* is this really needed? */
+       msleep(10);
+
        /* Initialize the transceiver */
        if (pdata->otg) {
                pdata->otg->io_priv = hcd->regs + ULPI_VIEWPORT_OFFSET;
@@ -224,12 +244,34 @@ static int ehci_mxc_drv_probe(struct platform_device *pdev)
        if (ret)
                goto err_add;
 
+       if (pdata->otg) {
+               /*
+                * efikamx and efikasb have some hardware bug which is
+                * preventing usb to work unless CHRGVBUS is set.
+                * It's in violation of USB specs
+                */
+               if (machine_is_mx51_efikamx() || machine_is_mx51_efikasb()) {
+                       flags = otg_io_read(pdata->otg, ULPI_OTG_CTRL);
+                       flags |= ULPI_OTG_CTRL_CHRGVBUS;
+                       ret = otg_io_write(pdata->otg, flags, ULPI_OTG_CTRL);
+                       if (ret) {
+                               dev_err(dev, "unable to set CHRVBUS\n");
+                               goto err_add;
+                       }
+               }
+       }
+
        return 0;
 
 err_add:
        if (pdata && pdata->exit)
                pdata->exit(pdev);
 err_init:
+       if (priv->phy1clk) {
+               clk_disable(priv->phy1clk);
+               clk_put(priv->phy1clk);
+       }
+err_clk_phy:
        if (priv->ahbclk) {
                clk_disable(priv->ahbclk);
                clk_put(priv->ahbclk);
@@ -273,6 +315,10 @@ static int __exit ehci_mxc_drv_remove(struct platform_device *pdev)
                clk_disable(priv->ahbclk);
                clk_put(priv->ahbclk);
        }
+       if (priv->phy1clk) {
+               clk_disable(priv->phy1clk);
+               clk_put(priv->phy1clk);
+       }
 
        kfree(priv);