| /* |
| * Copyright (C) 2014-2015 Freescale Semiconductor, Inc. All Rights Reserved. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; either version 2 of the License, or |
| * (at your option) any later version. |
| |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| */ |
| |
| #include <linux/linkage.h> |
| |
| #define PM_INFO_PBASE_OFFSET 0x0 |
| #define PM_INFO_RESUME_ADDR_OFFSET 0x4 |
| #define PM_INFO_PM_INFO_SIZE_OFFSET 0x8 |
| #define PM_INFO_PM_INFO_TTBR_OFFSET 0xc |
| #define PM_INFO_MX6Q_MMDC_P_OFFSET 0x10 |
| #define PM_INFO_MX6Q_MMDC_V_OFFSET 0x14 |
| #define PM_INFO_MX6Q_IOMUXC_P_OFFSET 0x18 |
| #define PM_INFO_MX6Q_IOMUXC_V_OFFSET 0x1c |
| #define PM_INFO_MX6Q_CCM_P_OFFSET 0x20 |
| #define PM_INFO_MX6Q_CCM_V_OFFSET 0x24 |
| #define PM_INFO_MX6Q_GPC_P_OFFSET 0x28 |
| #define PM_INFO_MX6Q_GPC_V_OFFSET 0x2c |
| #define PM_INFO_MX6Q_L2_P_OFFSET 0x30 |
| #define PM_INFO_MX6Q_L2_V_OFFSET 0x34 |
| #define PM_INFO_MX6Q_ANATOP_P_OFFSET 0x38 |
| #define PM_INFO_MX6Q_ANATOP_V_OFFSET 0x3c |
| #define PM_INFO_MX6Q_SRC_P_OFFSET 0x40 |
| #define PM_INFO_MX6Q_SRC_V_OFFSET 0x44 |
| #define PM_INFO_MX6Q_SEMA4_P_OFFSET 0x48 |
| #define PM_INFO_MX6Q_SEMA4_V_OFFSET 0x4c |
| #define PM_INFO_MX6Q_SAVED_DIAGNOSTIC_OFFSET 0x50 |
| #define PM_INFO_MMDC_IO_NUM_OFFSET 0x54 |
| #define PM_INFO_MMDC_IO_VAL_OFFSET 0x58 |
| |
| #define MX6Q_MMDC_MAPSR 0x404 |
| #define MX6Q_MMDC_MPDGCTRL0 0x83c |
| #define MX6Q_SRC_GPR1 0x20 |
| #define MX6Q_SRC_GPR2 0x24 |
| #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 |
| |
| .globl mx6sx_lpm_wfi_start |
| .globl mx6sx_lpm_wfi_end |
| |
| .macro pll_do_wait_lock |
| 1: |
| ldr r7, [r10, r8] |
| ands r7, #0x80000000 |
| beq 1b |
| |
| .endm |
| |
| .macro ccm_do_wait |
| 2: |
| ldr r7, [r10, #0x48] |
| cmp r7, #0x0 |
| bne 2b |
| |
| .endm |
| |
| .macro ccm_enter_idle |
| |
| ldr r10, [r0, #PM_INFO_MX6Q_CCM_V_OFFSET] |
| |
| /* set ahb to 3MHz */ |
| ldr r7, [r10, #0x14] |
| orr r7, r7, #0x1c00 |
| str r7, [r10, #0x14] |
| |
| /* set perclk to 6MHz */ |
| ldr r7, [r10, #0x1c] |
| bic r7, r7, #0x3f |
| orr r7, r7, #0x3 |
| str r7, [r10, #0x1c] |
| |
| /* set mmdc to 1MHz, periph2_clk2 need to be @8MHz */ |
| ldr r7, [r10, #0x14] |
| orr r7, r7, #0x2 |
| orr r7, r7, #(0x7 << 3) |
| str r7, [r10, #0x14] |
| |
| ccm_do_wait |
| |
| ldr r10, [r0, #PM_INFO_MX6Q_CCM_V_OFFSET] |
| |
| /* set pll1_sw to from pll1 main */ |
| ldr r7, [r10, #0xc] |
| bic r7, r7, #0x4 |
| str r7, [r10, #0xc] |
| |
| /* set step from osc */ |
| ldr r7, [r10, #0xc] |
| bic r7, r7, #0x100 |
| str r7, [r10, #0xc] |
| |
| /* set pll1_sw to from step */ |
| ldr r7, [r10, #0xc] |
| orr r7, r7, #0x4 |
| str r7, [r10, #0xc] |
| |
| ldr r10, [r0, #PM_INFO_MX6Q_ANATOP_V_OFFSET] |
| |
| /* Disable PLL1 bypass output */ |
| ldr r7, [r10] |
| bic r7, r7, #0x12000 |
| str r7, [r10] |
| |
| /* |
| * disable pll2, suppose when system enter low |
| * power idle mode, only 396MHz pfd needs pll2, |
| * now we switch arm clock to OSC, we can disable |
| * pll2 now, gate pll2_pfd2 first. |
| */ |
| ldr r7, [r10, #0x100] |
| orr r7, #0x800000 |
| str r7, [r10, #0x100] |
| |
| ldr r7, [r10, #0x30] |
| orr r7, r7, #0x1000 |
| bic r7, r7, #0x2000 |
| str r7, [r10, #0x30] |
| |
| .endm |
| |
| .macro ccm_exit_idle |
| |
| cmp r5, #0x0 |
| ldreq r10, [r0, #PM_INFO_MX6Q_ANATOP_V_OFFSET] |
| ldrne r10, [r0, #PM_INFO_MX6Q_ANATOP_P_OFFSET] |
| |
| /* enable pll2 and pll2_pfd2 */ |
| ldr r7, [r10, #0x30] |
| bic r7, r7, #0x1000 |
| orr r7, r7, #0x2000 |
| str r7, [r10, #0x30] |
| |
| ldr r8, =0x30 |
| pll_do_wait_lock |
| |
| ldr r7, [r10, #0x100] |
| bic r7, #0x800000 |
| str r7, [r10, #0x100] |
| |
| /* enable PLL1 bypass output */ |
| ldr r7, [r10] |
| orr r7, r7, #0x12000 |
| str r7, [r10] |
| |
| cmp r5, #0x0 |
| ldreq r10, [r0, #PM_INFO_MX6Q_CCM_V_OFFSET] |
| ldrne r10, [r0, #PM_INFO_MX6Q_CCM_P_OFFSET] |
| |
| /* set perclk back to 24MHz */ |
| ldr r7, [r10, #0x1c] |
| bic r7, r7, #0x3f |
| str r7, [r10, #0x1c] |
| |
| /* set mmdc back to 24MHz */ |
| ldr r7, [r10, #0x14] |
| bic r7, r7, #0x7 |
| bic r7, r7, #(0x7 << 3) |
| str r7, [r10, #0x14] |
| |
| /* set ahb div back to 24MHz */ |
| ldr r7, [r10, #0x14] |
| bic r7, r7, #0x1c00 |
| str r7, [r10, #0x14] |
| |
| ccm_do_wait |
| |
| /* set pll1_sw to from pll1 main */ |
| ldr r7, [r10, #0xc] |
| bic r7, r7, #0x4 |
| str r7, [r10, #0xc] |
| |
| /* set step from pll2_pfd2 */ |
| ldr r7, [r10, #0xc] |
| orr r7, r7, #0x100 |
| str r7, [r10, #0xc] |
| |
| /* set pll1_sw to from step */ |
| ldr r7, [r10, #0xc] |
| orr r7, r7, #0x4 |
| str r7, [r10, #0xc] |
| |
| cmp r5, #0x0 |
| ldreq r10, [r0, #PM_INFO_MX6Q_ANATOP_V_OFFSET] |
| ldrne r10, [r0, #PM_INFO_MX6Q_ANATOP_P_OFFSET] |
| |
| .endm |
| |
| .macro anatop_enter_idle |
| |
| ldr r10, [r0, #PM_INFO_MX6Q_ANATOP_V_OFFSET] |
| |
| /* |
| * check whether any PLL is enabled, as only when |
| * there is no PLLs enabled, 2P5 and 1P1 can be |
| * off and only enable weak ones. |
| */ |
| |
| /* arm pll1 */ |
| ldr r7, [r10, #0] |
| ands r7, r7, #(1 << 31) |
| bne 10f |
| |
| /* sys pll2 */ |
| ldr r7, [r10, #0x30] |
| ands r7, r7, #(1 << 31) |
| bne 10f |
| |
| /* usb pll3 */ |
| ldr r7, [r10, #0x10] |
| ands r7, r7, #(1 << 31) |
| bne 10f |
| |
| /* audio pll4 */ |
| ldr r7, [r10, #0x70] |
| ands r7, r7, #(1 << 31) |
| bne 10f |
| |
| /* vidio pll5 */ |
| ldr r7, [r10, #0xa0] |
| ands r7, r7, #(1 << 31) |
| bne 10f |
| |
| /* enet pll6 */ |
| ldr r7, [r10, #0xe0] |
| ands r7, r7, #(1 << 31) |
| bne 10f |
| |
| /* usb host pll7 */ |
| ldr r7, [r10, #0x20] |
| ands r7, r7, #(1 << 31) |
| bne 10f |
| |
| /* enable weak 2P5 and turn off regular 2P5 */ |
| ldr r7, [r10, #0x130] |
| orr r7, r7, #0x40000 |
| str r7, [r10, #0x130] |
| bic r7, r7, #0x1 |
| str r7, [r10, #0x130] |
| |
| /* enable weak 1p1 and turn off regular 1P1 */ |
| ldr r7, [r10, #0x110] |
| orr r7, r7, #0x40000 |
| str r7, [r10, #0x110] |
| bic r7, r7, #0x1 |
| str r7, [r10, #0x110] |
| |
| /* check whether ARM LDO is bypassed */ |
| ldr r7, [r10, #0x140] |
| and r7, r7, #0x1f |
| cmp r7, #0x1f |
| bne 10f |
| |
| /* low power band gap enable */ |
| ldr r7, [r10, #0x270] |
| orr r7, r7, #0x20 |
| str r7, [r10, #0x270] |
| |
| /* turn off the bias current from the regular bandgap */ |
| ldr r7, [r10, #0x270] |
| orr r7, r7, #0x80 |
| str r7, [r10, #0x270] |
| |
| /* |
| * clear the REFTOP_SELFBIASOFF, |
| * self-bias circuit of the band gap. |
| * Per RM, should be cleared when |
| * band gap is powered down. |
| */ |
| ldr r7, [r10, #0x150] |
| bic r7, r7, #0x8 |
| str r7, [r10, #0x150] |
| |
| /* turn off regular bandgap */ |
| ldr r7, [r10, #0x150] |
| orr r7, r7, #0x1 |
| str r7, [r10, #0x150] |
| |
| /* only switch to RC-OSC clk after TO1.2 */ |
| ldr r7, [r10, #0x260] |
| and r7, r7, #0x3 |
| cmp r7, #0x2 |
| blt 10f |
| |
| /* switch to RC-OSC */ |
| ldr r7, [r10, #0x270] |
| orr r7, r7, #0x10 |
| str r7, [r10, #0x270] |
| |
| /* turn off XTAL-OSC */ |
| ldr r7, [r10, #0x150] |
| orr r7, r7, #0x40000000 |
| str r7, [r10, #0x150] |
| 10: |
| /* lower OSC current by 37.5% */ |
| ldr r7, [r10, #0x150] |
| orr r7, r7, #0x6000 |
| str r7, [r10, #0x150] |
| |
| .endm |
| |
| .macro anatop_exit_idle |
| |
| cmp r5, #0x0 |
| ldreq r10, [r0, #PM_INFO_MX6Q_ANATOP_V_OFFSET] |
| ldrne r10, [r0, #PM_INFO_MX6Q_ANATOP_P_OFFSET] |
| |
| /* increase OSC current to normal */ |
| ldr r7, [r10, #0x150] |
| bic r7, r7, #0x6000 |
| str r7, [r10, #0x150] |
| |
| /* only switch to RC-OSC after TO1.2 */ |
| ldr r7, [r10, #0x260] |
| and r7, r7, #0x3 |
| cmp r7, #0x2 |
| blt 15f |
| |
| /* turn on XTAL-OSC and detector */ |
| ldr r7, [r10, #0x150] |
| bic r7, r7, #0x40000000 |
| orr r7, r7, #0x10000 |
| str r7, [r10, #0x150] |
| |
| /* wait for XTAL stable */ |
| 14: |
| ldr r7, [r10, #0x150] |
| ands r7, r7, #0x8000 |
| beq 14b |
| |
| /* switch to XTAL-OSC */ |
| ldr r7, [r10, #0x270] |
| bic r7, r7, #0x10 |
| str r7, [r10, #0x270] |
| |
| /* turn off XTAL-OSC detector */ |
| ldr r7, [r10, #0x150] |
| bic r7, r7, #0x10000 |
| str r7, [r10, #0x150] |
| 15: |
| /* check whether we need to enable 2P5/1P1 */ |
| ldr r7, [r10, #0x110] |
| ands r7, r7, #0x40000 |
| beq 11f |
| |
| /* check whether ARM LDO is bypassed */ |
| ldr r7, [r10, #0x140] |
| and r7, r7, #0x1f |
| cmp r7, #0x1f |
| bne 12f |
| |
| /* turn on regular bandgap and wait for stable */ |
| ldr r7, [r10, #0x150] |
| bic r7, r7, #0x1 |
| str r7, [r10, #0x150] |
| 13: |
| ldr r7, [r10, #0x150] |
| ands r7, #0x80 |
| beq 13b |
| |
| /* |
| * set the REFTOP_SELFBIASOFF, |
| * self-bias circuit of the band gap. |
| */ |
| ldr r7, [r10, #0x150] |
| orr r7, r7, #0x8 |
| str r7, [r10, #0x150] |
| |
| /* turn on the bias current from the regular bandgap */ |
| ldr r7, [r10, #0x270] |
| bic r7, r7, #0x80 |
| str r7, [r10, #0x270] |
| |
| /* low power band gap disable */ |
| ldr r7, [r10, #0x270] |
| bic r7, r7, #0x20 |
| str r7, [r10, #0x270] |
| 12: |
| /* enable regular 2P5 and turn off weak 2P5 */ |
| ldr r7, [r10, #0x130] |
| orr r7, r7, #0x1 |
| str r7, [r10, #0x130] |
| |
| /* Ensure the 2P5 is up. */ |
| 3: |
| ldr r7, [r10, #0x130] |
| ands r7, r7, #0x20000 |
| beq 3b |
| ldr r7, [r10, #0x130] |
| bic r7, r7, #0x40000 |
| str r7, [r10, #0x130] |
| |
| /* enable regular 1p1 and turn off weak 1P1 */ |
| ldr r7, [r10, #0x110] |
| orr r7, r7, #0x1 |
| str r7, [r10, #0x110] |
| 4: |
| ldr r7, [r10, #0x110] |
| ands r7, r7, #0x20000 |
| beq 4b |
| ldr r7, [r10, #0x110] |
| bic r7, r7, #0x40000 |
| str r7, [r10, #0x110] |
| 11: |
| .endm |
| |
| .macro disable_l1_dcache |
| |
| /* |
| * Flush all data from the L1 data cache before disabling |
| * SCTLR.C bit. |
| */ |
| push {r0 - r10, lr} |
| ldr r7, =v7_flush_dcache_all |
| mov lr, pc |
| mov pc, r7 |
| pop {r0 - r10, lr} |
| |
| /* disable d-cache */ |
| mrc p15, 0, r7, c1, c0, 0 |
| bic r7, r7, #(1 << 2) |
| mcr p15, 0, r7, c1, c0, 0 |
| dsb |
| isb |
| |
| push {r0 - r10, lr} |
| ldr r7, =v7_flush_dcache_all |
| mov lr, pc |
| mov pc, r7 |
| pop {r0 - r10, lr} |
| |
| .endm |
| |
| .macro mmdc_enter_dvfs_mode |
| |
| /* disable automatic power savings. */ |
| ldr r7, [r10, #MX6Q_MMDC_MAPSR] |
| orr r7, r7, #0x1 |
| str r7, [r10, #MX6Q_MMDC_MAPSR] |
| |
| /* disable power down timer */ |
| ldr r7, [r10, #0x4] |
| bic r7, r7, #0xff00 |
| str r7, [r10, #0x4] |
| |
| /* make the DDR explicitly enter self-refresh. */ |
| ldr r7, [r10, #MX6Q_MMDC_MAPSR] |
| orr r7, r7, #(1 << 21) |
| str r7, [r10, #MX6Q_MMDC_MAPSR] |
| 5: |
| ldr r7, [r10, #MX6Q_MMDC_MAPSR] |
| ands r7, r7, #(1 << 25) |
| beq 5b |
| |
| .endm |
| |
| .macro resume_mmdc |
| |
| /* restore MMDC IO */ |
| cmp r5, #0x0 |
| ldreq r10, [r0, #PM_INFO_MX6Q_IOMUXC_V_OFFSET] |
| ldrne r10, [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 |
| 6: |
| ldr r8, [r7], #0x4 |
| ldr r9, [r7], #0x4 |
| str r9, [r10, r8] |
| subs r6, r6, #0x1 |
| bne 6b |
| |
| cmp r5, #0x0 |
| ldreq r10, [r0, #PM_INFO_MX6Q_MMDC_V_OFFSET] |
| ldrne r10, [r0, #PM_INFO_MX6Q_MMDC_P_OFFSET] |
| |
| /* reset read FIFO, RST_RD_FIFO */ |
| ldr r7, =MX6Q_MMDC_MPDGCTRL0 |
| ldr r6, [r10, r7] |
| orr r6, r6, #(1 << 31) |
| str r6, [r10, r7] |
| 7: |
| ldr r6, [r10, r7] |
| ands r6, r6, #(1 << 31) |
| bne 7b |
| |
| /* reset FIFO a second time */ |
| ldr r6, [r10, r7] |
| orr r6, r6, #(1 << 31) |
| str r6, [r10, r7] |
| 8: |
| ldr r6, [r10, r7] |
| ands r6, r6, #(1 << 31) |
| bne 8b |
| |
| /* let DDR out of self-refresh */ |
| ldr r7, [r10, #MX6Q_MMDC_MAPSR] |
| bic r7, r7, #(1 << 21) |
| str r7, [r10, #MX6Q_MMDC_MAPSR] |
| 9: |
| ldr r7, [r10, #MX6Q_MMDC_MAPSR] |
| ands r7, r7, #(1 << 25) |
| bne 9b |
| |
| /* enable power down timer */ |
| ldr r7, [r10, #0x4] |
| orr r7, r7, #0x5500 |
| str r7, [r10, #0x4] |
| |
| /* enable DDR auto power saving */ |
| ldr r7, [r10, #MX6Q_MMDC_MAPSR] |
| bic r7, r7, #0x1 |
| str r7, [r10, #MX6Q_MMDC_MAPSR] |
| |
| .endm |
| |
| .macro sema4_lock |
| |
| /* lock share memory sema4 */ |
| cmp r5, #0x0 |
| ldreq r10, [r0, #PM_INFO_MX6Q_SEMA4_V_OFFSET] |
| ldrne r10, [r0, #PM_INFO_MX6Q_SEMA4_P_OFFSET] |
| ldrb r6, =0x1 |
| 16: |
| ldrb r7, [r10, #0x6] |
| cmp r7, #0x0 |
| bne 16b |
| strb r6, [r10, #0x6] |
| |
| .endm |
| |
| .macro sema4_unlock |
| |
| /* unlock share memory sema4 */ |
| cmp r5, #0x0 |
| ldreq r10, [r0, #PM_INFO_MX6Q_SEMA4_V_OFFSET] |
| ldrne r10, [r0, #PM_INFO_MX6Q_SEMA4_P_OFFSET] |
| ldrb r6, =0x0 |
| strb r6, [r10, #0x6] |
| |
| .endm |
| |
| .macro tlb_set_to_ocram |
| |
| /* save ttbr */ |
| mrc p15, 0, r7, c2, c0, 1 |
| str r7, [r0, #PM_INFO_PM_INFO_TTBR_OFFSET] |
| |
| /* |
| * To ensure no page table walks occur in DDR, we |
| * have a another page table stored in IRAM that only |
| * contains entries pointing to IRAM, AIPS1 and AIPS2. |
| * We need to set the TTBR1 to the new IRAM TLB. |
| * Do the following steps: |
| * 1. Flush the Branch Target Address Cache (BTAC) |
| * 2. Set TTBR1 to point to IRAM page table. |
| * 3. Disable page table walks in TTBR0 (PD0 = 1) |
| * 4. Set TTBR0.N=1, implying 0-2G is translated by TTBR0 |
| * and 2-4G is translated by TTBR1. |
| */ |
| |
| ldr r6, =iram_tlb_phys_addr |
| ldr r7, [r6] |
| |
| /* Flush the BTAC. */ |
| ldr r6, =0x0 |
| mcr p15, 0, r6, c7, c1, 6 |
| |
| /* Disable Branch Prediction, Z bit in SCTLR. */ |
| mrc p15, 0, r6, c1, c0, 0 |
| bic r6, r6, #0x800 |
| mcr p15, 0, r6, c1, c0, 0 |
| |
| dsb |
| isb |
| |
| /* Store the IRAM table in TTBR1 */ |
| mcr p15, 0, r7, c2, c0, 1 |
| |
| /* Read TTBCR and set PD0=1, N = 1 */ |
| mrc p15, 0, r6, c2, c0, 2 |
| orr r6, r6, #0x11 |
| mcr p15, 0, r6, c2, c0, 2 |
| |
| dsb |
| isb |
| |
| /* flush the TLB */ |
| ldr r6, =0x0 |
| mcr p15, 0, r6, c8, c3, 0 |
| |
| .endm |
| |
| .macro tlb_back_to_ddr |
| |
| /* Restore the TTBCR */ |
| |
| dsb |
| isb |
| |
| /* Read TTBCR and set PD0=0, N = 0 */ |
| mrc p15, 0, r6, c2, c0, 2 |
| bic r6, r6, #0x11 |
| mcr p15, 0, r6, c2, c0, 2 |
| |
| dsb |
| isb |
| |
| /* flush the TLB */ |
| ldr r6, =0x0 |
| mcr p15, 0, r6, c8, c3, 0 |
| |
| dsb |
| isb |
| |
| /* Enable Branch Prediction, Z bit in SCTLR. */ |
| mrc p15, 0, r6, c1, c0, 0 |
| orr r6, r6, #0x800 |
| mcr p15, 0, r6, c1, c0, 0 |
| |
| /* Flush the Branch Target Address Cache (BTAC) */ |
| ldr r6, =0x0 |
| mcr p15, 0, r6, c7, c1, 6 |
| |
| /* restore ttbr */ |
| ldr r7, [r0, #PM_INFO_PM_INFO_TTBR_OFFSET] |
| mcr p15, 0, r7, c2, c0, 1 |
| |
| .endm |
| |
| .extern iram_tlb_phys_addr |
| |
| /* imx6sx_low_power_idle */ |
| |
| .align 3 |
| ENTRY(imx6sx_low_power_idle) |
| mx6sx_lpm_wfi_start: |
| push {r4 - r10} |
| |
| /* get necessary info from pm_info */ |
| ldr r1, [r0, #PM_INFO_PBASE_OFFSET] |
| ldr r2, [r0, #PM_INFO_PM_INFO_SIZE_OFFSET] |
| |
| /* |
| * counting the resume address in iram |
| * to set it in SRC register. |
| */ |
| ldr r5, =imx6sx_low_power_idle |
| ldr r6, =wakeup |
| sub r6, r6, r5 |
| add r8, r1, r2 |
| add r3, r8, r6 |
| |
| /* store physical resume addr and pm_info address. */ |
| ldr r10, [r0, #PM_INFO_MX6Q_SRC_V_OFFSET] |
| str r3, [r10, #0x20] |
| str r1, [r10, #0x24] |
| |
| /* save disagnostic register */ |
| mrc p15, 0, r7, c15, c0, 1 |
| str r7, [r0, #PM_INFO_MX6Q_SAVED_DIAGNOSTIC_OFFSET] |
| |
| /* set ARM power to be gated */ |
| ldr r10, [r0, #PM_INFO_MX6Q_GPC_V_OFFSET] |
| ldr r7, =0x1 |
| str r7, [r10, #0x2a0] |
| |
| disable_l1_dcache |
| |
| #ifdef CONFIG_CACHE_L2X0 |
| /* sync L2 */ |
| ldr r10, [r0, #PM_INFO_MX6Q_L2_V_OFFSET] |
| |
| /* Wait for background operations to complete. */ |
| wait_for_l2_to_idle: |
| ldr r7, [r10, #0x730] |
| cmp r7, #0x0 |
| bne wait_for_l2_to_idle |
| |
| mov r7, #0x0 |
| str r7, [r10, #0x730] |
| /* disable L2 */ |
| str r7, [r10, #0x100] |
| |
| dsb |
| isb |
| #endif |
| |
| tlb_set_to_ocram |
| |
| /* make sure MMDC in self-refresh */ |
| ldr r10, [r0, #PM_INFO_MX6Q_MMDC_V_OFFSET] |
| mmdc_enter_dvfs_mode |
| |
| /* save DDR IO settings */ |
| ldr r10, [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 |
| save_and_set_mmdc_io_lpm: |
| ldr r9, [r8], #0x4 |
| ldr r5, [r10, r9] |
| str r6, [r10, r9] |
| str r5, [r8], #0x4 |
| subs r7, r7, #0x1 |
| bne save_and_set_mmdc_io_lpm |
| |
| mov r5, #0x0 |
| sema4_lock |
| ccm_enter_idle |
| anatop_enter_idle |
| sema4_unlock |
| |
| /* |
| * mask all GPC interrupts before |
| * enabling the RBC counters to |
| * avoid the counter starting too |
| * early if an interupt is already |
| * pending. |
| */ |
| ldr r10, [r0, #PM_INFO_MX6Q_GPC_V_OFFSET] |
| ldr r4, [r10, #MX6Q_GPC_IMR1] |
| ldr r5, [r10, #MX6Q_GPC_IMR2] |
| ldr r6, [r10, #MX6Q_GPC_IMR3] |
| ldr r7, [r10, #MX6Q_GPC_IMR4] |
| |
| ldr r3, =0xffffffff |
| str r3, [r10, #MX6Q_GPC_IMR1] |
| str r3, [r10, #MX6Q_GPC_IMR2] |
| str r3, [r10, #MX6Q_GPC_IMR3] |
| str r3, [r10, #MX6Q_GPC_IMR4] |
| |
| /* |
| * enable the RBC bypass counter here |
| * to hold off the interrupts. RBC counter |
| * = 4 (120us). With this setting, the latency |
| * from wakeup interrupt to ARM power up |
| * is ~130uS. |
| */ |
| ldr r10, [r0, #PM_INFO_MX6Q_CCM_V_OFFSET] |
| ldr r3, [r10, #MX6Q_CCM_CCR] |
| bic r3, r3, #(0x3f << 21) |
| orr r3, r3, #(0x4 << 21) |
| str r3, [r10, #MX6Q_CCM_CCR] |
| |
| /* enable the counter. */ |
| ldr r3, [r10, #MX6Q_CCM_CCR] |
| orr r3, r3, #(0x1 << 27) |
| str r3, [r10, #MX6Q_CCM_CCR] |
| |
| /* unmask all the GPC interrupts. */ |
| ldr r10, [r0, #PM_INFO_MX6Q_GPC_V_OFFSET] |
| str r4, [r10, #MX6Q_GPC_IMR1] |
| str r5, [r10, #MX6Q_GPC_IMR2] |
| str r6, [r10, #MX6Q_GPC_IMR3] |
| str r7, [r10, #MX6Q_GPC_IMR4] |
| |
| /* |
| * now delay for a short while (3usec) |
| * ARM is at 24MHz 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 r4, =50 |
| rbc_loop: |
| subs r4, r4, #0x1 |
| bne rbc_loop |
| |
| wfi |
| |
| nop |
| nop |
| nop |
| nop |
| nop |
| |
| nop |
| nop |
| nop |
| nop |
| nop |
| |
| nop |
| nop |
| nop |
| nop |
| nop |
| |
| nop |
| nop |
| nop |
| nop |
| nop |
| |
| nop |
| nop |
| nop |
| nop |
| nop |
| |
| mov r5, #0x0 |
| sema4_lock |
| anatop_exit_idle |
| ccm_exit_idle |
| sema4_unlock |
| resume_mmdc |
| |
| /* clear ARM power gate setting */ |
| ldr r10, [r0, #PM_INFO_MX6Q_GPC_V_OFFSET] |
| ldr r7, =0x0 |
| str r7, [r10, #0x2a0] |
| |
| /* enable d-cache */ |
| mrc p15, 0, r7, c1, c0, 0 |
| orr r7, r7, #(1 << 2) |
| mcr p15, 0, r7, c1, c0, 0 |
| |
| #ifdef CONFIG_CACHE_L2X0 |
| ldr r10, [r0, #PM_INFO_MX6Q_L2_V_OFFSET] |
| mov r7, #0x1 |
| /* enable L2 */ |
| str r7, [r10, #0x100] |
| #endif |
| |
| tlb_back_to_ddr |
| |
| /* Restore registers */ |
| pop {r4 - r10} |
| mov pc, lr |
| |
| wakeup: |
| |
| /* invalidate L1 I-cache first */ |
| mov r1, #0x0 |
| mcr p15, 0, r1, c7, c5, 0 |
| mcr p15, 0, r1, c7, c5, 0 |
| mcr p15, 0, r1, c7, c5, 6 |
| /* enable the Icache and branch prediction */ |
| mov r1, #0x1800 |
| mcr p15, 0, r1, c1, c0, 0 |
| isb |
| /* restore disagnostic register */ |
| ldr r7, [r0, #PM_INFO_MX6Q_SAVED_DIAGNOSTIC_OFFSET] |
| mcr p15, 0, r7, c15, c0, 1 |
| |
| /* get physical resume address from pm_info. */ |
| ldr lr, [r0, #PM_INFO_RESUME_ADDR_OFFSET] |
| /* clear core0's entry and parameter */ |
| ldr r10, [r0, #PM_INFO_MX6Q_SRC_P_OFFSET] |
| mov r7, #0x0 |
| str r7, [r10, #MX6Q_SRC_GPR1] |
| str r7, [r10, #MX6Q_SRC_GPR2] |
| |
| /* clear ARM power gate setting */ |
| ldr r10, [r0, #PM_INFO_MX6Q_GPC_P_OFFSET] |
| ldr r7, =0x0 |
| str r7, [r10, #0x2a0] |
| |
| mov r5, #0x1 |
| sema4_lock |
| anatop_exit_idle |
| ccm_exit_idle |
| sema4_unlock |
| resume_mmdc |
| |
| /* Restore registers */ |
| mov pc, lr |
| .ltorg |
| mx6sx_lpm_wfi_end: |