xref: /linux/arch/arm/mach-davinci/pm.c (revision f3f6cc814f9cb61cfb738af2b126a8bf19e5ab4c)
1efc1bb8aSSekhar Nori /*
2efc1bb8aSSekhar Nori  * DaVinci Power Management Routines
3efc1bb8aSSekhar Nori  *
4efc1bb8aSSekhar Nori  * Copyright (C) 2009 Texas Instruments, Inc. http://www.ti.com/
5efc1bb8aSSekhar Nori  *
6efc1bb8aSSekhar Nori  * This program is free software; you can redistribute it and/or modify
7efc1bb8aSSekhar Nori  * it under the terms of the GNU General Public License version 2 as
8efc1bb8aSSekhar Nori  * published by the Free Software Foundation.
9efc1bb8aSSekhar Nori  */
10efc1bb8aSSekhar Nori 
11efc1bb8aSSekhar Nori #include <linux/pm.h>
12efc1bb8aSSekhar Nori #include <linux/suspend.h>
13efc1bb8aSSekhar Nori #include <linux/module.h>
14efc1bb8aSSekhar Nori #include <linux/platform_device.h>
15efc1bb8aSSekhar Nori #include <linux/clk.h>
16efc1bb8aSSekhar Nori #include <linux/spinlock.h>
17efc1bb8aSSekhar Nori 
18efc1bb8aSSekhar Nori #include <asm/cacheflush.h>
19efc1bb8aSSekhar Nori #include <asm/delay.h>
20b7f080cfSAlexey Dobriyan #include <asm/io.h>
21efc1bb8aSSekhar Nori 
22215a084dSSekhar Nori #include <mach/common.h>
23efc1bb8aSSekhar Nori #include <mach/da8xx.h>
24aa9aa1ecSKevin Hilman #include <mach/mux.h>
25efc1bb8aSSekhar Nori #include <mach/pm.h>
26efc1bb8aSSekhar Nori 
27efc1bb8aSSekhar Nori #include "clock.h"
28aa9aa1ecSKevin Hilman #include "psc.h"
29aa9aa1ecSKevin Hilman #include "sram.h"
30efc1bb8aSSekhar Nori 
31aa9aa1ecSKevin Hilman #define DA850_PLL1_BASE		0x01e1a000
32efc1bb8aSSekhar Nori #define DEEPSLEEP_SLEEPCOUNT_MASK	0xFFFF
33aa9aa1ecSKevin Hilman #define DEEPSLEEP_SLEEPCOUNT		128
34efc1bb8aSSekhar Nori 
35efc1bb8aSSekhar Nori static void (*davinci_sram_suspend) (struct davinci_pm_config *);
36aa9aa1ecSKevin Hilman static struct davinci_pm_config pm_config = {
37aa9aa1ecSKevin Hilman 	.sleepcount = DEEPSLEEP_SLEEPCOUNT,
38aa9aa1ecSKevin Hilman 	.ddrpsc_num = DA8XX_LPSC1_EMIF3C,
39aa9aa1ecSKevin Hilman };
40aa9aa1ecSKevin Hilman 
41efc1bb8aSSekhar Nori static void davinci_sram_push(void *dest, void *src, unsigned int size)
42efc1bb8aSSekhar Nori {
43efc1bb8aSSekhar Nori 	memcpy(dest, src, size);
44efc1bb8aSSekhar Nori 	flush_icache_range((unsigned long)dest, (unsigned long)(dest + size));
45efc1bb8aSSekhar Nori }
46efc1bb8aSSekhar Nori 
47efc1bb8aSSekhar Nori static void davinci_pm_suspend(void)
48efc1bb8aSSekhar Nori {
49efc1bb8aSSekhar Nori 	unsigned val;
50efc1bb8aSSekhar Nori 
511428ed1aSKevin Hilman 	if (pm_config.cpupll_reg_base != pm_config.ddrpll_reg_base) {
52efc1bb8aSSekhar Nori 
53efc1bb8aSSekhar Nori 		/* Switch CPU PLL to bypass mode */
541428ed1aSKevin Hilman 		val = __raw_readl(pm_config.cpupll_reg_base + PLLCTL);
55efc1bb8aSSekhar Nori 		val &= ~(PLLCTL_PLLENSRC | PLLCTL_PLLEN);
561428ed1aSKevin Hilman 		__raw_writel(val, pm_config.cpupll_reg_base + PLLCTL);
57efc1bb8aSSekhar Nori 
58efc1bb8aSSekhar Nori 		udelay(PLL_BYPASS_TIME);
59efc1bb8aSSekhar Nori 
60efc1bb8aSSekhar Nori 		/* Powerdown CPU PLL */
611428ed1aSKevin Hilman 		val = __raw_readl(pm_config.cpupll_reg_base + PLLCTL);
62efc1bb8aSSekhar Nori 		val |= PLLCTL_PLLPWRDN;
631428ed1aSKevin Hilman 		__raw_writel(val, pm_config.cpupll_reg_base + PLLCTL);
64efc1bb8aSSekhar Nori 	}
65efc1bb8aSSekhar Nori 
66efc1bb8aSSekhar Nori 	/* Configure sleep count in deep sleep register */
671428ed1aSKevin Hilman 	val = __raw_readl(pm_config.deepsleep_reg);
68efc1bb8aSSekhar Nori 	val &= ~DEEPSLEEP_SLEEPCOUNT_MASK,
691428ed1aSKevin Hilman 	val |= pm_config.sleepcount;
701428ed1aSKevin Hilman 	__raw_writel(val, pm_config.deepsleep_reg);
71efc1bb8aSSekhar Nori 
72efc1bb8aSSekhar Nori 	/* System goes to sleep in this call */
731428ed1aSKevin Hilman 	davinci_sram_suspend(&pm_config);
74efc1bb8aSSekhar Nori 
751428ed1aSKevin Hilman 	if (pm_config.cpupll_reg_base != pm_config.ddrpll_reg_base) {
76efc1bb8aSSekhar Nori 
77efc1bb8aSSekhar Nori 		/* put CPU PLL in reset */
781428ed1aSKevin Hilman 		val = __raw_readl(pm_config.cpupll_reg_base + PLLCTL);
79efc1bb8aSSekhar Nori 		val &= ~PLLCTL_PLLRST;
801428ed1aSKevin Hilman 		__raw_writel(val, pm_config.cpupll_reg_base + PLLCTL);
81efc1bb8aSSekhar Nori 
82efc1bb8aSSekhar Nori 		/* put CPU PLL in power down */
831428ed1aSKevin Hilman 		val = __raw_readl(pm_config.cpupll_reg_base + PLLCTL);
84efc1bb8aSSekhar Nori 		val &= ~PLLCTL_PLLPWRDN;
851428ed1aSKevin Hilman 		__raw_writel(val, pm_config.cpupll_reg_base + PLLCTL);
86efc1bb8aSSekhar Nori 
87efc1bb8aSSekhar Nori 		/* wait for CPU PLL reset */
88efc1bb8aSSekhar Nori 		udelay(PLL_RESET_TIME);
89efc1bb8aSSekhar Nori 
90efc1bb8aSSekhar Nori 		/* bring CPU PLL out of reset */
911428ed1aSKevin Hilman 		val = __raw_readl(pm_config.cpupll_reg_base + PLLCTL);
92efc1bb8aSSekhar Nori 		val |= PLLCTL_PLLRST;
931428ed1aSKevin Hilman 		__raw_writel(val, pm_config.cpupll_reg_base + PLLCTL);
94efc1bb8aSSekhar Nori 
95efc1bb8aSSekhar Nori 		/* Wait for CPU PLL to lock */
96efc1bb8aSSekhar Nori 		udelay(PLL_LOCK_TIME);
97efc1bb8aSSekhar Nori 
98efc1bb8aSSekhar Nori 		/* Remove CPU PLL from bypass mode */
991428ed1aSKevin Hilman 		val = __raw_readl(pm_config.cpupll_reg_base + PLLCTL);
100efc1bb8aSSekhar Nori 		val &= ~PLLCTL_PLLENSRC;
101efc1bb8aSSekhar Nori 		val |= PLLCTL_PLLEN;
1021428ed1aSKevin Hilman 		__raw_writel(val, pm_config.cpupll_reg_base + PLLCTL);
103efc1bb8aSSekhar Nori 	}
104efc1bb8aSSekhar Nori }
105efc1bb8aSSekhar Nori 
106efc1bb8aSSekhar Nori static int davinci_pm_enter(suspend_state_t state)
107efc1bb8aSSekhar Nori {
108efc1bb8aSSekhar Nori 	int ret = 0;
109efc1bb8aSSekhar Nori 
110efc1bb8aSSekhar Nori 	switch (state) {
111efc1bb8aSSekhar Nori 	case PM_SUSPEND_MEM:
112efc1bb8aSSekhar Nori 		davinci_pm_suspend();
113efc1bb8aSSekhar Nori 		break;
114efc1bb8aSSekhar Nori 	default:
115efc1bb8aSSekhar Nori 		ret = -EINVAL;
116efc1bb8aSSekhar Nori 	}
117efc1bb8aSSekhar Nori 
118efc1bb8aSSekhar Nori 	return ret;
119efc1bb8aSSekhar Nori }
120efc1bb8aSSekhar Nori 
1212f55ac07SLionel Debroux static const struct platform_suspend_ops davinci_pm_ops = {
122efc1bb8aSSekhar Nori 	.enter		= davinci_pm_enter,
123efc1bb8aSSekhar Nori 	.valid		= suspend_valid_only_mem,
124efc1bb8aSSekhar Nori };
125efc1bb8aSSekhar Nori 
126aa9aa1ecSKevin Hilman int __init davinci_pm_init(void)
127efc1bb8aSSekhar Nori {
128aa9aa1ecSKevin Hilman 	int ret;
129aa9aa1ecSKevin Hilman 
130aa9aa1ecSKevin Hilman 	ret = davinci_cfg_reg(DA850_RTC_ALARM);
131aa9aa1ecSKevin Hilman 	if (ret)
132aa9aa1ecSKevin Hilman 		return ret;
133aa9aa1ecSKevin Hilman 
1341428ed1aSKevin Hilman 	pm_config.ddr2_ctlr_base = da8xx_get_mem_ctlr();
1351428ed1aSKevin Hilman 	pm_config.deepsleep_reg = DA8XX_SYSCFG1_VIRT(DA8XX_DEEPSLEEP_REG);
136aa9aa1ecSKevin Hilman 
1371428ed1aSKevin Hilman 	pm_config.cpupll_reg_base = ioremap(DA8XX_PLL0_BASE, SZ_4K);
1381428ed1aSKevin Hilman 	if (!pm_config.cpupll_reg_base)
139aa9aa1ecSKevin Hilman 		return -ENOMEM;
140aa9aa1ecSKevin Hilman 
1411428ed1aSKevin Hilman 	pm_config.ddrpll_reg_base = ioremap(DA850_PLL1_BASE, SZ_4K);
1421428ed1aSKevin Hilman 	if (!pm_config.ddrpll_reg_base) {
143aa9aa1ecSKevin Hilman 		ret = -ENOMEM;
144aa9aa1ecSKevin Hilman 		goto no_ddrpll_mem;
145aa9aa1ecSKevin Hilman 	}
146aa9aa1ecSKevin Hilman 
1471428ed1aSKevin Hilman 	pm_config.ddrpsc_reg_base = ioremap(DA8XX_PSC1_BASE, SZ_4K);
1481428ed1aSKevin Hilman 	if (!pm_config.ddrpsc_reg_base) {
149aa9aa1ecSKevin Hilman 		ret = -ENOMEM;
150aa9aa1ecSKevin Hilman 		goto no_ddrpsc_mem;
151efc1bb8aSSekhar Nori 	}
152efc1bb8aSSekhar Nori 
153efc1bb8aSSekhar Nori 	davinci_sram_suspend = sram_alloc(davinci_cpu_suspend_sz, NULL);
154efc1bb8aSSekhar Nori 	if (!davinci_sram_suspend) {
155aa9aa1ecSKevin Hilman 		pr_err("PM: cannot allocate SRAM memory\n");
156*f3f6cc81SChristophe JAILLET 		ret = -ENOMEM;
157*f3f6cc81SChristophe JAILLET 		goto no_sram_mem;
158efc1bb8aSSekhar Nori 	}
159efc1bb8aSSekhar Nori 
160efc1bb8aSSekhar Nori 	davinci_sram_push(davinci_sram_suspend, davinci_cpu_suspend,
161efc1bb8aSSekhar Nori 						davinci_cpu_suspend_sz);
162efc1bb8aSSekhar Nori 
163efc1bb8aSSekhar Nori 	suspend_set_ops(&davinci_pm_ops);
164efc1bb8aSSekhar Nori 
165*f3f6cc81SChristophe JAILLET no_sram_mem:
166*f3f6cc81SChristophe JAILLET 	iounmap(pm_config.ddrpsc_reg_base);
167aa9aa1ecSKevin Hilman no_ddrpsc_mem:
1681428ed1aSKevin Hilman 	iounmap(pm_config.ddrpll_reg_base);
169aa9aa1ecSKevin Hilman no_ddrpll_mem:
1701428ed1aSKevin Hilman 	iounmap(pm_config.cpupll_reg_base);
171aa9aa1ecSKevin Hilman 	return ret;
172efc1bb8aSSekhar Nori }
173