Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b0079fb7 authored by Omkar Savagaonkar's avatar Omkar Savagaonkar Committed by Gerrit - the friendly Code Review server
Browse files

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: default avatarOmkar Savagaonkar <omkars@codeaurora.org>
Signed-off-by: default avatarArun KS <arunks@codeaurora.org>
parent 13a99086
Loading
Loading
Loading
Loading
+106 −2
Original line number Diff line number Diff line
@@ -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;