usb: phy: tegra: don't call into tegra-ehci directly
[deliverable/linux.git] / drivers / usb / host / ehci-tegra.c
index acf17556bd87b87b7bb3cc1a1c5a167ec15d1f7c..e3eddc31ac83fd4f2065a5815967700fa6932ef7 100644 (file)
@@ -2,7 +2,7 @@
  * EHCI-compliant USB host controller driver for NVIDIA Tegra SoCs
  *
  * Copyright (C) 2010 Google, Inc.
- * Copyright (C) 2009 NVIDIA Corporation
+ * Copyright (C) 2009 - 2013 NVIDIA Corporation
  *
  * 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
 #include <linux/of.h>
 #include <linux/of_gpio.h>
 #include <linux/pm_runtime.h>
-
+#include <linux/usb/ehci_def.h>
 #include <linux/usb/tegra_usb_phy.h>
+#include <linux/clk/tegra.h>
 
 #define TEGRA_USB_BASE                 0xC5000000
 #define TEGRA_USB2_BASE                        0xC5004000
 #define TEGRA_USB3_BASE                        0xC5008000
 
+/* PORTSC registers */
+#define TEGRA_USB_PORTSC1                      0x184
+#define TEGRA_USB_PORTSC1_PTS(x)       (((x) & 0x3) << 30)
+#define TEGRA_USB_PORTSC1_PHCD (1 << 23)
+
 #define TEGRA_USB_DMA_ALIGN 32
 
 struct tegra_ehci_hcd {
        struct ehci_hcd *ehci;
        struct tegra_usb_phy *phy;
        struct clk *clk;
-       struct clk *emc_clk;
        struct usb_phy *transceiver;
        int host_resumed;
        int port_resuming;
+       bool needs_double_reset;
        enum tegra_usb_phy_port_speed port_speed;
 };
 
@@ -50,9 +56,8 @@ static void tegra_ehci_power_up(struct usb_hcd *hcd)
 {
        struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller);
 
-       clk_prepare_enable(tegra->emc_clk);
        clk_prepare_enable(tegra->clk);
-       usb_phy_set_suspend(&tegra->phy->u_phy, 0);
+       usb_phy_set_suspend(hcd->phy, 0);
        tegra->host_resumed = 1;
 }
 
@@ -61,9 +66,8 @@ static void tegra_ehci_power_down(struct usb_hcd *hcd)
        struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller);
 
        tegra->host_resumed = 0;
-       usb_phy_set_suspend(&tegra->phy->u_phy, 1);
+       usb_phy_set_suspend(hcd->phy, 1);
        clk_disable_unprepare(tegra->clk);
-       clk_disable_unprepare(tegra->emc_clk);
 }
 
 static int tegra_ehci_internal_port_reset(
@@ -156,7 +160,7 @@ static int tegra_ehci_hub_control(
                if (tegra->port_resuming && !(temp & PORT_SUSPEND)) {
                        /* Resume completed, re-enable disconnect detection */
                        tegra->port_resuming = 0;
-                       tegra_usb_phy_postresume(tegra->phy);
+                       tegra_usb_phy_postresume(hcd->phy);
                }
        }
 
@@ -184,7 +188,7 @@ static int tegra_ehci_hub_control(
        }
 
        /* For USB1 port we need to issue Port Reset twice internally */
-       if (tegra->phy->instance == 0 &&
+       if (tegra->needs_double_reset &&
           (typeReq == SetPortFeature && wValue == USB_PORT_FEAT_RESET)) {
                spin_unlock_irqrestore(&ehci->lock, flags);
                return tegra_ehci_internal_port_reset(ehci, status_reg);
@@ -209,7 +213,7 @@ static int tegra_ehci_hub_control(
                        goto done;
 
                /* Disable disconnect detection during port resume */
-               tegra_usb_phy_preresume(tegra->phy);
+               tegra_usb_phy_preresume(hcd->phy);
 
                ehci->reset_done[wIndex-1] = jiffies + msecs_to_jiffies(25);
 
@@ -473,7 +477,7 @@ static int controller_resume(struct device *dev)
        }
 
        /* Force the phy to keep data lines in suspend state */
-       tegra_ehci_phy_restore_start(tegra->phy, tegra->port_speed);
+       tegra_ehci_phy_restore_start(hcd->phy, tegra->port_speed);
 
        /* Enable host mode */
        tdi_reset(ehci);
@@ -540,17 +544,17 @@ static int controller_resume(struct device *dev)
                }
        }
 
-       tegra_ehci_phy_restore_end(tegra->phy);
+       tegra_ehci_phy_restore_end(hcd->phy);
        goto done;
 
  restart:
        if (tegra->port_speed <= TEGRA_USB_PHY_PORT_SPEED_HIGH)
-               tegra_ehci_phy_restore_end(tegra->phy);
+               tegra_ehci_phy_restore_end(hcd->phy);
 
        tegra_ehci_restart(hcd);
 
  done:
-       tegra_usb_phy_preresume(tegra->phy);
+       tegra_usb_phy_preresume(hcd->phy);
        tegra->port_resuming = 1;
        return 0;
 }
