]>
Commit | Line | Data |
---|---|---|
0b05ac6e BH |
1 | /* |
2 | * Copyright 2011 IBM Corporation. | |
3 | * | |
4 | * This program is free software; you can redistribute it and/or | |
5 | * modify it under the terms of the GNU General Public License | |
6 | * as published by the Free Software Foundation; either version | |
7 | * 2 of the License, or (at your option) any later version. | |
8 | * | |
9 | */ | |
a1d0d98d | 10 | |
0b05ac6e BH |
11 | #include <linux/types.h> |
12 | #include <linux/kernel.h> | |
13 | #include <linux/irq.h> | |
14 | #include <linux/smp.h> | |
15 | #include <linux/interrupt.h> | |
16 | #include <linux/init.h> | |
17 | #include <linux/cpu.h> | |
18 | #include <linux/of.h> | |
19 | #include <linux/spinlock.h> | |
371fefd6 | 20 | #include <linux/module.h> |
0b05ac6e BH |
21 | |
22 | #include <asm/prom.h> | |
23 | #include <asm/io.h> | |
24 | #include <asm/smp.h> | |
25 | #include <asm/irq.h> | |
26 | #include <asm/errno.h> | |
27 | #include <asm/xics.h> | |
371fefd6 | 28 | #include <asm/kvm_ppc.h> |
0b05ac6e BH |
29 | |
30 | struct icp_ipl { | |
31 | union { | |
32 | u32 word; | |
33 | u8 bytes[4]; | |
34 | } xirr_poll; | |
35 | union { | |
36 | u32 word; | |
37 | u8 bytes[4]; | |
38 | } xirr; | |
39 | u32 dummy; | |
40 | union { | |
41 | u32 word; | |
42 | u8 bytes[4]; | |
43 | } qirr; | |
44 | u32 link_a; | |
45 | u32 link_b; | |
46 | u32 link_c; | |
47 | }; | |
48 | ||
49 | static struct icp_ipl __iomem *icp_native_regs[NR_CPUS]; | |
50 | ||
51 | static inline unsigned int icp_native_get_xirr(void) | |
52 | { | |
53 | int cpu = smp_processor_id(); | |
54695c30 BH |
54 | unsigned int xirr; |
55 | ||
56 | /* Handled an interrupt latched by KVM */ | |
57 | xirr = kvmppc_get_xics_latch(); | |
58 | if (xirr) | |
59 | return xirr; | |
0b05ac6e BH |
60 | |
61 | return in_be32(&icp_native_regs[cpu]->xirr.word); | |
62 | } | |
63 | ||
64 | static inline void icp_native_set_xirr(unsigned int value) | |
65 | { | |
66 | int cpu = smp_processor_id(); | |
67 | ||
68 | out_be32(&icp_native_regs[cpu]->xirr.word, value); | |
69 | } | |
70 | ||
71 | static inline void icp_native_set_cppr(u8 value) | |
72 | { | |
73 | int cpu = smp_processor_id(); | |
74 | ||
75 | out_8(&icp_native_regs[cpu]->xirr.bytes[0], value); | |
76 | } | |
77 | ||
78 | static inline void icp_native_set_qirr(int n_cpu, u8 value) | |
79 | { | |
80 | out_8(&icp_native_regs[n_cpu]->qirr.bytes[0], value); | |
81 | } | |
82 | ||
83 | static void icp_native_set_cpu_priority(unsigned char cppr) | |
84 | { | |
85 | xics_set_base_cppr(cppr); | |
86 | icp_native_set_cppr(cppr); | |
87 | iosync(); | |
88 | } | |
89 | ||
90 | static void icp_native_eoi(struct irq_data *d) | |
91 | { | |
476eb491 | 92 | unsigned int hw_irq = (unsigned int)irqd_to_hwirq(d); |
0b05ac6e BH |
93 | |
94 | iosync(); | |
95 | icp_native_set_xirr((xics_pop_cppr() << 24) | hw_irq); | |
96 | } | |
97 | ||
98 | static void icp_native_teardown_cpu(void) | |
99 | { | |
100 | int cpu = smp_processor_id(); | |
101 | ||
102 | /* Clear any pending IPI */ | |
103 | icp_native_set_qirr(cpu, 0xff); | |
104 | } | |
105 | ||
106 | static void icp_native_flush_ipi(void) | |
107 | { | |
108 | /* We take the ipi irq but and never return so we | |
109 | * need to EOI the IPI, but want to leave our priority 0 | |
110 | * | |
111 | * should we check all the other interrupts too? | |
112 | * should we be flagging idle loop instead? | |
113 | * or creating some task to be scheduled? | |
114 | */ | |
115 | ||
116 | icp_native_set_xirr((0x00 << 24) | XICS_IPI); | |
117 | } | |
118 | ||
119 | static unsigned int icp_native_get_irq(void) | |
120 | { | |
121 | unsigned int xirr = icp_native_get_xirr(); | |
122 | unsigned int vec = xirr & 0x00ffffff; | |
123 | unsigned int irq; | |
124 | ||
125 | if (vec == XICS_IRQ_SPURIOUS) | |
126 | return NO_IRQ; | |
127 | ||
d6b0d1f7 | 128 | irq = irq_find_mapping(xics_host, vec); |
0b05ac6e BH |
129 | if (likely(irq != NO_IRQ)) { |
130 | xics_push_cppr(vec); | |
131 | return irq; | |
132 | } | |
133 | ||
134 | /* We don't have a linux mapping, so have rtas mask it. */ | |
135 | xics_mask_unknown_vec(vec); | |
136 | ||
137 | /* We might learn about it later, so EOI it */ | |
138 | icp_native_set_xirr(xirr); | |
139 | ||
140 | return NO_IRQ; | |
141 | } | |
142 | ||
143 | #ifdef CONFIG_SMP | |
144 | ||
23d72bfd | 145 | static void icp_native_cause_ipi(int cpu, unsigned long data) |
0b05ac6e | 146 | { |
54695c30 | 147 | kvmppc_set_host_ipi(cpu, 1); |
0b05ac6e BH |
148 | icp_native_set_qirr(cpu, IPI_PRIORITY); |
149 | } | |
150 | ||
371fefd6 PM |
151 | void xics_wake_cpu(int cpu) |
152 | { | |
153 | icp_native_set_qirr(cpu, IPI_PRIORITY); | |
154 | } | |
155 | EXPORT_SYMBOL_GPL(xics_wake_cpu); | |
156 | ||
0b05ac6e BH |
157 | static irqreturn_t icp_native_ipi_action(int irq, void *dev_id) |
158 | { | |
159 | int cpu = smp_processor_id(); | |
160 | ||
54695c30 | 161 | kvmppc_set_host_ipi(cpu, 0); |
0b05ac6e BH |
162 | icp_native_set_qirr(cpu, 0xff); |
163 | ||
23d72bfd | 164 | return smp_ipi_demux(); |
0b05ac6e BH |
165 | } |
166 | ||
167 | #endif /* CONFIG_SMP */ | |
168 | ||
169 | static int __init icp_native_map_one_cpu(int hw_id, unsigned long addr, | |
170 | unsigned long size) | |
171 | { | |
172 | char *rname; | |
173 | int i, cpu = -1; | |
174 | ||
175 | /* This may look gross but it's good enough for now, we don't quite | |
176 | * have a hard -> linux processor id matching. | |
177 | */ | |
178 | for_each_possible_cpu(i) { | |
179 | if (!cpu_present(i)) | |
180 | continue; | |
181 | if (hw_id == get_hard_smp_processor_id(i)) { | |
182 | cpu = i; | |
183 | break; | |
184 | } | |
185 | } | |
186 | ||
187 | /* Fail, skip that CPU. Don't print, it's normal, some XICS come up | |
188 | * with way more entries in there than you have CPUs | |
189 | */ | |
190 | if (cpu == -1) | |
191 | return 0; | |
192 | ||
193 | rname = kasprintf(GFP_KERNEL, "CPU %d [0x%x] Interrupt Presentation", | |
194 | cpu, hw_id); | |
195 | ||
196 | if (!request_mem_region(addr, size, rname)) { | |
197 | pr_warning("icp_native: Could not reserve ICP MMIO" | |
198 | " for CPU %d, interrupt server #0x%x\n", | |
199 | cpu, hw_id); | |
200 | return -EBUSY; | |
201 | } | |
202 | ||
203 | icp_native_regs[cpu] = ioremap(addr, size); | |
371fefd6 | 204 | kvmppc_set_xics_phys(cpu, addr); |
0b05ac6e BH |
205 | if (!icp_native_regs[cpu]) { |
206 | pr_warning("icp_native: Failed ioremap for CPU %d, " | |
207 | "interrupt server #0x%x, addr %#lx\n", | |
208 | cpu, hw_id, addr); | |
209 | release_mem_region(addr, size); | |
210 | return -ENOMEM; | |
211 | } | |
212 | return 0; | |
213 | } | |
214 | ||
215 | static int __init icp_native_init_one_node(struct device_node *np, | |
216 | unsigned int *indx) | |
217 | { | |
218 | unsigned int ilen; | |
219 | const u32 *ireg; | |
220 | int i; | |
221 | int reg_tuple_size; | |
222 | int num_servers = 0; | |
223 | ||
224 | /* This code does the theorically broken assumption that the interrupt | |
225 | * server numbers are the same as the hard CPU numbers. | |
226 | * This happens to be the case so far but we are playing with fire... | |
227 | * should be fixed one of these days. -BenH. | |
228 | */ | |
229 | ireg = of_get_property(np, "ibm,interrupt-server-ranges", &ilen); | |
230 | ||
231 | /* Do that ever happen ? we'll know soon enough... but even good'old | |
232 | * f80 does have that property .. | |
233 | */ | |
234 | WARN_ON((ireg == NULL) || (ilen != 2*sizeof(u32))); | |
235 | ||
236 | if (ireg) { | |
237 | *indx = of_read_number(ireg, 1); | |
238 | if (ilen >= 2*sizeof(u32)) | |
239 | num_servers = of_read_number(ireg + 1, 1); | |
240 | } | |
241 | ||
242 | ireg = of_get_property(np, "reg", &ilen); | |
243 | if (!ireg) { | |
244 | pr_err("icp_native: Can't find interrupt reg property"); | |
245 | return -1; | |
246 | } | |
247 | ||
248 | reg_tuple_size = (of_n_addr_cells(np) + of_n_size_cells(np)) * 4; | |
249 | if (((ilen % reg_tuple_size) != 0) | |
250 | || (num_servers && (num_servers != (ilen / reg_tuple_size)))) { | |
251 | pr_err("icp_native: ICP reg len (%d) != num servers (%d)", | |
252 | ilen / reg_tuple_size, num_servers); | |
253 | return -1; | |
254 | } | |
255 | ||
256 | for (i = 0; i < (ilen / reg_tuple_size); i++) { | |
257 | struct resource r; | |
258 | int err; | |
259 | ||
260 | err = of_address_to_resource(np, i, &r); | |
261 | if (err) { | |
262 | pr_err("icp_native: Could not translate ICP MMIO" | |
263 | " for interrupt server 0x%x (%d)\n", *indx, err); | |
264 | return -1; | |
265 | } | |
266 | ||
28f65c11 | 267 | if (icp_native_map_one_cpu(*indx, r.start, resource_size(&r))) |
0b05ac6e BH |
268 | return -1; |
269 | ||
270 | (*indx)++; | |
271 | } | |
272 | return 0; | |
273 | } | |
274 | ||
275 | static const struct icp_ops icp_native_ops = { | |
276 | .get_irq = icp_native_get_irq, | |
277 | .eoi = icp_native_eoi, | |
278 | .set_priority = icp_native_set_cpu_priority, | |
279 | .teardown_cpu = icp_native_teardown_cpu, | |
280 | .flush_ipi = icp_native_flush_ipi, | |
281 | #ifdef CONFIG_SMP | |
282 | .ipi_action = icp_native_ipi_action, | |
23d72bfd | 283 | .cause_ipi = icp_native_cause_ipi, |
0b05ac6e BH |
284 | #endif |
285 | }; | |
286 | ||
cf01a404 | 287 | int __init icp_native_init(void) |
0b05ac6e BH |
288 | { |
289 | struct device_node *np; | |
290 | u32 indx = 0; | |
291 | int found = 0; | |
292 | ||
293 | for_each_compatible_node(np, NULL, "ibm,ppc-xicp") | |
294 | if (icp_native_init_one_node(np, &indx) == 0) | |
295 | found = 1; | |
296 | if (!found) { | |
297 | for_each_node_by_type(np, | |
298 | "PowerPC-External-Interrupt-Presentation") { | |
299 | if (icp_native_init_one_node(np, &indx) == 0) | |
300 | found = 1; | |
301 | } | |
302 | } | |
303 | ||
304 | if (found == 0) | |
305 | return -ENODEV; | |
306 | ||
307 | icp_ops = &icp_native_ops; | |
308 | ||
309 | return 0; | |
310 | } |