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

Commit 90ca81d0 authored by qctecmdr's avatar qctecmdr Committed by Gerrit - the friendly Code Review server
Browse files

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

parents e4986b0e b0079fb7
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;