diff --git a/arch/arm64/boot/dts/rockchip/rk3566-pinenote.dtsi b/arch/arm64/boot/dts/rockchip/rk3566-pinenote.dtsi index 93f2998b8610..b85e20cbc205 100644 --- a/arch/arm64/boot/dts/rockchip/rk3566-pinenote.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3566-pinenote.dtsi @@ -6,6 +6,7 @@ #include #include #include +#include #include "rk3566.dtsi" @@ -132,6 +133,28 @@ }; }; + rockchip-suspend { + compatible = "rockchip,pm-rk3568"; + status = "okay"; + rockchip,sleep-debug-en = <0>; + rockchip,sleep-mode-config = < + (0 + | RKPM_SLP_CENTER_OFF + | RKPM_SLP_ARMOFF_LOGOFF + | RKPM_SLP_PMIC_LP + | RKPM_SLP_HW_PLLS_OFF + | RKPM_SLP_PMUALIVE_32K + | RKPM_SLP_OSC_DIS + | RKPM_SLP_32K_PVTM + ) + >; + rockchip,wakeup-config = < + (0 + | RKPM_GPIO_WKUP_EN + ) + >; + }; + sdio_pwrseq: sdio-pwrseq { compatible = "mmc-pwrseq-simple"; clocks = <&rk817 1>; diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig index aadc395ee168..4a0693d9ab82 100644 --- a/drivers/firmware/Kconfig +++ b/drivers/firmware/Kconfig @@ -179,6 +179,13 @@ config MTK_ADSP_IPC ADSP exists on some mtk processors. Client might use shared memory to exchange information with ADSP. +config ROCKCHIP_SIP + tristate "Rockchip SIP interface" + depends on HAVE_ARM_SMCCC && ARCH_ROCKCHIP + help + Say Y here if you want to enable SIP callbacks for Rockchip platforms + This option enables support for communicating with the ATF. + config SYSFB bool select BOOT_VESA_SUPPORT diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile index 4ddec2820c96..32850a0d1f06 100644 --- a/drivers/firmware/Makefile +++ b/drivers/firmware/Makefile @@ -16,6 +16,7 @@ obj-$(CONFIG_FIRMWARE_MEMMAP) += memmap.o obj-$(CONFIG_MTK_ADSP_IPC) += mtk-adsp-ipc.o obj-$(CONFIG_RASPBERRYPI_FIRMWARE) += raspberrypi.o obj-$(CONFIG_FW_CFG_SYSFS) += qemu_fw_cfg.o +obj-$(CONFIG_ROCKCHIP_SIP) += rockchip_sip.o obj-$(CONFIG_SYSFB) += sysfb.o obj-$(CONFIG_SYSFB_SIMPLEFB) += sysfb_simplefb.o obj-$(CONFIG_TH1520_AON_PROTOCOL) += thead,th1520-aon.o diff --git a/drivers/firmware/rockchip_sip.c b/drivers/firmware/rockchip_sip.c new file mode 100644 index 000000000000..1e1d8cac0ab0 --- /dev/null +++ b/drivers/firmware/rockchip_sip.c @@ -0,0 +1,546 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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. + * + * Copyright (C) 2016, Fuzhou Rockchip Electronics Co., Ltd + */ + +#include +#include +#include +#include +#include +#ifdef CONFIG_ARM +#include +#endif +#include +#include +#include +#include +#include + +#ifdef CONFIG_64BIT +#define PSCI_FN_NATIVE(version, name) PSCI_##version##_FN64_##name +#else +#define PSCI_FN_NATIVE(version, name) PSCI_##version##_FN_##name +#endif + +#define SIZE_PAGE(n) ((n) << 12) + +static struct arm_smccc_res __invoke_sip_fn_smc(unsigned long function_id, + unsigned long arg0, + unsigned long arg1, + unsigned long arg2) +{ + struct arm_smccc_res res; + + arm_smccc_smc(function_id, arg0, arg1, arg2, 0, 0, 0, 0, &res); + return res; +} + +struct arm_smccc_res sip_smc_dram(u32 arg0, u32 arg1, u32 arg2) +{ + return __invoke_sip_fn_smc(SIP_DRAM_CONFIG, arg0, arg1, arg2); +} +EXPORT_SYMBOL_GPL(sip_smc_dram); + +struct arm_smccc_res sip_smc_get_atf_version(void) +{ + return __invoke_sip_fn_smc(SIP_ATF_VERSION, 0, 0, 0); +} +EXPORT_SYMBOL_GPL(sip_smc_get_atf_version); + +struct arm_smccc_res sip_smc_get_sip_version(void) +{ + return __invoke_sip_fn_smc(SIP_SIP_VERSION, 0, 0, 0); +} +EXPORT_SYMBOL_GPL(sip_smc_get_sip_version); + +int sip_smc_set_suspend_mode(u32 ctrl, u32 config1, u32 config2) +{ + struct arm_smccc_res res; + + res = __invoke_sip_fn_smc(SIP_SUSPEND_MODE, ctrl, config1, config2); + return res.a0; +} +EXPORT_SYMBOL_GPL(sip_smc_set_suspend_mode); + +struct arm_smccc_res sip_smc_get_suspend_info(u32 info) +{ + struct arm_smccc_res res; + + res = __invoke_sip_fn_smc(SIP_SUSPEND_MODE, info, 0, 0); + return res; +} +EXPORT_SYMBOL_GPL(sip_smc_get_suspend_info); + +int sip_smc_virtual_poweroff(void) +{ + struct arm_smccc_res res; + + res = __invoke_sip_fn_smc(PSCI_FN_NATIVE(1_0, SYSTEM_SUSPEND), 0, 0, 0); + return res.a0; +} +EXPORT_SYMBOL_GPL(sip_smc_virtual_poweroff); + +int sip_smc_remotectl_config(u32 func, u32 data) +{ + struct arm_smccc_res res; + + res = __invoke_sip_fn_smc(SIP_REMOTECTL_CFG, func, data, 0); + + return res.a0; +} +EXPORT_SYMBOL_GPL(sip_smc_remotectl_config); + +u32 sip_smc_secure_reg_read(u32 addr_phy) +{ + struct arm_smccc_res res; + + res = __invoke_sip_fn_smc(SIP_ACCESS_REG, 0, addr_phy, SECURE_REG_RD); + if (res.a0) + pr_err("%s error: %d, addr phy: 0x%x\n", + __func__, (int)res.a0, addr_phy); + + return res.a1; +} +EXPORT_SYMBOL_GPL(sip_smc_secure_reg_read); + +int sip_smc_secure_reg_write(u32 addr_phy, u32 val) +{ + struct arm_smccc_res res; + + res = __invoke_sip_fn_smc(SIP_ACCESS_REG, val, addr_phy, SECURE_REG_WR); + if (res.a0) + pr_err("%s error: %d, addr phy: 0x%x\n", + __func__, (int)res.a0, addr_phy); + + return res.a0; +} +EXPORT_SYMBOL_GPL(sip_smc_secure_reg_write); + +static void *sip_map(phys_addr_t start, size_t size) +{ + struct page **pages; + phys_addr_t page_start; + unsigned int page_count; + pgprot_t prot; + unsigned int i; + void *vaddr; + + if (!pfn_valid(__phys_to_pfn(start))) + return ioremap(start, size); + + page_start = start - offset_in_page(start); + page_count = DIV_ROUND_UP(size + offset_in_page(start), PAGE_SIZE); + + prot = pgprot_noncached(PAGE_KERNEL); + + pages = kmalloc_array(page_count, sizeof(struct page *), GFP_KERNEL); + if (!pages) { + pr_err("%s: Failed to allocate array for %u pages\n", + __func__, page_count); + return NULL; + } + + for (i = 0; i < page_count; i++) + pages[i] = phys_to_page(page_start + i * PAGE_SIZE); + + vaddr = vmap(pages, page_count, VM_MAP, prot); + kfree(pages); + + /* + * Since vmap() uses page granularity, we must add the offset + * into the page here, to get the byte granularity address + * into the mapping to represent the actual "start" location. + */ + return vaddr + offset_in_page(start); +} + +struct arm_smccc_res sip_smc_request_share_mem(u32 page_num, + share_page_type_t page_type) +{ + struct arm_smccc_res res; + unsigned long share_mem_phy; + + res = __invoke_sip_fn_smc(SIP_SHARE_MEM, page_num, page_type, 0); + if (IS_SIP_ERROR(res.a0)) + goto error; + + share_mem_phy = res.a1; + res.a1 = (unsigned long)sip_map(share_mem_phy, SIZE_PAGE(page_num)); + +error: + return res; +} +EXPORT_SYMBOL_GPL(sip_smc_request_share_mem); + +struct arm_smccc_res sip_smc_mcu_el3fiq(u32 arg0, u32 arg1, u32 arg2) +{ + return __invoke_sip_fn_smc(SIP_MCU_EL3FIQ_CFG, arg0, arg1, arg2); +} +EXPORT_SYMBOL_GPL(sip_smc_mcu_el3fiq); + +struct arm_smccc_res sip_smc_vpu_reset(u32 arg0, u32 arg1, u32 arg2) +{ + struct arm_smccc_res res; + + res = __invoke_sip_fn_smc(PSCI_SIP_VPU_RESET, arg0, arg1, arg2); + return res; +} +EXPORT_SYMBOL_GPL(sip_smc_vpu_reset); + +struct arm_smccc_res sip_smc_bus_config(u32 arg0, u32 arg1, u32 arg2) +{ + struct arm_smccc_res res; + + res = __invoke_sip_fn_smc(SIP_BUS_CFG, arg0, arg1, arg2); + return res; +} +EXPORT_SYMBOL_GPL(sip_smc_bus_config); + +struct arm_smccc_res sip_smc_lastlog_request(void) +{ + struct arm_smccc_res res; + void __iomem *addr1, *addr2; + + res = __invoke_sip_fn_smc(SIP_LAST_LOG, local_clock(), 0, 0); + if (IS_SIP_ERROR(res.a0)) + return res; + + addr1 = sip_map(res.a1, res.a3); + if (!addr1) { + pr_err("%s: share memory buffer0 ioremap failed\n", __func__); + res.a0 = SIP_RET_INVALID_ADDRESS; + return res; + } + addr2 = sip_map(res.a2, res.a3); + if (!addr2) { + pr_err("%s: share memory buffer1 ioremap failed\n", __func__); + res.a0 = SIP_RET_INVALID_ADDRESS; + return res; + } + + res.a1 = (unsigned long)addr1; + res.a2 = (unsigned long)addr2; + + return res; +} +EXPORT_SYMBOL_GPL(sip_smc_lastlog_request); + +/************************** fiq debugger **************************************/ +/* + * AArch32 is not allowed to call SMC64(ATF framework does not support), so we + * don't change SIP_UARTDBG_FN to SIP_UARTDBG_CFG64 even when cpu is AArch32 + * mode. Let ATF support SIP_UARTDBG_CFG, and we just initialize SIP_UARTDBG_FN + * depends on compile option(CONFIG_ARM or CONFIG_ARM64). + */ +#ifdef CONFIG_ARM64 +#define SIP_UARTDBG_FN SIP_UARTDBG_CFG64 +#else +#define SIP_UARTDBG_FN SIP_UARTDBG_CFG +static int firmware_64_32bit; +#endif + +static int fiq_sip_enabled; +static int fiq_target_cpu; +static phys_addr_t ft_fiq_mem_phy; +static void __iomem *ft_fiq_mem_base; +static void (*sip_fiq_debugger_uart_irq_tf)(struct pt_regs _pt_regs, + unsigned long cpu); +int sip_fiq_debugger_is_enabled(void) +{ + return fiq_sip_enabled; +} +EXPORT_SYMBOL_GPL(sip_fiq_debugger_is_enabled); + +static struct pt_regs sip_fiq_debugger_get_pt_regs(void *reg_base, + unsigned long sp_el1) +{ + struct pt_regs fiq_pt_regs; + __maybe_unused struct sm_nsec_ctx *nsec_ctx = reg_base; + __maybe_unused struct gp_regs_ctx *gp_regs = reg_base; + +#ifdef CONFIG_ARM64 + /* + * 64-bit ATF + 64-bit kernel + */ + /* copy cpu context: x0 ~ spsr_el3 */ + memcpy(&fiq_pt_regs, reg_base, 8 * 31); + + /* copy pstate: spsr_el3 */ + memcpy(&fiq_pt_regs.pstate, reg_base + 0x110, 8); + fiq_pt_regs.sp = sp_el1; + + /* copy pc: elr_el3 */ + memcpy(&fiq_pt_regs.pc, reg_base + 0x118, 8); +#else + if (firmware_64_32bit == FIRMWARE_ATF_64BIT) { + /* + * 64-bit ATF + 32-bit kernel + */ + fiq_pt_regs.ARM_r0 = gp_regs->x0; + fiq_pt_regs.ARM_r1 = gp_regs->x1; + fiq_pt_regs.ARM_r2 = gp_regs->x2; + fiq_pt_regs.ARM_r3 = gp_regs->x3; + fiq_pt_regs.ARM_r4 = gp_regs->x4; + fiq_pt_regs.ARM_r5 = gp_regs->x5; + fiq_pt_regs.ARM_r6 = gp_regs->x6; + fiq_pt_regs.ARM_r7 = gp_regs->x7; + fiq_pt_regs.ARM_r8 = gp_regs->x8; + fiq_pt_regs.ARM_r9 = gp_regs->x9; + fiq_pt_regs.ARM_r10 = gp_regs->x10; + fiq_pt_regs.ARM_fp = gp_regs->x11; + fiq_pt_regs.ARM_ip = gp_regs->x12; + fiq_pt_regs.ARM_sp = gp_regs->x19; /* aarch32 svc_r13 */ + fiq_pt_regs.ARM_lr = gp_regs->x18; /* aarch32 svc_r14 */ + fiq_pt_regs.ARM_cpsr = gp_regs->spsr_el3; + fiq_pt_regs.ARM_pc = gp_regs->elr_el3; + } else { + /* + * 32-bit tee firmware + 32-bit kernel + */ + fiq_pt_regs.ARM_r0 = nsec_ctx->r0; + fiq_pt_regs.ARM_r1 = nsec_ctx->r1; + fiq_pt_regs.ARM_r2 = nsec_ctx->r2; + fiq_pt_regs.ARM_r3 = nsec_ctx->r3; + fiq_pt_regs.ARM_r4 = nsec_ctx->r4; + fiq_pt_regs.ARM_r5 = nsec_ctx->r5; + fiq_pt_regs.ARM_r6 = nsec_ctx->r6; + fiq_pt_regs.ARM_r7 = nsec_ctx->r7; + fiq_pt_regs.ARM_r8 = nsec_ctx->r8; + fiq_pt_regs.ARM_r9 = nsec_ctx->r9; + fiq_pt_regs.ARM_r10 = nsec_ctx->r10; + fiq_pt_regs.ARM_fp = nsec_ctx->r11; + fiq_pt_regs.ARM_ip = nsec_ctx->r12; + fiq_pt_regs.ARM_sp = nsec_ctx->svc_sp; + fiq_pt_regs.ARM_lr = nsec_ctx->svc_lr; + fiq_pt_regs.ARM_cpsr = nsec_ctx->mon_spsr; + + /* + * 'nsec_ctx->mon_lr' is not the fiq break point's PC, because it will + * be override as 'psci_fiq_debugger_uart_irq_tf_cb' for optee-os to + * jump to fiq_debugger handler. + * + * As 'nsec_ctx->und_lr' is not used for kernel, so optee-os uses it to + * deliver fiq break point's PC. + * + */ + fiq_pt_regs.ARM_pc = nsec_ctx->und_lr; + } +#endif + + return fiq_pt_regs; +} + +static void sip_fiq_debugger_uart_irq_tf_cb(unsigned long sp_el1, + unsigned long offset, + unsigned long cpu) +{ + struct pt_regs fiq_pt_regs; + char *cpu_context; + + /* calling fiq handler */ + if (ft_fiq_mem_base) { + cpu_context = (char *)ft_fiq_mem_base + offset; + fiq_pt_regs = sip_fiq_debugger_get_pt_regs(cpu_context, sp_el1); + sip_fiq_debugger_uart_irq_tf(fiq_pt_regs, cpu); + } + + /* fiq handler done, return to EL3(then EL3 return to EL1 entry) */ + __invoke_sip_fn_smc(SIP_UARTDBG_FN, 0, 0, UARTDBG_CFG_OSHDL_TO_OS); +} + +int sip_fiq_debugger_uart_irq_tf_init(u32 irq_id, void *callback_fn) +{ + struct arm_smccc_res res; + + /* init fiq debugger callback */ + sip_fiq_debugger_uart_irq_tf = callback_fn; + res = __invoke_sip_fn_smc(SIP_UARTDBG_FN, irq_id, + (unsigned long)sip_fiq_debugger_uart_irq_tf_cb, + UARTDBG_CFG_INIT); + if (IS_SIP_ERROR(res.a0)) { + pr_err("%s error: %d\n", __func__, (int)res.a0); + return res.a0; + } + + /* share memory ioremap */ + if (!ft_fiq_mem_base) { + ft_fiq_mem_phy = res.a1; + ft_fiq_mem_base = sip_map(ft_fiq_mem_phy, + FIQ_UARTDBG_SHARE_MEM_SIZE); + if (!ft_fiq_mem_base) { + pr_err("%s: share memory ioremap failed\n", __func__); + return -ENOMEM; + } + } + + fiq_sip_enabled = 1; + + return SIP_RET_SUCCESS; +} +EXPORT_SYMBOL_GPL(sip_fiq_debugger_uart_irq_tf_init); + +static ulong cpu_logical_map_mpidr(u32 cpu) +{ +#ifdef MODULE + /* Empirically, local "cpu_logical_map()" for rockchip platforms */ + ulong mpidr = 0x00; + + if (cpu < 4) + /* 0x00, 0x01, 0x02, 0x03 */ + mpidr = cpu; + else if (cpu < 8) + /* 0x100, 0x101, 0x102, 0x103 */ + mpidr = 0x100 | (cpu - 4); + else + pr_err("Unsupported map cpu: %d\n", cpu); + + return mpidr; +#else + return cpu_logical_map(cpu); +#endif +} + +int sip_fiq_debugger_switch_cpu(u32 cpu) +{ + struct arm_smccc_res res; + + fiq_target_cpu = cpu; + res = __invoke_sip_fn_smc(SIP_UARTDBG_FN, cpu_logical_map_mpidr(cpu), + 0, UARTDBG_CFG_OSHDL_CPUSW); + return res.a0; +} + +int sip_fiq_debugger_sdei_switch_cpu(u32 cur_cpu, u32 target_cpu, u32 flag) +{ + struct arm_smccc_res res; + + res = __invoke_sip_fn_smc(SIP_SDEI_FIQ_DBG_SWITCH_CPU, + cur_cpu, target_cpu, flag); + return res.a0; +} + +int sip_fiq_debugger_sdei_get_event_id(u32 *fiq, u32 *sw_cpu, u32 *flag) +{ + struct arm_smccc_res res; + + res = __invoke_sip_fn_smc(SIP_SDEI_FIQ_DBG_GET_EVENT_ID, + 0, 0, 0); + *fiq = res.a1; + *sw_cpu = res.a2; + if (flag) + *flag = res.a3; + + return res.a0; +} + +EXPORT_SYMBOL_GPL(sip_fiq_debugger_switch_cpu); + +void sip_fiq_debugger_enable_debug(bool enable) +{ + unsigned long val; + + val = enable ? UARTDBG_CFG_OSHDL_DEBUG_ENABLE : + UARTDBG_CFG_OSHDL_DEBUG_DISABLE; + + __invoke_sip_fn_smc(SIP_UARTDBG_FN, 0, 0, val); +} +EXPORT_SYMBOL_GPL(sip_fiq_debugger_enable_debug); + +int sip_fiq_debugger_set_print_port(u32 port_phyaddr, u32 baudrate) +{ + struct arm_smccc_res res; + + res = __invoke_sip_fn_smc(SIP_UARTDBG_FN, port_phyaddr, baudrate, + UARTDBG_CFG_PRINT_PORT); + return res.a0; +} +EXPORT_SYMBOL_GPL(sip_fiq_debugger_set_print_port); + +int sip_fiq_debugger_request_share_memory(void) +{ + struct arm_smccc_res res; + + /* request page share memory */ + res = sip_smc_request_share_mem(FIQ_UARTDBG_PAGE_NUMS, + SHARE_PAGE_TYPE_UARTDBG); + if (IS_SIP_ERROR(res.a0)) + return res.a0; + + return SIP_RET_SUCCESS; +} +EXPORT_SYMBOL_GPL(sip_fiq_debugger_request_share_memory); + +int sip_fiq_debugger_get_target_cpu(void) +{ + return fiq_target_cpu; +} +EXPORT_SYMBOL_GPL(sip_fiq_debugger_get_target_cpu); + +void sip_fiq_debugger_enable_fiq(bool enable, uint32_t tgt_cpu) +{ + u32 en; + + fiq_target_cpu = tgt_cpu; + en = enable ? UARTDBG_CFG_FIQ_ENABEL : UARTDBG_CFG_FIQ_DISABEL; + __invoke_sip_fn_smc(SIP_UARTDBG_FN, tgt_cpu, 0, en); +} +EXPORT_SYMBOL_GPL(sip_fiq_debugger_enable_fiq); + +/******************************************************************************/ +#ifdef CONFIG_ARM +static __init int sip_firmware_init(void) +{ + struct arm_smccc_res res; + + if (!psci_smp_available()) + return 0; + + /* + * OP-TEE works on kernel 3.10 and 4.4 and we have different sip + * implement. We should tell OP-TEE the current rockchip sip version. + */ + res = __invoke_sip_fn_smc(SIP_SIP_VERSION, SIP_IMPLEMENT_V2, + SECURE_REG_WR, 0); + if (IS_SIP_ERROR(res.a0)) + pr_err("%s: set rockchip sip version v2 failed\n", __func__); + + /* + * Currently, we support: + * + * 1. 64-bit ATF + 64-bit kernel; + * 2. 64-bit ATF + 32-bit kernel; + * 3. 32-bit TEE + 32-bit kernel; + * + * We need to detect which case of above and record in firmware_64_32bit + * We get info from cpuid and compare with all supported ARMv7 cpu. + */ + switch (read_cpuid_part()) { + case ARM_CPU_PART_CORTEX_A7: + case ARM_CPU_PART_CORTEX_A8: + case ARM_CPU_PART_CORTEX_A9: + case ARM_CPU_PART_CORTEX_A12: + case ARM_CPU_PART_CORTEX_A15: + case ARM_CPU_PART_CORTEX_A17: + firmware_64_32bit = FIRMWARE_TEE_32BIT; + break; + default: + firmware_64_32bit = FIRMWARE_ATF_64BIT; + break; + } + + return 0; +} +arch_initcall(sip_firmware_init); +#endif + +MODULE_DESCRIPTION("Rockchip SIP Call"); +MODULE_LICENSE("GPL"); diff --git a/drivers/soc/rockchip/Kconfig b/drivers/soc/rockchip/Kconfig index 785f60c6f3ad..17ccb0fb19e1 100644 --- a/drivers/soc/rockchip/Kconfig +++ b/drivers/soc/rockchip/Kconfig @@ -30,4 +30,10 @@ config ROCKCHIP_DTPM on this platform. That will create all the power capping capable devices. +config ROCKCHIP_SUSPEND_MODE + tristate "Rockchip suspend mode config" + depends on ROCKCHIP_SIP + help + Say Y here if you want to set the suspend mode to the ATF. + endif diff --git a/drivers/soc/rockchip/Makefile b/drivers/soc/rockchip/Makefile index 23d414433c8c..d6267628340b 100644 --- a/drivers/soc/rockchip/Makefile +++ b/drivers/soc/rockchip/Makefile @@ -5,3 +5,4 @@ obj-$(CONFIG_ROCKCHIP_GRF) += grf.o obj-$(CONFIG_ROCKCHIP_IODOMAIN) += io-domain.o obj-$(CONFIG_ROCKCHIP_DTPM) += dtpm.o +obj-$(CONFIG_ROCKCHIP_SUSPEND_MODE) += rockchip_pm_config.o diff --git a/drivers/soc/rockchip/rockchip_pm_config.c b/drivers/soc/rockchip/rockchip_pm_config.c new file mode 100644 index 000000000000..7d1286ccf653 --- /dev/null +++ b/drivers/soc/rockchip/rockchip_pm_config.c @@ -0,0 +1,291 @@ +/* + * Rockchip Generic power configuration support. + * + * Copyright (c) 2017 ROCKCHIP, Co. Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include <../drivers/regulator/internal.h> + +#define PM_INVALID_GPIO 0xffff +#define MAX_ON_OFF_REG_NUM 30 +#define MAX_ON_OFF_REG_PROP_NAME_LEN 60 + +enum rk_pm_state { + RK_PM_MEM = 0, + RK_PM_MEM_LITE, + RK_PM_MEM_ULTRA, + RK_PM_STATE_MAX +}; + +static struct rk_on_off_regulator_list { + struct regulator_dev *on_reg_list[MAX_ON_OFF_REG_NUM]; + struct regulator_dev *off_reg_list[MAX_ON_OFF_REG_NUM]; +} on_off_regs_list[RK_PM_STATE_MAX]; + +static const struct of_device_id pm_match_table[] = { + { .compatible = "rockchip,pm-px30",}, + { .compatible = "rockchip,pm-rk1808",}, + { .compatible = "rockchip,pm-rk322x",}, + { .compatible = "rockchip,pm-rk3288",}, + { .compatible = "rockchip,pm-rk3308",}, + { .compatible = "rockchip,pm-rk3328",}, + { .compatible = "rockchip,pm-rk3368",}, + { .compatible = "rockchip,pm-rk3399",}, + { .compatible = "rockchip,pm-rk3568",}, + { .compatible = "rockchip,pm-rv1126",}, + { }, +}; + +static void rockchip_pm_virt_pwroff_prepare(void) +{ + int error; + + regulator_suspend_prepare(PM_SUSPEND_MEM); + + error = suspend_disable_secondary_cpus(); + if (error) { + pr_err("Disable nonboot cpus failed!\n"); + return; + } + + sip_smc_set_suspend_mode(VIRTUAL_POWEROFF, 0, 1); + sip_smc_virtual_poweroff(); +} + +static int parse_on_off_regulator(struct device_node *node, enum rk_pm_state state) +{ + char on_prop_name[MAX_ON_OFF_REG_PROP_NAME_LEN] = {0}; + char off_prop_name[MAX_ON_OFF_REG_PROP_NAME_LEN] = {0}; + int i, j; + struct device_node *dn; + struct regulator_dev *reg; + struct regulator_dev **on_list; + struct regulator_dev **off_list; + + switch (state) { + case RK_PM_MEM: + strncpy(on_prop_name, "rockchip,regulator-on-in-mem", + MAX_ON_OFF_REG_PROP_NAME_LEN); + strncpy(off_prop_name, "rockchip,regulator-off-in-mem", + MAX_ON_OFF_REG_PROP_NAME_LEN); + break; + + case RK_PM_MEM_LITE: + strncpy(on_prop_name, "rockchip,regulator-on-in-mem-lite", + MAX_ON_OFF_REG_PROP_NAME_LEN); + strncpy(off_prop_name, "rockchip,regulator-off-in-mem-lite", + MAX_ON_OFF_REG_PROP_NAME_LEN); + break; + + case RK_PM_MEM_ULTRA: + strncpy(on_prop_name, "rockchip,regulator-on-in-mem-ultra", + MAX_ON_OFF_REG_PROP_NAME_LEN); + strncpy(off_prop_name, "rockchip,regulator-off-in-mem-ultra", + MAX_ON_OFF_REG_PROP_NAME_LEN); + break; + + default: + return 0; + } + + on_list = on_off_regs_list[state].on_reg_list; + off_list = on_off_regs_list[state].off_reg_list; + + if (of_find_property(node, on_prop_name, NULL)) { + for (i = 0, j = 0; + (dn = of_parse_phandle(node, on_prop_name, i)); + i++) { + reg = of_find_regulator_by_node(dn); + if (reg == NULL) { + pr_warn("failed to find regulator %s for %s\n", + dn->name, on_prop_name); + } else { + pr_debug("%s on regulator=%s\n", __func__, + reg->desc->name); + on_list[j++] = reg; + } + of_node_put(dn); + + if (j >= MAX_ON_OFF_REG_NUM) + return 0; + } + } + + if (of_find_property(node, off_prop_name, NULL)) { + for (i = 0, j = 0; + (dn = of_parse_phandle(node, off_prop_name, i)); + i++) { + reg = of_find_regulator_by_node(dn); + if (reg == NULL) { + pr_warn("failed to find regulator %s for %s\n", + dn->name, off_prop_name); + } else { + pr_debug("%s off regulator=%s\n", __func__, + reg->desc->name); + off_list[j++] = reg; + } + of_node_put(dn); + + if (j >= MAX_ON_OFF_REG_NUM) + return 0; + } + } + + return 0; +} + +static int pm_config_probe(struct platform_device *pdev) +{ + const struct of_device_id *match_id; + struct device_node *node; + u32 mode_config = 0; + u32 wakeup_config = 0; + u32 pwm_regulator_config = 0; + int gpio_temp[10]; + u32 sleep_debug_en = 0; + u32 apios_suspend = 0; + u32 virtual_poweroff_en = 0; + enum of_gpio_flags flags; + int i = 0; + int length; + + match_id = of_match_node(pm_match_table, pdev->dev.of_node); + if (!match_id) + return -ENODEV; + + node = of_find_node_by_name(NULL, "rockchip-suspend"); + + if (IS_ERR_OR_NULL(node)) { + dev_err(&pdev->dev, "%s dev node err\n", __func__); + return -ENODEV; + } + + if (of_property_read_u32_array(node, + "rockchip,sleep-mode-config", + &mode_config, 1)) + dev_warn(&pdev->dev, "not set sleep mode config\n"); + else + sip_smc_set_suspend_mode(SUSPEND_MODE_CONFIG, mode_config, 0); + + if (of_property_read_u32_array(node, + "rockchip,wakeup-config", + &wakeup_config, 1)) + dev_warn(&pdev->dev, "not set wakeup-config\n"); + else + sip_smc_set_suspend_mode(WKUP_SOURCE_CONFIG, wakeup_config, 0); + + if (of_property_read_u32_array(node, + "rockchip,pwm-regulator-config", + &pwm_regulator_config, 1)) + dev_warn(&pdev->dev, "not set pwm-regulator-config\n"); + else + sip_smc_set_suspend_mode(PWM_REGULATOR_CONFIG, + pwm_regulator_config, + 0); + + length = of_gpio_named_count(node, "rockchip,power-ctrl"); + + if (length > 0 && length < 10) { + for (i = 0; i < length; i++) { + gpio_temp[i] = of_get_named_gpio_flags(node, + "rockchip,power-ctrl", + i, + &flags); + if (!gpio_is_valid(gpio_temp[i])) + break; + sip_smc_set_suspend_mode(GPIO_POWER_CONFIG, + i, + gpio_temp[i]); + } + } + sip_smc_set_suspend_mode(GPIO_POWER_CONFIG, i, PM_INVALID_GPIO); + + if (!of_property_read_u32_array(node, + "rockchip,sleep-debug-en", + &sleep_debug_en, 1)) + sip_smc_set_suspend_mode(SUSPEND_DEBUG_ENABLE, + sleep_debug_en, + 0); + + if (!of_property_read_u32_array(node, + "rockchip,apios-suspend", + &apios_suspend, 1)) + sip_smc_set_suspend_mode(APIOS_SUSPEND_CONFIG, + apios_suspend, + 0); + + if (!of_property_read_u32_array(node, + "rockchip,virtual-poweroff", + &virtual_poweroff_en, 1) && + virtual_poweroff_en) + pm_power_off_prepare = rockchip_pm_virt_pwroff_prepare; + + for (i = RK_PM_MEM; i < RK_PM_STATE_MAX; i++) + parse_on_off_regulator(node, i); + + return 0; +} + +static int pm_config_prepare(struct device *dev) +{ + int i; + suspend_state_t suspend_state = mem_sleep_current; + enum rk_pm_state state = suspend_state - PM_SUSPEND_MEM; + struct regulator_dev **on_list; + struct regulator_dev **off_list; + + sip_smc_set_suspend_mode(LINUX_PM_STATE, + suspend_state, + 0); + + if (state >= RK_PM_STATE_MAX) + return 0; + + on_list = on_off_regs_list[state].on_reg_list; + off_list = on_off_regs_list[state].off_reg_list; + + for (i = 0; i < MAX_ON_OFF_REG_NUM && on_list[i]; i++) + regulator_suspend_enable(on_list[i], PM_SUSPEND_MEM); + + for (i = 0; i < MAX_ON_OFF_REG_NUM && off_list[i]; i++) + regulator_suspend_disable(off_list[i], PM_SUSPEND_MEM); + + return 0; +} + +static const struct dev_pm_ops rockchip_pm_ops = { + .prepare = pm_config_prepare, +}; + +static struct platform_driver pm_driver = { + .probe = pm_config_probe, + .driver = { + .name = "rockchip-pm", + .of_match_table = pm_match_table, + .pm = &rockchip_pm_ops, + }, +}; + +static int __init rockchip_pm_drv_register(void) +{ + return platform_driver_register(&pm_driver); +} +late_initcall_sync(rockchip_pm_drv_register); +MODULE_DESCRIPTION("Rockchip suspend mode config"); +MODULE_LICENSE("GPL"); diff --git a/include/dt-bindings/suspend/rockchip-rk3568.h b/include/dt-bindings/suspend/rockchip-rk3568.h new file mode 100644 index 000000000000..91e89f64a067 --- /dev/null +++ b/include/dt-bindings/suspend/rockchip-rk3568.h @@ -0,0 +1,57 @@ +/* SPDX-License-Identifier: (GPL-2.0+ OR MIT) */ +/* + * Header providing constants for Rockchip suspend bindings. + * + * Copyright (C) 2021, Rockchip Electronics Co., Ltd. + * Author: XiaoDong.Huang + */ + +#ifndef __DT_BINDINGS_SUSPEND_ROCKCHIP_RK3568_H__ +#define __DT_BINDINGS_SUSPEND_ROCKCHIP_RK3568_H__ +/******************************bits ops************************************/ + +#ifndef BIT +#define BIT(nr) (1 << (nr)) +#endif + +#define RKPM_SLP_WFI BIT(0) +#define RKPM_SLP_ARMOFF BIT(1) +#define RKPM_SLP_CENTER_OFF BIT(2) +#define RKPM_SLP_ARMOFF_LOGOFF BIT(3) +#define RKPM_SLP_FROM_UBOOT BIT(4) +#define RKPM_SLP_PMIC_LP BIT(5) +#define RKPM_SLP_HW_PLLS_OFF BIT(6) +#define RKPM_SLP_PMUALIVE_32K BIT(7) +#define RKPM_SLP_OSC_DIS BIT(8) +#define RKPM_SLP_32K_EXT BIT(9) +#define RKPM_SLP_32K_PVTM BIT(10) +/* the wake up source */ +#define RKPM_CPU0_WKUP_EN BIT(0) +#define RKPM_CPU1_WKUP_EN BIT(1) +#define RKPM_CPU2_WKUP_EN BIT(2) +#define RKPM_CPU3_WKUP_EN BIT(3) +#define RKPM_GPIO_WKUP_EN BIT(4) +#define RKPM_UART0_WKUP_EN BIT(5) +#define RKPM_SDMMC0_WKUP_EN BIT(6) +#define RKPM_SDMMC1_WKUP_EN BIT(7) +#define RKPM_SDMMC2_WKUP_EN BIT(8) +#define RKPM_USB_WKUP_EN BIT(9) +#define RKPM_PCIE_WKUP_EN BIT(10) +#define RKPM_VAD_WKUP_EN BIT(11) +#define RKPM_TIMER_WKUP_EN BIT(12) +#define RKPM_PWM0_WKUP_EN BIT(13) +#define RKPM_TIMEOUT_WKUP_EN BIT(14) +#define RKPM_SFT_WKUP_EN BIT(15) +#define RKPM_USB_LINESTATE_WKUP_EN BIT(16) + +#define RKPM_SLP_LDO1_ON BIT(0) +#define RKPM_SLP_LDO2_ON BIT(1) +#define RKPM_SLP_LDO3_ON BIT(2) +#define RKPM_SLP_LDO4_ON BIT(3) +#define RKPM_SLP_LDO5_ON BIT(4) +#define RKPM_SLP_LDO6_ON BIT(5) +#define RKPM_SLP_LDO7_ON BIT(6) +#define RKPM_SLP_LDO8_ON BIT(7) +#define RKPM_SLP_LDO9_ON BIT(8) + +#endif diff --git a/include/linux/rockchip/rockchip_sip.h b/include/linux/rockchip/rockchip_sip.h new file mode 100644 index 000000000000..c77bbb78c830 --- /dev/null +++ b/include/linux/rockchip/rockchip_sip.h @@ -0,0 +1,333 @@ +/* Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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. + */ +#ifndef __ROCKCHIP_SIP_H +#define __ROCKCHIP_SIP_H + +#include +#include + +/* SMC function IDs for SiP Service queries, compatible with kernel-3.10 */ +#define SIP_ATF_VERSION 0x82000001 +#define SIP_ACCESS_REG 0x82000002 +#define SIP_SUSPEND_MODE 0x82000003 +#define SIP_PENDING_CPUS 0x82000004 +#define SIP_UARTDBG_CFG 0x82000005 +#define SIP_UARTDBG_CFG64 0xc2000005 +#define SIP_MCU_EL3FIQ_CFG 0x82000006 +#define SIP_ACCESS_CHIP_STATE64 0xc2000006 +#define SIP_SECURE_MEM_CONFIG 0x82000007 +#define SIP_ACCESS_CHIP_EXTRA_STATE64 0xc2000007 +#define SIP_DRAM_CONFIG 0x82000008 +#define SIP_SHARE_MEM 0x82000009 +#define SIP_SIP_VERSION 0x8200000a +#define SIP_REMOTECTL_CFG 0x8200000b +#define PSCI_SIP_VPU_RESET 0x8200000c +#define SIP_BUS_CFG 0x8200000d +#define SIP_LAST_LOG 0x8200000e +#define SIP_SCMI_AGENT0 0x82000010 +#define SIP_SCMI_AGENT1 0x82000011 +#define SIP_SCMI_AGENT2 0x82000012 +#define SIP_SCMI_AGENT3 0x82000013 +#define SIP_SCMI_AGENT4 0x82000014 +#define SIP_SCMI_AGENT5 0x82000015 +#define SIP_SCMI_AGENT6 0x82000016 +#define SIP_SCMI_AGENT7 0x82000017 +#define SIP_SCMI_AGENT8 0x82000018 +#define SIP_SCMI_AGENT9 0x82000019 +#define SIP_SCMI_AGENT10 0x8200001a +#define SIP_SCMI_AGENT11 0x8200001b +#define SIP_SCMI_AGENT12 0x8200001c +#define SIP_SCMI_AGENT13 0x8200001d +#define SIP_SCMI_AGENT14 0x8200001e +#define SIP_SCMI_AGENT15 0x8200001f +#define SIP_SDEI_FIQ_DBG_SWITCH_CPU 0x82000020 +#define SIP_SDEI_FIQ_DBG_GET_EVENT_ID 0x82000021 + +/* Rockchip Sip version */ +#define SIP_IMPLEMENT_V1 (1) +#define SIP_IMPLEMENT_V2 (2) + +/* Trust firmware version */ +#define ATF_VER_MAJOR(ver) (((ver) >> 16) & 0xffff) +#define ATF_VER_MINOR(ver) (((ver) >> 0) & 0xffff) + +/* SIP_ACCESS_REG: read or write */ +#define SECURE_REG_RD 0x0 +#define SECURE_REG_WR 0x1 + +/* Fiq debugger share memory: 8KB enough */ +#define FIQ_UARTDBG_PAGE_NUMS 2 +#define FIQ_UARTDBG_SHARE_MEM_SIZE ((FIQ_UARTDBG_PAGE_NUMS) * 4096) + +/* Error return code */ +#define IS_SIP_ERROR(x) (!!(x)) + +#define SIP_RET_SUCCESS 0 +#define SIP_RET_SMC_UNKNOWN -1 +#define SIP_RET_NOT_SUPPORTED -2 +#define SIP_RET_INVALID_PARAMS -3 +#define SIP_RET_INVALID_ADDRESS -4 +#define SIP_RET_DENIED -5 +#define SIP_RET_SET_RATE_TIMEOUT -6 + +/* SIP_UARTDBG_CFG64 call types */ +#define UARTDBG_CFG_INIT 0xf0 +#define UARTDBG_CFG_OSHDL_TO_OS 0xf1 +#define UARTDBG_CFG_OSHDL_CPUSW 0xf3 +#define UARTDBG_CFG_OSHDL_DEBUG_ENABLE 0xf4 +#define UARTDBG_CFG_OSHDL_DEBUG_DISABLE 0xf5 +#define UARTDBG_CFG_PRINT_PORT 0xf7 +#define UARTDBG_CFG_FIQ_ENABEL 0xf8 +#define UARTDBG_CFG_FIQ_DISABEL 0xf9 + +/* SIP_SUSPEND_MODE32 call types */ +#define SUSPEND_MODE_CONFIG 0x01 +#define WKUP_SOURCE_CONFIG 0x02 +#define PWM_REGULATOR_CONFIG 0x03 +#define GPIO_POWER_CONFIG 0x04 +#define SUSPEND_DEBUG_ENABLE 0x05 +#define APIOS_SUSPEND_CONFIG 0x06 +#define VIRTUAL_POWEROFF 0x07 +#define SUSPEND_WFI_TIME_MS 0x08 +#define LINUX_PM_STATE 0x09 + +/* SIP_REMOTECTL_CFG call types */ +#define REMOTECTL_SET_IRQ 0xf0 +#define REMOTECTL_SET_PWM_CH 0xf1 +#define REMOTECTL_SET_PWRKEY 0xf2 +#define REMOTECTL_GET_WAKEUP_STATE 0xf3 +#define REMOTECTL_ENABLE 0xf4 +/* wakeup state */ +#define REMOTECTL_PWRKEY_WAKEUP 0xdeadbeaf + +enum { + FIRMWARE_NONE, + FIRMWARE_TEE_32BIT, + FIRMWARE_ATF_32BIT, + FIRMWARE_ATF_64BIT, + FIRMWARE_END, +}; + +/* Share mem page types */ +typedef enum { + SHARE_PAGE_TYPE_INVALID = 0, + SHARE_PAGE_TYPE_UARTDBG, + SHARE_PAGE_TYPE_DDR, + SHARE_PAGE_TYPE_DDRDBG, + SHARE_PAGE_TYPE_DDRECC, + SHARE_PAGE_TYPE_LAST_LOG, + SHARE_PAGE_TYPE_MAX, +} share_page_type_t; + +/* + * Rules: struct arm_smccc_res contains result and data, details: + * + * a0: error code(0: success, !0: error); + * a1~a3: data + */ +#if IS_ENABLED(CONFIG_ROCKCHIP_SIP) +struct arm_smccc_res sip_smc_get_atf_version(void); +struct arm_smccc_res sip_smc_get_sip_version(void); +struct arm_smccc_res sip_smc_dram(u32 arg0, u32 arg1, u32 arg2); +struct arm_smccc_res sip_smc_request_share_mem(u32 page_num, + share_page_type_t page_type); +struct arm_smccc_res sip_smc_mcu_el3fiq(u32 arg0, u32 arg1, u32 arg2); +struct arm_smccc_res sip_smc_vpu_reset(u32 arg0, u32 arg1, u32 arg2); +struct arm_smccc_res sip_smc_get_suspend_info(u32 info); +struct arm_smccc_res sip_smc_lastlog_request(void); + +int sip_smc_set_suspend_mode(u32 ctrl, u32 config1, u32 config2); +int sip_smc_virtual_poweroff(void); +int sip_smc_remotectl_config(u32 func, u32 data); + +int sip_smc_secure_reg_write(u32 addr_phy, u32 val); +u32 sip_smc_secure_reg_read(u32 addr_phy); +struct arm_smccc_res sip_smc_bus_config(u32 arg0, u32 arg1, u32 arg2); + +/***************************fiq debugger **************************************/ +void sip_fiq_debugger_enable_fiq(bool enable, uint32_t tgt_cpu); +void sip_fiq_debugger_enable_debug(bool enable); +int sip_fiq_debugger_uart_irq_tf_init(u32 irq_id, void *callback_fn); +int sip_fiq_debugger_set_print_port(u32 port_phyaddr, u32 baudrate); +int sip_fiq_debugger_request_share_memory(void); +int sip_fiq_debugger_get_target_cpu(void); +int sip_fiq_debugger_switch_cpu(u32 cpu); +int sip_fiq_debugger_sdei_switch_cpu(u32 cur_cpu, u32 target_cpu, u32 flag); +int sip_fiq_debugger_is_enabled(void); +int sip_fiq_debugger_sdei_get_event_id(u32 *fiq, u32 *sw_cpu, u32 *flag); +#else +static inline struct arm_smccc_res sip_smc_get_atf_version(void) +{ + struct arm_smccc_res tmp = {0}; + return tmp; +} + +static inline struct arm_smccc_res sip_smc_get_sip_version(void) +{ + struct arm_smccc_res tmp = {0}; + return tmp; +} + +static inline struct arm_smccc_res sip_smc_dram(u32 arg0, u32 arg1, u32 arg2) +{ + struct arm_smccc_res tmp = {0}; + return tmp; +} + +static inline struct arm_smccc_res sip_smc_request_share_mem + (u32 page_num, share_page_type_t page_type) +{ + struct arm_smccc_res tmp = {0}; + return tmp; +} + +static inline struct arm_smccc_res sip_smc_mcu_el3fiq + (u32 arg0, u32 arg1, u32 arg2) +{ + struct arm_smccc_res tmp = {0}; + return tmp; +} + +static inline struct arm_smccc_res +sip_smc_vpu_reset(u32 arg0, u32 arg1, u32 arg2) +{ + struct arm_smccc_res tmp = {0}; + return tmp; +} + +static inline struct arm_smccc_res sip_smc_lastlog_request(void) +{ + struct arm_smccc_res tmp = {0}; + return tmp; +} + +static inline int sip_smc_set_suspend_mode(u32 ctrl, u32 config1, u32 config2) +{ + return 0; +} + +static inline int sip_smc_get_suspend_info(u32 info) +{ + return 0; +} + +static inline int sip_smc_virtual_poweroff(void) { return 0; } +static inline int sip_smc_remotectl_config(u32 func, u32 data) { return 0; } +static inline u32 sip_smc_secure_reg_read(u32 addr_phy) { return 0; } +static inline int sip_smc_secure_reg_write(u32 addr_phy, u32 val) { return 0; } +static inline int sip_smc_soc_bus_div(u32 arg0, u32 arg1, u32 arg2) +{ + return 0; +} + +/***************************fiq debugger **************************************/ +static inline void sip_fiq_debugger_enable_fiq + (bool enable, uint32_t tgt_cpu) { return; } + +static inline void sip_fiq_debugger_enable_debug(bool enable) { return; } +static inline int sip_fiq_debugger_uart_irq_tf_init(u32 irq_id, + void *callback_fn) +{ + return 0; +} + +static inline int sip_fiq_debugger_set_print_port(u32 port_phyaddr, + u32 baudrate) +{ + return 0; +} + +static inline int sip_fiq_debugger_request_share_memory(void) { return 0; } +static inline int sip_fiq_debugger_get_target_cpu(void) { return 0; } +static inline int sip_fiq_debugger_switch_cpu(u32 cpu) { return 0; } +static inline int sip_fiq_debugger_sdei_switch_cpu(u32 cur_cpu, u32 target_cpu, + u32 flag) { return 0; } +static inline int sip_fiq_debugger_is_enabled(void) { return 0; } +#endif + +/* 32-bit OP-TEE context, never change order of members! */ +struct sm_nsec_ctx { + u32 usr_sp; + u32 usr_lr; + u32 irq_spsr; + u32 irq_sp; + u32 irq_lr; + u32 fiq_spsr; + u32 fiq_sp; + u32 fiq_lr; + u32 svc_spsr; + u32 svc_sp; + u32 svc_lr; + u32 abt_spsr; + u32 abt_sp; + u32 abt_lr; + u32 und_spsr; + u32 und_sp; + u32 und_lr; + u32 mon_lr; + u32 mon_spsr; + u32 r4; + u32 r5; + u32 r6; + u32 r7; + u32 r8; + u32 r9; + u32 r10; + u32 r11; + u32 r12; + u32 r0; + u32 r1; + u32 r2; + u32 r3; +}; + +/* 64-bit ATF context, never change order of members! */ +struct gp_regs_ctx { + u64 x0; + u64 x1; + u64 x2; + u64 x3; + u64 x4; + u64 x5; + u64 x6; + u64 x7; + u64 x8; + u64 x9; + u64 x10; + u64 x11; + u64 x12; + u64 x13; + u64 x14; + u64 x15; + u64 x16; + u64 x17; + u64 x18; + u64 x19; + u64 x20; + u64 x21; + u64 x22; + u64 x23; + u64 x24; + u64 x25; + u64 x26; + u64 x27; + u64 x28; + u64 x29; + u64 lr; + u64 sp_el0; + u64 scr_el3; + u64 runtime_sp; + u64 spsr_el3; + u64 elr_el3; +}; + +#endif