linux/arch/arm/mach-imx/suspend-imx6.S
<<
>>
Prefs
   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