aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm
diff options
context:
space:
mode:
authorColin Cross <ccross@android.com>2011-02-10 15:54:10 -0500
committerSantosh Shilimkar <santosh.shilimkar@ti.com>2011-09-23 02:35:30 -0400
commit254056f3b12563c11e6dbcfad2fbfce20a4f3302 (patch)
tree4e25156bfe5cc0613a0aaeba91233bf9936a8d92 /arch/arm
parent1fb9026344ea9ecc7c5860240104109112384a61 (diff)
ARM: gic: Use cpu pm notifiers to save gic state
When the cpu is powered down in a low power mode, the gic cpu interface may be reset, and when the cpu cluster is powered down, the gic distributor may also be reset. This patch uses CPU_PM_ENTER and CPU_PM_EXIT notifiers to save and restore the gic cpu interface registers, and the CPU_CLUSTER_PM_ENTER and CPU_CLUSTER_PM_EXIT notifiers to save and restore the gic distributor registers. Original-author: Gary King <gking@nvidia.com> Signed-off-by: Colin Cross <ccross@android.com> Signed-off-by: Santosh Shilimkar <santosh.shilimkar@ti.com> Tested-and-Acked-by: Shawn Guo <shawn.guo@linaro.org> Tested-by: Vishwanath BS <vishwanath.bs@ti.com>
Diffstat (limited to 'arch/arm')
-rw-r--r--arch/arm/common/gic.c187
-rw-r--r--arch/arm/include/asm/hardware/gic.h8
2 files changed, 195 insertions, 0 deletions
diff --git a/arch/arm/common/gic.c b/arch/arm/common/gic.c
index 3227ca952a12..66c7c4820108 100644
--- a/arch/arm/common/gic.c
+++ b/arch/arm/common/gic.c
@@ -26,6 +26,7 @@
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>
31 32
@@ -276,6 +277,8 @@ static void __init gic_dist_init(struct gic_chip_data *gic,
276 if (gic_irqs > 1020) 277 if (gic_irqs > 1020)
277 gic_irqs = 1020; 278 gic_irqs = 1020;
278 279
280 gic->gic_irqs = gic_irqs;
281
279 /* 282 /*
280 * Set all global interrupts to be level triggered, active low. 283 * Set all global interrupts to be level triggered, active low.
281 */ 284 */
@@ -343,6 +346,189 @@ static void __cpuinit gic_cpu_init(struct gic_chip_data *gic)
343 writel_relaxed(1, base + GIC_CPU_CTRL); 346 writel_relaxed(1, base + GIC_CPU_CTRL);
344} 347}
345 348
349#ifdef CONFIG_CPU_PM
350/*
351 * Saves the GIC distributor registers during suspend or idle. Must be called
352 * with interrupts disabled but before powering down the GIC. After calling
353 * this function, no interrupts will be delivered by the GIC, and another
354 * platform-specific wakeup source must be enabled.
355 */
356static void gic_dist_save(unsigned int gic_nr)
357{
358 unsigned int gic_irqs;
359 void __iomem *dist_base;
360 int i;
361
362 if (gic_nr >= MAX_GIC_NR)
363 BUG();
364
365 gic_irqs = gic_data[gic_nr].gic_irqs;
366 dist_base = gic_data[gic_nr].dist_base;
367
368 if (!dist_base)
369 return;
370
371 for (i = 0; i < DIV_ROUND_UP(gic_irqs, 16); i++)
372 gic_data[gic_nr].saved_spi_conf[i] =
373 readl_relaxed(dist_base + GIC_DIST_CONFIG + i * 4);
374
375 for (i = 0; i < DIV_ROUND_UP(gic_irqs, 4); i++)
376 gic_data[gic_nr].saved_spi_target[i] =
377 readl_relaxed(dist_base + GIC_DIST_TARGET + i * 4);
378
379 for (i = 0; i < DIV_ROUND_UP(gic_irqs, 32); i++)
380 gic_data[gic_nr].saved_spi_enable[i] =
381 readl_relaxed(dist_base + GIC_DIST_ENABLE_SET + i * 4);
382}
383
384/*
385 * Restores the GIC distributor registers during resume or when coming out of
386 * idle. Must be called before enabling interrupts. If a level interrupt
387 * that occured while the GIC was suspended is still present, it will be
388 * handled normally, but any edge interrupts that occured will not be seen by
389 * the GIC and need to be handled by the platform-specific wakeup source.
390 */
391static void gic_dist_restore(unsigned int gic_nr)
392{
393 unsigned int gic_irqs;
394 unsigned int i;
395 void __iomem *dist_base;
396
397 if (gic_nr >= MAX_GIC_NR)
398 BUG();
399
400 gic_irqs = gic_data[gic_nr].gic_irqs;
401 dist_base = gic_data[gic_nr].dist_base;
402
403 if (!dist_base)
404 return;
405
406 writel_relaxed(0, dist_base + GIC_DIST_CTRL);
407
408 for (i = 0; i < DIV_ROUND_UP(gic_irqs, 16); i++)
409 writel_relaxed(gic_data[gic_nr].saved_spi_conf[i],
410 dist_base + GIC_DIST_CONFIG + i * 4);
411
412 for (i = 0; i < DIV_ROUND_UP(gic_irqs, 4); i++)
413 writel_relaxed(0xa0a0a0a0,
414 dist_base + GIC_DIST_PRI + i * 4);
415
416 for (i = 0; i < DIV_ROUND_UP(gic_irqs, 4); i++)
417 writel_relaxed(gic_data[gic_nr].saved_spi_target[i],
418 dist_base + GIC_DIST_TARGET + i * 4);
419
420 for (i = 0; i < DIV_ROUND_UP(gic_irqs, 32); i++)
421 writel_relaxed(gic_data[gic_nr].saved_spi_enable[i],
422 dist_base + GIC_DIST_ENABLE_SET + i * 4);
423
424 writel_relaxed(1, dist_base + GIC_DIST_CTRL);
425}
426
427static void gic_cpu_save(unsigned int gic_nr)
428{
429 int i;
430 u32 *ptr;
431 void __iomem *dist_base;
432 void __iomem *cpu_base;
433
434 if (gic_nr >= MAX_GIC_NR)
435 BUG();
436
437 dist_base = gic_data[gic_nr].dist_base;
438 cpu_base = gic_data[gic_nr].cpu_base;
439
440 if (!dist_base || !cpu_base)
441 return;
442
443 ptr = __this_cpu_ptr(gic_data[gic_nr].saved_ppi_enable);
444 for (i = 0; i < DIV_ROUND_UP(32, 32); i++)
445 ptr[i] = readl_relaxed(dist_base + GIC_DIST_ENABLE_SET + i * 4);
446
447 ptr = __this_cpu_ptr(gic_data[gic_nr].saved_ppi_conf);
448 for (i = 0; i < DIV_ROUND_UP(32, 16); i++)
449 ptr[i] = readl_relaxed(dist_base + GIC_DIST_CONFIG + i * 4);
450
451}
452
453static void gic_cpu_restore(unsigned int gic_nr)
454{
455 int i;
456 u32 *ptr;
457 void __iomem *dist_base;
458 void __iomem *cpu_base;
459
460 if (gic_nr >= MAX_GIC_NR)
461 BUG();
462
463 dist_base = gic_data[gic_nr].dist_base;
464 cpu_base = gic_data[gic_nr].cpu_base;
465
466 if (!dist_base || !cpu_base)
467 return;
468
469 ptr = __this_cpu_ptr(gic_data[gic_nr].saved_ppi_enable);
470 for (i = 0; i < DIV_ROUND_UP(32, 32); i++)
471 writel_relaxed(ptr[i], dist_base + GIC_DIST_ENABLE_SET + i * 4);
472
473 ptr = __this_cpu_ptr(gic_data[gic_nr].saved_ppi_conf);
474 for (i = 0; i < DIV_ROUND_UP(32, 16); i++)
475 writel_relaxed(ptr[i], dist_base + GIC_DIST_CONFIG + i * 4);
476
477 for (i = 0; i < DIV_ROUND_UP(32, 4); i++)
478 writel_relaxed(0xa0a0a0a0, dist_base + GIC_DIST_PRI + i * 4);
479
480 writel_relaxed(0xf0, cpu_base + GIC_CPU_PRIMASK);
481 writel_relaxed(1, cpu_base + GIC_CPU_CTRL);
482}
483
484static int gic_notifier(struct notifier_block *self, unsigned long cmd, void *v)
485{
486 int i;
487
488 for (i = 0; i < MAX_GIC_NR; i++) {
489 switch (cmd) {
490 case CPU_PM_ENTER:
491 gic_cpu_save(i);
492 break;
493 case CPU_PM_ENTER_FAILED:
494 case CPU_PM_EXIT:
495 gic_cpu_restore(i);
496 break;
497 case CPU_CLUSTER_PM_ENTER:
498 gic_dist_save(i);
499 break;
500 case CPU_CLUSTER_PM_ENTER_FAILED:
501 case CPU_CLUSTER_PM_EXIT:
502 gic_dist_restore(i);
503 break;
504 }
505 }
506
507 return NOTIFY_OK;
508}
509
510static struct notifier_block gic_notifier_block = {
511 .notifier_call = gic_notifier,
512};
513
514static void __init gic_pm_init(struct gic_chip_data *gic)
515{
516 gic->saved_ppi_enable = __alloc_percpu(DIV_ROUND_UP(32, 32) * 4,
517 sizeof(u32));
518 BUG_ON(!gic->saved_ppi_enable);
519
520 gic->saved_ppi_conf = __alloc_percpu(DIV_ROUND_UP(32, 16) * 4,
521 sizeof(u32));
522 BUG_ON(!gic->saved_ppi_conf);
523
524 cpu_pm_register_notifier(&gic_notifier_block);
525}
526#else
527static void __init gic_pm_init(struct gic_chip_data *gic)
528{
529}
530#endif
531
346void __init gic_init(unsigned int gic_nr, unsigned int irq_start, 532void __init gic_init(unsigned int gic_nr, unsigned int irq_start,
347 void __iomem *dist_base, void __iomem *cpu_base) 533 void __iomem *dist_base, void __iomem *cpu_base)
348{ 534{
@@ -360,6 +546,7 @@ void __init gic_init(unsigned int gic_nr, unsigned int irq_start,
360 546
361 gic_dist_init(gic, irq_start); 547 gic_dist_init(gic, irq_start);
362 gic_cpu_init(gic); 548 gic_cpu_init(gic);
549 gic_pm_init(gic);
363} 550}
364 551
365void __cpuinit gic_secondary_init(unsigned int gic_nr) 552void __cpuinit gic_secondary_init(unsigned int gic_nr)
diff --git a/arch/arm/include/asm/hardware/gic.h b/arch/arm/include/asm/hardware/gic.h
index 435d3f86c708..c5627057b1c7 100644
--- a/arch/arm/include/asm/hardware/gic.h
+++ b/arch/arm/include/asm/hardware/gic.h
@@ -46,6 +46,14 @@ struct gic_chip_data {
46 unsigned int irq_offset; 46 unsigned int irq_offset;
47 void __iomem *dist_base; 47 void __iomem *dist_base;
48 void __iomem *cpu_base; 48 void __iomem *cpu_base;
49#ifdef CONFIG_CPU_PM
50 u32 saved_spi_enable[DIV_ROUND_UP(1020, 32)];
51 u32 saved_spi_conf[DIV_ROUND_UP(1020, 16)];
52 u32 saved_spi_target[DIV_ROUND_UP(1020, 4)];
53 u32 __percpu *saved_ppi_enable;
54 u32 __percpu *saved_ppi_conf;
55#endif
56 unsigned int gic_irqs;
49}; 57};
50#endif 58#endif
51 59