aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStafford Horne <shorne@gmail.com>2017-10-30 08:38:35 -0400
committerStafford Horne <shorne@gmail.com>2017-11-03 01:01:13 -0400
commit9b54470afd836278a7e6f0f08194e2e2dca4b6eb (patch)
tree5471b19b526dd33b375f2ce7b129fce5f226a794
parentfab8be88ac0478b0157859f74fad5088c292356b (diff)
irqchip: add initial support for ompic
IPI driver for the Open Multi-Processor Interrupt Controller (ompic) as described in the Multi-core support section of the OpenRISC 1.2 architecture specification: https://github.com/openrisc/doc/raw/master/openrisc-arch-1.2-rev0.pdf Each OpenRISC core contains a full interrupt controller which is used in the SMP architecture for interrupt balancing. This IPI device, the ompic, is the only external device required for enabling SMP on OpenRISC. Pending ops are stored in a memory bit mask which can allow multiple pending operations to be set and serviced at a time. This is mostly borrowed from the alpha IPI implementation. Reviewed-by: Marc Zyngier <marc.zyngier@arm.com> Acked-by: Rob Herring <robh@kernel.org> Signed-off-by: Stefan Kristiansson <stefan.kristiansson@saunalahti.fi> [shorne@gmail.com: converted ops to bitmask, wrote commit message] Signed-off-by: Stafford Horne <shorne@gmail.com>
-rw-r--r--Documentation/devicetree/bindings/interrupt-controller/openrisc,ompic.txt22
-rw-r--r--MAINTAINERS1
-rw-r--r--arch/openrisc/Kconfig1
-rw-r--r--drivers/irqchip/Kconfig3
-rw-r--r--drivers/irqchip/Makefile1
-rw-r--r--drivers/irqchip/irq-ompic.c202
6 files changed, 230 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/interrupt-controller/openrisc,ompic.txt b/Documentation/devicetree/bindings/interrupt-controller/openrisc,ompic.txt
new file mode 100644
index 000000000000..caec07cc7149
--- /dev/null
+++ b/Documentation/devicetree/bindings/interrupt-controller/openrisc,ompic.txt
@@ -0,0 +1,22 @@
1Open Multi-Processor Interrupt Controller
2
3Required properties:
4
5- compatible : This should be "openrisc,ompic"
6- reg : Specifies base physical address and size of the register space. The
7 size is based on the number of cores the controller has been configured
8 to handle, this should be set to 8 bytes per cpu core.
9- interrupt-controller : Identifies the node as an interrupt controller.
10- #interrupt-cells : This should be set to 0 as this will not be an irq
11 parent.
12- interrupts : Specifies the interrupt line to which the ompic is wired.
13
14Example:
15
16ompic: interrupt-controller@98000000 {
17 compatible = "openrisc,ompic";
18 reg = <0x98000000 16>;
19 interrupt-controller;
20 #interrupt-cells = <0>;
21 interrupts = <1>;
22};
diff --git a/MAINTAINERS b/MAINTAINERS
index bcb7e2525f54..bb129d5f0d3a 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -10034,6 +10034,7 @@ S: Maintained
10034F: Documentation/devicetree/bindings/openrisc/ 10034F: Documentation/devicetree/bindings/openrisc/
10035F: Documentation/openrisc/ 10035F: Documentation/openrisc/
10036F: arch/openrisc/ 10036F: arch/openrisc/
10037F: drivers/irqchip/irq-ompic.c
10037F: drivers/irqchip/irq-or1k-* 10038F: drivers/irqchip/irq-or1k-*
10038 10039
10039OPENVSWITCH 10040OPENVSWITCH
diff --git a/arch/openrisc/Kconfig b/arch/openrisc/Kconfig
index b49acda5e8f4..34eb4e90f56c 100644
--- a/arch/openrisc/Kconfig
+++ b/arch/openrisc/Kconfig
@@ -30,6 +30,7 @@ config OPENRISC
30 select NO_BOOTMEM 30 select NO_BOOTMEM
31 select ARCH_USE_QUEUED_SPINLOCKS 31 select ARCH_USE_QUEUED_SPINLOCKS
32 select ARCH_USE_QUEUED_RWLOCKS 32 select ARCH_USE_QUEUED_RWLOCKS
33 select OMPIC if SMP
33 34
34config CPU_BIG_ENDIAN 35config CPU_BIG_ENDIAN
35 def_bool y 36 def_bool y
diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index 9d8a1dd2e2c2..a2ca82f6c2dd 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -151,6 +151,9 @@ config CLPS711X_IRQCHIP
151 select SPARSE_IRQ 151 select SPARSE_IRQ
152 default y 152 default y
153 153
154config OMPIC
155 bool
156
154config OR1K_PIC 157config OR1K_PIC
155 bool 158 bool
156 select IRQ_DOMAIN 159 select IRQ_DOMAIN
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index 845abc107ad5..771f8e7f46f8 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -17,6 +17,7 @@ obj-$(CONFIG_DW_APB_ICTL) += irq-dw-apb-ictl.o
17obj-$(CONFIG_METAG) += irq-metag-ext.o 17obj-$(CONFIG_METAG) += irq-metag-ext.o
18obj-$(CONFIG_METAG_PERFCOUNTER_IRQS) += irq-metag.o 18obj-$(CONFIG_METAG_PERFCOUNTER_IRQS) += irq-metag.o
19obj-$(CONFIG_CLPS711X_IRQCHIP) += irq-clps711x.o 19obj-$(CONFIG_CLPS711X_IRQCHIP) += irq-clps711x.o
20obj-$(CONFIG_OMPIC) += irq-ompic.o
20obj-$(CONFIG_OR1K_PIC) += irq-or1k-pic.o 21obj-$(CONFIG_OR1K_PIC) += irq-or1k-pic.o
21obj-$(CONFIG_ORION_IRQCHIP) += irq-orion.o 22obj-$(CONFIG_ORION_IRQCHIP) += irq-orion.o
22obj-$(CONFIG_OMAP_IRQCHIP) += irq-omap-intc.o 23obj-$(CONFIG_OMAP_IRQCHIP) += irq-omap-intc.o
diff --git a/drivers/irqchip/irq-ompic.c b/drivers/irqchip/irq-ompic.c
new file mode 100644
index 000000000000..cf6d0c455518
--- /dev/null
+++ b/drivers/irqchip/irq-ompic.c
@@ -0,0 +1,202 @@
1/*
2 * Open Multi-Processor Interrupt Controller driver
3 *
4 * Copyright (C) 2014 Stefan Kristiansson <stefan.kristiansson@saunalahti.fi>
5 * Copyright (C) 2017 Stafford Horne <shorne@gmail.com>
6 *
7 * This file is licensed under the terms of the GNU General Public License
8 * version 2. This program is licensed "as is" without any warranty of any
9 * kind, whether express or implied.
10 *
11 * The ompic device handles IPI communication between cores in multi-core
12 * OpenRISC systems.
13 *
14 * Registers
15 *
16 * For each CPU the ompic has 2 registers. The control register for sending
17 * and acking IPIs and the status register for receiving IPIs. The register
18 * layouts are as follows:
19 *
20 * Control register
21 * +---------+---------+----------+---------+
22 * | 31 | 30 | 29 .. 16 | 15 .. 0 |
23 * ----------+---------+----------+----------
24 * | IRQ ACK | IRQ GEN | DST CORE | DATA |
25 * +---------+---------+----------+---------+
26 *
27 * Status register
28 * +----------+-------------+----------+---------+
29 * | 31 | 30 | 29 .. 16 | 15 .. 0 |
30 * -----------+-------------+----------+---------+
31 * | Reserved | IRQ Pending | SRC CORE | DATA |
32 * +----------+-------------+----------+---------+
33 *
34 * Architecture
35 *
36 * - The ompic generates a level interrupt to the CPU PIC when a message is
37 * ready. Messages are delivered via the memory bus.
38 * - The ompic does not have any interrupt input lines.
39 * - The ompic is wired to the same irq line on each core.
40 * - Devices are wired to the same irq line on each core.
41 *
42 * +---------+ +---------+
43 * | CPU | | CPU |
44 * | Core 0 |<==\ (memory access) /==>| Core 1 |
45 * | [ PIC ]| | | | [ PIC ]|
46 * +----^-^--+ | | +----^-^--+
47 * | | v v | |
48 * <====|=|=================================|=|==> (memory bus)
49 * | | ^ ^ | |
50 * (ipi | +------|---------+--------|-------|-+ (device irq)
51 * irq | | | | |
52 * core0)| +------|---------|--------|-------+ (ipi irq core1)
53 * | | | | |
54 * +----o-o-+ | +--------+ |
55 * | ompic |<===/ | Device |<===/
56 * | IPI | +--------+
57 * +--------+*
58 *
59 */
60
61#include <linux/io.h>
62#include <linux/ioport.h>
63#include <linux/interrupt.h>
64#include <linux/smp.h>
65#include <linux/of.h>
66#include <linux/of_irq.h>
67#include <linux/of_address.h>
68
69#include <linux/irqchip.h>
70
71#define OMPIC_CPUBYTES 8
72#define OMPIC_CTRL(cpu) (0x0 + (cpu * OMPIC_CPUBYTES))
73#define OMPIC_STAT(cpu) (0x4 + (cpu * OMPIC_CPUBYTES))
74
75#define OMPIC_CTRL_IRQ_ACK (1 << 31)
76#define OMPIC_CTRL_IRQ_GEN (1 << 30)
77#define OMPIC_CTRL_DST(cpu) (((cpu) & 0x3fff) << 16)
78
79#define OMPIC_STAT_IRQ_PENDING (1 << 30)
80
81#define OMPIC_DATA(x) ((x) & 0xffff)
82
83DEFINE_PER_CPU(unsigned long, ops);
84
85static void __iomem *ompic_base;
86
87static inline u32 ompic_readreg(void __iomem *base, loff_t offset)
88{
89 return ioread32be(base + offset);
90}
91
92static void ompic_writereg(void __iomem *base, loff_t offset, u32 data)
93{
94 iowrite32be(data, base + offset);
95}
96
97static void ompic_raise_softirq(const struct cpumask *mask,
98 unsigned int ipi_msg)
99{
100 unsigned int dst_cpu;
101 unsigned int src_cpu = smp_processor_id();
102
103 for_each_cpu(dst_cpu, mask) {
104 set_bit(ipi_msg, &per_cpu(ops, dst_cpu));
105
106 /*
107 * On OpenRISC the atomic set_bit() call implies a memory
108 * barrier. Otherwise we would need: smp_wmb(); paired
109 * with the read in ompic_ipi_handler.
110 */
111
112 ompic_writereg(ompic_base, OMPIC_CTRL(src_cpu),
113 OMPIC_CTRL_IRQ_GEN |
114 OMPIC_CTRL_DST(dst_cpu) |
115 OMPIC_DATA(1));
116 }
117}
118
119static irqreturn_t ompic_ipi_handler(int irq, void *dev_id)
120{
121 unsigned int cpu = smp_processor_id();
122 unsigned long *pending_ops = &per_cpu(ops, cpu);
123 unsigned long ops;
124
125 ompic_writereg(ompic_base, OMPIC_CTRL(cpu), OMPIC_CTRL_IRQ_ACK);
126 while ((ops = xchg(pending_ops, 0)) != 0) {
127
128 /*
129 * On OpenRISC the atomic xchg() call implies a memory
130 * barrier. Otherwise we may need an smp_rmb(); paired
131 * with the write in ompic_raise_softirq.
132 */
133
134 do {
135 unsigned long ipi_msg;
136
137 ipi_msg = __ffs(ops);
138 ops &= ~(1UL << ipi_msg);
139
140 handle_IPI(ipi_msg);
141 } while (ops);
142 }
143
144 return IRQ_HANDLED;
145}
146
147static int __init ompic_of_init(struct device_node *node,
148 struct device_node *parent)
149{
150 struct resource res;
151 int irq;
152 int ret;
153
154 /* Validate the DT */
155 if (ompic_base) {
156 pr_err("ompic: duplicate ompic's are not supported");
157 return -EEXIST;
158 }
159
160 if (of_address_to_resource(node, 0, &res)) {
161 pr_err("ompic: reg property requires an address and size");
162 return -EINVAL;
163 }
164
165 if (resource_size(&res) < (num_possible_cpus() * OMPIC_CPUBYTES)) {
166 pr_err("ompic: reg size, currently %d must be at least %d",
167 resource_size(&res),
168 (num_possible_cpus() * OMPIC_CPUBYTES));
169 return -EINVAL;
170 }
171
172 /* Setup the device */
173 ompic_base = ioremap(res.start, resource_size(&res));
174 if (IS_ERR(ompic_base)) {
175 pr_err("ompic: unable to map registers");
176 return PTR_ERR(ompic_base);
177 }
178
179 irq = irq_of_parse_and_map(node, 0);
180 if (irq <= 0) {
181 pr_err("ompic: unable to parse device irq");
182 ret = -EINVAL;
183 goto out_unmap;
184 }
185
186 ret = request_irq(irq, ompic_ipi_handler, IRQF_PERCPU,
187 "ompic_ipi", NULL);
188 if (ret)
189 goto out_irq_disp;
190
191 set_smp_cross_call(ompic_raise_softirq);
192
193 return 0;
194
195out_irq_disp:
196 irq_dispose_mapping(irq);
197out_unmap:
198 iounmap(ompic_base);
199 ompic_base = NULL;
200 return ret;
201}
202IRQCHIP_DECLARE(ompic, "openrisc,ompic", ompic_of_init);