diff options
Diffstat (limited to 'arch/powerpc/platforms/ps3/interrupt.c')
-rw-r--r-- | arch/powerpc/platforms/ps3/interrupt.c | 442 |
1 files changed, 245 insertions, 197 deletions
diff --git a/arch/powerpc/platforms/ps3/interrupt.c b/arch/powerpc/platforms/ps3/interrupt.c index 6f5de438b980..bb17283275aa 100644 --- a/arch/powerpc/platforms/ps3/interrupt.c +++ b/arch/powerpc/platforms/ps3/interrupt.c | |||
@@ -24,7 +24,6 @@ | |||
24 | 24 | ||
25 | #include <asm/machdep.h> | 25 | #include <asm/machdep.h> |
26 | #include <asm/udbg.h> | 26 | #include <asm/udbg.h> |
27 | #include <asm/ps3.h> | ||
28 | #include <asm/lv1call.h> | 27 | #include <asm/lv1call.h> |
29 | 28 | ||
30 | #include "platform.h" | 29 | #include "platform.h" |
@@ -36,15 +35,148 @@ | |||
36 | #endif | 35 | #endif |
37 | 36 | ||
38 | /** | 37 | /** |
38 | * struct ps3_bmp - a per cpu irq status and mask bitmap structure | ||
39 | * @status: 256 bit status bitmap indexed by plug | ||
40 | * @unused_1: | ||
41 | * @mask: 256 bit mask bitmap indexed by plug | ||
42 | * @unused_2: | ||
43 | * @lock: | ||
44 | * @ipi_debug_brk_mask: | ||
45 | * | ||
46 | * The HV mantains per SMT thread mappings of HV outlet to HV plug on | ||
47 | * behalf of the guest. These mappings are implemented as 256 bit guest | ||
48 | * supplied bitmaps indexed by plug number. The addresses of the bitmaps | ||
49 | * are registered with the HV through lv1_configure_irq_state_bitmap(). | ||
50 | * The HV requires that the 512 bits of status + mask not cross a page | ||
51 | * boundary. PS3_BMP_MINALIGN is used to define this minimal 64 byte | ||
52 | * alignment. | ||
53 | * | ||
54 | * The HV supports 256 plugs per thread, assigned as {0..255}, for a total | ||
55 | * of 512 plugs supported on a processor. To simplify the logic this | ||
56 | * implementation equates HV plug value to Linux virq value, constrains each | ||
57 | * interrupt to have a system wide unique plug number, and limits the range | ||
58 | * of the plug values to map into the first dword of the bitmaps. This | ||
59 | * gives a usable range of plug values of {NUM_ISA_INTERRUPTS..63}. Note | ||
60 | * that there is no constraint on how many in this set an individual thread | ||
61 | * can acquire. | ||
62 | */ | ||
63 | |||
64 | #define PS3_BMP_MINALIGN 64 | ||
65 | |||
66 | struct ps3_bmp { | ||
67 | struct { | ||
68 | u64 status; | ||
69 | u64 unused_1[3]; | ||
70 | u64 mask; | ||
71 | u64 unused_2[3]; | ||
72 | }; | ||
73 | u64 ipi_debug_brk_mask; | ||
74 | spinlock_t lock; | ||
75 | }; | ||
76 | |||
77 | /** | ||
78 | * struct ps3_private - a per cpu data structure | ||
79 | * @bmp: ps3_bmp structure | ||
80 | * @node: HV logical_ppe_id | ||
81 | * @cpu: HV thread_id | ||
82 | */ | ||
83 | |||
84 | struct ps3_private { | ||
85 | struct ps3_bmp bmp __attribute__ ((aligned (PS3_BMP_MINALIGN))); | ||
86 | u64 node; | ||
87 | unsigned int cpu; | ||
88 | }; | ||
89 | |||
90 | static DEFINE_PER_CPU(struct ps3_private, ps3_private); | ||
91 | |||
92 | int ps3_alloc_irq(enum ps3_cpu_binding cpu, unsigned long outlet, | ||
93 | unsigned int *virq) | ||
94 | { | ||
95 | int result; | ||
96 | struct ps3_private *pd; | ||
97 | |||
98 | /* This defines the default interrupt distribution policy. */ | ||
99 | |||
100 | if (cpu == PS3_BINDING_CPU_ANY) | ||
101 | cpu = 0; | ||
102 | |||
103 | pd = &per_cpu(ps3_private, cpu); | ||
104 | |||
105 | *virq = irq_create_mapping(NULL, outlet); | ||
106 | |||
107 | if (*virq == NO_IRQ) { | ||
108 | pr_debug("%s:%d: irq_create_mapping failed: outlet %lu\n", | ||
109 | __func__, __LINE__, outlet); | ||
110 | result = -ENOMEM; | ||
111 | goto fail_create; | ||
112 | } | ||
113 | |||
114 | /* Binds outlet to cpu + virq. */ | ||
115 | |||
116 | result = lv1_connect_irq_plug_ext(pd->node, pd->cpu, *virq, outlet, 0); | ||
117 | |||
118 | if (result) { | ||
119 | pr_info("%s:%d: lv1_connect_irq_plug_ext failed: %s\n", | ||
120 | __func__, __LINE__, ps3_result(result)); | ||
121 | result = -EPERM; | ||
122 | goto fail_connect; | ||
123 | } | ||
124 | |||
125 | pr_debug("%s:%d: outlet %lu => cpu %u, virq %u\n", __func__, __LINE__, | ||
126 | outlet, cpu, *virq); | ||
127 | |||
128 | result = set_irq_chip_data(*virq, pd); | ||
129 | |||
130 | if (result) { | ||
131 | pr_debug("%s:%d: set_irq_chip_data failed\n", | ||
132 | __func__, __LINE__); | ||
133 | goto fail_set; | ||
134 | } | ||
135 | |||
136 | return result; | ||
137 | |||
138 | fail_set: | ||
139 | lv1_disconnect_irq_plug_ext(pd->node, pd->cpu, *virq); | ||
140 | fail_connect: | ||
141 | irq_dispose_mapping(*virq); | ||
142 | fail_create: | ||
143 | return result; | ||
144 | } | ||
145 | EXPORT_SYMBOL_GPL(ps3_alloc_irq); | ||
146 | |||
147 | int ps3_free_irq(unsigned int virq) | ||
148 | { | ||
149 | int result; | ||
150 | const struct ps3_private *pd = get_irq_chip_data(virq); | ||
151 | |||
152 | pr_debug("%s:%d: node %lu, cpu %d, virq %u\n", __func__, __LINE__, | ||
153 | pd->node, pd->cpu, virq); | ||
154 | |||
155 | result = lv1_disconnect_irq_plug_ext(pd->node, pd->cpu, virq); | ||
156 | |||
157 | if (result) | ||
158 | pr_info("%s:%d: lv1_disconnect_irq_plug_ext failed: %s\n", | ||
159 | __func__, __LINE__, ps3_result(result)); | ||
160 | |||
161 | set_irq_chip_data(virq, NULL); | ||
162 | irq_dispose_mapping(virq); | ||
163 | return result; | ||
164 | } | ||
165 | EXPORT_SYMBOL_GPL(ps3_free_irq); | ||
166 | |||
167 | /** | ||
39 | * ps3_alloc_io_irq - Assign a virq to a system bus device. | 168 | * ps3_alloc_io_irq - Assign a virq to a system bus device. |
40 | * interrupt_id: The device interrupt id read from the system repository. | 169 | * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be |
170 | * serviced on. | ||
171 | * @interrupt_id: The device interrupt id read from the system repository. | ||
41 | * @virq: The assigned Linux virq. | 172 | * @virq: The assigned Linux virq. |
42 | * | 173 | * |
43 | * An io irq represents a non-virtualized device interrupt. interrupt_id | 174 | * An io irq represents a non-virtualized device interrupt. interrupt_id |
44 | * coresponds to the interrupt number of the interrupt controller. | 175 | * coresponds to the interrupt number of the interrupt controller. |
45 | */ | 176 | */ |
46 | 177 | ||
47 | int ps3_alloc_io_irq(unsigned int interrupt_id, unsigned int *virq) | 178 | int ps3_alloc_io_irq(enum ps3_cpu_binding cpu, unsigned int interrupt_id, |
179 | unsigned int *virq) | ||
48 | { | 180 | { |
49 | int result; | 181 | int result; |
50 | unsigned long outlet; | 182 | unsigned long outlet; |
@@ -57,12 +189,10 @@ int ps3_alloc_io_irq(unsigned int interrupt_id, unsigned int *virq) | |||
57 | return result; | 189 | return result; |
58 | } | 190 | } |
59 | 191 | ||
60 | *virq = irq_create_mapping(NULL, outlet); | 192 | result = ps3_alloc_irq(cpu, outlet, virq); |
61 | 193 | BUG_ON(result); | |
62 | pr_debug("%s:%d: interrupt_id %u => outlet %lu, virq %u\n", | ||
63 | __func__, __LINE__, interrupt_id, outlet, *virq); | ||
64 | 194 | ||
65 | return 0; | 195 | return result; |
66 | } | 196 | } |
67 | 197 | ||
68 | int ps3_free_io_irq(unsigned int virq) | 198 | int ps3_free_io_irq(unsigned int virq) |
@@ -75,13 +205,15 @@ int ps3_free_io_irq(unsigned int virq) | |||
75 | pr_debug("%s:%d: lv1_destruct_io_irq_outlet failed: %s\n", | 205 | pr_debug("%s:%d: lv1_destruct_io_irq_outlet failed: %s\n", |
76 | __func__, __LINE__, ps3_result(result)); | 206 | __func__, __LINE__, ps3_result(result)); |
77 | 207 | ||
78 | irq_dispose_mapping(virq); | 208 | ps3_free_irq(virq); |
79 | 209 | ||
80 | return result; | 210 | return result; |
81 | } | 211 | } |
82 | 212 | ||
83 | /** | 213 | /** |
84 | * ps3_alloc_event_irq - Allocate a virq for use with a system event. | 214 | * ps3_alloc_event_irq - Allocate a virq for use with a system event. |
215 | * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be | ||
216 | * serviced on. | ||
85 | * @virq: The assigned Linux virq. | 217 | * @virq: The assigned Linux virq. |
86 | * | 218 | * |
87 | * The virq can be used with lv1_connect_interrupt_event_receive_port() to | 219 | * The virq can be used with lv1_connect_interrupt_event_receive_port() to |
@@ -89,7 +221,7 @@ int ps3_free_io_irq(unsigned int virq) | |||
89 | * events. | 221 | * events. |
90 | */ | 222 | */ |
91 | 223 | ||
92 | int ps3_alloc_event_irq(unsigned int *virq) | 224 | int ps3_alloc_event_irq(enum ps3_cpu_binding cpu, unsigned int *virq) |
93 | { | 225 | { |
94 | int result; | 226 | int result; |
95 | unsigned long outlet; | 227 | unsigned long outlet; |
@@ -103,12 +235,10 @@ int ps3_alloc_event_irq(unsigned int *virq) | |||
103 | return result; | 235 | return result; |
104 | } | 236 | } |
105 | 237 | ||
106 | *virq = irq_create_mapping(NULL, outlet); | 238 | result = ps3_alloc_irq(cpu, outlet, virq); |
107 | 239 | BUG_ON(result); | |
108 | pr_debug("%s:%d: outlet %lu, virq %u\n", __func__, __LINE__, outlet, | ||
109 | *virq); | ||
110 | 240 | ||
111 | return 0; | 241 | return result; |
112 | } | 242 | } |
113 | 243 | ||
114 | int ps3_free_event_irq(unsigned int virq) | 244 | int ps3_free_event_irq(unsigned int virq) |
@@ -123,7 +253,7 @@ int ps3_free_event_irq(unsigned int virq) | |||
123 | pr_debug("%s:%d: lv1_destruct_event_receive_port failed: %s\n", | 253 | pr_debug("%s:%d: lv1_destruct_event_receive_port failed: %s\n", |
124 | __func__, __LINE__, ps3_result(result)); | 254 | __func__, __LINE__, ps3_result(result)); |
125 | 255 | ||
126 | irq_dispose_mapping(virq); | 256 | ps3_free_irq(virq); |
127 | 257 | ||
128 | pr_debug(" <- %s:%d\n", __func__, __LINE__); | 258 | pr_debug(" <- %s:%d\n", __func__, __LINE__); |
129 | return result; | 259 | return result; |
@@ -136,6 +266,8 @@ int ps3_send_event_locally(unsigned int virq) | |||
136 | 266 | ||
137 | /** | 267 | /** |
138 | * ps3_connect_event_irq - Assign a virq to a system bus device. | 268 | * ps3_connect_event_irq - Assign a virq to a system bus device. |
269 | * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be | ||
270 | * serviced on. | ||
139 | * @did: The HV device identifier read from the system repository. | 271 | * @did: The HV device identifier read from the system repository. |
140 | * @interrupt_id: The device interrupt id read from the system repository. | 272 | * @interrupt_id: The device interrupt id read from the system repository. |
141 | * @virq: The assigned Linux virq. | 273 | * @virq: The assigned Linux virq. |
@@ -144,12 +276,13 @@ int ps3_send_event_locally(unsigned int virq) | |||
144 | * coresponds to the software interrupt number. | 276 | * coresponds to the software interrupt number. |
145 | */ | 277 | */ |
146 | 278 | ||
147 | int ps3_connect_event_irq(const struct ps3_device_id *did, | 279 | int ps3_connect_event_irq(enum ps3_cpu_binding cpu, |
148 | unsigned int interrupt_id, unsigned int *virq) | 280 | const struct ps3_device_id *did, unsigned int interrupt_id, |
281 | unsigned int *virq) | ||
149 | { | 282 | { |
150 | int result; | 283 | int result; |
151 | 284 | ||
152 | result = ps3_alloc_event_irq(virq); | 285 | result = ps3_alloc_event_irq(cpu, virq); |
153 | 286 | ||
154 | if (result) | 287 | if (result) |
155 | return result; | 288 | return result; |
@@ -196,6 +329,8 @@ int ps3_disconnect_event_irq(const struct ps3_device_id *did, | |||
196 | 329 | ||
197 | /** | 330 | /** |
198 | * ps3_alloc_vuart_irq - Configure the system virtual uart virq. | 331 | * ps3_alloc_vuart_irq - Configure the system virtual uart virq. |
332 | * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be | ||
333 | * serviced on. | ||
199 | * @virt_addr_bmp: The caller supplied virtual uart interrupt bitmap. | 334 | * @virt_addr_bmp: The caller supplied virtual uart interrupt bitmap. |
200 | * @virq: The assigned Linux virq. | 335 | * @virq: The assigned Linux virq. |
201 | * | 336 | * |
@@ -203,13 +338,14 @@ int ps3_disconnect_event_irq(const struct ps3_device_id *did, | |||
203 | * freeing the interrupt will return a wrong state error. | 338 | * freeing the interrupt will return a wrong state error. |
204 | */ | 339 | */ |
205 | 340 | ||
206 | int ps3_alloc_vuart_irq(void* virt_addr_bmp, unsigned int *virq) | 341 | int ps3_alloc_vuart_irq(enum ps3_cpu_binding cpu, void* virt_addr_bmp, |
342 | unsigned int *virq) | ||
207 | { | 343 | { |
208 | int result; | 344 | int result; |
209 | unsigned long outlet; | 345 | unsigned long outlet; |
210 | unsigned long lpar_addr; | 346 | u64 lpar_addr; |
211 | 347 | ||
212 | BUG_ON(!is_kernel_addr((unsigned long)virt_addr_bmp)); | 348 | BUG_ON(!is_kernel_addr((u64)virt_addr_bmp)); |
213 | 349 | ||
214 | lpar_addr = ps3_mm_phys_to_lpar(__pa(virt_addr_bmp)); | 350 | lpar_addr = ps3_mm_phys_to_lpar(__pa(virt_addr_bmp)); |
215 | 351 | ||
@@ -221,12 +357,10 @@ int ps3_alloc_vuart_irq(void* virt_addr_bmp, unsigned int *virq) | |||
221 | return result; | 357 | return result; |
222 | } | 358 | } |
223 | 359 | ||
224 | *virq = irq_create_mapping(NULL, outlet); | 360 | result = ps3_alloc_irq(cpu, outlet, virq); |
225 | 361 | BUG_ON(result); | |
226 | pr_debug("%s:%d: outlet %lu, virq %u\n", __func__, __LINE__, | ||
227 | outlet, *virq); | ||
228 | 362 | ||
229 | return 0; | 363 | return result; |
230 | } | 364 | } |
231 | 365 | ||
232 | int ps3_free_vuart_irq(unsigned int virq) | 366 | int ps3_free_vuart_irq(unsigned int virq) |
@@ -241,21 +375,23 @@ int ps3_free_vuart_irq(unsigned int virq) | |||
241 | return result; | 375 | return result; |
242 | } | 376 | } |
243 | 377 | ||
244 | irq_dispose_mapping(virq); | 378 | ps3_free_irq(virq); |
245 | 379 | ||
246 | return result; | 380 | return result; |
247 | } | 381 | } |
248 | 382 | ||
249 | /** | 383 | /** |
250 | * ps3_alloc_spe_irq - Configure an spe virq. | 384 | * ps3_alloc_spe_irq - Configure an spe virq. |
385 | * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be | ||
386 | * serviced on. | ||
251 | * @spe_id: The spe_id returned from lv1_construct_logical_spe(). | 387 | * @spe_id: The spe_id returned from lv1_construct_logical_spe(). |
252 | * @class: The spe interrupt class {0,1,2}. | 388 | * @class: The spe interrupt class {0,1,2}. |
253 | * @virq: The assigned Linux virq. | 389 | * @virq: The assigned Linux virq. |
254 | * | 390 | * |
255 | */ | 391 | */ |
256 | 392 | ||
257 | int ps3_alloc_spe_irq(unsigned long spe_id, unsigned int class, | 393 | int ps3_alloc_spe_irq(enum ps3_cpu_binding cpu, unsigned long spe_id, |
258 | unsigned int *virq) | 394 | unsigned int class, unsigned int *virq) |
259 | { | 395 | { |
260 | int result; | 396 | int result; |
261 | unsigned long outlet; | 397 | unsigned long outlet; |
@@ -270,73 +406,24 @@ int ps3_alloc_spe_irq(unsigned long spe_id, unsigned int class, | |||
270 | return result; | 406 | return result; |
271 | } | 407 | } |
272 | 408 | ||
273 | *virq = irq_create_mapping(NULL, outlet); | 409 | result = ps3_alloc_irq(cpu, outlet, virq); |
274 | 410 | BUG_ON(result); | |
275 | pr_debug("%s:%d: spe_id %lu, class %u, outlet %lu, virq %u\n", | ||
276 | __func__, __LINE__, spe_id, class, outlet, *virq); | ||
277 | 411 | ||
278 | return 0; | 412 | return result; |
279 | } | 413 | } |
280 | 414 | ||
281 | int ps3_free_spe_irq(unsigned int virq) | 415 | int ps3_free_spe_irq(unsigned int virq) |
282 | { | 416 | { |
283 | irq_dispose_mapping(virq); | 417 | ps3_free_irq(virq); |
284 | return 0; | 418 | return 0; |
285 | } | 419 | } |
286 | 420 | ||
421 | |||
287 | #define PS3_INVALID_OUTLET ((irq_hw_number_t)-1) | 422 | #define PS3_INVALID_OUTLET ((irq_hw_number_t)-1) |
288 | #define PS3_PLUG_MAX 63 | 423 | #define PS3_PLUG_MAX 63 |
289 | 424 | ||
290 | /** | ||
291 | * struct bmp - a per cpu irq status and mask bitmap structure | ||
292 | * @status: 256 bit status bitmap indexed by plug | ||
293 | * @unused_1: | ||
294 | * @mask: 256 bit mask bitmap indexed by plug | ||
295 | * @unused_2: | ||
296 | * @lock: | ||
297 | * @ipi_debug_brk_mask: | ||
298 | * | ||
299 | * The HV mantains per SMT thread mappings of HV outlet to HV plug on | ||
300 | * behalf of the guest. These mappings are implemented as 256 bit guest | ||
301 | * supplied bitmaps indexed by plug number. The address of the bitmaps are | ||
302 | * registered with the HV through lv1_configure_irq_state_bitmap(). | ||
303 | * | ||
304 | * The HV supports 256 plugs per thread, assigned as {0..255}, for a total | ||
305 | * of 512 plugs supported on a processor. To simplify the logic this | ||
306 | * implementation equates HV plug value to linux virq value, constrains each | ||
307 | * interrupt to have a system wide unique plug number, and limits the range | ||
308 | * of the plug values to map into the first dword of the bitmaps. This | ||
309 | * gives a usable range of plug values of {NUM_ISA_INTERRUPTS..63}. Note | ||
310 | * that there is no constraint on how many in this set an individual thread | ||
311 | * can aquire. | ||
312 | */ | ||
313 | |||
314 | struct bmp { | ||
315 | struct { | ||
316 | unsigned long status; | ||
317 | unsigned long unused_1[3]; | ||
318 | unsigned long mask; | ||
319 | unsigned long unused_2[3]; | ||
320 | } __attribute__ ((packed)); | ||
321 | spinlock_t lock; | ||
322 | unsigned long ipi_debug_brk_mask; | ||
323 | }; | ||
324 | |||
325 | /** | ||
326 | * struct private - a per cpu data structure | ||
327 | * @node: HV node id | ||
328 | * @cpu: HV thread id | ||
329 | * @bmp: an HV bmp structure | ||
330 | */ | ||
331 | |||
332 | struct private { | ||
333 | unsigned long node; | ||
334 | unsigned int cpu; | ||
335 | struct bmp bmp; | ||
336 | }; | ||
337 | |||
338 | #if defined(DEBUG) | 425 | #if defined(DEBUG) |
339 | static void _dump_64_bmp(const char *header, const unsigned long *p, unsigned cpu, | 426 | static void _dump_64_bmp(const char *header, const u64 *p, unsigned cpu, |
340 | const char* func, int line) | 427 | const char* func, int line) |
341 | { | 428 | { |
342 | pr_debug("%s:%d: %s %u {%04lx_%04lx_%04lx_%04lx}\n", | 429 | pr_debug("%s:%d: %s %u {%04lx_%04lx_%04lx_%04lx}\n", |
@@ -346,14 +433,14 @@ static void _dump_64_bmp(const char *header, const unsigned long *p, unsigned cp | |||
346 | } | 433 | } |
347 | 434 | ||
348 | static void __attribute__ ((unused)) _dump_256_bmp(const char *header, | 435 | static void __attribute__ ((unused)) _dump_256_bmp(const char *header, |
349 | const unsigned long *p, unsigned cpu, const char* func, int line) | 436 | const u64 *p, unsigned cpu, const char* func, int line) |
350 | { | 437 | { |
351 | pr_debug("%s:%d: %s %u {%016lx:%016lx:%016lx:%016lx}\n", | 438 | pr_debug("%s:%d: %s %u {%016lx:%016lx:%016lx:%016lx}\n", |
352 | func, line, header, cpu, p[0], p[1], p[2], p[3]); | 439 | func, line, header, cpu, p[0], p[1], p[2], p[3]); |
353 | } | 440 | } |
354 | 441 | ||
355 | #define dump_bmp(_x) _dump_bmp(_x, __func__, __LINE__) | 442 | #define dump_bmp(_x) _dump_bmp(_x, __func__, __LINE__) |
356 | static void _dump_bmp(struct private* pd, const char* func, int line) | 443 | static void _dump_bmp(struct ps3_private* pd, const char* func, int line) |
357 | { | 444 | { |
358 | unsigned long flags; | 445 | unsigned long flags; |
359 | 446 | ||
@@ -364,7 +451,7 @@ static void _dump_bmp(struct private* pd, const char* func, int line) | |||
364 | } | 451 | } |
365 | 452 | ||
366 | #define dump_mask(_x) _dump_mask(_x, __func__, __LINE__) | 453 | #define dump_mask(_x) _dump_mask(_x, __func__, __LINE__) |
367 | static void __attribute__ ((unused)) _dump_mask(struct private* pd, | 454 | static void __attribute__ ((unused)) _dump_mask(struct ps3_private* pd, |
368 | const char* func, int line) | 455 | const char* func, int line) |
369 | { | 456 | { |
370 | unsigned long flags; | 457 | unsigned long flags; |
@@ -374,109 +461,94 @@ static void __attribute__ ((unused)) _dump_mask(struct private* pd, | |||
374 | spin_unlock_irqrestore(&pd->bmp.lock, flags); | 461 | spin_unlock_irqrestore(&pd->bmp.lock, flags); |
375 | } | 462 | } |
376 | #else | 463 | #else |
377 | static void dump_bmp(struct private* pd) {}; | 464 | static void dump_bmp(struct ps3_private* pd) {}; |
378 | #endif /* defined(DEBUG) */ | 465 | #endif /* defined(DEBUG) */ |
379 | 466 | ||
380 | static void chip_mask(unsigned int virq) | 467 | static void ps3_chip_mask(unsigned int virq) |
381 | { | 468 | { |
469 | struct ps3_private *pd = get_irq_chip_data(virq); | ||
470 | u64 bit = 0x8000000000000000UL >> virq; | ||
471 | u64 *p = &pd->bmp.mask; | ||
472 | u64 old; | ||
382 | unsigned long flags; | 473 | unsigned long flags; |
383 | struct private *pd = get_irq_chip_data(virq); | ||
384 | 474 | ||
385 | pr_debug("%s:%d: cpu %u, virq %d\n", __func__, __LINE__, pd->cpu, virq); | 475 | pr_debug("%s:%d: cpu %u, virq %d\n", __func__, __LINE__, pd->cpu, virq); |
386 | 476 | ||
387 | BUG_ON(virq < NUM_ISA_INTERRUPTS); | 477 | local_irq_save(flags); |
388 | BUG_ON(virq > PS3_PLUG_MAX); | 478 | asm volatile( |
389 | 479 | "1: ldarx %0,0,%3\n" | |
390 | spin_lock_irqsave(&pd->bmp.lock, flags); | 480 | "andc %0,%0,%2\n" |
391 | pd->bmp.mask &= ~(0x8000000000000000UL >> virq); | 481 | "stdcx. %0,0,%3\n" |
392 | spin_unlock_irqrestore(&pd->bmp.lock, flags); | 482 | "bne- 1b" |
483 | : "=&r" (old), "+m" (*p) | ||
484 | : "r" (bit), "r" (p) | ||
485 | : "cc" ); | ||
393 | 486 | ||
394 | lv1_did_update_interrupt_mask(pd->node, pd->cpu); | 487 | lv1_did_update_interrupt_mask(pd->node, pd->cpu); |
488 | local_irq_restore(flags); | ||
395 | } | 489 | } |
396 | 490 | ||
397 | static void chip_unmask(unsigned int virq) | 491 | static void ps3_chip_unmask(unsigned int virq) |
398 | { | 492 | { |
493 | struct ps3_private *pd = get_irq_chip_data(virq); | ||
494 | u64 bit = 0x8000000000000000UL >> virq; | ||
495 | u64 *p = &pd->bmp.mask; | ||
496 | u64 old; | ||
399 | unsigned long flags; | 497 | unsigned long flags; |
400 | struct private *pd = get_irq_chip_data(virq); | ||
401 | 498 | ||
402 | pr_debug("%s:%d: cpu %u, virq %d\n", __func__, __LINE__, pd->cpu, virq); | 499 | pr_debug("%s:%d: cpu %u, virq %d\n", __func__, __LINE__, pd->cpu, virq); |
403 | 500 | ||
404 | BUG_ON(virq < NUM_ISA_INTERRUPTS); | 501 | local_irq_save(flags); |
405 | BUG_ON(virq > PS3_PLUG_MAX); | 502 | asm volatile( |
406 | 503 | "1: ldarx %0,0,%3\n" | |
407 | spin_lock_irqsave(&pd->bmp.lock, flags); | 504 | "or %0,%0,%2\n" |
408 | pd->bmp.mask |= (0x8000000000000000UL >> virq); | 505 | "stdcx. %0,0,%3\n" |
409 | spin_unlock_irqrestore(&pd->bmp.lock, flags); | 506 | "bne- 1b" |
507 | : "=&r" (old), "+m" (*p) | ||
508 | : "r" (bit), "r" (p) | ||
509 | : "cc" ); | ||
410 | 510 | ||
411 | lv1_did_update_interrupt_mask(pd->node, pd->cpu); | 511 | lv1_did_update_interrupt_mask(pd->node, pd->cpu); |
512 | local_irq_restore(flags); | ||
412 | } | 513 | } |
413 | 514 | ||
414 | static void chip_eoi(unsigned int virq) | 515 | static void ps3_chip_eoi(unsigned int virq) |
415 | { | 516 | { |
416 | lv1_end_of_interrupt(virq); | 517 | const struct ps3_private *pd = get_irq_chip_data(virq); |
518 | lv1_end_of_interrupt_ext(pd->node, pd->cpu, virq); | ||
417 | } | 519 | } |
418 | 520 | ||
419 | static struct irq_chip irq_chip = { | 521 | static struct irq_chip irq_chip = { |
420 | .typename = "ps3", | 522 | .typename = "ps3", |
421 | .mask = chip_mask, | 523 | .mask = ps3_chip_mask, |
422 | .unmask = chip_unmask, | 524 | .unmask = ps3_chip_unmask, |
423 | .eoi = chip_eoi, | 525 | .eoi = ps3_chip_eoi, |
424 | }; | 526 | }; |
425 | 527 | ||
426 | static void host_unmap(struct irq_host *h, unsigned int virq) | 528 | static void ps3_host_unmap(struct irq_host *h, unsigned int virq) |
427 | { | 529 | { |
428 | int result; | 530 | set_irq_chip_data(virq, NULL); |
429 | |||
430 | pr_debug("%s:%d: virq %d\n", __func__, __LINE__, virq); | ||
431 | |||
432 | lv1_disconnect_irq_plug(virq); | ||
433 | |||
434 | result = set_irq_chip_data(virq, NULL); | ||
435 | BUG_ON(result); | ||
436 | } | 531 | } |
437 | 532 | ||
438 | static DEFINE_PER_CPU(struct private, private); | 533 | static int ps3_host_map(struct irq_host *h, unsigned int virq, |
439 | |||
440 | static int host_map(struct irq_host *h, unsigned int virq, | ||
441 | irq_hw_number_t hwirq) | 534 | irq_hw_number_t hwirq) |
442 | { | 535 | { |
443 | int result; | 536 | pr_debug("%s:%d: hwirq %lu, virq %u\n", __func__, __LINE__, hwirq, |
444 | unsigned int cpu; | ||
445 | |||
446 | pr_debug(" -> %s:%d\n", __func__, __LINE__); | ||
447 | pr_debug("%s:%d: hwirq %lu => virq %u\n", __func__, __LINE__, hwirq, | ||
448 | virq); | 537 | virq); |
449 | 538 | ||
450 | /* bind this virq to a cpu */ | ||
451 | |||
452 | preempt_disable(); | ||
453 | cpu = smp_processor_id(); | ||
454 | result = lv1_connect_irq_plug(virq, hwirq); | ||
455 | preempt_enable(); | ||
456 | |||
457 | if (result) { | ||
458 | pr_info("%s:%d: lv1_connect_irq_plug failed:" | ||
459 | " %s\n", __func__, __LINE__, ps3_result(result)); | ||
460 | return -EPERM; | ||
461 | } | ||
462 | |||
463 | result = set_irq_chip_data(virq, &per_cpu(private, cpu)); | ||
464 | BUG_ON(result); | ||
465 | |||
466 | set_irq_chip_and_handler(virq, &irq_chip, handle_fasteoi_irq); | 539 | set_irq_chip_and_handler(virq, &irq_chip, handle_fasteoi_irq); |
467 | 540 | ||
468 | pr_debug(" <- %s:%d\n", __func__, __LINE__); | 541 | return 0; |
469 | return result; | ||
470 | } | 542 | } |
471 | 543 | ||
472 | static struct irq_host_ops host_ops = { | 544 | static struct irq_host_ops ps3_host_ops = { |
473 | .map = host_map, | 545 | .map = ps3_host_map, |
474 | .unmap = host_unmap, | 546 | .unmap = ps3_host_unmap, |
475 | }; | 547 | }; |
476 | 548 | ||
477 | void __init ps3_register_ipi_debug_brk(unsigned int cpu, unsigned int virq) | 549 | void __init ps3_register_ipi_debug_brk(unsigned int cpu, unsigned int virq) |
478 | { | 550 | { |
479 | struct private *pd = &per_cpu(private, cpu); | 551 | struct ps3_private *pd = &per_cpu(ps3_private, cpu); |
480 | 552 | ||
481 | pd->bmp.ipi_debug_brk_mask = 0x8000000000000000UL >> virq; | 553 | pd->bmp.ipi_debug_brk_mask = 0x8000000000000000UL >> virq; |
482 | 554 | ||
@@ -484,57 +556,32 @@ void __init ps3_register_ipi_debug_brk(unsigned int cpu, unsigned int virq) | |||
484 | cpu, virq, pd->bmp.ipi_debug_brk_mask); | 556 | cpu, virq, pd->bmp.ipi_debug_brk_mask); |
485 | } | 557 | } |
486 | 558 | ||
487 | static int bmp_get_and_clear_status_bit(struct bmp *m) | 559 | unsigned int ps3_get_irq(void) |
488 | { | 560 | { |
489 | unsigned long flags; | 561 | struct ps3_private *pd = &__get_cpu_var(ps3_private); |
490 | unsigned int bit; | 562 | u64 x = (pd->bmp.status & pd->bmp.mask); |
491 | unsigned long x; | 563 | unsigned int plug; |
492 | |||
493 | spin_lock_irqsave(&m->lock, flags); | ||
494 | 564 | ||
495 | /* check for ipi break first to stop this cpu ASAP */ | 565 | /* check for ipi break first to stop this cpu ASAP */ |
496 | 566 | ||
497 | if (m->status & m->ipi_debug_brk_mask) { | 567 | if (x & pd->bmp.ipi_debug_brk_mask) |
498 | m->status &= ~m->ipi_debug_brk_mask; | 568 | x &= pd->bmp.ipi_debug_brk_mask; |
499 | spin_unlock_irqrestore(&m->lock, flags); | ||
500 | return __ilog2(m->ipi_debug_brk_mask); | ||
501 | } | ||
502 | |||
503 | x = (m->status & m->mask); | ||
504 | 569 | ||
505 | for (bit = NUM_ISA_INTERRUPTS, x <<= bit; x; bit++, x <<= 1) | 570 | asm volatile("cntlzd %0,%1" : "=r" (plug) : "r" (x)); |
506 | if (x & 0x8000000000000000UL) { | 571 | plug &= 0x3f; |
507 | m->status &= ~(0x8000000000000000UL >> bit); | ||
508 | spin_unlock_irqrestore(&m->lock, flags); | ||
509 | return bit; | ||
510 | } | ||
511 | 572 | ||
512 | spin_unlock_irqrestore(&m->lock, flags); | 573 | if (unlikely(plug) == NO_IRQ) { |
513 | |||
514 | pr_debug("%s:%d: not found\n", __func__, __LINE__); | ||
515 | return -1; | ||
516 | } | ||
517 | |||
518 | unsigned int ps3_get_irq(void) | ||
519 | { | ||
520 | int plug; | ||
521 | |||
522 | struct private *pd = &__get_cpu_var(private); | ||
523 | |||
524 | plug = bmp_get_and_clear_status_bit(&pd->bmp); | ||
525 | |||
526 | if (plug < 1) { | ||
527 | pr_debug("%s:%d: no plug found: cpu %u\n", __func__, __LINE__, | 574 | pr_debug("%s:%d: no plug found: cpu %u\n", __func__, __LINE__, |
528 | pd->cpu); | 575 | pd->cpu); |
529 | dump_bmp(&per_cpu(private, 0)); | 576 | dump_bmp(&per_cpu(ps3_private, 0)); |
530 | dump_bmp(&per_cpu(private, 1)); | 577 | dump_bmp(&per_cpu(ps3_private, 1)); |
531 | return NO_IRQ; | 578 | return NO_IRQ; |
532 | } | 579 | } |
533 | 580 | ||
534 | #if defined(DEBUG) | 581 | #if defined(DEBUG) |
535 | if (plug < NUM_ISA_INTERRUPTS || plug > PS3_PLUG_MAX) { | 582 | if (unlikely(plug < NUM_ISA_INTERRUPTS || plug > PS3_PLUG_MAX)) { |
536 | dump_bmp(&per_cpu(private, 0)); | 583 | dump_bmp(&per_cpu(ps3_private, 0)); |
537 | dump_bmp(&per_cpu(private, 1)); | 584 | dump_bmp(&per_cpu(ps3_private, 1)); |
538 | BUG(); | 585 | BUG(); |
539 | } | 586 | } |
540 | #endif | 587 | #endif |
@@ -544,26 +591,27 @@ unsigned int ps3_get_irq(void) | |||
544 | void __init ps3_init_IRQ(void) | 591 | void __init ps3_init_IRQ(void) |
545 | { | 592 | { |
546 | int result; | 593 | int result; |
547 | unsigned long node; | ||
548 | unsigned cpu; | 594 | unsigned cpu; |
549 | struct irq_host *host; | 595 | struct irq_host *host; |
550 | 596 | ||
551 | lv1_get_logical_ppe_id(&node); | 597 | host = irq_alloc_host(IRQ_HOST_MAP_NOMAP, 0, &ps3_host_ops, |
552 | |||
553 | host = irq_alloc_host(IRQ_HOST_MAP_NOMAP, 0, &host_ops, | ||
554 | PS3_INVALID_OUTLET); | 598 | PS3_INVALID_OUTLET); |
555 | irq_set_default_host(host); | 599 | irq_set_default_host(host); |
556 | irq_set_virq_count(PS3_PLUG_MAX + 1); | 600 | irq_set_virq_count(PS3_PLUG_MAX + 1); |
557 | 601 | ||
558 | for_each_possible_cpu(cpu) { | 602 | for_each_possible_cpu(cpu) { |
559 | struct private *pd = &per_cpu(private, cpu); | 603 | struct ps3_private *pd = &per_cpu(ps3_private, cpu); |
560 | 604 | ||
561 | pd->node = node; | 605 | lv1_get_logical_ppe_id(&pd->node); |
562 | pd->cpu = cpu; | 606 | pd->cpu = get_hard_smp_processor_id(cpu); |
563 | spin_lock_init(&pd->bmp.lock); | 607 | spin_lock_init(&pd->bmp.lock); |
564 | 608 | ||
565 | result = lv1_configure_irq_state_bitmap(node, cpu, | 609 | pr_debug("%s:%d: node %lu, cpu %d, bmp %lxh\n", __func__, |
566 | ps3_mm_phys_to_lpar(__pa(&pd->bmp.status))); | 610 | __LINE__, pd->node, pd->cpu, |
611 | ps3_mm_phys_to_lpar(__pa(&pd->bmp))); | ||
612 | |||
613 | result = lv1_configure_irq_state_bitmap(pd->node, pd->cpu, | ||
614 | ps3_mm_phys_to_lpar(__pa(&pd->bmp))); | ||
567 | 615 | ||
568 | if (result) | 616 | if (result) |
569 | pr_debug("%s:%d: lv1_configure_irq_state_bitmap failed:" | 617 | pr_debug("%s:%d: lv1_configure_irq_state_bitmap failed:" |