diff --git a/drivers/irqchip/msm_show_resume_irq.c b/drivers/irqchip/msm_show_resume_irq.c index 246e851dee26..e8d24ce10cb5 100644 --- a/drivers/irqchip/msm_show_resume_irq.c +++ b/drivers/irqchip/msm_show_resume_irq.c @@ -15,10 +15,77 @@ #include #include #include +#include +#include +#define MPIDR_RS(mpidr) (((mpidr) & 0xF0UL) >> 4) +#define GIC_LINE_NR min(GICD_TYPER_SPIS(gic_data->rdists.gicd_typer), 1020U) +#define gic_data_rdist() (this_cpu_ptr(gic_data->rdists.rdist)) +#define gic_data_rdist_rd_base() (gic_data_rdist()->rd_base) +#define gic_data_rdist_sgi_base() (gic_data_rdist_rd_base() + SZ_64K) + +static bool hibernation; + +struct gic_chip_data_ds { + 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; +}; + +static struct gic_chip_data_ds gic_data_ds __read_mostly; int msm_show_resume_irq_mask; module_param_named(debug_mask, msm_show_resume_irq_mask, int, 0664); +static void gic_suspend_ds(void *data, struct gic_chip_data *gic_data) +{ + int i; + void __iomem *base = gic_data->dist_base; + void __iomem *rdist_base = gic_data_rdist_sgi_base(); + + if (unlikely(!hibernation)) + return; + gic_data_ds.enabled_sgis = readl_relaxed(rdist_base + GICD_ISENABLER); + gic_data_ds.pending_sgis = readl_relaxed(rdist_base + GICD_ISPENDR); + /* Store edge level for PPIs by reading GICR_ICFGR1 */ + gic_data_ds.ppi_edg_lvl = readl_relaxed(rdist_base + GICR_ICFGR0 + 4); + + for (i = 0; i * 32 < GIC_LINE_NR; i++) { + gic_data_ds.enabled_irqs[i] = readl_relaxed(base + GICD_ISENABLER + i * 4); + gic_data_ds.active_irqs[i] = readl_relaxed(base + GICD_ISPENDR + i * 4); + } + + for (i = 2; i < GIC_LINE_NR / 16; i++) + gic_data_ds.irq_edg_lvl[i] = readl_relaxed(base + GICD_ICFGR + i * 4); +} + +static void gic_resume_ds(void *data, struct gic_chip_data *gic_data) +{ + int i; + void __iomem *base = gic_data->dist_base; + void __iomem *rdist_base = gic_data_rdist_sgi_base(); + + pr_info("Re-initializing gic in hibernation restore\n"); + gic_dist_init(); + gic_cpu_init(); + writel_relaxed(gic_data_ds.enabled_sgis, rdist_base + GICD_ISENABLER); + writel_relaxed(gic_data_ds.pending_sgis, rdist_base + GICD_ISPENDR); + /* Restore edge and level triggers for PPIs from GICR_ICFGR1 */ + writel_relaxed(gic_data_ds.ppi_edg_lvl, rdist_base + GICR_ICFGR0 + 4); + /* Restore edge and level triggers */ + for (i = 2; i < GIC_LINE_NR / 16; i++) + writel_relaxed(gic_data_ds.irq_edg_lvl[i], base + GICD_ICFGR + i * 4); + gic_dist_wait_for_rwp(); + /* Activate and enable interrupts from backup */ + for (i = 0; i * 32 < GIC_LINE_NR; i++) { + writel_relaxed(gic_data_ds.active_irqs[i], base + GICD_ISPENDR + i * 4); + writel_relaxed(gic_data_ds.enabled_irqs[i], base + GICD_ISENABLER + i * 4); + } + gic_dist_wait_for_rwp(); +} + static void msm_show_resume_irqs(void *data, struct gic_chip_data *gic_data) { struct irq_domain *domain; @@ -29,6 +96,8 @@ static void msm_show_resume_irqs(void *data, struct gic_chip_data *gic_data) u32 gic_line_nr; u32 typer; + if (unlikely(hibernation)) + gic_resume_ds(data, gic_data); if (!msm_show_resume_irq_mask) return; @@ -84,12 +153,30 @@ static void gic_s2idle_exit(void *unused, struct cpuidle_device *dev, bool s2idl atomic_dec(&cpus_in_s2idle); } +static int gic_suspend_notifier(struct notifier_block *nb, unsigned long event, void *dummy) +{ + if ((event == PM_HIBERNATION_PREPARE) || ((event == PM_SUSPEND_PREPARE) + && pm_suspend_via_firmware())) + hibernation = true; + else if ((event == PM_POST_HIBERNATION) || ((event == PM_POST_SUSPEND) + && pm_suspend_via_firmware())) + hibernation = false; + return NOTIFY_OK; +} + +static struct notifier_block gic_notif_block = { + .notifier_call = gic_suspend_notifier, +}; + static int __init msm_show_resume_irq_init(void) { register_trace_prio_android_vh_cpuidle_psci_enter(gic_s2idle_enter, NULL, INT_MAX); register_trace_prio_android_vh_cpuidle_psci_exit(gic_s2idle_exit, NULL, INT_MAX); - return register_trace_android_vh_gic_resume(msm_show_resume_irqs, NULL); + register_trace_android_vh_gic_resume(msm_show_resume_irqs, NULL); + + register_pm_notifier(&gic_notif_block); + return register_trace_android_vh_gic_suspend(gic_suspend_ds, NULL); } #if IS_MODULE(CONFIG_QCOM_SHOW_RESUME_IRQ)