[HACK] Copy in Rockchip suspend mode driver

This is needed for suspend with the BSP TF-A.

Signed-off-by: Samuel Holland <samuel@sholland.org>
Signed-off-by: Maximilian Weigand <mweigand@mweigand.net>

---
original commit: b97e1e9f39
This commit is contained in:
Samuel Holland 2022-01-03 19:44:52 -06:00 committed by Antoine Martin
parent 43775c97dd
commit 715641d84e
9 changed files with 1265 additions and 0 deletions

View file

@ -6,6 +6,7 @@
#include <dt-bindings/leds/common.h> #include <dt-bindings/leds/common.h>
#include <dt-bindings/pinctrl/rockchip.h> #include <dt-bindings/pinctrl/rockchip.h>
#include <dt-bindings/usb/pd.h> #include <dt-bindings/usb/pd.h>
#include <dt-bindings/suspend/rockchip-rk3568.h>
#include "rk3566.dtsi" #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 { sdio_pwrseq: sdio-pwrseq {
compatible = "mmc-pwrseq-simple"; compatible = "mmc-pwrseq-simple";
clocks = <&rk817 1>; clocks = <&rk817 1>;

View file

@ -179,6 +179,13 @@ config MTK_ADSP_IPC
ADSP exists on some mtk processors. ADSP exists on some mtk processors.
Client might use shared memory to exchange information with ADSP. 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 config SYSFB
bool bool
select BOOT_VESA_SUPPORT select BOOT_VESA_SUPPORT

View file

@ -16,6 +16,7 @@ obj-$(CONFIG_FIRMWARE_MEMMAP) += memmap.o
obj-$(CONFIG_MTK_ADSP_IPC) += mtk-adsp-ipc.o obj-$(CONFIG_MTK_ADSP_IPC) += mtk-adsp-ipc.o
obj-$(CONFIG_RASPBERRYPI_FIRMWARE) += raspberrypi.o obj-$(CONFIG_RASPBERRYPI_FIRMWARE) += raspberrypi.o
obj-$(CONFIG_FW_CFG_SYSFS) += qemu_fw_cfg.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) += sysfb.o
obj-$(CONFIG_SYSFB_SIMPLEFB) += sysfb_simplefb.o obj-$(CONFIG_SYSFB_SIMPLEFB) += sysfb_simplefb.o
obj-$(CONFIG_TH1520_AON_PROTOCOL) += thead,th1520-aon.o obj-$(CONFIG_TH1520_AON_PROTOCOL) += thead,th1520-aon.o

View file

@ -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 <linux/arm-smccc.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/rockchip/rockchip_sip.h>
#include <asm/cputype.h>
#ifdef CONFIG_ARM
#include <asm/psci.h>
#endif
#include <asm/smp_plat.h>
#include <uapi/linux/psci.h>
#include <linux/ptrace.h>
#include <linux/sched/clock.h>
#include <linux/slab.h>
#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");

View file

@ -30,4 +30,10 @@ config ROCKCHIP_DTPM
on this platform. That will create all the power capping capable on this platform. That will create all the power capping capable
devices. 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 endif

View file

@ -5,3 +5,4 @@
obj-$(CONFIG_ROCKCHIP_GRF) += grf.o obj-$(CONFIG_ROCKCHIP_GRF) += grf.o
obj-$(CONFIG_ROCKCHIP_IODOMAIN) += io-domain.o obj-$(CONFIG_ROCKCHIP_IODOMAIN) += io-domain.o
obj-$(CONFIG_ROCKCHIP_DTPM) += dtpm.o obj-$(CONFIG_ROCKCHIP_DTPM) += dtpm.o
obj-$(CONFIG_ROCKCHIP_SUSPEND_MODE) += rockchip_pm_config.o

View file

@ -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 <linux/arm-smccc.h>
#include <linux/bitops.h>
#include <linux/cpu.h>
#include <linux/module.h>
#include <linux/of_gpio.h>
#include <linux/platform_device.h>
#include <linux/pm.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
#include <linux/rockchip/rockchip_sip.h>
#include <linux/suspend.h>
#include <dt-bindings/input/input.h>
#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");

View file

@ -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

View file

@ -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 <linux/arm-smccc.h>
#include <linux/io.h>
/* 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