Skip to content

Commit bb40311

Browse files
Joakim ZhangMarc Zyngier
authored andcommitted
irqchip/imx-intmux: Implement intmux runtime power management
When the system is suspended, we can explicitly disable clock to save power. To achieve this, we need save registers' state since it could be lost after power off. Implement power management which will: - Turn the clock off after probing - Disable clock and save registers' state on system suspend, as well as enable clock and restore registers' state on resume - Rely on the Power Domain framework to shutdown the intmux power domain Without CONFIG_PM, the clock is always on after probe stage. Signed-off-by: Joakim Zhang <[email protected]> [maz: revamped commit message] Signed-off-by: Marc Zyngier <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent d1bd7e0 commit bb40311

File tree

1 file changed

+65
-2
lines changed

1 file changed

+65
-2
lines changed

drivers/irqchip/irq-imx-intmux.c

Lines changed: 65 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,13 +53,16 @@
5353
#include <linux/of_irq.h>
5454
#include <linux/of_platform.h>
5555
#include <linux/spinlock.h>
56+
#include <linux/pm_runtime.h>
5657

5758
#define CHANIER(n) (0x10 + (0x40 * n))
5859
#define CHANIPR(n) (0x20 + (0x40 * n))
5960

6061
#define CHAN_MAX_NUM 0x8
6162

6263
struct intmux_irqchip_data {
64+
struct irq_chip chip;
65+
u32 saved_reg;
6366
int chanidx;
6467
int irq;
6568
struct irq_domain *domain;
@@ -120,8 +123,10 @@ static struct irq_chip imx_intmux_irq_chip = {
120123
static int imx_intmux_irq_map(struct irq_domain *h, unsigned int irq,
121124
irq_hw_number_t hwirq)
122125
{
123-
irq_set_chip_data(irq, h->host_data);
124-
irq_set_chip_and_handler(irq, &imx_intmux_irq_chip, handle_level_irq);
126+
struct intmux_irqchip_data *data = h->host_data;
127+
128+
irq_set_chip_data(irq, data);
129+
irq_set_chip_and_handler(irq, &data->chip, handle_level_irq);
125130

126131
return 0;
127132
}
@@ -231,13 +236,19 @@ static int imx_intmux_probe(struct platform_device *pdev)
231236
data->channum = channum;
232237
raw_spin_lock_init(&data->lock);
233238

239+
pm_runtime_get_noresume(&pdev->dev);
240+
pm_runtime_set_active(&pdev->dev);
241+
pm_runtime_enable(&pdev->dev);
242+
234243
ret = clk_prepare_enable(data->ipg_clk);
235244
if (ret) {
236245
dev_err(&pdev->dev, "failed to enable ipg clk: %d\n", ret);
237246
return ret;
238247
}
239248

240249
for (i = 0; i < channum; i++) {
250+
data->irqchip_data[i].chip = imx_intmux_irq_chip;
251+
data->irqchip_data[i].chip.parent_device = &pdev->dev;
241252
data->irqchip_data[i].chanidx = i;
242253

243254
data->irqchip_data[i].irq = irq_of_parse_and_map(np, i);
@@ -266,6 +277,12 @@ static int imx_intmux_probe(struct platform_device *pdev)
266277

267278
platform_set_drvdata(pdev, data);
268279

280+
/*
281+
* Let pm_runtime_put() disable clock.
282+
* If CONFIG_PM is not enabled, the clock will stay powered.
283+
*/
284+
pm_runtime_put(&pdev->dev);
285+
269286
return 0;
270287
out:
271288
clk_disable_unprepare(data->ipg_clk);
@@ -287,11 +304,56 @@ static int imx_intmux_remove(struct platform_device *pdev)
287304
irq_domain_remove(data->irqchip_data[i].domain);
288305
}
289306

307+
pm_runtime_disable(&pdev->dev);
308+
309+
return 0;
310+
}
311+
312+
#ifdef CONFIG_PM
313+
static int imx_intmux_runtime_suspend(struct device *dev)
314+
{
315+
struct intmux_data *data = dev_get_drvdata(dev);
316+
struct intmux_irqchip_data irqchip_data;
317+
int i;
318+
319+
for (i = 0; i < data->channum; i++) {
320+
irqchip_data = data->irqchip_data[i];
321+
irqchip_data.saved_reg = readl_relaxed(data->regs + CHANIER(i));
322+
}
323+
290324
clk_disable_unprepare(data->ipg_clk);
291325

292326
return 0;
293327
}
294328

329+
static int imx_intmux_runtime_resume(struct device *dev)
330+
{
331+
struct intmux_data *data = dev_get_drvdata(dev);
332+
struct intmux_irqchip_data irqchip_data;
333+
int ret, i;
334+
335+
ret = clk_prepare_enable(data->ipg_clk);
336+
if (ret) {
337+
dev_err(dev, "failed to enable ipg clk: %d\n", ret);
338+
return ret;
339+
}
340+
341+
for (i = 0; i < data->channum; i++) {
342+
irqchip_data = data->irqchip_data[i];
343+
writel_relaxed(irqchip_data.saved_reg, data->regs + CHANIER(i));
344+
}
345+
346+
return 0;
347+
}
348+
#endif
349+
350+
static const struct dev_pm_ops imx_intmux_pm_ops = {
351+
SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
352+
pm_runtime_force_resume)
353+
SET_RUNTIME_PM_OPS(imx_intmux_runtime_suspend,
354+
imx_intmux_runtime_resume, NULL)
355+
};
356+
295357
static const struct of_device_id imx_intmux_id[] = {
296358
{ .compatible = "fsl,imx-intmux", },
297359
{ /* sentinel */ },
@@ -301,6 +363,7 @@ static struct platform_driver imx_intmux_driver = {
301363
.driver = {
302364
.name = "imx-intmux",
303365
.of_match_table = imx_intmux_id,
366+
.pm = &imx_intmux_pm_ops,
304367
},
305368
.probe = imx_intmux_probe,
306369
.remove = imx_intmux_remove,

0 commit comments

Comments
 (0)