Skip to content

Commit 74d2ef5

Browse files
claudiubezneaKAGA-KOKO
authored andcommitted
irqchip/renesas-rzg2l: Add support for suspend to RAM
The irqchip-renesas-rzg2l driver is used on RZ/G3S SoC. RZ/G3S can go into deep sleep states where power to different SoC's parts is cut off and RAM is switched to self-refresh. The resume from these states is done with the help of the bootloader. The IA55 IRQ controller needs to be reconfigured when resuming from deep sleep state. For this the IA55 registers are cached in suspend and restored in resume. The IA55 IRQ controller is connected to GPIO controller and GIC as follows: ┌──────────┐ ┌──────────┐ │ │ SPIX │ │ │ ├─────────►│ │ │ │ │ │ │ │ │ │ ┌────────┐IRQ0-7 │ IA55 │ │ GIC │ Pin0 ───────►│ ├─────────────►│ │ │ │ │ │ │ │ PPIY │ │ ... │ GPIO │ │ ├─────────►│ │ │ │GPIOINT0-127 │ │ │ │ PinN ───────►│ ├─────────────►│ │ │ │ └────────┘ └──────────┘ └──────────┘ where: - Pin0 is the first GPIO controller pin - PinN is the last GPIO controller pin - SPIX is the SPI interrupt with identifier X - PPIY is the PPI interrupt with identifier Y Implement suspend/resume functionality with syscore_ops to be able to cache/restore the registers after/before the GPIO controller suspend/resume functions are invoked. As the syscore_ops suspend/resume functions do not take any argument make the driver private data static so it can be accessed from the suspend/resume functions. The IA55 interrupt controller is resumed before the GPIO controller. As GPIO pins could be in an a state which causes spurious interrupts, the reconfiguration of the interrupt controller is restricted to restore the interrupt type and leave them disabled. An eventually required interrupt enable operation will be done as part of the GPIO controller resume function after restoring the GPIO state. [ tglx: Massaged changelog ] Signed-off-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com> Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Link: https://lore.kernel.org/r/20231120111820.87398-8-claudiu.beznea.uj@bp.renesas.com
1 parent 2eca473 commit 74d2ef5

1 file changed

Lines changed: 57 additions & 11 deletions

File tree

drivers/irqchip/irq-renesas-rzg2l.c

Lines changed: 57 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include <linux/pm_runtime.h>
1919
#include <linux/reset.h>
2020
#include <linux/spinlock.h>
21+
#include <linux/syscore_ops.h>
2122

2223
#define IRQC_IRQ_START 1
2324
#define IRQC_IRQ_COUNT 8
@@ -55,17 +56,29 @@
5556
#define TINT_EXTRACT_HWIRQ(x) FIELD_GET(GENMASK(15, 0), (x))
5657
#define TINT_EXTRACT_GPIOINT(x) FIELD_GET(GENMASK(31, 16), (x))
5758

59+
/**
60+
* struct rzg2l_irqc_reg_cache - registers cache (necessary for suspend/resume)
61+
* @iitsr: IITSR register
62+
* @titsr: TITSR registers
63+
*/
64+
struct rzg2l_irqc_reg_cache {
65+
u32 iitsr;
66+
u32 titsr[2];
67+
};
68+
5869
/**
5970
* struct rzg2l_irqc_priv - IRQ controller private data structure
6071
* @base: Controller's base address
6172
* @fwspec: IRQ firmware specific data
6273
* @lock: Lock to serialize access to hardware registers
74+
* @cache: Registers cache for suspend/resume
6375
*/
64-
struct rzg2l_irqc_priv {
76+
static struct rzg2l_irqc_priv {
6577
void __iomem *base;
6678
struct irq_fwspec fwspec[IRQC_NUM_IRQ];
6779
raw_spinlock_t lock;
68-
};
80+
struct rzg2l_irqc_reg_cache cache;
81+
} *rzg2l_irqc_data;
6982

7083
static struct rzg2l_irqc_priv *irq_data_to_priv(struct irq_data *data)
7184
{
@@ -246,6 +259,38 @@ static int rzg2l_irqc_set_type(struct irq_data *d, unsigned int type)
246259
return irq_chip_set_type_parent(d, IRQ_TYPE_LEVEL_HIGH);
247260
}
248261

