diff options
Diffstat (limited to 'arch/ia64/kernel/iosapic.c')
-rw-r--r-- | arch/ia64/kernel/iosapic.c | 358 |
1 files changed, 272 insertions, 86 deletions
diff --git a/arch/ia64/kernel/iosapic.c b/arch/ia64/kernel/iosapic.c index c15be5c38f56..11a221cc8dc3 100644 --- a/arch/ia64/kernel/iosapic.c +++ b/arch/ia64/kernel/iosapic.c | |||
@@ -79,6 +79,7 @@ | |||
79 | #include <linux/smp.h> | 79 | #include <linux/smp.h> |
80 | #include <linux/smp_lock.h> | 80 | #include <linux/smp_lock.h> |
81 | #include <linux/string.h> | 81 | #include <linux/string.h> |
82 | #include <linux/bootmem.h> | ||
82 | 83 | ||
83 | #include <asm/delay.h> | 84 | #include <asm/delay.h> |
84 | #include <asm/hw_irq.h> | 85 | #include <asm/hw_irq.h> |
@@ -98,19 +99,30 @@ | |||
98 | #define DBG(fmt...) | 99 | #define DBG(fmt...) |
99 | #endif | 100 | #endif |
100 | 101 | ||
102 | #define NR_PREALLOCATE_RTE_ENTRIES (PAGE_SIZE / sizeof(struct iosapic_rte_info)) | ||
103 | #define RTE_PREALLOCATED (1) | ||
104 | |||
101 | static DEFINE_SPINLOCK(iosapic_lock); | 105 | static DEFINE_SPINLOCK(iosapic_lock); |
102 | 106 | ||
103 | /* These tables map IA-64 vectors to the IOSAPIC pin that generates this vector. */ | 107 | /* These tables map IA-64 vectors to the IOSAPIC pin that generates this vector. */ |
104 | 108 | ||
105 | static struct iosapic_intr_info { | 109 | struct iosapic_rte_info { |
110 | struct list_head rte_list; /* node in list of RTEs sharing the same vector */ | ||
106 | char __iomem *addr; /* base address of IOSAPIC */ | 111 | char __iomem *addr; /* base address of IOSAPIC */ |
107 | u32 low32; /* current value of low word of Redirection table entry */ | ||
108 | unsigned int gsi_base; /* first GSI assigned to this IOSAPIC */ | 112 | unsigned int gsi_base; /* first GSI assigned to this IOSAPIC */ |
109 | char rte_index; /* IOSAPIC RTE index (-1 => not an IOSAPIC interrupt) */ | 113 | char rte_index; /* IOSAPIC RTE index */ |
114 | int refcnt; /* reference counter */ | ||
115 | unsigned int flags; /* flags */ | ||
116 | } ____cacheline_aligned; | ||
117 | |||
118 | static struct iosapic_intr_info { | ||
119 | struct list_head rtes; /* RTEs using this vector (empty => not an IOSAPIC interrupt) */ | ||
120 | int count; /* # of RTEs that shares this vector */ | ||
121 | u32 low32; /* current value of low word of Redirection table entry */ | ||
122 | unsigned int dest; /* destination CPU physical ID */ | ||
110 | unsigned char dmode : 3; /* delivery mode (see iosapic.h) */ | 123 | unsigned char dmode : 3; /* delivery mode (see iosapic.h) */ |
111 | unsigned char polarity: 1; /* interrupt polarity (see iosapic.h) */ | 124 | unsigned char polarity: 1; /* interrupt polarity (see iosapic.h) */ |
112 | unsigned char trigger : 1; /* trigger mode (see iosapic.h) */ | 125 | unsigned char trigger : 1; /* trigger mode (see iosapic.h) */ |
113 | int refcnt; /* reference counter */ | ||
114 | } iosapic_intr_info[IA64_NUM_VECTORS]; | 126 | } iosapic_intr_info[IA64_NUM_VECTORS]; |
115 | 127 | ||
116 | static struct iosapic { | 128 | static struct iosapic { |
@@ -126,6 +138,8 @@ static int num_iosapic; | |||
126 | 138 | ||
127 | static unsigned char pcat_compat __initdata; /* 8259 compatibility flag */ | 139 | static unsigned char pcat_compat __initdata; /* 8259 compatibility flag */ |
128 | 140 | ||
141 | static int iosapic_kmalloc_ok; | ||
142 | static LIST_HEAD(free_rte_list); | ||
129 | 143 | ||
130 | /* | 144 | /* |
131 | * Find an IOSAPIC associated with a GSI | 145 | * Find an IOSAPIC associated with a GSI |
@@ -147,10 +161,12 @@ static inline int | |||
147 | _gsi_to_vector (unsigned int gsi) | 161 | _gsi_to_vector (unsigned int gsi) |
148 | { | 162 | { |
149 | struct iosapic_intr_info *info; | 163 | struct iosapic_intr_info *info; |
164 | struct iosapic_rte_info *rte; | ||
150 | 165 | ||
151 | for (info = iosapic_intr_info; info < iosapic_intr_info + IA64_NUM_VECTORS; ++info) | 166 | for (info = iosapic_intr_info; info < iosapic_intr_info + IA64_NUM_VECTORS; ++info) |
152 | if (info->gsi_base + info->rte_index == gsi) | 167 | list_for_each_entry(rte, &info->rtes, rte_list) |
153 | return info - iosapic_intr_info; | 168 | if (rte->gsi_base + rte->rte_index == gsi) |
169 | return info - iosapic_intr_info; | ||
154 | return -1; | 170 | return -1; |
155 | } | 171 | } |
156 | 172 | ||
@@ -167,33 +183,52 @@ gsi_to_vector (unsigned int gsi) | |||
167 | int | 183 | int |
168 | gsi_to_irq (unsigned int gsi) | 184 | gsi_to_irq (unsigned int gsi) |
169 | { | 185 | { |
186 | unsigned long flags; | ||
187 | int irq; | ||
170 | /* | 188 | /* |
171 | * XXX fix me: this assumes an identity mapping vetween IA-64 vector and Linux irq | 189 | * XXX fix me: this assumes an identity mapping vetween IA-64 vector and Linux irq |
172 | * numbers... | 190 | * numbers... |
173 | */ | 191 | */ |
174 | return _gsi_to_vector(gsi); | 192 | spin_lock_irqsave(&iosapic_lock, flags); |
193 | { | ||
194 | irq = _gsi_to_vector(gsi); | ||
195 | } | ||
196 | spin_unlock_irqrestore(&iosapic_lock, flags); | ||
197 | |||
198 | return irq; | ||
199 | } | ||
200 | |||
201 | static struct iosapic_rte_info *gsi_vector_to_rte(unsigned int gsi, unsigned int vec) | ||
202 | { | ||
203 | struct iosapic_rte_info *rte; | ||
204 | |||
205 | list_for_each_entry(rte, &iosapic_intr_info[vec].rtes, rte_list) | ||
206 | if (rte->gsi_base + rte->rte_index == gsi) | ||
207 | return rte; | ||
208 | return NULL; | ||
175 | } | 209 | } |
176 | 210 | ||
177 | static void | 211 | static void |
178 | set_rte (unsigned int vector, unsigned int dest, int mask) | 212 | set_rte (unsigned int gsi, unsigned int vector, unsigned int dest, int mask) |
179 | { | 213 | { |
180 | unsigned long pol, trigger, dmode; | 214 | unsigned long pol, trigger, dmode; |
181 | u32 low32, high32; | 215 | u32 low32, high32; |
182 | char __iomem *addr; | 216 | char __iomem *addr; |
183 | int rte_index; | 217 | int rte_index; |
184 | char redir; | 218 | char redir; |
219 | struct iosapic_rte_info *rte; | ||
185 | 220 | ||
186 | DBG(KERN_DEBUG"IOSAPIC: routing vector %d to 0x%x\n", vector, dest); | 221 | DBG(KERN_DEBUG"IOSAPIC: routing vector %d to 0x%x\n", vector, dest); |
187 | 222 | ||
188 | rte_index = iosapic_intr_info[vector].rte_index; | 223 | rte = gsi_vector_to_rte(gsi, vector); |
189 | if (rte_index < 0) | 224 | if (!rte) |
190 | return; /* not an IOSAPIC interrupt */ | 225 | return; /* not an IOSAPIC interrupt */ |
191 | 226 | ||
192 | addr = iosapic_intr_info[vector].addr; | 227 | rte_index = rte->rte_index; |
228 | addr = rte->addr; | ||
193 | pol = iosapic_intr_info[vector].polarity; | 229 | pol = iosapic_intr_info[vector].polarity; |
194 | trigger = iosapic_intr_info[vector].trigger; | 230 | trigger = iosapic_intr_info[vector].trigger; |
195 | dmode = iosapic_intr_info[vector].dmode; | 231 | dmode = iosapic_intr_info[vector].dmode; |
196 | vector &= (~IA64_IRQ_REDIRECTED); | ||
197 | 232 | ||
198 | redir = (dmode == IOSAPIC_LOWEST_PRIORITY) ? 1 : 0; | 233 | redir = (dmode == IOSAPIC_LOWEST_PRIORITY) ? 1 : 0; |
199 | 234 | ||
@@ -221,6 +256,7 @@ set_rte (unsigned int vector, unsigned int dest, int mask) | |||
221 | iosapic_write(addr, IOSAPIC_RTE_HIGH(rte_index), high32); | 256 | iosapic_write(addr, IOSAPIC_RTE_HIGH(rte_index), high32); |
222 | iosapic_write(addr, IOSAPIC_RTE_LOW(rte_index), low32); | 257 | iosapic_write(addr, IOSAPIC_RTE_LOW(rte_index), low32); |
223 | iosapic_intr_info[vector].low32 = low32; | 258 | iosapic_intr_info[vector].low32 = low32; |
259 | iosapic_intr_info[vector].dest = dest; | ||
224 | } | 260 | } |
225 | 261 | ||
226 | static void | 262 | static void |
@@ -237,18 +273,20 @@ mask_irq (unsigned int irq) | |||
237 | u32 low32; | 273 | u32 low32; |
238 | int rte_index; | 274 | int rte_index; |
239 | ia64_vector vec = irq_to_vector(irq); | 275 | ia64_vector vec = irq_to_vector(irq); |
276 | struct iosapic_rte_info *rte; | ||
240 | 277 | ||
241 | addr = iosapic_intr_info[vec].addr; | 278 | if (list_empty(&iosapic_intr_info[vec].rtes)) |
242 | rte_index = iosapic_intr_info[vec].rte_index; | ||
243 | |||
244 | if (rte_index < 0) | ||
245 | return; /* not an IOSAPIC interrupt! */ | 279 | return; /* not an IOSAPIC interrupt! */ |
246 | 280 | ||
247 | spin_lock_irqsave(&iosapic_lock, flags); | 281 | spin_lock_irqsave(&iosapic_lock, flags); |
248 | { | 282 | { |
249 | /* set only the mask bit */ | 283 | /* set only the mask bit */ |
250 | low32 = iosapic_intr_info[vec].low32 |= IOSAPIC_MASK; | 284 | low32 = iosapic_intr_info[vec].low32 |= IOSAPIC_MASK; |
251 | iosapic_write(addr, IOSAPIC_RTE_LOW(rte_index), low32); | 285 | list_for_each_entry(rte, &iosapic_intr_info[vec].rtes, rte_list) { |
286 | addr = rte->addr; | ||
287 | rte_index = rte->rte_index; | ||
288 | iosapic_write(addr, IOSAPIC_RTE_LOW(rte_index), low32); | ||
289 | } | ||
252 | } | 290 | } |
253 | spin_unlock_irqrestore(&iosapic_lock, flags); | 291 | spin_unlock_irqrestore(&iosapic_lock, flags); |
254 | } | 292 | } |
@@ -261,16 +299,19 @@ unmask_irq (unsigned int irq) | |||
261 | u32 low32; | 299 | u32 low32; |
262 | int rte_index; | 300 | int rte_index; |
263 | ia64_vector vec = irq_to_vector(irq); | 301 | ia64_vector vec = irq_to_vector(irq); |
302 | struct iosapic_rte_info *rte; | ||
264 | 303 | ||
265 | addr = iosapic_intr_info[vec].addr; | 304 | if (list_empty(&iosapic_intr_info[vec].rtes)) |
266 | rte_index = iosapic_intr_info[vec].rte_index; | ||
267 | if (rte_index < 0) | ||
268 | return; /* not an IOSAPIC interrupt! */ | 305 | return; /* not an IOSAPIC interrupt! */ |
269 | 306 | ||
270 | spin_lock_irqsave(&iosapic_lock, flags); | 307 | spin_lock_irqsave(&iosapic_lock, flags); |
271 | { | 308 | { |
272 | low32 = iosapic_intr_info[vec].low32 &= ~IOSAPIC_MASK; | 309 | low32 = iosapic_intr_info[vec].low32 &= ~IOSAPIC_MASK; |
273 | iosapic_write(addr, IOSAPIC_RTE_LOW(rte_index), low32); | 310 | list_for_each_entry(rte, &iosapic_intr_info[vec].rtes, rte_list) { |
311 | addr = rte->addr; | ||
312 | rte_index = rte->rte_index; | ||
313 | iosapic_write(addr, IOSAPIC_RTE_LOW(rte_index), low32); | ||
314 | } | ||
274 | } | 315 | } |
275 | spin_unlock_irqrestore(&iosapic_lock, flags); | 316 | spin_unlock_irqrestore(&iosapic_lock, flags); |
276 | } | 317 | } |
@@ -286,6 +327,7 @@ iosapic_set_affinity (unsigned int irq, cpumask_t mask) | |||
286 | char __iomem *addr; | 327 | char __iomem *addr; |
287 | int redir = (irq & IA64_IRQ_REDIRECTED) ? 1 : 0; | 328 | int redir = (irq & IA64_IRQ_REDIRECTED) ? 1 : 0; |
288 | ia64_vector vec; | 329 | ia64_vector vec; |
330 | struct iosapic_rte_info *rte; | ||
289 | 331 | ||
290 | irq &= (~IA64_IRQ_REDIRECTED); | 332 | irq &= (~IA64_IRQ_REDIRECTED); |
291 | vec = irq_to_vector(irq); | 333 | vec = irq_to_vector(irq); |
@@ -295,10 +337,7 @@ iosapic_set_affinity (unsigned int irq, cpumask_t mask) | |||
295 | 337 | ||
296 | dest = cpu_physical_id(first_cpu(mask)); | 338 | dest = cpu_physical_id(first_cpu(mask)); |
297 | 339 | ||
298 | rte_index = iosapic_intr_info[vec].rte_index; | 340 | if (list_empty(&iosapic_intr_info[vec].rtes)) |
299 | addr = iosapic_intr_info[vec].addr; | ||
300 | |||
301 | if (rte_index < 0) | ||
302 | return; /* not an IOSAPIC interrupt */ | 341 | return; /* not an IOSAPIC interrupt */ |
303 | 342 | ||
304 | set_irq_affinity_info(irq, dest, redir); | 343 | set_irq_affinity_info(irq, dest, redir); |
@@ -318,8 +357,13 @@ iosapic_set_affinity (unsigned int irq, cpumask_t mask) | |||
318 | low32 |= (IOSAPIC_FIXED << IOSAPIC_DELIVERY_SHIFT); | 357 | low32 |= (IOSAPIC_FIXED << IOSAPIC_DELIVERY_SHIFT); |
319 | 358 | ||
320 | iosapic_intr_info[vec].low32 = low32; | 359 | iosapic_intr_info[vec].low32 = low32; |
321 | iosapic_write(addr, IOSAPIC_RTE_HIGH(rte_index), high32); | 360 | iosapic_intr_info[vec].dest = dest; |
322 | iosapic_write(addr, IOSAPIC_RTE_LOW(rte_index), low32); | 361 | list_for_each_entry(rte, &iosapic_intr_info[vec].rtes, rte_list) { |
362 | addr = rte->addr; | ||
363 | rte_index = rte->rte_index; | ||
364 | iosapic_write(addr, IOSAPIC_RTE_HIGH(rte_index), high32); | ||
365 | iosapic_write(addr, IOSAPIC_RTE_LOW(rte_index), low32); | ||
366 | } | ||
323 | } | 367 | } |
324 | spin_unlock_irqrestore(&iosapic_lock, flags); | 368 | spin_unlock_irqrestore(&iosapic_lock, flags); |
325 | #endif | 369 | #endif |
@@ -340,9 +384,11 @@ static void | |||
340 | iosapic_end_level_irq (unsigned int irq) | 384 | iosapic_end_level_irq (unsigned int irq) |
341 | { | 385 | { |
342 | ia64_vector vec = irq_to_vector(irq); | 386 | ia64_vector vec = irq_to_vector(irq); |
387 | struct iosapic_rte_info *rte; | ||
343 | 388 | ||
344 | move_irq(irq); | 389 | move_irq(irq); |
345 | iosapic_eoi(iosapic_intr_info[vec].addr, vec); | 390 | list_for_each_entry(rte, &iosapic_intr_info[vec].rtes, rte_list) |
391 | iosapic_eoi(rte->addr, vec); | ||
346 | } | 392 | } |
347 | 393 | ||
348 | #define iosapic_shutdown_level_irq mask_irq | 394 | #define iosapic_shutdown_level_irq mask_irq |
@@ -422,6 +468,34 @@ iosapic_version (char __iomem *addr) | |||
422 | return iosapic_read(addr, IOSAPIC_VERSION); | 468 | return iosapic_read(addr, IOSAPIC_VERSION); |
423 | } | 469 | } |
424 | 470 | ||
471 | static int iosapic_find_sharable_vector (unsigned long trigger, unsigned long pol) | ||
472 | { | ||
473 | int i, vector = -1, min_count = -1; | ||
474 | struct iosapic_intr_info *info; | ||
475 | |||
476 | /* | ||
477 | * shared vectors for edge-triggered interrupts are not | ||
478 | * supported yet | ||
479 | */ | ||
480 | if (trigger == IOSAPIC_EDGE) | ||
481 | return -1; | ||
482 | |||
483 | for (i = IA64_FIRST_DEVICE_VECTOR; i <= IA64_LAST_DEVICE_VECTOR; i++) { | ||
484 | info = &iosapic_intr_info[i]; | ||
485 | if (info->trigger == trigger && info->polarity == pol && | ||
486 | (info->dmode == IOSAPIC_FIXED || info->dmode == IOSAPIC_LOWEST_PRIORITY)) { | ||
487 | if (min_count == -1 || info->count < min_count) { | ||
488 | vector = i; | ||
489 | min_count = info->count; | ||
490 | } | ||
491 | } | ||
492 | } | ||
493 | if (vector < 0) | ||
494 | panic("%s: out of interrupt vectors!\n", __FUNCTION__); | ||
495 | |||
496 | return vector; | ||
497 | } | ||
498 | |||
425 | /* | 499 | /* |
426 | * if the given vector is already owned by other, | 500 | * if the given vector is already owned by other, |
427 | * assign a new vector for the other and make the vector available | 501 | * assign a new vector for the other and make the vector available |
@@ -431,19 +505,63 @@ iosapic_reassign_vector (int vector) | |||
431 | { | 505 | { |
432 | int new_vector; | 506 | int new_vector; |
433 | 507 | ||
434 | if (iosapic_intr_info[vector].rte_index >= 0 || iosapic_intr_info[vector].addr | 508 | if (!list_empty(&iosapic_intr_info[vector].rtes)) { |
435 | || iosapic_intr_info[vector].gsi_base || iosapic_intr_info[vector].dmode | ||
436 | || iosapic_intr_info[vector].polarity || iosapic_intr_info[vector].trigger) | ||
437 | { | ||
438 | new_vector = assign_irq_vector(AUTO_ASSIGN); | 509 | new_vector = assign_irq_vector(AUTO_ASSIGN); |
439 | printk(KERN_INFO "Reassigning vector %d to %d\n", vector, new_vector); | 510 | printk(KERN_INFO "Reassigning vector %d to %d\n", vector, new_vector); |
440 | memcpy(&iosapic_intr_info[new_vector], &iosapic_intr_info[vector], | 511 | memcpy(&iosapic_intr_info[new_vector], &iosapic_intr_info[vector], |
441 | sizeof(struct iosapic_intr_info)); | 512 | sizeof(struct iosapic_intr_info)); |
513 | INIT_LIST_HEAD(&iosapic_intr_info[new_vector].rtes); | ||
514 | list_move(iosapic_intr_info[vector].rtes.next, &iosapic_intr_info[new_vector].rtes); | ||
442 | memset(&iosapic_intr_info[vector], 0, sizeof(struct iosapic_intr_info)); | 515 | memset(&iosapic_intr_info[vector], 0, sizeof(struct iosapic_intr_info)); |
443 | iosapic_intr_info[vector].rte_index = -1; | 516 | iosapic_intr_info[vector].low32 = IOSAPIC_MASK; |
517 | INIT_LIST_HEAD(&iosapic_intr_info[vector].rtes); | ||
444 | } | 518 | } |
445 | } | 519 | } |
446 | 520 | ||
521 | static struct iosapic_rte_info *iosapic_alloc_rte (void) | ||
522 | { | ||
523 | int i; | ||
524 | struct iosapic_rte_info *rte; | ||
525 | int preallocated = 0; | ||
526 | |||
527 | if (!iosapic_kmalloc_ok && list_empty(&free_rte_list)) { | ||
528 | rte = alloc_bootmem(sizeof(struct iosapic_rte_info) * NR_PREALLOCATE_RTE_ENTRIES); | ||
529 | if (!rte) | ||
530 | return NULL; | ||
531 | for (i = 0; i < NR_PREALLOCATE_RTE_ENTRIES; i++, rte++) | ||
532 | list_add(&rte->rte_list, &free_rte_list); | ||
533 | } | ||
534 | |||
535 | if (!list_empty(&free_rte_list)) { | ||
536 | rte = list_entry(free_rte_list.next, struct iosapic_rte_info, rte_list); | ||
537 | list_del(&rte->rte_list); | ||
538 | preallocated++; | ||
539 | } else { | ||
540 | rte = kmalloc(sizeof(struct iosapic_rte_info), GFP_ATOMIC); | ||
541 | if (!rte) | ||
542 | return NULL; | ||
543 | } | ||
544 | |||
545 | memset(rte, 0, sizeof(struct iosapic_rte_info)); | ||
546 | if (preallocated) | ||
547 | rte->flags |= RTE_PREALLOCATED; | ||
548 | |||
549 | return rte; | ||
550 | } | ||
551 | |||
552 | static void iosapic_free_rte (struct iosapic_rte_info *rte) | ||
553 | { | ||
554 | if (rte->flags & RTE_PREALLOCATED) | ||
555 | list_add_tail(&rte->rte_list, &free_rte_list); | ||
556 | else | ||
557 | kfree(rte); | ||
558 | } | ||
559 | |||
560 | static inline int vector_is_shared (int vector) | ||
561 | { | ||
562 | return (iosapic_intr_info[vector].count > 1); | ||
563 | } | ||
564 | |||
447 | static void | 565 | static void |
448 | register_intr (unsigned int gsi, int vector, unsigned char delivery, | 566 | register_intr (unsigned int gsi, int vector, unsigned char delivery, |
449 | unsigned long polarity, unsigned long trigger) | 567 | unsigned long polarity, unsigned long trigger) |
@@ -454,6 +572,7 @@ register_intr (unsigned int gsi, int vector, unsigned char delivery, | |||
454 | int index; | 572 | int index; |
455 | unsigned long gsi_base; | 573 | unsigned long gsi_base; |
456 | void __iomem *iosapic_address; | 574 | void __iomem *iosapic_address; |
575 | struct iosapic_rte_info *rte; | ||
457 | 576 | ||
458 | index = find_iosapic(gsi); | 577 | index = find_iosapic(gsi); |
459 | if (index < 0) { | 578 | if (index < 0) { |
@@ -464,14 +583,33 @@ register_intr (unsigned int gsi, int vector, unsigned char delivery, | |||
464 | iosapic_address = iosapic_lists[index].addr; | 583 | iosapic_address = iosapic_lists[index].addr; |
465 | gsi_base = iosapic_lists[index].gsi_base; | 584 | gsi_base = iosapic_lists[index].gsi_base; |
466 | 585 | ||
467 | rte_index = gsi - gsi_base; | 586 | rte = gsi_vector_to_rte(gsi, vector); |
468 | iosapic_intr_info[vector].rte_index = rte_index; | 587 | if (!rte) { |
588 | rte = iosapic_alloc_rte(); | ||
589 | if (!rte) { | ||
590 | printk(KERN_WARNING "%s: cannot allocate memory\n", __FUNCTION__); | ||
591 | return; | ||
592 | } | ||
593 | |||
594 | rte_index = gsi - gsi_base; | ||
595 | rte->rte_index = rte_index; | ||
596 | rte->addr = iosapic_address; | ||
597 | rte->gsi_base = gsi_base; | ||
598 | rte->refcnt++; | ||
599 | list_add_tail(&rte->rte_list, &iosapic_intr_info[vector].rtes); | ||
600 | iosapic_intr_info[vector].count++; | ||
601 | } | ||
602 | else if (vector_is_shared(vector)) { | ||
603 | struct iosapic_intr_info *info = &iosapic_intr_info[vector]; | ||
604 | if (info->trigger != trigger || info->polarity != polarity) { | ||
605 | printk (KERN_WARNING "%s: cannot override the interrupt\n", __FUNCTION__); | ||
606 | return; | ||
607 | } | ||
608 | } | ||
609 | |||
469 | iosapic_intr_info[vector].polarity = polarity; | 610 | iosapic_intr_info[vector].polarity = polarity; |
470 | iosapic_intr_info[vector].dmode = delivery; | 611 | iosapic_intr_info[vector].dmode = delivery; |
471 | iosapic_intr_info[vector].addr = iosapic_address; | ||
472 | iosapic_intr_info[vector].gsi_base = gsi_base; | ||
473 | iosapic_intr_info[vector].trigger = trigger; | 612 | iosapic_intr_info[vector].trigger = trigger; |
474 | iosapic_intr_info[vector].refcnt++; | ||
475 | 613 | ||
476 | if (trigger == IOSAPIC_EDGE) | 614 | if (trigger == IOSAPIC_EDGE) |
477 | irq_type = &irq_type_iosapic_edge; | 615 | irq_type = &irq_type_iosapic_edge; |
@@ -494,6 +632,13 @@ get_target_cpu (unsigned int gsi, int vector) | |||
494 | static int cpu = -1; | 632 | static int cpu = -1; |
495 | 633 | ||
496 | /* | 634 | /* |
635 | * In case of vector shared by multiple RTEs, all RTEs that | ||
636 | * share the vector need to use the same destination CPU. | ||
637 | */ | ||
638 | if (!list_empty(&iosapic_intr_info[vector].rtes)) | ||
639 | return iosapic_intr_info[vector].dest; | ||
640 | |||
641 | /* | ||
497 | * If the platform supports redirection via XTP, let it | 642 | * If the platform supports redirection via XTP, let it |
498 | * distribute interrupts. | 643 | * distribute interrupts. |
499 | */ | 644 | */ |
@@ -565,10 +710,12 @@ int | |||
565 | iosapic_register_intr (unsigned int gsi, | 710 | iosapic_register_intr (unsigned int gsi, |
566 | unsigned long polarity, unsigned long trigger) | 711 | unsigned long polarity, unsigned long trigger) |
567 | { | 712 | { |
568 | int vector; | 713 | int vector, mask = 1; |
569 | unsigned int dest; | 714 | unsigned int dest; |
570 | unsigned long flags; | 715 | unsigned long flags; |
571 | 716 | struct iosapic_rte_info *rte; | |
717 | u32 low32; | ||
718 | again: | ||
572 | /* | 719 | /* |
573 | * If this GSI has already been registered (i.e., it's a | 720 | * If this GSI has already been registered (i.e., it's a |
574 | * shared interrupt, or we lost a race to register it), | 721 | * shared interrupt, or we lost a race to register it), |
@@ -578,19 +725,45 @@ iosapic_register_intr (unsigned int gsi, | |||
578 | { | 725 | { |
579 | vector = gsi_to_vector(gsi); | 726 | vector = gsi_to_vector(gsi); |
580 | if (vector > 0) { | 727 | if (vector > 0) { |
581 | iosapic_intr_info[vector].refcnt++; | 728 | rte = gsi_vector_to_rte(gsi, vector); |
729 | rte->refcnt++; | ||
582 | spin_unlock_irqrestore(&iosapic_lock, flags); | 730 | spin_unlock_irqrestore(&iosapic_lock, flags); |
583 | return vector; | 731 | return vector; |
584 | } | 732 | } |
733 | } | ||
734 | spin_unlock_irqrestore(&iosapic_lock, flags); | ||
735 | |||
736 | /* If vector is running out, we try to find a sharable vector */ | ||
737 | vector = assign_irq_vector_nopanic(AUTO_ASSIGN); | ||
738 | if (vector < 0) | ||
739 | vector = iosapic_find_sharable_vector(trigger, polarity); | ||
740 | |||
741 | spin_lock_irqsave(&irq_descp(vector)->lock, flags); | ||
742 | spin_lock(&iosapic_lock); | ||
743 | { | ||
744 | if (gsi_to_vector(gsi) > 0) { | ||
745 | if (list_empty(&iosapic_intr_info[vector].rtes)) | ||
746 | free_irq_vector(vector); | ||
747 | spin_unlock(&iosapic_lock); | ||
748 | spin_unlock_irqrestore(&irq_descp(vector)->lock, flags); | ||
749 | goto again; | ||
750 | } | ||
585 | 751 | ||
586 | vector = assign_irq_vector(AUTO_ASSIGN); | ||
587 | dest = get_target_cpu(gsi, vector); | 752 | dest = get_target_cpu(gsi, vector); |
588 | register_intr(gsi, vector, IOSAPIC_LOWEST_PRIORITY, | 753 | register_intr(gsi, vector, IOSAPIC_LOWEST_PRIORITY, |
589 | polarity, trigger); | 754 | polarity, trigger); |
590 | 755 | ||
591 | set_rte(vector, dest, 1); | 756 | /* |
757 | * If the vector is shared and already unmasked for | ||
758 | * other interrupt sources, don't mask it. | ||
759 | */ | ||
760 | low32 = iosapic_intr_info[vector].low32; | ||
761 | if (vector_is_shared(vector) && !(low32 & IOSAPIC_MASK)) | ||
762 | mask = 0; | ||
763 | set_rte(gsi, vector, dest, mask); | ||
592 | } | 764 | } |
593 | spin_unlock_irqrestore(&iosapic_lock, flags); | 765 | spin_unlock_irq(&iosapic_lock); |
766 | spin_unlock_irqrestore(&irq_descp(vector)->lock, flags); | ||
594 | 767 | ||
595 | printk(KERN_INFO "GSI %u (%s, %s) -> CPU %d (0x%04x) vector %d\n", | 768 | printk(KERN_INFO "GSI %u (%s, %s) -> CPU %d (0x%04x) vector %d\n", |
596 | gsi, (trigger == IOSAPIC_EDGE ? "edge" : "level"), | 769 | gsi, (trigger == IOSAPIC_EDGE ? "edge" : "level"), |
@@ -607,8 +780,10 @@ iosapic_unregister_intr (unsigned int gsi) | |||
607 | unsigned long flags; | 780 | unsigned long flags; |
608 | int irq, vector; | 781 | int irq, vector; |
609 | irq_desc_t *idesc; | 782 | irq_desc_t *idesc; |
610 | int rte_index; | 783 | u32 low32; |
611 | unsigned long trigger, polarity; | 784 | unsigned long trigger, polarity; |
785 | unsigned int dest; | ||
786 | struct iosapic_rte_info *rte; | ||
612 | 787 | ||
613 | /* | 788 | /* |
614 | * If the irq associated with the gsi is not found, | 789 | * If the irq associated with the gsi is not found, |
@@ -627,54 +802,56 @@ iosapic_unregister_intr (unsigned int gsi) | |||
627 | spin_lock_irqsave(&idesc->lock, flags); | 802 | spin_lock_irqsave(&idesc->lock, flags); |
628 | spin_lock(&iosapic_lock); | 803 | spin_lock(&iosapic_lock); |
629 | { | 804 | { |
630 | rte_index = iosapic_intr_info[vector].rte_index; | 805 | if ((rte = gsi_vector_to_rte(gsi, vector)) == NULL) { |
631 | if (rte_index < 0) { | ||
632 | spin_unlock(&iosapic_lock); | ||
633 | spin_unlock_irqrestore(&idesc->lock, flags); | ||
634 | printk(KERN_ERR "iosapic_unregister_intr(%u) unbalanced\n", gsi); | 806 | printk(KERN_ERR "iosapic_unregister_intr(%u) unbalanced\n", gsi); |
635 | WARN_ON(1); | 807 | WARN_ON(1); |
636 | return; | 808 | goto out; |
637 | } | 809 | } |
638 | 810 | ||
639 | if (--iosapic_intr_info[vector].refcnt > 0) { | 811 | if (--rte->refcnt > 0) |
640 | spin_unlock(&iosapic_lock); | 812 | goto out; |
641 | spin_unlock_irqrestore(&idesc->lock, flags); | ||
642 | return; | ||
643 | } | ||
644 | 813 | ||
645 | /* | 814 | /* Mask the interrupt */ |
646 | * If interrupt handlers still exist on the irq | 815 | low32 = iosapic_intr_info[vector].low32 | IOSAPIC_MASK; |
647 | * associated with the gsi, don't unregister the | 816 | iosapic_write(rte->addr, IOSAPIC_RTE_LOW(rte->rte_index), low32); |
648 | * interrupt. | ||
649 | */ | ||
650 | if (idesc->action) { | ||
651 | iosapic_intr_info[vector].refcnt++; | ||
652 | spin_unlock(&iosapic_lock); | ||
653 | spin_unlock_irqrestore(&idesc->lock, flags); | ||
654 | printk(KERN_WARNING "Cannot unregister GSI. IRQ %u is still in use.\n", irq); | ||
655 | return; | ||
656 | } | ||
657 | 817 | ||
658 | /* Clear the interrupt controller descriptor. */ | 818 | /* Remove the rte entry from the list */ |
659 | idesc->handler = &no_irq_type; | 819 | list_del(&rte->rte_list); |
820 | iosapic_intr_info[vector].count--; | ||
821 | iosapic_free_rte(rte); | ||
660 | 822 | ||
661 | trigger = iosapic_intr_info[vector].trigger; | 823 | trigger = iosapic_intr_info[vector].trigger; |
662 | polarity = iosapic_intr_info[vector].polarity; | 824 | polarity = iosapic_intr_info[vector].polarity; |
825 | dest = iosapic_intr_info[vector].dest; | ||
826 | printk(KERN_INFO "GSI %u (%s, %s) -> CPU %d (0x%04x) vector %d unregistered\n", | ||
827 | gsi, (trigger == IOSAPIC_EDGE ? "edge" : "level"), | ||
828 | (polarity == IOSAPIC_POL_HIGH ? "high" : "low"), | ||
829 | cpu_logical_id(dest), dest, vector); | ||
830 | |||
831 | if (list_empty(&iosapic_intr_info[vector].rtes)) { | ||
832 | /* Sanity check */ | ||
833 | BUG_ON(iosapic_intr_info[vector].count); | ||
834 | |||
835 | /* Clear the interrupt controller descriptor */ | ||
836 | idesc->handler = &no_irq_type; | ||
837 | |||
838 | /* Clear the interrupt information */ | ||
839 | memset(&iosapic_intr_info[vector], 0, sizeof(struct iosapic_intr_info)); | ||
840 | iosapic_intr_info[vector].low32 |= IOSAPIC_MASK; | ||
841 | INIT_LIST_HEAD(&iosapic_intr_info[vector].rtes); | ||
842 | |||
843 | if (idesc->action) { | ||
844 | printk(KERN_ERR "interrupt handlers still exist on IRQ %u\n", irq); | ||
845 | WARN_ON(1); | ||
846 | } | ||
663 | 847 | ||
664 | /* Clear the interrupt information. */ | 848 | /* Free the interrupt vector */ |
665 | memset(&iosapic_intr_info[vector], 0, sizeof(struct iosapic_intr_info)); | 849 | free_irq_vector(vector); |
666 | iosapic_intr_info[vector].rte_index = -1; /* mark as unused */ | 850 | } |
667 | } | 851 | } |
852 | out: | ||
668 | spin_unlock(&iosapic_lock); | 853 | spin_unlock(&iosapic_lock); |
669 | spin_unlock_irqrestore(&idesc->lock, flags); | 854 | spin_unlock_irqrestore(&idesc->lock, flags); |
670 | |||
671 | /* Free the interrupt vector */ | ||
672 | free_irq_vector(vector); | ||
673 | |||
674 | printk(KERN_INFO "GSI %u (%s, %s) -> vector %d unregisterd.\n", | ||
675 | gsi, (trigger == IOSAPIC_EDGE ? "edge" : "level"), | ||
676 | (polarity == IOSAPIC_POL_HIGH ? "high" : "low"), | ||
677 | vector); | ||
678 | } | 855 | } |
679 | #endif /* CONFIG_ACPI_DEALLOCATE_IRQ */ | 856 | #endif /* CONFIG_ACPI_DEALLOCATE_IRQ */ |
680 | 857 | ||
@@ -724,7 +901,7 @@ iosapic_register_platform_intr (u32 int_type, unsigned int gsi, | |||
724 | (polarity == IOSAPIC_POL_HIGH ? "high" : "low"), | 901 | (polarity == IOSAPIC_POL_HIGH ? "high" : "low"), |
725 | cpu_logical_id(dest), dest, vector); | 902 | cpu_logical_id(dest), dest, vector); |
726 | 903 | ||
727 | set_rte(vector, dest, mask); | 904 | set_rte(gsi, vector, dest, mask); |
728 | return vector; | 905 | return vector; |
729 | } | 906 | } |
730 | 907 | ||
@@ -750,7 +927,7 @@ iosapic_override_isa_irq (unsigned int isa_irq, unsigned int gsi, | |||
750 | polarity == IOSAPIC_POL_HIGH ? "high" : "low", | 927 | polarity == IOSAPIC_POL_HIGH ? "high" : "low", |
751 | cpu_logical_id(dest), dest, vector); | 928 | cpu_logical_id(dest), dest, vector); |
752 | 929 | ||
753 | set_rte(vector, dest, 1); | 930 | set_rte(gsi, vector, dest, 1); |
754 | } | 931 | } |
755 | 932 | ||
756 | void __init | 933 | void __init |
@@ -758,8 +935,10 @@ iosapic_system_init (int system_pcat_compat) | |||
758 | { | 935 | { |
759 | int vector; | 936 | int vector; |
760 | 937 | ||
761 | for (vector = 0; vector < IA64_NUM_VECTORS; ++vector) | 938 | for (vector = 0; vector < IA64_NUM_VECTORS; ++vector) { |
762 | iosapic_intr_info[vector].rte_index = -1; /* mark as unused */ | 939 | iosapic_intr_info[vector].low32 = IOSAPIC_MASK; |
940 | INIT_LIST_HEAD(&iosapic_intr_info[vector].rtes); /* mark as unused */ | ||
941 | } | ||
763 | 942 | ||
764 | pcat_compat = system_pcat_compat; | 943 | pcat_compat = system_pcat_compat; |
765 | if (pcat_compat) { | 944 | if (pcat_compat) { |
@@ -825,3 +1004,10 @@ map_iosapic_to_node(unsigned int gsi_base, int node) | |||
825 | return; | 1004 | return; |
826 | } | 1005 | } |
827 | #endif | 1006 | #endif |
1007 | |||
1008 | static int __init iosapic_enable_kmalloc (void) | ||
1009 | { | ||
1010 | iosapic_kmalloc_ok = 1; | ||
1011 | return 0; | ||
1012 | } | ||
1013 | core_initcall (iosapic_enable_kmalloc); | ||