Commit | Line | Data |
---|---|---|
9b54470a SH |
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(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 | ||
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(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(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)); | |
404e6bea | 174 | if (!ompic_base) { |
9b54470a | 175 | pr_err("ompic: unable to map registers"); |
404e6bea | 176 | return -ENOMEM; |
9b54470a SH |
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 | ||
195 | out_irq_disp: | |
196 | irq_dispose_mapping(irq); | |
197 | out_unmap: | |
198 | iounmap(ompic_base); | |
199 | ompic_base = NULL; | |
200 | return ret; | |
201 | } | |
202 | IRQCHIP_DECLARE(ompic, "openrisc,ompic", ompic_of_init); |