-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
davinci: add power management support
This patch adds core power management (suspend-to-RAM) support for DaVinci SoCs. The code depends on the the "deepsleep" feature to suspend the SoC and saves power by gating the input clock. The wakeup can be based on an external event as supported by the SoC. Assembly code (in sleep.S) is added to aid gating DDR2 clocks. Code doing this work should not be accessing DDR2. The assembly code is relocated to SRAM by the code in pm.c The support has been validated on DA850/OMAP-L138 only though the code is (hopefully) generic enough that other SoCs supporting deepsleep feature simply requires SoC specific code to start using this driver. Note that all the device drivers don't support suspend/resume still and are being worked on. Signed-off-by: Sekhar Nori <nsekhar@ti.com> Signed-off-by: Kevin Hilman <khilman@deeprootsystems.com>
- Loading branch information
Sekhar Nori
authored and
Kevin Hilman
committed
Feb 4, 2010
1 parent
7307d09
commit efc1bb8
Showing
5 changed files
with
438 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
/* | ||
* TI DaVinci platform support for power management. | ||
* | ||
* Copyright (C) 2009 Texas Instruments, Inc. http://www.ti.com/ | ||
* | ||
* 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 the Free Software Foundation version 2. | ||
* | ||
* This program is distributed "as is" WITHOUT ANY WARRANTY of any | ||
* kind, whether express or implied; without even the implied warranty | ||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU General Public License for more details. | ||
*/ | ||
#ifndef _MACH_DAVINCI_PM_H | ||
#define _MACH_DAVINCI_PM_H | ||
|
||
/* | ||
* Caution: Assembly code in sleep.S makes assumtion on the order | ||
* of the members of this structure. | ||
*/ | ||
struct davinci_pm_config { | ||
void __iomem *ddr2_ctlr_base; | ||
void __iomem *ddrpsc_reg_base; | ||
int ddrpsc_num; | ||
void __iomem *ddrpll_reg_base; | ||
void __iomem *deepsleep_reg; | ||
void __iomem *cpupll_reg_base; | ||
/* | ||
* Note on SLEEPCOUNT: | ||
* The SLEEPCOUNT feature is mainly intended for cases in which | ||
* the internal oscillator is used. The internal oscillator is | ||
* fully disabled in deep sleep mode. When you exist deep sleep | ||
* mode, the oscillator will be turned on and will generate very | ||
* small oscillations which will not be detected by the deep sleep | ||
* counter. Eventually those oscillations will grow to an amplitude | ||
* large enough to start incrementing the deep sleep counter. | ||
* In this case recommendation from hardware engineers is that the | ||
* SLEEPCOUNT be set to 4096. This means that 4096 valid clock cycles | ||
* must be detected before the clock is passed to the rest of the | ||
* system. | ||
* In the case that the internal oscillator is not used and the | ||
* clock is generated externally, the SLEEPCOUNT value can be very | ||
* small since the clock input is assumed to be stable before SoC | ||
* is taken out of deepsleep mode. A value of 128 would be more than | ||
* adequate. | ||
*/ | ||
int sleepcount; | ||
}; | ||
|
||
extern unsigned int davinci_cpu_suspend_sz; | ||
extern void davinci_cpu_suspend(struct davinci_pm_config *); | ||
|
||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,158 @@ | ||
/* | ||
* DaVinci Power Management Routines | ||
* | ||
* Copyright (C) 2009 Texas Instruments, Inc. http://www.ti.com/ | ||
* | ||
* This program is free software; you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License version 2 as | ||
* published by the Free Software Foundation. | ||
*/ | ||
|
||
#include <linux/pm.h> | ||
#include <linux/suspend.h> | ||
#include <linux/module.h> | ||
#include <linux/platform_device.h> | ||
#include <linux/clk.h> | ||
#include <linux/spinlock.h> | ||
|
||
#include <asm/cacheflush.h> | ||
#include <asm/delay.h> | ||
|
||
#include <mach/da8xx.h> | ||
#include <mach/sram.h> | ||
#include <mach/pm.h> | ||
|
||
#include "clock.h" | ||
|
||
#define DEEPSLEEP_SLEEPCOUNT_MASK 0xFFFF | ||
|
||
static void (*davinci_sram_suspend) (struct davinci_pm_config *); | ||
static struct davinci_pm_config *pdata; | ||
|
||
static void davinci_sram_push(void *dest, void *src, unsigned int size) | ||
{ | ||
memcpy(dest, src, size); | ||
flush_icache_range((unsigned long)dest, (unsigned long)(dest + size)); | ||
} | ||
|
||
static void davinci_pm_suspend(void) | ||
{ | ||
unsigned val; | ||
|
||
if (pdata->cpupll_reg_base != pdata->ddrpll_reg_base) { | ||
|
||
/* Switch CPU PLL to bypass mode */ | ||
val = __raw_readl(pdata->cpupll_reg_base + PLLCTL); | ||
val &= ~(PLLCTL_PLLENSRC | PLLCTL_PLLEN); | ||
__raw_writel(val, pdata->cpupll_reg_base + PLLCTL); | ||
|
||
udelay(PLL_BYPASS_TIME); | ||
|
||
/* Powerdown CPU PLL */ | ||
val = __raw_readl(pdata->cpupll_reg_base + PLLCTL); | ||
val |= PLLCTL_PLLPWRDN; | ||
__raw_writel(val, pdata->cpupll_reg_base + PLLCTL); | ||
} | ||
|
||
/* Configure sleep count in deep sleep register */ | ||
val = __raw_readl(pdata->deepsleep_reg); | ||
val &= ~DEEPSLEEP_SLEEPCOUNT_MASK, | ||
val |= pdata->sleepcount; | ||
__raw_writel(val, pdata->deepsleep_reg); | ||
|
||
/* System goes to sleep in this call */ | ||
davinci_sram_suspend(pdata); | ||
|
||
if (pdata->cpupll_reg_base != pdata->ddrpll_reg_base) { | ||
|
||
/* put CPU PLL in reset */ | ||
val = __raw_readl(pdata->cpupll_reg_base + PLLCTL); | ||
val &= ~PLLCTL_PLLRST; | ||
__raw_writel(val, pdata->cpupll_reg_base + PLLCTL); | ||
|
||
/* put CPU PLL in power down */ | ||
val = __raw_readl(pdata->cpupll_reg_base + PLLCTL); | ||
val &= ~PLLCTL_PLLPWRDN; | ||
__raw_writel(val, pdata->cpupll_reg_base + PLLCTL); | ||
|
||
/* wait for CPU PLL reset */ | ||
udelay(PLL_RESET_TIME); | ||
|
||
/* bring CPU PLL out of reset */ | ||
val = __raw_readl(pdata->cpupll_reg_base + PLLCTL); | ||
val |= PLLCTL_PLLRST; | ||
__raw_writel(val, pdata->cpupll_reg_base + PLLCTL); | ||
|
||
/* Wait for CPU PLL to lock */ | ||
udelay(PLL_LOCK_TIME); | ||
|
||
/* Remove CPU PLL from bypass mode */ | ||
val = __raw_readl(pdata->cpupll_reg_base + PLLCTL); | ||
val &= ~PLLCTL_PLLENSRC; | ||
val |= PLLCTL_PLLEN; | ||
__raw_writel(val, pdata->cpupll_reg_base + PLLCTL); | ||
} | ||
} | ||
|
||
static int davinci_pm_enter(suspend_state_t state) | ||
{ | ||
int ret = 0; | ||
|
||
switch (state) { | ||
case PM_SUSPEND_STANDBY: | ||
case PM_SUSPEND_MEM: | ||
davinci_pm_suspend(); | ||
break; | ||
default: | ||
ret = -EINVAL; | ||
} | ||
|
||
return ret; | ||
} | ||
|
||
static struct platform_suspend_ops davinci_pm_ops = { | ||
.enter = davinci_pm_enter, | ||
.valid = suspend_valid_only_mem, | ||
}; | ||
|
||
static int __init davinci_pm_probe(struct platform_device *pdev) | ||
{ | ||
pdata = pdev->dev.platform_data; | ||
if (!pdata) { | ||
dev_err(&pdev->dev, "cannot get platform data\n"); | ||
return -ENOENT; | ||
} | ||
|
||
davinci_sram_suspend = sram_alloc(davinci_cpu_suspend_sz, NULL); | ||
if (!davinci_sram_suspend) { | ||
dev_err(&pdev->dev, "cannot allocate SRAM memory\n"); | ||
return -ENOMEM; | ||
} | ||
|
||
davinci_sram_push(davinci_sram_suspend, davinci_cpu_suspend, | ||
davinci_cpu_suspend_sz); | ||
|
||
suspend_set_ops(&davinci_pm_ops); | ||
|
||
return 0; | ||
} | ||
|
||
static int __exit davinci_pm_remove(struct platform_device *pdev) | ||
{ | ||
sram_free(davinci_sram_suspend, davinci_cpu_suspend_sz); | ||
return 0; | ||
} | ||
|
||
static struct platform_driver davinci_pm_driver = { | ||
.driver = { | ||
.name = "pm-davinci", | ||
.owner = THIS_MODULE, | ||
}, | ||
.remove = __exit_p(davinci_pm_remove), | ||
}; | ||
|
||
static int __init davinci_pm_init(void) | ||
{ | ||
return platform_driver_probe(&davinci_pm_driver, davinci_pm_probe); | ||
} | ||
late_initcall(davinci_pm_init); |
Oops, something went wrong.