summaryrefslogtreecommitdiff
path: root/arch
diff options
context:
space:
mode:
authorJoshua Cha <joshuac@nvidia.com>2011-07-27 11:39:54 +0900
committerNiket Sirsi <nsirsi@nvidia.com>2011-07-27 15:05:50 -0700
commita5dc52ce49d00bba963544251a1fe858e774780b (patch)
treee9510f6d33d89c72840647fa8ecedcaf2795c044 /arch
parenteb7639ad28dae74d982b29f6caa7c98ec0baa501 (diff)
usb: host: ehci-tegra: Power saving on USB suspend
In current implementation, the HSIC port cannot have PHY power off without re-enumeration when it is suspended. This patch will do following upon USB port suspend: * disable the USB PHY clock * disable the shared EMC clock * keep the USB core but running it at 0.95v VDD With this patch, VDD can be reduced to when the HSIC port is suspended. There is no re-enumeration when HSIC port is resumed, also it will not affect the normal LP0 suspend/resume. BUG 817725 BUG 796594 (cherry picked from http://git-master/r/30224) Change-Id: Ie0553335ae50bca1a91e94d46d14bb9127874ae4 Reviewed-on: http://git-master/r/37226 Reviewed-by: Joshua Cha <joshuac@nvidia.com> Tested-by: Joshua Cha <joshuac@nvidia.com> Reviewed-by: Bharat Nihalani <bnihalani@nvidia.com>
Diffstat (limited to 'arch')
-rw-r--r--arch/arm/mach-tegra/include/mach/usb_phy.h2
-rw-r--r--arch/arm/mach-tegra/tegra2_clocks.c3
-rw-r--r--arch/arm/mach-tegra/tegra2_dvfs.c4
-rw-r--r--arch/arm/mach-tegra/usb_phy.c43
4 files changed, 51 insertions, 1 deletions
diff --git a/arch/arm/mach-tegra/include/mach/usb_phy.h b/arch/arm/mach-tegra/include/mach/usb_phy.h
index 609134c4ff82..f3520a91dc59 100644
--- a/arch/arm/mach-tegra/include/mach/usb_phy.h
+++ b/arch/arm/mach-tegra/include/mach/usb_phy.h
@@ -116,6 +116,8 @@ void tegra_ehci_phy_restore_start(struct tegra_usb_phy *phy,
void tegra_ehci_phy_restore_end(struct tegra_usb_phy *phy);
+int tegra_usb_set_phy_clock(struct tegra_usb_phy *phy, char clock_on);
+
void tegra_usb_phy_close(struct tegra_usb_phy *phy);
int tegra_usb_phy_bus_connect(struct tegra_usb_phy *phy);
diff --git a/arch/arm/mach-tegra/tegra2_clocks.c b/arch/arm/mach-tegra/tegra2_clocks.c
index a48baf9c8d35..a86010bca6d8 100644
--- a/arch/arm/mach-tegra/tegra2_clocks.c
+++ b/arch/arm/mach-tegra/tegra2_clocks.c
@@ -2174,7 +2174,8 @@ struct clk tegra_list_periph_clks[] = {
PERIPH_CLK("disp1", "tegradc.0", NULL, 27, 0x138, 0x31E, 600000000, mux_pllp_plld_pllc_clkm, MUX | DIV_U71), /* scales with voltage and process_id */
PERIPH_CLK("disp2", "tegradc.1", NULL, 26, 0x13c, 0x31E, 600000000, mux_pllp_plld_pllc_clkm, MUX | DIV_U71), /* scales with voltage and process_id */
PERIPH_CLK("usbd", "fsl-tegra-udc", NULL, 22, 0, 0x31E, 480000000, mux_clk_m, 0), /* requires min voltage */
- PERIPH_CLK("usb2", "tegra-ehci.1", NULL, 58, 0, 0x31E, 480000000, mux_clk_m, 0), /* requires min voltage */
+ PERIPH_CLK("usb2", "tegra-ehci.1", "usb2", 58, 0, 0x31E, 480000000, mux_clk_m, 0), /* requires min voltage */
+ PERIPH_CLK("usb2min", "tegra-ehci.1", "usb2min", 95, 0, 0x31E, 60000000, mux_clk_m, 0), /* requires min voltage */
PERIPH_CLK("usb3", "tegra-ehci.2", NULL, 59, 0, 0x31E, 480000000, mux_clk_m, 0), /* requires min voltage */
PERIPH_CLK("dsi", "dsi", NULL, 48, 0, 0x31E, 500000000, mux_plld_out0, 0), /* scales with voltage */
PERIPH_CLK("csi", "tegra_camera", "csi", 52, 0, 0x31E, 72000000, mux_pllp_out3, 0),
diff --git a/arch/arm/mach-tegra/tegra2_dvfs.c b/arch/arm/mach-tegra/tegra2_dvfs.c
index b0c0a0c55372..4a7242e38165 100644
--- a/arch/arm/mach-tegra/tegra2_dvfs.c
+++ b/arch/arm/mach-tegra/tegra2_dvfs.c
@@ -191,6 +191,10 @@ static struct dvfs dvfs_init[] = {
CORE_DVFS("mipi", -1, 1, KHZ, 0, 40000, 40000, 40000, 40000, 60000, 60000),
CORE_DVFS("usbd", -1, 1, KHZ, 0, 0, 480000, 480000, 480000, 480000, 480000),
CORE_DVFS("usb2", -1, 1, KHZ, 0, 0, 480000, 480000, 480000, 480000, 480000),
+ /*
+ * Set VDD core to 0.95v when HSIC port is idle
+ */
+ CORE_DVFS("usb2min",-1, 1, KHZ, 60000, 60000, 60000, 60000, 60000, 60000, 60000),
CORE_DVFS("usb3", -1, 1, KHZ, 0, 0, 480000, 480000, 480000, 480000, 480000),
CORE_DVFS("pcie", -1, 1, KHZ, 0, 0, 0, 250000, 250000, 250000, 250000),
CORE_DVFS("dsi", -1, 1, KHZ, 100000, 100000, 100000, 500000, 500000, 500000, 500000),
diff --git a/arch/arm/mach-tegra/usb_phy.c b/arch/arm/mach-tegra/usb_phy.c
index 0dc14abc3fa4..a9d3dac8aea1 100644
--- a/arch/arm/mach-tegra/usb_phy.c
+++ b/arch/arm/mach-tegra/usb_phy.c
@@ -1225,6 +1225,49 @@ err0:
return ERR_PTR(err);
}
+static void pulse_writel(int bit, void __iomem *addr)
+{
+ int val;
+
+ val = readl(addr);
+ writel(val | bit, addr);
+ udelay(10);
+ writel(val & (~bit), addr);
+}
+
+int tegra_usb_set_phy_clock(struct tegra_usb_phy *phy, char clock_on)
+{
+ int ret = 0;
+ int val;
+ void __iomem *base = phy->regs;
+
+ BUG_ON(phy == NULL);
+
+ if (clock_on) {
+ pulse_writel(USB_SUSP_CLR, base + USB_SUSP_CTRL);
+ } else {
+ switch(phy->instance) {
+ case 0:
+ pulse_writel(USB_SUSP_SET, base + USB_SUSP_CTRL);
+ break;
+ case 1:
+ case 2:
+ val = readl(base + USB_PORTSC1);
+ writel(val | USB_PORTSC1_PHCD, base + USB_PORTSC1);
+ break;
+ default:
+ pr_err("unknow USB instance: %d\n", phy->instance);
+ }
+ }
+
+ ret = utmi_wait_register(base + USB_SUSP_CTRL, USB_PHY_CLK_VALID,
+ clock_on ? USB_PHY_CLK_VALID : 0);
+ if (ret)
+ pr_err("failed turn %s EHCI port PHY clock\n",
+ clock_on ? "on" : "off");
+ return ret;
+}
+
int tegra_usb_phy_power_on(struct tegra_usb_phy *phy, bool is_dpd)
{
if (!phy->regulator_on) {