1 /* SPDX-License-Identifier: GPL-2.0-or-later */ 2 /* 3 * Copyright 2014 Freescale Semiconductor, Inc. 4 */ 5 6 #include <linux/linkage.h> 7 #include <asm/assembler.h> 8 #include <asm/asm-offsets.h> 9 #include <asm/hardware/cache-l2x0.h> 10 #include "hardware.h" 11 12 .arch armv7-a 13 14 /* 15 * ==================== low level suspend ==================== 16 * 17 * Better to follow below rules to use ARM registers: 18 * r0: pm_info structure address; 19 * r1 ~ r4: for saving pm_info members; 20 * r5 ~ r10: free registers; 21 * r11: io base address. 22 * 23 * suspend ocram space layout: 24 * ======================== high address ====================== 25 * . 26 * . 27 * . 28 * ^ 29 * ^ 30 * ^ 31 * imx6_suspend code 32 * PM_INFO structure(imx6_cpu_pm_info) 33 * ======================== low address ======================= 34 */ 35 36 /* 37 * Below offsets are based on struct imx6_cpu_pm_info 38 * which defined in arch/arm/mach-imx/pm-imx6q.c, this 39 * structure contains necessary pm info for low level 40 * suspend related code. 41 */ 42 #define PM_INFO_PBASE_OFFSET 0x0 43 #define PM_INFO_RESUME_ADDR_OFFSET 0x4 44 #define PM_INFO_DDR_TYPE_OFFSET 0x8 45 #define PM_INFO_PM_INFO_SIZE_OFFSET 0xC 46 #define PM_INFO_MX6Q_MMDC_P_OFFSET 0x10 47 #define PM_INFO_MX6Q_MMDC_V_OFFSET 0x14 48 #define PM_INFO_MX6Q_SRC_P_OFFSET 0x18 49 #define PM_INFO_MX6Q_SRC_V_OFFSET 0x1C 50 #define PM_INFO_MX6Q_IOMUXC_P_OFFSET 0x20 51 #define PM_INFO_MX6Q_IOMUXC_V_OFFSET 0x24 52 #define PM_INFO_MX6Q_CCM_P_OFFSET 0x28 53 #define PM_INFO_MX6Q_CCM_V_OFFSET 0x2C 54 #define PM_INFO_MX6Q_GPC_P_OFFSET 0x30 55 #define PM_INFO_MX6Q_GPC_V_OFFSET 0x34 56 #define PM_INFO_MX6Q_L2_P_OFFSET 0x38 57 #define PM_INFO_MX6Q_L2_V_OFFSET 0x3C 58 #define PM_INFO_MMDC_IO_NUM_OFFSET 0x40 59 #define PM_INFO_MMDC_IO_VAL_OFFSET 0x44 60 61 #define MX6Q_SRC_GPR1 0x20 62 #define MX6Q_SRC_GPR2 0x24 63 #define MX6Q_MMDC_MAPSR 0x404 64 #define MX6Q_MMDC_MPDGCTRL0 0x83c 65 #define MX6Q_GPC_IMR1 0x08 66 #define MX6Q_GPC_IMR2 0x0c 67 #define MX6Q_GPC_IMR3 0x10 68 #define MX6Q_GPC_IMR4 0x14 69 #define MX6Q_CCM_CCR 0x0 70 71 .align 3 72 .arm 73 74 .macro sync_l2_cache 75 76 /* sync L2 cache to drain L2's buffers to DRAM. */ 77 #ifdef CONFIG_CACHE_L2X0 78 ldr r11, [r0, #PM_INFO_MX6Q_L2_V_OFFSET] 79 teq r11, #0 80 beq 6f 81 mov r6, #0x0 82 str r6, [r11, #L2X0_CACHE_SYNC] 83 1: 84 ldr r6, [r11, #L2X0_CACHE_SYNC] 85 ands r6, r6, #0x1 86 bne 1b 87 6: 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 102 1: 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, #IMX_DDR_TYPE_LPDDR2 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] 121 2: 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] 130 3: 131 ldr r6, [r11, r7] 132 ands r6, r6, #(1 << 31) 133 bne 3b 134 4: 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] 139 5: 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 151 ENTRY(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_DDR_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 ldr r11, [r0, #PM_INFO_MX6Q_IOMUXC_V_OFFSET] 177 ldr r6, [r11, #0x0] 178 179 /* use r11 to store the IO address */ 180 ldr r11, [r0, #PM_INFO_MX6Q_SRC_V_OFFSET] 181 /* store physical resume addr and pm_info address. */ 182 str r9, [r11, #MX6Q_SRC_GPR1] 183 str r1, [r11, #MX6Q_SRC_GPR2] 184 185 /* need to sync L2 cache before DSM. */ 186 sync_l2_cache 187 188 ldr r11, [r0, #PM_INFO_MX6Q_MMDC_V_OFFSET] 189 /* 190 * put DDR explicitly into self-refresh and 191 * disable automatic power savings. 192 */ 193 ldr r7, [r11, #MX6Q_MMDC_MAPSR] 194 orr r7, r7, #0x1 195 str r7, [r11, #MX6Q_MMDC_MAPSR] 196 197 /* make the DDR explicitly enter self-refresh. */ 198 ldr r7, [r11, #MX6Q_MMDC_MAPSR] 199 orr r7, r7, #(1 << 21) 200 str r7, [r11, #MX6Q_MMDC_MAPSR] 201 202 poll_dvfs_set: 203 ldr r7, [r11, #MX6Q_MMDC_MAPSR] 204 ands r7, r7, #(1 << 25) 205 beq poll_dvfs_set 206 207 ldr r11, [r0, #PM_INFO_MX6Q_IOMUXC_V_OFFSET] 208 ldr r6, =0x0 209 ldr r7, [r0, #PM_INFO_MMDC_IO_NUM_OFFSET] 210 ldr r8, =PM_INFO_MMDC_IO_VAL_OFFSET 211 add r8, r8, r0 212 /* LPDDR2's last 3 IOs need special setting */ 213 cmp r3, #IMX_DDR_TYPE_LPDDR2 214 subeq r7, r7, #0x3 215 set_mmdc_io_lpm: 216 ldr r9, [r8], #0x8 217 str r6, [r11, r9] 218 subs r7, r7, #0x1 219 bne set_mmdc_io_lpm 220 221 cmp r3, #IMX_DDR_TYPE_LPDDR2 222 bne set_mmdc_io_lpm_done 223 ldr r6, =0x1000 224 ldr r9, [r8], #0x8 225 str r6, [r11, r9] 226 ldr r9, [r8], #0x8 227 str r6, [r11, r9] 228 ldr r6, =0x80000 229 ldr r9, [r8] 230 str r6, [r11, r9] 231 set_mmdc_io_lpm_done: 232 233 /* 234 * mask all GPC interrupts before 235 * enabling the RBC counters to 236 * avoid the counter starting too 237 * early if an interupt is already 238 * pending. 239 */ 240 ldr r11, [r0, #PM_INFO_MX6Q_GPC_V_OFFSET] 241 ldr r6, [r11, #MX6Q_GPC_IMR1] 242 ldr r7, [r11, #MX6Q_GPC_IMR2] 243 ldr r8, [r11, #MX6Q_GPC_IMR3] 244 ldr r9, [r11, #MX6Q_GPC_IMR4] 245 246 ldr r10, =0xffffffff 247 str r10, [r11, #MX6Q_GPC_IMR1] 248 str r10, [r11, #MX6Q_GPC_IMR2] 249 str r10, [r11, #MX6Q_GPC_IMR3] 250 str r10, [r11, #MX6Q_GPC_IMR4] 251 252 /* 253 * enable the RBC bypass counter here 254 * to hold off the interrupts. RBC counter 255 * = 32 (1ms), Minimum RBC delay should be 256 * 400us for the analog LDOs to power down. 257 */ 258 ldr r11, [r0, #PM_INFO_MX6Q_CCM_V_OFFSET] 259 ldr r10, [r11, #MX6Q_CCM_CCR] 260 bic r10, r10, #(0x3f << 21) 261 orr r10, r10, #(0x20 << 21) 262 str r10, [r11, #MX6Q_CCM_CCR] 263 264 /* enable the counter. */ 265 ldr r10, [r11, #MX6Q_CCM_CCR] 266 orr r10, r10, #(0x1 << 27) 267 str r10, [r11, #MX6Q_CCM_CCR] 268 269 /* unmask all the GPC interrupts. */ 270 ldr r11, [r0, #PM_INFO_MX6Q_GPC_V_OFFSET] 271 str r6, [r11, #MX6Q_GPC_IMR1] 272 str r7, [r11, #MX6Q_GPC_IMR2] 273 str r8, [r11, #MX6Q_GPC_IMR3] 274 str r9, [r11, #MX6Q_GPC_IMR4] 275 276 /* 277 * now delay for a short while (3usec) 278 * ARM is at 1GHz at this point 279 * so a short loop should be enough. 280 * this delay is required to ensure that 281 * the RBC counter can start counting in 282 * case an interrupt is already pending 283 * or in case an interrupt arrives just 284 * as ARM is about to assert DSM_request. 285 */ 286 ldr r6, =2000 287 rbc_loop: 288 subs r6, r6, #0x1 289 bne rbc_loop 290 291 /* Zzz, enter stop mode */ 292 wfi 293 nop 294 nop 295 nop 296 nop 297 298 /* 299 * run to here means there is pending 300 * wakeup source, system should auto 301 * resume, we need to restore MMDC IO first 302 */ 303 mov r5, #0x0 304 resume_mmdc 305 306 /* return to suspend finish */ 307 ret lr 308 309 resume: 310 /* invalidate L1 I-cache first */ 311 mov r6, #0x0 312 mcr p15, 0, r6, c7, c5, 0 313 mcr p15, 0, r6, c7, c5, 6 314 /* enable the Icache and branch prediction */ 315 mov r6, #0x1800 316 mcr p15, 0, r6, c1, c0, 0 317 isb 318 319 /* get physical resume address from pm_info. */ 320 ldr lr, [r0, #PM_INFO_RESUME_ADDR_OFFSET] 321 /* clear core0's entry and parameter */ 322 ldr r11, [r0, #PM_INFO_MX6Q_SRC_P_OFFSET] 323 mov r7, #0x0 324 str r7, [r11, #MX6Q_SRC_GPR1] 325 str r7, [r11, #MX6Q_SRC_GPR2] 326 327 ldr r3, [r0, #PM_INFO_DDR_TYPE_OFFSET] 328 mov r5, #0x1 329 resume_mmdc 330 331 ret lr 332 ENDPROC(imx6_suspend)
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.