xref: /openbmc/linux/arch/arm/mach-mvebu/pm.c (revision 55fd7e02)
1 /*
2  * Suspend/resume support. Currently supporting Armada XP only.
3  *
4  * Copyright (C) 2014 Marvell
5  *
6  * Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
7  *
8  * This file is licensed under the terms of the GNU General Public
9  * License version 2.  This program is licensed "as is" without any
10  * warranty of any kind, whether express or implied.
11  */
12 
13 #include <linux/cpu_pm.h>
14 #include <linux/delay.h>
15 #include <linux/gpio.h>
16 #include <linux/io.h>
17 #include <linux/kernel.h>
18 #include <linux/mbus.h>
19 #include <linux/of_address.h>
20 #include <linux/suspend.h>
21 #include <asm/cacheflush.h>
22 #include <asm/outercache.h>
23 #include <asm/suspend.h>
24 
25 #include "coherency.h"
26 #include "common.h"
27 #include "pmsu.h"
28 
29 #define SDRAM_CONFIG_OFFS                  0x0
30 #define  SDRAM_CONFIG_SR_MODE_BIT          BIT(24)
31 #define SDRAM_OPERATION_OFFS               0x18
32 #define  SDRAM_OPERATION_SELF_REFRESH      0x7
33 #define SDRAM_DLB_EVICTION_OFFS            0x30c
34 #define  SDRAM_DLB_EVICTION_THRESHOLD_MASK 0xff
35 
36 static void (*mvebu_board_pm_enter)(void __iomem *sdram_reg, u32 srcmd);
37 static void __iomem *sdram_ctrl;
38 
39 static int mvebu_pm_powerdown(unsigned long data)
40 {
41 	u32 reg, srcmd;
42 
43 	flush_cache_all();
44 	outer_flush_all();
45 
46 	/*
47 	 * Issue a Data Synchronization Barrier instruction to ensure
48 	 * that all state saving has been completed.
49 	 */
50 	dsb();
51 
52 	/* Flush the DLB and wait ~7 usec */
53 	reg = readl(sdram_ctrl + SDRAM_DLB_EVICTION_OFFS);
54 	reg &= ~SDRAM_DLB_EVICTION_THRESHOLD_MASK;
55 	writel(reg, sdram_ctrl + SDRAM_DLB_EVICTION_OFFS);
56 
57 	udelay(7);
58 
59 	/* Set DRAM in battery backup mode */
60 	reg = readl(sdram_ctrl + SDRAM_CONFIG_OFFS);
61 	reg &= ~SDRAM_CONFIG_SR_MODE_BIT;
62 	writel(reg, sdram_ctrl + SDRAM_CONFIG_OFFS);
63 
64 	/* Prepare to go to self-refresh */
65 
66 	srcmd = readl(sdram_ctrl + SDRAM_OPERATION_OFFS);
67 	srcmd &= ~0x1F;
68 	srcmd |= SDRAM_OPERATION_SELF_REFRESH;
69 
70 	mvebu_board_pm_enter(sdram_ctrl + SDRAM_OPERATION_OFFS, srcmd);
71 
72 	return 0;
73 }
74 
75 #define BOOT_INFO_ADDR      0x3000
76 #define BOOT_MAGIC_WORD	    0xdeadb002
77 #define BOOT_MAGIC_LIST_END 0xffffffff
78 
79 /*
80  * Those registers are accessed before switching the internal register
81  * base, which is why we hardcode the 0xd0000000 base address, the one
82  * used by the SoC out of reset.
83  */
84 #define MBUS_WINDOW_12_CTRL       0xd00200b0
85 #define MBUS_INTERNAL_REG_ADDRESS 0xd0020080
86 
87 #define SDRAM_WIN_BASE_REG(x)	(0x20180 + (0x8*x))
88 #define SDRAM_WIN_CTRL_REG(x)	(0x20184 + (0x8*x))
89 
90 static phys_addr_t mvebu_internal_reg_base(void)
91 {
92 	struct device_node *np;
93 	__be32 in_addr[2];
94 
95 	np = of_find_node_by_name(NULL, "internal-regs");
96 	BUG_ON(!np);
97 
98 	/*
99 	 * Ask the DT what is the internal register address on this
100 	 * platform. In the mvebu-mbus DT binding, 0xf0010000
101 	 * corresponds to the internal register window.
102 	 */
103 	in_addr[0] = cpu_to_be32(0xf0010000);
104 	in_addr[1] = 0x0;
105 
106 	return of_translate_address(np, in_addr);
107 }
108 
109 static void mvebu_pm_store_armadaxp_bootinfo(u32 *store_addr)
110 {
111 	phys_addr_t resume_pc;
112 
113 	resume_pc = __pa_symbol(armada_370_xp_cpu_resume);
114 
115 	/*
116 	 * The bootloader expects the first two words to be a magic
117 	 * value (BOOT_MAGIC_WORD), followed by the address of the
118 	 * resume code to jump to. Then, it expects a sequence of
119 	 * (address, value) pairs, which can be used to restore the
120 	 * value of certain registers. This sequence must end with the
121 	 * BOOT_MAGIC_LIST_END magic value.
122 	 */
123 
124 	writel(BOOT_MAGIC_WORD, store_addr++);
125 	writel(resume_pc, store_addr++);
126 
127 	/*
128 	 * Some platforms remap their internal register base address
129 	 * to 0xf1000000. However, out of reset, window 12 starts at
130 	 * 0xf0000000 and ends at 0xf7ffffff, which would overlap with
131 	 * the internal registers. Therefore, disable window 12.
132 	 */
133 	writel(MBUS_WINDOW_12_CTRL, store_addr++);
134 	writel(0x0, store_addr++);
135 
136 	/*
137 	 * Set the internal register base address to the value
138 	 * expected by Linux, as read from the Device Tree.
139 	 */
140 	writel(MBUS_INTERNAL_REG_ADDRESS, store_addr++);
141 	writel(mvebu_internal_reg_base(), store_addr++);
142 
143 	/*
144 	 * Ask the mvebu-mbus driver to store the SDRAM window
145 	 * configuration, which has to be restored by the bootloader
146 	 * before re-entering the kernel on resume.
147 	 */
148 	store_addr += mvebu_mbus_save_cpu_target(store_addr);
149 
150 	writel(BOOT_MAGIC_LIST_END, store_addr);
151 }
152 
153 static int mvebu_pm_store_bootinfo(void)
154 {
155 	u32 *store_addr;
156 
157 	store_addr = phys_to_virt(BOOT_INFO_ADDR);
158 
159 	if (of_machine_is_compatible("marvell,armadaxp"))
160 		mvebu_pm_store_armadaxp_bootinfo(store_addr);
161 	else
162 		return -ENODEV;
163 
164 	return 0;
165 }
166 
167 static int mvebu_enter_suspend(void)
168 {
169 	int ret;
170 
171 	ret = mvebu_pm_store_bootinfo();
172 	if (ret)
173 		return ret;
174 
175 	cpu_pm_enter();
176 
177 	cpu_suspend(0, mvebu_pm_powerdown);
178 
179 	outer_resume();
180 
181 	mvebu_v7_pmsu_idle_exit();
182 
183 	set_cpu_coherent();
184 
185 	cpu_pm_exit();
186 	return 0;
187 }
188 
189 static int mvebu_pm_enter(suspend_state_t state)
190 {
191 	switch (state) {
192 	case PM_SUSPEND_STANDBY:
193 		cpu_do_idle();
194 		break;
195 	case PM_SUSPEND_MEM:
196 		pr_warn("Entering suspend to RAM. Only special wake-up sources will resume the system\n");
197 		return mvebu_enter_suspend();
198 	default:
199 		return -EINVAL;
200 	}
201 	return 0;
202 }
203 
204 static int mvebu_pm_valid(suspend_state_t state)
205 {
206 	if (state == PM_SUSPEND_STANDBY)
207 		return 1;
208 
209 	if (state == PM_SUSPEND_MEM && mvebu_board_pm_enter != NULL)
210 		return 1;
211 
212 	return 0;
213 }
214 
215 static const struct platform_suspend_ops mvebu_pm_ops = {
216 	.enter = mvebu_pm_enter,
217 	.valid = mvebu_pm_valid,
218 };
219 
220 static int __init mvebu_pm_init(void)
221 {
222 	if (!of_machine_is_compatible("marvell,armadaxp") &&
223 	    !of_machine_is_compatible("marvell,armada370") &&
224 	    !of_machine_is_compatible("marvell,armada380") &&
225 	    !of_machine_is_compatible("marvell,armada390"))
226 		return -ENODEV;
227 
228 	suspend_set_ops(&mvebu_pm_ops);
229 
230 	return 0;
231 }
232 
233 
234 late_initcall(mvebu_pm_init);
235 
236 int __init mvebu_pm_suspend_init(void (*board_pm_enter)(void __iomem *sdram_reg,
237 							u32 srcmd))
238 {
239 	struct device_node *np;
240 	struct resource res;
241 
242 	np = of_find_compatible_node(NULL, NULL,
243 				     "marvell,armada-xp-sdram-controller");
244 	if (!np)
245 		return -ENODEV;
246 
247 	if (of_address_to_resource(np, 0, &res)) {
248 		of_node_put(np);
249 		return -ENODEV;
250 	}
251 
252 	if (!request_mem_region(res.start, resource_size(&res),
253 				np->full_name)) {
254 		of_node_put(np);
255 		return -EBUSY;
256 	}
257 
258 	sdram_ctrl = ioremap(res.start, resource_size(&res));
259 	if (!sdram_ctrl) {
260 		release_mem_region(res.start, resource_size(&res));
261 		of_node_put(np);
262 		return -ENOMEM;
263 	}
264 
265 	of_node_put(np);
266 
267 	mvebu_board_pm_enter = board_pm_enter;
268 
269 	return 0;
270 }
271