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 | |
83 | DEFINE_PER_CPU(unsigned long, ops); |
84 | |
85 | static void __iomem *ompic_base; |
86 | |
87 | static inline u32 ompic_readreg(void __iomem *base, loff_t offset) |
88 | { |
89 | return ioread32be(base + offset); |
90 | } |
91 | |
92 | static void ompic_writereg(void __iomem *base, loff_t offset, u32 data) |
93 | { |
94 | iowrite32be(data, base + offset); |
95 | } |
96 | |
97 | static 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(nr: ipi_msg, addr: &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(base: ompic_base, OMPIC_CTRL(src_cpu), |
113 | OMPIC_CTRL_IRQ_GEN | |
114 | OMPIC_CTRL_DST(dst_cpu) | |
115 | OMPIC_DATA(1)); |
116 | } |
117 | } |
118 | |
119 | static 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(base: 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 | |
147 | static 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(dev: node, index: 0, r: &res)) { |
161 | pr_err("ompic: reg property requires an address and size" ); |
162 | return -EINVAL; |
163 | } |
164 | |
165 | if (resource_size(res: &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(offset: res.start, size: resource_size(res: &res)); |
174 | if (!ompic_base) { |
175 | pr_err("ompic: unable to map registers" ); |
176 | return -ENOMEM; |
177 | } |
178 | |
179 | irq = irq_of_parse_and_map(node, index: 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, handler: ompic_ipi_handler, IRQF_PERCPU, |
187 | name: "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 | |
195 | out_irq_disp: |
196 | irq_dispose_mapping(virq: irq); |
197 | out_unmap: |
198 | iounmap(addr: ompic_base); |
199 | ompic_base = NULL; |
200 | return ret; |
201 | } |
202 | IRQCHIP_DECLARE(ompic, "openrisc,ompic" , ompic_of_init); |
203 | |