diff options
author | Rakesh Bodla <rbodla@nvidia.com> | 2011-02-28 21:58:49 +0530 |
---|---|---|
committer | Varun Colbert <vcolbert@nvidia.com> | 2011-02-28 13:59:34 -0800 |
commit | 3e5c594a08f4bee917d9617e88fd0c0cf64d4e40 (patch) | |
tree | f709d5ebf7fb21c6062b486ed8bf402cfc15daf8 | |
parent | b2f6823a58c005e68471abb1a48f3079bb424588 (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.h | 11 | ||||
-rw-r--r-- | arch/arm/mach-tegra/usb_phy.c | 118 |
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; + } + } +} |