diff options
Diffstat (limited to 'arch/arm/mach-tegra/pm-t3.c')
-rw-r--r-- | arch/arm/mach-tegra/pm-t3.c | 136 |
1 files changed, 127 insertions, 9 deletions
diff --git a/arch/arm/mach-tegra/pm-t3.c b/arch/arm/mach-tegra/pm-t3.c index a83174224498..939a9b8ad404 100644 --- a/arch/arm/mach-tegra/pm-t3.c +++ b/arch/arm/mach-tegra/pm-t3.c @@ -3,7 +3,7 @@ * * Tegra3 SOC-specific power and cluster management * - * Copyright (c) 2009-2012, NVIDIA Corporation. + * Copyright (c) 2009-2012, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -31,6 +31,7 @@ #include <mach/gpio.h> #include <mach/iomap.h> #include <mach/irqs.h> +#include <mach/io_dpd.h> #include <asm/cpu_pm.h> #include <asm/hardware/gic.h> @@ -101,6 +102,12 @@ #define CPU_CLOCK(cpu) (0x1<<(8+cpu)) #define CPU_RESET(cpu) (0x1111ul<<(cpu)) +#define PLLX_FO_G (1<<28) +#define PLLX_FO_LP (1<<29) + +#define CLK_RST_CONTROLLER_PLLX_MISC_0 \ + (IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0xE4) + static int cluster_switch_prolog_clock(unsigned int flags) { u32 reg; @@ -188,6 +195,20 @@ static int cluster_switch_prolog_clock(unsigned int flags) return 0; } +static inline void enable_pllx_cluster_port(void) +{ + u32 val = readl(CLK_RST_CONTROLLER_PLLX_MISC_0); + val &= (is_lp_cluster()?(~PLLX_FO_G):(~PLLX_FO_LP)); + writel(val, CLK_RST_CONTROLLER_PLLX_MISC_0); +} + +static inline void disable_pllx_cluster_port(void) +{ + u32 val = readl(CLK_RST_CONTROLLER_PLLX_MISC_0); + val |= (is_lp_cluster()?PLLX_FO_G:PLLX_FO_LP); + writel(val, CLK_RST_CONTROLLER_PLLX_MISC_0); +} + void tegra_cluster_switch_prolog(unsigned int flags) { unsigned int target_cluster = flags & TEGRA_POWER_CLUSTER_MASK; @@ -222,6 +243,9 @@ void tegra_cluster_switch_prolog(unsigned int flags) /* Set up the flow controller to switch CPUs. */ reg |= FLOW_CTRL_CPU_CSR_SWITCH_CLUSTER; + + /* Enable target port of PLL_X */ + enable_pllx_cluster_port(); } } @@ -304,6 +328,9 @@ void tegra_cluster_switch_epilog(unsigned int flags) cluster_switch_epilog_gic(); } + /* Disable unused port of PLL_X */ + disable_pllx_cluster_port(); + #if DEBUG_CLUSTER_SWITCH { /* FIXME: clock functions below are taking mutex */ @@ -450,16 +477,22 @@ void tegra_lp0_cpu_mode(bool enter) #define PMC_DPD_SAMPLE 0x20 struct tegra_io_dpd tegra_list_io_dpd[] = { -/* Empty DPD list - sd dpd entries removed */ + /* sd dpd bits in dpd2 register */ + IO_DPD_INFO("sdhci-tegra.0", 1, 1), /* SDMMC1 */ + IO_DPD_INFO("sdhci-tegra.2", 1, 2), /* SDMMC3 */ + IO_DPD_INFO("sdhci-tegra.3", 1, 3), /* SDMMC4 */ }; +/* we want to cleanup bootloader io dpd setting in kernel */ +static void __iomem *pmc = IO_ADDRESS(TEGRA_PMC_BASE); + +#ifdef CONFIG_PM_SLEEP struct tegra_io_dpd *tegra_io_dpd_get(struct device *dev) { int i; const char *name = dev ? dev_name(dev) : NULL; if (name) { - for (i = 0; i < (sizeof(tegra_list_io_dpd) / - sizeof(struct tegra_io_dpd)); i++) { + for (i = 0; i < ARRAY_SIZE(tegra_list_io_dpd); i++) { if (!(strncmp(tegra_list_io_dpd[i].name, name, strlen(name)))) { return &tegra_list_io_dpd[i]; @@ -470,9 +503,7 @@ struct tegra_io_dpd *tegra_io_dpd_get(struct device *dev) ((name) ? name : "NULL")); return NULL; } -EXPORT_SYMBOL(tegra_io_dpd_get); -static void __iomem *pmc = IO_ADDRESS(TEGRA_PMC_BASE); static DEFINE_SPINLOCK(tegra_io_dpd_lock); void tegra_io_dpd_enable(struct tegra_io_dpd *hnd) @@ -481,8 +512,10 @@ void tegra_io_dpd_enable(struct tegra_io_dpd *hnd) unsigned int dpd_status; unsigned int dpd_enable_lsb; - if ((!hnd)) + if ((!hnd)) { + pr_warn("SD IO DPD handle NULL in %s\n", __func__); return; + } spin_lock(&tegra_io_dpd_lock); dpd_enable_lsb = (hnd->io_dpd_reg_index) ? APBDEV_DPD2_ENABLE_LSB : APBDEV_DPD_ENABLE_LSB; @@ -502,7 +535,6 @@ void tegra_io_dpd_enable(struct tegra_io_dpd *hnd) spin_unlock(&tegra_io_dpd_lock); return; } -EXPORT_SYMBOL(tegra_io_dpd_enable); void tegra_io_dpd_disable(struct tegra_io_dpd *hnd) { @@ -510,8 +542,10 @@ void tegra_io_dpd_disable(struct tegra_io_dpd *hnd) unsigned int dpd_status; unsigned int dpd_enable_lsb; - if ((!hnd)) + if ((!hnd)) { + pr_warn("SD IO DPD handle NULL in %s\n", __func__); return; + } spin_lock(&tegra_io_dpd_lock); dpd_enable_lsb = (hnd->io_dpd_reg_index) ? APBDEV_DPD2_ENABLE_LSB : APBDEV_DPD_ENABLE_LSB; @@ -526,4 +560,88 @@ void tegra_io_dpd_disable(struct tegra_io_dpd *hnd) spin_unlock(&tegra_io_dpd_lock); return; } + +static void tegra_io_dpd_delayed_disable(struct work_struct *work) +{ + struct tegra_io_dpd *hnd = container_of( + to_delayed_work(work), struct tegra_io_dpd, delay_dpd); + tegra_io_dpd_disable(hnd); + hnd->need_delay_dpd = 0; +} + +int tegra_io_dpd_init(void) +{ + int i; + for (i = 0; + i < (sizeof(tegra_list_io_dpd) / sizeof(struct tegra_io_dpd)); + i++) { + INIT_DELAYED_WORK(&(tegra_list_io_dpd[i].delay_dpd), + tegra_io_dpd_delayed_disable); + mutex_init(&(tegra_list_io_dpd[i].delay_lock)); + tegra_list_io_dpd[i].need_delay_dpd = 0; + } + return 0; +} + +#else + +int tegra_io_dpd_init(void) +{ + return 0; +} + +void tegra_io_dpd_enable(struct tegra_io_dpd *hnd) +{ +} + +void tegra_io_dpd_disable(struct tegra_io_dpd *hnd) +{ +} + +struct tegra_io_dpd *tegra_io_dpd_get(struct device *dev) +{ + return NULL; +} + +#endif + +EXPORT_SYMBOL(tegra_io_dpd_get); +EXPORT_SYMBOL(tegra_io_dpd_enable); EXPORT_SYMBOL(tegra_io_dpd_disable); +EXPORT_SYMBOL(tegra_io_dpd_init); + +struct io_dpd_reg_info { + u32 req_reg_off; + u8 dpd_code_lsb; +}; + +static struct io_dpd_reg_info t3_io_dpd_req_regs[] = { + {0x1b8, 30}, + {0x1c0, 5}, +}; + +/* io dpd off request code */ +#define IO_DPD_CODE_OFF 1 + +/* cleans io dpd settings from bootloader during kernel init */ +void tegra_bl_io_dpd_cleanup() +{ + int i; + unsigned int dpd_mask; + unsigned int dpd_status; + + pr_info("Clear bootloader IO dpd settings\n"); + /* clear all dpd requests from bootloader */ + for (i = 0; i < ARRAY_SIZE(t3_io_dpd_req_regs); i++) { + dpd_mask = ((1 << t3_io_dpd_req_regs[i].dpd_code_lsb) - 1); + dpd_mask |= (IO_DPD_CODE_OFF << + t3_io_dpd_req_regs[i].dpd_code_lsb); + writel(dpd_mask, pmc + t3_io_dpd_req_regs[i].req_reg_off); + /* dpd status register is next to req reg in tegra3 */ + dpd_status = readl(pmc + + (t3_io_dpd_req_regs[i].req_reg_off + 4)); + } + return; +} +EXPORT_SYMBOL(tegra_bl_io_dpd_cleanup); + |