diff --git a/Kconfig b/Kconfig index 797038b0371..6194dcb472b 100644 --- a/Kconfig +++ b/Kconfig @@ -415,7 +415,7 @@ config BUILD_TARGET default "u-boot-spl.kwb" if ARCH_MVEBU && SPL default "u-boot-elf.srec" if RCAR_GEN3 default "u-boot.itb" if !BINMAN && SPL_LOAD_FIT && (ARCH_ROCKCHIP || \ - ARCH_SUNXI || RISCV || ARCH_ZYNQMP) + RISCV || ARCH_ZYNQMP) default "u-boot.kwb" if ARCH_KIRKWOOD default "u-boot-with-spl.bin" if ARCH_AT91 && SPL_NAND_SUPPORT default "u-boot-with-spl.imx" if ARCH_MX6 && SPL diff --git a/Makefile b/Makefile index 61927f8918b..891ab387a57 100644 --- a/Makefile +++ b/Makefile @@ -1005,6 +1005,23 @@ endif endif endif +ifeq ($(CONFIG_MACH_SUN8I_H3)$(CONFIG_ARMV7_PSCI),yy) +INPUTS-$(CONFIG_ARMV7_PSCI) += u-boot-resume.img + +MKIMAGEFLAGS_u-boot-resume.img := -B 0x400 -T sunxi_egon + +u-boot-resume.img: u-boot-resume.bin + $(call if_changed,mkimage) + +OBJCOPYFLAGS_u-boot-resume.bin := -O binary + +u-boot-resume.bin: u-boot-resume.o + $(call if_changed,objcopy) + +u-boot-resume.S: u-boot + @sed -En 's/(0x[[:xdigit:]]+) +psci_cpu_entry/ldr pc, =\1/p' $<.map > $@ +endif + INPUTS-$(CONFIG_X86) += u-boot-x86-start16.bin u-boot-x86-reset16.bin \ $(if $(CONFIG_SPL_X86_16BIT_INIT),spl/u-boot-spl.bin) \ $(if $(CONFIG_TPL_X86_16BIT_INIT),tpl/u-boot-tpl.bin) diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 9898c7d68e1..2ccb0b0a2a7 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -1194,6 +1194,7 @@ config ARCH_SUNXI imply SPL_GPIO imply SPL_LIBCOMMON_SUPPORT imply SPL_LIBGENERIC_SUPPORT + imply SPL_LOAD_FIT imply SPL_MMC if MMC imply SPL_POWER imply SPL_SERIAL diff --git a/arch/arm/cpu/armv7/Kconfig b/arch/arm/cpu/armv7/Kconfig index f1e4e26b8f0..cd5dc202755 100644 --- a/arch/arm/cpu/armv7/Kconfig +++ b/arch/arm/cpu/armv7/Kconfig @@ -75,11 +75,14 @@ config ARMV7_PSCI choice prompt "Supported PSCI version" depends on ARMV7_PSCI - default ARMV7_PSCI_0_1 if ARCH_SUNXI + default ARMV7_PSCI_1_1 if ARCH_SUNXI default ARMV7_PSCI_1_0 help Select the supported PSCI version. +config ARMV7_PSCI_1_1 + bool "PSCI V1.1" + config ARMV7_PSCI_1_0 bool "PSCI V1.0" diff --git a/arch/arm/cpu/armv7/sunxi/Makefile b/arch/arm/cpu/armv7/sunxi/Makefile index 3e975b366c4..6473b9acbde 100644 --- a/arch/arm/cpu/armv7/sunxi/Makefile +++ b/arch/arm/cpu/armv7/sunxi/Makefile @@ -13,8 +13,12 @@ obj-$(CONFIG_MACH_SUN6I) += sram.o obj-$(CONFIG_MACH_SUN8I) += sram.o ifndef CONFIG_SPL_BUILD +ifdef CONFIG_MACH_SUN8I_H3 +obj-$(CONFIG_ARMV7_PSCI) += psci-scpi.o +else obj-$(CONFIG_ARMV7_PSCI) += psci.o endif +endif ifdef CONFIG_SPL_BUILD obj-y += fel_utils.o diff --git a/arch/arm/cpu/armv7/sunxi/psci-scpi.c b/arch/arm/cpu/armv7/sunxi/psci-scpi.c new file mode 100644 index 00000000000..b3849b366e3 --- /dev/null +++ b/arch/arm/cpu/armv7/sunxi/psci-scpi.c @@ -0,0 +1,451 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2016 Chen-Yu Tsai + * Copyright (C) 2018-2021 Samuel Holland + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define GICD_BASE (SUNXI_GIC400_BASE + GIC_DIST_OFFSET) +#define GICC_BASE (SUNXI_GIC400_BASE + GIC_CPU_OFFSET_A15) + +#define HW_ON 0 +#define HW_OFF 1 +#define HW_STANDBY 2 + +#define MPIDR_AFFLVL0(mpidr) (mpidr & 0xf) +#define MPIDR_AFFLVL1(mpidr) (mpidr >> 8 & 0xf) + +#define SCPI_SHMEM_BASE 0x0004be00 +#define SCPI_SHMEM ((struct scpi_shmem *)SCPI_SHMEM_BASE) + +#define SCPI_RX_CHANNEL 1 +#define SCPI_TX_CHANNEL 0 +#define SCPI_VIRTUAL_CHANNEL BIT(0) + +#define SCPI_MESSAGE_SIZE 0x100 +#define SCPI_PAYLOAD_SIZE (SCPI_MESSAGE_SIZE - sizeof(struct scpi_header)) + +#define SUNXI_MSGBOX_BASE 0x01c17000 +#define REMOTE_IRQ_STAT_REG (SUNXI_MSGBOX_BASE + 0x0050) +#define LOCAL_IRQ_STAT_REG (SUNXI_MSGBOX_BASE + 0x0070) +#define MSG_STAT_REG(n) (SUNXI_MSGBOX_BASE + 0x0140 + 0x4 * (n)) +#define MSG_DATA_REG(n) (SUNXI_MSGBOX_BASE + 0x0180 + 0x4 * (n)) + +#define RX_IRQ(n) BIT(0 + 2 * (n)) +#define TX_IRQ(n) BIT(1 + 2 * (n)) + +enum { + CORE_POWER_LEVEL = 0, + CLUSTER_POWER_LEVEL = 1, + CSS_POWER_LEVEL = 2, +}; + +enum { + SCPI_CMD_SCP_READY = 0x01, + SCPI_CMD_SET_CSS_POWER_STATE = 0x03, + SCPI_CMD_GET_CSS_POWER_STATE = 0x04, + SCPI_CMD_SET_SYS_POWER_STATE = 0x05, +}; + +enum { + SCPI_E_OK = 0, + SCPI_E_PARAM = 1, + SCPI_E_ALIGN = 2, + SCPI_E_SIZE = 3, + SCPI_E_HANDLER = 4, + SCPI_E_ACCESS = 5, + SCPI_E_RANGE = 6, + SCPI_E_TIMEOUT = 7, + SCPI_E_NOMEM = 8, + SCPI_E_PWRSTATE = 9, + SCPI_E_SUPPORT = 10, + SCPI_E_DEVICE = 11, + SCPI_E_BUSY = 12, + SCPI_E_OS = 13, + SCPI_E_DATA = 14, + SCPI_E_STATE = 15, +}; + +enum { + SCPI_POWER_ON = 0x00, + SCPI_POWER_RETENTION = 0x01, + SCPI_POWER_OFF = 0x03, +}; + +enum { + SCPI_SYSTEM_SHUTDOWN = 0x00, + SCPI_SYSTEM_REBOOT = 0x01, + SCPI_SYSTEM_RESET = 0x02, +}; + +struct scpi_header { + u8 command; + u8 sender; + u16 size; + u32 status; +}; + +struct scpi_message { + struct scpi_header header; + u8 payload[SCPI_PAYLOAD_SIZE]; +}; + +struct scpi_shmem { + struct scpi_message rx; + struct scpi_message tx; +}; + +static bool __secure_data gic_dist_init; + +static u32 __secure_data lock; + +static inline u32 __secure read_mpidr(void) +{ + u32 val; + + asm volatile ("mrc p15, 0, %0, c0, c0, 5" : "=r" (val)); + + return val; +} + +static void __secure scpi_begin_command(void) +{ + u32 mpidr = read_mpidr(); + + do { + while (readl(&lock)); + writel(mpidr, &lock); + dsb(); + } while (readl(&lock) != mpidr); + while (readl(REMOTE_IRQ_STAT_REG) & RX_IRQ(SCPI_TX_CHANNEL)); +} + +static void __secure scpi_send_command(void) +{ + writel(SCPI_VIRTUAL_CHANNEL, MSG_DATA_REG(SCPI_TX_CHANNEL)); +} + +static void __secure scpi_wait_response(void) +{ + while (!readl(MSG_STAT_REG(SCPI_RX_CHANNEL))); +} + +static void __secure scpi_end_command(void) +{ + while (readl(MSG_STAT_REG(SCPI_RX_CHANNEL))) + readl(MSG_DATA_REG(SCPI_RX_CHANNEL)); + writel(RX_IRQ(SCPI_RX_CHANNEL), LOCAL_IRQ_STAT_REG); + writel(0, &lock); +} + +static void __secure scpi_set_css_power_state(u32 target_cpu, u32 core_state, + u32 cluster_state, u32 css_state) +{ + struct scpi_shmem *shmem = SCPI_SHMEM; + + scpi_begin_command(); + + shmem->tx.header.command = SCPI_CMD_SET_CSS_POWER_STATE; + shmem->tx.header.size = 4; + + shmem->tx.payload[0] = target_cpu >> 4 | target_cpu; + shmem->tx.payload[1] = cluster_state << 4 | core_state; + shmem->tx.payload[2] = css_state; + shmem->tx.payload[3] = 0; + + scpi_send_command(); + scpi_end_command(); +} + +static s32 __secure scpi_get_css_power_state(u32 target_cpu, u8 *core_states, + u8 *cluster_state) +{ + struct scpi_shmem *shmem = SCPI_SHMEM; + u32 cluster = MPIDR_AFFLVL1(target_cpu); + u32 offset; + s32 ret; + + scpi_begin_command(); + + shmem->tx.header.command = SCPI_CMD_GET_CSS_POWER_STATE; + shmem->tx.header.size = 0; + + scpi_send_command(); + scpi_wait_response(); + + for (offset = 0; offset < shmem->rx.header.size; offset += 2) { + if ((shmem->rx.payload[offset] & 0xf) == cluster) { + *cluster_state = shmem->rx.payload[offset+0] >> 4; + *core_states = shmem->rx.payload[offset+1]; + + break; + } + } + + ret = shmem->rx.header.status; + + scpi_end_command(); + + return ret; +} + +static s32 __secure scpi_set_sys_power_state(u32 sys_state) +{ + struct scpi_shmem *shmem = SCPI_SHMEM; + s32 ret; + + scpi_begin_command(); + + shmem->tx.header.command = SCPI_CMD_SET_SYS_POWER_STATE; + shmem->tx.header.size = 1; + + shmem->tx.payload[0] = sys_state; + + scpi_send_command(); + scpi_wait_response(); + + ret = shmem->rx.header.status; + + scpi_end_command(); + + return ret; +} + +void psci_enable_smp(void); + +static s32 __secure psci_suspend_common(u32 pc, u32 context_id, u32 core_state, + u32 cluster_state, u32 css_state) + +{ + u32 target_cpu = read_mpidr(); + + if (core_state == SCPI_POWER_OFF) + psci_save(MPIDR_AFFLVL0(target_cpu), pc, context_id); + if (css_state == SCPI_POWER_OFF) + gic_dist_init = true; + + scpi_set_css_power_state(target_cpu, core_state, + cluster_state, css_state); + + psci_cpu_off_common(); + + wfi(); + + psci_enable_smp(); + + return ARM_PSCI_RET_SUCCESS; +} + +u32 __secure psci_version(void) +{ + return ARM_PSCI_VER_1_1; +} + +s32 __secure psci_cpu_suspend(u32 __always_unused function_id, + u32 power_state, u32 pc, u32 context_id) +{ + return psci_suspend_common(pc, context_id, + power_state >> 0 & 0xf, + power_state >> 4 & 0xf, + power_state >> 8 & 0xf); +} + +s32 __secure psci_cpu_off(void) +{ + u32 pc = 0, context_id = 0; + + return psci_suspend_common(pc, context_id, SCPI_POWER_OFF, + SCPI_POWER_OFF, SCPI_POWER_ON); +} + +s32 __secure psci_cpu_on(u32 __always_unused function_id, + u32 target_cpu, u32 pc, u32 context_id) +{ + psci_save(MPIDR_AFFLVL0(target_cpu), pc, context_id); + + scpi_set_css_power_state(target_cpu, SCPI_POWER_ON, + SCPI_POWER_ON, SCPI_POWER_ON); + + return ARM_PSCI_RET_SUCCESS; +} + +s32 __secure psci_affinity_info(u32 function_id, + u32 target_cpu, u32 power_level) +{ + if (power_level != CORE_POWER_LEVEL) + return ARM_PSCI_RET_INVAL; + + /* This happens to have the same HW_ON/HW_OFF encoding. */ + return psci_node_hw_state(function_id, target_cpu, power_level); +} + +void __secure psci_system_off(void) +{ + scpi_set_sys_power_state(SCPI_SYSTEM_SHUTDOWN); + + /* Wait to be turned off. */ + for (;;) wfi(); +} + +void __secure psci_system_reset(void) +{ + scpi_set_sys_power_state(SCPI_SYSTEM_REBOOT); + + /* Wait to be turned off. */ + for (;;) wfi(); +} + +s32 __secure psci_features(u32 __always_unused function_id, + u32 psci_fid) +{ + switch (psci_fid) { + case ARM_PSCI_0_2_FN_PSCI_VERSION: + case ARM_PSCI_0_2_FN_CPU_SUSPEND: + case ARM_PSCI_0_2_FN_CPU_OFF: + case ARM_PSCI_0_2_FN_CPU_ON: + case ARM_PSCI_0_2_FN_AFFINITY_INFO: + case ARM_PSCI_0_2_FN_SYSTEM_OFF: + case ARM_PSCI_0_2_FN_SYSTEM_RESET: + case ARM_PSCI_1_0_FN_PSCI_FEATURES: + case ARM_PSCI_1_0_FN_CPU_DEFAULT_SUSPEND: + case ARM_PSCI_1_0_FN_NODE_HW_STATE: + case ARM_PSCI_1_0_FN_SYSTEM_SUSPEND: + case ARM_PSCI_1_1_FN_SYSTEM_RESET2: + return ARM_PSCI_RET_SUCCESS; + default: + return ARM_PSCI_RET_NI; + } +} + +s32 __secure psci_cpu_default_suspend(u32 __always_unused function_id, + u32 pc, u32 context_id) +{ + return psci_suspend_common(pc, context_id, SCPI_POWER_OFF, + SCPI_POWER_OFF, SCPI_POWER_RETENTION); +} + +s32 __secure psci_node_hw_state(u32 __always_unused function_id, + u32 target_cpu, u32 power_level) +{ + u32 core = MPIDR_AFFLVL0(target_cpu); + u8 core_states, cluster_state; + + if (power_level >= CSS_POWER_LEVEL) + return HW_ON; + if (scpi_get_css_power_state(target_cpu, &core_states, &cluster_state)) + return ARM_PSCI_RET_NI; + if (power_level == CLUSTER_POWER_LEVEL) { + if (cluster_state == SCPI_POWER_ON) + return HW_ON; + if (cluster_state < SCPI_POWER_OFF) + return HW_STANDBY; + return HW_OFF; + } + + return (core_states & BIT(core)) ? HW_ON : HW_OFF; +} + +s32 __secure psci_system_suspend(u32 __always_unused function_id, + u32 pc, u32 context_id) +{ + return psci_suspend_common(pc, context_id, SCPI_POWER_OFF, + SCPI_POWER_OFF, SCPI_POWER_OFF); +} + +s32 __secure psci_system_reset2(u32 __always_unused function_id, + u32 reset_type, u32 cookie) +{ + s32 ret; + + if (reset_type) + return ARM_PSCI_RET_INVAL; + + ret = scpi_set_sys_power_state(SCPI_SYSTEM_RESET); + if (ret) + return ARM_PSCI_RET_INVAL; + + /* Wait to be turned off. */ + for (;;) wfi(); +} + +/* + * R40 is different from other single cluster SoCs. The secondary core + * entry address register is in the SRAM controller address range. + */ +#define SUN8I_R40_SRAMC_SOFT_ENTRY_REG0 (0xbc) + +#ifdef CONFIG_MACH_SUN8I_R40 +/* secondary core entry address is programmed differently on R40 */ +static void __secure sunxi_set_entry_address(void *entry) +{ + writel((u32)entry, + SUNXI_SRAMC_BASE + SUN8I_R40_SRAMC_SOFT_ENTRY_REG0); +} +#else +static void __secure sunxi_set_entry_address(void *entry) +{ + struct sunxi_cpucfg_reg *cpucfg = + (struct sunxi_cpucfg_reg *)SUNXI_CPUCFG_BASE; + + writel((u32)entry, &cpucfg->priv0); + + if (IS_ENABLED(CONFIG_MACH_SUN8I_H3)) { + /* Redirect CPU 0 to the secure monitor via the resume shim. */ + writel(0x16aaefe8, &cpucfg->super_standy_flag); + writel(0xaa16efe8, &cpucfg->super_standy_flag); + writel(SUNXI_RESUME_BASE, &cpucfg->priv1); + } +} +#endif + +void __secure psci_arch_init(void) +{ + static bool __secure_data one_time_init = true; + + if (one_time_init) { + /* Set secondary core power-on PC. */ + sunxi_set_entry_address(psci_cpu_entry); + + /* Wait for the SCP firmware to boot. */ + scpi_begin_command(); + scpi_wait_response(); + scpi_end_command(); + + one_time_init = false; + } + + /* + * Copied from arch/arm/cpu/armv7/virt-v7.c + * See also gic_resume() in arch/arm/mach-imx/mx7/psci-mx7.c + */ + if (gic_dist_init) { + u32 i, itlinesnr; + + /* enable the GIC distributor */ + writel(readl(GICD_BASE + GICD_CTLR) | 0x03, GICD_BASE + GICD_CTLR); + + /* TYPER[4:0] contains an encoded number of available interrupts */ + itlinesnr = readl(GICD_BASE + GICD_TYPER) & 0x1f; + + /* set all bits in the GIC group registers to one to allow access + * from non-secure state. The first 32 interrupts are private per + * CPU and will be set later when enabling the GIC for each core + */ + for (i = 1; i <= itlinesnr; i++) + writel((unsigned)-1, GICD_BASE + GICD_IGROUPRn + 4 * i); + + gic_dist_init = false; + } + + /* Be cool with non-secure. */ + writel(0xff, GICC_BASE + GICC_PMR); +} diff --git a/arch/arm/cpu/armv7/sunxi/psci.c b/arch/arm/cpu/armv7/sunxi/psci.c index d1bd6b9be41..bb578fcbbef 100644 --- a/arch/arm/cpu/armv7/sunxi/psci.c +++ b/arch/arm/cpu/armv7/sunxi/psci.c @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -38,6 +39,15 @@ #define SUN8I_R40_PWR_CLAMP(cpu) (0x120 + (cpu) * 0x4) #define SUN8I_R40_SRAMC_SOFT_ENTRY_REG0 (0xbc) +static inline u32 __secure cp15_read_mpidr(void) +{ + u32 val; + + asm volatile ("mrc p15, 0, %0, c0, c0, 5" : "=r" (val)); + + return val; +} + static void __secure cp15_write_cntp_tval(u32 tval) { asm volatile ("mcr p15, 0, %0, c14, c2, 0" : : "r" (tval)); @@ -132,6 +142,13 @@ static void __secure sunxi_set_entry_address(void *entry) (struct sunxi_cpucfg_reg *)SUNXI_CPUCFG_BASE; writel((u32)entry, &cpucfg->priv0); + +#ifdef CONFIG_MACH_SUN8I_H3 + /* Redirect CPU 0 to the secure monitor via the resume shim. */ + writel(0x16aaefe8, &cpucfg->super_standy_flag); + writel(0xaa16efe8, &cpucfg->super_standy_flag); + writel(SUNXI_RESUME_BASE, &cpucfg->priv1); +#endif } #endif @@ -246,9 +263,12 @@ out: int __secure psci_cpu_on(u32 __always_unused unused, u32 mpidr, u32 pc, u32 context_id) { + struct sunxi_ccm_reg *ccu = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; struct sunxi_cpucfg_reg *cpucfg = (struct sunxi_cpucfg_reg *)SUNXI_CPUCFG_BASE; u32 cpu = (mpidr & 0x3); + u32 cpu_clk; + u32 bus_clk; /* store target PC and context id */ psci_save(cpu, pc, context_id); @@ -265,12 +285,32 @@ int __secure psci_cpu_on(u32 __always_unused unused, u32 mpidr, u32 pc, /* Lock CPU (Disable external debug access) */ clrbits_le32(&cpucfg->dbg_ctrl1, BIT(cpu)); + if (IS_ENABLED(CONFIG_MACH_SUN8I_H3) && cpu == 0) { + /* Save registers that will be clobbered by the BROM. */ + cpu_clk = readl(&ccu->cpu_axi_cfg); + bus_clk = readl(&ccu->ahb1_apb1_div); + + /* Bypass PLL_PERIPH0 so AHB1 frequency does not spike. */ + setbits_le32(&ccu->pll6_cfg, BIT(25)); + } + /* Power up target CPU */ sunxi_cpu_set_power(cpu, true); /* De-assert reset on target CPU */ writel(BIT(1) | BIT(0), &cpucfg->cpu[cpu].rst); + if (IS_ENABLED(CONFIG_MACH_SUN8I_H3) && cpu == 0) { + /* Spin until the BROM has clobbered the clock registers. */ + while (readl(&ccu->ahb1_apb1_div) != 0x00001100); + + /* Restore the registers and turn off PLL_PERIPH0 bypass. */ + writel(cpu_clk, &ccu->cpu_axi_cfg); + writel(bus_clk, &ccu->ahb1_apb1_div); + + clrbits_le32(&ccu->pll6_cfg, BIT(25)); + } + /* Unlock CPU (Disable external debug access) */ setbits_le32(&cpucfg->dbg_ctrl1, BIT(cpu)); @@ -281,9 +321,14 @@ s32 __secure psci_cpu_off(void) { psci_cpu_off_common(); - /* Ask CPU0 via SGI15 to pull the rug... */ - writel(BIT(16) | 15, GICD_BASE + GICD_SGIR); - dsb(); + if (cp15_read_mpidr() & 3) { + /* Ask CPU0 via SGI15 to pull the rug... */ + writel(BIT(16) | 15, GICD_BASE + GICD_SGIR); + dsb(); + } else { + /* Unmask FIQs to service SGI15. */ + asm volatile ("cpsie f"); + } /* Wait to be turned off */ while (1) diff --git a/arch/arm/cpu/armv8/fwcall.c b/arch/arm/cpu/armv8/fwcall.c index 16914dc1eed..87de09979b1 100644 --- a/arch/arm/cpu/armv8/fwcall.c +++ b/arch/arm/cpu/armv8/fwcall.c @@ -103,7 +103,7 @@ void __noreturn psci_system_reset2(u32 reset_level, u32 cookie) { struct pt_regs regs; - regs.regs[0] = ARM_PSCI_0_2_FN64_SYSTEM_RESET2; + regs.regs[0] = ARM_PSCI_1_1_FN64_SYSTEM_RESET2; regs.regs[1] = PSCI_RESET2_TYPE_VENDOR | reset_level; regs.regs[2] = cookie; if (use_smc_for_psci) diff --git a/arch/arm/dts/sun8i-h3.dtsi b/arch/arm/dts/sun8i-h3.dtsi index eac2349a238..b88dcd42720 100644 --- a/arch/arm/dts/sun8i-h3.dtsi +++ b/arch/arm/dts/sun8i-h3.dtsi @@ -170,6 +170,14 @@ #size-cells = <1>; ranges; + sram_a2: sram@40000 { + compatible = "mmio-sram"; + reg = <0x00040000 0xc000>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0 0x00040000 0xc000>; + }; + sram_c: sram@1d00000 { compatible = "mmio-sram"; reg = <0x01d00000 0x80000>; @@ -239,6 +247,12 @@ nvmem-cell-names = "calibration"; #thermal-sensor-cells = <0>; }; + + remoteproc@1f01c00 { + compatible = "allwinner,sun6i-a31-ar100"; + reg = <0x01f01c00 0x400>; + sram = <&sram_a2>; + }; }; thermal-zones { diff --git a/arch/arm/dts/sunxi-u-boot.dtsi b/arch/arm/dts/sunxi-u-boot.dtsi index 0f573e6d7ae..ee4c374b096 100644 --- a/arch/arm/dts/sunxi-u-boot.dtsi +++ b/arch/arm/dts/sunxi-u-boot.dtsi @@ -1,13 +1,24 @@ #include -#ifdef CONFIG_MACH_SUN50I_H6 -#define BL31_ADDR 0x104000 -#define SCP_ADDR 0x114000 -#elif defined(CONFIG_MACH_SUN50I_H616) -#define BL31_ADDR 0x40000000 +#ifdef CONFIG_ARM64 +#define ARCH "arm64" #else -#define BL31_ADDR 0x44000 -#define SCP_ADDR 0x50000 +#define ARCH "arm" +#endif + +#if defined(CONFIG_MACH_SUN8I_H3) +#ifdef CONFIG_ARMV7_PSCI +#define RESUME_ADDR SUNXI_RESUME_BASE +#define SCP_ADDR SUNXI_SCP_BASE +#endif +#elif defined(CONFIG_MACH_SUN50I) || defined(CONFIG_MACH_SUN50I_H5) +#define BL31_ADDR 0x00044000 +#define SCP_ADDR 0x00050000 +#elif defined(CONFIG_MACH_SUN50I_H6) +#define BL31_ADDR 0x00104000 +#define SCP_ADDR 0x00114000 +#elif defined(CONFIG_MACH_SUN50I_H616) +#define BL31_ADDR 0x40000000 #endif / { @@ -34,30 +45,33 @@ filename = "spl/sunxi-spl.bin"; }; -#ifdef CONFIG_ARM64 +#ifdef CONFIG_SPL_LOAD_FIT fit { - description = "Configuration to load ATF before U-Boot"; + description = "Configuration to load U-Boot and firmware"; + offset = <32768>; #address-cells = <1>; fit,fdt-list = "of-list"; images { uboot { - description = "U-Boot (64-bit)"; + description = "U-Boot"; type = "standalone"; os = "u-boot"; - arch = "arm64"; + arch = ARCH; compression = "none"; load = ; + entry = ; u-boot-nodtb { }; }; +#ifdef BL31_ADDR atf { description = "ARM Trusted Firmware"; type = "firmware"; os = "arm-trusted-firmware"; - arch = "arm64"; + arch = ARCH; compression = "none"; load = ; entry = ; @@ -67,6 +81,21 @@ missing-msg = "atf-bl31-sunxi"; }; }; +#endif + +#ifdef RESUME_ADDR + resume { + description = "Super Standby resume image"; + type = "standalone"; + arch = ARCH; + compression = "none"; + load = ; + + blob-ext { + filename = "u-boot-resume.img"; + }; + }; +#endif #ifdef SCP_ADDR scp { @@ -95,19 +124,26 @@ @config-SEQ { description = "NAME"; +#ifdef BL31_ADDR firmware = "atf"; -#ifndef SCP_ADDR - loadables = "uboot"; #else - loadables = "scp", "uboot"; + firmware = "uboot"; #endif + loadables = +#ifdef RESUME_ADDR + "resume", +#endif +#ifdef SCP_ADDR + "scp", +#endif + "uboot"; fdt = "fdt-SEQ"; }; }; }; #else u-boot-img { - offset = ; + offset = <32768>; }; #endif }; diff --git a/arch/arm/include/asm/psci.h b/arch/arm/include/asm/psci.h index 67e9234066b..aa351867eee 100644 --- a/arch/arm/include/asm/psci.h +++ b/arch/arm/include/asm/psci.h @@ -22,8 +22,9 @@ #include #endif -#define ARM_PSCI_VER_1_0 (0x00010000) #define ARM_PSCI_VER_0_2 (0x00000002) +#define ARM_PSCI_VER_1_0 (0x00010000) +#define ARM_PSCI_VER_1_1 (0x00010001) /* PSCI 0.1 interface */ #define ARM_PSCI_FN_BASE 0x95c1ba5e @@ -68,7 +69,6 @@ #define ARM_PSCI_0_2_FN64_AFFINITY_INFO ARM_PSCI_0_2_FN64(4) #define ARM_PSCI_0_2_FN64_MIGRATE ARM_PSCI_0_2_FN64(5) #define ARM_PSCI_0_2_FN64_MIGRATE_INFO_UP_CPU ARM_PSCI_0_2_FN64(7) -#define ARM_PSCI_0_2_FN64_SYSTEM_RESET2 ARM_PSCI_0_2_FN64(18) /* PSCI 1.0 interface */ #define ARM_PSCI_1_0_FN_PSCI_FEATURES ARM_PSCI_0_2_FN(10) @@ -86,6 +86,11 @@ #define ARM_PSCI_1_0_FN64_STAT_RESIDENCY ARM_PSCI_0_2_FN64(16) #define ARM_PSCI_1_0_FN64_STAT_COUNT ARM_PSCI_0_2_FN64(17) +/* PSCI 1.1 interface */ +#define ARM_PSCI_1_1_FN_SYSTEM_RESET2 ARM_PSCI_0_2_FN(18) + +#define ARM_PSCI_1_1_FN64_SYSTEM_RESET2 ARM_PSCI_0_2_FN64(18) + /* 1KB stack per core */ #define ARM_PSCI_STACK_SHIFT 10 #define ARM_PSCI_STACK_SIZE (1 << ARM_PSCI_STACK_SHIFT) diff --git a/arch/arm/include/asm/system.h b/arch/arm/include/asm/system.h index 87d1c77e8b1..bcbb999b494 100644 --- a/arch/arm/include/asm/system.h +++ b/arch/arm/include/asm/system.h @@ -557,16 +557,20 @@ void mmu_page_table_flush(unsigned long start, unsigned long stop); #ifdef CONFIG_ARMV7_PSCI void psci_arch_cpu_entry(void); void psci_arch_init(void); + u32 psci_version(void); -s32 psci_features(u32 function_id, u32 psci_fid); +s32 psci_cpu_suspend(u32 function_id, u32 power_state, u32 pc, u32 context_id); s32 psci_cpu_off(void); -s32 psci_cpu_on(u32 function_id, u32 target_cpu, u32 pc, - u32 context_id); -s32 psci_affinity_info(u32 function_id, u32 target_affinity, - u32 lowest_affinity_level); +s32 psci_cpu_on(u32 function_id, u32 target_cpu, u32 pc, u32 context_id); +s32 psci_affinity_info(u32 function_id, u32 target_affinity, u32 power_level); u32 psci_migrate_info_type(void); void psci_system_off(void); void psci_system_reset(void); +s32 psci_features(u32 function_id, u32 psci_fid); +s32 psci_cpu_default_suspend(u32 function_id, u32 pc, u32 context_id); +s32 psci_node_hw_state(u32 function_id, u32 target_cpu, u32 power_level); +s32 psci_system_suspend(u32 function_id, u32 pc, u32 context_id); +s32 psci_system_reset2(u32 function_id, u32 reset_type, u32 cookie); #endif #endif /* __ASSEMBLY__ */ diff --git a/arch/arm/lib/psci-dt.c b/arch/arm/lib/psci-dt.c index 903b3357048..ea9d1c8355c 100644 --- a/arch/arm/lib/psci-dt.c +++ b/arch/arm/lib/psci-dt.c @@ -66,6 +66,8 @@ int fdt_psci(void *fdt) init_psci_node: #if CONFIG_IS_ENABLED(ARMV8_SEC_FIRMWARE_SUPPORT) psci_ver = sec_firmware_support_psci_version(); +#elif defined(CONFIG_ARMV7_PSCI_1_1) + psci_ver = ARM_PSCI_VER_1_1; #elif defined(CONFIG_ARMV7_PSCI_1_0) || defined(CONFIG_ARMV8_PSCI) psci_ver = ARM_PSCI_VER_1_0; #elif defined(CONFIG_ARMV7_PSCI_0_2) diff --git a/board/sunxi/board.c b/board/sunxi/board.c index 7a43448f49d..f6863cf687b 100644 --- a/board/sunxi/board.c +++ b/board/sunxi/board.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -857,6 +858,13 @@ int board_late_init(void) usb_ether_init(); #endif +#ifdef SUNXI_SCP_BASE + if (!rproc_load(0, SUNXI_SCP_BASE, SUNXI_SCP_MAX_SIZE)) { + puts("Starting SCP...\n"); + rproc_start(0); + } +#endif + return 0; } diff --git a/common/spl/Kconfig b/common/spl/Kconfig index 50ff113cab2..69adcf66fea 100644 --- a/common/spl/Kconfig +++ b/common/spl/Kconfig @@ -477,8 +477,7 @@ config SPL_MD5 config SPL_FIT_IMAGE_TINY bool "Remove functionality from SPL FIT loading to reduce size" depends on SPL_FIT - default y if MACH_SUN50I || MACH_SUN50I_H5 || SUN50I_GEN_H6 - default y if ARCH_IMX8M + default y if ARCH_IMX8M || ARCH_SUNXI help Enable this to reduce the size of the FIT image loading code in SPL, if space for the SPL binary is very tight. diff --git a/configs/orangepi_one_defconfig b/configs/orangepi_one_defconfig index 1064b4a39de..7af932870ab 100644 --- a/configs/orangepi_one_defconfig +++ b/configs/orangepi_one_defconfig @@ -6,5 +6,6 @@ CONFIG_MACH_SUN8I_H3=y CONFIG_DRAM_CLK=672 # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set CONFIG_SUN8I_EMAC=y +CONFIG_REMOTEPROC_SUN6I_AR100=y CONFIG_USB_EHCI_HCD=y CONFIG_USB_OHCI_HCD=y diff --git a/configs/orangepi_plus2e_defconfig b/configs/orangepi_plus2e_defconfig index 138a6a72b8c..ba2ea6598b6 100644 --- a/configs/orangepi_plus2e_defconfig +++ b/configs/orangepi_plus2e_defconfig @@ -12,5 +12,6 @@ CONFIG_SPL_SYS_I2C_LEGACY=y CONFIG_SYS_I2C_MVTWSI=y CONFIG_SUN8I_EMAC=y CONFIG_SY8106A_POWER=y +CONFIG_REMOTEPROC_SUN6I_AR100=y CONFIG_USB_EHCI_HCD=y CONFIG_USB_OHCI_HCD=y diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig index 27e4a60ff5b..bcaf6a2753d 100644 --- a/drivers/remoteproc/Kconfig +++ b/drivers/remoteproc/Kconfig @@ -41,6 +41,15 @@ config REMOTEPROC_STM32_COPRO Say 'y' here to add support for STM32 Cortex-M4 coprocessors via the remoteproc framework. +config REMOTEPROC_SUN6I_AR100 + bool "Support for Allwinner AR100 SCP" + select REMOTEPROC + depends on ARCH_SUNXI + help + Say 'y' here to support Allwinner's AR100 System Control Processor + (SCP), found in various sun6i/sun8i/sun50i family SoCs, through the + remoteproc framework. + config REMOTEPROC_TI_K3_ARM64 bool "Support for TI's K3 based ARM64 remoteproc driver" select REMOTEPROC diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile index fbe9c172bc0..9529844b2aa 100644 --- a/drivers/remoteproc/Makefile +++ b/drivers/remoteproc/Makefile @@ -10,6 +10,7 @@ obj-$(CONFIG_$(SPL_)REMOTEPROC) += rproc-uclass.o rproc-elf-loader.o obj-$(CONFIG_K3_SYSTEM_CONTROLLER) += k3_system_controller.o obj-$(CONFIG_REMOTEPROC_SANDBOX) += sandbox_testproc.o obj-$(CONFIG_REMOTEPROC_STM32_COPRO) += stm32_copro.o +obj-$(CONFIG_REMOTEPROC_SUN6I_AR100) += sun6i_ar100_rproc.o obj-$(CONFIG_REMOTEPROC_TI_K3_ARM64) += ti_k3_arm64_rproc.o obj-$(CONFIG_REMOTEPROC_TI_K3_DSP) += ti_k3_dsp_rproc.o obj-$(CONFIG_REMOTEPROC_TI_K3_R5F) += ti_k3_r5f_rproc.o diff --git a/drivers/remoteproc/sun6i_ar100_rproc.c b/drivers/remoteproc/sun6i_ar100_rproc.c new file mode 100644 index 00000000000..c94f6c752bd --- /dev/null +++ b/drivers/remoteproc/sun6i_ar100_rproc.c @@ -0,0 +1,111 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include +#include +#include + +#define SUNXI_SCP_MAGIC 0xb4400012 + +#define OR1K_VEC_FIRST 0x01 +#define OR1K_VEC_LAST 0x0e +#define OR1K_VEC_ADDR(n) (0x100 * (n)) + +struct sun6i_ar100_rproc_priv { + void *cfg_base; + ulong sram_base; +}; + +static int sun6i_ar100_rproc_load(struct udevice *dev, ulong addr, ulong size) +{ + struct sun6i_ar100_rproc_priv *priv = dev_get_priv(dev); + + /* Check for a valid SCP firmware. */ + if (readl_relaxed(addr) != SUNXI_SCP_MAGIC) + return -ENOENT; + + /* Program exception vectors to the firmware entry point. */ + for (u32 i = OR1K_VEC_FIRST; i <= OR1K_VEC_LAST; ++i) { + ulong vector = priv->sram_base + OR1K_VEC_ADDR(i); + ulong offset = addr - vector; + + writel_relaxed(offset >> 2, vector); + } + + return 0; +} + +static int sun6i_ar100_rproc_start(struct udevice *dev) +{ + struct sun6i_ar100_rproc_priv *priv = dev_get_priv(dev); + + setbits_le32(priv->cfg_base, BIT(0)); + + return 0; +} + +static int sun6i_ar100_rproc_stop(struct udevice *dev) +{ + struct sun6i_ar100_rproc_priv *priv = dev_get_priv(dev); + + clrbits_le32(priv->cfg_base, BIT(0)); + + return 0; +} + +static int sun6i_ar100_rproc_reset(struct udevice *dev) +{ + int ret; + + ret = sun6i_ar100_rproc_stop(dev); + if (ret) + return ret; + + return sun6i_ar100_rproc_start(dev); +} + +static int sun6i_ar100_rproc_is_running(struct udevice *dev) +{ + struct sun6i_ar100_rproc_priv *priv = dev_get_priv(dev); + + return !(readl_relaxed(priv->cfg_base) & BIT(0)); +} + +static const struct dm_rproc_ops sun6i_ar100_rproc_ops = { + .load = sun6i_ar100_rproc_load, + .start = sun6i_ar100_rproc_start, + .stop = sun6i_ar100_rproc_stop, + .reset = sun6i_ar100_rproc_reset, + .is_running = sun6i_ar100_rproc_is_running, +}; + +static int sun6i_ar100_rproc_probe(struct udevice *dev) +{ + struct sun6i_ar100_rproc_priv *priv = dev_get_priv(dev); + struct ofnode_phandle_args sram_handle; + int ret; + + priv->cfg_base = dev_read_addr_ptr(dev); + + ret = dev_read_phandle_with_args(dev, "sram", NULL, 0, 0, &sram_handle); + if (ret) + return ret; + + priv->sram_base = ofnode_get_addr(sram_handle.node); + + return 0; +} + +static const struct udevice_id sun6i_ar100_rproc_ids[] = { + { .compatible = "allwinner,sun6i-a31-ar100" }, + { } +}; + +U_BOOT_DRIVER(sun6i_ar100_rproc) = { + .name = "sun6i_ar100_rproc", + .id = UCLASS_REMOTEPROC, + .of_match = sun6i_ar100_rproc_ids, + .probe = sun6i_ar100_rproc_probe, + .priv_auto = sizeof(struct sun6i_ar100_rproc_priv), + .ops = &sun6i_ar100_rproc_ops, +}; diff --git a/include/configs/sun8i.h b/include/configs/sun8i.h index 106139d0904..c6514ca57c6 100644 --- a/include/configs/sun8i.h +++ b/include/configs/sun8i.h @@ -14,6 +14,15 @@ #include +#ifdef SUNXI_SRAM_A2_SIZE +#define SUNXI_RESUME_BASE (CONFIG_ARMV7_SECURE_BASE + \ + CONFIG_ARMV7_SECURE_MAX_SIZE) +#define SUNXI_RESUME_SIZE 1024 + +#define SUNXI_SCP_BASE (SUNXI_RESUME_BASE + SUNXI_RESUME_SIZE) +#define SUNXI_SCP_MAX_SIZE (16 * 1024) +#endif + /* * Include common sunxi configuration where most the settings are */ diff --git a/tools/binman/entry.py b/tools/binman/entry.py index a07a5888643..a1ece8a2f98 100644 --- a/tools/binman/entry.py +++ b/tools/binman/entry.py @@ -465,7 +465,9 @@ class Entry(object): if self.offset_unset: self.Raise('No offset set with offset-unset: should another ' 'entry provide this correct offset?') - self.offset = tools.align(offset, self.align) + elif self.offset > offset: + offset = self.offset + self.offset = tools.align(offset, self.align) needed = self.pad_before + self.contents_size + self.pad_after needed = tools.align(needed, self.align_size) size = self.size