irqchip/gic-v3: Re-init GIC hardware upon hibernation restore

Code added in this patch takes backup of different set of
registers during hibernation suspend. On receiving hibernation
restore callback, it restores register values from backup. This
ensures state of hardware to be same just before hibernation and
after restore.

Change-Id: I36cfce8901ccfd82562764d8f6614f8760273248
Signed-off-by: Omkar Savagaonkar <omkars@codeaurora.org>
Signed-off-by: Arun KS <arunks@codeaurora.org>
tirimbino
Omkar Savagaonkar 6 years ago committed by Gerrit - the friendly Code Review server
parent 13a9908688
commit b0079fb73c
  1. 108
      drivers/irqchip/irq-gic-v3.c

@ -41,6 +41,8 @@
#include <asm/virt.h>
#include <linux/syscore_ops.h>
#include <linux/suspend.h>
#include <linux/notifier.h>
#include "irq-gic-common.h"
@ -60,6 +62,14 @@ struct gic_chip_data {
u32 nr_redist_regions;
unsigned int irq_nr;
struct partition_desc *ppi_descs[16];
#ifdef CONFIG_HIBERNATION
unsigned int enabled_irqs[32];
unsigned int active_irqs[32];
unsigned int irq_edg_lvl[64];
unsigned int ppi_edg_lvl;
unsigned int enabled_sgis;
unsigned int pending_sgis;
#endif
};
static struct gic_chip_data gic_data __read_mostly;
@ -74,6 +84,9 @@ static struct gic_kvm_info gic_v3_kvm_info;
/* Our default, arbitrary priority value. Linux only uses one anyway. */
#define DEFAULT_PMR_VALUE 0xf0
static void gic_dist_init(void);
static void gic_cpu_init(void);
static inline unsigned int gic_irq(struct irq_data *d)
{
return d->hwirq;
@ -334,9 +347,54 @@ static int gic_irq_set_vcpu_affinity(struct irq_data *d, void *vcpu)
}
#ifdef CONFIG_PM
#ifdef CONFIG_HIBERNATION
extern int in_suspend;
static bool hibernation;
static int gic_suspend_notifier(struct notifier_block *nb,
unsigned long event,
void *dummy)
{
if (event == PM_HIBERNATION_PREPARE)
hibernation = true;
else if (event == PM_POST_HIBERNATION)
hibernation = false;
return NOTIFY_OK;
}
static struct notifier_block gic_notif_block = {
.notifier_call = gic_suspend_notifier,
};
static void gic_hibernation_suspend(void)
{
int i;
void __iomem *base = gic_data.dist_base;
void __iomem *rdist_base = gic_data_rdist_sgi_base();
gic_data.enabled_sgis = readl_relaxed(rdist_base + GICD_ISENABLER);
gic_data.pending_sgis = readl_relaxed(rdist_base + GICD_ISPENDR);
/* Store edge level for PPIs by reading GICR_ICFGR1 */
gic_data.ppi_edg_lvl = readl_relaxed(rdist_base + GICR_ICFGR0 + 4);
for (i = 0; i * 32 < gic_data.irq_nr; i++) {
gic_data.enabled_irqs[i] = readl_relaxed(base +
GICD_ISENABLER + i * 4);
gic_data.active_irqs[i] = readl_relaxed(base +
GICD_ISPENDR + i * 4);
}
for (i = 2; i < gic_data.irq_nr / 16; i++)
gic_data.irq_edg_lvl[i] = readl_relaxed(base +
GICD_ICFGR + i * 4);
}
#endif
static int gic_suspend(void)
{
#ifdef CONFIG_HIBERNATION
if (unlikely(hibernation))
gic_hibernation_suspend();
#endif
return 0;
}
@ -379,6 +437,48 @@ static void gic_resume_one(struct gic_chip_data *gic)
static void gic_resume(void)
{
#ifdef CONFIG_HIBERNATION
int i;
void __iomem *base = gic_data.dist_base;
void __iomem *rdist_base = gic_data_rdist_sgi_base();
/*
* in_suspend is defined in hibernate.c and will be 0 during
* hibernation restore case. Also it willl be 0 for suspend to ram case
* and similar cases. Underlying code will not get executed in regular
* cases and will be executed only for hibernation restore.
*/
if (unlikely((in_suspend == 0 && hibernation))) {
pr_info("Re-initializing gic in hibernation restore\n");
gic_dist_init();
gic_cpu_init();
/* Activate and enable SGIs and PPIs */
writel_relaxed(gic_data.enabled_sgis,
rdist_base + GICD_ISENABLER);
writel_relaxed(gic_data.pending_sgis,
rdist_base + GICD_ISPENDR);
/* Restore edge and level triggers for PPIs from GICR_ICFGR1 */
writel_relaxed(gic_data.ppi_edg_lvl,
rdist_base + GICR_ICFGR0 + 4);
/* Restore edge and level triggers */
for (i = 2; i < gic_data.irq_nr / 16; i++)
writel_relaxed(gic_data.irq_edg_lvl[i],
base + GICD_ICFGR + i * 4);
gic_dist_wait_for_rwp();
/* Activate and enable interupts from backup */
for (i = 0; i * 32 < gic_data.irq_nr; i++) {
writel_relaxed(gic_data.active_irqs[i],
base + GICD_ISPENDR + i * 4);
writel_relaxed(gic_data.enabled_irqs[i],
base + GICD_ISENABLER + i * 4);
}
gic_dist_wait_for_rwp();
}
#endif
gic_resume_one(&gic_data);
}
@ -458,7 +558,7 @@ static asmlinkage void __exception_irq_entry gic_handle_irq(struct pt_regs *regs
} while (irqnr != ICC_IAR1_EL1_SPURIOUS);
}
static void __init gic_dist_init(void)
static void gic_dist_init(void)
{
unsigned int i;
u64 affinity;
@ -1278,7 +1378,11 @@ static int __init gic_of_init(struct device_node *node, struct device_node *pare
redist_stride, &node->fwnode);
if (err)
goto out_unmap_rdist;
#ifdef CONFIG_HIBERNATION
err = register_pm_notifier(&gic_notif_block);
if (err)
goto out_unmap_rdist;
#endif
gic_populate_ppi_partitions(node);
gic_of_setup_kvm_info(node);
return 0;

Loading…
Cancel
Save