[ARM] tegra: usb_phy: Power up/down bias and otg config pad circuitry
authorBenoit Goby <benoit@android.com>
Wed, 4 Aug 2010 04:27:49 +0000 (21:27 -0700)
committerColin Cross <ccross@android.com>
Wed, 6 Oct 2010 23:27:36 +0000 (16:27 -0700)
On tegra, the UTMIP PAD control logic is common to all utmip phys and
are controlled from usb1. This adds a reference count to turn off the
pads when all utmip phy are off.

Change-Id: I3537d5cc52df929f817e547a79da235394d2c265
Signed-off-by: Benoit Goby <benoit@android.com>
arch/arm/mach-tegra/include/mach/usb_phy.h
arch/arm/mach-tegra/usb_phy.c

index 85de3df94774340f21faae8c3dd9e2f298b16e13..eb25044fa1502e370a63ad2c37408cc1e732074b 100644 (file)
@@ -33,7 +33,9 @@ struct tegra_utmip_config {
 struct tegra_usb_phy {
        int instance;
        void __iomem *regs;
+       void __iomem *pad_regs;
        struct clk *pll_u;
+       struct clk *pad_clk;
        struct tegra_utmip_config *config;
 };
 
index 40358d3532ab24e9dfc7ec48ce5c08eb4c8197bc..8e9f24cf7ce4d2c52cccd531ae9c3483034f4536 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/io.h>
 #include <asm/mach-types.h>
 #include <mach/usb_phy.h>
+#include <mach/iomap.h>
 
 #define USB_PORTSC1            0x184
 #define   USB_PORTSC1_PTS(x)   (((x) & 0x3) << 30)
@@ -98,6 +99,9 @@
 #define   UTMIP_FORCE_PDDR_POWERDOWN   (1 << 4)
 #define   UTMIP_XCVR_TERM_RANGE_ADJ(x) (((x) & 0xf) << 18)
 
+static DEFINE_SPINLOCK(utmip_pad_lock);
+static int utmip_pad_count;
+
 static const int udc_freq_table[] = {
        12000000,
        13000000,
@@ -141,6 +145,76 @@ static struct tegra_utmip_config utmip_default[] = {
        },
 };
 
+static int utmip_pad_open(struct tegra_usb_phy *phy)
+{
+       phy->pad_clk = clk_get_sys("utmip-pad", NULL);
+       if (IS_ERR(phy->pad_clk)) {
+               pr_err("%s: can't get utmip pad clock\n", __func__);
+               return -1;
+       }
+
+       if (phy->instance == 0) {
+               phy->pad_regs = phy->regs;
+       } else {
+               phy->pad_regs = ioremap(TEGRA_USB_BASE, TEGRA_USB_SIZE);
+               if (!phy->pad_regs) {
+                       pr_err("%s: can't remap usb registers\n", __func__);
+                       clk_put(phy->pad_clk);
+                       return -ENOMEM;
+               }
+       }
+       return 0;
+}
+
+static void utmip_pad_close(struct tegra_usb_phy *phy)
+{
+       if (phy->instance != 0)
+               iounmap(phy->pad_regs);
+       clk_put(phy->pad_clk);
+}
+
+static void utmip_pad_power_on(struct tegra_usb_phy *phy)
+{
+       unsigned long val, flags;
+       void __iomem *base = phy->pad_regs;
+
+       clk_enable(phy->pad_clk);
+
+       spin_lock_irqsave(&utmip_pad_lock, flags);
+
+       if (utmip_pad_count++ == 0) {
+               val = readl(base + UTMIP_BIAS_CFG0);
+               val &= ~(UTMIP_OTGPD | UTMIP_BIASPD);
+               writel(val, base + UTMIP_BIAS_CFG0);
+       }
+
+       spin_unlock_irqrestore(&utmip_pad_lock, flags);
+}
+
+static int utmip_pad_power_off(struct tegra_usb_phy *phy)
+{
+       unsigned long val, flags;
+       void __iomem *base = phy->pad_regs;
+
+       if (!utmip_pad_count) {
+               pr_err("%s: utmip pad already powered off\n", __func__);
+               return -1;
+       }
+
+       spin_lock_irqsave(&utmip_pad_lock, flags);
+
+       if (--utmip_pad_count == 0) {
+               val = readl(base + UTMIP_BIAS_CFG0);
+               val |= UTMIP_OTGPD | UTMIP_BIASPD;
+               writel(val, base + UTMIP_BIAS_CFG0);
+       }
+
+       clk_disable(phy->pad_clk);
+
+       spin_unlock_irqrestore(&utmip_pad_lock, flags);
+       return 0;
+}
+
 static int utmi_phy_wait_stable(struct tegra_usb_phy *phy)
 {
        void __iomem *base = phy->regs;
@@ -220,6 +294,8 @@ void utmi_phy_power_on(struct tegra_usb_phy *phy,
                writel(val, base + USB_SUSP_CTRL);
        }
 
+       utmip_pad_power_on(phy);
+
        val = readl(base + UTMIP_XCVR_CFG0);
        val &= ~(UTMIP_FORCE_PD_POWERDOWN | UTMIP_FORCE_PD2_POWERDOWN |
                 UTMIP_FORCE_PDZI_POWERDOWN | UTMIP_XCVR_SETUP(~0) |
@@ -230,10 +306,6 @@ void utmi_phy_power_on(struct tegra_usb_phy *phy,
        val |= UTMIP_XCVR_LSRSLEW(config->xcvr_lsrslew);
        writel(val, base + UTMIP_XCVR_CFG0);
 
-       val = readl(base + UTMIP_BIAS_CFG0);
-       val &= ~(UTMIP_OTGPD | UTMIP_BIASPD);
-       writel(val, base + UTMIP_BIAS_CFG0);
-
        val = readl(base + UTMIP_XCVR_CFG1);
        val &= ~(UTMIP_FORCE_PDDISC_POWERDOWN | UTMIP_FORCE_PDCHRP_POWERDOWN |
                 UTMIP_FORCE_PDDR_POWERDOWN | UTMIP_XCVR_TERM_RANGE_ADJ(~0));
@@ -329,6 +401,8 @@ void utmi_phy_power_off(struct tegra_usb_phy *phy)
        val |= UTMIP_FORCE_PDDISC_POWERDOWN | UTMIP_FORCE_PDCHRP_POWERDOWN |
               UTMIP_FORCE_PDDR_POWERDOWN;
        writel(val, base + UTMIP_XCVR_CFG1);
+
+       utmip_pad_power_off(phy);
 }
 
 struct tegra_usb_phy *tegra_usb_phy_open(int instance, void __iomem *regs,
@@ -352,7 +426,7 @@ struct tegra_usb_phy *tegra_usb_phy_open(int instance, void __iomem *regs,
 
        phy->pll_u = clk_get_sys(NULL, "pll_u");
        if (IS_ERR(phy->pll_u)) {
-               printk(KERN_ERR "Can't get pll_u clock\n");
+               pr_err("Can't get pll_u clock\n");
                err = PTR_ERR(phy->pll_u);
                goto err0;
        }
@@ -363,14 +437,18 @@ struct tegra_usb_phy *tegra_usb_phy_open(int instance, void __iomem *regs,
                        break;
        }
        if (freq_sel == ARRAY_SIZE(udc_freq_table)) {
-               printk(KERN_ERR "invalid pll_u parent rate %ld\n", parent_rate);
+               pr_err("invalid pll_u parent rate %ld\n", parent_rate);
                err = -EINVAL;
                goto err1;
        }
 
        /* TODO usb2 ulpi */
-       if (phy->instance != 1)
+       if (phy->instance != 1) {
+               err = utmip_pad_open(phy);
+               if (err < 0)
+                       goto err1;
                utmi_phy_init(phy, freq_sel);
+       }
 
        return phy;
 
@@ -405,6 +483,8 @@ int tegra_usb_phy_power_off(struct tegra_usb_phy *phy)
 
 int tegra_usb_phy_close(struct tegra_usb_phy *phy)
 {
+       if (phy->instance != 1)
+               utmip_pad_close(phy);
        clk_put(phy->pll_u);
        kfree(phy);
        return 0;