blob: a1baf79221d6e3c2f73aed7fbaa4c10abceeb201 [file] [log] [blame]
/*
* Copyright 2018-2021 NXP
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <asm_macros.S>
#include <cortex_a53.h>
#include <dcfg_lsch2.h>
#include <plat_gic.h>
#include <scfg.h>
#include <bl31_data.h>
#include <plat_psci.h>
#include <platform_def.h>
/* the BASE address for these offsets is AUX_01_DATA in the */
/* bootcore's psci data region */
#define DEVDISR2_MASK_OFFSET 0x0 /* references AUX_01_DATA */
#define DEVDISR5_MASK_OFFSET 0x8 /* references AUX_02_DATA */
#define CPUACTLR_DATA_OFFSET 0x10 /* references AUX_03_DATA */
/* the BASE address for these offsets is AUX_04_DATA in the */
/* bootcore's psci data region */
#define GICD_BASE_ADDR_OFFSET 0x0 /* references AUX_04_DATA */
#define GICC_BASE_ADDR_OFFSET 0x8 /* references AUX_05_DATA */
#define DAIF_DATA AUX_06_DATA /* references AUX_06_DATA */
#define IPSTPACK_RETRY_CNT 0x10000
#define DDR_SLEEP_RETRY_CNT 0x10000
#define CPUACTLR_EL1 S3_1_C15_C2_0
#define DDR_SDRAM_CFG_2_FRCSR 0x80000000
#define DDR_SDRAM_CFG_2_OFFSET 0x114
#define DDR_TIMING_CFG_4_OFFSET 0x160
#define DDR_CNTRL_BASE_ADDR 0x01080000
#define DLL_LOCK_MASK 0x3
#define DLL_LOCK_VALUE 0x2
#define ERROR_DDR_SLEEP -1
#define ERROR_DDR_WAKE -2
#define ERROR_NO_QUIESCE -3
#define CORE_RESTARTABLE 0
#define CORE_NOT_RESTARTABLE 1
#define RESET_RETRY_CNT 800
.global soc_init_lowlevel
.global soc_init_percpu
.global _soc_core_release
.global _soc_core_restart
.global _soc_ck_disabled
.global _soc_sys_reset
.global _soc_sys_off
.global _getGICD_BaseAddr
.global _getGICC_BaseAddr
.global _soc_set_start_addr
.global _soc_core_prep_off
.global _soc_core_entr_off
.global _soc_core_exit_off
.global _soc_core_prep_stdby
.global _soc_core_entr_stdby
.global _soc_core_exit_stdby
.global _soc_core_prep_pwrdn
.global _soc_core_entr_pwrdn
.global _soc_core_exit_pwrdn
.global _soc_clstr_prep_stdby
.global _soc_clstr_exit_stdby
.global _soc_clstr_prep_pwrdn
.global _soc_clstr_exit_pwrdn
.global _soc_sys_prep_stdby
.global _soc_sys_exit_stdby
.global _soc_sys_prep_pwrdn
.global _soc_sys_pwrdn_wfi
.global _soc_sys_exit_pwrdn
/*
* This function initialize the soc.
* in: void
* out: void
*/
func soc_init_lowlevel
ret
endfunc soc_init_lowlevel
/*
* void soc_init_percpu(void)
* this function performs any soc-specific initialization that is needed on
* a per-core basis
* in: none
* out: none
* uses x0, x1, x2, x3
*/
func soc_init_percpu
mov x3, x30
bl plat_my_core_mask
mov x2, x0
/* see if this core is marked for prefetch disable */
mov x0, #PREFETCH_DIS_OFFSET
bl _get_global_data /* 0-1 */
tst x0, x2
b.eq 1f
bl _disable_ldstr_pfetch_A53 /* 0 */
1:
mov x30, x3
ret
endfunc soc_init_percpu
/*
* part of CPU_ON
* this function releases a secondary core from reset
* in: x0 = core_mask_lsb
* out: none
* uses: x0, x1, x2, x3
*/
_soc_core_release:
#if (TEST_BL31)
mov w2, w0
CoreMaskMsb w2, w3
/* x2 = core mask msb */
#else
mov x2, x0
#endif
/* write COREBCR */
ldr x1, =NXP_SCFG_ADDR
rev w3, w2
str w3, [x1, #SCFG_COREBCR_OFFSET]
isb
/* read-modify-write BRR */
mov x1, #NXP_DCFG_ADDR
ldr w2, [x1, #DCFG_BRR_OFFSET]
rev w3, w2
orr w3, w3, w0
rev w2, w3
str w2, [x1, #DCFG_BRR_OFFSET]
isb
/* send event */
sev
isb
ret
/*
* part of CPU_ON
* this function restarts a core shutdown via _soc_core_entr_off
* in: x0 = core mask lsb (of the target cpu)
* out: x0 == 0, on success
* x0 != 0, on failure
* uses x0 ~ x5
*/
_soc_core_restart:
mov x5, x30
mov x3, x0
/* x3 = core mask lsb */
bl _getGICD_BaseAddr
mov x4, x0
/* x4 = GICD_BASE_ADDR */
/* enable forwarding of group 0 interrupts by setting GICD_CTLR[0] = 1 */
ldr w1, [x4, #GICD_CTLR_OFFSET]
orr w1, w1, #GICD_CTLR_EN_GRP0
str w1, [x4, #GICD_CTLR_OFFSET]
dsb sy
isb
/*
* fire SGI by writing to GICD_SGIR the following values:
* [25:24] = 0x0 (forward interrupt to the CPU interfaces specified in CPUTargetList field)
* [23:16] = core mask lsb[7:0] (forward interrupt to target cpu)
* [15] = 0 (forward SGI only if it is configured as group 0 interrupt)
* [3:0] = 0xF (interrupt ID = 15)
*/
lsl w1, w3, #16
orr w1, w1, #0xF
str w1, [x4, #GICD_SGIR_OFFSET]
dsb sy
isb
/* load '0' on success */
mov x0, xzr
mov x30, x5
ret
/*
* this function determines if a core is disabled via COREDISR
* in: w0 = core_mask_lsb
* out: w0 = 0, core not disabled
* w0 != 0, core disabled
* uses x0, x1, x2
*/
_soc_ck_disabled:
/* get base addr of dcfg block */
ldr x1, =NXP_DCFG_ADDR
/* read COREDISR */
ldr w1, [x1, #DCFG_COREDISR_OFFSET]
rev w2, w1
/* test core bit */
and w0, w2, w0
ret
/*
* this function resets the system via SoC-specific methods
* in: none
* out: none
* uses x0, x1, x2, x3
*/
_soc_sys_reset:
ldr x2, =NXP_DCFG_ADDR
/* make sure the mask is cleared in the reset request mask register */
mov w1, wzr
str w1, [x2, #DCFG_RSTRQMR1_OFFSET]
/* x2 = NXP_DCFG_ADDR */
/* set the reset request */
ldr w1, =RSTCR_RESET_REQ
ldr x3, =DCFG_RSTCR_OFFSET
rev w0, w1
str w0, [x2, x3]
/* x2 = NXP_DCFG_ADDR */
/* x3 = DCFG_RSTCR_OFFSET */
/* just in case this address range is mapped as cacheable,
* flush the write out of the dcaches */
add x3, x2, x3
dc cvac, x3
dsb st
isb
/* Note: this function does not return */
1:
wfi
b 1b
/*
* part of SYSTEM_OFF
* this function turns off the SoC clocks
* Note: this function is not intended to return, and the only allowable
* recovery is POR
* in: none
* out: none
* uses x0 ~ x8
*/
_soc_sys_off:
/* mask interrupts at the core */
mrs x1, DAIF
mov x0, #DAIF_SET_MASK
orr x0, x1, x0
msr DAIF, x0
/* disable icache, dcache, mmu @ EL1 */
mov x1, #SCTLR_I_C_M_MASK
mrs x0, sctlr_el1
bic x0, x0, x1
msr sctlr_el1, x0
/* disable dcache for EL3 */
mrs x1, SCTLR_EL3
bic x1, x1, #SCTLR_C_MASK
/* make sure icache is enabled */
orr x1, x1, #SCTLR_I_MASK
msr SCTLR_EL3, x1
isb
/* set WFIL2_EN in SCFG_COREPMCR */
ldr x0, =SCFG_COREPMCR_OFFSET
ldr x1, =COREPMCR_WFIL2
bl write_reg_scfg
/* set OVRD_EN in RCPM2_POWMGTDCR */
ldr x0, =RCPM2_POWMGTDCR_OFFSET
ldr x1, =POWMGTDCR_OVRD_EN
bl write_reg_rcpm2
/* read IPPDEXPCR0 @ RCPM_IPPDEXPCR0 */
ldr x0, =RCPM_IPPDEXPCR0_OFFSET
bl read_reg_rcpm
mov x7, x0
/* build an override mask for IPSTPCR4/IPSTPACK4/DEVDISR5 */
mov x5, xzr
ldr x6, =IPPDEXPCR_MASK2
and x6, x6, x7
cbz x6, 1f
/* x5 = override mask
* x6 = IPPDEXPCR bits for DEVDISR5
* x7 = IPPDEXPCR */
/* get the overrides */
orr x4, x5, #DEVDISR5_I2C_1
tst x6, #IPPDEXPCR_I2C1
csel x5, x5, x4, EQ
orr x4, x5, #DEVDISR5_LPUART1
tst x6, #IPPDEXPCR_LPUART1
csel x5, x5, x4, EQ
orr x4, x5, #DEVDISR5_FLX_TMR
tst x6, #IPPDEXPCR_FLX_TMR1
csel x5, x5, x4, EQ
orr x4, x5, #DEVDISR5_OCRAM1
tst x6, #IPPDEXPCR_OCRAM1
csel x5, x5, x4, EQ
orr x4, x5, #DEVDISR5_GPIO
tst x6, #IPPDEXPCR_GPIO1
csel x5, x5, x4, EQ
1:
/* store the DEVDISR5 override mask */
ldr x2, =BC_PSCI_BASE
add x2, x2, #AUX_01_DATA
str w5, [x2, #DEVDISR5_MASK_OFFSET]
/* build an override mask for IPSTPCR1/IPSTPACK1/DEVDISR2 */
mov x5, xzr
ldr x6, =IPPDEXPCR_MASK1
and x6, x6, x7
cbz x6, 2f
/* x5 = override mask */
/* x6 = IPPDEXPCR bits for DEVDISR2 */
/* get the overrides */
orr x4, x5, #DEVDISR2_FMAN1_MAC1
tst x6, #IPPDEXPCR_MAC1_1
csel x5, x5, x4, EQ
orr x4, x5, #DEVDISR2_FMAN1_MAC2
tst x6, #IPPDEXPCR_MAC1_2
csel x5, x5, x4, EQ
orr x4, x5, #DEVDISR2_FMAN1_MAC3
tst x6, #IPPDEXPCR_MAC1_3
csel x5, x5, x4, EQ
orr x4, x5, #DEVDISR2_FMAN1_MAC4
tst x6, #IPPDEXPCR_MAC1_4
csel x5, x5, x4, EQ
orr x4, x5, #DEVDISR2_FMAN1_MAC5
tst x6, #IPPDEXPCR_MAC1_5
csel x5, x5, x4, EQ
orr x4, x5, #DEVDISR2_FMAN1_MAC6
tst x6, #IPPDEXPCR_MAC1_6
csel x5, x5, x4, EQ
orr x4, x5, #DEVDISR2_FMAN1_MAC9
tst x6, #IPPDEXPCR_MAC1_9
csel x5, x5, x4, EQ
orr x4, x5, #DEVDISR2_FMAN1
tst x6, #IPPDEXPCR_FM1
csel x5, x5, x4, EQ
2:
/* store the DEVDISR2 override mask */
ldr x2, =BC_PSCI_BASE
add x2, x2, #AUX_01_DATA
str w5, [x2, #DEVDISR2_MASK_OFFSET]
/* x5 = DEVDISR2 override mask */
/* write IPSTPCR0 - no overrides */
ldr x0, =RCPM2_IPSTPCR0_OFFSET
ldr x1, =IPSTPCR0_VALUE
bl write_reg_rcpm2
/* x5 = DEVDISR2 override mask */
/* write IPSTPCR1 - overrides possible */
ldr x0, =RCPM2_IPSTPCR1_OFFSET
ldr x1, =IPSTPCR1_VALUE
bic x1, x1, x5
bl write_reg_rcpm2
/* write IPSTPCR2 - no overrides */
ldr x0, =RCPM2_IPSTPCR2_OFFSET
ldr x1, =IPSTPCR2_VALUE
bl write_reg_rcpm2
/* write IPSTPCR3 - no overrides */
ldr x0, =RCPM2_IPSTPCR3_OFFSET
ldr x1, =IPSTPCR3_VALUE
bl write_reg_rcpm2
/* write IPSTPCR4 - overrides possible */
ldr x2, =BC_PSCI_BASE
add x2, x2, #AUX_01_DATA
ldr w6, [x2, #DEVDISR5_MASK_OFFSET]
ldr x0, =RCPM2_IPSTPCR4_OFFSET
ldr x1, =IPSTPCR4_VALUE
bic x1, x1, x6
bl write_reg_rcpm2
/* x5 = DEVDISR2 override mask */
/* x6 = DEVDISR5 override mask */
/* poll on IPSTPACK0 */
ldr x3, =RCPM2_IPSTPACKR0_OFFSET
ldr x4, =IPSTPCR0_VALUE
ldr x7, =IPSTPACK_RETRY_CNT
3:
mov x0, x3
bl read_reg_rcpm2
cmp x0, x4
b.eq 14f
sub x7, x7, #1
cbnz x7, 3b
14:
/* poll on IPSTPACK1 */
ldr x3, =IPSTPCR1_VALUE
ldr x7, =IPSTPACK_RETRY_CNT
bic x4, x3, x5
ldr x3, =RCPM2_IPSTPACKR1_OFFSET
4:
mov x0, x3
bl read_reg_rcpm2
cmp x0, x4
b.eq 15f
sub x7, x7, #1
cbnz x7, 4b
15:
/* poll on IPSTPACK2 */
ldr x3, =RCPM2_IPSTPACKR2_OFFSET
ldr x4, =IPSTPCR2_VALUE
ldr x7, =IPSTPACK_RETRY_CNT
5:
mov x0, x3
bl read_reg_rcpm2
cmp x0, x4
b.eq 16f
sub x7, x7, #1
cbnz x7, 5b
16:
/* poll on IPSTPACK3 */
ldr x3, =RCPM2_IPSTPACKR3_OFFSET
ldr x4, =IPSTPCR3_VALUE
ldr x7, =IPSTPACK_RETRY_CNT
6:
mov x0, x3
bl read_reg_rcpm2
cmp x0, x4
b.eq 17f
sub x7, x7, #1
cbnz x7, 6b
17:
/* poll on IPSTPACK4 */
ldr x3, =IPSTPCR4_VALUE
ldr x7, =IPSTPACK_RETRY_CNT
bic x4, x3, x6
ldr x3, =RCPM2_IPSTPACKR4_OFFSET
7:
mov x0, x3
bl read_reg_rcpm2
cmp x0, x4
b.eq 18f
sub x7, x7, #1
cbnz x7, 7b
18:
ldr x7, =BC_PSCI_BASE
add x7, x7, #AUX_01_DATA
/* x5 = DEVDISR2 override mask
* x6 = DEVDISR5 override mask
* x7 = [soc_data_area] */
/* DEVDISR1 - load new value */
mov x0, #DCFG_DEVDISR1_OFFSET
bl read_reg_dcfg
mov x0, #DCFG_DEVDISR1_OFFSET
ldr x1, =DEVDISR1_VALUE
bl write_reg_dcfg
/* DEVDISR2 - load new value */
mov x0, #DCFG_DEVDISR2_OFFSET
bl read_reg_dcfg
mov x0, #DCFG_DEVDISR2_OFFSET
ldr x1, =DEVDISR2_VALUE
bic x1, x1, x5
bl write_reg_dcfg
/* x6 = DEVDISR5 override mask */
/* x7 = [soc_data_area] */
/* DEVDISR3 - load new value */
mov x0, #DCFG_DEVDISR3_OFFSET
bl read_reg_dcfg
mov x0, #DCFG_DEVDISR3_OFFSET
ldr x1, =DEVDISR3_VALUE
bl write_reg_dcfg
/* DEVDISR4 - load new value */
mov x0, #DCFG_DEVDISR4_OFFSET
bl read_reg_dcfg
mov x0, #DCFG_DEVDISR4_OFFSET
ldr x1, =DEVDISR4_VALUE
bl write_reg_dcfg
/* DEVDISR5 - load new value */
mov x0, #DCFG_DEVDISR5_OFFSET
bl read_reg_dcfg
mov x0, #DCFG_DEVDISR5_OFFSET
ldr x1, =DEVDISR5_VALUE
bic x1, x1, x6
bl write_reg_dcfg
/* x7 = [soc_data_area] */
/* disable data prefetch */
mrs x0, CPUACTLR_EL1
bic x0, x0, #CPUACTLR_L1PCTL_MASK
msr CPUACTLR_EL1, x0
/* x6 = DEVDISR5 override mask */
/* setup registers for cache-only execution */
ldr x5, =IPSTPCR4_VALUE
bic x5, x5, x6
mov x6, #DDR_CNTRL_BASE_ADDR
mov x7, #DCSR_RCPM2_BASE
mov x8, #NXP_DCFG_ADDR
dsb sy
isb
/* set the DLL_LOCK cycle count */
ldr w1, [x6, #DDR_TIMING_CFG_4_OFFSET]
rev w2, w1
bic w2, w2, #DLL_LOCK_MASK
orr w2, w2, #DLL_LOCK_VALUE
rev w1, w2
str w1, [x6, #DDR_TIMING_CFG_4_OFFSET]
/* x5 = ipstpcr4 (IPSTPCR4_VALUE bic DEVDISR5_MASK)
* x6 = DDR_CNTRL_BASE_ADDR
* x7 = DCSR_RCPM2_BASE
* x8 = NXP_DCFG_ADDR */
/* enter the cache-only sequence - there is no return */
b final_shutdown
/*
* part of CPU_OFF
* this function programs SoC & GIC registers in preparation for shutting down
* the core
* in: x0 = core mask lsb
* out: none
* uses x0 ~ x7
*/
_soc_core_prep_off:
mov x7, x30
mov x6, x0
/* make sure the smpen bit is set */
mrs x2, CORTEX_A53_ECTLR_EL1
orr x2, x2, #CPUECTLR_SMPEN_MASK
msr CORTEX_A53_ECTLR_EL1, x2
isb
/* configure the cpu interface */
/* disable signaling of ints */
bl _getGICC_BaseAddr // 0-1
mov x4, x0
ldr w3, [x4, #GICC_CTLR_OFFSET]
bic w3, w3, #GICC_CTLR_EN_GRP0
bic w3, w3, #GICC_CTLR_EN_GRP1
str w3, [x4, #GICC_CTLR_OFFSET]
dsb sy
isb
/*
* x3 = GICC_CTRL
* x4 = GICC_BASE_ADDR
* x6 = core mask
*/
/* set the priority filter */
ldr w2, [x4, #GICC_PMR_OFFSET]
orr w2, w2, #GICC_PMR_FILTER
str w2, [x4, #GICC_PMR_OFFSET]
/* setup GICC_CTLR */
bic w3, w3, #GICC_CTLR_ACKCTL_MASK
orr w3, w3, #GICC_CTLR_FIQ_EN_MASK
orr w3, w3, #GICC_CTLR_EOImodeS_MASK
orr w3, w3, #GICC_CTLR_CBPR_MASK
str w3, [x4, #GICC_CTLR_OFFSET]
/* x3 = GICC_CTRL */
/* x4 = GICC_BASE_ADDR */
/* setup the banked-per-core GICD registers */
bl _getGICD_BaseAddr
/*
* x0 = GICD_BASE_ADDR
* x3 = GICC_CTRL
* x4 = GICC_BASE_ADDR
* x6 = core mask
*/
/* define SGI15 as Grp0 */
ldr w2, [x0, #GICD_IGROUPR0_OFFSET]
bic w2, w2, #GICD_IGROUP0_SGI15
str w2, [x0, #GICD_IGROUPR0_OFFSET]
/* set priority of SGI 15 to highest... */
ldr w2, [x0, #GICD_IPRIORITYR3_OFFSET]
bic w2, w2, #GICD_IPRIORITY_SGI15_MASK
str w2, [x0, #GICD_IPRIORITYR3_OFFSET]
/* enable SGI 15 */
ldr w2, [x0, #GICD_ISENABLER0_OFFSET]
orr w2, w2, #GICD_ISENABLE0_SGI15
str w2, [x0, #GICD_ISENABLER0_OFFSET]
/* enable the cpu interface */
orr w3, w3, #GICC_CTLR_EN_GRP0
str w3, [x4, #GICC_CTLR_OFFSET]
/* x0 = GICD_BASE_ADDR
* x6 = core mask */
/* clear any pending SGIs */
add x0, x0, #GICD_CPENDSGIR3_OFFSET
ldr x2, =GICD_CPENDSGIR_CLR_MASK
str w2, [x0]
dsb sy
isb
mov x30, x7
ret
/*
* part of CPU_OFF
* this function performs the final steps to shutdown the core
* in: x0 = core mask lsb
* out: none
* uses x0 ~ x5
*/
_soc_core_entr_off:
mov x5, x30
mov x4, x0
bl _getGICD_BaseAddr
mov x3, x0
/* x3 = GICD_BASE_ADDR */
/* x4 = core mask (lsb) */
3:
/* enter low-power state by executing wfi */
wfi
/* x3 = GICD_BASE_ADDR */
/* x4 = core mask (lsb) */
/* see if we got hit by SGI 15 */
add x0, x3, #GICD_SPENDSGIR3_OFFSET
ldr w2, [x0]
and w2, w2, #GICD_SPENDSGIR3_SGI15_MASK
cbz w2, 4f
/* clear the pending SGI */
ldr x2, =GICD_CPENDSGIR_CLR_MASK
add x0, x3, #GICD_CPENDSGIR3_OFFSET
str w2, [x0]
4:
/* check if core has been turned on */
mov x0, x4
bl _getCoreState
/* x0 = core state */
cmp x0, #CORE_WAKEUP
b.ne 3b
/* if we get here, then we have exited the wfi */
dsb sy
isb
mov x30, x5
ret
/*
* part of CPU_OFF
* this function starts the process of starting a core back up
* in: x0 = core mask lsb
* out: none
* uses x0 ~ x5
*/
_soc_core_exit_off:
mov x5, x30
mov x4, x0
/* x4 = core mask */
bl _getGICC_BaseAddr
mov x2, x0
/* read GICC_IAR */
ldr w0, [x2, #GICC_IAR_OFFSET]
/* write GICC_EIOR - signal end-of-interrupt */
str w0, [x2, #GICC_EOIR_OFFSET]
/* write GICC_DIR - disable interrupt */
str w0, [x2, #GICC_DIR_OFFSET]
/* x2 = GICC_BASE_ADDR */
/* disable signaling of grp0 ints */
ldr w1, [x2, #GICC_CTLR_OFFSET]
bic w1, w1, #GICC_CTLR_EN_GRP0
str w1, [x2, #GICC_CTLR_OFFSET]
dsb sy
isb
mov x30, x5
ret
/*
* this function loads a 64-bit execution address of the core in the soc registers
* BOOTLOCPTRL/H
* in: x0, 64-bit address to write to BOOTLOCPTRL/H
* uses x0, x1, x2, x3
*/
_soc_set_start_addr:
/* get the 64-bit base address of the scfg block */
ldr x2, =NXP_SCFG_ADDR
/* write the 32-bit BOOTLOCPTRL register (offset 0x604 in the scfg block) */
mov x1, x0
rev w3, w1
str w3, [x2, #SCFG_BOOTLOCPTRL_OFFSET]
/* write the 32-bit BOOTLOCPTRH register (offset 0x600 in the scfg block) */
lsr x1, x0, #32
rev w3, w1
str w3, [x2, #SCFG_BOOTLOCPTRH_OFFSET]
ret
/*
* part of CPU_SUSPEND
* this function puts the calling core into standby state
* in: x0 = core mask lsb
* out: none
* uses x0
*/
_soc_core_entr_stdby:
dsb sy
isb
wfi
ret
/*
* part of CPU_SUSPEND
* this function performs SoC-specific programming prior to standby
* in: x0 = core mask lsb
* out: none
* uses x0, x1
*/
_soc_core_prep_stdby:
/* clear CORTEX_A53_ECTLR_EL1[2:0] */
mrs x1, CORTEX_A53_ECTLR_EL1
bic x1, x1, #CPUECTLR_TIMER_MASK
msr CORTEX_A53_ECTLR_EL1, x1
ret
/*
* part of CPU_SUSPEND
* this function performs any SoC-specific cleanup after standby state
* in: x0 = core mask lsb
* out: none
* uses none
*/
_soc_core_exit_stdby:
ret
/*
* part of CPU_SUSPEND
* this function performs SoC-specific programming prior to power-down
* in: x0 = core mask lsb
* out: none
* uses x0, x1
*/
_soc_core_prep_pwrdn:
/* make sure the smp bit is set */
mrs x1, CORTEX_A53_ECTLR_EL1
orr x1, x1, #CPUECTLR_SMPEN_MASK
msr CORTEX_A53_ECTLR_EL1, x1
isb
ret
/*
* part of CPU_SUSPEND
* this function puts the calling core into a power-down state
* in: x0 = core mask lsb
* out: none
* uses x0
*/
_soc_core_entr_pwrdn:
dsb sy
isb
wfi
ret
/*
* part of CPU_SUSPEND
* this function performs any SoC-specific cleanup after power-down
* in: x0 = core mask lsb
* out: none
* uses none
*/
_soc_core_exit_pwrdn:
ret
/*
* part of CPU_SUSPEND
* this function performs SoC-specific programming prior to standby
* in: x0 = core mask lsb
* out: none
* uses x0, x1
*/
_soc_clstr_prep_stdby:
/* clear CORTEX_A53_ECTLR_EL1[2:0] */
mrs x1, CORTEX_A53_ECTLR_EL1
bic x1, x1, #CPUECTLR_TIMER_MASK
msr CORTEX_A53_ECTLR_EL1, x1
ret
/*
* part of CPU_SUSPEND
* this function performs any SoC-specific cleanup after standby state
* in: x0 = core mask lsb
* out: none
* uses none
*/
_soc_clstr_exit_stdby:
ret
/*
* part of CPU_SUSPEND
* this function performs SoC-specific programming prior to power-down
* in: x0 = core mask lsb
* out: none
* uses x0, x1
*/
_soc_clstr_prep_pwrdn:
/* make sure the smp bit is set */
mrs x1, CORTEX_A53_ECTLR_EL1
orr x1, x1, #CPUECTLR_SMPEN_MASK
msr CORTEX_A53_ECTLR_EL1, x1
isb
ret
/*
* part of CPU_SUSPEND
* this function performs any SoC-specific cleanup after power-down
* in: x0 = core mask lsb
* out: none
* uses none
*/
_soc_clstr_exit_pwrdn:
ret
/*
* part of CPU_SUSPEND
* this function performs SoC-specific programming prior to standby
* in: x0 = core mask lsb
* out: none
* uses x0, x1
*/
_soc_sys_prep_stdby:
/* clear CORTEX_A53_ECTLR_EL1[2:0] */
mrs x1, CORTEX_A53_ECTLR_EL1
bic x1, x1, #CPUECTLR_TIMER_MASK
msr CORTEX_A53_ECTLR_EL1, x1
ret
/*
* part of CPU_SUSPEND
* this function performs any SoC-specific cleanup after standby state
* in: x0 = core mask lsb
* out: none
* uses none
*/
_soc_sys_exit_stdby:
ret
/*
* part of CPU_SUSPEND
* this function performs SoC-specific programming prior to
* suspend-to-power-down
* in: x0 = core mask lsb
* out: none
* uses x0, x1, x2, x3, x4
*/
_soc_sys_prep_pwrdn:
mov x4, x30
/* make sure the smp bit is set */
mrs x1, CORTEX_A53_ECTLR_EL1
orr x1, x1, #CPUECTLR_SMPEN_MASK
msr CORTEX_A53_ECTLR_EL1, x1
isb
/* set WFIL2_EN in SCFG_COREPMCR */
ldr x0, =SCFG_COREPMCR_OFFSET
ldr x1, =COREPMCR_WFIL2
bl write_reg_scfg // 0-3
/* set OVRD_EN in RCPM2_POWMGTDCR */
ldr x0, =RCPM2_POWMGTDCR_OFFSET
ldr x1, =POWMGTDCR_OVRD_EN
bl write_reg_rcpm2 // 0-3
mov x30, x4
ret
/*
* part of CPU_SUSPEND
* this function puts the calling core, and potentially the soc, into a
* low-power state
* in: x0 = core mask lsb
* out: x0 = 0, success
* x0 < 0, failure
* uses x0 ~ x9
*/
_soc_sys_pwrdn_wfi:
mov x18, x30
/* read IPPDEXPCR0 @ RCPM_IPPDEXPCR0 */
ldr x0, =RCPM_IPPDEXPCR0_OFFSET
bl read_reg_rcpm
mov x7, x0
/* build an override mask for IPSTPCR4/IPSTPACK4/DEVDISR5 */
mov x5, xzr
ldr x6, =IPPDEXPCR_MASK2
and x6, x6, x7
cbz x6, 1f
/* x5 = override mask
* x6 = IPPDEXPCR bits for DEVDISR5
* x7 = IPPDEXPCR */
/* get the overrides */
orr x4, x5, #DEVDISR5_I2C_1
tst x6, #IPPDEXPCR_I2C1
csel x5, x5, x4, EQ
orr x4, x5, #DEVDISR5_LPUART1
tst x6, #IPPDEXPCR_LPUART1
csel x5, x5, x4, EQ
orr x4, x5, #DEVDISR5_FLX_TMR
tst x6, #IPPDEXPCR_FLX_TMR1
csel x5, x5, x4, EQ
orr x4, x5, #DEVDISR5_OCRAM1
tst x6, #IPPDEXPCR_OCRAM1
csel x5, x5, x4, EQ
orr x4, x5, #DEVDISR5_GPIO
tst x6, #IPPDEXPCR_GPIO1
csel x5, x5, x4, EQ
1:
/* store the DEVDISR5 override mask */
ldr x2, =BC_PSCI_BASE
add x2, x2, #AUX_01_DATA
str w5, [x2, #DEVDISR5_MASK_OFFSET]
/* build an override mask for IPSTPCR1/IPSTPACK1/DEVDISR2 */
mov x5, xzr
ldr x6, =IPPDEXPCR_MASK1
and x6, x6, x7
cbz x6, 2f
/* x5 = override mask */
/* x6 = IPPDEXPCR bits for DEVDISR2 */
/* get the overrides */
orr x4, x5, #DEVDISR2_FMAN1_MAC1
tst x6, #IPPDEXPCR_MAC1_1
csel x5, x5, x4, EQ
orr x4, x5, #DEVDISR2_FMAN1_MAC2
tst x6, #IPPDEXPCR_MAC1_2
csel x5, x5, x4, EQ
orr x4, x5, #DEVDISR2_FMAN1_MAC3
tst x6, #IPPDEXPCR_MAC1_3
csel x5, x5, x4, EQ
orr x4, x5, #DEVDISR2_FMAN1_MAC4
tst x6, #IPPDEXPCR_MAC1_4
csel x5, x5, x4, EQ
orr x4, x5, #DEVDISR2_FMAN1_MAC5
tst x6, #IPPDEXPCR_MAC1_5
csel x5, x5, x4, EQ
orr x4, x5, #DEVDISR2_FMAN1_MAC6
tst x6, #IPPDEXPCR_MAC1_6
csel x5, x5, x4, EQ
orr x4, x5, #DEVDISR2_FMAN1_MAC9
tst x6, #IPPDEXPCR_MAC1_9
csel x5, x5, x4, EQ
orr x4, x5, #DEVDISR2_FMAN1
tst x6, #IPPDEXPCR_FM1
csel x5, x5, x4, EQ
2:
/* store the DEVDISR2 override mask */
ldr x2, =BC_PSCI_BASE
add x2, x2, #AUX_01_DATA
str w5, [x2, #DEVDISR2_MASK_OFFSET]
/* x5 = DEVDISR2 override mask */
/* write IPSTPCR0 - no overrides */
ldr x0, =RCPM2_IPSTPCR0_OFFSET
ldr x1, =IPSTPCR0_VALUE
bl write_reg_rcpm2
/* x5 = DEVDISR2 override mask */
/* write IPSTPCR1 - overrides possible */
ldr x0, =RCPM2_IPSTPCR1_OFFSET
ldr x1, =IPSTPCR1_VALUE
bic x1, x1, x5
bl write_reg_rcpm2
/* write IPSTPCR2 - no overrides */
ldr x0, =RCPM2_IPSTPCR2_OFFSET
ldr x1, =IPSTPCR2_VALUE
bl write_reg_rcpm2
/* write IPSTPCR3 - no overrides */
ldr x0, =RCPM2_IPSTPCR3_OFFSET
ldr x1, =IPSTPCR3_VALUE
bl write_reg_rcpm2
/* write IPSTPCR4 - overrides possible */
ldr x2, =BC_PSCI_BASE
add x2, x2, #AUX_01_DATA
ldr w6, [x2, #DEVDISR5_MASK_OFFSET]
ldr x0, =RCPM2_IPSTPCR4_OFFSET
ldr x1, =IPSTPCR4_VALUE
bic x1, x1, x6
bl write_reg_rcpm2
/* x5 = DEVDISR2 override mask */
/* x6 = DEVDISR5 override mask */
/* poll on IPSTPACK0 */
ldr x3, =RCPM2_IPSTPACKR0_OFFSET
ldr x4, =IPSTPCR0_VALUE
ldr x7, =IPSTPACK_RETRY_CNT
3:
mov x0, x3
bl read_reg_rcpm2
cmp x0, x4
b.eq 14f
sub x7, x7, #1
cbnz x7, 3b
14:
/* poll on IPSTPACK1 */
ldr x3, =IPSTPCR1_VALUE
ldr x7, =IPSTPACK_RETRY_CNT
bic x4, x3, x5
ldr x3, =RCPM2_IPSTPACKR1_OFFSET
4:
mov x0, x3
bl read_reg_rcpm2
cmp x0, x4
b.eq 15f
sub x7, x7, #1
cbnz x7, 4b
15:
/* poll on IPSTPACK2 */
ldr x3, =RCPM2_IPSTPACKR2_OFFSET
ldr x4, =IPSTPCR2_VALUE
ldr x7, =IPSTPACK_RETRY_CNT
5:
mov x0, x3
bl read_reg_rcpm2
cmp x0, x4
b.eq 16f
sub x7, x7, #1
cbnz x7, 5b
16:
/* poll on IPSTPACK3 */
ldr x3, =RCPM2_IPSTPACKR3_OFFSET
ldr x4, =IPSTPCR3_VALUE
ldr x7, =IPSTPACK_RETRY_CNT
6:
mov x0, x3
bl read_reg_rcpm2
cmp x0, x4
b.eq 17f
sub x7, x7, #1
cbnz x7, 6b
17:
/* poll on IPSTPACK4 */
ldr x3, =IPSTPCR4_VALUE
ldr x7, =IPSTPACK_RETRY_CNT
bic x4, x3, x6
ldr x3, =RCPM2_IPSTPACKR4_OFFSET
7:
mov x0, x3
bl read_reg_rcpm2
cmp x0, x4
b.eq 18f
sub x7, x7, #1
cbnz x7, 7b
18:
ldr x7, =BC_PSCI_BASE
add x7, x7, #AUX_01_DATA
/* x5 = DEVDISR2 override mask
* x6 = DEVDISR5 override mask
* x7 = [soc_data_area] */
/* save DEVDISR1 and load new value */
mov x0, #DCFG_DEVDISR1_OFFSET
bl read_reg_dcfg
mov w13, w0
mov x0, #DCFG_DEVDISR1_OFFSET
ldr x1, =DEVDISR1_VALUE
bl write_reg_dcfg
/* save DEVDISR2 and load new value */
mov x0, #DCFG_DEVDISR2_OFFSET
bl read_reg_dcfg
mov w14, w0
mov x0, #DCFG_DEVDISR2_OFFSET
ldr x1, =DEVDISR2_VALUE
bic x1, x1, x5
bl write_reg_dcfg
/* x6 = DEVDISR5 override mask */
/* x7 = [soc_data_area] */
/* save DEVDISR3 and load new value */
mov x0, #DCFG_DEVDISR3_OFFSET
bl read_reg_dcfg
mov w15, w0
mov x0, #DCFG_DEVDISR3_OFFSET
ldr x1, =DEVDISR3_VALUE
bl write_reg_dcfg
/* save DEVDISR4 and load new value */
mov x0, #DCFG_DEVDISR4_OFFSET
bl read_reg_dcfg
mov w16, w0
mov x0, #DCFG_DEVDISR4_OFFSET
ldr x1, =DEVDISR4_VALUE
bl write_reg_dcfg
/* save DEVDISR5 and load new value */
mov x0, #DCFG_DEVDISR5_OFFSET
bl read_reg_dcfg
mov w17, w0
mov x0, #DCFG_DEVDISR5_OFFSET
ldr x1, =DEVDISR5_VALUE
bic x1, x1, x6
bl write_reg_dcfg
/* x7 = [soc_data_area] */
/* save cpuactlr and disable data prefetch */
mrs x0, CPUACTLR_EL1
str w0, [x7, #CPUACTLR_DATA_OFFSET]
bic x0, x0, #CPUACTLR_L1PCTL_MASK
msr CPUACTLR_EL1, x0
/* x6 = DEVDISR5 override mask */
/* setup registers for cache-only execution */
ldr x5, =IPSTPCR4_VALUE
bic x5, x5, x6
mov x6, #DDR_CNTRL_BASE_ADDR
mov x7, #DCSR_RCPM2_BASE
mov x8, #NXP_DCFG_ADDR
dsb sy
isb
/* set the DLL_LOCK cycle count */
ldr w1, [x6, #DDR_TIMING_CFG_4_OFFSET]
rev w2, w1
bic w2, w2, #DLL_LOCK_MASK
orr w2, w2, #DLL_LOCK_VALUE
rev w1, w2
str w1, [x6, #DDR_TIMING_CFG_4_OFFSET]
/*
* x5 = ipstpcr4 (IPSTPCR4_VALUE bic DEVDISR5_MASK)
* x6 = DDR_CNTRL_BASE_ADDR
* x7 = DCSR_RCPM2_BASE
* x8 = NXP_DCFG_ADDR
* w13 = DEVDISR1 saved value
* w14 = DEVDISR2 saved value
* w15 = DEVDISR3 saved value
* w16 = DEVDISR4 saved value
* w17 = DEVDISR5 saved value
*/
/* enter the cache-only sequence */
mov x9, #CORE_RESTARTABLE
bl final_pwrdown
/* when we are here, the core has come out of wfi and the SoC is back up */
mov x30, x18
ret
/*
* part of CPU_SUSPEND
* this function performs any SoC-specific cleanup after power-down
* in: x0 = core mask lsb
* out: none
* uses x0, x1
*/
_soc_sys_exit_pwrdn:
/* clear POWMGTDCR */
mov x1, #DCSR_RCPM2_BASE
str wzr, [x1, #RCPM2_POWMGTDCR_OFFSET]
/* clear WFIL2_EN in SCFG_COREPMCR */
mov x1, #NXP_SCFG_ADDR
str wzr, [x1, #SCFG_COREPMCR_OFFSET]
ret
/*
* write a register in the SCFG block
* in: x0 = offset
* in: w1 = value to write
* uses x0, x1, x2, x3
*/
write_reg_scfg:
ldr x2, =NXP_SCFG_ADDR
/* swap for BE */
rev w3, w1
str w3, [x2, x0]
ret
/*
* read a register in the SCFG block
* in: x0 = offset
* out: w0 = value read
* uses x0, x1, x2
*/
read_reg_scfg:
ldr x2, =NXP_SCFG_ADDR
ldr w1, [x2, x0]
/* swap for BE */
rev w0, w1
ret
/*
* write a register in the DCFG block
* in: x0 = offset
* in: w1 = value to write
* uses x0, x1, x2, x3
*/
write_reg_dcfg:
ldr x2, =NXP_DCFG_ADDR
/* swap for BE */
rev w3, w1
str w3, [x2, x0]
ret
/*
* read a register in the DCFG block
* in: x0 = offset
* out: w0 = value read
* uses x0, x1, x2
*/
read_reg_dcfg:
ldr x2, =NXP_DCFG_ADDR
ldr w1, [x2, x0]
/* swap for BE */
rev w0, w1
ret
/*
* write a register in the RCPM block
* in: x0 = offset
* in: w1 = value to write
* uses x0, x1, x2, x3
*/
write_reg_rcpm:
ldr x2, =NXP_RCPM_ADDR
/* swap for BE */
rev w3, w1
str w3, [x2, x0]
ret
/*
* read a register in the RCPM block
* in: x0 = offset
* out: w0 = value read
* uses x0, x1, x2
*/
read_reg_rcpm:
ldr x2, =NXP_RCPM_ADDR
ldr w1, [x2, x0]
/* swap for BE */
rev w0, w1
ret
/*
* write a register in the DCSR-RCPM2 block
* in: x0 = offset
* in: w1 = value to write
* uses x0, x1, x2, x3
*/
write_reg_rcpm2:
ldr x2, =DCSR_RCPM2_BASE
/* swap for BE */
rev w3, w1
str w3, [x2, x0]
ret
/*
* read a register in the DCSR-RCPM2 block
* in: x0 = offset
* out: w0 = value read
* uses x0, x1, x2
*/
read_reg_rcpm2:
ldr x2, =DCSR_RCPM2_BASE
ldr w1, [x2, x0]
/* swap for BE */
rev w0, w1
ret
/*
* this function returns the base address of the gic distributor
* in: none
* out: x0 = base address of gic distributor
* uses x0, x1
*/
_getGICD_BaseAddr:
/* read SVR and get the SoC version */
mov x0, #NXP_DCFG_ADDR
ldr w1, [x0, #DCFG_SVR_OFFSET]
rev w0, w1
/* x0 = svr */
and w0, w0, #SVR_MIN_VER_MASK
cmp w0, #SVR_MINOR_VER_0
b.ne 8f
/* load the gic base addresses for rev 1.0 parts */
ldr x0, =NXP_GICD_4K_ADDR
b 10f
8:
/* for rev 1.1 and later parts, the GIC base addresses */
/* can be at 4k or 64k offsets */
/* read the scfg reg GIC400_ADDR_ALIGN */
mov x0, #NXP_SCFG_ADDR
ldr w1, [x0, #SCFG_GIC400_ADDR_ALIGN_OFFSET]
rev w0, w1
/* x0 = GIC400_ADDR_ALIGN value */
and x0, x0, #SCFG_GIC400_ADDR_ALIGN_4KMODE_MASK
mov x1, #SCFG_GIC400_ADDR_ALIGN_4KMODE_EN
cmp x0, x1
b.ne 9f
/* load the base addresses for 4k offsets */
ldr x0, =NXP_GICD_4K_ADDR
b 10f
9:
/* load the base address for 64k offsets */
ldr x0, =NXP_GICD_64K_ADDR
10:
ret
/*
* this function returns the base address of the gic distributor
* in: none
* out: x0 = base address of gic controller
* uses x0, x1
*/
_getGICC_BaseAddr:
/* read SVR and get the SoC version */
mov x0, #NXP_DCFG_ADDR
ldr w1, [x0, #DCFG_SVR_OFFSET]
rev w0, w1
/* x0 = svr */
and w0, w0, #SVR_MIN_VER_MASK
cmp w0, #SVR_MINOR_VER_0
b.ne 8f
/* load the gic base addresses for rev 1.0 parts */
ldr x0, =NXP_GICC_4K_ADDR
b 10f
8:
/* for rev 1.1 and later parts, the GIC base addresses */
/* can be at 4k or 64k offsets */
/* read the scfg reg GIC400_ADDR_ALIGN */
mov x0, #NXP_SCFG_ADDR
ldr w1, [x0, #SCFG_GIC400_ADDR_ALIGN_OFFSET]
rev w0, w1
/* x0 = GIC400_ADDR_ALIGN value */
and x0, x0, #SCFG_GIC400_ADDR_ALIGN_4KMODE_MASK
mov x1, #SCFG_GIC400_ADDR_ALIGN_4KMODE_EN
cmp x0, x1
b.ne 9f
/* load the base addresses for 4k offsets */
ldr x0, =NXP_GICC_4K_ADDR
b 10f
9:
/* load the base address for 64k offsets */
ldr x0, =NXP_GICC_64K_ADDR
10:
ret
/*
* this function will pwrdown ddr and the final core - it will do this
* by loading itself into the icache and then executing from there
* in: x5 = ipstpcr4 (IPSTPCR4_VALUE bic DEVDISR5_MASK)
* x6 = DDR_CNTRL_BASE_ADDR
* x7 = DCSR_RCPM2_BASE
* x8 = NXP_DCFG_ADDR
* x9 = 0, restartable
* = 1, non-restartable
* w13 = DEVDISR1 saved value
* w14 = DEVDISR2 saved value
* w15 = DEVDISR3 saved value
* w16 = DEVDISR4 saved value
* w17 = DEVDISR5 saved value
* out: none
* uses x0 ~ x9
*/
/* 4Kb aligned */
.align 12
final_pwrdown:
mov x0, xzr
b touch_line_0
start_line_0:
mov x0, #1
mov x2, #DDR_SDRAM_CFG_2_FRCSR /* put ddr in self refresh - start */
ldr w3, [x6, #DDR_SDRAM_CFG_2_OFFSET]
rev w4, w3
orr w4, w4, w2
rev w3, w4
str w3, [x6, #DDR_SDRAM_CFG_2_OFFSET] /* put ddr in self refresh - end */
orr w3, w5, #DEVDISR5_MEM /* quiesce ddr clocks - start */
rev w4, w3
str w4, [x7, #RCPM2_IPSTPCR4_OFFSET] /* quiesce ddr clocks - end */
mov w3, #DEVDISR5_MEM
rev w3, w3 /* polling mask */
mov x2, #DDR_SLEEP_RETRY_CNT /* poll on ipstpack4 - start */
touch_line_0:
cbz x0, touch_line_1
start_line_1:
ldr w1, [x7, #RCPM2_IPSTPACKR4_OFFSET]
tst w1, w3
b.ne 1f
subs x2, x2, #1
b.gt start_line_1 /* poll on ipstpack4 - end */
/* if we get here, we have a timeout err */
rev w4, w5
str w4, [x7, #RCPM2_IPSTPCR4_OFFSET] /* re-enable ddr clks interface */
mov x0, #ERROR_DDR_SLEEP /* load error code */
b 2f
1:
str w4, [x8, #DCFG_DEVDISR5_OFFSET] /* disable ddr cntrlr clk in devdisr5 */
5:
wfi /* stop the final core */
cbnz x9, 5b /* if non-restartable, keep in wfi */
rev w4, w5
str w4, [x8, #DCFG_DEVDISR5_OFFSET] /* re-enable ddr in devdisr5 */
str w4, [x7, #RCPM2_IPSTPCR4_OFFSET] /* re-enable ddr clk in ipstpcr4 */
touch_line_1:
cbz x0, touch_line_2
start_line_2:
ldr w1, [x7, #RCPM2_IPSTPACKR4_OFFSET] /* poll on ipstpack4 - start */
tst w1, w3
b.eq 2f
nop
b start_line_2 /* poll on ipstpack4 - end */
2:
mov x2, #DDR_SDRAM_CFG_2_FRCSR /* take ddr out-of self refresh - start */
ldr w3, [x6, #DDR_SDRAM_CFG_2_OFFSET]
rev w4, w3
bic w4, w4, w2
rev w3, w4
mov x1, #DDR_SLEEP_RETRY_CNT /* wait for ddr cntrlr clock - start */
3:
subs x1, x1, #1
b.gt 3b /* wait for ddr cntrlr clock - end */
str w3, [x6, #DDR_SDRAM_CFG_2_OFFSET] /* take ddr out-of self refresh - end */
rev w1, w17
touch_line_2:
cbz x0, touch_line_3
start_line_3:
str w1, [x8, #DCFG_DEVDISR5_OFFSET] /* reset devdisr5 */
rev w1, w16
str w1, [x8, #DCFG_DEVDISR4_OFFSET] /* reset devdisr4 */
rev w1, w15
str w1, [x8, #DCFG_DEVDISR3_OFFSET] /* reset devdisr3 */
rev w1, w14
str w1, [x8, #DCFG_DEVDISR2_OFFSET] /* reset devdisr2 */
rev w1, w13
str w1, [x8, #DCFG_DEVDISR1_OFFSET] /* reset devdisr1 */
str wzr, [x7, #RCPM2_IPSTPCR4_OFFSET] /* reset ipstpcr4 */
str wzr, [x7, #RCPM2_IPSTPCR3_OFFSET] /* reset ipstpcr3 */
str wzr, [x7, #RCPM2_IPSTPCR2_OFFSET] /* reset ipstpcr2 */
str wzr, [x7, #RCPM2_IPSTPCR1_OFFSET] /* reset ipstpcr1 */
str wzr, [x7, #RCPM2_IPSTPCR0_OFFSET] /* reset ipstpcr0 */
b continue_restart
touch_line_3:
cbz x0, start_line_0
/* execute here after ddr is back up */
continue_restart:
/*
* if x0 = 1, all is well
* if x0 < 1, we had an error
*/
cmp x0, #1
b.ne 4f
mov x0, #0
4:
ret
/*
* Note: there is no return from this function
* this function will shutdown ddr and the final core - it will do this
* by loading itself into the icache and then executing from there
* in: x5 = ipstpcr4 (IPSTPCR4_VALUE bic DEVDISR5_MASK)
* x6 = DDR_CNTRL_BASE_ADDR
* x7 = DCSR_RCPM2_BASE
* x8 = NXP_DCFG_ADDR
* out: none
* uses x0 ~ x8
*/
/* 4Kb aligned */
.align 12
final_shutdown:
mov x0, xzr
b touch_line0
start_line0:
mov x0, #1
mov x2, #DDR_SDRAM_CFG_2_FRCSR /* put ddr in self refresh - start */
ldr w3, [x6, #DDR_SDRAM_CFG_2_OFFSET]
rev w4, w3
orr w4, w4, w2
rev w3, w4
str w3, [x6, #DDR_SDRAM_CFG_2_OFFSET] /* put ddr in self refresh - end */
orr w3, w5, #DEVDISR5_MEM /* quiesce ddr clocks - start */
rev w4, w3
str w4, [x7, #RCPM2_IPSTPCR4_OFFSET] /* quiesce ddr clocks - end */
mov w3, #DEVDISR5_MEM
rev w3, w3 /* polling mask */
mov x2, #DDR_SLEEP_RETRY_CNT /* poll on ipstpack4 - start */
touch_line0:
cbz x0, touch_line1
start_line1:
ldr w1, [x7, #RCPM2_IPSTPACKR4_OFFSET]
tst w1, w3
b.ne 1f
subs x2, x2, #1
b.gt start_line1 /* poll on ipstpack4 - end */
nop
nop
nop
nop
1:
str w4, [x8, #DCFG_DEVDISR5_OFFSET] /* disable ddr cntrlr clk in devdisr5 */
5:
wfi /* stop the final core */
b 5b /* stay here until POR */
nop
nop
nop
touch_line1:
cbz x0, start_line0