layerscape: add LS1043A Rev1.1 support
[openwrt/staging/yousong.git] / target / linux / layerscape / patches-4.4 / 8239-irqchip-ls-scfg-msi-add-LS1046a-MSI-support.patch
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
5
6 Cherry-pick patchwork patch with context adjustment.
7
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.
12
13 The patch adds structure 'ls_scfg_msir' to describe MSIR setting and
14 'ibs_shift' to store the different value between the SoCs.
15
16 Signed-off-by: Minghuan Lian <Minghuan.Lian@nxp.com>
17 Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
18 ---
19 drivers/irqchip/irq-ls-scfg-msi.c | 168 +++++++++++++++++++++++++++++---------
20 1 file changed, 131 insertions(+), 37 deletions(-)
21
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
26 @@ -17,13 +17,24 @@
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>
34
35 -#define MSI_MAX_IRQS 32
36 -#define MSI_IBS_SHIFT 3
37 -#define MSIR 4
38 +#define MSI_IRQS_PER_MSIR 32
39 +#define MSI_MSIR_OFFSET 4
40 +
41 +struct ls_scfg_msi_cfg {
42 + u32 ibs_shift; /* Shift of interrupt bit select */
43 +};
44 +
45 +struct ls_scfg_msir {
46 + struct ls_scfg_msi *msi_data;
47 + unsigned int index;
48 + unsigned int gic_irq;
49 + void __iomem *reg;
50 +};
51
52 struct ls_scfg_msi {
53 spinlock_t lock;
54 @@ -32,8 +43,11 @@ struct ls_scfg_msi {
55 struct irq_domain *msi_domain;
56 void __iomem *regs;
57 phys_addr_t msiir_addr;
58 - int irq;
59 - DECLARE_BITMAP(used, MSI_MAX_IRQS);
60 + struct ls_scfg_msi_cfg *cfg;
61 + u32 msir_num;
62 + struct ls_scfg_msir *msir;
63 + u32 irqs_num;
64 + unsigned long *used;
65 };
66
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)
69
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;
74 }
75
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);
79
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);
86 else
87 err = -ENOSPC;
88 @@ -106,7 +120,7 @@ static void ls_scfg_msi_domain_irq_free(struct irq_domain *domain,
89 int pos;
90
91 pos = d->hwirq;
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);
95 return;
96 }
97 @@ -123,15 +137,17 @@ static const struct irq_domain_ops ls_scfg_msi_domain_ops = {
98
99 static void ls_scfg_msi_irq_handler(struct irq_desc *desc)
100 {
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;
104 unsigned long val;
105 - int pos, virq;
106 + int pos, virq, hwirq;
107
108 chained_irq_enter(irq_desc_get_chip(desc), desc);
109
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);
117 if (virq)
118 generic_handle_irq(virq);
119 }
120 @@ -143,7 +159,7 @@ static int ls_scfg_msi_domains_init(struct ls_scfg_msi *msi_data)
121 {
122 /* Initialize MSI domain parent */
123 msi_data->parent = irq_domain_add_linear(NULL,
124 - MSI_MAX_IRQS,
125 + msi_data->irqs_num,
126 &ls_scfg_msi_domain_ops,
127 msi_data);
128 if (!msi_data->parent) {
129 @@ -164,16 +180,88 @@ static int ls_scfg_msi_domains_init(struct ls_scfg_msi *msi_data)
130 return 0;
131 }
132
133 +static int ls_scfg_msi_setup_hwirq(struct ls_scfg_msi *msi_data, int index)
134 +{
135 + struct ls_scfg_msir *msir;
136 + int virq, i, hwirq;
137 +
138 + virq = platform_get_irq(msi_data->pdev, index);
139 + if (virq <= 0)
140 + return -ENODEV;
141 +
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;
147 +
148 + irq_set_chained_handler_and_data(msir->gic_irq,
149 + ls_scfg_msi_irq_handler,
150 + msir);
151 +
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);
156 + }
157 +
158 + return 0;
159 +}
160 +
161 +static int ls_scfg_msi_teardown_hwirq(struct ls_scfg_msir *msir)
162 +{
163 + struct ls_scfg_msi *msi_data = msir->msi_data;
164 + int i, hwirq;
165 +
166 + if (msir->gic_irq > 0)
167 + irq_set_chained_handler_and_data(msir->gic_irq, NULL, NULL);
168 +
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);
172 + }
173 +
174 + return 0;
175 +}
176 +
177 +static struct ls_scfg_msi_cfg ls1021_msi_cfg = {
178 + .ibs_shift = 3,
179 +};
180 +
181 +static struct ls_scfg_msi_cfg ls1046_msi_cfg = {
182 + .ibs_shift = 2,
183 +};
184 +
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},
189 +
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 },
194 + {},
195 +};
196 +MODULE_DEVICE_TABLE(of, ls_scfg_msi_id);
197 +
198 static int ls_scfg_msi_probe(struct platform_device *pdev)
199 {
200 + const struct of_device_id *match;
201 struct ls_scfg_msi *msi_data;
202 struct resource *res;
203 - int ret;
204 + int i, ret;
205 +
206 + match = of_match_device(ls_scfg_msi_id, &pdev->dev);
207 + if (!match)
208 + return -ENODEV;
209
210 msi_data = devm_kzalloc(&pdev->dev, sizeof(*msi_data), GFP_KERNEL);
211 if (!msi_data)
212 return -ENOMEM;
213
214 + msi_data->cfg = (struct ls_scfg_msi_cfg *) match->data;
215 +
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)
220 }
221 msi_data->msiir_addr = res->start;
222
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");
226 - return -ENODEV;
227 - }
228 -
229 msi_data->pdev = pdev;
230 spin_lock_init(&msi_data->lock);
231
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),
237 + GFP_KERNEL);
238 + if (!msi_data->used)
239 + return -ENOMEM;
240 + /*
241 + * Reserve all the hwirqs
242 + * The available hwirqs will be released in ls1_msi_setup_hwirq()
243 + */
244 + bitmap_set(msi_data->used, 0, msi_data->irqs_num);
245 +
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),
249 + GFP_KERNEL);
250 + if (!msi_data->msir)
251 + return -ENOMEM;
252 +
253 + for (i = 0; i < msi_data->msir_num; i++)
254 + ls_scfg_msi_setup_hwirq(msi_data, i);
255 +
256 ret = ls_scfg_msi_domains_init(msi_data);
257 if (ret)
258 return ret;
259
260 - irq_set_chained_handler_and_data(msi_data->irq,
261 - ls_scfg_msi_irq_handler,
262 - msi_data);
263 -
264 platform_set_drvdata(pdev, msi_data);
265
266 return 0;
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)
269 {
270 struct ls_scfg_msi *msi_data = platform_get_drvdata(pdev);
271 + int i;
272
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]);
276
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)
280 return 0;
281 }
282
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", },
290 - {},
291 -};
292 -
293 static struct platform_driver ls_scfg_msi_driver = {
294 .driver = {
295 .name = "ls-scfg-msi",
296 --
297 2.1.0.27.g96db324
298