Merge "drivers: irqchip: Add Deepsleep support for GIC"

This commit is contained in:
qctecmdr
2023-02-13 02:48:00 -08:00
committed by Gerrit - the friendly Code Review server

View File

@@ -15,10 +15,77 @@
#include <linux/irqchip/arm-gic-v3.h>
#include <trace/hooks/cpuidle_psci.h>
#include <trace/hooks/gic_v3.h>
#include <linux/notifier.h>
#include <linux/suspend.h>
#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)