Loading drivers/irqchip/irq-gic-v3.c +106 −2 Original line number Diff line number Diff line Loading @@ -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" Loading @@ -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; Loading @@ -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; Loading Loading @@ -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; } Loading Loading @@ -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); } Loading Loading @@ -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; Loading Loading @@ -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 Loading
drivers/irqchip/irq-gic-v3.c +106 −2 Original line number Diff line number Diff line Loading @@ -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" Loading @@ -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; Loading @@ -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; Loading Loading @@ -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; } Loading Loading @@ -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); } Loading Loading @@ -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; Loading Loading @@ -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