summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Frid <afrid@nvidia.com>2010-07-13 23:34:15 -0700
committerGary King <gking@nvidia.com>2010-07-31 13:33:07 -0700
commit4e481ff43ff51d1c00361231fc64f401da000f8a (patch)
treef4941740e87106f757c5fe4b8b36d99788e9b0d7
parentfcecf3fdc2f7a68f9ee7dac84cf0db03907da53f (diff)
[ARM/tegra] power: Added run-time LP0/LP1 selection.
Added sysfs node /sys/power/nvrm/core_lock to dynamically select lowest tegra power state in conjunction with static ODM query: - if ODM query specifies DeepSleep as lowest power state and core_lock is cleared, tegra platform enters DeepSleep (LP0) when system is suspended - if ODM query specifies DeepSleep as lowest power state and core_lock is set, tegra platform enters Suspend (LP1) when system is suspended - if ODM query specifies any state other than DeepSleep as lowest power state, core_lock is ignored (tegra platform follows ODM specification in suspend) Addresses bug 697619, facilitates LP0/LP1 testing. Change-Id: Id2d046ba202cf7630cff5e9b524995ec5e867eaa Reviewed-on: http://git-master/r/4127 Reviewed-by: Aleksandr Frid <afrid@nvidia.com> Tested-by: Aleksandr Frid <afrid@nvidia.com> Reviewed-by: Gary King <gking@nvidia.com>
-rw-r--r--arch/arm/mach-tegra/include/nvrm_power_private.h13
-rw-r--r--arch/arm/mach-tegra/nvddk/nvddk_usbphy.c10
-rw-r--r--arch/arm/mach-tegra/nvrm/core/common/nvrm_power.c6
-rw-r--r--arch/arm/mach-tegra/nvrm_user.c28
-rw-r--r--arch/arm/mach-tegra/suspend.c58
5 files changed, 82 insertions, 33 deletions
diff --git a/arch/arm/mach-tegra/include/nvrm_power_private.h b/arch/arm/mach-tegra/include/nvrm_power_private.h
index a2663df8df6c..9ca3cee24e92 100644
--- a/arch/arm/mach-tegra/include/nvrm_power_private.h
+++ b/arch/arm/mach-tegra/include/nvrm_power_private.h
@@ -151,6 +151,19 @@ typedef enum
extern NvRmLp2Policy g_Lp2Policy;
/**
+ * Gets lowest power state, taking into the account run-time core lock.
+ */
+extern bool core_lock_on;
+static inline NvOdmSocPowerState NvRmPowerLowestStateGet(void)
+{
+ NvOdmSocPowerState state =
+ NvOdmQueryLowestSocPowerState()->LowestPowerState;
+ if ((state == NvOdmSocPowerState_DeepSleep) && core_lock_on)
+ state = NvOdmSocPowerState_Suspend;
+ return state;
+}
+
+/**
* NVRM PM function called within OS shim high priority thread
*/
NvRmPmRequest NvRmPrivPmThread(void);
diff --git a/arch/arm/mach-tegra/nvddk/nvddk_usbphy.c b/arch/arm/mach-tegra/nvddk/nvddk_usbphy.c
index 55dc3ab0e1b7..aa80b2fa0eec 100644
--- a/arch/arm/mach-tegra/nvddk/nvddk_usbphy.c
+++ b/arch/arm/mach-tegra/nvddk/nvddk_usbphy.c
@@ -40,7 +40,7 @@
*/
#include "nvrm_pinmux.h"
-#include "nvrm_power.h"
+#include "nvrm_power_private.h"
#include "nvrm_pmu.h"
#include "nvrm_hardware_access.h"
#include "nvddk_usbphy_priv.h"
@@ -634,7 +634,7 @@ NvDdkUsbPhyPowerUp(
NvBool IsDpd)
{
NvError e = NvSuccess;
- NvOdmSocPowerStateInfo *pLowPowerState = NvOdmQueryLowestSocPowerState();
+ NvOdmSocPowerState state = NvRmPowerLowestStateGet();
NV_ASSERT(hUsbPhy);
@@ -672,7 +672,7 @@ NvDdkUsbPhyPowerUp(
/* Allow restoring register context for the USB host if it is a ULPI
interface or if the lowest power state is LP1 */
if ((hUsbPhy->pProperty->UsbMode == NvOdmUsbModeType_Host) &&
- ((pLowPowerState->LowestPowerState != NvOdmSocPowerState_DeepSleep) ||
+ ((state != NvOdmSocPowerState_DeepSleep) ||
(hUsbPhy->pProperty->UsbInterfaceType == NvOdmUsbInterfaceType_UlpiExternalPhy)))
{
hUsbPhy->RestoreContext(hUsbPhy);
@@ -707,7 +707,7 @@ NvDdkUsbPhyPowerDown(
NvError e = NvSuccess;
NvDdkUsbPhyIoctl_VBusStatusOutputArgs VBusStatus;
NvU32 TimeOut = USB_PHY_HW_TIMEOUT_US;
- NvOdmSocPowerStateInfo *pLowPowerState = NvOdmQueryLowestSocPowerState();
+ NvOdmSocPowerState state = NvRmPowerLowestStateGet();
NV_ASSERT(hUsbPhy);
@@ -721,7 +721,7 @@ NvDdkUsbPhyPowerDown(
/* Allow saving register context for the USB host if it is a ULPI
interface or if the lowest power state is LP1 */
if ((hUsbPhy->pProperty->UsbMode == NvOdmUsbModeType_Host) &&
- ((pLowPowerState->LowestPowerState != NvOdmSocPowerState_DeepSleep) ||
+ ((state != NvOdmSocPowerState_DeepSleep) ||
(hUsbPhy->pProperty->UsbInterfaceType == NvOdmUsbInterfaceType_UlpiExternalPhy)))
{
hUsbPhy->SaveContext(hUsbPhy);
diff --git a/arch/arm/mach-tegra/nvrm/core/common/nvrm_power.c b/arch/arm/mach-tegra/nvrm/core/common/nvrm_power.c
index cb6f9745a00a..4845def3123e 100644
--- a/arch/arm/mach-tegra/nvrm/core/common/nvrm_power.c
+++ b/arch/arm/mach-tegra/nvrm/core/common/nvrm_power.c
@@ -1515,8 +1515,7 @@ NvRmPowerActivityHint (
NvError
NvRmKernelPowerSuspend( NvRmDeviceHandle hRmDeviceHandle )
{
- NvOdmSocPowerState state =
- NvOdmQueryLowestSocPowerState()->LowestPowerState;
+ NvOdmSocPowerState state = NvRmPowerLowestStateGet();
if (state == NvOdmSocPowerState_Suspend)
NvRmPrivPowerGroupSuspend(hRmDeviceHandle);
@@ -1565,8 +1564,7 @@ NvRmKernelPowerSuspend( NvRmDeviceHandle hRmDeviceHandle )
NvError
NvRmKernelPowerResume( NvRmDeviceHandle hRmDeviceHandle )
{
- NvOdmSocPowerState state =
- NvOdmQueryLowestSocPowerState()->LowestPowerState;
+ NvOdmSocPowerState state = NvRmPowerLowestStateGet();
NvOsMutexLock(s_hPowerClientMutex);
ReportRmPowerState(hRmDeviceHandle);
diff --git a/arch/arm/mach-tegra/nvrm_user.c b/arch/arm/mach-tegra/nvrm_user.c
index f5ab25bd5d3d..93603aa23bc3 100644
--- a/arch/arm/mach-tegra/nvrm_user.c
+++ b/arch/arm/mach-tegra/nvrm_user.c
@@ -616,6 +616,33 @@ nvrm_lp2policy_store(struct kobject *kobj, struct kobj_attribute *attr,
static struct kobj_attribute nvrm_lp2policy_attribute =
__ATTR(lp2policy, 0644, nvrm_lp2policy_show, nvrm_lp2policy_store);
+/*
+ * NVRM lowest power state run-time selection
+ */
+static ssize_t
+nvrm_core_lock_show(struct kobject *kobj, struct kobj_attribute *attr,
+ char *buf)
+{
+ return sprintf(buf, "%u\n", core_lock_on);
+}
+
+static ssize_t
+nvrm_core_lock_store(struct kobject *kobj, struct kobj_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned int n, lock;
+
+ n = sscanf(buf, "%u", &lock);
+ if (n != 1)
+ return -1;
+
+ core_lock_on = (bool)lock;
+ return count;
+}
+
+static struct kobj_attribute nvrm_core_lock_attribute =
+ __ATTR(core_lock, 0666, nvrm_core_lock_show, nvrm_core_lock_store);
+
#endif
static int __init nvrm_init(void)
@@ -635,6 +662,7 @@ static int __init nvrm_init(void)
// Create /sys/power/nvrm/notifier.
nvrm_kobj = kobject_create_and_add("nvrm", power_kobj);
+ sysfs_create_file(nvrm_kobj, &nvrm_core_lock_attribute.attr);
sysfs_create_file(nvrm_kobj, &nvrm_lp2policy_attribute.attr);
sysfs_create_file(nvrm_kobj, &nvrm_notifier_attribute.attr);
sys_nvrm_notifier = NULL;
diff --git a/arch/arm/mach-tegra/suspend.c b/arch/arm/mach-tegra/suspend.c
index b0d84eee10ee..b2b461d8b0a6 100644
--- a/arch/arm/mach-tegra/suspend.c
+++ b/arch/arm/mach-tegra/suspend.c
@@ -66,6 +66,7 @@ struct suspend_context
};
volatile struct suspend_context tegra_sctx;
+bool core_lock_on = false;
#ifdef CONFIG_HOTPLUG_CPU
extern void tegra_board_nvodm_suspend(void);
@@ -95,6 +96,7 @@ static void __iomem *tmrus = IO_ADDRESS(TEGRA_TMRUS_BASE);
#define PMC_COREPWROFF_TIMER PMC_WAKE_DELAY
#define PMC_SCRATCH38 0x134
#define PMC_SCRATCH39 0x138
+#define PMC_SCRATCH41 0x140
#define CLK_RESET_CCLK_BURST 0x20
#define CLK_RESET_CCLK_DIVIDER 0x24
@@ -341,24 +343,33 @@ static void pmc_32kwritel(u32 val, unsigned long offs)
udelay(130);
}
-static void tegra_setup_warmboot(void)
+static void tegra_setup_warmboot(bool lp0_ok)
{
- u32 scratch0;
-
- scratch0 = readl(pmc + PMC_SCRATCH0);
- /* lp0 restore is broken in the ap20 a03 boot rom, so fake the
- * bootrom into performing a regular boot, but pass a flag to the
- * bootloader to bypass the kernel reload and jump to the lp0
- * restore sequence */
- if (tegra_is_ap20_a03())
- scratch0 |= (1<<5);
- else
- scratch0 |= 1;
+ if (lp0_ok) {
+ u32 scratch0;
+
+ scratch0 = readl(pmc + PMC_SCRATCH0);
+ /* lp0 restore is broken in the ap20 a03 boot rom, so fake the
+ * bootrom into performing a regular boot, but pass a flag to the
+ * bootloader to bypass the kernel reload and jump to the lp0
+ * restore sequence */
+ if (tegra_is_ap20_a03())
+ scratch0 |= (1<<5);
+ else
+ scratch0 |= 1;
- pmc_32kwritel(scratch0, PMC_SCRATCH0);
+ pmc_32kwritel(scratch0, PMC_SCRATCH0);
- /* Write the AVP warmboot entry address in SCRATCH1 */
- pmc_32kwritel(s_AvpWarmbootEntry, PMC_SCRATCH1);
+ /* Write the AVP warmboot entry address in SCRATCH1 */
+ pmc_32kwritel(s_AvpWarmbootEntry, PMC_SCRATCH1);
+
+ /* Write the CPU LP0 reset vector address in SCRATCH41 */
+ writel(virt_to_phys(tegra_lp2_startup), pmc + PMC_SCRATCH41);
+ } else {
+ /* Setup LP1 start addresses */
+ writel(TEGRA_IRAM_CODE_AREA, evp_reset);
+ writel(virt_to_phys(tegra_lp2_startup), pmc + PMC_SCRATCH1);
+ }
}
static void tegra_setup_wakepads(bool lp0_ok)
@@ -434,7 +445,6 @@ static void tegra_suspend_dram(bool lp0_ok)
mode |= ((reg >> TEGRA_POWER_PMC_SHIFT) & TEGRA_POWER_PMC_MASK);
if (!lp0_ok) {
- writel(TEGRA_IRAM_CODE_AREA, evp_reset);
NvRmPrivPowerSetState(s_hRmGlobal, NvRmPowerState_LP1);
mode |= TEGRA_POWER_CPU_PWRREQ_OE;
@@ -445,7 +455,7 @@ static void tegra_suspend_dram(bool lp0_ok)
mode &= ~TEGRA_POWER_EFFECT_LP0;
} else {
NvRmPrivPowerSetState(s_hRmGlobal, NvRmPowerState_LP0);
- tegra_setup_warmboot();
+
mode |= TEGRA_POWER_CPU_PWRREQ_OE;
mode |= TEGRA_POWER_PWRREQ_OE;
mode |= TEGRA_POWER_EFFECT_LP0;
@@ -462,6 +472,7 @@ static void tegra_suspend_dram(bool lp0_ok)
}
}
+ tegra_setup_warmboot(lp0_ok);
tegra_setup_wakepads(lp0_ok);
suspend_cpu_complex();
flush_cache_all();
@@ -497,8 +508,7 @@ static void tegra_suspend_dram(bool lp0_ok)
static int tegra_suspend_prepare(void)
{
#ifdef CONFIG_TEGRA_NVRM
- NvOdmSocPowerState state =
- NvOdmQueryLowestSocPowerState()->LowestPowerState;
+ NvOdmSocPowerState state = NvRmPowerLowestStateGet();
NvRmPrivDfsSuspend(state);
NvRmPrivPmuLPxStateConfig(s_hRmGlobal, state, NV_TRUE);
@@ -509,8 +519,7 @@ static int tegra_suspend_prepare(void)
static void tegra_suspend_finish(void)
{
#ifdef CONFIG_TEGRA_NVRM
- NvOdmSocPowerState state =
- NvOdmQueryLowestSocPowerState()->LowestPowerState;
+ NvOdmSocPowerState state = NvRmPowerLowestStateGet();
NvRmPrivPmuLPxStateConfig(s_hRmGlobal, state, NV_FALSE);
#endif
@@ -665,10 +674,11 @@ static int tegra_suspend_enter(suspend_state_t state)
unsigned long flags;
u32 mc_data[2];
int irq;
+ bool lp0_ok = (pdata->core_off && (!core_lock_on));
local_irq_save(flags);
- if (pdata->core_off) {
+ if (lp0_ok) {
tegra_irq_suspend();
tegra_dma_suspend();
tegra_pinmux_suspend();
@@ -691,7 +701,7 @@ static int tegra_suspend_enter(suspend_state_t state)
NvRmPrivPowerSetState(s_hRmGlobal, NvRmPowerState_LP1);
tegra_suspend_lp2(0);
} else
- tegra_suspend_dram(pdata->core_off);
+ tegra_suspend_dram(lp0_ok);
for_each_irq_desc(irq, desc) {
if ((desc->status & IRQ_WAKEUP) &&
@@ -703,7 +713,7 @@ static int tegra_suspend_enter(suspend_state_t state)
/* Clear DPD sample */
writel(0x0, pmc + PMC_DPD_SAMPLE);
- if (pdata->core_off) {
+ if (lp0_ok) {
writel(mc_data[0], mc + MC_SECURITY_START);
writel(mc_data[1], mc + MC_SECURITY_SIZE);