aboutsummaryrefslogtreecommitdiffstats
path: root/arch/sh
diff options
context:
space:
mode:
authorRich Felker <dalias@libc.org>2016-02-15 13:36:13 -0500
committerRich Felker <dalias@libc.org>2016-08-04 23:29:39 -0400
commitb4214e41b7152b1964a3421a40251d202ae2d2c0 (patch)
treeedfab19d4f16bf2d6f42168c6598aa1fac4ac0e3 /arch/sh
parent4b6ef05b3e89f5bdb888fbd5467e996a9193b884 (diff)
sh: add SMP support for J2
Support is hooked up via a cpu start method specified in the device tree, and also depends on DT nodes that describe the interfaces for performing IPI and identifying which cpu execution is taking place on. The currently used method is a form of spin table, where secondary cpus are unblocked by writing to a special address. Signed-off-by: Rich Felker <dalias@libc.org>
Diffstat (limited to 'arch/sh')
-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.c139
3 files changed, 145 insertions, 0 deletions
diff --git a/arch/sh/Kconfig b/arch/sh/Kconfig
index 2ef6f652bc50..ee086958b2b2 100644
--- a/arch/sh/Kconfig
+++ b/arch/sh/Kconfig
@@ -261,6 +261,8 @@ config CPU_SUBTYPE_SH7619
261config CPU_SUBTYPE_J2 261config CPU_SUBTYPE_J2
262 bool "Support J2 processor" 262 bool "Support J2 processor"
263 select CPU_J2 263 select CPU_J2
264 select SYS_SUPPORTS_SMP
265 select GENERIC_CLOCKEVENTS_BROADCAST if SMP
264 266
265# SH-2A Processor Support 267# SH-2A Processor Support
266 268
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 @@
5obj-y := ex.o probe.o entry.o 5obj-y := ex.o probe.o entry.o
6 6
7obj-$(CONFIG_CPU_SUBTYPE_SH7619) += setup-sh7619.o clock-sh7619.o 7obj-$(CONFIG_CPU_SUBTYPE_SH7619) += setup-sh7619.o clock-sh7619.o
8
9# SMP setup
10smp-$(CONFIG_CPU_J2) := smp-j2.o
11obj-$(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..6ccd7e4dc008
--- /dev/null
+++ b/arch/sh/kernel/cpu/sh2/smp-j2.c
@@ -0,0 +1,139 @@
1/*
2 * SMP support for J2 processor
3 *
4 * Copyright (C) 2015-2016 Smart Energy Instruments, Inc.
5 *
6 * This file is subject to the terms and conditions of the GNU General Public
7 * License. See the file "COPYING" in the main directory of this archive
8 * for more details.
9 */
10
11#include <linux/smp.h>
12#include <linux/interrupt.h>
13#include <linux/io.h>
14#include <linux/of_address.h>
15#include <linux/of_irq.h>
16#include <asm/cmpxchg.h>
17
18DEFINE_PER_CPU(unsigned, j2_ipi_messages);
19
20extern u32 *sh2_cpuid_addr;
21static u32 *j2_ipi_trigger;
22static int j2_ipi_irq;
23
24static irqreturn_t j2_ipi_interrupt_handler(int irq, void *arg)
25{
26 unsigned cpu = hard_smp_processor_id();
27 volatile unsigned *pmsg = &per_cpu(j2_ipi_messages, cpu);
28 unsigned messages, i;
29
30 do messages = *pmsg;
31 while (cmpxchg(pmsg, messages, 0) != messages);
32
33 if (!messages) return IRQ_NONE;
34
35 for (i=0; i<SMP_MSG_NR; i++)
36 if (messages & (1U<<i))
37 smp_message_recv(i);
38
39 return IRQ_HANDLED;
40}
41
42static void j2_smp_setup(void)
43{
44}
45
46static void j2_prepare_cpus(unsigned int max_cpus)
47{
48 struct device_node *np;
49 unsigned i, max = 1;
50
51 np = of_find_compatible_node(NULL, NULL, "jcore,ipi-controller");
52 if (!np)
53 goto out;
54
55 j2_ipi_irq = irq_of_parse_and_map(np, 0);
56 j2_ipi_trigger = of_iomap(np, 0);
57 if (!j2_ipi_irq || !j2_ipi_trigger)
58 goto out;
59
60 np = of_find_compatible_node(NULL, NULL, "jcore,cpuid-mmio");
61 if (!np)
62 goto out;
63
64 sh2_cpuid_addr = of_iomap(np, 0);
65 if (!sh2_cpuid_addr)
66 goto out;
67
68 if (request_irq(j2_ipi_irq, j2_ipi_interrupt_handler, IRQF_PERCPU,
69 "ipi", (void *)j2_ipi_interrupt_handler) != 0)
70 goto out;
71
72 max = max_cpus;
73out:
74 /* Disable any cpus past max_cpus, or all secondaries if we didn't
75 * get the necessary resources to support SMP. */
76 for (i=max; i<NR_CPUS; i++) {
77 set_cpu_possible(i, false);
78 set_cpu_present(i, false);
79 }
80}
81
82static void j2_start_cpu(unsigned int cpu, unsigned long entry_point)
83{
84 struct device_node *np;
85 u32 regs[2];
86 void __iomem *release, *initpc;
87
88 if (!cpu) return;
89
90 np = of_get_cpu_node(cpu, NULL);
91 if (!np) return;
92
93 if (of_property_read_u32_array(np, "cpu-release-addr", regs, 2)) return;
94 release = ioremap_nocache(regs[0], sizeof(u32));
95 initpc = ioremap_nocache(regs[1], sizeof(u32));
96
97 __raw_writel(entry_point, initpc);
98 __raw_writel(1, release);
99
100 iounmap(initpc);
101 iounmap(release);
102
103 pr_info("J2 SMP: requested start of cpu %u\n", cpu);
104}
105
106static unsigned int j2_smp_processor_id(void)
107{
108 return __raw_readl(sh2_cpuid_addr);
109}
110
111static void j2_send_ipi(unsigned int cpu, unsigned int message)
112{
113 volatile unsigned *pmsg;
114 unsigned old;
115 unsigned long val;
116
117 /* There is only one IPI interrupt shared by all messages, so
118 * we keep a separate interrupt flag per message type in sw. */
119 pmsg = &per_cpu(j2_ipi_messages, cpu);
120 do old = *pmsg;
121 while (cmpxchg(pmsg, old, old|(1U<<message)) != old);
122
123 /* Generate the actual interrupt by writing to CCRn bit 28. */
124 val = __raw_readl(j2_ipi_trigger + cpu);
125 __raw_writel(val | (1U<<28), j2_ipi_trigger + cpu);
126}
127
128static struct plat_smp_ops j2_smp_ops = {
129 .smp_setup = j2_smp_setup,
130 .prepare_cpus = j2_prepare_cpus,
131 .start_cpu = j2_start_cpu,
132 .smp_processor_id = j2_smp_processor_id,
133 .send_ipi = j2_send_ipi,
134 .cpu_die = native_cpu_die,
135 .cpu_disable = native_cpu_disable,
136 .play_dead = native_play_dead,
137};
138
139CPU_METHOD_OF_DECLARE(j2_cpu_method, "jcore,spin-table", &j2_smp_ops);