1 From 20fd0e76257005cb46a2ce1a30018a45d96199bf Mon Sep 17 00:00:00 2001
2 From: Minghuan Lian <Minghuan.Lian@nxp.com>
3 Date: Tue, 17 Jan 2017 17:32:41 +0800
4 Subject: [PATCH 10/13] irqchip/ls-scfg-msi: add LS1046a MSI support
6 Cherry-pick patchwork patch with context adjustment.
8 LS1046a includes 4 MSIRs, each MSIR is assigned a dedicate GIC
9 SPI interrupt and provides 32 MSI interrupts. Compared to previous
10 MSI, LS1046a's IBS(interrupt bit select) shift is changed to 2 and
11 total MSI interrupt number is changed to 128.
13 The patch adds structure 'ls_scfg_msir' to describe MSIR setting and
14 'ibs_shift' to store the different value between the SoCs.
16 Signed-off-by: Minghuan Lian <Minghuan.Lian@nxp.com>
17 Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
19 drivers/irqchip/irq-ls-scfg-msi.c | 168 +++++++++++++++++++++++++++++---------
20 1 file changed, 131 insertions(+), 37 deletions(-)
22 diff --git a/drivers/irqchip/irq-ls-scfg-msi.c b/drivers/irqchip/irq-ls-scfg-msi.c
23 index 5b16f4a..6586076 100644
24 --- a/drivers/irqchip/irq-ls-scfg-msi.c
25 +++ b/drivers/irqchip/irq-ls-scfg-msi.c
27 #include <linux/irq.h>
28 #include <linux/irqchip/chained_irq.h>
29 #include <linux/irqdomain.h>
30 +#include <linux/of_irq.h>
31 #include <linux/of_pci.h>
32 #include <linux/of_platform.h>
33 #include <linux/spinlock.h>
35 -#define MSI_MAX_IRQS 32
36 -#define MSI_IBS_SHIFT 3
38 +#define MSI_IRQS_PER_MSIR 32
39 +#define MSI_MSIR_OFFSET 4
41 +struct ls_scfg_msi_cfg {
42 + u32 ibs_shift; /* Shift of interrupt bit select */
45 +struct ls_scfg_msir {
46 + struct ls_scfg_msi *msi_data;
48 + unsigned int gic_irq;
54 @@ -32,8 +43,11 @@ struct ls_scfg_msi {
55 struct irq_domain *msi_domain;
57 phys_addr_t msiir_addr;
59 - DECLARE_BITMAP(used, MSI_MAX_IRQS);
60 + struct ls_scfg_msi_cfg *cfg;
62 + struct ls_scfg_msir *msir;
64 + unsigned long *used;
67 static struct irq_chip ls_scfg_msi_irq_chip = {
68 @@ -55,7 +69,7 @@ static void ls_scfg_msi_compose_msg(struct irq_data *data, struct msi_msg *msg)
70 msg->address_hi = upper_32_bits(msi_data->msiir_addr);
71 msg->address_lo = lower_32_bits(msi_data->msiir_addr);
72 - msg->data = data->hwirq << MSI_IBS_SHIFT;
73 + msg->data = data->hwirq;
76 static int ls_scfg_msi_set_affinity(struct irq_data *irq_data,
77 @@ -81,8 +95,8 @@ static int ls_scfg_msi_domain_irq_alloc(struct irq_domain *domain,
78 WARN_ON(nr_irqs != 1);
80 spin_lock(&msi_data->lock);
81 - pos = find_first_zero_bit(msi_data->used, MSI_MAX_IRQS);
82 - if (pos < MSI_MAX_IRQS)
83 + pos = find_first_zero_bit(msi_data->used, msi_data->irqs_num);
84 + if (pos < msi_data->irqs_num)
85 __set_bit(pos, msi_data->used);
88 @@ -106,7 +120,7 @@ static void ls_scfg_msi_domain_irq_free(struct irq_domain *domain,
92 - if (pos < 0 || pos >= MSI_MAX_IRQS) {
93 + if (pos < 0 || pos >= msi_data->irqs_num) {
94 pr_err("failed to teardown msi. Invalid hwirq %d\n", pos);
97 @@ -123,15 +137,17 @@ static const struct irq_domain_ops ls_scfg_msi_domain_ops = {
99 static void ls_scfg_msi_irq_handler(struct irq_desc *desc)
101 - struct ls_scfg_msi *msi_data = irq_desc_get_handler_data(desc);
102 + struct ls_scfg_msir *msir = irq_desc_get_handler_data(desc);
103 + struct ls_scfg_msi *msi_data = msir->msi_data;
106 + int pos, virq, hwirq;
108 chained_irq_enter(irq_desc_get_chip(desc), desc);
110 - val = ioread32be(msi_data->regs + MSIR);
111 - for_each_set_bit(pos, &val, MSI_MAX_IRQS) {
112 - virq = irq_find_mapping(msi_data->parent, (31 - pos));
113 + val = ioread32be(msir->reg);
114 + for_each_set_bit(pos, &val, MSI_IRQS_PER_MSIR) {
115 + hwirq = ((31 - pos) << msi_data->cfg->ibs_shift) | msir->index;
116 + virq = irq_find_mapping(msi_data->parent, hwirq);
118 generic_handle_irq(virq);
120 @@ -143,7 +159,7 @@ static int ls_scfg_msi_domains_init(struct ls_scfg_msi *msi_data)
122 /* Initialize MSI domain parent */
123 msi_data->parent = irq_domain_add_linear(NULL,
125 + msi_data->irqs_num,
126 &ls_scfg_msi_domain_ops,
128 if (!msi_data->parent) {
129 @@ -164,16 +180,88 @@ static int ls_scfg_msi_domains_init(struct ls_scfg_msi *msi_data)
133 +static int ls_scfg_msi_setup_hwirq(struct ls_scfg_msi *msi_data, int index)
135 + struct ls_scfg_msir *msir;
136 + int virq, i, hwirq;
138 + virq = platform_get_irq(msi_data->pdev, index);
142 + msir = &msi_data->msir[index];
143 + msir->index = index;
144 + msir->msi_data = msi_data;
145 + msir->gic_irq = virq;
146 + msir->reg = msi_data->regs + MSI_MSIR_OFFSET + 4 * index;
148 + irq_set_chained_handler_and_data(msir->gic_irq,
149 + ls_scfg_msi_irq_handler,
152 + /* Release the hwirqs corresponding to this MSIR */
153 + for (i = 0; i < MSI_IRQS_PER_MSIR; i++) {
154 + hwirq = i << msi_data->cfg->ibs_shift | msir->index;
155 + bitmap_clear(msi_data->used, hwirq, 1);
161 +static int ls_scfg_msi_teardown_hwirq(struct ls_scfg_msir *msir)
163 + struct ls_scfg_msi *msi_data = msir->msi_data;
166 + if (msir->gic_irq > 0)
167 + irq_set_chained_handler_and_data(msir->gic_irq, NULL, NULL);
169 + for (i = 0; i < MSI_IRQS_PER_MSIR; i++) {
170 + hwirq = i << msi_data->cfg->ibs_shift | msir->index;
171 + bitmap_set(msi_data->used, hwirq, 1);
177 +static struct ls_scfg_msi_cfg ls1021_msi_cfg = {
181 +static struct ls_scfg_msi_cfg ls1046_msi_cfg = {
185 +static const struct of_device_id ls_scfg_msi_id[] = {
186 + /* The following two misspelled compatibles are obsolete */
187 + { .compatible = "fsl,1s1021a-msi", .data = &ls1021_msi_cfg},
188 + { .compatible = "fsl,1s1043a-msi", .data = &ls1021_msi_cfg},
190 + { .compatible = "fsl,ls1012a-msi", .data = &ls1021_msi_cfg },
191 + { .compatible = "fsl,ls1021a-msi", .data = &ls1021_msi_cfg },
192 + { .compatible = "fsl,ls1043a-msi", .data = &ls1021_msi_cfg },
193 + { .compatible = "fsl,ls1046a-msi", .data = &ls1046_msi_cfg },
196 +MODULE_DEVICE_TABLE(of, ls_scfg_msi_id);
198 static int ls_scfg_msi_probe(struct platform_device *pdev)
200 + const struct of_device_id *match;
201 struct ls_scfg_msi *msi_data;
202 struct resource *res;
206 + match = of_match_device(ls_scfg_msi_id, &pdev->dev);
210 msi_data = devm_kzalloc(&pdev->dev, sizeof(*msi_data), GFP_KERNEL);
214 + msi_data->cfg = (struct ls_scfg_msi_cfg *) match->data;
216 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
217 msi_data->regs = devm_ioremap_resource(&pdev->dev, res);
218 if (IS_ERR(msi_data->regs)) {
219 @@ -182,23 +270,37 @@ static int ls_scfg_msi_probe(struct platform_device *pdev)
221 msi_data->msiir_addr = res->start;
223 - msi_data->irq = platform_get_irq(pdev, 0);
224 - if (msi_data->irq <= 0) {
225 - dev_err(&pdev->dev, "failed to get MSI irq\n");
229 msi_data->pdev = pdev;
230 spin_lock_init(&msi_data->lock);
232 + msi_data->irqs_num = MSI_IRQS_PER_MSIR *
233 + (1 << msi_data->cfg->ibs_shift);
234 + msi_data->used = devm_kcalloc(&pdev->dev,
235 + BITS_TO_LONGS(msi_data->irqs_num),
236 + sizeof(*msi_data->used),
238 + if (!msi_data->used)
241 + * Reserve all the hwirqs
242 + * The available hwirqs will be released in ls1_msi_setup_hwirq()
244 + bitmap_set(msi_data->used, 0, msi_data->irqs_num);
246 + msi_data->msir_num = of_irq_count(pdev->dev.of_node);
247 + msi_data->msir = devm_kcalloc(&pdev->dev, msi_data->msir_num,
248 + sizeof(*msi_data->msir),
250 + if (!msi_data->msir)
253 + for (i = 0; i < msi_data->msir_num; i++)
254 + ls_scfg_msi_setup_hwirq(msi_data, i);
256 ret = ls_scfg_msi_domains_init(msi_data);
260 - irq_set_chained_handler_and_data(msi_data->irq,
261 - ls_scfg_msi_irq_handler,
264 platform_set_drvdata(pdev, msi_data);
267 @@ -207,8 +309,10 @@ static int ls_scfg_msi_probe(struct platform_device *pdev)
268 static int ls_scfg_msi_remove(struct platform_device *pdev)
270 struct ls_scfg_msi *msi_data = platform_get_drvdata(pdev);
273 - irq_set_chained_handler_and_data(msi_data->irq, NULL, NULL);
274 + for (i = 0; i < msi_data->msir_num; i++)
275 + ls_scfg_msi_teardown_hwirq(&msi_data->msir[i]);
277 irq_domain_remove(msi_data->msi_domain);
278 irq_domain_remove(msi_data->parent);
279 @@ -218,16 +322,6 @@ static int ls_scfg_msi_remove(struct platform_device *pdev)
283 -static const struct of_device_id ls_scfg_msi_id[] = {
284 - { .compatible = "fsl,ls1012a-msi", },
285 - { .compatible = "fsl,1s1021a-msi", }, /* a typo */
286 - { .compatible = "fsl,1s1043a-msi", }, /* a typo */
287 - { .compatible = "fsl,ls1021a-msi", },
288 - { .compatible = "fsl,ls1043a-msi", },
289 - { .compatible = "fsl,ls1046a-msi", },
293 static struct platform_driver ls_scfg_msi_driver = {
295 .name = "ls-scfg-msi",