123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350 |
- /*
- * Copyright 2014 Freescale Semiconductor, Inc.
- *
- * The code contained herein is licensed under the GNU General Public
- * License. You may obtain a copy of the GNU General Public License
- * Version 2 or later at the following locations:
- *
- * http://www.opensource.org/licenses/gpl-license.html
- * http://www.gnu.org/copyleft/gpl.html
- */
- #include <linux/linkage.h>
- #include <asm/assembler.h>
- #include <asm/asm-offsets.h>
- #include <asm/hardware/cache-l2x0.h>
- #include "hardware.h"
- /*
- * ==================== low level suspend ====================
- *
- * Better to follow below rules to use ARM registers:
- * r0: pm_info structure address;
- * r1 ~ r4: for saving pm_info members;
- * r5 ~ r10: free registers;
- * r11: io base address.
- *
- * suspend ocram space layout:
- * ======================== high address ======================
- * .
- * .
- * .
- * ^
- * ^
- * ^
- * imx6_suspend code
- * PM_INFO structure(imx6_cpu_pm_info)
- * ======================== low address =======================
- */
- /*
- * Below offsets are based on struct imx6_cpu_pm_info
- * which defined in arch/arm/mach-imx/pm-imx6q.c, this
- * structure contains necessary pm info for low level
- * suspend related code.
- */
- #define PM_INFO_PBASE_OFFSET 0x0
- #define PM_INFO_RESUME_ADDR_OFFSET 0x4
- #define PM_INFO_DDR_TYPE_OFFSET 0x8
- #define PM_INFO_PM_INFO_SIZE_OFFSET 0xC
- #define PM_INFO_MX6Q_MMDC_P_OFFSET 0x10
- #define PM_INFO_MX6Q_MMDC_V_OFFSET 0x14
- #define PM_INFO_MX6Q_SRC_P_OFFSET 0x18
- #define PM_INFO_MX6Q_SRC_V_OFFSET 0x1C
- #define PM_INFO_MX6Q_IOMUXC_P_OFFSET 0x20
- #define PM_INFO_MX6Q_IOMUXC_V_OFFSET 0x24
- #define PM_INFO_MX6Q_CCM_P_OFFSET 0x28
- #define PM_INFO_MX6Q_CCM_V_OFFSET 0x2C
- #define PM_INFO_MX6Q_GPC_P_OFFSET 0x30
- #define PM_INFO_MX6Q_GPC_V_OFFSET 0x34
- #define PM_INFO_MX6Q_L2_P_OFFSET 0x38
- #define PM_INFO_MX6Q_L2_V_OFFSET 0x3C
- #define PM_INFO_MMDC_IO_NUM_OFFSET 0x40
- #define PM_INFO_MMDC_IO_VAL_OFFSET 0x44
- #define MX6Q_SRC_GPR1 0x20
- #define MX6Q_SRC_GPR2 0x24
- #define MX6Q_MMDC_MAPSR 0x404
- #define MX6Q_MMDC_MPDGCTRL0 0x83c
- #define MX6Q_GPC_IMR1 0x08
- #define MX6Q_GPC_IMR2 0x0c
- #define MX6Q_GPC_IMR3 0x10
- #define MX6Q_GPC_IMR4 0x14
- #define MX6Q_CCM_CCR 0x0
- .align 3
- .macro sync_l2_cache
- /* sync L2 cache to drain L2's buffers to DRAM. */
- #ifdef CONFIG_CACHE_L2X0
- ldr r11, [r0, #PM_INFO_MX6Q_L2_V_OFFSET]
- teq r11, #0
- beq 6f
- mov r6, #0x0
- str r6, [r11, #L2X0_CACHE_SYNC]
- 1:
- ldr r6, [r11, #L2X0_CACHE_SYNC]
- ands r6, r6, #0x1
- bne 1b
- 6:
- #endif
- .endm
- .macro resume_mmdc
- /* restore MMDC IO */
- cmp r5, #0x0
- ldreq r11, [r0, #PM_INFO_MX6Q_IOMUXC_V_OFFSET]
- ldrne r11, [r0, #PM_INFO_MX6Q_IOMUXC_P_OFFSET]
- ldr r6, [r0, #PM_INFO_MMDC_IO_NUM_OFFSET]
- ldr r7, =PM_INFO_MMDC_IO_VAL_OFFSET
- add r7, r7, r0
- 1:
- ldr r8, [r7], #0x4
- ldr r9, [r7], #0x4
- str r9, [r11, r8]
- subs r6, r6, #0x1
- bne 1b
- cmp r5, #0x0
- ldreq r11, [r0, #PM_INFO_MX6Q_MMDC_V_OFFSET]
- ldrne r11, [r0, #PM_INFO_MX6Q_MMDC_P_OFFSET]
- cmp r3, #IMX_DDR_TYPE_LPDDR2
- bne 4f
- /* reset read FIFO, RST_RD_FIFO */
- ldr r7, =MX6Q_MMDC_MPDGCTRL0
- ldr r6, [r11, r7]
- orr r6, r6, #(1 << 31)
- str r6, [r11, r7]
- 2:
- ldr r6, [r11, r7]
- ands r6, r6, #(1 << 31)
- bne 2b
- /* reset FIFO a second time */
- ldr r6, [r11, r7]
- orr r6, r6, #(1 << 31)
- str r6, [r11, r7]
- 3:
- ldr r6, [r11, r7]
- ands r6, r6, #(1 << 31)
- bne 3b
- 4:
- /* let DDR out of self-refresh */
- ldr r7, [r11, #MX6Q_MMDC_MAPSR]
- bic r7, r7, #(1 << 21)
- str r7, [r11, #MX6Q_MMDC_MAPSR]
- 5:
- ldr r7, [r11, #MX6Q_MMDC_MAPSR]
- ands r7, r7, #(1 << 25)
- bne 5b
- /* enable DDR auto power saving */
- ldr r7, [r11, #MX6Q_MMDC_MAPSR]
- bic r7, r7, #0x1
- str r7, [r11, #MX6Q_MMDC_MAPSR]
- .endm
- ENTRY(imx6_suspend)
- ldr r1, [r0, #PM_INFO_PBASE_OFFSET]
- ldr r2, [r0, #PM_INFO_RESUME_ADDR_OFFSET]
- ldr r3, [r0, #PM_INFO_DDR_TYPE_OFFSET]
- ldr r4, [r0, #PM_INFO_PM_INFO_SIZE_OFFSET]
- /*
- * counting the resume address in iram
- * to set it in SRC register.
- */
- ldr r6, =imx6_suspend
- ldr r7, =resume
- sub r7, r7, r6
- add r8, r1, r4
- add r9, r8, r7
- /*
- * make sure TLB contain the addr we want,
- * as we will access them after MMDC IO floated.
- */
- ldr r11, [r0, #PM_INFO_MX6Q_CCM_V_OFFSET]
- ldr r6, [r11, #0x0]
- ldr r11, [r0, #PM_INFO_MX6Q_GPC_V_OFFSET]
- ldr r6, [r11, #0x0]
- ldr r11, [r0, #PM_INFO_MX6Q_IOMUXC_V_OFFSET]
- ldr r6, [r11, #0x0]
- /* use r11 to store the IO address */
- ldr r11, [r0, #PM_INFO_MX6Q_SRC_V_OFFSET]
- /* store physical resume addr and pm_info address. */
- str r9, [r11, #MX6Q_SRC_GPR1]
- str r1, [r11, #MX6Q_SRC_GPR2]
- /* need to sync L2 cache before DSM. */
- sync_l2_cache
- ldr r11, [r0, #PM_INFO_MX6Q_MMDC_V_OFFSET]
- /*
- * put DDR explicitly into self-refresh and
- * disable automatic power savings.
- */
- ldr r7, [r11, #MX6Q_MMDC_MAPSR]
- orr r7, r7, #0x1
- str r7, [r11, #MX6Q_MMDC_MAPSR]
- /* make the DDR explicitly enter self-refresh. */
- ldr r7, [r11, #MX6Q_MMDC_MAPSR]
- orr r7, r7, #(1 << 21)
- str r7, [r11, #MX6Q_MMDC_MAPSR]
- poll_dvfs_set:
- ldr r7, [r11, #MX6Q_MMDC_MAPSR]
- ands r7, r7, #(1 << 25)
- beq poll_dvfs_set
- ldr r11, [r0, #PM_INFO_MX6Q_IOMUXC_V_OFFSET]
- ldr r6, =0x0
- ldr r7, [r0, #PM_INFO_MMDC_IO_NUM_OFFSET]
- ldr r8, =PM_INFO_MMDC_IO_VAL_OFFSET
- add r8, r8, r0
- /* LPDDR2's last 3 IOs need special setting */
- cmp r3, #IMX_DDR_TYPE_LPDDR2
- subeq r7, r7, #0x3
- set_mmdc_io_lpm:
- ldr r9, [r8], #0x8
- str r6, [r11, r9]
- subs r7, r7, #0x1
- bne set_mmdc_io_lpm
- cmp r3, #IMX_DDR_TYPE_LPDDR2
- bne set_mmdc_io_lpm_done
- ldr r6, =0x1000
- ldr r9, [r8], #0x8
- str r6, [r11, r9]
- ldr r9, [r8], #0x8
- str r6, [r11, r9]
- ldr r6, =0x80000
- ldr r9, [r8]
- str r6, [r11, r9]
- set_mmdc_io_lpm_done:
- /*
- * mask all GPC interrupts before
- * enabling the RBC counters to
- * avoid the counter starting too
- * early if an interupt is already
- * pending.
- */
- ldr r11, [r0, #PM_INFO_MX6Q_GPC_V_OFFSET]
- ldr r6, [r11, #MX6Q_GPC_IMR1]
- ldr r7, [r11, #MX6Q_GPC_IMR2]
- ldr r8, [r11, #MX6Q_GPC_IMR3]
- ldr r9, [r11, #MX6Q_GPC_IMR4]
- ldr r10, =0xffffffff
- str r10, [r11, #MX6Q_GPC_IMR1]
- str r10, [r11, #MX6Q_GPC_IMR2]
- str r10, [r11, #MX6Q_GPC_IMR3]
- str r10, [r11, #MX6Q_GPC_IMR4]
- /*
- * enable the RBC bypass counter here
- * to hold off the interrupts. RBC counter
- * = 32 (1ms), Minimum RBC delay should be
- * 400us for the analog LDOs to power down.
- */
- ldr r11, [r0, #PM_INFO_MX6Q_CCM_V_OFFSET]
- ldr r10, [r11, #MX6Q_CCM_CCR]
- bic r10, r10, #(0x3f << 21)
- orr r10, r10, #(0x20 << 21)
- str r10, [r11, #MX6Q_CCM_CCR]
- /* enable the counter. */
- ldr r10, [r11, #MX6Q_CCM_CCR]
- orr r10, r10, #(0x1 << 27)
- str r10, [r11, #MX6Q_CCM_CCR]
- /* unmask all the GPC interrupts. */
- ldr r11, [r0, #PM_INFO_MX6Q_GPC_V_OFFSET]
- str r6, [r11, #MX6Q_GPC_IMR1]
- str r7, [r11, #MX6Q_GPC_IMR2]
- str r8, [r11, #MX6Q_GPC_IMR3]
- str r9, [r11, #MX6Q_GPC_IMR4]
- /*
- * now delay for a short while (3usec)
- * ARM is at 1GHz at this point
- * so a short loop should be enough.
- * this delay is required to ensure that
- * the RBC counter can start counting in
- * case an interrupt is already pending
- * or in case an interrupt arrives just
- * as ARM is about to assert DSM_request.
- */
- ldr r6, =2000
- rbc_loop:
- subs r6, r6, #0x1
- bne rbc_loop
- /* Zzz, enter stop mode */
- wfi
- nop
- nop
- nop
- nop
- /*
- * run to here means there is pending
- * wakeup source, system should auto
- * resume, we need to restore MMDC IO first
- */
- mov r5, #0x0
- resume_mmdc
- /* return to suspend finish */
- ret lr
- resume:
- /* invalidate L1 I-cache first */
- mov r6, #0x0
- mcr p15, 0, r6, c7, c5, 0
- mcr p15, 0, r6, c7, c5, 6
- /* enable the Icache and branch prediction */
- mov r6, #0x1800
- mcr p15, 0, r6, c1, c0, 0
- isb
- /* get physical resume address from pm_info. */
- ldr lr, [r0, #PM_INFO_RESUME_ADDR_OFFSET]
- /* clear core0's entry and parameter */
- ldr r11, [r0, #PM_INFO_MX6Q_SRC_P_OFFSET]
- mov r7, #0x0
- str r7, [r11, #MX6Q_SRC_GPR1]
- str r7, [r11, #MX6Q_SRC_GPR2]
- ldr r3, [r0, #PM_INFO_DDR_TYPE_OFFSET]
- mov r5, #0x1
- resume_mmdc
- ret lr
- ENDPROC(imx6_suspend)
- /*
- * The following code must assume it is running from physical address
- * where absolute virtual addresses to the data section have to be
- * turned into relative ones.
- */
- ENTRY(v7_cpu_resume)
- bl v7_invalidate_l1
- #ifdef CONFIG_CACHE_L2X0
- bl l2c310_early_resume
- #endif
- b cpu_resume
- ENDPROC(v7_cpu_resume)
|