1/* 2 * Copyright 2014 Freescale Semiconductor, Inc. 3 * 4 * The code contained herein is licensed under the GNU General Public 5 * License. You may obtain a copy of the GNU General Public License 6 * Version 2 or later at the following locations: 7 * 8 * http://www.opensource.org/licenses/gpl-license.html 9 * http://www.gnu.org/copyleft/gpl.html 10 */ 11 12#include <linux/linkage.h> 13#include <asm/assembler.h> 14#include <asm/asm-offsets.h> 15#include <asm/hardware/cache-l2x0.h> 16#include "hardware.h" 17 18/* 19 * ==================== low level suspend ==================== 20 * 21 * Better to follow below rules to use ARM registers: 22 * r0: pm_info structure address; 23 * r1 ~ r4: for saving pm_info members; 24 * r5 ~ r10: free registers; 25 * r11: io base address. 26 * 27 * suspend ocram space layout: 28 * ======================== high address ====================== 29 * . 30 * . 31 * . 32 * ^ 33 * ^ 34 * ^ 35 * imx6_suspend code 36 * PM_INFO structure(imx6_cpu_pm_info) 37 * ======================== low address ======================= 38 */ 39 40/* 41 * Below offsets are based on struct imx6_cpu_pm_info 42 * which defined in arch/arm/mach-imx/pm-imx6q.c, this 43 * structure contains necessary pm info for low level 44 * suspend related code. 45 */ 46#define PM_INFO_PBASE_OFFSET 0x0 47#define PM_INFO_RESUME_ADDR_OFFSET 0x4 48#define PM_INFO_CPU_TYPE_OFFSET 0x8 49#define PM_INFO_PM_INFO_SIZE_OFFSET 0xC 50#define PM_INFO_MX6Q_MMDC_P_OFFSET 0x10 51#define PM_INFO_MX6Q_MMDC_V_OFFSET 0x14 52#define PM_INFO_MX6Q_SRC_P_OFFSET 0x18 53#define PM_INFO_MX6Q_SRC_V_OFFSET 0x1C 54#define PM_INFO_MX6Q_IOMUXC_P_OFFSET 0x20 55#define PM_INFO_MX6Q_IOMUXC_V_OFFSET 0x24 56#define PM_INFO_MX6Q_CCM_P_OFFSET 0x28 57#define PM_INFO_MX6Q_CCM_V_OFFSET 0x2C 58#define PM_INFO_MX6Q_GPC_P_OFFSET 0x30 59#define PM_INFO_MX6Q_GPC_V_OFFSET 0x34 60#define PM_INFO_MX6Q_L2_P_OFFSET 0x38 61#define PM_INFO_MX6Q_L2_V_OFFSET 0x3C 62#define PM_INFO_MMDC_IO_NUM_OFFSET 0x40 63#define PM_INFO_MMDC_IO_VAL_OFFSET 0x44 64 65#define MX6Q_SRC_GPR1 0x20 66#define MX6Q_SRC_GPR2 0x24 67#define MX6Q_MMDC_MAPSR 0x404 68#define MX6Q_MMDC_MPDGCTRL0 0x83c 69#define MX6Q_GPC_IMR1 0x08 70#define MX6Q_GPC_IMR2 0x0c 71#define MX6Q_GPC_IMR3 0x10 72#define MX6Q_GPC_IMR4 0x14 73#define MX6Q_CCM_CCR 0x0 74 75 .align 3 76 77 .macro sync_l2_cache 78 79 /* sync L2 cache to drain L2's buffers to DRAM. */ 80#ifdef CONFIG_CACHE_L2X0 81 ldr r11, [r0, #PM_INFO_MX6Q_L2_V_OFFSET] 82 mov r6, #0x0 83 str r6, [r11, #L2X0_CACHE_SYNC] 841: 85 ldr r6, [r11, #L2X0_CACHE_SYNC] 86 ands r6, r6, #0x1 87 bne 1b 88#endif 89 90 .endm 91 92 .macro resume_mmdc 93 94 /* restore MMDC IO */ 95 cmp r5, #0x0 96 ldreq r11, [r0, #PM_INFO_MX6Q_IOMUXC_V_OFFSET] 97 ldrne r11, [r0, #PM_INFO_MX6Q_IOMUXC_P_OFFSET] 98 99 ldr r6, [r0, #PM_INFO_MMDC_IO_NUM_OFFSET] 100 ldr r7, =PM_INFO_MMDC_IO_VAL_OFFSET 101 add r7, r7, r0 1021: 103 ldr r8, [r7], #0x4 104 ldr r9, [r7], #0x4 105 str r9, [r11, r8] 106 subs r6, r6, #0x1 107 bne 1b 108 109 cmp r5, #0x0 110 ldreq r11, [r0, #PM_INFO_MX6Q_MMDC_V_OFFSET] 111 ldrne r11, [r0, #PM_INFO_MX6Q_MMDC_P_OFFSET] 112 113 cmp r3, #MXC_CPU_IMX6SL 114 bne 4f 115 116 /* reset read FIFO, RST_RD_FIFO */ 117 ldr r7, =MX6Q_MMDC_MPDGCTRL0 118 ldr r6, [r11, r7] 119 orr r6, r6, #(1 << 31) 120 str r6, [r11, r7] 1212: 122 ldr r6, [r11, r7] 123 ands r6, r6, #(1 << 31) 124 bne 2b 125 126 /* reset FIFO a second time */ 127 ldr r6, [r11, r7] 128 orr r6, r6, #(1 << 31) 129 str r6, [r11, r7] 1303: 131 ldr r6, [r11, r7] 132 ands r6, r6, #(1 << 31) 133 bne 3b 1344: 135 /* let DDR out of self-refresh */ 136 ldr r7, [r11, #MX6Q_MMDC_MAPSR] 137 bic r7, r7, #(1 << 21) 138 str r7, [r11, #MX6Q_MMDC_MAPSR] 1395: 140 ldr r7, [r11, #MX6Q_MMDC_MAPSR] 141 ands r7, r7, #(1 << 25) 142 bne 5b 143 144 /* enable DDR auto power saving */ 145 ldr r7, [r11, #MX6Q_MMDC_MAPSR] 146 bic r7, r7, #0x1 147 str r7, [r11, #MX6Q_MMDC_MAPSR] 148 149 .endm 150 151ENTRY(imx6_suspend) 152 ldr r1, [r0, #PM_INFO_PBASE_OFFSET] 153 ldr r2, [r0, #PM_INFO_RESUME_ADDR_OFFSET] 154 ldr r3, [r0, #PM_INFO_CPU_TYPE_OFFSET] 155 ldr r4, [r0, #PM_INFO_PM_INFO_SIZE_OFFSET] 156 157 /* 158 * counting the resume address in iram 159 * to set it in SRC register. 160 */ 161 ldr r6, =imx6_suspend 162 ldr r7, =resume 163 sub r7, r7, r6 164 add r8, r1, r4 165 add r9, r8, r7 166 167 /* 168 * make sure TLB contain the addr we want, 169 * as we will access them after MMDC IO floated. 170 */ 171 172 ldr r11, [r0, #PM_INFO_MX6Q_CCM_V_OFFSET] 173 ldr r6, [r11, #0x0] 174 ldr r11, [r0, #PM_INFO_MX6Q_GPC_V_OFFSET] 175 ldr r6, [r11, #0x0] 176 177 /* use r11 to store the IO address */ 178 ldr r11, [r0, #PM_INFO_MX6Q_SRC_V_OFFSET] 179 /* store physical resume addr and pm_info address. */ 180 str r9, [r11, #MX6Q_SRC_GPR1] 181 str r1, [r11, #MX6Q_SRC_GPR2] 182 183 /* need to sync L2 cache before DSM. */ 184 sync_l2_cache 185 186 ldr r11, [r0, #PM_INFO_MX6Q_MMDC_V_OFFSET] 187 /* 188 * put DDR explicitly into self-refresh and 189 * disable automatic power savings. 190 */ 191 ldr r7, [r11, #MX6Q_MMDC_MAPSR] 192 orr r7, r7, #0x1 193 str r7, [r11, #MX6Q_MMDC_MAPSR] 194 195 /* make the DDR explicitly enter self-refresh. */ 196 ldr r7, [r11, #MX6Q_MMDC_MAPSR] 197 orr r7, r7, #(1 << 21) 198 str r7, [r11, #MX6Q_MMDC_MAPSR] 199 200poll_dvfs_set: 201 ldr r7, [r11, #MX6Q_MMDC_MAPSR] 202 ands r7, r7, #(1 << 25) 203 beq poll_dvfs_set 204 205 ldr r11, [r0, #PM_INFO_MX6Q_IOMUXC_V_OFFSET] 206 ldr r6, =0x0 207 ldr r7, [r0, #PM_INFO_MMDC_IO_NUM_OFFSET] 208 ldr r8, =PM_INFO_MMDC_IO_VAL_OFFSET 209 add r8, r8, r0 210 /* i.MX6SL's last 3 IOs need special setting */ 211 cmp r3, #MXC_CPU_IMX6SL 212 subeq r7, r7, #0x3 213set_mmdc_io_lpm: 214 ldr r9, [r8], #0x8 215 str r6, [r11, r9] 216 subs r7, r7, #0x1 217 bne set_mmdc_io_lpm 218 219 cmp r3, #MXC_CPU_IMX6SL 220 bne set_mmdc_io_lpm_done 221 ldr r6, =0x1000 222 ldr r9, [r8], #0x8 223 str r6, [r11, r9] 224 ldr r9, [r8], #0x8 225 str r6, [r11, r9] 226 ldr r6, =0x80000 227 ldr r9, [r8] 228 str r6, [r11, r9] 229set_mmdc_io_lpm_done: 230 231 /* 232 * mask all GPC interrupts before 233 * enabling the RBC counters to 234 * avoid the counter starting too 235 * early if an interupt is already 236 * pending. 237 */ 238 ldr r11, [r0, #PM_INFO_MX6Q_GPC_V_OFFSET] 239 ldr r6, [r11, #MX6Q_GPC_IMR1] 240 ldr r7, [r11, #MX6Q_GPC_IMR2] 241 ldr r8, [r11, #MX6Q_GPC_IMR3] 242 ldr r9, [r11, #MX6Q_GPC_IMR4] 243 244 ldr r10, =0xffffffff 245 str r10, [r11, #MX6Q_GPC_IMR1] 246 str r10, [r11, #MX6Q_GPC_IMR2] 247 str r10, [r11, #MX6Q_GPC_IMR3] 248 str r10, [r11, #MX6Q_GPC_IMR4] 249 250 /* 251 * enable the RBC bypass counter here 252 * to hold off the interrupts. RBC counter 253 * = 32 (1ms), Minimum RBC delay should be 254 * 400us for the analog LDOs to power down. 255 */ 256 ldr r11, [r0, #PM_INFO_MX6Q_CCM_V_OFFSET] 257 ldr r10, [r11, #MX6Q_CCM_CCR] 258 bic r10, r10, #(0x3f << 21) 259 orr r10, r10, #(0x20 << 21) 260 str r10, [r11, #MX6Q_CCM_CCR] 261 262 /* enable the counter. */ 263 ldr r10, [r11, #MX6Q_CCM_CCR] 264 orr r10, r10, #(0x1 << 27) 265 str r10, [r11, #MX6Q_CCM_CCR] 266 267 /* unmask all the GPC interrupts. */ 268 ldr r11, [r0, #PM_INFO_MX6Q_GPC_V_OFFSET] 269 str r6, [r11, #MX6Q_GPC_IMR1] 270 str r7, [r11, #MX6Q_GPC_IMR2] 271 str r8, [r11, #MX6Q_GPC_IMR3] 272 str r9, [r11, #MX6Q_GPC_IMR4] 273 274 /* 275 * now delay for a short while (3usec) 276 * ARM is at 1GHz at this point 277 * so a short loop should be enough. 278 * this delay is required to ensure that 279 * the RBC counter can start counting in 280 * case an interrupt is already pending 281 * or in case an interrupt arrives just 282 * as ARM is about to assert DSM_request. 283 */ 284 ldr r6, =2000 285rbc_loop: 286 subs r6, r6, #0x1 287 bne rbc_loop 288 289 /* Zzz, enter stop mode */ 290 wfi 291 nop 292 nop 293 nop 294 nop 295 296 /* 297 * run to here means there is pending 298 * wakeup source, system should auto 299 * resume, we need to restore MMDC IO first 300 */ 301 mov r5, #0x0 302 resume_mmdc 303 304 /* return to suspend finish */ 305 ret lr 306 307resume: 308 /* invalidate L1 I-cache first */ 309 mov r6, #0x0 310 mcr p15, 0, r6, c7, c5, 0 311 mcr p15, 0, r6, c7, c5, 6 312 /* enable the Icache and branch prediction */ 313 mov r6, #0x1800 314 mcr p15, 0, r6, c1, c0, 0 315 isb 316 317 /* get physical resume address from pm_info. */ 318 ldr lr, [r0, #PM_INFO_RESUME_ADDR_OFFSET] 319 /* clear core0's entry and parameter */ 320 ldr r11, [r0, #PM_INFO_MX6Q_SRC_P_OFFSET] 321 mov r7, #0x0 322 str r7, [r11, #MX6Q_SRC_GPR1] 323 str r7, [r11, #MX6Q_SRC_GPR2] 324 325 ldr r3, [r0, #PM_INFO_CPU_TYPE_OFFSET] 326 mov r5, #0x1 327 resume_mmdc 328 329 ret lr 330ENDPROC(imx6_suspend) 331 332/* 333 * The following code must assume it is running from physical address 334 * where absolute virtual addresses to the data section have to be 335 * turned into relative ones. 336 */ 337 338ENTRY(v7_cpu_resume) 339 bl v7_invalidate_l1 340#ifdef CONFIG_CACHE_L2X0 341 bl l2c310_early_resume 342#endif 343 b cpu_resume 344ENDPROC(v7_cpu_resume) 345