diff options
Diffstat (limited to 'arch/arm/common/gic.c')
-rw-r--r-- | arch/arm/common/gic.c | 218 |
1 files changed, 207 insertions, 11 deletions
diff --git a/arch/arm/common/gic.c b/arch/arm/common/gic.c index 4ddd0a6ac7f..05cd423c575 100644 --- a/arch/arm/common/gic.c +++ b/arch/arm/common/gic.c | |||
@@ -29,6 +29,7 @@ | |||
29 | #include <linux/cpumask.h> | 29 | #include <linux/cpumask.h> |
30 | #include <linux/io.h> | 30 | #include <linux/io.h> |
31 | 31 | ||
32 | #include <asm/cpu_pm.h> | ||
32 | #include <asm/irq.h> | 33 | #include <asm/irq.h> |
33 | #include <asm/mach/irq.h> | 34 | #include <asm/mach/irq.h> |
34 | #include <asm/hardware/gic.h> | 35 | #include <asm/hardware/gic.h> |
@@ -38,12 +39,6 @@ static DEFINE_SPINLOCK(irq_controller_lock); | |||
38 | /* Address of GIC 0 CPU interface */ | 39 | /* Address of GIC 0 CPU interface */ |
39 | void __iomem *gic_cpu_base_addr __read_mostly; | 40 | void __iomem *gic_cpu_base_addr __read_mostly; |
40 | 41 | ||
41 | struct gic_chip_data { | ||
42 | unsigned int irq_offset; | ||
43 | void __iomem *dist_base; | ||
44 | void __iomem *cpu_base; | ||
45 | }; | ||
46 | |||
47 | /* | 42 | /* |
48 | * Supported arch specific GIC irq extension. | 43 | * Supported arch specific GIC irq extension. |
49 | * Default make them NULL. | 44 | * Default make them NULL. |
@@ -179,22 +174,32 @@ static int gic_set_affinity(struct irq_data *d, const struct cpumask *mask_val, | |||
179 | { | 174 | { |
180 | void __iomem *reg = gic_dist_base(d) + GIC_DIST_TARGET + (gic_irq(d) & ~3); | 175 | void __iomem *reg = gic_dist_base(d) + GIC_DIST_TARGET + (gic_irq(d) & ~3); |
181 | unsigned int shift = (d->irq % 4) * 8; | 176 | unsigned int shift = (d->irq % 4) * 8; |
182 | unsigned int cpu = cpumask_first(mask_val); | 177 | unsigned int cpu = cpumask_any_and(mask_val, cpu_online_mask); |
183 | u32 val, mask, bit; | 178 | u32 val, mask, bit; |
179 | #ifdef CONFIG_GIC_SET_MULTIPLE_CPUS | ||
180 | struct irq_desc *desc = irq_to_desc(d->irq); | ||
181 | #endif | ||
184 | 182 | ||
185 | if (cpu >= 8) | 183 | if (cpu >= 8 || cpu >= nr_cpu_ids) |
186 | return -EINVAL; | 184 | return -EINVAL; |
187 | 185 | ||
188 | mask = 0xff << shift; | 186 | mask = 0xff << shift; |
189 | bit = 1 << (cpu + shift); | 187 | bit = 1 << (cpu + shift); |
190 | 188 | ||
191 | spin_lock(&irq_controller_lock); | 189 | spin_lock(&irq_controller_lock); |
192 | d->node = cpu; | ||
193 | val = readl_relaxed(reg) & ~mask; | 190 | val = readl_relaxed(reg) & ~mask; |
194 | writel_relaxed(val | bit, reg); | 191 | val |= bit; |
192 | #ifdef CONFIG_GIC_SET_MULTIPLE_CPUS | ||
193 | if (desc && desc->affinity_hint) { | ||
194 | struct cpumask mask_hint; | ||
195 | if (cpumask_and(&mask_hint, desc->affinity_hint, mask_val)) | ||
196 | val |= (*cpumask_bits(&mask_hint) << shift) & mask; | ||
197 | } | ||
198 | #endif | ||
199 | writel_relaxed(val, reg); | ||
195 | spin_unlock(&irq_controller_lock); | 200 | spin_unlock(&irq_controller_lock); |
196 | 201 | ||
197 | return 0; | 202 | return IRQ_SET_MASK_OK; |
198 | } | 203 | } |
199 | #endif | 204 | #endif |
200 | 205 | ||
@@ -283,6 +288,8 @@ static void __init gic_dist_init(struct gic_chip_data *gic, | |||
283 | if (gic_irqs > 1020) | 288 | if (gic_irqs > 1020) |
284 | gic_irqs = 1020; | 289 | gic_irqs = 1020; |
285 | 290 | ||
291 | gic->gic_irqs = gic_irqs; | ||
292 | |||
286 | /* | 293 | /* |
287 | * Set all global interrupts to be level triggered, active low. | 294 | * Set all global interrupts to be level triggered, active low. |
288 | */ | 295 | */ |
@@ -350,6 +357,180 @@ static void __cpuinit gic_cpu_init(struct gic_chip_data *gic) | |||
350 | writel_relaxed(1, base + GIC_CPU_CTRL); | 357 | writel_relaxed(1, base + GIC_CPU_CTRL); |
351 | } | 358 | } |
352 | 359 | ||
360 | /* | ||
361 | * Saves the GIC distributor registers during suspend or idle. Must be called | ||
362 | * with interrupts disabled but before powering down the GIC. After calling | ||
363 | * this function, no interrupts will be delivered by the GIC, and another | ||
364 | * platform-specific wakeup source must be enabled. | ||
365 | */ | ||
366 | static void gic_dist_save(unsigned int gic_nr) | ||
367 | { | ||
368 | unsigned int gic_irqs; | ||
369 | void __iomem *dist_base; | ||
370 | int i; | ||
371 | |||
372 | if (gic_nr >= MAX_GIC_NR) | ||
373 | BUG(); | ||
374 | |||
375 | gic_irqs = gic_data[gic_nr].gic_irqs; | ||
376 | dist_base = gic_data[gic_nr].dist_base; | ||
377 | |||
378 | if (!dist_base) | ||
379 | return; | ||
380 | |||
381 | for (i = 0; i < DIV_ROUND_UP(gic_irqs, 16); i++) | ||
382 | gic_data[gic_nr].saved_spi_conf[i] = | ||
383 | readl_relaxed(dist_base + GIC_DIST_CONFIG + i * 4); | ||
384 | |||
385 | for (i = 0; i < DIV_ROUND_UP(gic_irqs, 4); i++) | ||
386 | gic_data[gic_nr].saved_spi_pri[i] = | ||
387 | readl_relaxed(dist_base + GIC_DIST_PRI + i * 4); | ||
388 | |||
389 | for (i = 0; i < DIV_ROUND_UP(gic_irqs, 4); i++) | ||
390 | gic_data[gic_nr].saved_spi_target[i] = | ||
391 | readl_relaxed(dist_base + GIC_DIST_TARGET + i * 4); | ||
392 | |||
393 | for (i = 0; i < DIV_ROUND_UP(gic_irqs, 32); i++) | ||
394 | gic_data[gic_nr].saved_spi_enable[i] = | ||
395 | readl_relaxed(dist_base + GIC_DIST_ENABLE_SET + i * 4); | ||
396 | |||
397 | writel_relaxed(0, dist_base + GIC_DIST_CTRL); | ||
398 | } | ||
399 | |||
400 | /* | ||
401 | * Restores the GIC distributor registers during resume or when coming out of | ||
402 | * idle. Must be called before enabling interrupts. If a level interrupt | ||
403 | * that occured while the GIC was suspended is still present, it will be | ||
404 | * handled normally, but any edge interrupts that occured will not be seen by | ||
405 | * the GIC and need to be handled by the platform-specific wakeup source. | ||
406 | */ | ||
407 | static void gic_dist_restore(unsigned int gic_nr) | ||
408 | { | ||
409 | unsigned int gic_irqs; | ||
410 | unsigned int i; | ||
411 | void __iomem *dist_base; | ||
412 | |||
413 | if (gic_nr >= MAX_GIC_NR) | ||
414 | BUG(); | ||
415 | |||
416 | gic_irqs = gic_data[gic_nr].gic_irqs; | ||
417 | dist_base = gic_data[gic_nr].dist_base; | ||
418 | |||
419 | if (!dist_base) | ||
420 | return; | ||
421 | |||
422 | writel_relaxed(0, dist_base + GIC_DIST_CTRL); | ||
423 | |||
424 | for (i = 0; i < DIV_ROUND_UP(gic_irqs, 16); i++) | ||
425 | writel_relaxed(gic_data[gic_nr].saved_spi_conf[i], | ||
426 | dist_base + GIC_DIST_CONFIG + i * 4); | ||
427 | |||
428 | for (i = 0; i < DIV_ROUND_UP(gic_irqs, 4); i++) | ||
429 | writel_relaxed(gic_data[gic_nr].saved_spi_pri[i], | ||
430 | dist_base + GIC_DIST_PRI + i * 4); | ||
431 | |||
432 | for (i = 0; i < DIV_ROUND_UP(gic_irqs, 4); i++) | ||
433 | writel_relaxed(gic_data[gic_nr].saved_spi_target[i], | ||
434 | dist_base + GIC_DIST_TARGET + i * 4); | ||
435 | |||
436 | for (i = 0; i < DIV_ROUND_UP(gic_irqs, 32); i++) | ||
437 | writel_relaxed(gic_data[gic_nr].saved_spi_enable[i], | ||
438 | dist_base + GIC_DIST_ENABLE_SET + i * 4); | ||
439 | |||
440 | writel_relaxed(1, dist_base + GIC_DIST_CTRL); | ||
441 | } | ||
442 | |||
443 | static void gic_cpu_save(unsigned int gic_nr) | ||
444 | { | ||
445 | int i; | ||
446 | u32 *ptr; | ||
447 | void __iomem *dist_base; | ||
448 | void __iomem *cpu_base; | ||
449 | |||
450 | if (gic_nr >= MAX_GIC_NR) | ||
451 | BUG(); | ||
452 | |||
453 | dist_base = gic_data[gic_nr].dist_base; | ||
454 | cpu_base = gic_data[gic_nr].cpu_base; | ||
455 | |||
456 | if (!dist_base || !cpu_base) | ||
457 | return; | ||
458 | |||
459 | ptr = __this_cpu_ptr(gic_data[gic_nr].saved_ppi_enable); | ||
460 | for (i = 0; i < DIV_ROUND_UP(32, 32); i++) | ||
461 | ptr[i] = readl_relaxed(dist_base + GIC_DIST_ENABLE_SET + i * 4); | ||
462 | |||
463 | ptr = __this_cpu_ptr(gic_data[gic_nr].saved_ppi_conf); | ||
464 | for (i = 0; i < DIV_ROUND_UP(32, 16); i++) | ||
465 | ptr[i] = readl_relaxed(dist_base + GIC_DIST_CONFIG + i * 4); | ||
466 | |||
467 | ptr = __this_cpu_ptr(gic_data[gic_nr].saved_ppi_pri); | ||
468 | for (i = 0; i < DIV_ROUND_UP(32, 4); i++) | ||
469 | ptr[i] = readl_relaxed(dist_base + GIC_DIST_PRI + i * 4); | ||
470 | } | ||
471 | |||
472 | static void gic_cpu_restore(unsigned int gic_nr) | ||
473 | { | ||
474 | int i; | ||
475 | u32 *ptr; | ||
476 | void __iomem *dist_base; | ||
477 | void __iomem *cpu_base; | ||
478 | |||
479 | if (gic_nr >= MAX_GIC_NR) | ||
480 | BUG(); | ||
481 | |||
482 | dist_base = gic_data[gic_nr].dist_base; | ||
483 | cpu_base = gic_data[gic_nr].cpu_base; | ||
484 | |||
485 | if (!dist_base || !cpu_base) | ||
486 | return; | ||
487 | |||
488 | ptr = __this_cpu_ptr(gic_data[gic_nr].saved_ppi_enable); | ||
489 | for (i = 0; i < DIV_ROUND_UP(32, 32); i++) | ||
490 | writel_relaxed(ptr[i], dist_base + GIC_DIST_ENABLE_SET + i * 4); | ||
491 | |||
492 | ptr = __this_cpu_ptr(gic_data[gic_nr].saved_ppi_conf); | ||
493 | for (i = 0; i < DIV_ROUND_UP(32, 16); i++) | ||
494 | writel_relaxed(ptr[i], dist_base + GIC_DIST_CONFIG + i * 4); | ||
495 | |||
496 | ptr = __this_cpu_ptr(gic_data[gic_nr].saved_ppi_pri); | ||
497 | for (i = 0; i < DIV_ROUND_UP(32, 4); i++) | ||
498 | writel_relaxed(ptr[i], dist_base + GIC_DIST_PRI + i * 4); | ||
499 | |||
500 | writel_relaxed(0xf0, cpu_base + GIC_CPU_PRIMASK); | ||
501 | writel_relaxed(1, cpu_base + GIC_CPU_CTRL); | ||
502 | } | ||
503 | |||
504 | static int gic_notifier(struct notifier_block *self, unsigned long cmd, void *v) | ||
505 | { | ||
506 | int i; | ||
507 | |||
508 | for (i = 0; i < MAX_GIC_NR; i++) { | ||
509 | switch (cmd) { | ||
510 | case CPU_PM_ENTER: | ||
511 | gic_cpu_save(i); | ||
512 | break; | ||
513 | case CPU_PM_ENTER_FAILED: | ||
514 | case CPU_PM_EXIT: | ||
515 | gic_cpu_restore(i); | ||
516 | break; | ||
517 | case CPU_COMPLEX_PM_ENTER: | ||
518 | gic_dist_save(i); | ||
519 | break; | ||
520 | case CPU_COMPLEX_PM_ENTER_FAILED: | ||
521 | case CPU_COMPLEX_PM_EXIT: | ||
522 | gic_dist_restore(i); | ||
523 | break; | ||
524 | } | ||
525 | } | ||
526 | |||
527 | return NOTIFY_OK; | ||
528 | } | ||
529 | |||
530 | static struct notifier_block gic_notifier_block = { | ||
531 | .notifier_call = gic_notifier, | ||
532 | }; | ||
533 | |||
353 | void __init gic_init(unsigned int gic_nr, unsigned int irq_start, | 534 | void __init gic_init(unsigned int gic_nr, unsigned int irq_start, |
354 | void __iomem *dist_base, void __iomem *cpu_base) | 535 | void __iomem *dist_base, void __iomem *cpu_base) |
355 | { | 536 | { |
@@ -365,8 +546,23 @@ void __init gic_init(unsigned int gic_nr, unsigned int irq_start, | |||
365 | if (gic_nr == 0) | 546 | if (gic_nr == 0) |
366 | gic_cpu_base_addr = cpu_base; | 547 | gic_cpu_base_addr = cpu_base; |
367 | 548 | ||
549 | gic_chip.flags |= gic_arch_extn.flags; | ||
368 | gic_dist_init(gic, irq_start); | 550 | gic_dist_init(gic, irq_start); |
369 | gic_cpu_init(gic); | 551 | gic_cpu_init(gic); |
552 | |||
553 | gic->saved_ppi_enable = __alloc_percpu(DIV_ROUND_UP(32, 32) * 4, | ||
554 | sizeof(u32)); | ||
555 | BUG_ON(!gic->saved_ppi_enable); | ||
556 | |||
557 | gic->saved_ppi_conf = __alloc_percpu(DIV_ROUND_UP(32, 16) * 4, | ||
558 | sizeof(u32)); | ||
559 | BUG_ON(!gic->saved_ppi_conf); | ||
560 | |||
561 | gic->saved_ppi_pri = __alloc_percpu(DIV_ROUND_UP(32, 4) * 4, | ||
562 | sizeof(u32)); | ||
563 | BUG_ON(!gic->saved_ppi_pri); | ||
564 | |||
565 | cpu_pm_register_notifier(&gic_notifier_block); | ||
370 | } | 566 | } |
371 | 567 | ||
372 | void __cpuinit gic_secondary_init(unsigned int gic_nr) | 568 | void __cpuinit gic_secondary_init(unsigned int gic_nr) |