summaryrefslogtreecommitdiffstats
path: root/drivers/irqchip
diff options
context:
space:
mode:
authorMinghuan Lian <Minghuan.Lian@nxp.com>2017-07-05 02:59:01 -0400
committerMarc Zyngier <marc.zyngier@arm.com>2017-08-31 11:19:34 -0400
commit4dd5da65a39d9a0405304fdef0804afffece044b (patch)
tree6ae8318a51d2af9a04b9400b80e2b31e687ba314 /drivers/irqchip
parentcb3421684ee778d60da26232bfea626dca2eb8db (diff)
irqchip/ls-scfg-msi: Add LS1046a MSI support
LS1046a includes 4 MSIRs, each MSIR is assigned a dedicate GIC SPI interrupt and provides 32 MSI interrupts. Compared to previous MSI, LS1046a's IBS(interrupt bit select) shift is changed to 2 and total MSI interrupt number is changed to 128. The patch adds structure 'ls_scfg_msir' to describe MSIR setting and 'ibs_shift' to store the different value between the SoCs. Signed-off-by: Minghuan Lian <Minghuan.Lian@nxp.com> Signed-off-by: Hou Zhiqiang <Zhiqiang.Hou@nxp.com> Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
Diffstat (limited to 'drivers/irqchip')
-rw-r--r--drivers/irqchip/irq-ls-scfg-msi.c165
1 files changed, 130 insertions, 35 deletions
diff --git a/drivers/irqchip/irq-ls-scfg-msi.c b/drivers/irqchip/irq-ls-scfg-msi.c
index cef67cc5c0f2..0b1f34ddab00 100644
--- a/drivers/irqchip/irq-ls-scfg-msi.c
+++ b/drivers/irqchip/irq-ls-scfg-msi.c
@@ -17,13 +17,24 @@
17#include <linux/irq.h> 17#include <linux/irq.h>
18#include <linux/irqchip/chained_irq.h> 18#include <linux/irqchip/chained_irq.h>
19#include <linux/irqdomain.h> 19#include <linux/irqdomain.h>
20#include <linux/of_irq.h>
20#include <linux/of_pci.h> 21#include <linux/of_pci.h>
21#include <linux/of_platform.h> 22#include <linux/of_platform.h>
22#include <linux/spinlock.h> 23#include <linux/spinlock.h>
23 24
24#define MSI_MAX_IRQS 32 25#define MSI_IRQS_PER_MSIR 32
25#define MSI_IBS_SHIFT 3 26#define MSI_MSIR_OFFSET 4
26#define MSIR 4 27
28struct ls_scfg_msi_cfg {
29 u32 ibs_shift; /* Shift of interrupt bit select */
30};
31
32struct ls_scfg_msir {
33 struct ls_scfg_msi *msi_data;
34 unsigned int index;
35 unsigned int gic_irq;
36 void __iomem *reg;
37};
27 38
28struct ls_scfg_msi { 39struct ls_scfg_msi {
29 spinlock_t lock; 40 spinlock_t lock;
@@ -32,8 +43,11 @@ struct ls_scfg_msi {
32 struct irq_domain *msi_domain; 43 struct irq_domain *msi_domain;
33 void __iomem *regs; 44 void __iomem *regs;
34 phys_addr_t msiir_addr; 45 phys_addr_t msiir_addr;
35 int irq; 46 struct ls_scfg_msi_cfg *cfg;
36 DECLARE_BITMAP(used, MSI_MAX_IRQS); 47 u32 msir_num;
48 struct ls_scfg_msir *msir;
49 u32 irqs_num;
50 unsigned long *used;
37}; 51};
38 52
39static struct irq_chip ls_scfg_msi_irq_chip = { 53static struct irq_chip ls_scfg_msi_irq_chip = {
@@ -55,7 +69,7 @@ static void ls_scfg_msi_compose_msg(struct irq_data *data, struct msi_msg *msg)
55 69
56 msg->address_hi = upper_32_bits(msi_data->msiir_addr); 70 msg->address_hi = upper_32_bits(msi_data->msiir_addr);
57 msg->address_lo = lower_32_bits(msi_data->msiir_addr); 71 msg->address_lo = lower_32_bits(msi_data->msiir_addr);
58 msg->data = data->hwirq << MSI_IBS_SHIFT; 72 msg->data = data->hwirq;
59} 73}
60 74
61static int ls_scfg_msi_set_affinity(struct irq_data *irq_data, 75static int ls_scfg_msi_set_affinity(struct irq_data *irq_data,
@@ -81,8 +95,8 @@ static int ls_scfg_msi_domain_irq_alloc(struct irq_domain *domain,
81 WARN_ON(nr_irqs != 1); 95 WARN_ON(nr_irqs != 1);
82 96
83 spin_lock(&msi_data->lock); 97 spin_lock(&msi_data->lock);
84 pos = find_first_zero_bit(msi_data->used, MSI_MAX_IRQS); 98 pos = find_first_zero_bit(msi_data->used, msi_data->irqs_num);
85 if (pos < MSI_MAX_IRQS) 99 if (pos < msi_data->irqs_num)
86 __set_bit(pos, msi_data->used); 100 __set_bit(pos, msi_data->used);
87 else 101 else
88 err = -ENOSPC; 102 err = -ENOSPC;
@@ -106,7 +120,7 @@ static void ls_scfg_msi_domain_irq_free(struct irq_domain *domain,
106 int pos; 120 int pos;
107 121
108 pos = d->hwirq; 122 pos = d->hwirq;
109 if (pos < 0 || pos >= MSI_MAX_IRQS) { 123 if (pos < 0 || pos >= msi_data->irqs_num) {
110 pr_err("failed to teardown msi. Invalid hwirq %d\n", pos); 124 pr_err("failed to teardown msi. Invalid hwirq %d\n", pos);
111 return; 125 return;
112 } 126 }
@@ -123,15 +137,17 @@ static const struct irq_domain_ops ls_scfg_msi_domain_ops = {
123 137
124static void ls_scfg_msi_irq_handler(struct irq_desc *desc) 138static void ls_scfg_msi_irq_handler(struct irq_desc *desc)
125{ 139{
126 struct ls_scfg_msi *msi_data = irq_desc_get_handler_data(desc); 140 struct ls_scfg_msir *msir = irq_desc_get_handler_data(desc);
141 struct ls_scfg_msi *msi_data = msir->msi_data;
127 unsigned long val; 142 unsigned long val;
128 int pos, virq; 143 int pos, virq, hwirq;
129 144
130 chained_irq_enter(irq_desc_get_chip(desc), desc); 145 chained_irq_enter(irq_desc_get_chip(desc), desc);
131 146
132 val = ioread32be(msi_data->regs + MSIR); 147 val = ioread32be(msir->reg);
133 for_each_set_bit(pos, &val, MSI_MAX_IRQS) { 148 for_each_set_bit(pos, &val, MSI_IRQS_PER_MSIR) {
134 virq = irq_find_mapping(msi_data->parent, (31 - pos)); 149 hwirq = ((31 - pos) << msi_data->cfg->ibs_shift) | msir->index;
150 virq = irq_find_mapping(msi_data->parent, hwirq);
135 if (virq) 151 if (virq)
136 generic_handle_irq(virq); 152 generic_handle_irq(virq);
137 } 153 }
@@ -143,7 +159,7 @@ static int ls_scfg_msi_domains_init(struct ls_scfg_msi *msi_data)
143{ 159{
144 /* Initialize MSI domain parent */ 160 /* Initialize MSI domain parent */
145 msi_data->parent = irq_domain_add_linear(NULL, 161 msi_data->parent = irq_domain_add_linear(NULL,
146 MSI_MAX_IRQS, 162 msi_data->irqs_num,
147 &ls_scfg_msi_domain_ops, 163 &ls_scfg_msi_domain_ops,
148 msi_data); 164 msi_data);
149 if (!msi_data->parent) { 165 if (!msi_data->parent) {
@@ -164,16 +180,87 @@ static int ls_scfg_msi_domains_init(struct ls_scfg_msi *msi_data)
164 return 0; 180 return 0;
165} 181}
166 182
183static int ls_scfg_msi_setup_hwirq(struct ls_scfg_msi *msi_data, int index)
184{
185 struct ls_scfg_msir *msir;
186 int virq, i, hwirq;
187
188 virq = platform_get_irq(msi_data->pdev, index);
189 if (virq <= 0)
190 return -ENODEV;
191
192 msir = &msi_data->msir[index];
193 msir->index = index;
194 msir->msi_data = msi_data;
195 msir->gic_irq = virq;
196 msir->reg = msi_data->regs + MSI_MSIR_OFFSET + 4 * index;
197
198 irq_set_chained_handler_and_data(msir->gic_irq,
199 ls_scfg_msi_irq_handler,
200 msir);
201
202 /* Release the hwirqs corresponding to this MSIR */
203 for (i = 0; i < MSI_IRQS_PER_MSIR; i++) {
204 hwirq = i << msi_data->cfg->ibs_shift | msir->index;
205 bitmap_clear(msi_data->used, hwirq, 1);
206 }
207
208 return 0;
209}
210
211static int ls_scfg_msi_teardown_hwirq(struct ls_scfg_msir *msir)
212{
213 struct ls_scfg_msi *msi_data = msir->msi_data;
214 int i, hwirq;
215
216 if (msir->gic_irq > 0)
217 irq_set_chained_handler_and_data(msir->gic_irq, NULL, NULL);
218
219 for (i = 0; i < MSI_IRQS_PER_MSIR; i++) {
220 hwirq = i << msi_data->cfg->ibs_shift | msir->index;
221 bitmap_set(msi_data->used, hwirq, 1);
222 }
223
224 return 0;
225}
226
227static struct ls_scfg_msi_cfg ls1021_msi_cfg = {
228 .ibs_shift = 3,
229};
230
231static struct ls_scfg_msi_cfg ls1046_msi_cfg = {
232 .ibs_shift = 2,
233};
234
235static const struct of_device_id ls_scfg_msi_id[] = {
236 /* The following two misspelled compatibles are obsolete */
237 { .compatible = "fsl,1s1021a-msi", .data = &ls1021_msi_cfg},
238 { .compatible = "fsl,1s1043a-msi", .data = &ls1021_msi_cfg},
239
240 { .compatible = "fsl,ls1021a-msi", .data = &ls1021_msi_cfg },
241 { .compatible = "fsl,ls1043a-msi", .data = &ls1021_msi_cfg },
242 { .compatible = "fsl,ls1046a-msi", .data = &ls1046_msi_cfg },
243 {},
244};
245MODULE_DEVICE_TABLE(of, ls_scfg_msi_id);
246
167static int ls_scfg_msi_probe(struct platform_device *pdev) 247static int ls_scfg_msi_probe(struct platform_device *pdev)
168{ 248{
249 const struct of_device_id *match;
169 struct ls_scfg_msi *msi_data; 250 struct ls_scfg_msi *msi_data;
170 struct resource *res; 251 struct resource *res;
171 int ret; 252 int i, ret;
253
254 match = of_match_device(ls_scfg_msi_id, &pdev->dev);
255 if (!match)
256 return -ENODEV;
172 257
173 msi_data = devm_kzalloc(&pdev->dev, sizeof(*msi_data), GFP_KERNEL); 258 msi_data = devm_kzalloc(&pdev->dev, sizeof(*msi_data), GFP_KERNEL);
174 if (!msi_data) 259 if (!msi_data)
175 return -ENOMEM; 260 return -ENOMEM;
176 261
262 msi_data->cfg = (struct ls_scfg_msi_cfg *) match->data;
263
177 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 264 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
178 msi_data->regs = devm_ioremap_resource(&pdev->dev, res); 265 msi_data->regs = devm_ioremap_resource(&pdev->dev, res);
179 if (IS_ERR(msi_data->regs)) { 266 if (IS_ERR(msi_data->regs)) {
@@ -182,23 +269,37 @@ static int ls_scfg_msi_probe(struct platform_device *pdev)
182 } 269 }
183 msi_data->msiir_addr = res->start; 270 msi_data->msiir_addr = res->start;
184 271
185 msi_data->irq = platform_get_irq(pdev, 0);
186 if (msi_data->irq <= 0) {
187 dev_err(&pdev->dev, "failed to get MSI irq\n");
188 return -ENODEV;
189 }
190
191 msi_data->pdev = pdev; 272 msi_data->pdev = pdev;
192 spin_lock_init(&msi_data->lock); 273 spin_lock_init(&msi_data->lock);
193 274
275 msi_data->irqs_num = MSI_IRQS_PER_MSIR *
276 (1 << msi_data->cfg->ibs_shift);
277 msi_data->used = devm_kcalloc(&pdev->dev,
278 BITS_TO_LONGS(msi_data->irqs_num),
279 sizeof(*msi_data->used),
280 GFP_KERNEL);
281 if (!msi_data->used)
282 return -ENOMEM;
283 /*
284 * Reserve all the hwirqs
285 * The available hwirqs will be released in ls1_msi_setup_hwirq()
286 */
287 bitmap_set(msi_data->used, 0, msi_data->irqs_num);
288
289 msi_data->msir_num = of_irq_count(pdev->dev.of_node);
290 msi_data->msir = devm_kcalloc(&pdev->dev, msi_data->msir_num,
291 sizeof(*msi_data->msir),
292 GFP_KERNEL);
293 if (!msi_data->msir)
294 return -ENOMEM;
295
296 for (i = 0; i < msi_data->msir_num; i++)
297 ls_scfg_msi_setup_hwirq(msi_data, i);
298
194 ret = ls_scfg_msi_domains_init(msi_data); 299 ret = ls_scfg_msi_domains_init(msi_data);
195 if (ret) 300 if (ret)
196 return ret; 301 return ret;
197 302
198 irq_set_chained_handler_and_data(msi_data->irq,
199 ls_scfg_msi_irq_handler,
200 msi_data);
201
202 platform_set_drvdata(pdev, msi_data); 303 platform_set_drvdata(pdev, msi_data);
203 304
204 return 0; 305 return 0;
@@ -207,8 +308,10 @@ static int ls_scfg_msi_probe(struct platform_device *pdev)
207static int ls_scfg_msi_remove(struct platform_device *pdev) 308static int ls_scfg_msi_remove(struct platform_device *pdev)
208{ 309{
209 struct ls_scfg_msi *msi_data = platform_get_drvdata(pdev); 310 struct ls_scfg_msi *msi_data = platform_get_drvdata(pdev);
311 int i;
210 312
211 irq_set_chained_handler_and_data(msi_data->irq, NULL, NULL); 313 for (i = 0; i < msi_data->msir_num; i++)
314 ls_scfg_msi_teardown_hwirq(&msi_data->msir[i]);
212 315
213 irq_domain_remove(msi_data->msi_domain); 316 irq_domain_remove(msi_data->msi_domain);
214 irq_domain_remove(msi_data->parent); 317 irq_domain_remove(msi_data->parent);
@@ -218,14 +321,6 @@ static int ls_scfg_msi_remove(struct platform_device *pdev)
218 return 0; 321 return 0;
219} 322}
220 323
221static const struct of_device_id ls_scfg_msi_id[] = {
222 { .compatible = "fsl,1s1021a-msi", }, /* a typo */
223 { .compatible = "fsl,1s1043a-msi", }, /* a typo */
224 { .compatible = "fsl,ls1021a-msi", },
225 { .compatible = "fsl,ls1043a-msi", },
226 {},
227};
228
229static struct platform_driver ls_scfg_msi_driver = { 324static struct platform_driver ls_scfg_msi_driver = {
230 .driver = { 325 .driver = {
231 .name = "ls-scfg-msi", 326 .name = "ls-scfg-msi",