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>
This commit is contained in:
parent
1fb9026344
commit
254056f3b1
@ -26,6 +26,7 @@
|
|||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/list.h>
|
#include <linux/list.h>
|
||||||
#include <linux/smp.h>
|
#include <linux/smp.h>
|
||||||
|
#include <linux/cpu_pm.h>
|
||||||
#include <linux/cpumask.h>
|
#include <linux/cpumask.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
|
|
||||||
@ -276,6 +277,8 @@ static void __init gic_dist_init(struct gic_chip_data *gic,
|
|||||||
if (gic_irqs > 1020)
|
if (gic_irqs > 1020)
|
||||||
gic_irqs = 1020;
|
gic_irqs = 1020;
|
||||||
|
|
||||||
|
gic->gic_irqs = gic_irqs;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Set all global interrupts to be level triggered, active low.
|
* Set all global interrupts to be level triggered, active low.
|
||||||
*/
|
*/
|
||||||
@ -343,6 +346,189 @@ static void __cpuinit gic_cpu_init(struct gic_chip_data *gic)
|
|||||||
writel_relaxed(1, base + GIC_CPU_CTRL);
|
writel_relaxed(1, base + GIC_CPU_CTRL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_CPU_PM
|
||||||
|
/*
|
||||||
|
* Saves the GIC distributor registers during suspend or idle. Must be called
|
||||||
|
* with interrupts disabled but before powering down the GIC. After calling
|
||||||
|
* this function, no interrupts will be delivered by the GIC, and another
|
||||||
|
* platform-specific wakeup source must be enabled.
|
||||||
|
*/
|
||||||
|
static void gic_dist_save(unsigned int gic_nr)
|
||||||
|
{
|
||||||
|
unsigned int gic_irqs;
|
||||||
|
void __iomem *dist_base;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (gic_nr >= MAX_GIC_NR)
|
||||||
|
BUG();
|
||||||
|
|
||||||
|
gic_irqs = gic_data[gic_nr].gic_irqs;
|
||||||
|
dist_base = gic_data[gic_nr].dist_base;
|
||||||
|
|
||||||
|
if (!dist_base)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (i = 0; i < DIV_ROUND_UP(gic_irqs, 16); i++)
|
||||||
|
gic_data[gic_nr].saved_spi_conf[i] =
|
||||||
|
readl_relaxed(dist_base + GIC_DIST_CONFIG + i * 4);
|
||||||
|
|
||||||
|
for (i = 0; i < DIV_ROUND_UP(gic_irqs, 4); i++)
|
||||||
|
gic_data[gic_nr].saved_spi_target[i] =
|
||||||
|
readl_relaxed(dist_base + GIC_DIST_TARGET + i * 4);
|
||||||
|
|
||||||
|
for (i = 0; i < DIV_ROUND_UP(gic_irqs, 32); i++)
|
||||||
|
gic_data[gic_nr].saved_spi_enable[i] =
|
||||||
|
readl_relaxed(dist_base + GIC_DIST_ENABLE_SET + i * 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Restores the GIC distributor registers during resume or when coming out of
|
||||||
|
* idle. Must be called before enabling interrupts. If a level interrupt
|
||||||
|
* that occured while the GIC was suspended is still present, it will be
|
||||||
|
* handled normally, but any edge interrupts that occured will not be seen by
|
||||||
|
* the GIC and need to be handled by the platform-specific wakeup source.
|
||||||
|
*/
|
||||||
|
static void gic_dist_restore(unsigned int gic_nr)
|
||||||
|
{
|
||||||
|
unsigned int gic_irqs;
|
||||||
|
unsigned int i;
|
||||||
|
void __iomem *dist_base;
|
||||||
|
|
||||||
|
if (gic_nr >= MAX_GIC_NR)
|
||||||
|
BUG();
|
||||||
|
|
||||||
|
gic_irqs = gic_data[gic_nr].gic_irqs;
|
||||||
|
dist_base = gic_data[gic_nr].dist_base;
|
||||||
|
|
||||||
|
if (!dist_base)
|
||||||
|
return;
|
||||||
|
|
||||||
|
writel_relaxed(0, dist_base + GIC_DIST_CTRL);
|
||||||
|
|
||||||
|
for (i = 0; i < DIV_ROUND_UP(gic_irqs, 16); i++)
|
||||||
|
writel_relaxed(gic_data[gic_nr].saved_spi_conf[i],
|
||||||
|
dist_base + GIC_DIST_CONFIG + i * 4);
|
||||||
|
|
||||||
|
for (i = 0; i < DIV_ROUND_UP(gic_irqs, 4); i++)
|
||||||
|
writel_relaxed(0xa0a0a0a0,
|
||||||
|
dist_base + GIC_DIST_PRI + i * 4);
|
||||||
|
|
||||||
|
for (i = 0; i < DIV_ROUND_UP(gic_irqs, 4); i++)
|
||||||
|
writel_relaxed(gic_data[gic_nr].saved_spi_target[i],
|
||||||
|
dist_base + GIC_DIST_TARGET + i * 4);
|
||||||
|
|
||||||
|
for (i = 0; i < DIV_ROUND_UP(gic_irqs, 32); i++)
|
||||||
|
writel_relaxed(gic_data[gic_nr].saved_spi_enable[i],
|
||||||
|
dist_base + GIC_DIST_ENABLE_SET + i * 4);
|
||||||
|
|
||||||
|
writel_relaxed(1, dist_base + GIC_DIST_CTRL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gic_cpu_save(unsigned int gic_nr)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
u32 *ptr;
|
||||||
|
void __iomem *dist_base;
|
||||||
|
void __iomem *cpu_base;
|
||||||
|
|
||||||
|
if (gic_nr >= MAX_GIC_NR)
|
||||||
|
BUG();
|
||||||
|
|
||||||
|
dist_base = gic_data[gic_nr].dist_base;
|
||||||
|
cpu_base = gic_data[gic_nr].cpu_base;
|
||||||
|
|
||||||
|
if (!dist_base || !cpu_base)
|
||||||
|
return;
|
||||||
|
|
||||||
|
ptr = __this_cpu_ptr(gic_data[gic_nr].saved_ppi_enable);
|
||||||
|
for (i = 0; i < DIV_ROUND_UP(32, 32); i++)
|
||||||
|
ptr[i] = readl_relaxed(dist_base + GIC_DIST_ENABLE_SET + i * 4);
|
||||||
|
|
||||||
|
ptr = __this_cpu_ptr(gic_data[gic_nr].saved_ppi_conf);
|
||||||
|
for (i = 0; i < DIV_ROUND_UP(32, 16); i++)
|
||||||
|
ptr[i] = readl_relaxed(dist_base + GIC_DIST_CONFIG + i * 4);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gic_cpu_restore(unsigned int gic_nr)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
u32 *ptr;
|
||||||
|
void __iomem *dist_base;
|
||||||
|
void __iomem *cpu_base;
|
||||||
|
|
||||||
|
if (gic_nr >= MAX_GIC_NR)
|
||||||
|
BUG();
|
||||||
|
|
||||||
|
dist_base = gic_data[gic_nr].dist_base;
|
||||||
|
cpu_base = gic_data[gic_nr].cpu_base;
|
||||||
|
|
||||||
|
if (!dist_base || !cpu_base)
|
||||||
|
return;
|
||||||
|
|
||||||
|
ptr = __this_cpu_ptr(gic_data[gic_nr].saved_ppi_enable);
|
||||||
|
for (i = 0; i < DIV_ROUND_UP(32, 32); i++)
|
||||||
|
writel_relaxed(ptr[i], dist_base + GIC_DIST_ENABLE_SET + i * 4);
|
||||||
|
|
||||||
|
ptr = __this_cpu_ptr(gic_data[gic_nr].saved_ppi_conf);
|
||||||
|
for (i = 0; i < DIV_ROUND_UP(32, 16); i++)
|
||||||
|
writel_relaxed(ptr[i], dist_base + GIC_DIST_CONFIG + i * 4);
|
||||||
|
|
||||||
|
for (i = 0; i < DIV_ROUND_UP(32, 4); i++)
|
||||||
|
writel_relaxed(0xa0a0a0a0, dist_base + GIC_DIST_PRI + i * 4);
|
||||||
|
|
||||||
|
writel_relaxed(0xf0, cpu_base + GIC_CPU_PRIMASK);
|
||||||
|
writel_relaxed(1, cpu_base + GIC_CPU_CTRL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int gic_notifier(struct notifier_block *self, unsigned long cmd, void *v)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < MAX_GIC_NR; i++) {
|
||||||
|
switch (cmd) {
|
||||||
|
case CPU_PM_ENTER:
|
||||||
|
gic_cpu_save(i);
|
||||||
|
break;
|
||||||
|
case CPU_PM_ENTER_FAILED:
|
||||||
|
case CPU_PM_EXIT:
|
||||||
|
gic_cpu_restore(i);
|
||||||
|
break;
|
||||||
|
case CPU_CLUSTER_PM_ENTER:
|
||||||
|
gic_dist_save(i);
|
||||||
|
break;
|
||||||
|
case CPU_CLUSTER_PM_ENTER_FAILED:
|
||||||
|
case CPU_CLUSTER_PM_EXIT:
|
||||||
|
gic_dist_restore(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NOTIFY_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct notifier_block gic_notifier_block = {
|
||||||
|
.notifier_call = gic_notifier,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void __init gic_pm_init(struct gic_chip_data *gic)
|
||||||
|
{
|
||||||
|
gic->saved_ppi_enable = __alloc_percpu(DIV_ROUND_UP(32, 32) * 4,
|
||||||
|
sizeof(u32));
|
||||||
|
BUG_ON(!gic->saved_ppi_enable);
|
||||||
|
|
||||||
|
gic->saved_ppi_conf = __alloc_percpu(DIV_ROUND_UP(32, 16) * 4,
|
||||||
|
sizeof(u32));
|
||||||
|
BUG_ON(!gic->saved_ppi_conf);
|
||||||
|
|
||||||
|
cpu_pm_register_notifier(&gic_notifier_block);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
static void __init gic_pm_init(struct gic_chip_data *gic)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void __init gic_init(unsigned int gic_nr, unsigned int irq_start,
|
void __init gic_init(unsigned int gic_nr, unsigned int irq_start,
|
||||||
void __iomem *dist_base, void __iomem *cpu_base)
|
void __iomem *dist_base, void __iomem *cpu_base)
|
||||||
{
|
{
|
||||||
@ -360,6 +546,7 @@ void __init gic_init(unsigned int gic_nr, unsigned int irq_start,
|
|||||||
|
|
||||||
gic_dist_init(gic, irq_start);
|
gic_dist_init(gic, irq_start);
|
||||||
gic_cpu_init(gic);
|
gic_cpu_init(gic);
|
||||||
|
gic_pm_init(gic);
|
||||||
}
|
}
|
||||||
|
|
||||||
void __cpuinit gic_secondary_init(unsigned int gic_nr)
|
void __cpuinit gic_secondary_init(unsigned int gic_nr)
|
||||||
|
@ -46,6 +46,14 @@ struct gic_chip_data {
|
|||||||
unsigned int irq_offset;
|
unsigned int irq_offset;
|
||||||
void __iomem *dist_base;
|
void __iomem *dist_base;
|
||||||
void __iomem *cpu_base;
|
void __iomem *cpu_base;
|
||||||
|
#ifdef CONFIG_CPU_PM
|
||||||
|
u32 saved_spi_enable[DIV_ROUND_UP(1020, 32)];
|
||||||
|
u32 saved_spi_conf[DIV_ROUND_UP(1020, 16)];
|
||||||
|
u32 saved_spi_target[DIV_ROUND_UP(1020, 4)];
|
||||||
|
u32 __percpu *saved_ppi_enable;
|
||||||
|
u32 __percpu *saved_ppi_conf;
|
||||||
|
#endif
|
||||||
|
unsigned int gic_irqs;
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user