pm.c (6449B)
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 36static void (*mvebu_board_pm_enter)(void __iomem *sdram_reg, u32 srcmd); 37static void __iomem *sdram_ctrl; 38 39static 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 90static 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 109static 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 153static 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 167static 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 189static 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 204static 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 215static const struct platform_suspend_ops mvebu_pm_ops = { 216 .enter = mvebu_pm_enter, 217 .valid = mvebu_pm_valid, 218}; 219 220static 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 234late_initcall(mvebu_pm_init); 235 236int __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}