From 949aa5d7db78be19156b2260df14b59520234745 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sat, 9 Oct 2021 10:40:45 -0500 Subject: [PATCH 01/11] Kconfig: Remove an impossible condition ARCH_SUNXI selects BINMAN, so the condition "!BINMAN && ARCH_SUNXI" is impossible to satisfy. Signed-off-by: Samuel Holland --- Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From 8dd7e45d3262c965b98ff013995bab11ed813db1 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sat, 9 Oct 2021 10:43:56 -0500 Subject: [PATCH 02/11] binman: Prevent entries in a section from overlapping Currently, if the "offset" property is given for an entry, the section's running offset is completely ignored. This causes entries to overlap if the provided offset is less than the size of the entries earlier in the section. Avoid the overlap by only using the provided offset when it is greater than the running offset. The motivation for this change is the rule used by SPL to find U-Boot on sunxi boards: U-Boot starts 32 KiB after the start of SPL, unless SPL is larger than 32 KiB, in which case U-Boot immediately follows SPL. Signed-off-by: Samuel Holland --- tools/binman/entry.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 From 2dee8941d7895199f36db3d8ff0547614da1733c Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sat, 17 Apr 2021 13:33:54 -0500 Subject: [PATCH 03/11] sunxi: binman: Enable SPL FIT loading for 32-bit SoCs Now that Crust (SCP firmware) has support for H3, we need a FIT image to load it. H3 also needs to load a SoC-specific eGon blob to support CPU 0 hotplug. Let's first enable FIT support before adding extra firmware. Update the binman description to work on either 32-bit or 64-bit SoCs: - Make BL31 optional, since it is not used on 32-bit SoCs (though BL32 may be used in the future). - Explicitly set the minimum offset of the FIT to 32 KiB, since SPL on some boards is still only 24 KiB large even with FIT support enabled. CONFIG_SPL_PAD_TO cannot be used because it is not defined for H616. FIT unlocks more features (signatures, multiple DTBs, etc.), so enable it by default. A10 (sun4i) only has 24 KiB of SRAM A1, so it needs SPL_FIT_IMAGE_TINY. For simplicity, enable that option everywhere. Cover-letter: sunxi: SPL FIT support for 32-bit sunxi SoCs This series makes the necessary changes so 32-bit sunxi SoCs can load additional device trees or firmware from SPL along with U-Boot proper. There was no existing binman entry property that put the FIT at the right offset. The minimum offset is 32k, but this matches neither the SPL size (which is no more than 24k on some SoCs) nor the FIT alignment (which is 512 bytes in practice due to SPL size constraints). So instead of adding a new property, I fixed what is arguably a bug in the offset property -- though this strategy will not work if someone is intentionally creating overlapping entries. END Series-to: sunxi Series-to: sjg Signed-off-by: Samuel Holland --- arch/arm/Kconfig | 1 + arch/arm/dts/sunxi-u-boot.dtsi | 46 ++++++++++++++++++++++------------ common/spl/Kconfig | 3 +-- 3 files changed, 32 insertions(+), 18 deletions(-) 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/dts/sunxi-u-boot.dtsi b/arch/arm/dts/sunxi-u-boot.dtsi index 0f573e6d7ae..e75c6904914 100644 --- a/arch/arm/dts/sunxi-u-boot.dtsi +++ b/arch/arm/dts/sunxi-u-boot.dtsi @@ -1,13 +1,19 @@ #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_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 +40,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 +76,7 @@ missing-msg = "atf-bl31-sunxi"; }; }; +#endif #ifdef SCP_ADDR scp { @@ -95,19 +105,23 @@ @config-SEQ { description = "NAME"; +#ifdef BL31_ADDR firmware = "atf"; -#ifndef SCP_ADDR - loadables = "uboot"; #else - loadables = "scp", "uboot"; + firmware = "uboot"; #endif + loadables = +#ifdef SCP_ADDR + "scp", +#endif + "uboot"; fdt = "fdt-SEQ"; }; }; }; #else u-boot-img { - offset = ; + offset = <32768>; }; #endif }; 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. From 9903022de1e23cca35f90055601b45bbbfa94117 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sat, 9 Oct 2021 22:00:22 -0500 Subject: [PATCH 04/11] sunxi: psci: Avoid hanging when CPU 0 is hot-unplugged Do not try to send an SGI from CPU 0 to itself. Since FIQs are masked when entering monitor mode, this will hang. Plus, CPU 0 cannot fully power itself off anyway. Instead, have it turn FIQs back on and continue servicing SGIs from other cores. Signed-off-by: Samuel Holland --- arch/arm/cpu/armv7/sunxi/psci.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/arch/arm/cpu/armv7/sunxi/psci.c b/arch/arm/cpu/armv7/sunxi/psci.c index d1bd6b9be41..6cc9f71167b 100644 --- a/arch/arm/cpu/armv7/sunxi/psci.c +++ b/arch/arm/cpu/armv7/sunxi/psci.c @@ -38,6 +38,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)); @@ -281,9 +290,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) From c71c588f3bb284434c6c98f456e4639cb4dd97a1 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sat, 9 Oct 2021 17:12:57 -0500 Subject: [PATCH 05/11] sunxi: psci: Add support for H3 CPU 0 hotplug Due to a bug in the H3 SoC, where the CPU 0 hotplug flag cannot be written, resuming CPU 0 requires using the "Super Standby" code path in the BROM instead of the hotplug path. This path requires jumping to an eGON image in SRAM. Add support to the build system to generate this eGON image and include it in the FIT, and add code to direct the BROM to its location in SRAM. Since the Super Standby code path in the BROM initializes the CPU and AHB1 clocks to 24 MHz, those registers need to be restored after control passes back to U-Boot. Furthermore, because the BROM lowers the AHB1 clock divider to /1 before switching to the lower-frequency parent, PLL_PERIPH0 must be bypassed to prevent AHB1 from temporarily running at 600 MHz. Otherwise, this locks up the SoC. Signed-off-by: Samuel Holland --- Makefile | 17 +++++++++++++++++ arch/arm/cpu/armv7/sunxi/psci.c | 31 +++++++++++++++++++++++++++++++ arch/arm/dts/sunxi-u-boot.dtsi | 23 ++++++++++++++++++++++- include/configs/sun8i.h | 6 ++++++ 4 files changed, 76 insertions(+), 1 deletion(-) 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/cpu/armv7/sunxi/psci.c b/arch/arm/cpu/armv7/sunxi/psci.c index 6cc9f71167b..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 @@ -141,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 @@ -255,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); @@ -274,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)); diff --git a/arch/arm/dts/sunxi-u-boot.dtsi b/arch/arm/dts/sunxi-u-boot.dtsi index e75c6904914..dc00d2d0f96 100644 --- a/arch/arm/dts/sunxi-u-boot.dtsi +++ b/arch/arm/dts/sunxi-u-boot.dtsi @@ -6,7 +6,11 @@ #define ARCH "arm" #endif -#if defined(CONFIG_MACH_SUN50I) || defined(CONFIG_MACH_SUN50I_H5) +#if defined(CONFIG_MACH_SUN8I_H3) +#ifdef CONFIG_ARMV7_PSCI +#define RESUME_ADDR SUNXI_RESUME_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) @@ -78,6 +82,20 @@ }; #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 { description = "SCP firmware"; @@ -111,6 +129,9 @@ firmware = "uboot"; #endif loadables = +#ifdef RESUME_ADDR + "resume", +#endif #ifdef SCP_ADDR "scp", #endif diff --git a/include/configs/sun8i.h b/include/configs/sun8i.h index 106139d0904..a25bb9647e5 100644 --- a/include/configs/sun8i.h +++ b/include/configs/sun8i.h @@ -14,6 +14,12 @@ #include +#ifdef SUNXI_SRAM_A2_SIZE +#define SUNXI_RESUME_BASE (CONFIG_ARMV7_SECURE_BASE + \ + CONFIG_ARMV7_SECURE_MAX_SIZE) +#define SUNXI_RESUME_SIZE 1024 +#endif + /* * Include common sunxi configuration where most the settings are */ From 21a63610ccc79e09e900f3fdf4b61f4de0c1d177 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sat, 9 Oct 2021 14:58:27 -0500 Subject: [PATCH 06/11] remoteproc: Add a driver for the Allwinner AR100 Signed-off-by: Samuel Holland --- drivers/remoteproc/Kconfig | 9 ++ drivers/remoteproc/Makefile | 1 + drivers/remoteproc/sun6i_ar100_rproc.c | 111 +++++++++++++++++++++++++ 3 files changed, 121 insertions(+) create mode 100644 drivers/remoteproc/sun6i_ar100_rproc.c 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, +}; From ce830d0f09919db56d60923aacbd1cc13c683edb Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sat, 9 Oct 2021 15:04:16 -0500 Subject: [PATCH 07/11] arm: dts: sunxi: h3: Add nodes for AR100 remoteproc Signed-off-by: Samuel Holland --- arch/arm/dts/sun8i-h3.dtsi | 14 ++++++++++++++ 1 file changed, 14 insertions(+) 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 { From 0869ef0c748257d3b1e2f4e2ef3eb4a83e607a73 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sat, 17 Apr 2021 13:33:54 -0500 Subject: [PATCH 08/11] sunxi: Enable support for SCP firmware on H3 Now that issues with the BROM have been sorted out, we can implement PSCI system suspend on H3 by delegating to SCP firmware. Let's start by including the firmware in the FIT image and starting the coprocessor if valid firmware is loaded. Signed-off-by: Samuel Holland --- arch/arm/dts/sunxi-u-boot.dtsi | 1 + board/sunxi/board.c | 8 ++++++++ include/configs/sun8i.h | 3 +++ 3 files changed, 12 insertions(+) diff --git a/arch/arm/dts/sunxi-u-boot.dtsi b/arch/arm/dts/sunxi-u-boot.dtsi index dc00d2d0f96..ee4c374b096 100644 --- a/arch/arm/dts/sunxi-u-boot.dtsi +++ b/arch/arm/dts/sunxi-u-boot.dtsi @@ -9,6 +9,7 @@ #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 diff --git a/board/sunxi/board.c b/board/sunxi/board.c index 806e3bcb694..0e58dfdd02e 100644 --- a/board/sunxi/board.c +++ b/board/sunxi/board.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -860,6 +861,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/include/configs/sun8i.h b/include/configs/sun8i.h index a25bb9647e5..c6514ca57c6 100644 --- a/include/configs/sun8i.h +++ b/include/configs/sun8i.h @@ -18,6 +18,9 @@ #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 /* From ae885f7c7be05aaf5bfa5ad15829d4c7f4a83be4 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sat, 9 Oct 2021 22:43:26 -0500 Subject: [PATCH 09/11] arm: psci: Add definitions for PSCI v1.1 Add the new option, function IDs, and prototypes for PSCI v1.1 implementations. In the process, fix some issues with the existing definitions: - Fix the incorrectly-named ARM_PSCI_0_2_FN64_SYSTEM_RESET2. - Replace the deprecated "affinity_level" naming with "power_level". Signed-off-by: Samuel Holland --- arch/arm/cpu/armv7/Kconfig | 3 +++ arch/arm/cpu/armv8/fwcall.c | 2 +- arch/arm/include/asm/psci.h | 9 +++++++-- arch/arm/include/asm/system.h | 14 +++++++++----- arch/arm/lib/psci-dt.c | 2 ++ 5 files changed, 22 insertions(+), 8 deletions(-) diff --git a/arch/arm/cpu/armv7/Kconfig b/arch/arm/cpu/armv7/Kconfig index f1e4e26b8f0..c2d0491a37b 100644 --- a/arch/arm/cpu/armv7/Kconfig +++ b/arch/arm/cpu/armv7/Kconfig @@ -80,6 +80,9 @@ choice 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/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/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) From a48abdd8f6d5de41ed1e2cf6950215a4366214a9 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Mon, 11 Oct 2021 03:20:28 -0500 Subject: [PATCH 10/11] [DO NOT MERGE] sunxi: Enable remoteproc on some H3 boards Signed-off-by: Samuel Holland --- configs/orangepi_one_defconfig | 1 + configs/orangepi_plus2e_defconfig | 1 + 2 files changed, 2 insertions(+) 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 From ca557c807b6d0ea835b4407e6e5ff65bf8086d40 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sat, 9 Oct 2021 23:01:05 -0500 Subject: [PATCH 11/11] [DO NOT MERGE] sunxi: psci: Delegate PSCI to SCPI This adds a new PSCI implementation which communicates with SCP firmware running on the AR100 using the SCPI protocol. This allows it to support the full set of PSCI v1.1 features, including CPU idle states, system suspend, and multiple reset methods. Signed-off-by: Samuel Holland --- arch/arm/cpu/armv7/Kconfig | 2 +- arch/arm/cpu/armv7/sunxi/Makefile | 4 + arch/arm/cpu/armv7/sunxi/psci-scpi.c | 451 +++++++++++++++++++++++++++ 3 files changed, 456 insertions(+), 1 deletion(-) create mode 100644 arch/arm/cpu/armv7/sunxi/psci-scpi.c diff --git a/arch/arm/cpu/armv7/Kconfig b/arch/arm/cpu/armv7/Kconfig index c2d0491a37b..cd5dc202755 100644 --- a/arch/arm/cpu/armv7/Kconfig +++ b/arch/arm/cpu/armv7/Kconfig @@ -75,7 +75,7 @@ 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. 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); +}