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