rk29: add pm support

This commit is contained in:
黄涛
2011-01-25 20:34:43 +08:00
parent 782dac8be2
commit 8febcb2f87
5 changed files with 416 additions and 52 deletions

View File

@@ -1,4 +1,5 @@
obj-y += timer.o io.o devices.o iomux.o clock.o rk29-pl330.o dma.o gpio.o ddr.o sram.o
obj-$(CONFIG_PM) += pm.o
obj-$(CONFIG_CPU_FREQ) += cpufreq.o
obj-$(CONFIG_RK29_VPU) += vpu.o vpu_mem.o
obj-$(CONFIG_MACH_RK29SDK) += board-rk29sdk.o board-rk29sdk-key.o board-rk29sdk-rfkill.o

View File

@@ -33,48 +33,6 @@
#include <mach/pmu.h>
/* CRU PLL CON */
#define PLL_HIGH_BAND (0x01 << 16)
#define PLL_LOW_BAND (0x00 << 16)
#define PLL_PD (0x01 << 15)
#define PLL_CLKR(i) ((((i) - 1) & 0x1f) << 10)
#define PLL_NR(v) ((((v) >> 10) & 0x1f) + 1)
#define PLL_CLKF(i) ((((i) - 1) & 0x7f) << 3)
#define PLL_NF(v) ((((v) >> 3) & 0x7f) + 1)
#define PLL_NF2(v) (((((v) >> 3) & 0x7f) + 1) << 1)
#define PLL_CLKOD(i) (((i) & 0x03) << 1)
#define PLL_NO_1 PLL_CLKOD(0)
#define PLL_NO_2 PLL_CLKOD(1)
#define PLL_NO_4 PLL_CLKOD(2)
#define PLL_NO_8 PLL_CLKOD(3)
#define PLL_NO_SHIFT(v) (((v) >> 1) & 0x03)
#define PLL_BYPASS (0x01)
/* CRU MODE CON */
#define CRU_CPU_MODE_MASK (0x03u << 0)
#define CRU_CPU_MODE_SLOW (0x00u << 0)
#define CRU_CPU_MODE_NORMAL (0x01u << 0)
#define CRU_CPU_MODE_SLOW27 (0x02u << 0)
#define CRU_GENERAL_MODE_MASK (0x03u << 2)
#define CRU_GENERAL_MODE_SLOW (0x00u << 2)
#define CRU_GENERAL_MODE_NORMAL (0x01u << 2)
#define CRU_GENERAL_MODE_SLOW27 (0x02u << 2)
#define CRU_CODEC_MODE_MASK (0x03u << 4)
#define CRU_CODEC_MODE_SLOW (0x00u << 4)
#define CRU_CODEC_MODE_NORMAL (0x01u << 4)
#define CRU_CODEC_MODE_SLOW27 (0x02u << 4)
#define CRU_DDR_MODE_MASK (0x03u << 6)
#define CRU_DDR_MODE_SLOW (0x00u << 6)
#define CRU_DDR_MODE_NORMAL (0x01u << 6)
#define CRU_DDR_MODE_SLOW27 (0x02u << 6)
/* Clock flags */
/* bit 0 is free */
#define RATE_FIXED (1 << 1) /* Fixed clock rate */
@@ -480,14 +438,16 @@ static struct clk ddr_pll_clk = {
static int codec_pll_clk_mode(struct clk *clk, int on)
{
u32 cpll = cru_readl(CRU_CPLL_CON);
if (on) {
cru_writel(cru_readl(CRU_CPLL_CON) & ~PLL_PD, CRU_CPLL_CON);
cru_writel(cpll & ~(PLL_PD | PLL_BYPASS), CRU_CPLL_CON);
delay_300us();
pll_wait_lock(CODEC_PLL_IDX);
cru_writel((cru_readl(CRU_MODE_CON) & ~CRU_CODEC_MODE_MASK) | CRU_CODEC_MODE_NORMAL, CRU_MODE_CON);
} else {
cru_writel((cru_readl(CRU_MODE_CON) & ~CRU_CODEC_MODE_MASK) | CRU_CODEC_MODE_SLOW, CRU_MODE_CON);
cru_writel(cru_readl(CRU_CPLL_CON) | PLL_PD, CRU_CPLL_CON);
cru_writel(cpll | PLL_BYPASS, CRU_CPLL_CON);
cru_writel(cpll | PLL_PD | PLL_BYPASS, CRU_CPLL_CON);
delay_500ns();
}
return 0;
@@ -2241,10 +2201,10 @@ static void __init rk29_clock_common_init(void)
/* codec pll */
clk_set_rate_nolock(&codec_pll_clk, 552 * MHZ);
clk_set_parent_nolock(&clk_gpu, &codec_pll_clk);
clk_set_parent_nolock(&clk_mac_ref_div, &codec_pll_clk);
/* arm pll */
clk_set_rate_nolock(&arm_pll_clk, armclk);
clk_set_parent_nolock(&clk_mac_ref_div, &arm_pll_clk);
}
static void __init clk_enable_init_clocks(void)

View File

@@ -13,11 +13,26 @@
*
*/
#ifdef CONFIG_CPU_FREQ_DEBUG
#define DEBUG
#endif
#define pr_fmt(fmt) "cpufreq: %s: " fmt, __func__
#include <linux/clk.h>
#include <linux/cpufreq.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/regulator/consumer.h>
#include <linux/suspend.h>
#define SLEEP_FREQ (800 * 1000) /* Use 800MHz when entering sleep */
/* additional symantics for "relation" in cpufreq with pm */
#define DISABLE_FURTHER_CPUFREQ 0x10
#define ENABLE_FURTHER_CPUFREQ 0x20
#define MASK_FURTHER_CPUFREQ 0x30
/* With 0x00(NOCHANGE), it depends on the previous "further" status */
static int no_cpufreq_access;
static struct cpufreq_frequency_table freq_table[] = {
// { .index = 950000, .frequency = 204000 },
@@ -46,8 +61,28 @@ static int rk29_cpufreq_target(struct cpufreq_policy *policy, unsigned int targe
if (policy->cpu != 0)
return -EINVAL;
if ((relation & ENABLE_FURTHER_CPUFREQ) &&
(relation & DISABLE_FURTHER_CPUFREQ)) {
/* Invalidate both if both marked */
relation &= ~ENABLE_FURTHER_CPUFREQ;
relation &= ~DISABLE_FURTHER_CPUFREQ;
pr_err("denied marking FURTHER_CPUFREQ as both marked.\n");
}
if (relation & ENABLE_FURTHER_CPUFREQ)
no_cpufreq_access--;
if (no_cpufreq_access) {
#ifdef CONFIG_PM_VERBOSE
pr_err("denied access to %s as it is disabled temporarily\n", __func__);
#endif
return -EINVAL;
}
if (relation & DISABLE_FURTHER_CPUFREQ)
no_cpufreq_access++;
relation &= ~MASK_FURTHER_CPUFREQ;
if (cpufreq_frequency_table_target(policy, freq_table, target_freq, relation, &index)) {
pr_err("cpufreq: invalid target_freq: %d\n", target_freq);
pr_err("invalid target_freq: %d\n", target_freq);
return -EINVAL;
}
freq = &freq_table[index];
@@ -58,15 +93,13 @@ static int rk29_cpufreq_target(struct cpufreq_policy *policy, unsigned int targe
freqs.old = policy->cur;
freqs.new = freq->frequency;
freqs.cpu = 0;
#ifdef CONFIG_CPU_FREQ_DEBUG
printk(KERN_DEBUG "%s %d r %d (%d-%d) selected %d (%duV)\n", __func__, target_freq, relation, policy->min, policy->max, freq->frequency, freq->index);
#endif
pr_debug("%d r %d (%d-%d) selected %d (%duV)\n", target_freq, relation, policy->min, policy->max, freq->frequency, freq->index);
#ifdef CONFIG_REGULATOR
if (vcore && freqs.new > freqs.old) {
err = regulator_set_voltage(vcore, freq->index, freq->index);
if (err) {
pr_err("cpufreq: fail to set vcore (%duV) for %dkHz: %d\n",
pr_err("fail to set vcore (%duV) for %dkHz: %d\n",
freq->index, freqs.new, err);
goto err_vol;
}
@@ -82,7 +115,7 @@ static int rk29_cpufreq_target(struct cpufreq_policy *policy, unsigned int targe
if (vcore && freqs.new < freqs.old) {
err = regulator_set_voltage(vcore, freq->index, freq->index);
if (err) {
pr_err("cpufreq: fail to set vcore (%duV) for %dkHz: %d\n",
pr_err("fail to set vcore (%duV) for %dkHz: %d\n",
freq->index, freqs.new, err);
}
}
@@ -106,7 +139,7 @@ static int __init rk29_cpufreq_init(struct cpufreq_policy *policy)
#ifdef CONFIG_REGULATOR
vcore = regulator_get(NULL, "vcore");
if (IS_ERR(vcore)) {
pr_err("cpufreq: fail to get regulator vcore: %ld\n", PTR_ERR(vcore));
pr_err("fail to get regulator vcore: %ld\n", PTR_ERR(vcore));
vcore = NULL;
}
#endif
@@ -143,8 +176,35 @@ static struct cpufreq_driver rk29_cpufreq_driver = {
.attr = rk29_cpufreq_attr,
};
static int rk29_cpufreq_pm_notifier_event(struct notifier_block *this,
unsigned long event, void *ptr)
{
int ret;
switch (event) {
case PM_SUSPEND_PREPARE:
ret = cpufreq_driver_target(cpufreq_cpu_get(0), SLEEP_FREQ,
DISABLE_FURTHER_CPUFREQ);
if (ret < 0)
return NOTIFY_BAD;
return NOTIFY_OK;
case PM_POST_RESTORE:
case PM_POST_SUSPEND:
cpufreq_driver_target(cpufreq_cpu_get(0), SLEEP_FREQ,
ENABLE_FURTHER_CPUFREQ);
return NOTIFY_OK;
}
return NOTIFY_DONE;
}
static struct notifier_block rk29_cpufreq_pm_notifier = {
.notifier_call = rk29_cpufreq_pm_notifier_event,
};
static int __init rk29_cpufreq_register(void)
{
register_pm_notifier(&rk29_cpufreq_pm_notifier);
return cpufreq_register_driver(&rk29_cpufreq_driver);
}

View File

@@ -137,6 +137,133 @@ enum cru_clk_gate
CLK_GATE_MAX,
};
enum cru_soft_reset {
SOFT_RST_ARM_CORE = 0,
SOFT_RST_CPU_SYSTEM_INTERCONNECT_1_AXI,
SOFT_RST_CPU_SYSTEM_INTERCONNECT_1_AHB,
SOFT_RST_CPU_SYSTEM_INTERCONNECT_1_APB,
SOFT_RST_CPU_SYSTEM_INTERCONNECT_1_ATB,
SOFT_RST_CPU_SYSTEM_INTERCONNECT_2,
SOFT_RST_DMA0 = 7,
SOFT_RST_GIC,
SOFT_RST_INTERNAL_MEMORY,
SOFT_RST_TZPC = 11,
SOFT_RST_ROM,
SOFT_RST_I2S0,
SOFT_RST_I2S1,
SOFT_RST_SPDIF,
SOFT_RST_UART0,
SOFT_RST_RTC,
SOFT_RST_DDR_PHY,
SOFT_RST_DDR_DLL_BYTE0,
SOFT_RST_DDR_DLL_BYTE1,
SOFT_RST_DDR_DLL_BYTE2,
SOFT_RST_DDR_DLL_BYTE3,
SOFT_RST_DDR_DLL_CMD,
SOFT_RST_DDR_CONTROLLER,
SOFT_RST_ARM_CORE_DEBUG,
SOFT_RST_DAP_DBG,
SOFT_RST_CPU_VODEC_A2A_AHB,
SOFT_RST_CPU_DISPLAY_A2A_AHB,
SOFT_RST_DAP_SYS,
SOFT_RST_PERI_SYSTEM_INTERCONNECT_1_AXI = 32,
SOFT_RST_PERI_SYSTEM_INTERCONNECT_1_AHB,
SOFT_RST_PERI_SYSTEM_INTERCONNECT_1_APB,
SOFT_RST_PERIPH_EMEM = 32 + 4,
SOFT_RST_PERIPH_USB,
SOFT_RST_DMA1,
SOFT_RST_MAC,
SOFT_RST_HIF,
SOFT_RST_NANDC,
SOFT_RST_SMC,
SOFT_RST_HSADC,
SOFT_RST_SDMMC,
SOFT_RST_SDIO,
SOFT_RST_EMMC,
SOFT_RST_USB_OTG_2_0_AHB_BUS,
SOFT_RST_USB_OTG_2_0_PHY,
SOFT_RST_USB_OTG_2_0_CONTROLLER,
SOFT_RST_USB_HOST_2_0_AHB_BUS,
SOFT_RST_USB_HOST_2_0_PHY,
SOFT_RST_USB_HOST_2_0_CONTROLLER,
SOFT_RST_UHOST,
SOFT_RST_VIP,
SOFT_RST_VIP_MATRIX_AHB,
SOFT_RST_SPI0,
SOFT_RST_SPI1,
SOFT_RST_SARADC,
SOFT_RST_UART1,
SOFT_RST_UART2,
SOFT_RST_UART3,
SOFT_RST_PWM,
SOFT_RST_DISPLAY_INTERCONNECTOR_AXI = 64,
SOFT_RST_DISPLAY_INTERCONNECTOR_AHB,
SOFT_RST_LCDC,
SOFT_RST_IPP,
SOFT_RST_EBC,
SOFT_RST_GPU = 64 + 7,
SOFT_RST_DDR_REG_PORT,
SOFT_RST_DDR_CPU_PORT,
SOFT_RST_PERIPH_USED_CPU_AXI,
SOFT_RST_DDR_PERIPH_PORT,
SOFT_RST_DDR_LCDC_PORT,
SOFT_RST_DDR_VCODEC_PORT = 64 + 15,
SOFT_RST_DDR_GPU_PORT,
SOFT_RST_PID_FILTER_AHB,
SOFT_RST_VCODEC_AXI_BUS,
SOFT_RST_VCODEC_AHB_BUS,
SOFT_RST_TIMER0,
SOFT_RST_TIMER1,
SOFT_RST_TIMER2,
SOFT_RST_TIMER3,
SOFT_RST_MAX,
};
/* CRU MODE CON */
#define CRU_CPU_MODE_MASK (0x03u << 0)
#define CRU_CPU_MODE_SLOW (0x00u << 0)
#define CRU_CPU_MODE_NORMAL (0x01u << 0)
#define CRU_CPU_MODE_SLOW27 (0x02u << 0)
#define CRU_GENERAL_MODE_MASK (0x03u << 2)
#define CRU_GENERAL_MODE_SLOW (0x00u << 2)
#define CRU_GENERAL_MODE_NORMAL (0x01u << 2)
#define CRU_GENERAL_MODE_SLOW27 (0x02u << 2)
#define CRU_CODEC_MODE_MASK (0x03u << 4)
#define CRU_CODEC_MODE_SLOW (0x00u << 4)
#define CRU_CODEC_MODE_NORMAL (0x01u << 4)
#define CRU_CODEC_MODE_SLOW27 (0x02u << 4)
#define CRU_DDR_MODE_MASK (0x03u << 6)
#define CRU_DDR_MODE_SLOW (0x00u << 6)
#define CRU_DDR_MODE_NORMAL (0x01u << 6)
#define CRU_DDR_MODE_SLOW27 (0x02u << 6)
/* CRU PLL CON */
#define PLL_HIGH_BAND (0x01 << 16)
#define PLL_LOW_BAND (0x00 << 16)
#define PLL_PD (0x01 << 15)
#define PLL_CLKR(i) ((((i) - 1) & 0x1f) << 10)
#define PLL_NR(v) ((((v) >> 10) & 0x1f) + 1)
#define PLL_CLKF(i) ((((i) - 1) & 0x7f) << 3)
#define PLL_NF(v) ((((v) >> 3) & 0x7f) + 1)
#define PLL_NF2(v) (((((v) >> 3) & 0x7f) + 1) << 1)
#define PLL_CLKOD(i) (((i) & 0x03) << 1)
#define PLL_NO_1 PLL_CLKOD(0)
#define PLL_NO_2 PLL_CLKOD(1)
#define PLL_NO_4 PLL_CLKOD(2)
#define PLL_NO_8 PLL_CLKOD(3)
#define PLL_NO_SHIFT(v) (((v) >> 1) & 0x03)
#define PLL_BYPASS (0x01)
/* Register definitions */
#define CRU_APLL_CON 0x00
#define CRU_DPLL_CON 0x04
@@ -169,4 +296,24 @@ enum cru_clk_gate
#define CRU_SOFTRST1_CON 0x70
#define CRU_SOFTRST2_CON 0x74
static inline void cru_set_soft_reset(enum cru_soft_reset idx, bool on)
{
unsigned long flags;
u32 addr = RK29_CRU_BASE + CRU_SOFTRST0_CON + ((idx >> 5) << 2);
u32 mask = 1 << (idx & 31);
u32 v;
if (idx >= SOFT_RST_MAX)
return;
local_irq_save(flags);
v = readl(addr);
if (on)
v |= mask;
else
v &= ~mask;
writel(v, addr);
local_irq_restore(flags);
}
#endif

196
arch/arm/mach-rk29/pm.c Normal file
View File

@@ -0,0 +1,196 @@
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/pm.h>
#include <linux/suspend.h>
#include <linux/regulator/consumer.h>
#include <linux/io.h>
#include <linux/wakelock.h>
#include <mach/rk29_iomap.h>
#include <mach/cru.h>
#include <mach/pmu.h>
#include <mach/board.h>
#include <mach/system.h>
#include <mach/sram.h>
#define cru_readl(offset) readl(RK29_CRU_BASE + offset)
#define cru_writel(v, offset) do { writel(v, RK29_CRU_BASE + offset); readl(RK29_CRU_BASE + offset); } while (0)
#define pmu_readl(offset) readl(RK29_PMU_BASE + offset)
#define pmu_writel(v, offset) do { writel(v, RK29_PMU_BASE + offset); readl(RK29_PMU_BASE + offset); } while (0)
static unsigned long save_sp;
static inline void delay_500ns(void)
{
int delay = 12;
while (delay--)
barrier();
}
static inline void delay_300us(void)
{
int i;
for (i = 0; i < 600; i++)
delay_500ns();
}
static u32 apll;
static u32 lpj;
static struct regulator *vcore;
static int vcore_uV;
extern void ddr_suspend(void);
extern void ddr_resume(void);
static void __sramfunc rk29_pm_enter_ddr(void)
{
u32 clksel0, dpll, mode;
asm("dsb");
ddr_suspend();
/* suspend ddr pll */
mode = cru_readl(CRU_MODE_CON);
dpll = cru_readl(CRU_DPLL_CON);
cru_writel((cru_readl(CRU_MODE_CON) & ~CRU_DDR_MODE_MASK) | CRU_DDR_MODE_SLOW, CRU_MODE_CON);
cru_writel(dpll | PLL_BYPASS, CRU_DPLL_CON);
cru_writel(dpll | PLL_PD | PLL_BYPASS, CRU_DPLL_CON);
delay_500ns();
/* set arm clk 24MHz/32 = 750KHz */
clksel0 = cru_readl(CRU_CLKSEL0_CON);
cru_writel(clksel0 | 0x1F, CRU_CLKSEL0_CON);
asm("wfi");
/* resume arm clk */
cru_writel(clksel0, CRU_CLKSEL0_CON);
/* resume ddr pll */
cru_writel(dpll, CRU_DPLL_CON);
delay_300us();
cru_writel((cru_readl(CRU_MODE_CON) & ~CRU_DDR_MODE_MASK) | (mode & CRU_DDR_MODE_MASK), CRU_MODE_CON);
ddr_resume();
}
static int rk29_pm_enter(suspend_state_t state)
{
u32 cpll, gpll, mode;
mode = cru_readl(CRU_MODE_CON);
/* suspend codec pll */
cpll = cru_readl(CRU_CPLL_CON);
cru_writel((cru_readl(CRU_MODE_CON) & ~CRU_CODEC_MODE_MASK) | CRU_CODEC_MODE_SLOW, CRU_MODE_CON);
cru_writel(cpll | PLL_BYPASS, CRU_CPLL_CON);
cru_writel(cpll | PLL_PD | PLL_BYPASS, CRU_CPLL_CON);
delay_500ns();
/* suspend general pll */
gpll = cru_readl(CRU_GPLL_CON);
cru_writel((cru_readl(CRU_MODE_CON) & ~CRU_GENERAL_MODE_MASK) | CRU_GENERAL_MODE_SLOW, CRU_MODE_CON);
cru_writel(gpll | PLL_BYPASS, CRU_GPLL_CON);
cru_writel(gpll | PLL_PD | PLL_BYPASS, CRU_GPLL_CON);
delay_500ns();
DDR_SAVE_SP(save_sp);
rk29_pm_enter_ddr();
DDR_RESTORE_SP(save_sp);
/* resume general pll */
cru_writel(gpll, CRU_GPLL_CON);
delay_300us();
cru_writel((cru_readl(CRU_MODE_CON) & ~CRU_GENERAL_MODE_MASK) | (mode & CRU_GENERAL_MODE_MASK), CRU_MODE_CON);
/* resume codec pll */
cru_writel(cpll, CRU_CPLL_CON);
delay_300us();
cru_writel((cru_readl(CRU_MODE_CON) & ~CRU_CODEC_MODE_MASK) | (mode & CRU_CODEC_MODE_MASK), CRU_MODE_CON);
return 0;
}
static int rk29_pm_prepare(void)
{
printk("+%s\n", __func__);
disable_hlt(); // disable entering rk29_idle() by disable_hlt()
/* suspend arm pll */
apll = cru_readl(CRU_APLL_CON);
cru_writel((cru_readl(CRU_MODE_CON) & ~CRU_CPU_MODE_MASK) | CRU_CPU_MODE_SLOW, CRU_MODE_CON);
cru_writel(apll | PLL_BYPASS, CRU_APLL_CON);
cru_writel(apll | PLL_PD | PLL_BYPASS, CRU_APLL_CON);
delay_500ns();
lpj = loops_per_jiffy;
loops_per_jiffy = 120000; // make udelay/mdelay correct
if (vcore) {
vcore_uV = regulator_get_voltage(vcore);
regulator_set_voltage(vcore, 1000000, 1000000);
}
printk("-%s\n", __func__);
return 0;
}
static void rk29_pm_finish(void)
{
printk("+%s\n", __func__);
if (vcore) {
regulator_set_voltage(vcore, vcore_uV, vcore_uV);
}
/* resume arm pll */
loops_per_jiffy = lpj;
cru_writel(apll, CRU_APLL_CON);
delay_300us();
cru_writel((cru_readl(CRU_MODE_CON) & ~CRU_CPU_MODE_MASK) | CRU_CPU_MODE_NORMAL, CRU_MODE_CON);
enable_hlt();
printk("-%s\n", __func__);
}
static struct platform_suspend_ops rk29_pm_ops = {
.enter = rk29_pm_enter,
.valid = suspend_valid_only_mem,
.prepare = rk29_pm_prepare,
.finish = rk29_pm_finish,
};
static void rk29_idle(void)
{
if (!need_resched()) {
int allow_sleep = 1;
#ifdef CONFIG_HAS_WAKELOCK
allow_sleep = !has_wake_lock(WAKE_LOCK_IDLE);
#endif
if (allow_sleep) {
u32 mode_con = cru_readl(CRU_MODE_CON);
cru_writel((mode_con & ~CRU_CPU_MODE_MASK) | CRU_CPU_MODE_SLOW, CRU_MODE_CON);
arch_idle();
cru_writel((mode_con & ~CRU_CPU_MODE_MASK) | CRU_CPU_MODE_NORMAL, CRU_MODE_CON);
} else {
arch_idle();
}
}
local_irq_enable();
}
static int __init rk29_pm_init(void)
{
vcore = regulator_get(NULL, "vcore");
if (IS_ERR(vcore)) {
pr_err("pm: fail to get regulator vcore: %ld\n", PTR_ERR(vcore));
vcore = NULL;
}
suspend_set_ops(&rk29_pm_ops);
/* set idle function */
pm_idle = rk29_idle;
return 0;
}
__initcall(rk29_pm_init);