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_DDR_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, #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] 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_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 202poll_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 215set_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] 231set_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 287rbc_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 309resume: 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 332ENDPROC(imx6_suspend) 333 334/* 335 * The following code must assume it is running from physical address 336 * where absolute virtual addresses to the data section have to be 337 * turned into relative ones. 338 */ 339 340ENTRY(v7_cpu_resume) 341 bl v7_invalidate_l1 342#ifdef CONFIG_CACHE_L2X0 343 bl l2c310_early_resume 344#endif 345 b cpu_resume 346ENDPROC(v7_cpu_resume) 347