Skip to content

Commit

Permalink
MLK-17290-01 irqchip: imx-irqsteer: add runtime pm support
Browse files Browse the repository at this point in the history
Add runtime pm to manage irqsteer clock and its power domain in system
idle and suspend status to save power.

Signed-off-by: Fugang Duan <fugang.duan@nxp.com>
Signed-off-by: Frank Li <Frank.Li@nxp.com>
Tested-by: Guoniu.Zhou <guoniu.zhou@nxp.com>
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Signed-off-by: Arulpandiyan Vadivel <arulpandiyan_vadivel@mentor.com>
(cherry picked and merged from commit 6c861656225d3b2407b5e7630106a7fd7fab119d)
  • Loading branch information
fugangduan authored and Guoniu.zhou committed Apr 8, 2020
1 parent d66c6d6 commit 1b98a50
Showing 1 changed file with 63 additions and 15 deletions.
78 changes: 63 additions & 15 deletions drivers/irqchip/irq-imx-irqsteer.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include <linux/of_platform.h>
#include <linux/spinlock.h>
#include <linux/pm_domain.h>
#include <linux/pm_runtime.h>
#include <linux/reset.h>

#define CTRL_STRIDE_OFF(_t, _r) (_t * 4 * _r)
Expand All @@ -27,6 +28,7 @@
#define CHAN_MAX_OUTPUT_INT 0x8

struct irqsteer_data {
struct irq_chip chip;
void __iomem *regs;
struct clk *ipg_clk;
int irq[CHAN_MAX_OUTPUT_INT];
Expand All @@ -36,6 +38,7 @@ struct irqsteer_data {
int channel;
struct irq_domain *domain;
u32 *saved_reg;
bool inited;

struct device *dev;
struct device *pd_csi;
Expand Down Expand Up @@ -125,9 +128,11 @@ static struct irq_chip imx_irqsteer_irq_chip = {
static int imx_irqsteer_irq_map(struct irq_domain *h, unsigned int irq,
irq_hw_number_t hwirq)
{
struct irqsteer_data *irqsteer_data = h->host_data;

irq_set_status_flags(irq, IRQ_LEVEL);
irq_set_chip_data(irq, h->host_data);
irq_set_chip_and_handler(irq, &imx_irqsteer_irq_chip, handle_level_irq);
irq_set_chip_and_handler(irq, &irqsteer_data->chip, handle_level_irq);

return 0;
}
Expand Down Expand Up @@ -186,6 +191,33 @@ static void imx_irqsteer_irq_handler(struct irq_desc *desc)
chained_irq_exit(irq_desc_get_chip(desc), desc);
}

#ifdef CONFIG_PM_SLEEP
static int imx_irqsteer_chans_enable(struct irqsteer_data *data)
{
return 0;
}
#else
static int imx_irqsteer_chans_enable(struct irqsteer_data *data)
{
int ret;

ret = clk_prepare_enable(irqsteer_data->ipg_clk);
if (ret) {
dev_err(data->dev, "failed to enable ipg clk: %d\n", ret);
return ret;
}

/* steer all IRQs into configured channel */
writel_relaxed(BIT(data->channel), data->regs + CHANCTRL);

/* read back CHANCTRL register cannot reflact on HW register
* real value due to the HW action, so add one flag here.
*/
data->inited = true;
return 0;
}
#endif

static int imx_irqsteer_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
Expand All @@ -197,7 +229,10 @@ static int imx_irqsteer_probe(struct platform_device *pdev)
if (!data)
return -ENOMEM;

data->chip = imx_irqsteer_irq_chip;
data->chip.parent_device = &pdev->dev;
data->dev = &pdev->dev;
data->inited = false;
data->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(data->regs)) {
dev_err(&pdev->dev, "failed to initialize reg\n");
Expand Down Expand Up @@ -244,14 +279,9 @@ static int imx_irqsteer_probe(struct platform_device *pdev)
return -ENOMEM;
}

ret = clk_prepare_enable(data->ipg_clk);
if (ret) {
dev_err(&pdev->dev, "failed to enable ipg clk: %d\n", ret);
ret = imx_irqsteer_chans_enable(data);
if (ret)
return ret;
}

/* steer all IRQs into configured channel */
writel_relaxed(BIT(data->channel), data->regs + CHANCTRL);

data->domain = irq_domain_add_linear(np, data->reg_num * 32,
&imx_irqsteer_domain_ops, data);
Expand Down Expand Up @@ -280,6 +310,7 @@ static int imx_irqsteer_probe(struct platform_device *pdev)

platform_set_drvdata(pdev, data);

pm_runtime_enable(&pdev->dev);
return 0;
out:
clk_disable_unprepare(data->ipg_clk);
Expand All @@ -297,12 +328,21 @@ static int imx_irqsteer_remove(struct platform_device *pdev)

irq_domain_remove(irqsteer_data->domain);

clk_disable_unprepare(irqsteer_data->ipg_clk);

return 0;
return pm_runtime_force_suspend(&pdev->dev);
}

#ifdef CONFIG_PM_SLEEP
static void imx_irqsteer_init(struct irqsteer_data *data)
{
/* steer all IRQs into configured channel */
writel_relaxed(BIT(data->channel), data->regs + CHANCTRL);

/* read back CHANCTRL register cannot reflact on HW register
* real value due to the HW action, so add one flag here.
*/
data->inited = true;
}

static void imx_irqsteer_save_regs(struct irqsteer_data *data)
{
int i;
Expand All @@ -322,7 +362,7 @@ static void imx_irqsteer_restore_regs(struct irqsteer_data *data)
data->regs + CHANMASK(i, data->reg_num));
}

static int imx_irqsteer_suspend(struct device *dev)
static int imx_irqsteer_runtime_suspend(struct device *dev)
{
struct irqsteer_data *irqsteer_data = dev_get_drvdata(dev);

Expand All @@ -332,7 +372,7 @@ static int imx_irqsteer_suspend(struct device *dev)
return 0;
}

static int imx_irqsteer_resume(struct device *dev)
static int imx_irqsteer_runtime_resume(struct device *dev)
{
struct irqsteer_data *irqsteer_data = dev_get_drvdata(dev);
int ret;
Expand All @@ -342,14 +382,22 @@ static int imx_irqsteer_resume(struct device *dev)
dev_err(dev, "failed to enable ipg clk: %d\n", ret);
return ret;
}
imx_irqsteer_restore_regs(irqsteer_data);

/* don't need restore registers when first sub_irq requested */
if (!irqsteer_data->inited)
imx_irqsteer_init(irqsteer_data);
else
imx_irqsteer_restore_regs(irqsteer_data);

return 0;
}
#endif

static const struct dev_pm_ops imx_irqsteer_pm_ops = {
SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(imx_irqsteer_suspend, imx_irqsteer_resume)
SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
pm_runtime_force_resume)
SET_RUNTIME_PM_OPS(imx_irqsteer_runtime_suspend,
imx_irqsteer_runtime_resume, NULL)
};

static const struct of_device_id imx_irqsteer_dt_ids[] = {
Expand Down

0 comments on commit 1b98a50

Please sign in to comment.