summaryrefslogtreecommitdiff
path: root/arch/arm/mach-tegra/pm-t3.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/mach-tegra/pm-t3.c')
-rw-r--r--arch/arm/mach-tegra/pm-t3.c136
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);
+