summaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-shmobile
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-08-30 09:22:00 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2026-08-30 09:22:00 -0700
commit034dd340b08be1f2f0477ad16131d609f9dbd53c (patch)
tree2536f4b2d7893ccd916b5abdf3c454ad6edc03c2 /arch/arm/mach-shmobile
downloadlinux-stable-034dd340b08be1f2f0477ad16131d609f9dbd53c.tar.gz
linux-stable-034dd340b08be1f2f0477ad16131d609f9dbd53c.zip
Merge tag 'trace-v7.3-2' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-tracegrafted
Pull tracing fixes from Steven Rostedt: - Fix error output of boot instance creation failure Currently if a boot instance creation fails, instead of printing out the name of the instance that failed, it prints "(null)". That is because it prints "cur_str" that had already been processed by strsep(). Print the saved name instead. While at it, print the error code of the failure. - Fix use-after-free for same named historgrams Histograms can be named so that they can be used in multiple events. But if the named histogram has a variable attached, the second event that uses the named histogram which duplicates it and needs to free the original after duplication leaves the old variable in place and still visible. If another histogram uses than variable, it will use the stale one which will try to reference the freed duplicate histogram and crash the kernel. Free the duplicate variables along with the duplicated histogram data. - Check return value of kthread_run() in event self test The events self tests uses a kthread for testing but does not check if it succeeded in creating a kthread. If the kthread creation were to fail, the code will still try to call kthread_stop() on the error returned. - Fix race between reading trace_pipe and updating subbuffer size If a user is reading the trace_pipe file at the same time they update the ring buffer sub-buffer size, can cause the trace_pipe read to read stale data. Add trace_access_lock() around updating the ring buffer sub-buffer size. - Fix eventfs_inode on failure path in creation of the events directory In the creation of the "events" directory, if after allocating the eventfs_inode a failure is detected, it calls cleanup_ei() which calls free_ei(). The free_ei() will test if eventfs_inode being freed has no children. It is a bug if it does. But on the failure case of the creation of the "events" directory, the children lists have not yet been initialized and the free will trigger a warning because list_empty() on an uninitialized list returns false. Move the initialization into init_ei() where it makes more sense and makes sure that a created eventfs_inode has its lists initialized upon creation. - Check return value of kthread_run() in ftrace direct sample code The sample code that shows how to use the ftrace direct calls does not test the return of kthread_run() to see if it succeeds. Return a failure if the kthread_run() doesn't succeed. - Clear user events state on fork in case of alloc failure On fork, the child gets a pointer to the parent's user events state. It makes a copy of it then updates the child's pointer to it. But if the allocation fails, the duplication function leaves the child with a pointer to its parent's descriptor. When the child cleans up its data, it will free the parent's descriptor while the parent is still using it. In the duplication function, set the child's user_event_mm to NULL before testing if the allocation succeeded, and when it exits it will not free the parent's descriptor. - Fix retry exhaustion in simple ring buffer reader swap simple_ring_buffer_swap_reader_page() starts with retry set to 8 and post-decrements it only after a failed link replacement. On the final attempt, a successful replacement leaves retry at zero, while a failed replacement leaves it at -1. But the check for success expects the retry value to be non-zero and exits with an error on zero. This is the opposite result. Fix it. - Fail nicely when the remote swap_reader_page() returns an error Currently, if the swap_reader_page() of a remote buffer fails, it triggers a WARN_ON_ONCE() and continues normally. Instead, have it exit with an error and a pr_warn() print instead of a full WARNING. * tag 'trace-v7.3-2' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace: ring-buffer: Stop remote reader update when page swap fails tracing: Fix retry exhaustion in simple ring buffer reader swap tracing/user_events: Clear copied tracing state before fork duplication samples/ftrace: Fix kthread_stop() on ERR_PTR in ftrace-direct-multi-modify samples/ftrace: Fix kthread_stop() on ERR_PTR in ftrace-direct-modify eventfs: Initialize ei->children and ei->list in init_ei() tracing: Fix use-after-free in trace_pipe read on sub-buffer order change tracing: Fix crash passing ERR_PTR to kthread_stop() tracing: Fix use-after-free with same-name named triggers tracing: Fix logged instance name on creation failure
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.S148
-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.c130
-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.c229
-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.c150
-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.c87
-rw-r--r--arch/arm/mach-shmobile/smp-sh73a0.c74
-rw-r--r--arch/arm/mach-shmobile/suspend.c47
-rw-r--r--arch/arm/mach-shmobile/timer.c41
29 files changed, 1867 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..2bc7e73a8
--- /dev/null
+++ b/arch/arm/mach-shmobile/headsmp.S
@@ -0,0 +1,148 @@
+/* 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
+ .align 2
+ .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..46654d196
--- /dev/null
+++ b/arch/arm/mach-shmobile/pm-rcar-gen2.c
@@ -0,0 +1,130 @@
+// 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;
+ struct device_node *np;
+ bool has_a7 = false;
+ bool has_a15 = false;
+ struct resource res;
+ int error;
+
+ if (!request_mem_region(0, SZ_256K, "Boot Area")) {
+ pr_err("Failed to request boot area\n");
+ 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 & (SZ_256K - 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..647e5f9f6
--- /dev/null
+++ b/arch/arm/mach-shmobile/regulator-quirk-rcar-gen2.c
@@ -0,0 +1,229 @@
+// 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)
+{
+ static const char *const boards[] __initconst = {
+ "renesas,koelsch", "renesas,lager", "renesas,porter",
+ "renesas,stout", "renesas,gose", NULL
+ };
+ struct regulator_quirk *quirk, *pos, *tmp;
+ struct of_phandle_args *args;
+ const struct of_device_id *id;
+ struct device_node *np;
+ u32 mon, addr;
+ int ret;
+
+ if (!of_machine_compatible_match(boards))
+ 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_obj(*quirk);
+ if (!quirk) {
+ ret = -ENOMEM;
+ of_node_put(np);
+ goto err_mem;
+ }
+
+ args = &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, args);
+ if (ret) { /* Skip invalid entry and continue */
+ of_node_put(np);
+ kfree(quirk);
+ continue;
+ }
+
+ list_for_each_entry(pos, &quirk_list, list) {
+ if (of_phandle_args_equal(args, &pos->irq_args)) {
+ 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..514558905
--- /dev/null
+++ b/arch/arm/mach-shmobile/setup-rcar-gen2.c
@@ -0,0 +1,150 @@
+// 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/io.h>
+#include <linux/kernel.h>
+#include <linux/memblock.h>
+#include <linux/of.h>
+#include <linux/of_clk.h>
+#include <linux/psci.h>
+#include <asm/mach/arch.h>
+#include <asm/secure_cntvoff.h>
+#include "common.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)
+{
+ static const char *const fixed_freq_socs[] __initconst = {
+ "renesas,r8a7745", "renesas,r8a77470", "renesas,r8a7792",
+ "renesas,r8a7794", NULL
+ };
+ 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_compatible_match(fixed_freq_socs)) {
+ 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();
+}
+
+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,
+ .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,
+ .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..474c32532
--- /dev/null
+++ b/arch/arm/mach-shmobile/smp-r8a7779.c
@@ -0,0 +1,87 @@
+// 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;
+
+ if (!request_mem_region(0, SZ_4K, "Boot Area")) {
+ pr_err("Failed to request boot area\n");
+ return;
+ }
+
+ 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..9196b37ea
--- /dev/null
+++ b/arch/arm/mach-shmobile/smp-sh73a0.c
@@ -0,0 +1,74 @@
+// 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, *sysc;
+
+ if (!request_mem_region(0, SZ_4K, "Boot Area")) {
+ pr_err("Failed to request boot area\n");
+ return;
+ }
+
+ /* Map the reset vector (in headsmp.S) */
+ ap = ioremap(AP_BASE, PAGE_SIZE);
+ sysc = ioremap(SYSC_BASE, PAGE_SIZE);
+ 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;
+}