summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRich Felker <dalias@libc.org>2016-02-15 18:36:13 +0000
committerRich Felker <dalias@libc.org>2016-04-11 22:55:58 +0000
commit9f70cf81462fff62a8256485c4c8896f57b90540 (patch)
tree7a23b1f12869fdefe6433a0c74d5601118e24023
parent67bf7c751df556e136fae8e8f27f89eedb5f1bf0 (diff)
downloadlinux-sh-9f70cf81462fff62a8256485c4c8896f57b90540.tar.gz
sh: add SMP support for J2
Signed-off-by: Rich Felker <dalias@libc.org>
-rw-r--r--arch/sh/Kconfig2
-rw-r--r--arch/sh/kernel/cpu/sh2/Makefile4
-rw-r--r--arch/sh/kernel/cpu/sh2/smp-j2.c127
3 files changed, 133 insertions, 0 deletions
diff --git a/arch/sh/Kconfig b/arch/sh/Kconfig
index 77bf1e8fbdd2..2e3a073d89c4 100644
--- a/arch/sh/Kconfig
+++ b/arch/sh/Kconfig
@@ -254,6 +254,8 @@ config CPU_SUBTYPE_SH7619
config CPU_SUBTYPE_J2
bool "Support J2 processor"
select CPU_J2
+ select SYS_SUPPORTS_SMP
+ select GENERIC_CLOCKEVENTS_BROADCAST if SMP
# SH-2A Processor Support
diff --git a/arch/sh/kernel/cpu/sh2/Makefile b/arch/sh/kernel/cpu/sh2/Makefile
index f0f059acfcfb..904c4283d923 100644
--- a/arch/sh/kernel/cpu/sh2/Makefile
+++ b/arch/sh/kernel/cpu/sh2/Makefile
@@ -5,3 +5,7 @@
obj-y := ex.o probe.o entry.o
obj-$(CONFIG_CPU_SUBTYPE_SH7619) += setup-sh7619.o clock-sh7619.o
+
+# SMP setup
+smp-$(CONFIG_CPU_J2) := smp-j2.o
+obj-$(CONFIG_SMP) += $(smp-y)
diff --git a/arch/sh/kernel/cpu/sh2/smp-j2.c b/arch/sh/kernel/cpu/sh2/smp-j2.c
new file mode 100644
index 000000000000..db91979847c6
--- /dev/null
+++ b/arch/sh/kernel/cpu/sh2/smp-j2.c
@@ -0,0 +1,127 @@
+/*
+ * SMP support for J2 processor
+ *
+ * Copyright (C) 2015-2016 Smart Energy Instruments, Inc.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/cpumask.h>
+#include <linux/smp.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/cpu.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <asm/sections.h>
+#include <asm/cmpxchg.h>
+
+#define J2_CPU_LIMIT 2
+
+DEFINE_PER_CPU(unsigned, j2_ipi_messages);
+
+extern u32 *sh2_cpuid_addr;
+static u32 *j2_cpustart_trigger, *j2_cpustart_initpc, *j2_ipi_trigger;
+static int j2_ipi_irq;
+
+static irqreturn_t j2_ipi_interrupt_handler(int irq, void *arg)
+{
+ unsigned cpu = hard_smp_processor_id();
+ volatile unsigned *pmsg = &per_cpu(j2_ipi_messages, cpu);
+ unsigned messages, i;
+
+ do messages = *pmsg;
+ while (cmpxchg(pmsg, messages, 0) != messages);
+
+ if (!messages) return IRQ_NONE;
+
+ for (i=0; i<SMP_MSG_NR; i++)
+ if (messages & (1U<<i))
+ smp_message_recv(i);
+
+ return IRQ_HANDLED;
+}
+
+static void j2_smp_setup(void)
+{
+ pr_info("j2_smp_setup finished\n");
+}
+
+static void j2_prepare_cpus(unsigned int max_cpus)
+{
+ struct device_node *np;
+ unsigned i;
+
+ for (i=max_cpus; i<NR_CPUS; i++) {
+ set_cpu_possible(i, false);
+ set_cpu_present(i, false);
+ }
+
+ np = of_find_compatible_node(NULL, NULL, "jcore,soc-ipi-0.1");
+ if (!np) return;
+ j2_ipi_irq = irq_of_parse_and_map(np, 0);
+ j2_ipi_trigger = of_iomap(np, 0);
+
+ np = of_find_compatible_node(NULL, NULL, "jcore,soc-cpuid-0.1");
+ if (!np) return;
+ sh2_cpuid_addr = of_iomap(np, 0);
+
+ np = of_find_compatible_node(NULL, NULL, "jcore,soc-cpustart-0.1");
+ if (!np) return;
+ j2_cpustart_trigger = of_iomap(np, 0);
+ j2_cpustart_initpc = of_iomap(np, 1);
+
+ request_irq(j2_ipi_irq, j2_ipi_interrupt_handler, IRQF_PERCPU,
+ "ipi", (void *)j2_ipi_interrupt_handler);
+
+ pr_info("j2_prepare_cpus finished\n");
+}
+
+static void j2_start_cpu(unsigned int cpu, unsigned long entry_point)
+{
+ if (!cpu) return;
+ __raw_writel(entry_point, j2_cpustart_initpc);
+ __raw_writel(1, j2_cpustart_trigger);
+ pr_info("j2_start_cpu(%u) finished\n", cpu);
+}
+
+static unsigned int j2_smp_processor_id(void)
+{
+ return __raw_readl(sh2_cpuid_addr);
+}
+
+static void j2_send_ipi(unsigned int cpu, unsigned int message)
+{
+ volatile unsigned *pmsg;
+ unsigned old;
+ unsigned long val;
+
+ /* There is only one IPI interrupt shared by all messages, so
+ * we keep a separate interrupt flag per message type in sw. */
+ pmsg = &per_cpu(j2_ipi_messages, cpu);
+ do old = *pmsg;
+ while (cmpxchg(pmsg, old, old|(1U<<message)) != old);
+
+ /* Generate the actual interrupt by writing to CCRn bit 28. */
+ val = __raw_readl(j2_ipi_trigger + cpu);
+ __raw_writel(val | (1U<<28), j2_ipi_trigger + cpu);
+}
+
+static struct plat_smp_ops j2_smp_ops = {
+ .smp_setup = j2_smp_setup,
+ .prepare_cpus = j2_prepare_cpus,
+ .start_cpu = j2_start_cpu,
+ .smp_processor_id = j2_smp_processor_id,
+ .send_ipi = j2_send_ipi,
+ .cpu_die = native_cpu_die,
+ .cpu_disable = native_cpu_disable,
+ .play_dead = native_play_dead,
+};
+
+CPU_METHOD_OF_DECLARE(j2_cpu_method, "jcore,j2-soc-smp", &j2_smp_ops);