1fcaf2036SThomas Gleixner/* SPDX-License-Identifier: GPL-2.0-or-later */ 2df595746SAnson Huang/* 3df595746SAnson Huang * Copyright 2014 Freescale Semiconductor, Inc. 4df595746SAnson Huang */ 5df595746SAnson Huang 6df595746SAnson Huang#include <linux/linkage.h> 76ebbf2ceSRussell King#include <asm/assembler.h> 8c356bdb4SShawn Guo#include <asm/asm-offsets.h> 9df595746SAnson Huang#include <asm/hardware/cache-l2x0.h> 10df595746SAnson Huang#include "hardware.h" 11df595746SAnson Huang 12*a2faac39SNick Desaulniers.arch armv7-a 13*a2faac39SNick Desaulniers 14df595746SAnson Huang/* 15df595746SAnson Huang * ==================== low level suspend ==================== 16df595746SAnson Huang * 17df595746SAnson Huang * Better to follow below rules to use ARM registers: 18df595746SAnson Huang * r0: pm_info structure address; 19df595746SAnson Huang * r1 ~ r4: for saving pm_info members; 20df595746SAnson Huang * r5 ~ r10: free registers; 21df595746SAnson Huang * r11: io base address. 22df595746SAnson Huang * 23df595746SAnson Huang * suspend ocram space layout: 24df595746SAnson Huang * ======================== high address ====================== 25df595746SAnson Huang * . 26df595746SAnson Huang * . 27df595746SAnson Huang * . 28df595746SAnson Huang * ^ 29df595746SAnson Huang * ^ 30df595746SAnson Huang * ^ 31df595746SAnson Huang * imx6_suspend code 32df595746SAnson Huang * PM_INFO structure(imx6_cpu_pm_info) 33df595746SAnson Huang * ======================== low address ======================= 34df595746SAnson Huang */ 35df595746SAnson Huang 36df595746SAnson Huang/* 37df595746SAnson Huang * Below offsets are based on struct imx6_cpu_pm_info 38df595746SAnson Huang * which defined in arch/arm/mach-imx/pm-imx6q.c, this 39df595746SAnson Huang * structure contains necessary pm info for low level 40df595746SAnson Huang * suspend related code. 41df595746SAnson Huang */ 42df595746SAnson Huang#define PM_INFO_PBASE_OFFSET 0x0 43df595746SAnson Huang#define PM_INFO_RESUME_ADDR_OFFSET 0x4 44ec336b28SAnson Huang#define PM_INFO_DDR_TYPE_OFFSET 0x8 45df595746SAnson Huang#define PM_INFO_PM_INFO_SIZE_OFFSET 0xC 46df595746SAnson Huang#define PM_INFO_MX6Q_MMDC_P_OFFSET 0x10 47df595746SAnson Huang#define PM_INFO_MX6Q_MMDC_V_OFFSET 0x14 48df595746SAnson Huang#define PM_INFO_MX6Q_SRC_P_OFFSET 0x18 49df595746SAnson Huang#define PM_INFO_MX6Q_SRC_V_OFFSET 0x1C 50df595746SAnson Huang#define PM_INFO_MX6Q_IOMUXC_P_OFFSET 0x20 51df595746SAnson Huang#define PM_INFO_MX6Q_IOMUXC_V_OFFSET 0x24 52df595746SAnson Huang#define PM_INFO_MX6Q_CCM_P_OFFSET 0x28 53df595746SAnson Huang#define PM_INFO_MX6Q_CCM_V_OFFSET 0x2C 54df595746SAnson Huang#define PM_INFO_MX6Q_GPC_P_OFFSET 0x30 55df595746SAnson Huang#define PM_INFO_MX6Q_GPC_V_OFFSET 0x34 56df595746SAnson Huang#define PM_INFO_MX6Q_L2_P_OFFSET 0x38 57df595746SAnson Huang#define PM_INFO_MX6Q_L2_V_OFFSET 0x3C 58df595746SAnson Huang#define PM_INFO_MMDC_IO_NUM_OFFSET 0x40 59df595746SAnson Huang#define PM_INFO_MMDC_IO_VAL_OFFSET 0x44 60df595746SAnson Huang 61df595746SAnson Huang#define MX6Q_SRC_GPR1 0x20 62df595746SAnson Huang#define MX6Q_SRC_GPR2 0x24 63df595746SAnson Huang#define MX6Q_MMDC_MAPSR 0x404 6464b08681SAnson Huang#define MX6Q_MMDC_MPDGCTRL0 0x83c 65df595746SAnson Huang#define MX6Q_GPC_IMR1 0x08 66df595746SAnson Huang#define MX6Q_GPC_IMR2 0x0c 67df595746SAnson Huang#define MX6Q_GPC_IMR3 0x10 68df595746SAnson Huang#define MX6Q_GPC_IMR4 0x14 69df595746SAnson Huang#define MX6Q_CCM_CCR 0x0 70df595746SAnson Huang 71df595746SAnson Huang .align 3 72a88afa46SMax Krummenacher .arm 73df595746SAnson Huang 74df595746SAnson Huang .macro sync_l2_cache 75df595746SAnson Huang 76df595746SAnson Huang /* sync L2 cache to drain L2's buffers to DRAM. */ 77df595746SAnson Huang#ifdef CONFIG_CACHE_L2X0 78df595746SAnson Huang ldr r11, [r0, #PM_INFO_MX6Q_L2_V_OFFSET] 79ee4a5f83SAnson Huang teq r11, #0 80ee4a5f83SAnson Huang beq 6f 81df595746SAnson Huang mov r6, #0x0 82df595746SAnson Huang str r6, [r11, #L2X0_CACHE_SYNC] 83df595746SAnson Huang1: 84df595746SAnson Huang ldr r6, [r11, #L2X0_CACHE_SYNC] 85df595746SAnson Huang ands r6, r6, #0x1 86df595746SAnson Huang bne 1b 87ee4a5f83SAnson Huang6: 88df595746SAnson Huang#endif 89df595746SAnson Huang 90df595746SAnson Huang .endm 91df595746SAnson Huang 92df595746SAnson Huang .macro resume_mmdc 93df595746SAnson Huang 94df595746SAnson Huang /* restore MMDC IO */ 95df595746SAnson Huang cmp r5, #0x0 96df595746SAnson Huang ldreq r11, [r0, #PM_INFO_MX6Q_IOMUXC_V_OFFSET] 97df595746SAnson Huang ldrne r11, [r0, #PM_INFO_MX6Q_IOMUXC_P_OFFSET] 98df595746SAnson Huang 99df595746SAnson Huang ldr r6, [r0, #PM_INFO_MMDC_IO_NUM_OFFSET] 100df595746SAnson Huang ldr r7, =PM_INFO_MMDC_IO_VAL_OFFSET 101df595746SAnson Huang add r7, r7, r0 102df595746SAnson Huang1: 103df595746SAnson Huang ldr r8, [r7], #0x4 104df595746SAnson Huang ldr r9, [r7], #0x4 105df595746SAnson Huang str r9, [r11, r8] 106df595746SAnson Huang subs r6, r6, #0x1 107df595746SAnson Huang bne 1b 108df595746SAnson Huang 109df595746SAnson Huang cmp r5, #0x0 110df595746SAnson Huang ldreq r11, [r0, #PM_INFO_MX6Q_MMDC_V_OFFSET] 111df595746SAnson Huang ldrne r11, [r0, #PM_INFO_MX6Q_MMDC_P_OFFSET] 112df595746SAnson Huang 113ec336b28SAnson Huang cmp r3, #IMX_DDR_TYPE_LPDDR2 11464b08681SAnson Huang bne 4f 11564b08681SAnson Huang 11664b08681SAnson Huang /* reset read FIFO, RST_RD_FIFO */ 11764b08681SAnson Huang ldr r7, =MX6Q_MMDC_MPDGCTRL0 11864b08681SAnson Huang ldr r6, [r11, r7] 11964b08681SAnson Huang orr r6, r6, #(1 << 31) 12064b08681SAnson Huang str r6, [r11, r7] 12164b08681SAnson Huang2: 12264b08681SAnson Huang ldr r6, [r11, r7] 12364b08681SAnson Huang ands r6, r6, #(1 << 31) 12464b08681SAnson Huang bne 2b 12564b08681SAnson Huang 12664b08681SAnson Huang /* reset FIFO a second time */ 12764b08681SAnson Huang ldr r6, [r11, r7] 12864b08681SAnson Huang orr r6, r6, #(1 << 31) 12964b08681SAnson Huang str r6, [r11, r7] 13064b08681SAnson Huang3: 13164b08681SAnson Huang ldr r6, [r11, r7] 13264b08681SAnson Huang ands r6, r6, #(1 << 31) 13364b08681SAnson Huang bne 3b 13464b08681SAnson Huang4: 135df595746SAnson Huang /* let DDR out of self-refresh */ 136df595746SAnson Huang ldr r7, [r11, #MX6Q_MMDC_MAPSR] 137df595746SAnson Huang bic r7, r7, #(1 << 21) 138df595746SAnson Huang str r7, [r11, #MX6Q_MMDC_MAPSR] 13964b08681SAnson Huang5: 140df595746SAnson Huang ldr r7, [r11, #MX6Q_MMDC_MAPSR] 141df595746SAnson Huang ands r7, r7, #(1 << 25) 14264b08681SAnson Huang bne 5b 143df595746SAnson Huang 144df595746SAnson Huang /* enable DDR auto power saving */ 145df595746SAnson Huang ldr r7, [r11, #MX6Q_MMDC_MAPSR] 146df595746SAnson Huang bic r7, r7, #0x1 147df595746SAnson Huang str r7, [r11, #MX6Q_MMDC_MAPSR] 148df595746SAnson Huang 149df595746SAnson Huang .endm 150df595746SAnson Huang 151df595746SAnson HuangENTRY(imx6_suspend) 152df595746SAnson Huang ldr r1, [r0, #PM_INFO_PBASE_OFFSET] 153df595746SAnson Huang ldr r2, [r0, #PM_INFO_RESUME_ADDR_OFFSET] 154ec336b28SAnson Huang ldr r3, [r0, #PM_INFO_DDR_TYPE_OFFSET] 155df595746SAnson Huang ldr r4, [r0, #PM_INFO_PM_INFO_SIZE_OFFSET] 156df595746SAnson Huang 157df595746SAnson Huang /* 158df595746SAnson Huang * counting the resume address in iram 159df595746SAnson Huang * to set it in SRC register. 160df595746SAnson Huang */ 161df595746SAnson Huang ldr r6, =imx6_suspend 162df595746SAnson Huang ldr r7, =resume 163df595746SAnson Huang sub r7, r7, r6 164df595746SAnson Huang add r8, r1, r4 165df595746SAnson Huang add r9, r8, r7 166df595746SAnson Huang 167df595746SAnson Huang /* 168df595746SAnson Huang * make sure TLB contain the addr we want, 169df595746SAnson Huang * as we will access them after MMDC IO floated. 170df595746SAnson Huang */ 171df595746SAnson Huang 172df595746SAnson Huang ldr r11, [r0, #PM_INFO_MX6Q_CCM_V_OFFSET] 173df595746SAnson Huang ldr r6, [r11, #0x0] 174df595746SAnson Huang ldr r11, [r0, #PM_INFO_MX6Q_GPC_V_OFFSET] 175df595746SAnson Huang ldr r6, [r11, #0x0] 17659d05b51SShawn Guo ldr r11, [r0, #PM_INFO_MX6Q_IOMUXC_V_OFFSET] 17759d05b51SShawn Guo ldr r6, [r11, #0x0] 178df595746SAnson Huang 179df595746SAnson Huang /* use r11 to store the IO address */ 180df595746SAnson Huang ldr r11, [r0, #PM_INFO_MX6Q_SRC_V_OFFSET] 181df595746SAnson Huang /* store physical resume addr and pm_info address. */ 182df595746SAnson Huang str r9, [r11, #MX6Q_SRC_GPR1] 183df595746SAnson Huang str r1, [r11, #MX6Q_SRC_GPR2] 184df595746SAnson Huang 185df595746SAnson Huang /* need to sync L2 cache before DSM. */ 186df595746SAnson Huang sync_l2_cache 187df595746SAnson Huang 188df595746SAnson Huang ldr r11, [r0, #PM_INFO_MX6Q_MMDC_V_OFFSET] 189df595746SAnson Huang /* 190df595746SAnson Huang * put DDR explicitly into self-refresh and 191df595746SAnson Huang * disable automatic power savings. 192df595746SAnson Huang */ 193df595746SAnson Huang ldr r7, [r11, #MX6Q_MMDC_MAPSR] 194df595746SAnson Huang orr r7, r7, #0x1 195df595746SAnson Huang str r7, [r11, #MX6Q_MMDC_MAPSR] 196df595746SAnson Huang 197df595746SAnson Huang /* make the DDR explicitly enter self-refresh. */ 198df595746SAnson Huang ldr r7, [r11, #MX6Q_MMDC_MAPSR] 199df595746SAnson Huang orr r7, r7, #(1 << 21) 200df595746SAnson Huang str r7, [r11, #MX6Q_MMDC_MAPSR] 201df595746SAnson Huang 202df595746SAnson Huangpoll_dvfs_set: 203df595746SAnson Huang ldr r7, [r11, #MX6Q_MMDC_MAPSR] 204df595746SAnson Huang ands r7, r7, #(1 << 25) 205df595746SAnson Huang beq poll_dvfs_set 206df595746SAnson Huang 207df595746SAnson Huang ldr r11, [r0, #PM_INFO_MX6Q_IOMUXC_V_OFFSET] 208df595746SAnson Huang ldr r6, =0x0 209df595746SAnson Huang ldr r7, [r0, #PM_INFO_MMDC_IO_NUM_OFFSET] 210df595746SAnson Huang ldr r8, =PM_INFO_MMDC_IO_VAL_OFFSET 211df595746SAnson Huang add r8, r8, r0 212ec336b28SAnson Huang /* LPDDR2's last 3 IOs need special setting */ 213ec336b28SAnson Huang cmp r3, #IMX_DDR_TYPE_LPDDR2 21464b08681SAnson Huang subeq r7, r7, #0x3 215df595746SAnson Huangset_mmdc_io_lpm: 216df595746SAnson Huang ldr r9, [r8], #0x8 217df595746SAnson Huang str r6, [r11, r9] 218df595746SAnson Huang subs r7, r7, #0x1 219df595746SAnson Huang bne set_mmdc_io_lpm 220df595746SAnson Huang 221ec336b28SAnson Huang cmp r3, #IMX_DDR_TYPE_LPDDR2 22264b08681SAnson Huang bne set_mmdc_io_lpm_done 22364b08681SAnson Huang ldr r6, =0x1000 22464b08681SAnson Huang ldr r9, [r8], #0x8 22564b08681SAnson Huang str r6, [r11, r9] 22664b08681SAnson Huang ldr r9, [r8], #0x8 22764b08681SAnson Huang str r6, [r11, r9] 22864b08681SAnson Huang ldr r6, =0x80000 22964b08681SAnson Huang ldr r9, [r8] 23064b08681SAnson Huang str r6, [r11, r9] 23164b08681SAnson Huangset_mmdc_io_lpm_done: 23264b08681SAnson Huang 233df595746SAnson Huang /* 234df595746SAnson Huang * mask all GPC interrupts before 235df595746SAnson Huang * enabling the RBC counters to 236df595746SAnson Huang * avoid the counter starting too 237df595746SAnson Huang * early if an interupt is already 238df595746SAnson Huang * pending. 239df595746SAnson Huang */ 240df595746SAnson Huang ldr r11, [r0, #PM_INFO_MX6Q_GPC_V_OFFSET] 241df595746SAnson Huang ldr r6, [r11, #MX6Q_GPC_IMR1] 242df595746SAnson Huang ldr r7, [r11, #MX6Q_GPC_IMR2] 243df595746SAnson Huang ldr r8, [r11, #MX6Q_GPC_IMR3] 244df595746SAnson Huang ldr r9, [r11, #MX6Q_GPC_IMR4] 245df595746SAnson Huang 246df595746SAnson Huang ldr r10, =0xffffffff 247df595746SAnson Huang str r10, [r11, #MX6Q_GPC_IMR1] 248df595746SAnson Huang str r10, [r11, #MX6Q_GPC_IMR2] 249df595746SAnson Huang str r10, [r11, #MX6Q_GPC_IMR3] 250df595746SAnson Huang str r10, [r11, #MX6Q_GPC_IMR4] 251df595746SAnson Huang 252df595746SAnson Huang /* 253df595746SAnson Huang * enable the RBC bypass counter here 254df595746SAnson Huang * to hold off the interrupts. RBC counter 255df595746SAnson Huang * = 32 (1ms), Minimum RBC delay should be 256df595746SAnson Huang * 400us for the analog LDOs to power down. 257df595746SAnson Huang */ 258df595746SAnson Huang ldr r11, [r0, #PM_INFO_MX6Q_CCM_V_OFFSET] 259df595746SAnson Huang ldr r10, [r11, #MX6Q_CCM_CCR] 260df595746SAnson Huang bic r10, r10, #(0x3f << 21) 261df595746SAnson Huang orr r10, r10, #(0x20 << 21) 262df595746SAnson Huang str r10, [r11, #MX6Q_CCM_CCR] 263df595746SAnson Huang 264df595746SAnson Huang /* enable the counter. */ 265df595746SAnson Huang ldr r10, [r11, #MX6Q_CCM_CCR] 266df595746SAnson Huang orr r10, r10, #(0x1 << 27) 267df595746SAnson Huang str r10, [r11, #MX6Q_CCM_CCR] 268df595746SAnson Huang 269df595746SAnson Huang /* unmask all the GPC interrupts. */ 270df595746SAnson Huang ldr r11, [r0, #PM_INFO_MX6Q_GPC_V_OFFSET] 271df595746SAnson Huang str r6, [r11, #MX6Q_GPC_IMR1] 272df595746SAnson Huang str r7, [r11, #MX6Q_GPC_IMR2] 273df595746SAnson Huang str r8, [r11, #MX6Q_GPC_IMR3] 274df595746SAnson Huang str r9, [r11, #MX6Q_GPC_IMR4] 275df595746SAnson Huang 276df595746SAnson Huang /* 277df595746SAnson Huang * now delay for a short while (3usec) 278df595746SAnson Huang * ARM is at 1GHz at this point 279df595746SAnson Huang * so a short loop should be enough. 280df595746SAnson Huang * this delay is required to ensure that 281df595746SAnson Huang * the RBC counter can start counting in 282df595746SAnson Huang * case an interrupt is already pending 283df595746SAnson Huang * or in case an interrupt arrives just 284df595746SAnson Huang * as ARM is about to assert DSM_request. 285df595746SAnson Huang */ 286df595746SAnson Huang ldr r6, =2000 287df595746SAnson Huangrbc_loop: 288df595746SAnson Huang subs r6, r6, #0x1 289df595746SAnson Huang bne rbc_loop 290df595746SAnson Huang 291df595746SAnson Huang /* Zzz, enter stop mode */ 292df595746SAnson Huang wfi 293df595746SAnson Huang nop 294df595746SAnson Huang nop 295df595746SAnson Huang nop 296df595746SAnson Huang nop 297df595746SAnson Huang 298df595746SAnson Huang /* 299df595746SAnson Huang * run to here means there is pending 300df595746SAnson Huang * wakeup source, system should auto 301df595746SAnson Huang * resume, we need to restore MMDC IO first 302df595746SAnson Huang */ 303df595746SAnson Huang mov r5, #0x0 304df595746SAnson Huang resume_mmdc 305df595746SAnson Huang 306df595746SAnson Huang /* return to suspend finish */ 3076ebbf2ceSRussell King ret lr 308df595746SAnson Huang 309df595746SAnson Huangresume: 310df595746SAnson Huang /* invalidate L1 I-cache first */ 311df595746SAnson Huang mov r6, #0x0 312df595746SAnson Huang mcr p15, 0, r6, c7, c5, 0 313df595746SAnson Huang mcr p15, 0, r6, c7, c5, 6 314df595746SAnson Huang /* enable the Icache and branch prediction */ 315df595746SAnson Huang mov r6, #0x1800 316df595746SAnson Huang mcr p15, 0, r6, c1, c0, 0 317df595746SAnson Huang isb 318df595746SAnson Huang 319df595746SAnson Huang /* get physical resume address from pm_info. */ 320df595746SAnson Huang ldr lr, [r0, #PM_INFO_RESUME_ADDR_OFFSET] 321df595746SAnson Huang /* clear core0's entry and parameter */ 322df595746SAnson Huang ldr r11, [r0, #PM_INFO_MX6Q_SRC_P_OFFSET] 323df595746SAnson Huang mov r7, #0x0 324df595746SAnson Huang str r7, [r11, #MX6Q_SRC_GPR1] 325df595746SAnson Huang str r7, [r11, #MX6Q_SRC_GPR2] 326df595746SAnson Huang 327ec336b28SAnson Huang ldr r3, [r0, #PM_INFO_DDR_TYPE_OFFSET] 328df595746SAnson Huang mov r5, #0x1 329df595746SAnson Huang resume_mmdc 330df595746SAnson Huang 3316ebbf2ceSRussell King ret lr 332df595746SAnson HuangENDPROC(imx6_suspend) 333