Skip to content

Commit 28528fc

Browse files
Aisheng DongMarc Zyngier
authored andcommitted
irqchip/imx-irqsteer: Add multi output interrupts support
One irqsteer channel can support up to 8 output interrupts. Cc: Marc Zyngier <marc.zyngier@arm.com> Cc: Lucas Stach <l.stach@pengutronix.de> Cc: Shawn Guo <shawnguo@kernel.org> Reviewed-by: Lucas Stach <l.stach@pengutronix.de> Signed-off-by: Dong Aisheng <aisheng.dong@nxp.com> Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
1 parent deb904e commit 28528fc

File tree

1 file changed

+68
-20
lines changed

1 file changed

+68
-20
lines changed

drivers/irqchip/irq-imx-irqsteer.c

Lines changed: 68 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include <linux/irqchip/chained_irq.h>
1111
#include <linux/irqdomain.h>
1212
#include <linux/kernel.h>
13+
#include <linux/of_irq.h>
1314
#include <linux/of_platform.h>
1415
#include <linux/spinlock.h>
1516

@@ -21,10 +22,13 @@
2122
#define CHAN_MINTDIS(t) (CTRL_STRIDE_OFF(t, 3) + 0x4)
2223
#define CHAN_MASTRSTAT(t) (CTRL_STRIDE_OFF(t, 3) + 0x8)
2324

25+
#define CHAN_MAX_OUTPUT_INT 0x8
26+
2427
struct irqsteer_data {
2528
void __iomem *regs;
2629
struct clk *ipg_clk;
27-
int irq;
30+
int irq[CHAN_MAX_OUTPUT_INT];
31+
int irq_count;
2832
raw_spinlock_t lock;
2933
int reg_num;
3034
int channel;
@@ -87,23 +91,47 @@ static const struct irq_domain_ops imx_irqsteer_domain_ops = {
8791
.xlate = irq_domain_xlate_onecell,
8892
};
8993

94+
static int imx_irqsteer_get_hwirq_base(struct irqsteer_data *data, u32 irq)
95+
{
96+
int i;
97+
98+
for (i = 0; i < data->irq_count; i++) {
99+
if (data->irq[i] == irq)
100+
return i * 64;
101+
}
102+
103+
return -EINVAL;
104+
}
105+
90106
static void imx_irqsteer_irq_handler(struct irq_desc *desc)
91107
{
92108
struct irqsteer_data *data = irq_desc_get_handler_data(desc);
93-
int i;
109+
int hwirq;
110+
int irq, i;
94111

95112
chained_irq_enter(irq_desc_get_chip(desc), desc);
96113

97-
for (i = 0; i < data->reg_num * 32; i += 32) {
98-
int idx = imx_irqsteer_get_reg_index(data, i);
114+
irq = irq_desc_get_irq(desc);
115+
hwirq = imx_irqsteer_get_hwirq_base(data, irq);
116+
if (hwirq < 0) {
117+
pr_warn("%s: unable to get hwirq base for irq %d\n",
118+
__func__, irq);
119+
return;
120+
}
121+
122+
for (i = 0; i < 2; i++, hwirq += 32) {
123+
int idx = imx_irqsteer_get_reg_index(data, hwirq);
99124
unsigned long irqmap;
100125
int pos, virq;
101126

127+
if (hwirq >= data->reg_num * 32)
128+
break;
129+
102130
irqmap = readl_relaxed(data->regs +
103131
CHANSTATUS(idx, data->reg_num));
104132

105133
for_each_set_bit(pos, &irqmap, 32) {
106-
virq = irq_find_mapping(data->domain, pos + i);
134+
virq = irq_find_mapping(data->domain, pos + hwirq);
107135
if (virq)
108136
generic_handle_irq(virq);
109137
}
@@ -117,7 +145,8 @@ static int imx_irqsteer_probe(struct platform_device *pdev)
117145
struct device_node *np = pdev->dev.of_node;
118146
struct irqsteer_data *data;
119147
struct resource *res;
120-
int ret;
148+
u32 irqs_num;
149+
int i, ret;
121150

122151
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
123152
if (!data)
@@ -130,12 +159,6 @@ static int imx_irqsteer_probe(struct platform_device *pdev)
130159
return PTR_ERR(data->regs);
131160
}
132161

133-
data->irq = platform_get_irq(pdev, 0);
134-
if (data->irq <= 0) {
135-
dev_err(&pdev->dev, "failed to get irq\n");
136-
return -ENODEV;
137-
}
138-
139162
data->ipg_clk = devm_clk_get(&pdev->dev, "ipg");
140163
if (IS_ERR(data->ipg_clk)) {
141164
ret = PTR_ERR(data->ipg_clk);
@@ -146,11 +169,15 @@ static int imx_irqsteer_probe(struct platform_device *pdev)
146169

147170
raw_spin_lock_init(&data->lock);
148171

149-
of_property_read_u32(np, "fsl,num-irqs", &data->reg_num);
172+
of_property_read_u32(np, "fsl,num-irqs", &irqs_num);
150173
of_property_read_u32(np, "fsl,channel", &data->channel);
151174

152-
/* one register bit map represents 32 input interrupts */
153-
data->reg_num /= 32;
175+
/*
176+
* There is one output irq for each group of 64 inputs.
177+
* One register bit map can represent 32 input interrupts.
178+
*/
179+
data->irq_count = DIV_ROUND_UP(irqs_num, 64);
180+
data->reg_num = irqs_num / 32;
154181

155182
if (IS_ENABLED(CONFIG_PM_SLEEP)) {
156183
data->saved_reg = devm_kzalloc(&pdev->dev,
@@ -173,23 +200,44 @@ static int imx_irqsteer_probe(struct platform_device *pdev)
173200
&imx_irqsteer_domain_ops, data);
174201
if (!data->domain) {
175202
dev_err(&pdev->dev, "failed to create IRQ domain\n");
176-
clk_disable_unprepare(data->ipg_clk);
177-
return -ENOMEM;
203+
ret = -ENOMEM;
204+
goto out;
178205
}
179206

180-
irq_set_chained_handler_and_data(data->irq, imx_irqsteer_irq_handler,
181-
data);
207+
if (!data->irq_count || data->irq_count > CHAN_MAX_OUTPUT_INT) {
208+
ret = -EINVAL;
209+
goto out;
210+
}
211+
212+
for (i = 0; i < data->irq_count; i++) {
213+
data->irq[i] = irq_of_parse_and_map(np, i);
214+
if (!data->irq[i]) {
215+
ret = -EINVAL;
216+
goto out;
217+
}
218+
219+
irq_set_chained_handler_and_data(data->irq[i],
220+
imx_irqsteer_irq_handler,
221+
data);
222+
}
182223

183224
platform_set_drvdata(pdev, data);
184225

185226
return 0;
227+
out:
228+
clk_disable_unprepare(data->ipg_clk);
229+
return ret;
186230
}
187231

188232
static int imx_irqsteer_remove(struct platform_device *pdev)
189233
{
190234
struct irqsteer_data *irqsteer_data = platform_get_drvdata(pdev);
235+
int i;
236+
237+
for (i = 0; i < irqsteer_data->irq_count; i++)
238+
irq_set_chained_handler_and_data(irqsteer_data->irq[i],
239+
NULL, NULL);
191240

192-
irq_set_chained_handler_and_data(irqsteer_data->irq, NULL, NULL);
193241
irq_domain_remove(irqsteer_data->domain);
194242

195243
clk_disable_unprepare(irqsteer_data->ipg_clk);

0 commit comments

Comments
 (0)