[ARM] tegra: usb_phy: Power up/down bias and otg config pad circuitry

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>
This commit is contained in:
Benoit Goby
2010-08-03 21:27:49 -07:00
committed by Colin Cross
parent f2b8046549
commit 56fc16b7e0
2 changed files with 89 additions and 7 deletions

View 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;
};

View 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;