Commit | Line | Data |
---|---|---|
8446be5d TP |
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 "pmsu.h" | |
27 | ||
28 | #define SDRAM_CONFIG_OFFS 0x0 | |
29 | #define SDRAM_CONFIG_SR_MODE_BIT BIT(24) | |
30 | #define SDRAM_OPERATION_OFFS 0x18 | |
31 | #define SDRAM_OPERATION_SELF_REFRESH 0x7 | |
32 | #define SDRAM_DLB_EVICTION_OFFS 0x30c | |
33 | #define SDRAM_DLB_EVICTION_THRESHOLD_MASK 0xff | |
34 | ||
35 | static void (*mvebu_board_pm_enter)(void __iomem *sdram_reg, u32 srcmd); | |
36 | static void __iomem *sdram_ctrl; | |
37 | ||
38 | static int mvebu_pm_powerdown(unsigned long data) | |
39 | { | |
40 | u32 reg, srcmd; | |
41 | ||
42 | flush_cache_all(); | |
43 | outer_flush_all(); | |
44 | ||
45 | /* | |
46 | * Issue a Data Synchronization Barrier instruction to ensure | |
47 | * that all state saving has been completed. | |
48 | */ | |
49 | dsb(); | |
50 | ||
51 | /* Flush the DLB and wait ~7 usec */ | |
52 | reg = readl(sdram_ctrl + SDRAM_DLB_EVICTION_OFFS); | |
53 | reg &= ~SDRAM_DLB_EVICTION_THRESHOLD_MASK; | |
54 | writel(reg, sdram_ctrl + SDRAM_DLB_EVICTION_OFFS); | |
55 | ||
56 | udelay(7); | |
57 | ||
58 | /* Set DRAM in battery backup mode */ | |
59 | reg = readl(sdram_ctrl + SDRAM_CONFIG_OFFS); | |
60 | reg &= ~SDRAM_CONFIG_SR_MODE_BIT; | |
61 | writel(reg, sdram_ctrl + SDRAM_CONFIG_OFFS); | |
62 | ||
63 | /* Prepare to go to self-refresh */ | |
64 | ||
65 | srcmd = readl(sdram_ctrl + SDRAM_OPERATION_OFFS); | |
66 | srcmd &= ~0x1F; | |
67 | srcmd |= SDRAM_OPERATION_SELF_REFRESH; | |
68 | ||
69 | mvebu_board_pm_enter(sdram_ctrl + SDRAM_OPERATION_OFFS, srcmd); | |
70 | ||
71 | return 0; | |
72 | } | |
73 | ||
74 | #define BOOT_INFO_ADDR 0x3000 | |
75 | #define BOOT_MAGIC_WORD 0xdeadb002 | |
76 | #define BOOT_MAGIC_LIST_END 0xffffffff | |
77 | ||
78 | /* | |
79 | * Those registers are accessed before switching the internal register | |
80 | * base, which is why we hardcode the 0xd0000000 base address, the one | |
81 | * used by the SoC out of reset. | |
82 | */ | |
83 | #define MBUS_WINDOW_12_CTRL 0xd00200b0 | |
84 | #define MBUS_INTERNAL_REG_ADDRESS 0xd0020080 | |
85 | ||
86 | #define SDRAM_WIN_BASE_REG(x) (0x20180 + (0x8*x)) | |
87 | #define SDRAM_WIN_CTRL_REG(x) (0x20184 + (0x8*x)) | |
88 | ||
89 | static phys_addr_t mvebu_internal_reg_base(void) | |
90 | { | |
91 | struct device_node *np; | |
92 | __be32 in_addr[2]; | |
93 | ||
94 | np = of_find_node_by_name(NULL, "internal-regs"); | |
95 | BUG_ON(!np); | |
96 | ||
97 | /* | |
98 | * Ask the DT what is the internal register address on this | |
99 | * platform. In the mvebu-mbus DT binding, 0xf0010000 | |
100 | * corresponds to the internal register window. | |
101 | */ | |
102 | in_addr[0] = cpu_to_be32(0xf0010000); | |
103 | in_addr[1] = 0x0; | |
104 | ||
105 | return of_translate_address(np, in_addr); | |
106 | } | |
107 | ||
108 | static void mvebu_pm_store_bootinfo(void) | |
109 | { | |
110 | u32 *store_addr; | |
111 | phys_addr_t resume_pc; | |
112 | ||
113 | store_addr = phys_to_virt(BOOT_INFO_ADDR); | |
114 | resume_pc = virt_to_phys(armada_370_xp_cpu_resume); | |
115 | ||
116 | /* | |
117 | * The bootloader expects the first two words to be a magic | |
118 | * value (BOOT_MAGIC_WORD), followed by the address of the | |
119 | * resume code to jump to. Then, it expects a sequence of | |
120 | * (address, value) pairs, which can be used to restore the | |
121 | * value of certain registers. This sequence must end with the | |
122 | * BOOT_MAGIC_LIST_END magic value. | |
123 | */ | |
124 | ||
125 | writel(BOOT_MAGIC_WORD, store_addr++); | |
126 | writel(resume_pc, store_addr++); | |
127 | ||
128 | /* | |
129 | * Some platforms remap their internal register base address | |
130 | * to 0xf1000000. However, out of reset, window 12 starts at | |
131 | * 0xf0000000 and ends at 0xf7ffffff, which would overlap with | |
132 | * the internal registers. Therefore, disable window 12. | |
133 | */ | |
134 | writel(MBUS_WINDOW_12_CTRL, store_addr++); | |
135 | writel(0x0, store_addr++); | |
136 | ||
137 | /* | |
138 | * Set the internal register base address to the value | |
139 | * expected by Linux, as read from the Device Tree. | |
140 | */ | |
141 | writel(MBUS_INTERNAL_REG_ADDRESS, store_addr++); | |
142 | writel(mvebu_internal_reg_base(), store_addr++); | |
143 | ||
144 | /* | |
145 | * Ask the mvebu-mbus driver to store the SDRAM window | |
146 | * configuration, which has to be restored by the bootloader | |
147 | * before re-entering the kernel on resume. | |
148 | */ | |
149 | store_addr += mvebu_mbus_save_cpu_target(store_addr); | |
150 | ||
151 | writel(BOOT_MAGIC_LIST_END, store_addr); | |
152 | } | |
153 | ||
154 | static int mvebu_pm_enter(suspend_state_t state) | |
155 | { | |
156 | if (state != PM_SUSPEND_MEM) | |
157 | return -EINVAL; | |
158 | ||
159 | cpu_pm_enter(); | |
160 | ||
161 | mvebu_pm_store_bootinfo(); | |
162 | cpu_suspend(0, mvebu_pm_powerdown); | |
163 | ||
164 | outer_resume(); | |
165 | ||
166 | mvebu_v7_pmsu_idle_exit(); | |
167 | ||
168 | set_cpu_coherent(); | |
169 | ||
170 | cpu_pm_exit(); | |
171 | ||
172 | return 0; | |
173 | } | |
174 | ||
175 | static const struct platform_suspend_ops mvebu_pm_ops = { | |
176 | .enter = mvebu_pm_enter, | |
177 | .valid = suspend_valid_only_mem, | |
178 | }; | |
179 | ||
180 | int mvebu_pm_init(void (*board_pm_enter)(void __iomem *sdram_reg, u32 srcmd)) | |
181 | { | |
182 | struct device_node *np; | |
183 | struct resource res; | |
184 | ||
8446be5d TP |
185 | np = of_find_compatible_node(NULL, NULL, |
186 | "marvell,armada-xp-sdram-controller"); | |
187 | if (!np) | |
188 | return -ENODEV; | |
189 | ||
190 | if (of_address_to_resource(np, 0, &res)) { | |
191 | of_node_put(np); | |
192 | return -ENODEV; | |
193 | } | |
194 | ||
195 | if (!request_mem_region(res.start, resource_size(&res), | |
196 | np->full_name)) { | |
197 | of_node_put(np); | |
198 | return -EBUSY; | |
199 | } | |
200 | ||
201 | sdram_ctrl = ioremap(res.start, resource_size(&res)); | |
202 | if (!sdram_ctrl) { | |
203 | release_mem_region(res.start, resource_size(&res)); | |
204 | of_node_put(np); | |
205 | return -ENOMEM; | |
206 | } | |
207 | ||
208 | of_node_put(np); | |
209 | ||
210 | mvebu_board_pm_enter = board_pm_enter; | |
211 | ||
212 | suspend_set_ops(&mvebu_pm_ops); | |
213 | ||
214 | return 0; | |
215 | } |