root/arch/arm/mach-imx/suspend-imx6.S

/* [<][>][^][v][top][bottom][index][help] */
   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]
  80 1:
  81         ldr     r6, [r11, #L2X0_CACHE_SYNC]
  82         ands    r6, r6, #0x1
  83         bne     1b
  84 6:
  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
  99 1:
 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]
 118 2:
 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]
 127 3:
 128         ldr     r6, [r11, r7]
 129         ands    r6, r6, #(1 << 31)
 130         bne     3b
 131 4:
 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]
 136 5:
 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 
 148 ENTRY(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 
 199 poll_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
 212 set_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]
 228 set_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
 284 rbc_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 
 306 resume:
 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
 329 ENDPROC(imx6_suspend)

/* [<][>][^][v][top][bottom][index][help] */