One irqsteer channel can support up to 8 output interrupts. Cc: Marc Zyngier <marc.zyngier@xxxxxxx> Cc: Lucas Stach <l.stach@xxxxxxxxxxxxxx> Cc: Shawn Guo <shawnguo@xxxxxxxxxx> Signed-off-by: Dong Aisheng <aisheng.dong@xxxxxxx> --- ChangeLog: v1->v2: * calculate irq_count by fsl,num-irqs instead of parsing interrupts property from devicetree to match the input interrupts and outputs * improve output interrupt handler by searching only two registers withint the same group --- drivers/irqchip/irq-imx-irqsteer.c | 76 +++++++++++++++++++++++++++++--------- 1 file changed, 59 insertions(+), 17 deletions(-) diff --git a/drivers/irqchip/irq-imx-irqsteer.c b/drivers/irqchip/irq-imx-irqsteer.c index 67ed862..cc40039 100644 --- a/drivers/irqchip/irq-imx-irqsteer.c +++ b/drivers/irqchip/irq-imx-irqsteer.c @@ -10,6 +10,7 @@ #include <linux/irqchip/chained_irq.h> #include <linux/irqdomain.h> #include <linux/kernel.h> +#include <linux/of_irq.h> #include <linux/of_platform.h> #include <linux/spinlock.h> @@ -21,10 +22,13 @@ #define CHAN_MINTDIS(t) (CTRL_STRIDE_OFF(t, 3) + 0x4) #define CHAN_MASTRSTAT(t) (CTRL_STRIDE_OFF(t, 3) + 0x8) +#define CHAN_MAX_OUTPUT_INT 0x8 + struct irqsteer_data { void __iomem *regs; struct clk *ipg_clk; - int irq; + int irq[CHAN_MAX_OUTPUT_INT]; + int irq_count; raw_spinlock_t lock; int reg_num; int channel; @@ -87,26 +91,45 @@ static const struct irq_domain_ops imx_irqsteer_domain_ops = { .xlate = irq_domain_xlate_onecell, }; +static int imx_irqsteer_get_hwirq_base(struct irqsteer_data *data, u32 irq) +{ + int i; + + for (i = 0; i < data->irq_count; i++) { + if (data->irq[i] == irq) + break; + } + + return i * 64; +} + static void imx_irqsteer_irq_handler(struct irq_desc *desc) { struct irqsteer_data *data = irq_desc_get_handler_data(desc); + int hwirq; int i; chained_irq_enter(irq_desc_get_chip(desc), desc); - for (i = 0; i < data->reg_num * 32; i += 32) { - int idx = imx_irqsteer_get_reg_index(data, i); + hwirq = imx_irqsteer_get_hwirq_base(data, irq_desc_get_irq(desc)); + + for (i = 0; i < 2; i++) { + int idx = imx_irqsteer_get_reg_index(data, hwirq); unsigned long irqmap; int pos, virq; + if (hwirq >= data->reg_num * 32) + break; + irqmap = readl_relaxed(data->regs + CHANSTATUS(idx, data->reg_num)); for_each_set_bit(pos, &irqmap, 32) { - virq = irq_find_mapping(data->domain, pos + i); + virq = irq_find_mapping(data->domain, pos + hwirq); if (virq) generic_handle_irq(virq); } + hwirq += 32; } chained_irq_exit(irq_desc_get_chip(desc), desc); @@ -117,7 +140,8 @@ static int imx_irqsteer_probe(struct platform_device *pdev) struct device_node *np = pdev->dev.of_node; struct irqsteer_data *data; struct resource *res; - int ret; + u32 irqs_num; + int i, ret; data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); if (!data) @@ -130,12 +154,6 @@ static int imx_irqsteer_probe(struct platform_device *pdev) return PTR_ERR(data->regs); } - data->irq = platform_get_irq(pdev, 0); - if (data->irq <= 0) { - dev_err(&pdev->dev, "failed to get irq\n"); - return -ENODEV; - } - data->ipg_clk = devm_clk_get(&pdev->dev, "ipg"); if (IS_ERR(data->ipg_clk)) { ret = PTR_ERR(data->ipg_clk); @@ -146,11 +164,17 @@ static int imx_irqsteer_probe(struct platform_device *pdev) raw_spin_lock_init(&data->lock); - of_property_read_u32(np, "fsl,num-irqs", &data->reg_num); + of_property_read_u32(np, "fsl,num-irqs", &irqs_num); of_property_read_u32(np, "fsl,channel", &data->channel); - /* one register bit map represents 32 input interrupts */ - data->reg_num /= 32; + /* + * There is one output irqs for each group of 64 inputs. + * One register bit map can represent 32 input interrupts. + */ + data->irq_count = irqs_num / 64; + if (irqs_num % 64) + data->irq_count += 1; + data->reg_num = irqs_num / 32; if (IS_ENABLED(CONFIG_PM_SLEEP)) { data->saved_reg = devm_kzalloc(&pdev->dev, @@ -177,8 +201,22 @@ static int imx_irqsteer_probe(struct platform_device *pdev) return -ENOMEM; } - irq_set_chained_handler_and_data(data->irq, imx_irqsteer_irq_handler, - data); + if (!data->irq_count || data->irq_count > CHAN_MAX_OUTPUT_INT) { + clk_disable_unprepare(data->ipg_clk); + return -EINVAL; + } + + for (i = 0; i < data->irq_count; i++) { + data->irq[i] = irq_of_parse_and_map(np, i); + if (!data->irq[i]) { + clk_disable_unprepare(data->ipg_clk); + return -EINVAL; + } + + irq_set_chained_handler_and_data(data->irq[i], + imx_irqsteer_irq_handler, + data); + } platform_set_drvdata(pdev, data); @@ -188,8 +226,12 @@ static int imx_irqsteer_probe(struct platform_device *pdev) static int imx_irqsteer_remove(struct platform_device *pdev) { struct irqsteer_data *irqsteer_data = platform_get_drvdata(pdev); + int i; + + for (i = 0; i < irqsteer_data->irq_count; i++) + irq_set_chained_handler_and_data(irqsteer_data->irq[i], + NULL, NULL); - irq_set_chained_handler_and_data(irqsteer_data->irq, NULL, NULL); irq_domain_remove(irqsteer_data->domain); clk_disable_unprepare(irqsteer_data->ipg_clk); -- 2.7.4