262+
static int rzg2l_irqc_irq_suspend(void)
263+
{
264+
struct rzg2l_irqc_reg_cache *cache = &rzg2l_irqc_data->cache;
265+
void __iomem *base = rzg2l_irqc_data->base;
266+
267+
cache->iitsr = readl_relaxed(base + IITSR);
268+
for (u8 i = 0; i < 2; i++)
269+
cache->titsr[i] = readl_relaxed(base + TITSR(i));
270+
271+
return 0;
272+
}
273+
274+
static void rzg2l_irqc_irq_resume(void)
275+
{
276+
struct rzg2l_irqc_reg_cache *cache = &rzg2l_irqc_data->cache;
277+
void __iomem *base = rzg2l_irqc_data->base;
278+
279+
/*
280+
* Restore only interrupt type. TSSRx will be restored at the
281+
* request of pin controller to avoid spurious interrupts due
282+
* to invalid PIN states.
283+
*/
284+
for (u8 i = 0; i < 2; i++)
285+
writel_relaxed(cache->titsr[i], base + TITSR(i));
286+
writel_relaxed(cache->iitsr, base + IITSR);
287+
}
288+
289+
static struct syscore_ops rzg2l_irqc_syscore_ops = {
290+
.suspend = rzg2l_irqc_irq_suspend,
291+
.resume = rzg2l_irqc_irq_resume,
292+
};
293+
249294
static const struct irq_chip irqc_chip = {
250295
.name = "rzg2l-irqc",
251296
.irq_eoi = rzg2l_irqc_eoi,
@@ -331,7 +376,6 @@ static int rzg2l_irqc_init(struct device_node *node, struct device_node *parent)
331376
struct irq_domain *irq_domain, *parent_domain;
332377
struct platform_device *pdev;
333378
struct reset_control *resetn;
334-
struct rzg2l_irqc_priv *priv;
335379
int ret;
336380

337381
pdev = of_find_device_by_node(node);
@@ -344,15 +388,15 @@ static int rzg2l_irqc_init(struct device_node *node, struct device_node *parent)
344388
return -ENODEV;
345389
}
346390

347-
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
348-
if (!priv)
391+
rzg2l_irqc_data = devm_kzalloc(&pdev->dev, sizeof(*rzg2l_irqc_data), GFP_KERNEL);
392+
if (!rzg2l_irqc_data)
349393
return -ENOMEM;
350394

351-
priv->base = devm_of_iomap(&pdev->dev, pdev->dev.of_node, 0, NULL);
352-
if (IS_ERR(priv->base))
353-
return PTR_ERR(priv->base);
395+
rzg2l_irqc_data->base = devm_of_iomap(&pdev->dev, pdev->dev.of_node, 0, NULL);
396+
if (IS_ERR(rzg2l_irqc_data->base))
397+
return PTR_ERR(rzg2l_irqc_data->base);
354398

355-
ret = rzg2l_irqc_parse_interrupts(priv, node);
399+
ret = rzg2l_irqc_parse_interrupts(rzg2l_irqc_data, node);
356400
if (ret) {
357401
dev_err(&pdev->dev, "cannot parse interrupts: %d\n", ret);
358402
return ret;
@@ -375,17 +419,19 @@ static int rzg2l_irqc_init(struct device_node *node, struct device_node *parent)
375419
goto pm_disable;
376420
}
377421

378-
raw_spin_lock_init(&priv->lock);
422+
raw_spin_lock_init(&rzg2l_irqc_data->lock);
379423

380424
irq_domain = irq_domain_add_hierarchy(parent_domain, 0, IRQC_NUM_IRQ,
381425
node, &rzg2l_irqc_domain_ops,
382-
priv);
426+
rzg2l_irqc_data);
383427
if (!irq_domain) {
384428
dev_err(&pdev->dev, "failed to add irq domain\n");
385429
ret = -ENOMEM;
386430
goto pm_put;
387431
}
388432

433+
register_syscore_ops(&rzg2l_irqc_syscore_ops);
434+
389435
return 0;
390436

391437
pm_put:

0 commit comments

Comments
 (0)