summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRakesh Bodla <rbodla@nvidia.com>2011-02-28 21:58:49 +0530
committerVarun Colbert <vcolbert@nvidia.com>2011-02-28 13:59:34 -0800
commit3e5c594a08f4bee917d9617e88fd0c0cf64d4e40 (patch)
treef709d5ebf7fb21c6062b486ed8bf402cfc15daf8
parentb2f6823a58c005e68471abb1a48f3079bb424588 (diff)
tegra: usb: phy: Regulator APIs for USB power rails
Added regulator APIs to control the USB power rails. Also, added support to turn on/off vbus appropriately. Bug 770041 Reviewed-on: http://git-master/r/20025 (cherry picked from commit 3c5b51ecb7a2135db66c40555048723c40f9ef54) Change-Id: I9d2210d219728247aa4ce04e8c26d16d4dd32731 Reviewed-on: http://git-master/r/20971 Reviewed-by: Rakesh Bodla <rbodla@nvidia.com> Tested-by: Rakesh Bodla <rbodla@nvidia.com> Reviewed-by: Varun Colbert <vcolbert@nvidia.com>
-rw-r--r--arch/arm/mach-tegra/include/mach/usb_phy.h11
-rw-r--r--arch/arm/mach-tegra/usb_phy.c118
2 files changed, 109 insertions, 20 deletions
diff --git a/arch/arm/mach-tegra/include/mach/usb_phy.h b/arch/arm/mach-tegra/include/mach/usb_phy.h
index 30c0f57fee70..d51b485fb96e 100644
--- a/arch/arm/mach-tegra/include/mach/usb_phy.h
+++ b/arch/arm/mach-tegra/include/mach/usb_phy.h
@@ -20,6 +20,7 @@
#include <linux/platform_device.h>
#include <linux/clk.h>
+#include <linux/regulator/consumer.h>
#define USB_PHY_MAX_CONTEXT_REGS 10
@@ -74,6 +75,12 @@ enum tegra_usb_phy_mode {
TEGRA_USB_PHY_MODE_HOST,
};
+struct usb_phy_plat_data {
+ int instance;
+ int vbus_irq;
+ int vbus_gpio;
+};
+
struct tegra_usb_phy {
int instance;
int freq_sel;
@@ -84,6 +91,8 @@ struct tegra_usb_phy {
struct clk *pad_clk;
enum tegra_usb_phy_mode mode;
void *config;
+ struct regulator *reg_vdd;
+ bool regulator_on;
};
struct tegra_usb_phy *tegra_usb_phy_open(int instance, void __iomem *regs,
@@ -116,4 +125,6 @@ int tegra_usb_phy_bus_idle(struct tegra_usb_phy *phy);
bool tegra_usb_phy_is_device_connected(struct tegra_usb_phy *phy);
+void __init tegra_usb_phy_init(struct usb_phy_plat_data *pdata, int size);
+
#endif /*__MACH_USB_PHY_H */
diff --git a/arch/arm/mach-tegra/usb_phy.c b/arch/arm/mach-tegra/usb_phy.c
index 7318d95e7317..a3c23bc5a9aa 100644
--- a/arch/arm/mach-tegra/usb_phy.c
+++ b/arch/arm/mach-tegra/usb_phy.c
@@ -21,6 +21,7 @@
#include <linux/resource.h>
#include <linux/delay.h>
+#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/platform_device.h>
@@ -30,7 +31,7 @@
#include <mach/usb_phy.h>
#include <mach/iomap.h>
#include <mach/pinmux.h>
-#include "gpio-names.h"
+#include "fuse.h"
#define USB_USBCMD 0x140
#define USB_USBCMD_RS (1 << 0)
@@ -286,6 +287,12 @@ static struct tegra_uhsic_config uhsic_default = {
.elastic_overrun_limit = 16,
};
+struct usb_phy_plat_data usb_phy_data[] = {
+ { 0, 0, -1},
+ { 0, 0, -1},
+ { 0, 0, -1},
+};
+
static int utmip_pad_open(struct tegra_usb_phy *phy)
{
phy->pad_clk = clk_get_sys("utmip-pad", NULL);
@@ -428,11 +435,42 @@ static void utmi_phy_clk_enable(struct tegra_usb_phy *phy)
pr_err("%s: timeout waiting for phy to stabilize\n", __func__);
}
+static void vbus_enable(int gpio)
+{
+ int gpio_status;
+
+ if (gpio == -1)
+ return;
+
+ gpio_status = gpio_request(gpio,"VBUS_USB");
+ if (gpio_status < 0) {
+ printk("VBUS_USB request GPIO FAILED\n");
+ WARN_ON(1);
+ return;
+ }
+ tegra_gpio_enable(gpio);
+ gpio_status = gpio_direction_output(gpio, 1);
+ if (gpio_status < 0) {
+ printk("VBUS_USB request GPIO DIRECTION FAILED \n");
+ WARN_ON(1);
+ return;
+ }
+ gpio_set_value(gpio, 1);
+}
+
+static void vbus_disable(int gpio)
+{
+ if (gpio == -1)
+ return;
+
+ gpio_set_value(gpio, 0);
+ gpio_free(gpio);
+}
+
static void utmi_phy_power_on(struct tegra_usb_phy *phy)
{
unsigned long val;
void __iomem *base = phy->regs;
- int gpio_status;
struct tegra_utmip_config *config = phy->config;
val = readl(base + USB_SUSP_CTRL);
@@ -542,21 +580,6 @@ static void utmi_phy_power_on(struct tegra_usb_phy *phy)
val = readl(base + USB_SUSP_CTRL);
val &= ~USB_SUSP_SET;
writel(val, base + USB_SUSP_CTRL);
- if (phy->mode == TEGRA_USB_PHY_MODE_HOST) {
- gpio_status = gpio_request(TEGRA_GPIO_PD0, "VBUS_BUS");
- if (gpio_status < 0) {
- printk(KERN_ERR "VBUS_USB1 request GPIO FAILED\n");
- WARN_ON(1);
- }
- tegra_gpio_enable(TEGRA_GPIO_PD0);
- gpio_status = gpio_direction_output(TEGRA_GPIO_PD0, 1);
- if (gpio_status < 0) {
- printk(KERN_ERR "VBUS_USB1 request GPIO DIRECTION FAILED\n");
- WARN_ON(1);
- }
- gpio_set_value(TEGRA_GPIO_PD0, 1);
- tegra_pinmux_set_tristate(TEGRA_PINGROUP_SLXK, TEGRA_TRI_NORMAL);
- }
}
utmi_phy_clk_enable(phy);
@@ -566,6 +589,9 @@ static void utmi_phy_power_on(struct tegra_usb_phy *phy)
val &= ~USB_PORTSC1_PTS(~0);
writel(val, base + USB_PORTSC1);
}
+ if (phy->mode == TEGRA_USB_PHY_MODE_HOST) {
+ vbus_enable(usb_phy_data[phy->instance].vbus_gpio);
+ }
}
static void utmi_phy_power_off(struct tegra_usb_phy *phy)
@@ -575,9 +601,8 @@ static void utmi_phy_power_off(struct tegra_usb_phy *phy)
utmi_phy_clk_disable(phy);
- if (phy->instance == 0 && phy->mode == TEGRA_USB_PHY_MODE_HOST) {
- gpio_free(TEGRA_GPIO_PD0);
- tegra_pinmux_set_tristate(TEGRA_PINGROUP_SLXK, TEGRA_TRI_TRISTATE);
+ if (phy->mode == TEGRA_USB_PHY_MODE_HOST) {
+ vbus_disable(usb_phy_data[phy->instance].vbus_gpio);
}
if (phy->mode == TEGRA_USB_PHY_MODE_DEVICE) {
@@ -952,6 +977,18 @@ static void uhsic_phy_power_off(struct tegra_usb_phy *phy)
}
+static irqreturn_t usb_phy_vbus_irq_thr(int irq, void *pdata)
+{
+ struct tegra_usb_phy *phy = pdata;
+
+ if (!phy->regulator_on) {
+ regulator_enable(phy->reg_vdd);
+ phy->regulator_on = 1;
+ }
+
+ return IRQ_HANDLED;
+}
+
struct tegra_usb_phy *tegra_usb_phy_open(int instance, void __iomem *regs,
void *config, enum tegra_usb_phy_mode phy_mode)
{
@@ -969,6 +1006,7 @@ struct tegra_usb_phy *tegra_usb_phy_open(int instance, void __iomem *regs,
phy->regs = regs;
phy->config = config;
phy->mode = phy_mode;
+ phy->regulator_on = 0;
if (!phy->config) {
if (instance == 1) {
@@ -1019,6 +1057,22 @@ struct tegra_usb_phy *tegra_usb_phy_open(int instance, void __iomem *regs,
if (err < 0)
goto err1;
}
+ phy->reg_vdd = regulator_get(NULL, "avdd_usb");
+ if (WARN_ON(IS_ERR_OR_NULL(phy->reg_vdd))) {
+ pr_err("couldn't get regulator avdd_usb: %ld \n",
+ PTR_ERR(phy->reg_vdd));
+ err = PTR_ERR(phy->reg_vdd);
+ goto err1;
+ }
+
+ if (instance == 0 && usb_phy_data[0].vbus_irq) {
+ err = request_threaded_irq(usb_phy_data[0].vbus_irq, NULL, usb_phy_vbus_irq_thr, IRQF_SHARED,
+ "usb_phy_vbus", phy);
+ if (err) {
+ pr_err("Failed to register IRQ\n");
+ goto err1;
+ }
+ }
return phy;
@@ -1032,6 +1086,10 @@ err0:
int tegra_usb_phy_power_on(struct tegra_usb_phy *phy)
{
+ if (!phy->regulator_on) {
+ regulator_enable(phy->reg_vdd);
+ phy->regulator_on = 1;
+ }
if (phy->instance == 1) {
struct tegra_ulpi_config *ulpi_config = phy->config;
if (ulpi_config->inf_type == TEGRA_USB_LINK_ULPI)
@@ -1059,6 +1117,11 @@ int tegra_usb_phy_power_off(struct tegra_usb_phy *phy)
} else {
utmi_phy_power_off(phy);
}
+
+ if (phy->regulator_on && (tegra_get_revision() >= TEGRA_REVISION_A03)) {
+ regulator_disable(phy->reg_vdd);
+ phy->regulator_on = 0;
+ }
return 0;
}
@@ -1118,6 +1181,9 @@ int tegra_usb_phy_close(struct tegra_usb_phy *phy)
utmip_pad_close(phy);
clk_disable(phy->pll_u);
clk_put(phy->pll_u);
+ regulator_put(phy->reg_vdd);
+ if (phy->instance == 0 && usb_phy_data[0].vbus_irq)
+ free_irq(usb_phy_data[0].vbus_irq, phy);
kfree(phy);
return 0;
}
@@ -1276,3 +1342,15 @@ bool tegra_usb_phy_is_device_connected(struct tegra_usb_phy *phy)
return true;
}
+void __init tegra_usb_phy_init(struct usb_phy_plat_data *pdata, int size)
+{
+ if (pdata) {
+ int i;
+
+ for (i = 0; i < size; i++, pdata++) {
+ usb_phy_data[pdata->instance].instance = pdata->instance;
+ usb_phy_data[pdata->instance].vbus_irq = pdata->vbus_irq;
+ usb_phy_data[pdata->instance].vbus_gpio = pdata->vbus_gpio;
+ }
+ }
+}