aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAisheng Dong <aisheng.dong@nxp.com>2019-02-20 06:40:51 -0500
committerMarc Zyngier <marc.zyngier@arm.com>2019-02-22 04:23:46 -0500
commit28528fca4908142bd1a3247956cba56c9c667d71 (patch)
tree64414a69aeed7b3326b979de5b0965266bd9417a
parentdeb904e45b4e32517f91512db5c50457004313d2 (diff)
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>
-rw-r--r--drivers/irqchip/irq-imx-irqsteer.c88
1 files changed, 68 insertions, 20 deletions
diff --git a/drivers/irqchip/irq-imx-irqsteer.c b/drivers/irqchip/irq-imx-irqsteer.c
index 67ed86250cbf..d1098f4da6a4 100644
--- a/drivers/irqchip/irq-imx-irqsteer.c
+++ b/drivers/irqchip/irq-imx-irqsteer.c
@@ -10,6 +10,7 @@
10#include <linux/irqchip/chained_irq.h> 10#include <linux/irqchip/chained_irq.h>
11#include <linux/irqdomain.h> 11#include <linux/irqdomain.h>
12#include <linux/kernel.h> 12#include <linux/kernel.h>
13#include <linux/of_irq.h>
13#include <linux/of_platform.h> 14#include <linux/of_platform.h>
14#include <linux/spinlock.h> 15#include <linux/spinlock.h>
15 16
@@ -21,10 +22,13 @@
21#define CHAN_MINTDIS(t) (CTRL_STRIDE_OFF(t, 3) + 0x4) 22#define CHAN_MINTDIS(t) (CTRL_STRIDE_OFF(t, 3) + 0x4)
22#define CHAN_MASTRSTAT(t) (CTRL_STRIDE_OFF(t, 3) + 0x8) 23#define CHAN_MASTRSTAT(t) (CTRL_STRIDE_OFF(t, 3) + 0x8)
23 24
25#define CHAN_MAX_OUTPUT_INT 0x8
26
24struct irqsteer_data { 27struct irqsteer_data {
25 void __iomem *regs; 28 void __iomem *regs;
26 struct clk *ipg_clk; 29 struct clk *ipg_clk;
27 int irq; 30 int irq[CHAN_MAX_OUTPUT_INT];
31 int irq_count;
28 raw_spinlock_t lock; 32 raw_spinlock_t lock;
29 int reg_num; 33 int reg_num;
30 int channel; 34 int channel;
@@ -87,23 +91,47 @@ static const struct irq_domain_ops imx_irqsteer_domain_ops = {
87 .xlate = irq_domain_xlate_onecell, 91 .xlate = irq_domain_xlate_onecell,
88}; 92};
89 93
94static 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
90static void imx_irqsteer_irq_handler(struct irq_desc *desc) 106static void imx_irqsteer_irq_handler(struct irq_desc *desc)
91{ 107{
92 struct irqsteer_data *data = irq_desc_get_handler_data(desc); 108 struct irqsteer_data *data = irq_desc_get_handler_data(desc);
93 int i; 109 int hwirq;
110 int irq, i;
94 111
95 chained_irq_enter(irq_desc_get_chip(desc), desc); 112 chained_irq_enter(irq_desc_get_chip(desc), desc);
96 113
97 for (i = 0; i < data->reg_num * 32; i += 32) { 114 irq = irq_desc_get_irq(desc);
98 int idx = imx_irqsteer_get_reg_index(data, i); 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);
99 unsigned long irqmap; 124 unsigned long irqmap;
100 int pos, virq; 125 int pos, virq;
101 126
127 if (hwirq >= data->reg_num * 32)
128 break;
129
102 irqmap = readl_relaxed(data->regs + 130 irqmap = readl_relaxed(data->regs +
103 CHANSTATUS(idx, data->reg_num)); 131 CHANSTATUS(idx, data->reg_num));
104 132
105 for_each_set_bit(pos, &irqmap, 32) { 133 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);
107 if (virq) 135 if (virq)
108 generic_handle_irq(virq); 136 generic_handle_irq(virq);
109 } 137 }
@@ -117,7 +145,8 @@ static int imx_irqsteer_probe(struct platform_device *pdev)
117 struct device_node *np = pdev->dev.of_node; 145 struct device_node *np = pdev->dev.of_node;
118 struct irqsteer_data *data; 146 struct irqsteer_data *data;
119 struct resource *res; 147 struct resource *res;
120 int ret; 148 u32 irqs_num;
149 int i, ret;
121 150
122 data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); 151 data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
123 if (!data) 152 if (!data)
@@ -130,12 +159,6 @@ static int imx_irqsteer_probe(struct platform_device *pdev)
130 return PTR_ERR(data->regs); 159 return PTR_ERR(data->regs);
131 } 160 }
132 161
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
139 data->ipg_clk = devm_clk_get(&pdev->dev, "ipg"); 162 data->ipg_clk = devm_clk_get(&pdev->dev, "ipg");
140 if (IS_ERR(data->ipg_clk)) { 163 if (IS_ERR(data->ipg_clk)) {
141 ret = PTR_ERR(data->ipg_clk); 164 ret = PTR_ERR(data->ipg_clk);
@@ -146,11 +169,15 @@ static int imx_irqsteer_probe(struct platform_device *pdev)
146 169
147 raw_spin_lock_init(&data->lock); 170 raw_spin_lock_init(&data->lock);
148 171
149 of_property_read_u32(np, "fsl,num-irqs", &data->reg_num); 172 of_property_read_u32(np, "fsl,num-irqs", &irqs_num);
150 of_property_read_u32(np, "fsl,channel", &data->channel); 173 of_property_read_u32(np, "fsl,channel", &data->channel);
151 174
152 /* one register bit map represents 32 input interrupts */ 175 /*
153 data->reg_num /= 32; 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;
154 181
155 if (IS_ENABLED(CONFIG_PM_SLEEP)) { 182 if (IS_ENABLED(CONFIG_PM_SLEEP)) {
156 data->saved_reg = devm_kzalloc(&pdev->dev, 183 data->saved_reg = devm_kzalloc(&pdev->dev,
@@ -173,23 +200,44 @@ static int imx_irqsteer_probe(struct platform_device *pdev)
173 &imx_irqsteer_domain_ops, data); 200 &imx_irqsteer_domain_ops, data);
174 if (!data->domain) { 201 if (!data->domain) {
175 dev_err(&pdev->dev, "failed to create IRQ domain\n"); 202 dev_err(&pdev->dev, "failed to create IRQ domain\n");
176 clk_disable_unprepare(data->ipg_clk); 203 ret = -ENOMEM;
177 return -ENOMEM; 204 goto out;
178 } 205 }
179 206
180 irq_set_chained_handler_and_data(data->irq, imx_irqsteer_irq_handler, 207 if (!data->irq_count || data->irq_count > CHAN_MAX_OUTPUT_INT) {
181 data); 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 }
182 223
183 platform_set_drvdata(pdev, data); 224 platform_set_drvdata(pdev, data);
184 225
185 return 0; 226 return 0;
227out:
228 clk_disable_unprepare(data->ipg_clk);
229 return ret;
186} 230}
187 231
188static int imx_irqsteer_remove(struct platform_device *pdev) 232static int imx_irqsteer_remove(struct platform_device *pdev)
189{ 233{
190 struct irqsteer_data *irqsteer_data = platform_get_drvdata(pdev); 234 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);
191 240
192 irq_set_chained_handler_and_data(irqsteer_data->irq, NULL, NULL);
193 irq_domain_remove(irqsteer_data->domain); 241 irq_domain_remove(irqsteer_data->domain);
194 242
195 clk_disable_unprepare(irqsteer_data->ipg_clk); 243 clk_disable_unprepare(irqsteer_data->ipg_clk);