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/* 13 * ==================== low level suspend ==================== 14 * 15 * Better to follow below rules to use ARM registers: 16 * r0: pm_info structure address; 17 * r1 ~ r4: for saving pm_info members; 18 * r5 ~ r10: free registers; 19 * r11: io base address. 20 * 21 * suspend ocram space layout: 22 * ======================== high address ====================== 23 * . 24 * . 25 * . 26 * ^ 27 * ^ 28 * ^ 29 * imx6_suspend code 30 * PM_INFO structure(imx6_cpu_pm_info) 31 * ======================== low address ======================= 32 */ 33 34/* 35 * Below offsets are based on struct imx6_cpu_pm_info 36 * which defined in arch/arm/mach-imx/pm-imx6q.c, this 37 * structure contains necessary pm info for low level 38 * suspend related code. 39 */ 40#define PM_INFO_PBASE_OFFSET 0x0 41#define PM_INFO_RESUME_ADDR_OFFSET 0x4 42#define PM_INFO_DDR_TYPE_OFFSET 0x8 43#define PM_INFO_PM_INFO_SIZE_OFFSET 0xC 44#define PM_INFO_MX6Q_MMDC_P_OFFSET 0x10 45#define PM_INFO_MX6Q_MMDC_V_OFFSET 0x14 46#define PM_INFO_MX6Q_SRC_P_OFFSET 0x18 47#define PM_INFO_MX6Q_SRC_V_OFFSET 0x1C 48#define PM_INFO_MX6Q_IOMUXC_P_OFFSET 0x20 49#define PM_INFO_MX6Q_IOMUXC_V_OFFSET 0x24 50#define PM_INFO_MX6Q_CCM_P_OFFSET 0x28 51#define PM_INFO_MX6Q_CCM_V_OFFSET 0x2C 52#define PM_INFO_MX6Q_GPC_P_OFFSET 0x30 53#define PM_INFO_MX6Q_GPC_V_OFFSET 0x34 54#define PM_INFO_MX6Q_L2_P_OFFSET 0x38 55#define PM_INFO_MX6Q_L2_V_OFFSET 0x3C 56#define PM_INFO_MMDC_IO_NUM_OFFSET 0x40 57#define PM_INFO_MMDC_IO_VAL_OFFSET 0x44 58 59#define MX6Q_SRC_GPR1 0x20 60#define MX6Q_SRC_GPR2 0x24 61#define MX6Q_MMDC_MAPSR 0x404 62#define MX6Q_MMDC_MPDGCTRL0 0x83c 63#define MX6Q_GPC_IMR1 0x08 64#define MX6Q_GPC_IMR2 0x0c 65#define MX6Q_GPC_IMR3 0x10 66#define MX6Q_GPC_IMR4 0x14 67#define MX6Q_CCM_CCR 0x0 68 69 .align 3 70 .arm 71 72 .macro sync_l2_cache 73 74 /* sync L2 cache to drain L2's buffers to DRAM. */ 75#ifdef CONFIG_CACHE_L2X0 76 ldr r11, [r0, #PM_INFO_MX6Q_L2_V_OFFSET] 77 teq r11, #0 78 beq 6f 79 mov r6, #0x0 80 str r6, [r11, #L2X0_CACHE_SYNC] 811: 82 ldr r6, [r11, #L2X0_CACHE_SYNC] 83 ands r6, r6, #0x1 84 bne 1b 856: 86#endif 87 88 .endm 89 90 .macro resume_mmdc 91 92 /* restore MMDC IO */ 93 cmp r5, #0x0 94 ldreq r11, [r0, #PM_INFO_MX6Q_IOMUXC_V_OFFSET] 95 ldrne r11, [r0, #PM_INFO_MX6Q_IOMUXC_P_OFFSET] 96 97 ldr r6, [r0, #PM_INFO_MMDC_IO_NUM_OFFSET] 98 ldr r7, =PM_INFO_MMDC_IO_VAL_OFFSET 99 add r7, r7, r0 1001: 101 ldr r8, [r7], #0x4 102 ldr r9, [r7], #0x4 103 str r9, [r11, r8] 104 subs r6, r6, #0x1 105 bne 1b 106 107 cmp r5, #0x0 108 ldreq r11, [r0, #PM_INFO_MX6Q_MMDC_V_OFFSET] 109 ldrne r11, [r0, #PM_INFO_MX6Q_MMDC_P_OFFSET] 110 111 cmp r3, #IMX_DDR_TYPE_LPDDR2 112 bne 4f 113 114 /* reset read FIFO, RST_RD_FIFO */ 115 ldr r7, =MX6Q_MMDC_MPDGCTRL0 116 ldr r6, [r11, r7] 117 orr r6, r6, #(1 << 31) 118 str r6, [r11, r7] 1192: 120 ldr r6, [r11, r7] 121 ands r6, r6, #(1 << 31) 122 bne 2b 123 124 /* reset FIFO a second time */ 125 ldr r6, [r11, r7] 126 orr r6, r6, #(1 << 31) 127 str r6, [r11, r7] 1283: 129 ldr r6, [r11, r7] 130 ands r6, r6, #(1 << 31) 131 bne 3b 1324: 133 /* let DDR out of self-refresh */ 134 ldr r7, [r11, #MX6Q_MMDC_MAPSR] 135 bic r7, r7, #(1 << 21) 136 str r7, [r11, #MX6Q_MMDC_MAPSR] 1375: 138 ldr r7, [r11, #MX6Q_MMDC_MAPSR] 139 ands r7, r7, #(1 << 25) 140 bne 5b 141 142 /* enable DDR auto power saving */ 143 ldr r7, [r11, #MX6Q_MMDC_MAPSR] 144 bic r7, r7, #0x1 145 str r7, [r11, #MX6Q_MMDC_MAPSR] 146 147 .endm 148 149ENTRY(imx6_suspend) 150 ldr r1, [r0, #PM_INFO_PBASE_OFFSET] 151 ldr r2, [r0, #PM_INFO_RESUME_ADDR_OFFSET] 152 ldr r3, [r0, #PM_INFO_DDR_TYPE_OFFSET] 153 ldr r4, [r0, #PM_INFO_PM_INFO_SIZE_OFFSET] 154 155 /* 156 * counting the resume address in iram 157 * to set it in SRC register. 158 */ 159 ldr r6, =imx6_suspend 160 ldr r7, =resume 161 sub r7, r7, r6 162 add r8, r1, r4 163 add r9, r8, r7 164 165 /* 166 * make sure TLB contain the addr we want, 167 * as we will access them after MMDC IO floated. 168 */ 169 170 ldr r11, [r0, #PM_INFO_MX6Q_CCM_V_OFFSET] 171 ldr r6, [r11, #0x0] 172 ldr r11, [r0, #PM_INFO_MX6Q_GPC_V_OFFSET] 173 ldr r6, [r11, #0x0] 174 ldr r11, [r0, #PM_INFO_MX6Q_IOMUXC_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 /* LPDDR2's last 3 IOs need special setting */ 211 cmp r3, #IMX_DDR_TYPE_LPDDR2 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, #IMX_DDR_TYPE_LPDDR2 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_DDR_TYPE_OFFSET] 326 mov r5, #0x1 327 resume_mmdc 328 329 ret lr 330ENDPROC(imx6_suspend) 331