@@ -604,6 +608,35 @@ static const struct dev_pm_ops tegra_ehci_pm_ops = {
 
 #endif
 
+/* Bits of PORTSC1, which will get cleared by writing 1 into them */
+#define TEGRA_PORTSC1_RWC_BITS (PORT_CSC | PORT_PEC | PORT_OCC)
+
+static void tegra_ehci_set_pts(struct usb_phy *x, u8 pts_val)
+{
+       unsigned long val;
+       struct usb_hcd *hcd = bus_to_hcd(x->otg->host);
+       void __iomem *base = hcd->regs;
+
+       val = readl(base + TEGRA_USB_PORTSC1) & ~TEGRA_PORTSC1_RWC_BITS;
+       val &= ~TEGRA_USB_PORTSC1_PTS(3);
+       val |= TEGRA_USB_PORTSC1_PTS(pts_val & 3);
+       writel(val, base + TEGRA_USB_PORTSC1);
+}
+
+static void tegra_ehci_set_phcd(struct usb_phy *x, bool enable)
+{
+       unsigned long val;
+       struct usb_hcd *hcd = bus_to_hcd(x->otg->host);
+       void __iomem *base = hcd->regs;
+
+       val = readl(base + TEGRA_USB_PORTSC1) & ~TEGRA_PORTSC1_RWC_BITS;
+       if (enable)
+               val |= TEGRA_USB_PORTSC1_PHCD;
+       else
+               val &= ~TEGRA_USB_PORTSC1_PHCD;
+       writel(val, base + TEGRA_USB_PORTSC1);
+}
+
 static u64 tegra_ehci_dma_mask = DMA_BIT_MASK(32);
 
 static int tegra_ehci_probe(struct platform_device *pdev)
@@ -615,6 +648,7 @@ static int tegra_ehci_probe(struct platform_device *pdev)
        int err = 0;
        int irq;
        int instance = pdev->id;
+       struct usb_phy *u_phy;
 
        pdata = pdev->dev.platform_data;
        if (!pdata) {
@@ -656,15 +690,12 @@ static int tegra_ehci_probe(struct platform_device *pdev)
        if (err)
                goto fail_clk;
 
-       tegra->emc_clk = devm_clk_get(&pdev->dev, "emc");
-       if (IS_ERR(tegra->emc_clk)) {
-               dev_err(&pdev->dev, "Can't get emc clock\n");
-               err = PTR_ERR(tegra->emc_clk);
-               goto fail_emc_clk;
-       }
+       tegra_periph_reset_assert(tegra->clk);
+       udelay(1);
+       tegra_periph_reset_deassert(tegra->clk);
 
-       clk_prepare_enable(tegra->emc_clk);
-       clk_set_rate(tegra->emc_clk, 400000000);
+       tegra->needs_double_reset = of_property_read_bool(pdev->dev.of_node,
+               "nvidia,needs-double-reset");
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        if (!res) {
@@ -705,19 +736,31 @@ static int tegra_ehci_probe(struct platform_device *pdev)
 
        tegra->phy = tegra_usb_phy_open(&pdev->dev, instance, hcd->regs,
                                        pdata->phy_config,
-                                       TEGRA_USB_PHY_MODE_HOST);
+                                       TEGRA_USB_PHY_MODE_HOST,
+                                       tegra_ehci_set_pts,
+                                       tegra_ehci_set_phcd);
        if (IS_ERR(tegra->phy)) {
                dev_err(&pdev->dev, "Failed to open USB phy\n");
                err = -ENXIO;
                goto fail_io;
        }
 
-       usb_phy_init(&tegra->phy->u_phy);
+       hcd->phy = u_phy = &tegra->phy->u_phy;
+       usb_phy_init(hcd->phy);
 
-       err = usb_phy_set_suspend(&tegra->phy->u_phy, 0);
+       u_phy->otg = devm_kzalloc(&pdev->dev, sizeof(struct usb_otg),
+                            GFP_KERNEL);
+       if (!u_phy->otg) {
+               dev_err(&pdev->dev, "Failed to alloc memory for otg\n");
+               err = -ENOMEM;
+               goto fail_io;
+       }
+       u_phy->otg->host = hcd_to_bus(hcd);
+
+       err = usb_phy_set_suspend(hcd->phy, 0);
        if (err) {
                dev_err(&pdev->dev, "Failed to power on the phy\n");
-               goto fail;
+               goto fail_phy;
        }
 
        tegra->host_resumed = 1;
@@ -727,17 +770,17 @@ static int tegra_ehci_probe(struct platform_device *pdev)
        if (!irq) {
                dev_err(&pdev->dev, "Failed to get IRQ\n");
                err = -ENODEV;
-               goto fail;
+               goto fail_phy;
        }
 
-#ifdef CONFIG_USB_OTG_UTILS
        if (pdata->operating_mode == TEGRA_USB_OTG) {
                tegra->transceiver =
                        devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2);
-               if (!IS_ERR_OR_NULL(tegra->transceiver))
+               if (!IS_ERR(tegra->transceiver))
                        otg_set_host(tegra->transceiver->otg, &hcd->self);
+       } else {
+               tegra->transceiver = ERR_PTR(-ENODEV);
        }
-#endif
 
        err = usb_add_hcd(hcd, irq, IRQF_SHARED);
        if (err) {
@@ -756,14 +799,11 @@ static int tegra_ehci_probe(struct platform_device *pdev)
        return err;
 
 fail:
-#ifdef CONFIG_USB_OTG_UTILS
-       if (!IS_ERR_OR_NULL(tegra->transceiver))
+       if (!IS_ERR(tegra->transceiver))
                otg_set_host(tegra->transceiver->otg, NULL);
-#endif
-       usb_phy_shutdown(&tegra->phy->u_phy);
+fail_phy:
+       usb_phy_shutdown(hcd->phy);
 fail_io:
-       clk_disable_unprepare(tegra->emc_clk);
-fail_emc_clk:
        clk_disable_unprepare(tegra->clk);
 fail_clk:
        usb_put_hcd(hcd);
@@ -779,20 +819,15 @@ static int tegra_ehci_remove(struct platform_device *pdev)
        pm_runtime_disable(&pdev->dev);
        pm_runtime_put_noidle(&pdev->dev);
 
-#ifdef CONFIG_USB_OTG_UTILS
-       if (!IS_ERR_OR_NULL(tegra->transceiver))
+       if (!IS_ERR(tegra->transceiver))
                otg_set_host(tegra->transceiver->otg, NULL);
-#endif
 
+       usb_phy_shutdown(hcd->phy);
        usb_remove_hcd(hcd);
        usb_put_hcd(hcd);
 
-       usb_phy_shutdown(&tegra->phy->u_phy);
-
        clk_disable_unprepare(tegra->clk);
 
-       clk_disable_unprepare(tegra->emc_clk);
-
        return 0;
 }
 
This page took 0.02932 seconds and 5 git commands to generate.