diff options
Diffstat (limited to 'arch/arm/mach-shmobile')
29 files changed, 1942 insertions, 0 deletions
diff --git a/arch/arm/mach-shmobile/Kconfig b/arch/arm/mach-shmobile/Kconfig new file mode 100644 index 000000000..37f862f13 --- /dev/null +++ b/arch/arm/mach-shmobile/Kconfig @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: GPL-2.0 +menuconfig ARCH_RENESAS + bool "Renesas ARM SoCs" + depends on ARCH_MULTI_V7 + select ARM_GIC + select GPIOLIB + select NO_IOPORT_MAP + select PINCTRL + select ZONE_DMA if ARM_LPAE diff --git a/arch/arm/mach-shmobile/Makefile b/arch/arm/mach-shmobile/Makefile new file mode 100644 index 000000000..f7bf17b7a --- /dev/null +++ b/arch/arm/mach-shmobile/Makefile @@ -0,0 +1,41 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for the linux kernel. +# + +# Common objects +obj-y := timer.o + +# CPU objects +obj-$(CONFIG_ARCH_SH73A0) += setup-sh73a0.o +obj-$(CONFIG_ARCH_R8A73A4) += setup-r8a73a4.o +obj-$(CONFIG_ARCH_R8A7740) += setup-r8a7740.o +obj-$(CONFIG_ARCH_R8A7778) += setup-r8a7778.o +obj-$(CONFIG_ARCH_R8A7779) += setup-r8a7779.o +obj-$(CONFIG_ARCH_EMEV2) += setup-emev2.o +obj-$(CONFIG_ARCH_R7S72100) += setup-r7s72100.o +obj-$(CONFIG_ARCH_R7S9210) += setup-r7s9210.o + +# CPU reset vector handling objects +cpu-y := platsmp.o headsmp.o + +# Shared SoC family objects +obj-$(CONFIG_ARCH_RCAR_GEN2) += setup-rcar-gen2.o platsmp-apmu.o $(cpu-y) +CFLAGS_setup-rcar-gen2.o += -march=armv7-a +obj-$(CONFIG_ARCH_R8A7790) += regulator-quirk-rcar-gen2.o +obj-$(CONFIG_ARCH_R8A7791) += regulator-quirk-rcar-gen2.o +obj-$(CONFIG_ARCH_R8A7793) += regulator-quirk-rcar-gen2.o + +# SMP objects +smp-y := $(cpu-y) +smp-$(CONFIG_ARCH_RCAR_GEN2) += headsmp-apmu.o +smp-$(CONFIG_ARCH_SH73A0) += smp-sh73a0.o headsmp-scu.o platsmp-scu.o +smp-$(CONFIG_ARCH_R8A7779) += smp-r8a7779.o headsmp-scu.o platsmp-scu.o +smp-$(CONFIG_ARCH_EMEV2) += smp-emev2.o headsmp-scu.o platsmp-scu.o + +# PM objects +obj-$(CONFIG_SUSPEND) += suspend.o +obj-$(CONFIG_ARCH_RCAR_GEN2) += pm-rcar-gen2.o + +# Framework support +obj-$(CONFIG_SMP) += $(smp-y) diff --git a/arch/arm/mach-shmobile/common.h b/arch/arm/mach-shmobile/common.h new file mode 100644 index 000000000..3ac4b36b5 --- /dev/null +++ b/arch/arm/mach-shmobile/common.h @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __ARCH_MACH_COMMON_H +#define __ARCH_MACH_COMMON_H + +extern void shmobile_init_delay(void); +extern void shmobile_boot_vector(void); +extern unsigned long shmobile_boot_fn; +extern unsigned long shmobile_boot_size; +extern void shmobile_boot_vector_gen2(void); +extern unsigned long shmobile_boot_fn_gen2; +extern unsigned long shmobile_boot_cpu_gen2; +extern unsigned long shmobile_boot_size_gen2; +extern void shmobile_smp_boot(void); +extern void shmobile_smp_sleep(void); +extern void shmobile_smp_hook(unsigned int cpu, unsigned long fn, + unsigned long arg); +extern bool shmobile_smp_cpu_can_disable(unsigned int cpu); +extern void shmobile_boot_apmu(void); +extern void shmobile_boot_scu(void); +extern void shmobile_smp_scu_prepare_cpus(phys_addr_t scu_base_phys, + unsigned int max_cpus); +extern void shmobile_smp_scu_cpu_die(unsigned int cpu); +extern int shmobile_smp_scu_cpu_kill(unsigned int cpu); +extern struct platform_suspend_ops shmobile_suspend_ops; + +#ifdef CONFIG_SUSPEND +int shmobile_suspend_init(void); +void shmobile_smp_apmu_suspend_init(void); +#else +static inline int shmobile_suspend_init(void) { return 0; } +static inline void shmobile_smp_apmu_suspend_init(void) { } +#endif + +static inline void __init shmobile_init_late(void) +{ + shmobile_suspend_init(); +} + +#endif /* __ARCH_MACH_COMMON_H */ diff --git a/arch/arm/mach-shmobile/emev2.h b/arch/arm/mach-shmobile/emev2.h new file mode 100644 index 000000000..39f6cd8e6 --- /dev/null +++ b/arch/arm/mach-shmobile/emev2.h @@ -0,0 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __ASM_EMEV2_H__ +#define __ASM_EMEV2_H__ + +extern const struct smp_operations emev2_smp_ops; + +#endif /* __ASM_EMEV2_H__ */ diff --git a/arch/arm/mach-shmobile/headsmp-apmu.S b/arch/arm/mach-shmobile/headsmp-apmu.S new file mode 100644 index 000000000..fabe9cadd --- /dev/null +++ b/arch/arm/mach-shmobile/headsmp-apmu.S @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * SMP support for APMU based systems with Cortex A7/A15 + * + * Copyright (C) 2014 Renesas Electronics Corporation + */ + +#include <linux/linkage.h> +#include <asm/assembler.h> + +ENTRY(shmobile_boot_apmu) + bl secure_cntvoff_init + b secondary_startup +ENDPROC(shmobile_boot_apmu) diff --git a/arch/arm/mach-shmobile/headsmp-scu.S b/arch/arm/mach-shmobile/headsmp-scu.S new file mode 100644 index 000000000..d0234296a --- /dev/null +++ b/arch/arm/mach-shmobile/headsmp-scu.S @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: GPL-2.0+ + * + * Shared SCU setup for mach-shmobile + * + * Copyright (C) 2012 Bastian Hecht + */ + +#include <linux/linkage.h> +#include <linux/init.h> +#include <asm/memory.h> + +/* + * Boot code for secondary CPUs. + * + * First we turn on L1 cache coherency for our CPU. Then we jump to + * secondary_startup that invalidates the cache and hands over control + * to the common ARM startup code. + */ +ENTRY(shmobile_boot_scu) + @ r0 = SCU base address + mrc p15, 0, r1, c0, c0, 5 @ read MPIDR + and r1, r1, #3 @ mask out cpu ID + lsl r1, r1, #3 @ we will shift by cpu_id * 8 bits + ldr r2, [r0, #8] @ SCU Power Status Register + mov r3, #3 + lsl r3, r3, r1 + bic r2, r2, r3 @ Clear bits of our CPU (Run Mode) + str r2, [r0, #8] @ write back + + b secondary_startup +ENDPROC(shmobile_boot_scu) diff --git a/arch/arm/mach-shmobile/headsmp.S b/arch/arm/mach-shmobile/headsmp.S new file mode 100644 index 000000000..9466ae61f --- /dev/null +++ b/arch/arm/mach-shmobile/headsmp.S @@ -0,0 +1,147 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * SMP support for R-Mobile / SH-Mobile + * + * Copyright (C) 2010 Magnus Damm + * Copyright (C) 2010 Takashi Yoshii + * + * Based on vexpress, Copyright (c) 2003 ARM Limited, All Rights Reserved + */ +#include <linux/init.h> +#include <linux/linkage.h> +#include <linux/threads.h> +#include <asm/assembler.h> +#include <asm/memory.h> + +#define SCTLR_MMU 0x01 +#define BOOTROM_ADDRESS 0xE6340000 +#define RWTCSRA_ADDRESS 0xE6020004 +#define RWTCSRA_WOVF 0x10 + +/* + * Reset vector for secondary CPUs. + * This will be mapped at address 0 by SBAR register. + * We need _long_ jump to the physical address. + */ + .arm + .align 12 +ENTRY(shmobile_boot_vector) + ldr r1, 1f + bx r1 + +ENDPROC(shmobile_boot_vector) + + .align 2 + .globl shmobile_boot_fn +shmobile_boot_fn: +1: .space 4 + .globl shmobile_boot_size +shmobile_boot_size: + .long . - shmobile_boot_vector + +#ifdef CONFIG_ARCH_RCAR_GEN2 +/* + * Reset vector for R-Car Gen2 and RZ/G1 secondary CPUs. + * This will be mapped at address 0 by SBAR register. + */ +ENTRY(shmobile_boot_vector_gen2) + mrc p15, 0, r0, c0, c0, 5 @ r0 = MPIDR + ldr r1, shmobile_boot_cpu_gen2 + cmp r0, r1 + bne shmobile_smp_continue_gen2 + + mrc p15, 0, r1, c1, c0, 0 @ r1 = SCTLR + and r0, r1, #SCTLR_MMU + cmp r0, #SCTLR_MMU + beq shmobile_smp_continue_gen2 + + ldr r0, rwtcsra + mov r1, #0 + ldrb r1, [r0] + and r0, r1, #RWTCSRA_WOVF + cmp r0, #RWTCSRA_WOVF + bne shmobile_smp_continue_gen2 + + ldr r0, bootrom + bx r0 + +shmobile_smp_continue_gen2: + ldr r1, shmobile_boot_fn_gen2 + bx r1 + +ENDPROC(shmobile_boot_vector_gen2) + + .align 4 +rwtcsra: + .word RWTCSRA_ADDRESS +bootrom: + .word BOOTROM_ADDRESS + .globl shmobile_boot_cpu_gen2 +shmobile_boot_cpu_gen2: + .word 0x00000000 + + .align 2 + .globl shmobile_boot_fn_gen2 +shmobile_boot_fn_gen2: + .space 4 + .globl shmobile_boot_size_gen2 +shmobile_boot_size_gen2: + .long . - shmobile_boot_vector_gen2 +#endif /* CONFIG_ARCH_RCAR_GEN2 */ + +/* + * Per-CPU SMP boot function/argument selection code based on MPIDR + */ + +ENTRY(shmobile_smp_boot) + mrc p15, 0, r1, c0, c0, 5 @ r1 = MPIDR + and r0, r1, #0xffffff @ MPIDR_HWID_BITMASK + @ r0 = cpu_logical_map() value + mov r1, #0 @ r1 = CPU index + adr r2, 1f + ldmia r2, {r5, r6, r7} + add r5, r5, r2 @ array of per-cpu mpidr values + add r6, r6, r2 @ array of per-cpu functions + add r7, r7, r2 @ array of per-cpu arguments + +shmobile_smp_boot_find_mpidr: + ldr r8, [r5, r1, lsl #2] + cmp r8, r0 + bne shmobile_smp_boot_next + + ldr r9, [r6, r1, lsl #2] + cmp r9, #0 + bne shmobile_smp_boot_found + +shmobile_smp_boot_next: + add r1, r1, #1 + cmp r1, #NR_CPUS + blo shmobile_smp_boot_find_mpidr + + b shmobile_smp_sleep + +shmobile_smp_boot_found: + ldr r0, [r7, r1, lsl #2] + ret r9 +ENDPROC(shmobile_smp_boot) + +ENTRY(shmobile_smp_sleep) + wfi + b shmobile_smp_boot +ENDPROC(shmobile_smp_sleep) + + .align 2 +1: .long shmobile_smp_mpidr - . + .long shmobile_smp_fn - 1b + .long shmobile_smp_arg - 1b + + .bss + .globl shmobile_smp_mpidr +shmobile_smp_mpidr: + .space NR_CPUS * 4 + .globl shmobile_smp_fn +shmobile_smp_fn: + .space NR_CPUS * 4 + .globl shmobile_smp_arg +shmobile_smp_arg: + .space NR_CPUS * 4 diff --git a/arch/arm/mach-shmobile/platsmp-apmu.c b/arch/arm/mach-shmobile/platsmp-apmu.c new file mode 100644 index 000000000..e771ce70e --- /dev/null +++ b/arch/arm/mach-shmobile/platsmp-apmu.c @@ -0,0 +1,286 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * SMP support for SoCs with APMU + * + * Copyright (C) 2014 Renesas Electronics Corporation + * Copyright (C) 2013 Magnus Damm + */ +#include <linux/cpu_pm.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/ioport.h> +#include <linux/of_address.h> +#include <linux/smp.h> +#include <linux/suspend.h> +#include <linux/threads.h> +#include <asm/cacheflush.h> +#include <asm/cp15.h> +#include <asm/proc-fns.h> +#include <asm/smp_plat.h> +#include <asm/suspend.h> +#include "common.h" +#include "rcar-gen2.h" + +static struct { + void __iomem *iomem; + int bit; +} apmu_cpus[NR_CPUS]; + +#define WUPCR_OFFS 0x10 /* Wake Up Control Register */ +#define PSTR_OFFS 0x40 /* Power Status Register */ +#define CPUNCR_OFFS(n) (0x100 + (0x10 * (n))) + /* CPUn Power Status Control Register */ +#define DBGRCR_OFFS 0x180 /* Debug Resource Reset Control Reg. */ + +/* Power Status Register */ +#define CPUNST(r, n) (((r) >> (n * 4)) & 3) /* CPUn Status Bit */ +#define CPUST_RUN 0 /* Run Mode */ +#define CPUST_STANDBY 3 /* CoreStandby Mode */ + +/* Debug Resource Reset Control Register */ +#define DBGCPUREN BIT(24) /* CPU Other Reset Request Enable */ +#define DBGCPUNREN(n) BIT((n) + 20) /* CPUn Reset Request Enable */ +#define DBGCPUPREN BIT(19) /* CPU Peripheral Reset Req. Enable */ + +static int __maybe_unused apmu_power_on(void __iomem *p, int bit) +{ + /* request power on */ + writel_relaxed(BIT(bit), p + WUPCR_OFFS); + + /* wait for APMU to finish */ + while (readl_relaxed(p + WUPCR_OFFS) != 0) + ; + + return 0; +} + +static int __maybe_unused apmu_power_off(void __iomem *p, int bit) +{ + /* request Core Standby for next WFI */ + writel_relaxed(3, p + CPUNCR_OFFS(bit)); + return 0; +} + +static int __maybe_unused apmu_power_off_poll(void __iomem *p, int bit) +{ + int k; + + for (k = 0; k < 1000; k++) { + if (CPUNST(readl_relaxed(p + PSTR_OFFS), bit) == CPUST_STANDBY) + return 1; + + mdelay(1); + } + + return 0; +} + +static int __maybe_unused apmu_wrap(int cpu, int (*fn)(void __iomem *p, int cpu)) +{ + void __iomem *p = apmu_cpus[cpu].iomem; + + return p ? fn(p, apmu_cpus[cpu].bit) : -EINVAL; +} + +#if defined(CONFIG_HOTPLUG_CPU) || defined(CONFIG_SUSPEND) +/* nicked from arch/arm/mach-exynos/hotplug.c */ +static inline void cpu_enter_lowpower_a15(void) +{ + unsigned int v; + + asm volatile( + " mrc p15, 0, %0, c1, c0, 0\n" + " bic %0, %0, %1\n" + " mcr p15, 0, %0, c1, c0, 0\n" + : "=&r" (v) + : "Ir" (CR_C) + : "cc"); + + flush_cache_louis(); + + asm volatile( + /* + * Turn off coherency + */ + " mrc p15, 0, %0, c1, c0, 1\n" + " bic %0, %0, %1\n" + " mcr p15, 0, %0, c1, c0, 1\n" + : "=&r" (v) + : "Ir" (0x40) + : "cc"); + + isb(); + dsb(); +} + +static void shmobile_smp_apmu_cpu_shutdown(unsigned int cpu) +{ + + /* Select next sleep mode using the APMU */ + apmu_wrap(cpu, apmu_power_off); + + /* Do ARM specific CPU shutdown */ + cpu_enter_lowpower_a15(); +} +#endif + +#if defined(CONFIG_HOTPLUG_CPU) +static void shmobile_smp_apmu_cpu_die(unsigned int cpu) +{ + /* For this particular CPU deregister boot vector */ + shmobile_smp_hook(cpu, 0, 0); + + /* Shutdown CPU core */ + shmobile_smp_apmu_cpu_shutdown(cpu); + + /* jump to shared mach-shmobile sleep / reset code */ + shmobile_smp_sleep(); +} + +static int shmobile_smp_apmu_cpu_kill(unsigned int cpu) +{ + return apmu_wrap(cpu, apmu_power_off_poll); +} +#endif + +#if defined(CONFIG_SUSPEND) +static int shmobile_smp_apmu_do_suspend(unsigned long cpu) +{ + shmobile_smp_hook(cpu, __pa_symbol(cpu_resume), 0); + shmobile_smp_apmu_cpu_shutdown(cpu); + cpu_do_idle(); /* WFI selects Core Standby */ + return 1; +} + +static inline void cpu_leave_lowpower(void) +{ + unsigned int v; + + asm volatile("mrc p15, 0, %0, c1, c0, 0\n" + " orr %0, %0, %1\n" + " mcr p15, 0, %0, c1, c0, 0\n" + " mrc p15, 0, %0, c1, c0, 1\n" + " orr %0, %0, %2\n" + " mcr p15, 0, %0, c1, c0, 1\n" + : "=&r" (v) + : "Ir" (CR_C), "Ir" (0x40) + : "cc"); +} + +static int shmobile_smp_apmu_enter_suspend(suspend_state_t state) +{ + cpu_suspend(smp_processor_id(), shmobile_smp_apmu_do_suspend); + cpu_leave_lowpower(); + return 0; +} + +void __init shmobile_smp_apmu_suspend_init(void) +{ + shmobile_suspend_ops.enter = shmobile_smp_apmu_enter_suspend; +} +#endif + +#ifdef CONFIG_SMP +static void apmu_init_cpu(struct resource *res, int cpu, int bit) +{ + u32 x; + + if ((cpu >= ARRAY_SIZE(apmu_cpus)) || apmu_cpus[cpu].iomem) + return; + + apmu_cpus[cpu].iomem = ioremap(res->start, resource_size(res)); + apmu_cpus[cpu].bit = bit; + + pr_debug("apmu ioremap %d %d %pr\n", cpu, bit, res); + + /* Setup for debug mode */ + x = readl(apmu_cpus[cpu].iomem + DBGRCR_OFFS); + x |= DBGCPUREN | DBGCPUNREN(bit) | DBGCPUPREN; + writel(x, apmu_cpus[cpu].iomem + DBGRCR_OFFS); +} + +static const struct of_device_id apmu_ids[] = { + { .compatible = "renesas,apmu" }, + { /*sentinel*/ } +}; + +static void apmu_parse_dt(void (*fn)(struct resource *res, int cpu, int bit)) +{ + struct device_node *np_apmu, *np_cpu; + struct resource res; + int bit, index; + u32 id; + + for_each_matching_node(np_apmu, apmu_ids) { + /* only enable the cluster that includes the boot CPU */ + bool is_allowed = false; + + for (bit = 0; bit < CONFIG_NR_CPUS; bit++) { + np_cpu = of_parse_phandle(np_apmu, "cpus", bit); + if (np_cpu) { + if (!of_property_read_u32(np_cpu, "reg", &id)) { + if (id == cpu_logical_map(0)) { + is_allowed = true; + of_node_put(np_cpu); + break; + } + + } + of_node_put(np_cpu); + } + } + if (!is_allowed) + continue; + + for (bit = 0; bit < CONFIG_NR_CPUS; bit++) { + np_cpu = of_parse_phandle(np_apmu, "cpus", bit); + if (np_cpu) { + if (!of_property_read_u32(np_cpu, "reg", &id)) { + index = get_logical_index(id); + if ((index >= 0) && + !of_address_to_resource(np_apmu, + 0, &res)) + fn(&res, index, bit); + } + of_node_put(np_cpu); + } + } + } +} + +static void __init shmobile_smp_apmu_setup_boot(void) +{ + /* install boot code shared by all CPUs */ + shmobile_boot_fn = __pa_symbol(shmobile_smp_boot); + shmobile_boot_fn_gen2 = shmobile_boot_fn; +} + +static int shmobile_smp_apmu_boot_secondary(unsigned int cpu, + struct task_struct *idle) +{ + /* For this particular CPU register boot vector */ + shmobile_smp_hook(cpu, __pa_symbol(shmobile_boot_apmu), 0); + + return apmu_wrap(cpu, apmu_power_on); +} + +static void __init shmobile_smp_apmu_prepare_cpus_dt(unsigned int max_cpus) +{ + shmobile_smp_apmu_setup_boot(); + apmu_parse_dt(apmu_init_cpu); + rcar_gen2_pm_init(); +} + +static struct smp_operations apmu_smp_ops __initdata = { + .smp_prepare_cpus = shmobile_smp_apmu_prepare_cpus_dt, + .smp_boot_secondary = shmobile_smp_apmu_boot_secondary, +#ifdef CONFIG_HOTPLUG_CPU + .cpu_can_disable = shmobile_smp_cpu_can_disable, + .cpu_die = shmobile_smp_apmu_cpu_die, + .cpu_kill = shmobile_smp_apmu_cpu_kill, +#endif +}; + +CPU_METHOD_OF_DECLARE(shmobile_smp_apmu, "renesas,apmu", &apmu_smp_ops); +#endif /* CONFIG_SMP */ diff --git a/arch/arm/mach-shmobile/platsmp-scu.c b/arch/arm/mach-shmobile/platsmp-scu.c new file mode 100644 index 000000000..3849f71e6 --- /dev/null +++ b/arch/arm/mach-shmobile/platsmp-scu.c @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * SMP support for SoCs with SCU covered by mach-shmobile + * + * Copyright (C) 2013 Magnus Damm + */ +#include <linux/cpu.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/smp.h> +#include <asm/cacheflush.h> +#include <asm/smp_plat.h> +#include <asm/smp_scu.h> +#include "common.h" + + +static phys_addr_t shmobile_scu_base_phys; +static void __iomem *shmobile_scu_base; + +static int shmobile_scu_cpu_prepare(unsigned int cpu) +{ + /* For this particular CPU register SCU SMP boot vector */ + shmobile_smp_hook(cpu, __pa_symbol(shmobile_boot_scu), + shmobile_scu_base_phys); + return 0; +} + +void __init shmobile_smp_scu_prepare_cpus(phys_addr_t scu_base_phys, + unsigned int max_cpus) +{ + /* install boot code shared by all CPUs */ + shmobile_boot_fn = __pa_symbol(shmobile_smp_boot); + + /* enable SCU and cache coherency on booting CPU */ + shmobile_scu_base_phys = scu_base_phys; + shmobile_scu_base = ioremap(scu_base_phys, PAGE_SIZE); + scu_enable(shmobile_scu_base); + scu_power_mode(shmobile_scu_base, SCU_PM_NORMAL); + + /* Use CPU notifier for reset vector control */ + cpuhp_setup_state_nocalls(CPUHP_ARM_SHMOBILE_SCU_PREPARE, + "arm/shmobile-scu:prepare", + shmobile_scu_cpu_prepare, NULL); +} + +#ifdef CONFIG_HOTPLUG_CPU +void shmobile_smp_scu_cpu_die(unsigned int cpu) +{ + /* For this particular CPU deregister boot vector */ + shmobile_smp_hook(cpu, 0, 0); + + dsb(); + flush_cache_all(); + + /* disable cache coherency */ + scu_power_mode(shmobile_scu_base, SCU_PM_POWEROFF); + + /* jump to shared mach-shmobile sleep / reset code */ + shmobile_smp_sleep(); +} + +static int shmobile_smp_scu_psr_core_disabled(int cpu) +{ + unsigned long mask = SCU_PM_POWEROFF << (cpu * 8); + + if ((readl(shmobile_scu_base + 8) & mask) == mask) + return 1; + + return 0; +} + +int shmobile_smp_scu_cpu_kill(unsigned int cpu) +{ + int k; + + /* this function is running on another CPU than the offline target, + * here we need wait for shutdown code in platform_cpu_die() to + * finish before asking SoC-specific code to power off the CPU core. + */ + for (k = 0; k < 1000; k++) { + if (shmobile_smp_scu_psr_core_disabled(cpu)) + return 1; + + mdelay(1); + } + + return 0; +} +#endif diff --git a/arch/arm/mach-shmobile/platsmp.c b/arch/arm/mach-shmobile/platsmp.c new file mode 100644 index 000000000..7437c0151 --- /dev/null +++ b/arch/arm/mach-shmobile/platsmp.c @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * SMP support for R-Mobile / SH-Mobile + * + * Copyright (C) 2010 Magnus Damm + * Copyright (C) 2011 Paul Mundt + * + * Based on vexpress, Copyright (C) 2002 ARM Ltd, All Rights Reserved + */ +#include <linux/init.h> +#include <asm/cacheflush.h> +#include <asm/smp_plat.h> +#include "common.h" + +extern unsigned long shmobile_smp_fn[]; +extern unsigned long shmobile_smp_arg[]; +extern unsigned long shmobile_smp_mpidr[]; + +void shmobile_smp_hook(unsigned int cpu, unsigned long fn, unsigned long arg) +{ + shmobile_smp_fn[cpu] = 0; + flush_cache_all(); + + shmobile_smp_mpidr[cpu] = cpu_logical_map(cpu); + shmobile_smp_fn[cpu] = fn; + shmobile_smp_arg[cpu] = arg; + flush_cache_all(); +} + +#ifdef CONFIG_HOTPLUG_CPU +bool shmobile_smp_cpu_can_disable(unsigned int cpu) +{ + return true; /* Hotplug of any CPU is supported */ +} +#endif diff --git a/arch/arm/mach-shmobile/pm-rcar-gen2.c b/arch/arm/mach-shmobile/pm-rcar-gen2.c new file mode 100644 index 000000000..672081405 --- /dev/null +++ b/arch/arm/mach-shmobile/pm-rcar-gen2.c @@ -0,0 +1,129 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * R-Car Generation 2 Power management support + * + * Copyright (C) 2013 - 2015 Renesas Electronics Corporation + * Copyright (C) 2011 Renesas Solutions Corp. + * Copyright (C) 2011 Magnus Damm + */ + +#include <linux/kernel.h> +#include <linux/ioport.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/smp.h> +#include <asm/io.h> +#include <asm/cputype.h> +#include "common.h" +#include "rcar-gen2.h" + +/* RST */ +#define RST 0xe6160000 + +#define CA15BAR 0x0020 /* CA15 Boot Address Register */ +#define CA7BAR 0x0030 /* CA7 Boot Address Register */ +#define CA15RESCNT 0x0040 /* CA15 Reset Control Register */ +#define CA7RESCNT 0x0044 /* CA7 Reset Control Register */ + +/* SYS Boot Address Register */ +#define SBAR_BAREN BIT(4) /* SBAR is valid */ + +/* Reset Control Registers */ +#define CA15RESCNT_CODE 0xa5a50000 +#define CA15RESCNT_CPUS 0xf /* CPU0-3 */ +#define CA7RESCNT_CODE 0x5a5a0000 +#define CA7RESCNT_CPUS 0xf /* CPU0-3 */ + +/* On-chip RAM */ +#define ICRAM1 0xe63c0000 /* Inter Connect RAM1 (4 KiB) */ + +static inline u32 phys_to_sbar(phys_addr_t addr) +{ + return (addr >> 8) & 0xfffffc00; +} + +void __init rcar_gen2_pm_init(void) +{ + void __iomem *p; + u32 bar; + static int once; + struct device_node *np; + bool has_a7 = false; + bool has_a15 = false; + struct resource res; + int error; + + if (once++) + return; + + for_each_of_cpu_node(np) { + if (of_device_is_compatible(np, "arm,cortex-a15")) + has_a15 = true; + else if (of_device_is_compatible(np, "arm,cortex-a7")) + has_a7 = true; + } + + np = of_find_compatible_node(NULL, NULL, "renesas,smp-sram"); + if (!np) { + /* No smp-sram in DT, fall back to hardcoded address */ + res = (struct resource)DEFINE_RES_MEM(ICRAM1, + shmobile_boot_size); + goto map; + } + + error = of_address_to_resource(np, 0, &res); + of_node_put(np); + if (error) { + pr_err("Failed to get smp-sram address: %d\n", error); + return; + } + +map: + /* RAM for jump stub, because BAR requires 256KB aligned address */ + if (res.start & (256 * 1024 - 1) || + resource_size(&res) < shmobile_boot_size) { + pr_err("Invalid smp-sram region\n"); + return; + } + + p = ioremap(res.start, resource_size(&res)); + if (!p) + return; + /* + * install the reset vector, use the largest version if we have enough + * memory available + */ + if (resource_size(&res) >= shmobile_boot_size_gen2) { + shmobile_boot_cpu_gen2 = read_cpuid_mpidr(); + memcpy_toio(p, shmobile_boot_vector_gen2, + shmobile_boot_size_gen2); + } else { + memcpy_toio(p, shmobile_boot_vector, shmobile_boot_size); + } + iounmap(p); + + /* setup reset vectors */ + p = ioremap(RST, 0x63); + bar = phys_to_sbar(res.start); + if (has_a15) { + writel_relaxed(bar, p + CA15BAR); + writel_relaxed(bar | SBAR_BAREN, p + CA15BAR); + + /* de-assert reset for CA15 CPUs */ + writel_relaxed((readl_relaxed(p + CA15RESCNT) & + ~CA15RESCNT_CPUS) | CA15RESCNT_CODE, + p + CA15RESCNT); + } + if (has_a7) { + writel_relaxed(bar, p + CA7BAR); + writel_relaxed(bar | SBAR_BAREN, p + CA7BAR); + + /* de-assert reset for CA7 CPUs */ + writel_relaxed((readl_relaxed(p + CA7RESCNT) & + ~CA7RESCNT_CPUS) | CA7RESCNT_CODE, + p + CA7RESCNT); + } + iounmap(p); + + shmobile_smp_apmu_suspend_init(); +} diff --git a/arch/arm/mach-shmobile/r8a7779.h b/arch/arm/mach-shmobile/r8a7779.h new file mode 100644 index 000000000..ca9db8fde --- /dev/null +++ b/arch/arm/mach-shmobile/r8a7779.h @@ -0,0 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __ASM_R8A7779_H__ +#define __ASM_R8A7779_H__ + +extern const struct smp_operations r8a7779_smp_ops; + +#endif /* __ASM_R8A7779_H__ */ diff --git a/arch/arm/mach-shmobile/rcar-gen2.h b/arch/arm/mach-shmobile/rcar-gen2.h new file mode 100644 index 000000000..af9dbd6aa --- /dev/null +++ b/arch/arm/mach-shmobile/rcar-gen2.h @@ -0,0 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __ASM_RCAR_GEN2_H__ +#define __ASM_RCAR_GEN2_H__ + +void rcar_gen2_pm_init(void); + +#endif /* __ASM_RCAR_GEN2_H__ */ diff --git a/arch/arm/mach-shmobile/regulator-quirk-rcar-gen2.c b/arch/arm/mach-shmobile/regulator-quirk-rcar-gen2.c new file mode 100644 index 000000000..117e7b079 --- /dev/null +++ b/arch/arm/mach-shmobile/regulator-quirk-rcar-gen2.c @@ -0,0 +1,237 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * R-Car Generation 2 da9063(L)/da9210 regulator quirk + * + * Certain Gen2 development boards have an da9063 and one or more da9210 + * regulators. All of these regulators have their interrupt request lines + * tied to the same interrupt pin (IRQ2) on the SoC. + * + * After cold boot or da9063-induced restart, both the da9063 and da9210 seem + * to assert their interrupt request lines. Hence as soon as one driver + * requests this irq, it gets stuck in an interrupt storm, as it only manages + * to deassert its own interrupt request line, and the other driver hasn't + * installed an interrupt handler yet. + * + * To handle this, install a quirk that masks the interrupts in both the + * da9063 and da9210. This quirk has to run after the i2c master driver has + * been initialized, but before the i2c slave drivers are initialized. + * + * Copyright (C) 2015 Glider bvba + */ + +#include <linux/device.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/list.h> +#include <linux/notifier.h> +#include <linux/of.h> +#include <linux/of_irq.h> +#include <linux/mfd/da9063/registers.h> + +#define IRQC_BASE 0xe61c0000 +#define IRQC_MONITOR 0x104 /* IRQn Signal Level Monitor Register */ + +#define REGULATOR_IRQ_MASK BIT(2) /* IRQ2, active low */ + +/* start of DA9210 System Control and Event Registers */ +#define DA9210_REG_MASK_A 0x54 + +struct regulator_quirk { + struct list_head list; + const struct of_device_id *id; + struct device_node *np; + struct of_phandle_args irq_args; + struct i2c_msg i2c_msg; + bool shared; /* IRQ line is shared */ +}; + +static LIST_HEAD(quirk_list); +static void __iomem *irqc; + +/* first byte sets the memory pointer, following are consecutive reg values */ +static u8 da9063_irq_clr[] = { DA9063_REG_IRQ_MASK_A, 0xff, 0xff, 0xff, 0xff }; +static u8 da9210_irq_clr[] = { DA9210_REG_MASK_A, 0xff, 0xff }; + +static struct i2c_msg da9063_msg = { + .len = ARRAY_SIZE(da9063_irq_clr), + .buf = da9063_irq_clr, +}; + +static struct i2c_msg da9210_msg = { + .len = ARRAY_SIZE(da9210_irq_clr), + .buf = da9210_irq_clr, +}; + +static const struct of_device_id rcar_gen2_quirk_match[] = { + { .compatible = "dlg,da9063", .data = &da9063_msg }, + { .compatible = "dlg,da9063l", .data = &da9063_msg }, + { .compatible = "dlg,da9210", .data = &da9210_msg }, + { /* sentinel */ } +}; + +static int regulator_quirk_notify(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct regulator_quirk *pos, *tmp; + struct device *dev = data; + struct i2c_client *client; + static bool done; + int ret; + u32 mon; + + if (done) + return 0; + + mon = ioread32(irqc + IRQC_MONITOR); + dev_dbg(dev, "%s: %ld, IRQC_MONITOR = 0x%x\n", __func__, action, mon); + if (mon & REGULATOR_IRQ_MASK) + goto remove; + + if (action != BUS_NOTIFY_ADD_DEVICE || dev->type == &i2c_adapter_type) + return 0; + + client = to_i2c_client(dev); + dev_dbg(dev, "Detected %s\n", client->name); + + /* + * Send message to all PMICs that share an IRQ line to deassert it. + * + * WARNING: This works only if all the PMICs are on the same I2C bus. + */ + list_for_each_entry(pos, &quirk_list, list) { + if (!pos->shared) + continue; + + if (pos->np->parent != client->dev.parent->of_node) + continue; + + dev_info(&client->dev, "clearing %s@0x%02x interrupts\n", + pos->id->compatible, pos->i2c_msg.addr); + + ret = i2c_transfer(client->adapter, &pos->i2c_msg, 1); + if (ret != 1) + dev_err(&client->dev, "i2c error %d\n", ret); + } + + mon = ioread32(irqc + IRQC_MONITOR); + if (mon & REGULATOR_IRQ_MASK) + goto remove; + + return 0; + +remove: + dev_info(dev, "IRQ2 is not asserted, removing quirk\n"); + + list_for_each_entry_safe(pos, tmp, &quirk_list, list) { + list_del(&pos->list); + of_node_put(pos->np); + kfree(pos); + } + + done = true; + iounmap(irqc); + return 0; +} + +static struct notifier_block regulator_quirk_nb = { + .notifier_call = regulator_quirk_notify +}; + +static int __init rcar_gen2_regulator_quirk(void) +{ + struct regulator_quirk *quirk, *pos, *tmp; + struct of_phandle_args *argsa, *argsb; + const struct of_device_id *id; + struct device_node *np; + u32 mon, addr; + int ret; + + if (!of_machine_is_compatible("renesas,koelsch") && + !of_machine_is_compatible("renesas,lager") && + !of_machine_is_compatible("renesas,porter") && + !of_machine_is_compatible("renesas,stout") && + !of_machine_is_compatible("renesas,gose")) + return -ENODEV; + + for_each_matching_node_and_match(np, rcar_gen2_quirk_match, &id) { + if (!of_device_is_available(np)) { + of_node_put(np); + break; + } + + ret = of_property_read_u32(np, "reg", &addr); + if (ret) /* Skip invalid entry and continue */ + continue; + + quirk = kzalloc(sizeof(*quirk), GFP_KERNEL); + if (!quirk) { + ret = -ENOMEM; + of_node_put(np); + goto err_mem; + } + + argsa = &quirk->irq_args; + memcpy(&quirk->i2c_msg, id->data, sizeof(quirk->i2c_msg)); + + quirk->id = id; + quirk->np = of_node_get(np); + quirk->i2c_msg.addr = addr; + + ret = of_irq_parse_one(np, 0, argsa); + if (ret) { /* Skip invalid entry and continue */ + of_node_put(np); + kfree(quirk); + continue; + } + + list_for_each_entry(pos, &quirk_list, list) { + argsb = &pos->irq_args; + + if (argsa->args_count != argsb->args_count) + continue; + + ret = memcmp(argsa->args, argsb->args, + argsa->args_count * + sizeof(argsa->args[0])); + if (!ret) { + pos->shared = true; + quirk->shared = true; + } + } + + list_add_tail(&quirk->list, &quirk_list); + } + + irqc = ioremap(IRQC_BASE, PAGE_SIZE); + if (!irqc) { + ret = -ENOMEM; + goto err_mem; + } + + mon = ioread32(irqc + IRQC_MONITOR); + if (mon & REGULATOR_IRQ_MASK) { + pr_debug("%s: IRQ2 is not asserted, not installing quirk\n", + __func__); + ret = 0; + goto err_free; + } + + pr_info("IRQ2 is asserted, installing regulator quirk\n"); + + bus_register_notifier(&i2c_bus_type, ®ulator_quirk_nb); + return 0; + +err_free: + iounmap(irqc); +err_mem: + list_for_each_entry_safe(pos, tmp, &quirk_list, list) { + list_del(&pos->list); + of_node_put(pos->np); + kfree(pos); + } + + return ret; +} + +arch_initcall(rcar_gen2_regulator_quirk); diff --git a/arch/arm/mach-shmobile/setup-emev2.c b/arch/arm/mach-shmobile/setup-emev2.c new file mode 100644 index 000000000..ed82d6429 --- /dev/null +++ b/arch/arm/mach-shmobile/setup-emev2.c @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Emma Mobile EV2 processor support + * + * Copyright (C) 2012 Magnus Damm + */ +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/mm.h> +#include <asm/mach-types.h> +#include <asm/mach/arch.h> +#include <asm/mach/map.h> + +#include "common.h" +#include "emev2.h" + +static const char *const emev2_boards_compat_dt[] __initconst = { + "renesas,emev2", + NULL +}; + +DT_MACHINE_START(EMEV2_DT, "Generic Emma Mobile EV2 (Flattened Device Tree)") + .smp = smp_ops(emev2_smp_ops), + .init_early = shmobile_init_delay, + .init_late = shmobile_init_late, + .dt_compat = emev2_boards_compat_dt, +MACHINE_END diff --git a/arch/arm/mach-shmobile/setup-r7s72100.c b/arch/arm/mach-shmobile/setup-r7s72100.c new file mode 100644 index 000000000..a70b99495 --- /dev/null +++ b/arch/arm/mach-shmobile/setup-r7s72100.c @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * r7s72100 processor support + * + * Copyright (C) 2013 Renesas Solutions Corp. + * Copyright (C) 2013 Magnus Damm + */ + +#include <linux/kernel.h> + +#include <asm/mach/arch.h> + +#include "common.h" + +static const char *const r7s72100_boards_compat_dt[] __initconst = { + "renesas,r7s72100", + NULL +}; + +DT_MACHINE_START(R7S72100_DT, "Generic R7S72100 (Flattened Device Tree)") + .l2c_aux_val = 0, + .l2c_aux_mask = ~0, + .init_early = shmobile_init_delay, + .init_late = shmobile_init_late, + .dt_compat = r7s72100_boards_compat_dt, +MACHINE_END diff --git a/arch/arm/mach-shmobile/setup-r7s9210.c b/arch/arm/mach-shmobile/setup-r7s9210.c new file mode 100644 index 000000000..90add728b --- /dev/null +++ b/arch/arm/mach-shmobile/setup-r7s9210.c @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * r7s9210 processor support + * + * Copyright (C) 2018 Renesas Electronics Corporation + * Copyright (C) 2018 Chris Brandt + * + */ + +#include <linux/kernel.h> + +#include <asm/mach/arch.h> + +#include "common.h" + +static const char *const r7s9210_boards_compat_dt[] __initconst = { + "renesas,r7s9210", + NULL +}; + +DT_MACHINE_START(R7S72100_DT, "Generic R7S9210 (Flattened Device Tree)") + .l2c_aux_val = 0, + .l2c_aux_mask = ~0, + .init_early = shmobile_init_delay, + .init_late = shmobile_init_late, + .dt_compat = r7s9210_boards_compat_dt, +MACHINE_END diff --git a/arch/arm/mach-shmobile/setup-r8a73a4.c b/arch/arm/mach-shmobile/setup-r8a73a4.c new file mode 100644 index 000000000..9e3f4dc08 --- /dev/null +++ b/arch/arm/mach-shmobile/setup-r8a73a4.c @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * r8a73a4 processor support + * + * Copyright (C) 2013 Renesas Solutions Corp. + * Copyright (C) 2013 Magnus Damm + */ + +#include <linux/init.h> + +#include <asm/mach/arch.h> + +#include "common.h" + +static const char *const r8a73a4_boards_compat_dt[] __initconst = { + "renesas,r8a73a4", + NULL +}; + +DT_MACHINE_START(R8A73A4_DT, "Generic R8A73A4 (Flattened Device Tree)") + .init_late = shmobile_init_late, + .dt_compat = r8a73a4_boards_compat_dt, +MACHINE_END diff --git a/arch/arm/mach-shmobile/setup-r8a7740.c b/arch/arm/mach-shmobile/setup-r8a7740.c new file mode 100644 index 000000000..9ac2b8a2a --- /dev/null +++ b/arch/arm/mach-shmobile/setup-r8a7740.c @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * R8A7740 processor support + * + * Copyright (C) 2011 Renesas Solutions Corp. + * Copyright (C) 2011 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> + */ +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/irqchip.h> +#include <linux/irqchip/arm-gic.h> + +#include <asm/mach/map.h> +#include <asm/mach/arch.h> +#include <asm/mach/time.h> + +#include "common.h" + +/* + * r8a7740 chip has lasting errata on MERAM buffer. + * this is work-around for it. + * see + * "Media RAM (MERAM)" on r8a7740 documentation + */ +#define MEBUFCNTR 0xFE950098 +static void __init r8a7740_meram_workaround(void) +{ + void __iomem *reg; + + reg = ioremap(MEBUFCNTR, 4); + if (reg) { + iowrite32(0x01600164, reg); + iounmap(reg); + } +} + +static void __init r8a7740_init_irq_of(void) +{ + void __iomem *intc_prio_base = ioremap(0xe6900010, 0x10); + void __iomem *intc_msk_base = ioremap(0xe6900040, 0x10); + void __iomem *pfc_inta_ctrl = ioremap(0xe605807c, 0x4); + + irqchip_init(); + + /* route signals to GIC */ + iowrite32(0x0, pfc_inta_ctrl); + + /* + * To mask the shared interrupt to SPI 149 we must ensure to set + * PRIO *and* MASK. Else we run into IRQ floods when registering + * the intc_irqpin devices + */ + iowrite32(0x0, intc_prio_base + 0x0); + iowrite32(0x0, intc_prio_base + 0x4); + iowrite32(0x0, intc_prio_base + 0x8); + iowrite32(0x0, intc_prio_base + 0xc); + iowrite8(0xff, intc_msk_base + 0x0); + iowrite8(0xff, intc_msk_base + 0x4); + iowrite8(0xff, intc_msk_base + 0x8); + iowrite8(0xff, intc_msk_base + 0xc); + + iounmap(intc_prio_base); + iounmap(intc_msk_base); + iounmap(pfc_inta_ctrl); +} + +static void __init r8a7740_generic_init(void) +{ + r8a7740_meram_workaround(); +} + +static const char *const r8a7740_boards_compat_dt[] __initconst = { + "renesas,r8a7740", + NULL +}; + +DT_MACHINE_START(R8A7740_DT, "Generic R8A7740 (Flattened Device Tree)") + .l2c_aux_val = 0, + .l2c_aux_mask = ~0, + .init_early = shmobile_init_delay, + .init_irq = r8a7740_init_irq_of, + .init_machine = r8a7740_generic_init, + .init_late = shmobile_init_late, + .dt_compat = r8a7740_boards_compat_dt, +MACHINE_END diff --git a/arch/arm/mach-shmobile/setup-r8a7778.c b/arch/arm/mach-shmobile/setup-r8a7778.c new file mode 100644 index 000000000..445017e8c --- /dev/null +++ b/arch/arm/mach-shmobile/setup-r8a7778.c @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * r8a7778 processor support + * + * Copyright (C) 2013 Renesas Solutions Corp. + * Copyright (C) 2013 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> + * Copyright (C) 2013 Cogent Embedded, Inc. + */ + +#include <linux/io.h> +#include <linux/irqchip.h> + +#include <asm/mach/arch.h> + +#include "common.h" + +#define HPBREG_BASE 0xfe700000 + +#define INT2SMSKCR0 0x82288 /* 0xfe782288 */ +#define INT2SMSKCR1 0x8228c /* 0xfe78228c */ + +#define INT2NTSR0 0x00018 /* 0xfe700018 */ +#define INT2NTSR1 0x0002c /* 0xfe70002c */ + +static void __init r8a7778_init_irq_dt(void) +{ + void __iomem *base = ioremap(HPBREG_BASE, 0x00100000); + + BUG_ON(!base); + + irqchip_init(); + + /* route all interrupts to ARM */ + writel(0x73ffffff, base + INT2NTSR0); + writel(0xffffffff, base + INT2NTSR1); + + /* unmask all known interrupts in INTCS2 */ + writel(0x08330773, base + INT2SMSKCR0); + writel(0x00311110, base + INT2SMSKCR1); + + iounmap(base); +} + +static const char *const r8a7778_compat_dt[] __initconst = { + "renesas,r8a7778", + NULL +}; + +DT_MACHINE_START(R8A7778_DT, "Generic R8A7778 (Flattened Device Tree)") + .init_early = shmobile_init_delay, + .init_irq = r8a7778_init_irq_dt, + .init_late = shmobile_init_late, + .dt_compat = r8a7778_compat_dt, +MACHINE_END diff --git a/arch/arm/mach-shmobile/setup-r8a7779.c b/arch/arm/mach-shmobile/setup-r8a7779.c new file mode 100644 index 000000000..c3af2c892 --- /dev/null +++ b/arch/arm/mach-shmobile/setup-r8a7779.c @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * r8a7779 processor support + * + * Copyright (C) 2011, 2013 Renesas Solutions Corp. + * Copyright (C) 2011 Magnus Damm + * Copyright (C) 2013 Cogent Embedded, Inc. + */ +#include <linux/init.h> +#include <linux/irqchip.h> + +#include <asm/mach/arch.h> +#include <asm/mach/map.h> + +#include "common.h" +#include "r8a7779.h" + +#define HPBREG_BASE 0xfe700000 + +/* IRQ */ +#define INT2SMSKCR0 0x822a0 /* Interrupt Submask Clear Register 0 */ +#define INT2SMSKCR1 0x822a4 /* Interrupt Submask Clear Register 1 */ +#define INT2SMSKCR2 0x822a8 /* Interrupt Submask Clear Register 2 */ +#define INT2SMSKCR3 0x822ac /* Interrupt Submask Clear Register 3 */ +#define INT2SMSKCR4 0x822b0 /* Interrupt Submask Clear Register 4 */ + +#define INT2NTSR0 0x00060 /* Interrupt Notification Select Register 0 */ +#define INT2NTSR1 0x00064 /* Interrupt Notification Select Register 1 */ + +static void __init r8a7779_init_irq_dt(void) +{ + void __iomem *base = ioremap(HPBREG_BASE, 0x00100000); + + irqchip_init(); + + /* route all interrupts to ARM */ + writel(0xffffffff, base + INT2NTSR0); + writel(0x3fffffff, base + INT2NTSR1); + + /* unmask all known interrupts in INTCS2 */ + writel(0xfffffff0, base + INT2SMSKCR0); + writel(0xfff7ffff, base + INT2SMSKCR1); + writel(0xfffbffdf, base + INT2SMSKCR2); + writel(0xbffffffc, base + INT2SMSKCR3); + writel(0x003fee3f, base + INT2SMSKCR4); + + iounmap(base); +} + +static const char *const r8a7779_compat_dt[] __initconst = { + "renesas,r8a7779", + NULL +}; + +DT_MACHINE_START(R8A7779_DT, "Generic R8A7779 (Flattened Device Tree)") + .smp = smp_ops(r8a7779_smp_ops), + .init_irq = r8a7779_init_irq_dt, + .init_late = shmobile_init_late, + .dt_compat = r8a7779_compat_dt, +MACHINE_END diff --git a/arch/arm/mach-shmobile/setup-rcar-gen2.c b/arch/arm/mach-shmobile/setup-rcar-gen2.c new file mode 100644 index 000000000..3edbf0719 --- /dev/null +++ b/arch/arm/mach-shmobile/setup-rcar-gen2.c @@ -0,0 +1,226 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * R-Car Generation 2 support + * + * Copyright (C) 2013 Renesas Solutions Corp. + * Copyright (C) 2013 Magnus Damm + * Copyright (C) 2014 Ulrich Hecht + */ + +#include <linux/clocksource.h> +#include <linux/device.h> +#include <linux/dma-map-ops.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/memblock.h> +#include <linux/of.h> +#include <linux/of_clk.h> +#include <linux/of_fdt.h> +#include <linux/of_platform.h> +#include <linux/psci.h> +#include <asm/mach/arch.h> +#include <asm/secure_cntvoff.h> +#include "common.h" +#include "rcar-gen2.h" + +static const struct of_device_id cpg_matches[] __initconst = { + { .compatible = "renesas,r8a7742-cpg-mssr", .data = "extal" }, + { .compatible = "renesas,r8a7743-cpg-mssr", .data = "extal" }, + { .compatible = "renesas,r8a7744-cpg-mssr", .data = "extal" }, + { .compatible = "renesas,r8a7790-cpg-mssr", .data = "extal" }, + { .compatible = "renesas,r8a7791-cpg-mssr", .data = "extal" }, + { .compatible = "renesas,r8a7793-cpg-mssr", .data = "extal" }, + { /* sentinel */ } +}; + +static unsigned int __init get_extal_freq(void) +{ + const struct of_device_id *match; + struct device_node *cpg, *extal; + u32 freq = 20000000; + int idx = 0; + + cpg = of_find_matching_node_and_match(NULL, cpg_matches, &match); + if (!cpg) + return freq; + + if (match->data) + idx = of_property_match_string(cpg, "clock-names", match->data); + extal = of_parse_phandle(cpg, "clocks", idx); + of_node_put(cpg); + if (!extal) + return freq; + + of_property_read_u32(extal, "clock-frequency", &freq); + of_node_put(extal); + return freq; +} + +#define CNTCR 0 +#define CNTFID0 0x20 + +static void __init rcar_gen2_timer_init(void) +{ + bool need_update = true; + void __iomem *base; + u32 freq; + + /* + * If PSCI is available then most likely we are running on PSCI-enabled + * U-Boot which, we assume, has already taken care of resetting CNTVOFF + * and updating counter module before switching to non-secure mode + * and we don't need to. + */ +#ifdef CONFIG_ARM_PSCI_FW + if (psci_ops.cpu_on) + need_update = false; +#endif + + if (need_update == false) + goto skip_update; + + secure_cntvoff_init(); + + if (of_machine_is_compatible("renesas,r8a7745") || + of_machine_is_compatible("renesas,r8a77470") || + of_machine_is_compatible("renesas,r8a7792") || + of_machine_is_compatible("renesas,r8a7794")) { + freq = 260000000 / 8; /* ZS / 8 */ + } else { + /* At Linux boot time the r8a7790 arch timer comes up + * with the counter disabled. Moreover, it may also report + * a potentially incorrect fixed 13 MHz frequency. To be + * correct these registers need to be updated to use the + * frequency EXTAL / 2. + */ + freq = get_extal_freq() / 2; + } + + /* Remap "armgcnt address map" space */ + base = ioremap(0xe6080000, PAGE_SIZE); + + /* + * Update the timer if it is either not running, or is not at the + * right frequency. The timer is only configurable in secure mode + * so this avoids an abort if the loader started the timer and + * entered the kernel in non-secure mode. + */ + + if ((ioread32(base + CNTCR) & 1) == 0 || + ioread32(base + CNTFID0) != freq) { + /* Update registers with correct frequency */ + iowrite32(freq, base + CNTFID0); + asm volatile("mcr p15, 0, %0, c14, c0, 0" : : "r" (freq)); + + /* make sure arch timer is started by setting bit 0 of CNTCR */ + iowrite32(1, base + CNTCR); + } + + iounmap(base); + +skip_update: + of_clk_init(NULL); + timer_probe(); +} + +struct memory_reserve_config { + u64 reserved; + u64 base, size; +}; + +static int __init rcar_gen2_scan_mem(unsigned long node, const char *uname, + int depth, void *data) +{ + const char *type = of_get_flat_dt_prop(node, "device_type", NULL); + const __be32 *reg, *endp; + int l; + struct memory_reserve_config *mrc = data; + u64 lpae_start = 1ULL << 32; + + /* We are scanning "memory" nodes only */ + if (type == NULL || strcmp(type, "memory")) + return 0; + + reg = of_get_flat_dt_prop(node, "linux,usable-memory", &l); + if (reg == NULL) + reg = of_get_flat_dt_prop(node, "reg", &l); + if (reg == NULL) + return 0; + + endp = reg + (l / sizeof(__be32)); + while ((endp - reg) >= (dt_root_addr_cells + dt_root_size_cells)) { + u64 base, size; + + base = dt_mem_next_cell(dt_root_addr_cells, ®); + size = dt_mem_next_cell(dt_root_size_cells, ®); + + if (base >= lpae_start) + continue; + + if ((base + size) >= lpae_start) + size = lpae_start - base; + + if (size < mrc->reserved) + continue; + + if (base < mrc->base) + continue; + + /* keep the area at top near the 32-bit legacy limit */ + mrc->base = base + size - mrc->reserved; + mrc->size = mrc->reserved; + } + + return 0; +} + +static void __init rcar_gen2_reserve(void) +{ + struct memory_reserve_config mrc; + + /* reserve 256 MiB at the top of the physical legacy 32-bit space */ + memset(&mrc, 0, sizeof(mrc)); + mrc.reserved = SZ_256M; + + of_scan_flat_dt(rcar_gen2_scan_mem, &mrc); +#ifdef CONFIG_DMA_CMA + if (mrc.size && memblock_is_region_memory(mrc.base, mrc.size)) { + static struct cma *rcar_gen2_dma_contiguous; + + dma_contiguous_reserve_area(mrc.size, mrc.base, 0, + &rcar_gen2_dma_contiguous, true); + } +#endif +} + +static const char * const rcar_gen2_boards_compat_dt[] __initconst = { + "renesas,r8a7790", + "renesas,r8a7791", + "renesas,r8a7792", + "renesas,r8a7793", + "renesas,r8a7794", + NULL +}; + +DT_MACHINE_START(RCAR_GEN2_DT, "Generic R-Car Gen2 (Flattened Device Tree)") + .init_late = shmobile_init_late, + .init_time = rcar_gen2_timer_init, + .reserve = rcar_gen2_reserve, + .dt_compat = rcar_gen2_boards_compat_dt, +MACHINE_END + +static const char * const rz_g1_boards_compat_dt[] __initconst = { + "renesas,r8a7742", + "renesas,r8a7743", + "renesas,r8a7744", + "renesas,r8a7745", + "renesas,r8a77470", + NULL +}; + +DT_MACHINE_START(RZ_G1_DT, "Generic RZ/G1 (Flattened Device Tree)") + .init_late = shmobile_init_late, + .init_time = rcar_gen2_timer_init, + .reserve = rcar_gen2_reserve, + .dt_compat = rz_g1_boards_compat_dt, +MACHINE_END diff --git a/arch/arm/mach-shmobile/setup-sh73a0.c b/arch/arm/mach-shmobile/setup-sh73a0.c new file mode 100644 index 000000000..7fb27240e --- /dev/null +++ b/arch/arm/mach-shmobile/setup-sh73a0.c @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * sh73a0 processor support + * + * Copyright (C) 2010 Takashi Yoshii + * Copyright (C) 2010 Magnus Damm + * Copyright (C) 2008 Yoshihiro Shimoda + */ +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/delay.h> +#include <linux/input.h> +#include <linux/io.h> + +#include <asm/hardware/cache-l2x0.h> +#include <asm/mach/map.h> +#include <asm/mach/arch.h> +#include <asm/mach/time.h> + +#include "common.h" +#include "sh73a0.h" + +static void __init sh73a0_generic_init(void) +{ +#ifdef CONFIG_CACHE_L2X0 + /* Shared attribute override enable, 64K*8way */ + l2x0_init(ioremap(0xf0100000, PAGE_SIZE), 0x00400000, 0xc20f0fff); +#endif +} + +static const char *const sh73a0_boards_compat_dt[] __initconst = { + "renesas,sh73a0", + NULL +}; + +DT_MACHINE_START(SH73A0_DT, "Generic SH73A0 (Flattened Device Tree)") + .smp = smp_ops(sh73a0_smp_ops), + .init_machine = sh73a0_generic_init, + .init_late = shmobile_init_late, + .dt_compat = sh73a0_boards_compat_dt, +MACHINE_END diff --git a/arch/arm/mach-shmobile/sh73a0.h b/arch/arm/mach-shmobile/sh73a0.h new file mode 100644 index 000000000..85c7c7c60 --- /dev/null +++ b/arch/arm/mach-shmobile/sh73a0.h @@ -0,0 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __ASM_SH73A0_H__ +#define __ASM_SH73A0_H__ + +extern const struct smp_operations sh73a0_smp_ops; + +#endif /* __ASM_SH73A0_H__ */ diff --git a/arch/arm/mach-shmobile/smp-emev2.c b/arch/arm/mach-shmobile/smp-emev2.c new file mode 100644 index 000000000..3853ecea4 --- /dev/null +++ b/arch/arm/mach-shmobile/smp-emev2.c @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * SMP support for Emma Mobile EV2 + * + * Copyright (C) 2012 Renesas Solutions Corp. + * Copyright (C) 2012 Magnus Damm + */ +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/smp.h> +#include <linux/spinlock.h> +#include <linux/io.h> +#include <linux/delay.h> +#include <asm/smp_plat.h> +#include <asm/smp_scu.h> + +#include "common.h" +#include "emev2.h" + +#define EMEV2_SCU_BASE 0x1e000000 +#define EMEV2_SMU_BASE 0xe0110000 +#define SMU_GENERAL_REG0 0x7c0 + +static int emev2_boot_secondary(unsigned int cpu, struct task_struct *idle) +{ + arch_send_wakeup_ipi_mask(cpumask_of(cpu_logical_map(cpu))); + return 0; +} + +static void __init emev2_smp_prepare_cpus(unsigned int max_cpus) +{ + void __iomem *smu; + + /* Tell ROM loader about our vector (in headsmp.S) */ + smu = ioremap(EMEV2_SMU_BASE, PAGE_SIZE); + if (smu) { + iowrite32(__pa(shmobile_boot_vector), smu + SMU_GENERAL_REG0); + iounmap(smu); + } + + /* setup EMEV2 specific SCU bits */ + shmobile_smp_scu_prepare_cpus(EMEV2_SCU_BASE, max_cpus); +} + +const struct smp_operations emev2_smp_ops __initconst = { + .smp_prepare_cpus = emev2_smp_prepare_cpus, + .smp_boot_secondary = emev2_boot_secondary, +}; diff --git a/arch/arm/mach-shmobile/smp-r8a7779.c b/arch/arm/mach-shmobile/smp-r8a7779.c new file mode 100644 index 000000000..1bc609986 --- /dev/null +++ b/arch/arm/mach-shmobile/smp-r8a7779.c @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * SMP support for R-Mobile / SH-Mobile - r8a7779 portion + * + * Copyright (C) 2011 Renesas Solutions Corp. + * Copyright (C) 2011 Magnus Damm + */ +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/smp.h> +#include <linux/spinlock.h> +#include <linux/io.h> +#include <linux/delay.h> +#include <linux/soc/renesas/rcar-sysc.h> + +#include <asm/cacheflush.h> +#include <asm/smp_plat.h> +#include <asm/smp_scu.h> + +#include "common.h" +#include "r8a7779.h" + +#define HPBREG_BASE 0xfe700000 +#define AVECR 0x0040 /* ARM Reset Vector Address Register */ + +#define R8A7779_SCU_BASE 0xf0000000 + +static int r8a7779_boot_secondary(unsigned int cpu, struct task_struct *idle) +{ + int ret = -EIO; + + cpu = cpu_logical_map(cpu); + if (cpu) + ret = rcar_sysc_power_up_cpu(cpu); + + return ret; +} + +static void __init r8a7779_smp_prepare_cpus(unsigned int max_cpus) +{ + void __iomem *base = ioremap(HPBREG_BASE, 0x1000); + + /* Map the reset vector (in headsmp-scu.S, headsmp.S) */ + writel(__pa(shmobile_boot_vector), base + AVECR); + + /* setup r8a7779 specific SCU bits */ + shmobile_smp_scu_prepare_cpus(R8A7779_SCU_BASE, max_cpus); + + iounmap(base); +} + +#ifdef CONFIG_HOTPLUG_CPU +static int r8a7779_platform_cpu_kill(unsigned int cpu) +{ + int ret = -EIO; + + cpu = cpu_logical_map(cpu); + if (cpu) + ret = rcar_sysc_power_down_cpu(cpu); + + return ret ? ret : 1; +} + +static int r8a7779_cpu_kill(unsigned int cpu) +{ + if (shmobile_smp_scu_cpu_kill(cpu)) + return r8a7779_platform_cpu_kill(cpu); + + return 0; +} +#endif /* CONFIG_HOTPLUG_CPU */ + +const struct smp_operations r8a7779_smp_ops __initconst = { + .smp_prepare_cpus = r8a7779_smp_prepare_cpus, + .smp_boot_secondary = r8a7779_boot_secondary, +#ifdef CONFIG_HOTPLUG_CPU + .cpu_die = shmobile_smp_scu_cpu_die, + .cpu_kill = r8a7779_cpu_kill, +#endif +}; diff --git a/arch/arm/mach-shmobile/smp-sh73a0.c b/arch/arm/mach-shmobile/smp-sh73a0.c new file mode 100644 index 000000000..453d48865 --- /dev/null +++ b/arch/arm/mach-shmobile/smp-sh73a0.c @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * SMP support for R-Mobile / SH-Mobile - sh73a0 portion + * + * Copyright (C) 2010 Magnus Damm + * Copyright (C) 2010 Takashi Yoshii + */ +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/smp.h> +#include <linux/io.h> +#include <linux/delay.h> + +#include <asm/smp_plat.h> + +#include "common.h" +#include "sh73a0.h" + +#define CPG_BASE2 0xe6151000 +#define WUPCR 0x10 /* System-CPU Wake Up Control Register */ +#define SRESCR 0x18 /* System-CPU Software Reset Control Register */ +#define PSTR 0x40 /* System-CPU Power Status Register */ + +#define SYSC_BASE 0xe6180000 +#define SBAR 0x20 /* SYS Boot Address Register */ + +#define AP_BASE 0xe6f10000 +#define APARMBAREA 0x20 /* Address Translation Area Register */ + +#define SH73A0_SCU_BASE 0xf0000000 + +static int sh73a0_boot_secondary(unsigned int cpu, struct task_struct *idle) +{ + unsigned int lcpu = cpu_logical_map(cpu); + void __iomem *cpg2 = ioremap(CPG_BASE2, PAGE_SIZE); + + if (((readl(cpg2 + PSTR) >> (4 * lcpu)) & 3) == 3) + writel(1 << lcpu, cpg2 + WUPCR); /* wake up */ + else + writel(1 << lcpu, cpg2 + SRESCR); /* reset */ + iounmap(cpg2); + return 0; +} + +static void __init sh73a0_smp_prepare_cpus(unsigned int max_cpus) +{ + void __iomem *ap = ioremap(AP_BASE, PAGE_SIZE); + void __iomem *sysc = ioremap(SYSC_BASE, PAGE_SIZE); + + /* Map the reset vector (in headsmp.S) */ + writel(0, ap + APARMBAREA); /* 4k */ + writel(__pa(shmobile_boot_vector), sysc + SBAR); + iounmap(sysc); + iounmap(ap); + + /* setup sh73a0 specific SCU bits */ + shmobile_smp_scu_prepare_cpus(SH73A0_SCU_BASE, max_cpus); +} + +const struct smp_operations sh73a0_smp_ops __initconst = { + .smp_prepare_cpus = sh73a0_smp_prepare_cpus, + .smp_boot_secondary = sh73a0_boot_secondary, +#ifdef CONFIG_HOTPLUG_CPU + .cpu_can_disable = shmobile_smp_cpu_can_disable, + .cpu_die = shmobile_smp_scu_cpu_die, + .cpu_kill = shmobile_smp_scu_cpu_kill, +#endif +}; diff --git a/arch/arm/mach-shmobile/suspend.c b/arch/arm/mach-shmobile/suspend.c new file mode 100644 index 000000000..3969a4997 --- /dev/null +++ b/arch/arm/mach-shmobile/suspend.c @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Suspend-to-RAM support code for SH-Mobile ARM + * + * Copyright (C) 2011 Magnus Damm + */ + +#include <linux/pm.h> +#include <linux/suspend.h> +#include <linux/module.h> +#include <linux/err.h> +#include <linux/cpu.h> + +#include <asm/io.h> +#include <asm/system_misc.h> + +#include "common.h" + +static int shmobile_suspend_default_enter(suspend_state_t suspend_state) +{ + cpu_do_idle(); + return 0; +} + +static int shmobile_suspend_begin(suspend_state_t state) +{ + cpu_idle_poll_ctrl(true); + return 0; +} + +static void shmobile_suspend_end(void) +{ + cpu_idle_poll_ctrl(false); +} + +struct platform_suspend_ops shmobile_suspend_ops = { + .begin = shmobile_suspend_begin, + .end = shmobile_suspend_end, + .enter = shmobile_suspend_default_enter, + .valid = suspend_valid_only_mem, +}; + +int __init shmobile_suspend_init(void) +{ + suspend_set_ops(&shmobile_suspend_ops); + return 0; +} diff --git a/arch/arm/mach-shmobile/timer.c b/arch/arm/mach-shmobile/timer.c new file mode 100644 index 000000000..2335311b5 --- /dev/null +++ b/arch/arm/mach-shmobile/timer.c @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * SH-Mobile Timer + * + * Copyright (C) 2010 Magnus Damm + * Copyright (C) 2002 - 2009 Paul Mundt + */ +#include <linux/platform_device.h> +#include <linux/clocksource.h> +#include <linux/delay.h> +#include <linux/of_address.h> + +#include "common.h" + +void __init shmobile_init_delay(void) +{ + struct device_node *np; + u32 max_freq = 0; + + for_each_of_cpu_node(np) { + u32 freq; + + if (!of_property_read_u32(np, "clock-frequency", &freq)) + max_freq = max(max_freq, freq); + } + + if (!max_freq) + return; + + /* + * Calculate a worst-case loops-per-jiffy value + * based on maximum cpu core hz setting and the + * __delay() implementation in arch/arm/lib/delay.S. + * + * This will result in a longer delay than expected + * when the cpu core runs on lower frequencies. + */ + + if (!preset_lpj) + preset_lpj = max_freq / HZ; +} |