summaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-shmobile
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-11 08:27:49 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-11 08:27:49 +0000
commitace9429bb58fd418f0c81d4c2835699bddf6bde6 (patch)
treeb2d64bc10158fdd5497876388cd68142ca374ed3 /arch/arm/mach-shmobile
parentInitial commit. (diff)
downloadlinux-ace9429bb58fd418f0c81d4c2835699bddf6bde6.tar.xz
linux-ace9429bb58fd418f0c81d4c2835699bddf6bde6.zip
Adding upstream version 6.6.15.upstream/6.6.15
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'arch/arm/mach-shmobile')
-rw-r--r--arch/arm/mach-shmobile/Kconfig7
-rw-r--r--arch/arm/mach-shmobile/Makefile41
-rw-r--r--arch/arm/mach-shmobile/common.h39
-rw-r--r--arch/arm/mach-shmobile/emev2.h7
-rw-r--r--arch/arm/mach-shmobile/headsmp-apmu.S14
-rw-r--r--arch/arm/mach-shmobile/headsmp-scu.S31
-rw-r--r--arch/arm/mach-shmobile/headsmp.S147
-rw-r--r--arch/arm/mach-shmobile/platsmp-apmu.c282
-rw-r--r--arch/arm/mach-shmobile/platsmp-scu.c90
-rw-r--r--arch/arm/mach-shmobile/platsmp.c35
-rw-r--r--arch/arm/mach-shmobile/pm-rcar-gen2.c129
-rw-r--r--arch/arm/mach-shmobile/r8a7779.h7
-rw-r--r--arch/arm/mach-shmobile/rcar-gen2.h7
-rw-r--r--arch/arm/mach-shmobile/regulator-quirk-rcar-gen2.c237
-rw-r--r--arch/arm/mach-shmobile/setup-emev2.c27
-rw-r--r--arch/arm/mach-shmobile/setup-r7s72100.c26
-rw-r--r--arch/arm/mach-shmobile/setup-r7s9210.c27
-rw-r--r--arch/arm/mach-shmobile/setup-r8a73a4.c23
-rw-r--r--arch/arm/mach-shmobile/setup-r8a7740.c86
-rw-r--r--arch/arm/mach-shmobile/setup-r8a7778.c54
-rw-r--r--arch/arm/mach-shmobile/setup-r8a7779.c60
-rw-r--r--arch/arm/mach-shmobile/setup-rcar-gen2.c225
-rw-r--r--arch/arm/mach-shmobile/setup-sh73a0.c43
-rw-r--r--arch/arm/mach-shmobile/sh73a0.h7
-rw-r--r--arch/arm/mach-shmobile/smp-emev2.c48
-rw-r--r--arch/arm/mach-shmobile/smp-r8a7779.c80
-rw-r--r--arch/arm/mach-shmobile/smp-sh73a0.c68
-rw-r--r--arch/arm/mach-shmobile/suspend.c47
-rw-r--r--arch/arm/mach-shmobile/timer.c41
29 files changed, 1935 insertions, 0 deletions
diff --git a/arch/arm/mach-shmobile/Kconfig b/arch/arm/mach-shmobile/Kconfig
new file mode 100644
index 000000000..8d64cc7ed
--- /dev/null
+++ b/arch/arm/mach-shmobile/Kconfig
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0
+menuconfig ARCH_RENESAS
+ bool "Renesas ARM SoCs"
+ depends on ARCH_MULTI_V7
+ select ARM_GIC
+ select NO_IOPORT_MAP
+ 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..e892ee794
--- /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/page.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..a956b489b
--- /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/page.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..ec6f421c0
--- /dev/null
+++ b/arch/arm/mach-shmobile/platsmp-apmu.c
@@ -0,0 +1,282 @@
+// 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.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;
+
+ 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)
+ break;
+ if (of_cpu_node_to_id(np_cpu) == 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)
+ break;
+
+ index = of_cpu_node_to_id(np_cpu);
+ 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, &regulator_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..c38367a10
--- /dev/null
+++ b/arch/arm/mach-shmobile/setup-rcar-gen2.c
@@ -0,0 +1,225 @@
+// 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/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, &reg);
+ size = dt_mem_next_cell(dt_root_size_cells, &reg);
+
+ 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;
+}