#include <linux/pm_runtime.h>
#include <linux/reset.h>
#include <linux/spinlock.h>
+#include <linux/syscore_ops.h>
/* DT "interrupts" indexes */
#define ICU_IRQ_START 1
#define ICU_RZG3E_TSSEL_MAX_VAL 0x8c
#define ICU_RZV2H_TSSEL_MAX_VAL 0x55
+/**
+ * struct rzv2h_irqc_reg_cache - registers cache (necessary for suspend/resume)
+ * @nitsr: ICU_NITSR register
+ * @iitsr: ICU_IITSR register
+ * @titsr: ICU_TITSR registers
+ */
+struct rzv2h_irqc_reg_cache {
+ u32 nitsr;
+ u32 iitsr;
+ u32 titsr[2];
+};
+
/**
* struct rzv2h_hw_info - Interrupt Control Unit controller hardware info structure.
* @tssel_lut: TINT lookup table
* @fwspec: IRQ firmware specific data
* @lock: Lock to serialize access to hardware registers
* @info: Pointer to struct rzv2h_hw_info
+ * @cache: Registers cache for suspend/resume
*/
-struct rzv2h_icu_priv {
+static struct rzv2h_icu_priv {
void __iomem *base;
struct irq_fwspec fwspec[ICU_NUM_IRQ];
raw_spinlock_t lock;
const struct rzv2h_hw_info *info;
-};
+ struct rzv2h_irqc_reg_cache cache;
+} *rzv2h_icu_data;
void rzv2h_icu_register_dma_req(struct platform_device *icu_dev, u8 dmac_index, u8 dmac_channel,
u16 req_no)
return irq_chip_set_type_parent(d, IRQ_TYPE_LEVEL_HIGH);
}
+static int rzv2h_irqc_irq_suspend(void *data)
+{
+ struct rzv2h_irqc_reg_cache *cache = &rzv2h_icu_data->cache;
+ void __iomem *base = rzv2h_icu_data->base;
+
+ cache->nitsr = readl_relaxed(base + ICU_NITSR);
+ cache->iitsr = readl_relaxed(base + ICU_IITSR);
+ for (unsigned int i = 0; i < 2; i++)
+ cache->titsr[i] = readl_relaxed(base + rzv2h_icu_data->info->t_offs + ICU_TITSR(i));
+
+ return 0;
+}
+
+static void rzv2h_irqc_irq_resume(void *data)
+{
+ struct rzv2h_irqc_reg_cache *cache = &rzv2h_icu_data->cache;
+ void __iomem *base = rzv2h_icu_data->base;
+
+ /*
+ * Restore only interrupt type. TSSRx will be restored at the
+ * request of pin controller to avoid spurious interrupts due
+ * to invalid PIN states.
+ */
+ for (unsigned int i = 0; i < 2; i++)
+ writel_relaxed(cache->titsr[i], base + rzv2h_icu_data->info->t_offs + ICU_TITSR(i));
+ writel_relaxed(cache->iitsr, base + ICU_IITSR);
+ writel_relaxed(cache->nitsr, base + ICU_NITSR);
+}
+
+static const struct syscore_ops rzv2h_irqc_syscore_ops = {
+ .suspend = rzv2h_irqc_irq_suspend,
+ .resume = rzv2h_irqc_irq_resume,
+};
+
+static struct syscore rzv2h_irqc_syscore = {
+ .ops = &rzv2h_irqc_syscore_ops,
+};
+
static const struct irq_chip rzv2h_icu_chip = {
.name = "rzv2h-icu",
.irq_eoi = rzv2h_icu_eoi,
{
struct irq_domain *irq_domain, *parent_domain;
struct device_node *node = pdev->dev.of_node;
- struct rzv2h_icu_priv *rzv2h_icu_data;
struct reset_control *resetn;
int ret;
rzv2h_icu_data->info = hw_info;
+ register_syscore(&rzv2h_irqc_syscore);
+
/*
* coccicheck complains about a missing put_device call before returning, but it's a false
* positive. We still need &pdev->dev after successfully returning from this function.