On 1/31/19 6:38 PM, Oleksandr Tyshchenko wrote: > From: Oleksandr Tyshchenko <oleksandr_tyshche...@epam.com> > > Also enable PSCI support for Stout and Lager boards where > actually the r8a7790 SoC is installed. > > All secondary CPUs will be switched to a non-secure HYP mode > after booting. > > Signed-off-by: Oleksandr Tyshchenko <oleksandr_tyshche...@epam.com> > --- > arch/arm/mach-rmobile/Kconfig.32 | 2 + > arch/arm/mach-rmobile/Makefile | 6 + > arch/arm/mach-rmobile/pm-r8a7790.c | 408 > +++++++++++++++++++++++++++++++++++++ > arch/arm/mach-rmobile/pm-r8a7790.h | 72 +++++++ > arch/arm/mach-rmobile/psci.c | 193 ++++++++++++++++++ > include/configs/lager.h | 2 + > include/configs/stout.h | 2 + > 7 files changed, 685 insertions(+) > create mode 100644 arch/arm/mach-rmobile/pm-r8a7790.c > create mode 100644 arch/arm/mach-rmobile/pm-r8a7790.h > create mode 100644 arch/arm/mach-rmobile/psci.c > > diff --git a/arch/arm/mach-rmobile/Kconfig.32 > b/arch/arm/mach-rmobile/Kconfig.32 > index a2e9e3d..728c323 100644 > --- a/arch/arm/mach-rmobile/Kconfig.32 > +++ b/arch/arm/mach-rmobile/Kconfig.32 > @@ -78,6 +78,7 @@ config TARGET_LAGER > imply CMD_DM > select CPU_V7_HAS_NONSEC > select CPU_V7_HAS_VIRT > + select ARCH_SUPPORT_PSCI > > config TARGET_KZM9G > bool "KZM9D board" > @@ -119,6 +120,7 @@ config TARGET_STOUT > imply CMD_DM > select CPU_V7_HAS_NONSEC > select CPU_V7_HAS_VIRT > + select ARCH_SUPPORT_PSCI > > endchoice > > diff --git a/arch/arm/mach-rmobile/Makefile b/arch/arm/mach-rmobile/Makefile > index 1f26ada..6f4c513 100644 > --- a/arch/arm/mach-rmobile/Makefile > +++ b/arch/arm/mach-rmobile/Makefile > @@ -13,3 +13,9 @@ obj-$(CONFIG_SH73A0) += lowlevel_init.o cpu_info-sh73a0.o > pfc-sh73a0.o > obj-$(CONFIG_R8A7740) += lowlevel_init.o cpu_info-r8a7740.o pfc-r8a7740.o > obj-$(CONFIG_RCAR_GEN2) += lowlevel_init_ca15.o cpu_info-rcar.o > obj-$(CONFIG_RCAR_GEN3) += lowlevel_init_gen3.o cpu_info-rcar.o memmap-gen3.o > + > +ifndef CONFIG_SPL_BUILD > +ifdef CONFIG_R8A7790 > +obj-$(CONFIG_ARMV7_PSCI) += psci.o pm-r8a7790.o > +endif > +endif > diff --git a/arch/arm/mach-rmobile/pm-r8a7790.c > b/arch/arm/mach-rmobile/pm-r8a7790.c > new file mode 100644 > index 0000000..c635cf6 > --- /dev/null > +++ b/arch/arm/mach-rmobile/pm-r8a7790.c > @@ -0,0 +1,408 @@ > +// SPDX-License-Identifier: GPL-2.0+ > +/* > + * CPU power management support for Renesas r8a7790 SoC > + * > + * Contains functions to control ARM Cortex A15/A7 cores and > + * related peripherals basically used for PSCI. > + * > + * Copyright (C) 2018 EPAM Systems Inc. > + * > + * Mainly based on Renesas R-Car Gen2 platform code from Linux: > + * arch/arm/mach-shmobile/... > + */ > + > +#include <common.h> > +#include <asm/secure.h> > +#include <asm/io.h> > + > +#include "pm-r8a7790.h" > + > +/*****************************************************************************
I'd expect checkpatch to complain about these long lines of asterisks. > + * APMU definitions > + > *****************************************************************************/ > +#define CA15_APMU_BASE 0xe6152000 > +#define CA7_APMU_BASE 0xe6151000 All these addresses should come from DT. And the driver should be DM capable and live in drivers/ [...] > +/***************************************************************************** > + * Functions which intended to be called from PSCI handlers. These functions > + * marked as __secure and are placed in .secure section. > + > *****************************************************************************/ > +void __secure r8a7790_apmu_power_on(int cpu) > +{ > + int cluster = r8a7790_cluster_id(cpu); > + u32 apmu_base; > + > + apmu_base = cluster == 0 ? CA15_APMU_BASE : CA7_APMU_BASE; > + > + /* Request power on */ > + writel(BIT(r8a7790_core_id(cpu)), apmu_base + WUPCR_OFFS); wait_for_bit of some sorts ? > + /* Wait for APMU to finish */ > + while (readl(apmu_base + WUPCR_OFFS)) > + ; Can this spin forever ? > +} > + > +void __secure r8a7790_apmu_power_off(int cpu) > +{ > + int cluster = r8a7790_cluster_id(cpu); > + u32 apmu_base; > + > + apmu_base = cluster == 0 ? CA15_APMU_BASE : CA7_APMU_BASE; > + > + /* Request Core Standby for next WFI */ > + writel(CPUPWR_STANDBY, apmu_base + CPUNCR_OFFS(r8a7790_core_id(cpu))); > +} > + > +void __secure r8a7790_assert_reset(int cpu) > +{ > + int cluster = r8a7790_cluster_id(cpu); > + u32 mask, magic, rescnt; > + > + mask = BIT(3 - r8a7790_core_id(cpu)); > + magic = cluster == 0 ? CA15RESCNT_CODE : CA7RESCNT_CODE; > + rescnt = RST_BASE + (cluster == 0 ? CA15RESCNT : CA7RESCNT); > + writel((readl(rescnt) | mask) | magic, rescnt); > +} > + > +void __secure r8a7790_deassert_reset(int cpu) > +{ > + int cluster = r8a7790_cluster_id(cpu); > + u32 mask, magic, rescnt; > + > + mask = BIT(3 - r8a7790_core_id(cpu)); > + magic = cluster == 0 ? CA15RESCNT_CODE : CA7RESCNT_CODE; > + rescnt = RST_BASE + (cluster == 0 ? CA15RESCNT : CA7RESCNT); > + writel((readl(rescnt) & ~mask) | magic, rescnt); > +} > + > +void __secure r8a7790_system_reset(void) > +{ > + u32 bar; > + > + /* > + * Before configuring internal watchdog timer (RWDT) to reboot system > + * we need to re-program BAR registers for the boot CPU to jump to > + * bootrom code. Without taking care of, the boot CPU will jump to > + * the reset vector previously installed in ICRAM1, since BAR registers > + * keep their values after watchdog triggered reset. > + */ > + bar = (BOOTROM >> 8) & 0xfffffc00; > + writel(bar, RST_BASE + CA15BAR); > + writel(bar | SBAR_BAREN, RST_BASE + CA15BAR); > + writel(bar, RST_BASE + CA7BAR); > + writel(bar | SBAR_BAREN, RST_BASE + CA7BAR); > + dsb(); > + > + /* Now, configure watchdog timer to reboot the system */ > + > + /* Trigger reset when counter overflows */ > + writel(WDTRSTCR_CODE | 0x2, RST_BASE + WDTRSTCR); > + dsb(); > + > + /* Stop counter */ > + writel(RWTCSRA_CODE, RWDT_BASE + RWTCSRA); > + > + /* Initialize counter with the highest value */ > + writel(RWTCNT_CODE | 0xffff, RWDT_BASE + RWTCNT); > + > + while (readb(RWDT_BASE + RWTCSRA) & RWTCSRA_WRFLG) > + ; > + > + /* Start counter */ > + writel(RWTCSRA_CODE | RWTCSRA_TME, RWDT_BASE + RWTCSRA); This seems to reimplement the reset code that's in U-Boot already. Why ? > +} > + > +/***************************************************************************** > + * Functions which intended to be called from PSCI board initialization. > + > *****************************************************************************/ > +static int sysc_power_up(int scu) > +{ > + u32 status, chan_offs, isr_bit; > + int i, j, ret = 0; > + > + if (scu == CA15_SCU) { > + chan_offs = CA15_SCU_CHAN_OFFS; > + isr_bit = CA15_SCU_ISR_BIT; > + } else { > + chan_offs = CA7_SCU_CHAN_OFFS; > + isr_bit = CA7_SCU_ISR_BIT; > + } > + > + writel(BIT(isr_bit), SYSC_BASE + SYSCISCR); > + > + /* Submit power resume request until it was accepted */ > + for (i = 0; i < PWRER_RETRIES; i++) { > + /* Wait until SYSC is ready to accept a power resume request */ > + for (j = 0; j < SYSCSR_RETRIES; j++) { > + if (readl(SYSC_BASE + SYSCSR) & BIT(1)) > + break; > + > + udelay(SYSCSR_DELAY_US); > + } > + > + if (j == SYSCSR_RETRIES) > + return -EAGAIN; > + > + /* Submit power resume request */ > + writel(BIT(0), SYSC_BASE + chan_offs + PWRONCR_OFFS); > + > + /* Check if power resume request was accepted */ > + status = readl(SYSC_BASE + chan_offs + PWRER_OFFS); > + if (!(status & BIT(0))) > + break; > + > + udelay(PWRER_DELAY_US); > + } > + > + if (i == PWRER_RETRIES) > + return -EIO; > + > + /* Wait until the power resume request has completed */ > + for (i = 0; i < SYSCISR_RETRIES; i++) { > + if (readl(SYSC_BASE + SYSCISR) & BIT(isr_bit)) > + break; > + udelay(SYSCISR_DELAY_US); > + } > + > + if (i == SYSCISR_RETRIES) > + ret = -EIO; > + > + writel(BIT(isr_bit), SYSC_BASE + SYSCISCR); > + > + return ret; > +} > + > +static bool sysc_power_is_off(int scu) > +{ > + u32 status, chan_offs; > + > + chan_offs = scu == CA15_SCU ? CA15_SCU_CHAN_OFFS : CA7_SCU_CHAN_OFFS; > + > + /* Check if SCU is in power shutoff state */ > + status = readl(SYSC_BASE + chan_offs + PWRSR_OFFS); > + if (status & BIT(0)) > + return true; > + > + return false; > +} > + > +static void apmu_setup_debug_mode(int cpu) > +{ > + int cluster = r8a7790_cluster_id(cpu); > + u32 apmu_base, reg; > + > + apmu_base = cluster == 0 ? CA15_APMU_BASE : CA7_APMU_BASE; > + > + /* > + * Enable reset requests derived from power shutoff to the AP-system > + * CPU cores in debug mode. Without taking care of, they may fail to > + * resume from the power shutoff state. > + */ > + reg = readl(apmu_base + DBGRCR_OFFS); > + reg |= DBGCPUREN | DBGCPUNREN(r8a7790_core_id(cpu)) | DBGCPUPREN; > + writel(reg, apmu_base + DBGRCR_OFFS); setbits_le32() > +} > + > +/* > + * Reset vector for secondary CPUs. > + * This will be mapped at address 0 by SBAR register. > + * We need _long_ jump to the physical address. > + */ > +asm(" .arm\n" > + " .align 12\n" > + " .globl shmobile_boot_vector\n" > + "shmobile_boot_vector:\n" > + " ldr r1, 1f\n" > + " bx r1\n" > + " .type shmobile_boot_vector, %function\n" > + " .size shmobile_boot_vector, .-shmobile_boot_vector\n" > + " .align 2\n" > + " .globl shmobile_boot_fn\n" > + "shmobile_boot_fn:\n" > + "1: .space 4\n" > + " .globl shmobile_boot_size\n" > + "shmobile_boot_size:\n" > + " .long .-shmobile_boot_vector\n"); Why can't this be implemented in C ? > +extern void shmobile_boot_vector(void); > +extern unsigned long shmobile_boot_fn; > +extern unsigned long shmobile_boot_size; Are the externs necessary ? > +void r8a7790_prepare_cpus(unsigned long addr, u32 nr_cpus) > +{ > + int cpu = get_current_cpu(); > + int i, ret = 0; > + u32 bar; > + > + shmobile_boot_fn = addr; > + dsb(); > + > + /* Install reset vector */ > + memcpy_toio((void __iomem *)ICRAM1, shmobile_boot_vector, > + shmobile_boot_size); > + dmb(); Does this take into consideration caches ? > + /* Setup reset vectors */ > + bar = (ICRAM1 >> 8) & 0xfffffc00; > + writel(bar, RST_BASE + CA15BAR); > + writel(bar | SBAR_BAREN, RST_BASE + CA15BAR); > + writel(bar, RST_BASE + CA7BAR); > + writel(bar | SBAR_BAREN, RST_BASE + CA7BAR); > + dsb(); > + > + /* Perform setup for debug mode for all CPUs */ > + for (i = 0; i < nr_cpus; i++) > + apmu_setup_debug_mode(i); > + > + /* > + * Indicate the completion status of power shutoff/resume procedure > + * for CA15/CA7 SCU. > + */ > + writel(BIT(CA15_SCU_ISR_BIT) | BIT(CA7_SCU_ISR_BIT), > + SYSC_BASE + SYSCIER); > + > + /* Power on CA15/CA7 SCU */ > + if (sysc_power_is_off(CA15_SCU)) > + ret += sysc_power_up(CA15_SCU); > + if (sysc_power_is_off(CA7_SCU)) > + ret += sysc_power_up(CA7_SCU); > + if (ret) > + printf("warning: some of secondary CPUs may not boot\n"); "some" is not very useful for debugging,. > + /* Keep secondary CPUs in reset */ > + for (i = 0; i < nr_cpus; i++) { > + /* Make sure that we don't reset boot CPU */ > + if (i == cpu) > + continue; > + > + r8a7790_assert_reset(i); > + } > + > + /* > + * Enable snoop requests and DVM message requests for slave interfaces > + * S4 (CA7) and S3 (CA15). > + */ > + writel(readl(CCI_BASE + CCI_SLAVE3 + CCI_SNOOP) | CCI_ENABLE_REQ, > + CCI_BASE + CCI_SLAVE3 + CCI_SNOOP); > + writel(readl(CCI_BASE + CCI_SLAVE4 + CCI_SNOOP) | CCI_ENABLE_REQ, > + CCI_BASE + CCI_SLAVE4 + CCI_SNOOP); > + /* Wait for pending bit low */ > + while (readl(CCI_BASE + CCI_STATUS)) > + ; can this spin forever ? > +} > diff --git a/arch/arm/mach-rmobile/pm-r8a7790.h > b/arch/arm/mach-rmobile/pm-r8a7790.h > new file mode 100644 > index 0000000..f649dd8 > --- /dev/null > +++ b/arch/arm/mach-rmobile/pm-r8a7790.h > @@ -0,0 +1,72 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +/* > + * Copyright (C) 2018 EPAM Systems Inc. > + */ > + > +#ifndef __PM_R8A7790_H__ > +#define __PM_R8A7790_H__ > + > +#include <linux/types.h> > + > +void r8a7790_apmu_power_on(int cpu); > +void r8a7790_apmu_power_off(int cpu); > +void r8a7790_assert_reset(int cpu); > +void r8a7790_deassert_reset(int cpu); > + > +void r8a7790_prepare_cpus(unsigned long addr, u32 nr_cpus); > +void r8a7790_system_reset(void); > + > +#define R8A7790_NR_CLUSTERS 2 > +#define R8A7790_NR_CPUS_PER_CLUSTER 4 > + > +/* Convert linear CPU index to core/cluster ID */ > +#define r8a7790_cluster_id(cpu) ((cpu) / R8A7790_NR_CPUS_PER_CLUSTER) > +#define r8a7790_core_id(cpu) ((cpu) % R8A7790_NR_CPUS_PER_CLUSTER) > + > +#define MPIDR_AFFLVL_MASK GENMASK(7, 0) > +#define MPIDR_AFF0_SHIFT 0 > +#define MPIDR_AFF1_SHIFT 8 > + > +/* All functions are inline so that they can be called from .secure section. > */ > + > +/* > + * Convert CPU ID in MPIDR format to linear CPU index. > + * > + * Below the possible CPU IDs and corresponding CPU indexes: > + * CPU ID CPU index > + * 0x80000000 - 0x00000000 > + * 0x80000001 - 0x00000001 > + * 0x80000002 - 0x00000002 > + * 0x80000003 - 0x00000003 > + * 0x80000100 - 0x00000004 > + * 0x80000101 - 0x00000005 > + * 0x80000102 - 0x00000006 > + * 0x80000103 - 0x00000007 > + */ > +static inline int mpidr_to_cpu_index(u32 mpidr) > +{ > + u32 cluster_id, cpu_id; > + > + cluster_id = (mpidr >> MPIDR_AFF1_SHIFT) & MPIDR_AFFLVL_MASK; > + cpu_id = (mpidr >> MPIDR_AFF0_SHIFT) & MPIDR_AFFLVL_MASK; > + > + if (cluster_id >= R8A7790_NR_CLUSTERS) > + return -1; > + > + if (cpu_id >= R8A7790_NR_CPUS_PER_CLUSTER) > + return -1; > + > + return (cpu_id + (cluster_id * R8A7790_NR_CPUS_PER_CLUSTER)); > +} > + > +/* Return an index of the CPU which performs this call */ > +static inline int get_current_cpu(void) > +{ > + u32 mpidr; > + > + asm volatile("mrc p15, 0, %0, c0, c0, 5" : "=r"(mpidr)); > + > + return mpidr_to_cpu_index(mpidr); > +} > + > +#endif /* __PM_R8A7790_H__ */ > diff --git a/arch/arm/mach-rmobile/psci.c b/arch/arm/mach-rmobile/psci.c > new file mode 100644 > index 0000000..95850b8 > --- /dev/null > +++ b/arch/arm/mach-rmobile/psci.c > @@ -0,0 +1,193 @@ > +// SPDX-License-Identifier: GPL-2.0+ > +/* > + * This file implements basic PSCI support for Renesas r8a7790 SoC > + * > + * Copyright (C) 2018 EPAM Systems Inc. > + * > + * Based on arch/arm/mach-uniphier/arm32/psci.c > + */ > + > +#include <common.h> > +#include <linux/psci.h> > +#include <asm/io.h> > +#include <asm/psci.h> > +#include <asm/secure.h> > + > +#include "pm-r8a7790.h" > + > +#define R8A7790_PSCI_NR_CPUS 8 > +#if R8A7790_PSCI_NR_CPUS > CONFIG_ARMV7_PSCI_NR_CPUS > +#error "invalid value for CONFIG_ARMV7_PSCI_NR_CPUS" > +#endif > + > +#define GICC_CTLR_OFFSET 0x2000 > + > +/* > + * The boot CPU is powered on by default, but it's index is not a const > + * value. An index the boot CPU has, depends on whether it is CA15 (index 0) > + * or CA7 (index 4). > + * So, we update state for the boot CPU during PSCI board initialization. > + */ > +u8 psci_state[R8A7790_PSCI_NR_CPUS] __secure_data = { > + PSCI_AFFINITY_LEVEL_OFF, > + PSCI_AFFINITY_LEVEL_OFF, > + PSCI_AFFINITY_LEVEL_OFF, > + PSCI_AFFINITY_LEVEL_OFF, > + PSCI_AFFINITY_LEVEL_OFF, > + PSCI_AFFINITY_LEVEL_OFF, > + PSCI_AFFINITY_LEVEL_OFF, > + PSCI_AFFINITY_LEVEL_OFF}; > + > +void __secure psci_set_state(int cpu, u8 state) > +{ > + psci_state[cpu] = state; > + dsb(); > + isb(); > +} > + > +u32 __secure psci_get_cpu_id(void) > +{ > + return get_current_cpu(); > +} > + > +void __secure psci_arch_cpu_entry(void) > +{ > + int cpu = get_current_cpu(); > + > + psci_set_state(cpu, PSCI_AFFINITY_LEVEL_ON); > +} > + > +int __secure psci_features(u32 function_id, u32 psci_fid) > +{ > + switch (psci_fid) { > + case ARM_PSCI_0_2_FN_PSCI_VERSION: > + 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_MIGRATE_INFO_TYPE: > + case ARM_PSCI_0_2_FN_SYSTEM_OFF: > + case ARM_PSCI_0_2_FN_SYSTEM_RESET: > + return 0x0; > + } > + > + return ARM_PSCI_RET_NI; > +} > + > +u32 __secure psci_version(u32 function_id) > +{ > + return ARM_PSCI_VER_1_0; > +} > + > +int __secure psci_affinity_info(u32 function_id, u32 target_affinity, > + u32 lowest_affinity_level) > +{ > + int cpu; > + > + if (lowest_affinity_level > 0) > + return ARM_PSCI_RET_INVAL; > + > + cpu = mpidr_to_cpu_index(target_affinity); > + if (cpu == -1) > + return ARM_PSCI_RET_INVAL; > + > + /* TODO flush cache */ > + return psci_state[cpu]; > +} > + > +int __secure psci_migrate_info_type(u32 function_id) > +{ > + /* Trusted OS is either not present or does not require migration */ > + return 2; > +} > + > +int __secure psci_cpu_on(u32 function_id, u32 target_cpu, u32 entry_point, > + u32 context_id) > +{ > + int cpu; > + > + cpu = mpidr_to_cpu_index(target_cpu); > + if (cpu == -1) > + return ARM_PSCI_RET_INVAL; > + > + if (psci_state[cpu] == PSCI_AFFINITY_LEVEL_ON) > + return ARM_PSCI_RET_ALREADY_ON; > + > + if (psci_state[cpu] == PSCI_AFFINITY_LEVEL_ON_PENDING) > + return ARM_PSCI_RET_ON_PENDING; > + > + psci_save(cpu, entry_point, context_id); > + > + psci_set_state(cpu, PSCI_AFFINITY_LEVEL_ON_PENDING); > + > + r8a7790_assert_reset(cpu); > + r8a7790_apmu_power_on(cpu); > + r8a7790_deassert_reset(cpu); > + > + return ARM_PSCI_RET_SUCCESS; > +} > + > +int __secure psci_cpu_off(void) > +{ > + int cpu = get_current_cpu(); > + > + /* > + * Place the CPU interface in a state where it can never make a CPU > + * exit WFI as result of an asserted interrupt. > + */ > + writel(0, CONFIG_ARM_GIC_BASE_ADDRESS + GICC_CTLR_OFFSET); > + dsb(); > + > + /* Select next sleep mode using the APMU */ > + r8a7790_apmu_power_off(cpu); > + > + /* Do ARM specific CPU shutdown */ > + psci_cpu_off_common(); > + > + psci_set_state(cpu, PSCI_AFFINITY_LEVEL_OFF); > + > + /* Drain the WB before WFI */ > + dsb(); > + > + while (1) > + wfi(); > +} > + > +void __secure psci_system_reset(u32 function_id) > +{ > + r8a7790_system_reset(); > + > + /* Drain the WB before WFI */ > + dsb(); > + > + /* The system is about to be rebooted, so just waiting for this */ > + while (1) > + wfi(); > +} > + > +void __secure psci_system_off(u32 function_id) > +{ > + /* Drain the WB before WFI */ > + dsb(); > + > + /* > + * System Off is not implemented yet, so waiting for powering off > + * manually > + */ > + while (1) > + wfi(); > +} > + > +void psci_board_init(void) > +{ > + int cpu = get_current_cpu(); > + > + /* Update state for the boot CPU */ > + psci_set_state(cpu, PSCI_AFFINITY_LEVEL_ON); > + > + /* > + * Perform needed actions for the secondary CPUs to be ready > + * for powering on > + */ > + r8a7790_prepare_cpus((unsigned long)psci_cpu_entry, > + R8A7790_PSCI_NR_CPUS); > +} > diff --git a/include/configs/lager.h b/include/configs/lager.h > index d8a0b01..d70c147 100644 > --- a/include/configs/lager.h > +++ b/include/configs/lager.h > @@ -51,4 +51,6 @@ > /* The PERIPHBASE in the CBAR register is wrong, so override it */ > #define CONFIG_ARM_GIC_BASE_ADDRESS 0xf1000000 > > +#define CONFIG_ARMV7_PSCI_1_0 Why is this in board code, shouldn't that be in platform code ? [...] -- Best regards, Marek Vasut _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de https://lists.denx.de/listinfo/u-boot