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 71 .macro sync_l2_cache 72 73 /* sync L2 cache to drain L2's buffers to DRAM. */ 74#ifdef CONFIG_CACHE_L2X0 75 ldr r11, [r0, #PM_INFO_MX6Q_L2_V_OFFSET] 76 teq r11, #0 77 beq 6f 78 mov r6, #0x0 79 str r6, [r11, #L2X0_CACHE_SYNC] 801: 81 ldr r6, [r11, #L2X0_CACHE_SYNC] 82 ands r6, r6, #0x1 83 bne 1b 846: 85#endif 86 87 .endm 88 89 .macro resume_mmdc 90 91 /* restore MMDC IO */ 92 cmp r5, #0x0 93 ldreq r11, [r0, #PM_INFO_MX6Q_IOMUXC_V_OFFSET] 94 ldrne r11, [r0, #PM_INFO_MX6Q_IOMUXC_P_OFFSET] 95 96 ldr r6, [r0, #PM_INFO_MMDC_IO_NUM_OFFSET] 97 ldr r7, =PM_INFO_MMDC_IO_VAL_OFFSET 98 add r7, r7, r0 991: 100 ldr r8, [r7], #0x4 101 ldr r9, [r7], #0x4 102 str r9, [r11, r8] 103 subs r6, r6, #0x1 104 bne 1b 105 106 cmp r5, #0x0 107 ldreq r11, [r0, #PM_INFO_MX6Q_MMDC_V_OFFSET] 108 ldrne r11, [r0, #PM_INFO_MX6Q_MMDC_P_OFFSET] 109 110 cmp r3, #IMX_DDR_TYPE_LPDDR2 111 bne 4f 112 113 /* reset read FIFO, RST_RD_FIFO */ 114 ldr r7, =MX6Q_MMDC_MPDGCTRL0 115 ldr r6, [r11, r7] 116 orr r6, r6, #(1 << 31) 117 str r6, [r11, r7] 1182: 119 ldr r6, [r11, r7] 120 ands r6, r6, #(1 << 31) 121 bne 2b 122 123 /* reset FIFO a second time */ 124 ldr r6, [r11, r7] 125 orr r6, r6, #(1 << 31) 126 str r6, [r11, r7] 1273: 128 ldr r6, [r11, r7] 129 ands r6, r6, #(1 << 31) 130 bne 3b 1314: 132 /* let DDR out of self-refresh */ 133 ldr r7, [r11, #MX6Q_MMDC_MAPSR] 134 bic r7, r7, #(1 << 21) 135 str r7, [r11, #MX6Q_MMDC_MAPSR] 1365: 137 ldr r7, [r11, #MX6Q_MMDC_MAPSR] 138 ands r7, r7, #(1 << 25) 139 bne 5b 140 141 /* enable DDR auto power saving */ 142 ldr r7, [r11, #MX6Q_MMDC_MAPSR] 143 bic r7, r7, #0x1 144 str r7, [r11, #MX6Q_MMDC_MAPSR] 145 146 .endm 147 148ENTRY(imx6_suspend) 149 ldr r1, [r0, #PM_INFO_PBASE_OFFSET] 150 ldr r2, [r0, #PM_INFO_RESUME_ADDR_OFFSET] 151 ldr r3, [r0, #PM_INFO_DDR_TYPE_OFFSET] 152 ldr r4, [r0, #PM_INFO_PM_INFO_SIZE_OFFSET] 153 154 /* 155 * counting the resume address in iram 156 * to set it in SRC register. 157 */ 158 ldr r6, =imx6_suspend 159 ldr r7, =resume 160 sub r7, r7, r6 161 add r8, r1, r4 162 add r9, r8, r7 163 164 /* 165 * make sure TLB contain the addr we want, 166 * as we will access them after MMDC IO floated. 167 */ 168 169 ldr r11, [r0, #PM_INFO_MX6Q_CCM_V_OFFSET] 170 ldr r6, [r11, #0x0] 171 ldr r11, [r0, #PM_INFO_MX6Q_GPC_V_OFFSET] 172 ldr r6, [r11, #0x0] 173 ldr r11, [r0, #PM_INFO_MX6Q_IOMUXC_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 /* LPDDR2's last 3 IOs need special setting */ 210 cmp r3, #IMX_DDR_TYPE_LPDDR2 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, #IMX_DDR_TYPE_LPDDR2 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 ret 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_DDR_TYPE_OFFSET] 325 mov r5, #0x1 326 resume_mmdc 327 328 ret lr 329ENDPROC(imx6_suspend) 330