diff options
author | Joshua Cha <joshuac@nvidia.com> | 2011-07-27 11:39:54 +0900 |
---|---|---|
committer | Niket Sirsi <nsirsi@nvidia.com> | 2011-07-27 15:05:50 -0700 |
commit | a5dc52ce49d00bba963544251a1fe858e774780b (patch) | |
tree | e9510f6d33d89c72840647fa8ecedcaf2795c044 /arch | |
parent | eb7639ad28dae74d982b29f6caa7c98ec0baa501 (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.h | 2 | ||||
-rw-r--r-- | arch/arm/mach-tegra/tegra2_clocks.c | 3 | ||||
-rw-r--r-- | arch/arm/mach-tegra/tegra2_dvfs.c | 4 | ||||
-rw-r--r-- | arch/arm/mach-tegra/usb_phy.c | 43 |
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) { |