diff options
Diffstat (limited to 'arch/arm/common/gic.c')
-rw-r--r-- | arch/arm/common/gic.c | 231 |
1 files changed, 220 insertions, 11 deletions
diff --git a/arch/arm/common/gic.c b/arch/arm/common/gic.c index bdbb3f74f0fe..a8fc6b237592 100644 --- a/arch/arm/common/gic.c +++ b/arch/arm/common/gic.c | |||
@@ -26,8 +26,12 @@ | |||
26 | #include <linux/kernel.h> | 26 | #include <linux/kernel.h> |
27 | #include <linux/list.h> | 27 | #include <linux/list.h> |
28 | #include <linux/smp.h> | 28 | #include <linux/smp.h> |
29 | #include <linux/cpu_pm.h> | ||
29 | #include <linux/cpumask.h> | 30 | #include <linux/cpumask.h> |
30 | #include <linux/io.h> | 31 | #include <linux/io.h> |
32 | #include <linux/interrupt.h> | ||
33 | #include <linux/percpu.h> | ||
34 | #include <linux/slab.h> | ||
31 | 35 | ||
32 | #include <asm/irq.h> | 36 | #include <asm/irq.h> |
33 | #include <asm/mach/irq.h> | 37 | #include <asm/mach/irq.h> |
@@ -262,6 +266,7 @@ static void __init gic_dist_init(struct gic_chip_data *gic, | |||
262 | u32 cpumask; | 266 | u32 cpumask; |
263 | void __iomem *base = gic->dist_base; | 267 | void __iomem *base = gic->dist_base; |
264 | u32 cpu = 0; | 268 | u32 cpu = 0; |
269 | u32 nrppis = 0, ppi_base = 0; | ||
265 | 270 | ||
266 | #ifdef CONFIG_SMP | 271 | #ifdef CONFIG_SMP |
267 | cpu = cpu_logical_map(smp_processor_id()); | 272 | cpu = cpu_logical_map(smp_processor_id()); |
@@ -282,6 +287,25 @@ static void __init gic_dist_init(struct gic_chip_data *gic, | |||
282 | if (gic_irqs > 1020) | 287 | if (gic_irqs > 1020) |
283 | gic_irqs = 1020; | 288 | gic_irqs = 1020; |
284 | 289 | ||
290 | gic->gic_irqs = gic_irqs; | ||
291 | |||
292 | /* | ||
293 | * Nobody would be insane enough to use PPIs on a secondary | ||
294 | * GIC, right? | ||
295 | */ | ||
296 | if (gic == &gic_data[0]) { | ||
297 | nrppis = (32 - irq_start) & 31; | ||
298 | |||
299 | /* The GIC only supports up to 16 PPIs. */ | ||
300 | if (nrppis > 16) | ||
301 | BUG(); | ||
302 | |||
303 | ppi_base = gic->irq_offset + 32 - nrppis; | ||
304 | } | ||
305 | |||
306 | pr_info("Configuring GIC with %d sources (%d PPIs)\n", | ||
307 | gic_irqs, (gic == &gic_data[0]) ? nrppis : 0); | ||
308 | |||
285 | /* | 309 | /* |
286 | * Set all global interrupts to be level triggered, active low. | 310 | * Set all global interrupts to be level triggered, active low. |
287 | */ | 311 | */ |
@@ -317,7 +341,17 @@ static void __init gic_dist_init(struct gic_chip_data *gic, | |||
317 | /* | 341 | /* |
318 | * Setup the Linux IRQ subsystem. | 342 | * Setup the Linux IRQ subsystem. |
319 | */ | 343 | */ |
320 | for (i = irq_start; i < irq_limit; i++) { | 344 | for (i = 0; i < nrppis; i++) { |
345 | int ppi = i + ppi_base; | ||
346 | |||
347 | irq_set_percpu_devid(ppi); | ||
348 | irq_set_chip_and_handler(ppi, &gic_chip, | ||
349 | handle_percpu_devid_irq); | ||
350 | irq_set_chip_data(ppi, gic); | ||
351 | set_irq_flags(ppi, IRQF_VALID | IRQF_NOAUTOEN); | ||
352 | } | ||
353 | |||
354 | for (i = irq_start + nrppis; i < irq_limit; i++) { | ||
321 | irq_set_chip_and_handler(i, &gic_chip, handle_fasteoi_irq); | 355 | irq_set_chip_and_handler(i, &gic_chip, handle_fasteoi_irq); |
322 | irq_set_chip_data(i, gic); | 356 | irq_set_chip_data(i, gic); |
323 | set_irq_flags(i, IRQF_VALID | IRQF_PROBE); | 357 | set_irq_flags(i, IRQF_VALID | IRQF_PROBE); |
@@ -349,6 +383,189 @@ static void __cpuinit gic_cpu_init(struct gic_chip_data *gic) | |||
349 | writel_relaxed(1, base + GIC_CPU_CTRL); | 383 | writel_relaxed(1, base + GIC_CPU_CTRL); |
350 | } | 384 | } |
351 | 385 | ||
386 | #ifdef CONFIG_CPU_PM | ||
387 | /* | ||
388 | * Saves the GIC distributor registers during suspend or idle. Must be called | ||
389 | * with interrupts disabled but before powering down the GIC. After calling | ||
390 | * this function, no interrupts will be delivered by the GIC, and another | ||
391 | * platform-specific wakeup source must be enabled. | ||
392 | */ | ||
393 | static void gic_dist_save(unsigned int gic_nr) | ||
394 | { | ||
395 | unsigned int gic_irqs; | ||
396 | void __iomem *dist_base; | ||
397 | int i; | ||
398 | |||
399 | if (gic_nr >= MAX_GIC_NR) | ||
400 | BUG(); | ||
401 | |||
402 | gic_irqs = gic_data[gic_nr].gic_irqs; | ||
403 | dist_base = gic_data[gic_nr].dist_base; | ||
404 | |||
405 | if (!dist_base) | ||
406 | return; | ||
407 | |||
408 | for (i = 0; i < DIV_ROUND_UP(gic_irqs, 16); i++) | ||
409 | gic_data[gic_nr].saved_spi_conf[i] = | ||
410 | readl_relaxed(dist_base + GIC_DIST_CONFIG + i * 4); | ||
411 | |||
412 | for (i = 0; i < DIV_ROUND_UP(gic_irqs, 4); i++) | ||
413 | gic_data[gic_nr].saved_spi_target[i] = | ||
414 | readl_relaxed(dist_base + GIC_DIST_TARGET + i * 4); | ||
415 | |||
416 | for (i = 0; i < DIV_ROUND_UP(gic_irqs, 32); i++) | ||
417 | gic_data[gic_nr].saved_spi_enable[i] = | ||
418 | readl_relaxed(dist_base + GIC_DIST_ENABLE_SET + i * 4); | ||
419 | } | ||
420 | |||
421 | /* | ||
422 | * Restores the GIC distributor registers during resume or when coming out of | ||
423 | * idle. Must be called before enabling interrupts. If a level interrupt | ||
424 | * that occured while the GIC was suspended is still present, it will be | ||
425 | * handled normally, but any edge interrupts that occured will not be seen by | ||
426 | * the GIC and need to be handled by the platform-specific wakeup source. | ||
427 | */ | ||
428 | static void gic_dist_restore(unsigned int gic_nr) | ||
429 | { | ||
430 | unsigned int gic_irqs; | ||
431 | unsigned int i; | ||
432 | void __iomem *dist_base; | ||
433 | |||
434 | if (gic_nr >= MAX_GIC_NR) | ||
435 | BUG(); | ||
436 | |||
437 | gic_irqs = gic_data[gic_nr].gic_irqs; | ||
438 | dist_base = gic_data[gic_nr].dist_base; | ||
439 | |||
440 | if (!dist_base) | ||
441 | return; | ||
442 | |||
443 | writel_relaxed(0, dist_base + GIC_DIST_CTRL); | ||
444 | |||
445 | for (i = 0; i < DIV_ROUND_UP(gic_irqs, 16); i++) | ||
446 | writel_relaxed(gic_data[gic_nr].saved_spi_conf[i], | ||
447 | dist_base + GIC_DIST_CONFIG + i * 4); | ||
448 | |||
449 | for (i = 0; i < DIV_ROUND_UP(gic_irqs, 4); i++) | ||
450 | writel_relaxed(0xa0a0a0a0, | ||
451 | dist_base + GIC_DIST_PRI + i * 4); | ||
452 | |||
453 | for (i = 0; i < DIV_ROUND_UP(gic_irqs, 4); i++) | ||
454 | writel_relaxed(gic_data[gic_nr].saved_spi_target[i], | ||
455 | dist_base + GIC_DIST_TARGET + i * 4); | ||
456 | |||
457 | for (i = 0; i < DIV_ROUND_UP(gic_irqs, 32); i++) | ||
458 | writel_relaxed(gic_data[gic_nr].saved_spi_enable[i], | ||
459 | dist_base + GIC_DIST_ENABLE_SET + i * 4); | ||
460 | |||
461 | writel_relaxed(1, dist_base + GIC_DIST_CTRL); | ||
462 | } | ||
463 | |||
464 | static void gic_cpu_save(unsigned int gic_nr) | ||
465 | { | ||
466 | int i; | ||
467 | u32 *ptr; | ||
468 | void __iomem *dist_base; | ||
469 | void __iomem *cpu_base; | ||
470 | |||
471 | if (gic_nr >= MAX_GIC_NR) | ||
472 | BUG(); | ||
473 | |||
474 | dist_base = gic_data[gic_nr].dist_base; | ||
475 | cpu_base = gic_data[gic_nr].cpu_base; | ||
476 | |||
477 | if (!dist_base || !cpu_base) | ||
478 | return; | ||
479 | |||
480 | ptr = __this_cpu_ptr(gic_data[gic_nr].saved_ppi_enable); | ||
481 | for (i = 0; i < DIV_ROUND_UP(32, 32); i++) | ||
482 | ptr[i] = readl_relaxed(dist_base + GIC_DIST_ENABLE_SET + i * 4); | ||
483 | |||
484 | ptr = __this_cpu_ptr(gic_data[gic_nr].saved_ppi_conf); | ||
485 | for (i = 0; i < DIV_ROUND_UP(32, 16); i++) | ||
486 | ptr[i] = readl_relaxed(dist_base + GIC_DIST_CONFIG + i * 4); | ||
487 | |||
488 | } | ||
489 | |||
490 | static void gic_cpu_restore(unsigned int gic_nr) | ||
491 | { | ||
492 | int i; | ||
493 | u32 *ptr; | ||
494 | void __iomem *dist_base; | ||
495 | void __iomem *cpu_base; | ||
496 | |||
497 | if (gic_nr >= MAX_GIC_NR) | ||
498 | BUG(); | ||
499 | |||
500 | dist_base = gic_data[gic_nr].dist_base; | ||
501 | cpu_base = gic_data[gic_nr].cpu_base; | ||
502 | |||
503 | if (!dist_base || !cpu_base) | ||
504 | return; | ||
505 | |||
506 | ptr = __this_cpu_ptr(gic_data[gic_nr].saved_ppi_enable); | ||
507 | for (i = 0; i < DIV_ROUND_UP(32, 32); i++) | ||
508 | writel_relaxed(ptr[i], dist_base + GIC_DIST_ENABLE_SET + i * 4); | ||
509 | |||
510 | ptr = __this_cpu_ptr(gic_data[gic_nr].saved_ppi_conf); | ||
511 | for (i = 0; i < DIV_ROUND_UP(32, 16); i++) | ||
512 | writel_relaxed(ptr[i], dist_base + GIC_DIST_CONFIG + i * 4); | ||
513 | |||
514 | for (i = 0; i < DIV_ROUND_UP(32, 4); i++) | ||
515 | writel_relaxed(0xa0a0a0a0, dist_base + GIC_DIST_PRI + i * 4); | ||
516 | |||
517 | writel_relaxed(0xf0, cpu_base + GIC_CPU_PRIMASK); | ||
518 | writel_relaxed(1, cpu_base + GIC_CPU_CTRL); | ||
519 | } | ||
520 | |||
521 | static int gic_notifier(struct notifier_block *self, unsigned long cmd, void *v) | ||
522 | { | ||
523 | int i; | ||
524 | |||
525 | for (i = 0; i < MAX_GIC_NR; i++) { | ||
526 | switch (cmd) { | ||
527 | case CPU_PM_ENTER: | ||
528 | gic_cpu_save(i); | ||
529 | break; | ||
530 | case CPU_PM_ENTER_FAILED: | ||
531 | case CPU_PM_EXIT: | ||
532 | gic_cpu_restore(i); | ||
533 | break; | ||
534 | case CPU_CLUSTER_PM_ENTER: | ||
535 | gic_dist_save(i); | ||
536 | break; | ||
537 | case CPU_CLUSTER_PM_ENTER_FAILED: | ||
538 | case CPU_CLUSTER_PM_EXIT: | ||
539 | gic_dist_restore(i); | ||
540 | break; | ||
541 | } | ||
542 | } | ||
543 | |||
544 | return NOTIFY_OK; | ||
545 | } | ||
546 | |||
547 | static struct notifier_block gic_notifier_block = { | ||
548 | .notifier_call = gic_notifier, | ||
549 | }; | ||
550 | |||
551 | static void __init gic_pm_init(struct gic_chip_data *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 | cpu_pm_register_notifier(&gic_notifier_block); | ||
562 | } | ||
563 | #else | ||
564 | static void __init gic_pm_init(struct gic_chip_data *gic) | ||
565 | { | ||
566 | } | ||
567 | #endif | ||
568 | |||
352 | void __init gic_init(unsigned int gic_nr, unsigned int irq_start, | 569 | void __init gic_init(unsigned int gic_nr, unsigned int irq_start, |
353 | void __iomem *dist_base, void __iomem *cpu_base) | 570 | void __iomem *dist_base, void __iomem *cpu_base) |
354 | { | 571 | { |
@@ -364,8 +581,10 @@ void __init gic_init(unsigned int gic_nr, unsigned int irq_start, | |||
364 | if (gic_nr == 0) | 581 | if (gic_nr == 0) |
365 | gic_cpu_base_addr = cpu_base; | 582 | gic_cpu_base_addr = cpu_base; |
366 | 583 | ||
584 | gic_chip.flags |= gic_arch_extn.flags; | ||
367 | gic_dist_init(gic, irq_start); | 585 | gic_dist_init(gic, irq_start); |
368 | gic_cpu_init(gic); | 586 | gic_cpu_init(gic); |
587 | gic_pm_init(gic); | ||
369 | } | 588 | } |
370 | 589 | ||
371 | void __cpuinit gic_secondary_init(unsigned int gic_nr) | 590 | void __cpuinit gic_secondary_init(unsigned int gic_nr) |
@@ -375,16 +594,6 @@ void __cpuinit gic_secondary_init(unsigned int gic_nr) | |||
375 | gic_cpu_init(&gic_data[gic_nr]); | 594 | gic_cpu_init(&gic_data[gic_nr]); |
376 | } | 595 | } |
377 | 596 | ||
378 | void __cpuinit gic_enable_ppi(unsigned int irq) | ||
379 | { | ||
380 | unsigned long flags; | ||
381 | |||
382 | local_irq_save(flags); | ||
383 | irq_set_status_flags(irq, IRQ_NOPROBE); | ||
384 | gic_unmask_irq(irq_get_irq_data(irq)); | ||
385 | local_irq_restore(flags); | ||
386 | } | ||
387 | |||
388 | #ifdef CONFIG_SMP | 597 | #ifdef CONFIG_SMP |
389 | void gic_raise_softirq(const struct cpumask *mask, unsigned int irq) | 598 | void gic_raise_softirq(const struct cpumask *mask, unsigned int irq) |
390 | { | 599 | { |