Back to home page

OSCL-LXR

 
 

    


0001 /* SPDX-License-Identifier: GPL-2.0-or-later */
0002 /*
0003  * Copyright 2014 Freescale Semiconductor, Inc.
0004  */
0005 
0006 #include <linux/linkage.h>
0007 #include <asm/assembler.h>
0008 #include <asm/asm-offsets.h>
0009 #include <asm/hardware/cache-l2x0.h>
0010 #include "hardware.h"
0011 
0012 /*
0013  * ==================== low level suspend ====================
0014  *
0015  * Better to follow below rules to use ARM registers:
0016  * r0: pm_info structure address;
0017  * r1 ~ r4: for saving pm_info members;
0018  * r5 ~ r10: free registers;
0019  * r11: io base address.
0020  *
0021  * suspend ocram space layout:
0022  * ======================== high address ======================
0023  *                              .
0024  *                              .
0025  *                              .
0026  *                              ^
0027  *                              ^
0028  *                              ^
0029  *                      imx6_suspend code
0030  *              PM_INFO structure(imx6_cpu_pm_info)
0031  * ======================== low address =======================
0032  */
0033 
0034 /*
0035  * Below offsets are based on struct imx6_cpu_pm_info
0036  * which defined in arch/arm/mach-imx/pm-imx6q.c, this
0037  * structure contains necessary pm info for low level
0038  * suspend related code.
0039  */
0040 #define PM_INFO_PBASE_OFFSET            0x0
0041 #define PM_INFO_RESUME_ADDR_OFFSET      0x4
0042 #define PM_INFO_DDR_TYPE_OFFSET         0x8
0043 #define PM_INFO_PM_INFO_SIZE_OFFSET     0xC
0044 #define PM_INFO_MX6Q_MMDC_P_OFFSET      0x10
0045 #define PM_INFO_MX6Q_MMDC_V_OFFSET      0x14
0046 #define PM_INFO_MX6Q_SRC_P_OFFSET       0x18
0047 #define PM_INFO_MX6Q_SRC_V_OFFSET       0x1C
0048 #define PM_INFO_MX6Q_IOMUXC_P_OFFSET        0x20
0049 #define PM_INFO_MX6Q_IOMUXC_V_OFFSET        0x24
0050 #define PM_INFO_MX6Q_CCM_P_OFFSET       0x28
0051 #define PM_INFO_MX6Q_CCM_V_OFFSET       0x2C
0052 #define PM_INFO_MX6Q_GPC_P_OFFSET       0x30
0053 #define PM_INFO_MX6Q_GPC_V_OFFSET       0x34
0054 #define PM_INFO_MX6Q_L2_P_OFFSET        0x38
0055 #define PM_INFO_MX6Q_L2_V_OFFSET        0x3C
0056 #define PM_INFO_MMDC_IO_NUM_OFFSET      0x40
0057 #define PM_INFO_MMDC_IO_VAL_OFFSET      0x44
0058 
0059 #define MX6Q_SRC_GPR1   0x20
0060 #define MX6Q_SRC_GPR2   0x24
0061 #define MX6Q_MMDC_MAPSR 0x404
0062 #define MX6Q_MMDC_MPDGCTRL0 0x83c
0063 #define MX6Q_GPC_IMR1   0x08
0064 #define MX6Q_GPC_IMR2   0x0c
0065 #define MX6Q_GPC_IMR3   0x10
0066 #define MX6Q_GPC_IMR4   0x14
0067 #define MX6Q_CCM_CCR    0x0
0068 
0069     .align 3
0070     .arm
0071 
0072     .macro  sync_l2_cache
0073 
0074     /* sync L2 cache to drain L2's buffers to DRAM. */
0075 #ifdef CONFIG_CACHE_L2X0
0076     ldr r11, [r0, #PM_INFO_MX6Q_L2_V_OFFSET]
0077     teq r11, #0
0078     beq 6f
0079     mov r6, #0x0
0080     str r6, [r11, #L2X0_CACHE_SYNC]
0081 1:
0082     ldr r6, [r11, #L2X0_CACHE_SYNC]
0083     ands    r6, r6, #0x1
0084     bne 1b
0085 6:
0086 #endif
0087 
0088     .endm
0089 
0090     .macro  resume_mmdc
0091 
0092     /* restore MMDC IO */
0093     cmp r5, #0x0
0094     ldreq   r11, [r0, #PM_INFO_MX6Q_IOMUXC_V_OFFSET]
0095     ldrne   r11, [r0, #PM_INFO_MX6Q_IOMUXC_P_OFFSET]
0096 
0097     ldr r6, [r0, #PM_INFO_MMDC_IO_NUM_OFFSET]
0098     ldr r7, =PM_INFO_MMDC_IO_VAL_OFFSET
0099     add r7, r7, r0
0100 1:
0101     ldr r8, [r7], #0x4
0102     ldr r9, [r7], #0x4
0103     str r9, [r11, r8]
0104     subs    r6, r6, #0x1
0105     bne 1b
0106 
0107     cmp r5, #0x0
0108     ldreq   r11, [r0, #PM_INFO_MX6Q_MMDC_V_OFFSET]
0109     ldrne   r11, [r0, #PM_INFO_MX6Q_MMDC_P_OFFSET]
0110 
0111     cmp r3, #IMX_DDR_TYPE_LPDDR2
0112     bne 4f
0113 
0114     /* reset read FIFO, RST_RD_FIFO */
0115     ldr r7, =MX6Q_MMDC_MPDGCTRL0
0116     ldr r6, [r11, r7]
0117     orr     r6, r6, #(1 << 31)
0118     str r6, [r11, r7]
0119 2:
0120     ldr r6, [r11, r7]
0121     ands    r6, r6, #(1 << 31)
0122     bne 2b
0123 
0124     /* reset FIFO a second time */
0125     ldr r6, [r11, r7]
0126     orr     r6, r6, #(1 << 31)
0127     str r6, [r11, r7]
0128 3:
0129     ldr r6, [r11, r7]
0130     ands    r6, r6, #(1 << 31)
0131     bne 3b
0132 4:
0133     /* let DDR out of self-refresh */
0134     ldr r7, [r11, #MX6Q_MMDC_MAPSR]
0135     bic r7, r7, #(1 << 21)
0136     str r7, [r11, #MX6Q_MMDC_MAPSR]
0137 5:
0138     ldr r7, [r11, #MX6Q_MMDC_MAPSR]
0139     ands    r7, r7, #(1 << 25)
0140     bne 5b
0141 
0142     /* enable DDR auto power saving */
0143     ldr r7, [r11, #MX6Q_MMDC_MAPSR]
0144     bic r7, r7, #0x1
0145     str r7, [r11, #MX6Q_MMDC_MAPSR]
0146 
0147     .endm
0148 
0149 ENTRY(imx6_suspend)
0150     ldr r1, [r0, #PM_INFO_PBASE_OFFSET]
0151     ldr r2, [r0, #PM_INFO_RESUME_ADDR_OFFSET]
0152     ldr r3, [r0, #PM_INFO_DDR_TYPE_OFFSET]
0153     ldr r4, [r0, #PM_INFO_PM_INFO_SIZE_OFFSET]
0154 
0155     /*
0156      * counting the resume address in iram
0157      * to set it in SRC register.
0158      */
0159     ldr r6, =imx6_suspend
0160     ldr r7, =resume
0161     sub r7, r7, r6
0162     add r8, r1, r4
0163     add r9, r8, r7
0164 
0165     /*
0166      * make sure TLB contain the addr we want,
0167      * as we will access them after MMDC IO floated.
0168      */
0169 
0170     ldr r11, [r0, #PM_INFO_MX6Q_CCM_V_OFFSET]
0171     ldr r6, [r11, #0x0]
0172     ldr r11, [r0, #PM_INFO_MX6Q_GPC_V_OFFSET]
0173     ldr r6, [r11, #0x0]
0174     ldr r11, [r0, #PM_INFO_MX6Q_IOMUXC_V_OFFSET]
0175     ldr r6, [r11, #0x0]
0176 
0177     /* use r11 to store the IO address */
0178     ldr r11, [r0, #PM_INFO_MX6Q_SRC_V_OFFSET]
0179     /* store physical resume addr and pm_info address. */
0180     str r9, [r11, #MX6Q_SRC_GPR1]
0181     str r1, [r11, #MX6Q_SRC_GPR2]
0182 
0183     /* need to sync L2 cache before DSM. */
0184     sync_l2_cache
0185 
0186     ldr r11, [r0, #PM_INFO_MX6Q_MMDC_V_OFFSET]
0187     /*
0188      * put DDR explicitly into self-refresh and
0189      * disable automatic power savings.
0190      */
0191     ldr r7, [r11, #MX6Q_MMDC_MAPSR]
0192     orr r7, r7, #0x1
0193     str r7, [r11, #MX6Q_MMDC_MAPSR]
0194 
0195     /* make the DDR explicitly enter self-refresh. */
0196     ldr r7, [r11, #MX6Q_MMDC_MAPSR]
0197     orr r7, r7, #(1 << 21)
0198     str r7, [r11, #MX6Q_MMDC_MAPSR]
0199 
0200 poll_dvfs_set:
0201     ldr r7, [r11, #MX6Q_MMDC_MAPSR]
0202     ands    r7, r7, #(1 << 25)
0203     beq poll_dvfs_set
0204 
0205     ldr r11, [r0, #PM_INFO_MX6Q_IOMUXC_V_OFFSET]
0206     ldr r6, =0x0
0207     ldr r7, [r0, #PM_INFO_MMDC_IO_NUM_OFFSET]
0208     ldr r8, =PM_INFO_MMDC_IO_VAL_OFFSET
0209     add r8, r8, r0
0210     /* LPDDR2's last 3 IOs need special setting */
0211     cmp r3, #IMX_DDR_TYPE_LPDDR2
0212     subeq   r7, r7, #0x3
0213 set_mmdc_io_lpm:
0214     ldr r9, [r8], #0x8
0215     str r6, [r11, r9]
0216     subs    r7, r7, #0x1
0217     bne set_mmdc_io_lpm
0218 
0219     cmp     r3, #IMX_DDR_TYPE_LPDDR2
0220     bne set_mmdc_io_lpm_done
0221     ldr r6, =0x1000
0222     ldr r9, [r8], #0x8
0223     str r6, [r11, r9]
0224     ldr r9, [r8], #0x8
0225     str r6, [r11, r9]
0226     ldr r6, =0x80000
0227     ldr r9, [r8]
0228     str r6, [r11, r9]
0229 set_mmdc_io_lpm_done:
0230 
0231     /*
0232      * mask all GPC interrupts before
0233      * enabling the RBC counters to
0234      * avoid the counter starting too
0235      * early if an interupt is already
0236      * pending.
0237      */
0238     ldr r11, [r0, #PM_INFO_MX6Q_GPC_V_OFFSET]
0239     ldr r6, [r11, #MX6Q_GPC_IMR1]
0240     ldr r7, [r11, #MX6Q_GPC_IMR2]
0241     ldr r8, [r11, #MX6Q_GPC_IMR3]
0242     ldr r9, [r11, #MX6Q_GPC_IMR4]
0243 
0244     ldr r10, =0xffffffff
0245     str r10, [r11, #MX6Q_GPC_IMR1]
0246     str r10, [r11, #MX6Q_GPC_IMR2]
0247     str r10, [r11, #MX6Q_GPC_IMR3]
0248     str r10, [r11, #MX6Q_GPC_IMR4]
0249 
0250     /*
0251      * enable the RBC bypass counter here
0252      * to hold off the interrupts. RBC counter
0253      * = 32 (1ms), Minimum RBC delay should be
0254      * 400us for the analog LDOs to power down.
0255      */
0256     ldr r11, [r0, #PM_INFO_MX6Q_CCM_V_OFFSET]
0257     ldr r10, [r11, #MX6Q_CCM_CCR]
0258     bic r10, r10, #(0x3f << 21)
0259     orr r10, r10, #(0x20 << 21)
0260     str r10, [r11, #MX6Q_CCM_CCR]
0261 
0262     /* enable the counter. */
0263     ldr r10, [r11, #MX6Q_CCM_CCR]
0264     orr r10, r10, #(0x1 << 27)
0265     str r10, [r11, #MX6Q_CCM_CCR]
0266 
0267     /* unmask all the GPC interrupts. */
0268     ldr r11, [r0, #PM_INFO_MX6Q_GPC_V_OFFSET]
0269     str r6, [r11, #MX6Q_GPC_IMR1]
0270     str r7, [r11, #MX6Q_GPC_IMR2]
0271     str r8, [r11, #MX6Q_GPC_IMR3]
0272     str r9, [r11, #MX6Q_GPC_IMR4]
0273 
0274     /*
0275      * now delay for a short while (3usec)
0276      * ARM is at 1GHz at this point
0277      * so a short loop should be enough.
0278      * this delay is required to ensure that
0279      * the RBC counter can start counting in
0280      * case an interrupt is already pending
0281      * or in case an interrupt arrives just
0282      * as ARM is about to assert DSM_request.
0283      */
0284     ldr r6, =2000
0285 rbc_loop:
0286     subs    r6, r6, #0x1
0287     bne rbc_loop
0288 
0289     /* Zzz, enter stop mode */
0290     wfi
0291     nop
0292     nop
0293     nop
0294     nop
0295 
0296     /*
0297      * run to here means there is pending
0298      * wakeup source, system should auto
0299      * resume, we need to restore MMDC IO first
0300      */
0301     mov r5, #0x0
0302     resume_mmdc
0303 
0304     /* return to suspend finish */
0305     ret lr
0306 
0307 resume:
0308     /* invalidate L1 I-cache first */
0309     mov     r6, #0x0
0310     mcr     p15, 0, r6, c7, c5, 0
0311     mcr     p15, 0, r6, c7, c5, 6
0312     /* enable the Icache and branch prediction */
0313     mov     r6, #0x1800
0314     mcr     p15, 0, r6, c1, c0, 0
0315     isb
0316 
0317     /* get physical resume address from pm_info. */
0318     ldr lr, [r0, #PM_INFO_RESUME_ADDR_OFFSET]
0319     /* clear core0's entry and parameter */
0320     ldr r11, [r0, #PM_INFO_MX6Q_SRC_P_OFFSET]
0321     mov r7, #0x0
0322     str r7, [r11, #MX6Q_SRC_GPR1]
0323     str r7, [r11, #MX6Q_SRC_GPR2]
0324 
0325     ldr r3, [r0, #PM_INFO_DDR_TYPE_OFFSET]
0326     mov r5, #0x1
0327     resume_mmdc
0328 
0329     ret lr
0330 ENDPROC(imx6_suspend)