| /* SPDX-License-Identifier: GPL-2.0+ */ |
| /* |
| * Copyright 2015 Freescale Semiconductor, Inc. |
| * Author: Wang Dongsheng <dongsheng.wang@freescale.com> |
| */ |
| |
| #include <config.h> |
| #include <linux/linkage.h> |
| |
| #include <asm/armv7.h> |
| #include <asm/arch-armv7/generictimer.h> |
| #include <asm/psci.h> |
| |
| #define RCPM_TWAITSR 0x04C |
| |
| #define SCFG_CORE0_SFT_RST 0x130 |
| #define SCFG_CORESRENCR 0x204 |
| |
| #define DCFG_CCSR_RSTCR 0x0B0 |
| #define DCFG_CCSR_RSTCR_RESET_REQ 0x2 |
| #define DCFG_CCSR_BRR 0x0E4 |
| #define DCFG_CCSR_SCRATCHRW1 0x200 |
| |
| #define PSCI_FN_PSCI_VERSION_FEATURE_MASK 0x0 |
| #define PSCI_FN_CPU_SUSPEND_FEATURE_MASK 0x0 |
| #define PSCI_FN_CPU_OFF_FEATURE_MASK 0x0 |
| #define PSCI_FN_CPU_ON_FEATURE_MASK 0x0 |
| #define PSCI_FN_AFFINITY_INFO_FEATURE_MASK 0x0 |
| #define PSCI_FN_SYSTEM_OFF_FEATURE_MASK 0x0 |
| #define PSCI_FN_SYSTEM_RESET_FEATURE_MASK 0x0 |
| #define PSCI_FN_SYSTEM_SUSPEND_FEATURE_MASK 0x0 |
| |
| .pushsection ._secure.text, "ax" |
| |
| .arch_extension sec |
| |
| .align 5 |
| |
| #define ONE_MS (COUNTER_FREQUENCY / 1000) |
| #define RESET_WAIT (30 * ONE_MS) |
| |
| .globl psci_version |
| psci_version: |
| movw r0, #0 |
| movt r0, #1 |
| |
| bx lr |
| |
| _ls102x_psci_supported_table: |
| .word ARM_PSCI_0_2_FN_PSCI_VERSION |
| .word PSCI_FN_PSCI_VERSION_FEATURE_MASK |
| .word ARM_PSCI_0_2_FN_CPU_SUSPEND |
| .word PSCI_FN_CPU_SUSPEND_FEATURE_MASK |
| .word ARM_PSCI_0_2_FN_CPU_OFF |
| .word PSCI_FN_CPU_OFF_FEATURE_MASK |
| .word ARM_PSCI_0_2_FN_CPU_ON |
| .word PSCI_FN_CPU_ON_FEATURE_MASK |
| .word ARM_PSCI_0_2_FN_AFFINITY_INFO |
| .word PSCI_FN_AFFINITY_INFO_FEATURE_MASK |
| .word ARM_PSCI_0_2_FN_SYSTEM_OFF |
| .word PSCI_FN_SYSTEM_OFF_FEATURE_MASK |
| .word ARM_PSCI_0_2_FN_SYSTEM_RESET |
| .word PSCI_FN_SYSTEM_RESET_FEATURE_MASK |
| .word ARM_PSCI_1_0_FN_SYSTEM_SUSPEND |
| .word PSCI_FN_SYSTEM_SUSPEND_FEATURE_MASK |
| .word 0 |
| .word ARM_PSCI_RET_NI |
| |
| .globl psci_features |
| psci_features: |
| adr r2, _ls102x_psci_supported_table |
| 1: ldr r3, [r2] |
| cmp r3, #0 |
| beq out_psci_features |
| cmp r1, r3 |
| addne r2, r2, #8 |
| bne 1b |
| |
| out_psci_features: |
| ldr r0, [r2, #4] |
| bx lr |
| |
| @ r0: return value ARM_PSCI_RET_SUCCESS or ARM_PSCI_RET_INVAL |
| @ r1: input target CPU ID in MPIDR format, original value in r1 may be dropped |
| @ r4: output validated CPU ID if ARM_PSCI_RET_SUCCESS returns, meaningless for |
| @ ARM_PSCI_RET_INVAL,suppose caller saves r4 before calling |
| LENTRY(psci_check_target_cpu_id) |
| @ Get the real CPU number |
| and r4, r1, #0xff |
| mov r0, #ARM_PSCI_RET_INVAL |
| |
| @ Bit[31:24], bits must be zero. |
| tst r1, #0xff000000 |
| bxne lr |
| |
| @ Affinity level 2 - Cluster: only one cluster in LS1021xa. |
| tst r1, #0xff0000 |
| bxne lr |
| |
| @ Affinity level 1 - Processors: should be in 0xf00 format. |
| lsr r1, r1, #8 |
| teq r1, #0xf |
| bxne lr |
| |
| @ Affinity level 0 - CPU: only 0, 1 are valid in LS1021xa. |
| cmp r4, #2 |
| bxge lr |
| |
| mov r0, #ARM_PSCI_RET_SUCCESS |
| bx lr |
| ENDPROC(psci_check_target_cpu_id) |
| |
| @ r1 = target CPU |
| @ r2 = target PC |
| .globl psci_cpu_on |
| psci_cpu_on: |
| push {r4, r5, r6, lr} |
| |
| @ Clear and Get the correct CPU number |
| @ r1 = 0xf01 |
| bl psci_check_target_cpu_id |
| cmp r0, #ARM_PSCI_RET_INVAL |
| beq out_psci_cpu_on |
| |
| mov r0, r4 |
| mov r1, r2 |
| mov r2, r3 |
| bl psci_save |
| mov r1, r4 |
| |
| @ Get DCFG base address |
| movw r4, #(CONFIG_SYS_FSL_GUTS_ADDR & 0xffff) |
| movt r4, #(CONFIG_SYS_FSL_GUTS_ADDR >> 16) |
| |
| @ Detect target CPU state |
| ldr r2, [r4, #DCFG_CCSR_BRR] |
| rev r2, r2 |
| lsr r2, r2, r1 |
| ands r2, r2, #1 |
| beq holdoff_release |
| |
| @ Reset target CPU |
| @ Get SCFG base address |
| movw r0, #(CONFIG_SYS_FSL_SCFG_ADDR & 0xffff) |
| movt r0, #(CONFIG_SYS_FSL_SCFG_ADDR >> 16) |
| |
| @ Enable CORE Soft Reset |
| movw r5, #0 |
| movt r5, #(1 << 15) |
| rev r5, r5 |
| str r5, [r0, #SCFG_CORESRENCR] |
| |
| @ Get CPUx offset register |
| mov r6, #0x4 |
| mul r6, r6, r1 |
| add r2, r0, r6 |
| |
| @ Do reset on target CPU |
| movw r5, #0 |
| movt r5, #(1 << 15) |
| rev r5, r5 |
| str r5, [r2, #SCFG_CORE0_SFT_RST] |
| |
| @ Wait target CPU up |
| timer_wait r2, RESET_WAIT |
| |
| @ Disable CORE soft reset |
| mov r5, #0 |
| str r5, [r0, #SCFG_CORESRENCR] |
| |
| holdoff_release: |
| @ Release on target CPU |
| ldr r2, [r4, #DCFG_CCSR_BRR] |
| mov r6, #1 |
| lsl r6, r6, r1 @ 32 bytes per CPU |
| |
| rev r6, r6 |
| orr r2, r2, r6 |
| str r2, [r4, #DCFG_CCSR_BRR] |
| |
| @ Set secondary boot entry |
| ldr r6, =psci_cpu_entry |
| rev r6, r6 |
| str r6, [r4, #DCFG_CCSR_SCRATCHRW1] |
| |
| isb |
| dsb |
| |
| @ Return |
| mov r0, #ARM_PSCI_RET_SUCCESS |
| |
| out_psci_cpu_on: |
| pop {r4, r5, r6, lr} |
| bx lr |
| |
| .globl psci_cpu_off |
| psci_cpu_off: |
| bl psci_cpu_off_common |
| |
| 1: wfi |
| b 1b |
| |
| .globl psci_affinity_info |
| psci_affinity_info: |
| push {lr} |
| |
| mov r0, #ARM_PSCI_RET_INVAL |
| |
| @ Verify Affinity level |
| cmp r2, #0 |
| bne out_affinity_info |
| |
| bl psci_check_target_cpu_id |
| cmp r0, #ARM_PSCI_RET_INVAL |
| beq out_affinity_info |
| mov r1, r4 |
| |
| @ Get RCPM base address |
| movw r4, #(CONFIG_SYS_FSL_RCPM_ADDR & 0xffff) |
| movt r4, #(CONFIG_SYS_FSL_RCPM_ADDR >> 16) |
| |
| mov r0, #PSCI_AFFINITY_LEVEL_ON |
| |
| @ Detect target CPU state |
| ldr r2, [r4, #RCPM_TWAITSR] |
| rev r2, r2 |
| lsr r2, r2, r1 |
| ands r2, r2, #1 |
| beq out_affinity_info |
| |
| mov r0, #PSCI_AFFINITY_LEVEL_OFF |
| |
| out_affinity_info: |
| pop {pc} |
| |
| .globl psci_system_reset |
| psci_system_reset: |
| @ Get DCFG base address |
| movw r1, #(CONFIG_SYS_FSL_GUTS_ADDR & 0xffff) |
| movt r1, #(CONFIG_SYS_FSL_GUTS_ADDR >> 16) |
| |
| mov r2, #DCFG_CCSR_RSTCR_RESET_REQ |
| rev r2, r2 |
| str r2, [r1, #DCFG_CCSR_RSTCR] |
| |
| 1: wfi |
| b 1b |
| |
| .globl psci_system_suspend |
| psci_system_suspend: |
| push {lr} |
| |
| bl ls1_system_suspend |
| |
| pop {pc} |
| |
| .popsection |