aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc/sysdev
diff options
context:
space:
mode:
authorJason Jin <Jason.jin@freescale.com>2008-05-23 04:32:46 -0400
committerKumar Gala <galak@kernel.crashing.org>2008-06-02 15:44:24 -0400
commit34e36c1541fe70e5b3842a3278c0e7631d31f4cb (patch)
treeea713b04c0634af6b446d7c0cf0d1b2f72fa5d34 /arch/powerpc/sysdev
parentaee1dc73b519227084d77b0b2fc972b68b4153d8 (diff)
[POWERPC] fsl: PCIe MSI support for 83xx/85xx/86xx processors.
This MSI driver can be used on 83xx/85xx/86xx board. In this driver, virtual interrupt host and chip were setup. There are 256 MSI interrupts in this host, Every 32 MSI interrupts cascaded to one IPIC/MPIC interrupt. The chip was treated as edge sensitive and some necessary functions were setup for this chip. Before using the MSI interrupt, PCI/PCIE device need to ask for a MSI interrupt in the 256 MSI interrupts. A 256bit bitmap show which MSI interrupt was used, reserve bit in the bitmap can be used to force the device use some designate MSI interrupt in the 256 MSI interrupts. Sometimes this is useful for testing the all the MSI interrupts. The msi-available-ranges property in the dts file was used for this purpose. Signed-off-by: Jason Jin <Jason.jin@freescale.com> Signed-off-by: Kumar Gala <galak@kernel.crashing.org>
Diffstat (limited to 'arch/powerpc/sysdev')
-rw-r--r--arch/powerpc/sysdev/Makefile3
-rw-r--r--arch/powerpc/sysdev/fsl_msi.c436
-rw-r--r--arch/powerpc/sysdev/fsl_msi.h42
-rw-r--r--arch/powerpc/sysdev/fsl_pci.c14
4 files changed, 494 insertions, 1 deletions
diff --git a/arch/powerpc/sysdev/Makefile b/arch/powerpc/sysdev/Makefile
index 2cc50520a698..dd6dff3ffb0f 100644
--- a/arch/powerpc/sysdev/Makefile
+++ b/arch/powerpc/sysdev/Makefile
@@ -4,6 +4,7 @@ endif
4 4
5mpic-msi-obj-$(CONFIG_PCI_MSI) += mpic_msi.o mpic_u3msi.o mpic_pasemi_msi.o 5mpic-msi-obj-$(CONFIG_PCI_MSI) += mpic_msi.o mpic_u3msi.o mpic_pasemi_msi.o
6obj-$(CONFIG_MPIC) += mpic.o $(mpic-msi-obj-y) 6obj-$(CONFIG_MPIC) += mpic.o $(mpic-msi-obj-y)
7fsl-msi-obj-$(CONFIG_PCI_MSI) += fsl_msi.o
7 8
8obj-$(CONFIG_PPC_MPC106) += grackle.o 9obj-$(CONFIG_PPC_MPC106) += grackle.o
9obj-$(CONFIG_PPC_DCR_NATIVE) += dcr-low.o 10obj-$(CONFIG_PPC_DCR_NATIVE) += dcr-low.o
@@ -11,7 +12,7 @@ obj-$(CONFIG_PPC_PMI) += pmi.o
11obj-$(CONFIG_U3_DART) += dart_iommu.o 12obj-$(CONFIG_U3_DART) += dart_iommu.o
12obj-$(CONFIG_MMIO_NVRAM) += mmio_nvram.o 13obj-$(CONFIG_MMIO_NVRAM) += mmio_nvram.o
13obj-$(CONFIG_FSL_SOC) += fsl_soc.o 14obj-$(CONFIG_FSL_SOC) += fsl_soc.o
14obj-$(CONFIG_FSL_PCI) += fsl_pci.o 15obj-$(CONFIG_FSL_PCI) += fsl_pci.o $(fsl-msi-obj-y)
15obj-$(CONFIG_FSL_LBC) += fsl_lbc.o 16obj-$(CONFIG_FSL_LBC) += fsl_lbc.o
16obj-$(CONFIG_RAPIDIO) += fsl_rio.o 17obj-$(CONFIG_RAPIDIO) += fsl_rio.o
17obj-$(CONFIG_TSI108_BRIDGE) += tsi108_pci.o tsi108_dev.o 18obj-$(CONFIG_TSI108_BRIDGE) += tsi108_pci.o tsi108_dev.o
diff --git a/arch/powerpc/sysdev/fsl_msi.c b/arch/powerpc/sysdev/fsl_msi.c
new file mode 100644
index 000000000000..9d0685babbd5
--- /dev/null
+++ b/arch/powerpc/sysdev/fsl_msi.c
@@ -0,0 +1,436 @@
1/*
2 * Copyright (C) 2007-2008 Freescale Semiconductor, Inc. All rights reserved.
3 *
4 * Author: Tony Li <tony.li@freescale.com>
5 * Jason Jin <Jason.jin@freescale.com>
6 *
7 * The hwirq alloc and free code reuse from sysdev/mpic_msi.c
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; version 2 of the
12 * License.
13 *
14 */
15#include <linux/irq.h>
16#include <linux/bootmem.h>
17#include <linux/bitmap.h>
18#include <linux/msi.h>
19#include <linux/pci.h>
20#include <linux/of_platform.h>
21#include <sysdev/fsl_soc.h>
22#include <asm/prom.h>
23#include <asm/hw_irq.h>
24#include <asm/ppc-pci.h>
25#include "fsl_msi.h"
26
27struct fsl_msi_feature {
28 u32 fsl_pic_ip;
29 u32 msiir_offset;
30};
31
32static struct fsl_msi *fsl_msi;
33
34static inline u32 fsl_msi_read(u32 __iomem *base, unsigned int reg)
35{
36 return in_be32(base + (reg >> 2));
37}
38
39static inline void fsl_msi_write(u32 __iomem *base,
40 unsigned int reg, u32 value)
41{
42 out_be32(base + (reg >> 2), value);
43}
44
45/*
46 * We do not need this actually. The MSIR register has been read once
47 * in the cascade interrupt. So, this MSI interrupt has been acked
48*/
49static void fsl_msi_end_irq(unsigned int virq)
50{
51}
52
53static struct irq_chip fsl_msi_chip = {
54 .mask = mask_msi_irq,
55 .unmask = unmask_msi_irq,
56 .ack = fsl_msi_end_irq,
57 .typename = " FSL-MSI ",
58};
59
60static int fsl_msi_host_map(struct irq_host *h, unsigned int virq,
61 irq_hw_number_t hw)
62{
63 struct irq_chip *chip = &fsl_msi_chip;
64
65 get_irq_desc(virq)->status |= IRQ_TYPE_EDGE_FALLING;
66
67 set_irq_chip_and_handler(virq, chip, handle_edge_irq);
68
69 return 0;
70}
71
72static struct irq_host_ops fsl_msi_host_ops = {
73 .map = fsl_msi_host_map,
74};
75
76irq_hw_number_t fsl_msi_alloc_hwirqs(struct fsl_msi *msi, int num)
77{
78 unsigned long flags;
79 int offset, order = get_count_order(num);
80
81 spin_lock_irqsave(&msi->bitmap_lock, flags);
82
83 offset = bitmap_find_free_region(msi->fsl_msi_bitmap,
84 NR_MSI_IRQS, order);
85
86 spin_unlock_irqrestore(&msi->bitmap_lock, flags);
87
88 pr_debug("%s: allocated 0x%x (2^%d) at offset 0x%x\n",
89 __func__, num, order, offset);
90
91 return offset;
92}
93
94void fsl_msi_free_hwirqs(struct fsl_msi *msi, int offset, int num)
95{
96 unsigned long flags;
97 int order = get_count_order(num);
98
99 pr_debug("%s: freeing 0x%x (2^%d) at offset 0x%x\n",
100 __func__, num, order, offset);
101
102 spin_lock_irqsave(&msi->bitmap_lock, flags);
103 bitmap_release_region(msi->fsl_msi_bitmap, offset, order);
104 spin_unlock_irqrestore(&msi->bitmap_lock, flags);
105}
106
107static int fsl_msi_free_dt_hwirqs(struct fsl_msi *msi)
108{
109 int i, len;
110 const u32 *p;
111
112 bitmap_allocate_region(msi->fsl_msi_bitmap, 0,
113 get_count_order(NR_MSI_IRQS));
114
115 p = of_get_property(msi->of_node, "msi-available-ranges", &len);
116
117 if (!p) {
118 /* No msi-available-ranges property,
119 * All the 256 MSI interrupts can be used
120 */
121 fsl_msi_free_hwirqs(msi, 0, 0x100);
122 return 0;
123 }
124
125 if ((len % (2 * sizeof(u32))) != 0) {
126 printk(KERN_WARNING "fsl_msi: Malformed msi-available-ranges "
127 "property on %s\n", msi->of_node->full_name);
128 return -EINVAL;
129 }
130
131 /* Format is: (<u32 start> <u32 count>)+ */
132 len /= 2 * sizeof(u32);
133 for (i = 0; i < len; i++, p += 2)
134 fsl_msi_free_hwirqs(msi, *p, *(p + 1));
135
136 return 0;
137}
138
139static int fsl_msi_init_allocator(struct fsl_msi *msi_data)
140{
141 int rc, size;
142
143 size = BITS_TO_LONGS(NR_MSI_IRQS) * sizeof(u32);
144
145 msi_data->fsl_msi_bitmap = kzalloc(size, GFP_KERNEL);
146
147 if (msi_data->fsl_msi_bitmap == NULL) {
148 pr_debug("%s: ENOMEM allocating allocator bitmap!\n",
149 __func__);
150 return -ENOMEM;
151 }
152
153 rc = fsl_msi_free_dt_hwirqs(msi_data);
154 if (rc)
155 goto out_free;
156
157 return 0;
158out_free:
159 kfree(msi_data->fsl_msi_bitmap);
160
161 msi_data->fsl_msi_bitmap = NULL;
162 return rc;
163
164}
165
166static int fsl_msi_check_device(struct pci_dev *pdev, int nvec, int type)
167{
168 if (type == PCI_CAP_ID_MSIX)
169 pr_debug("fslmsi: MSI-X untested, trying anyway.\n");
170
171 return 0;
172}
173
174static void fsl_teardown_msi_irqs(struct pci_dev *pdev)
175{
176 struct msi_desc *entry;
177 struct fsl_msi *msi_data = fsl_msi;
178
179 list_for_each_entry(entry, &pdev->msi_list, list) {
180 if (entry->irq == NO_IRQ)
181 continue;
182 set_irq_msi(entry->irq, NULL);
183 fsl_msi_free_hwirqs(msi_data, virq_to_hw(entry->irq), 1);
184 irq_dispose_mapping(entry->irq);
185 }
186
187 return;
188}
189
190static void fsl_compose_msi_msg(struct pci_dev *pdev, int hwirq,
191 struct msi_msg *msg)
192{
193 struct fsl_msi *msi_data = fsl_msi;
194
195 msg->address_lo = msi_data->msi_addr_lo;
196 msg->address_hi = msi_data->msi_addr_hi;
197 msg->data = hwirq;
198
199 pr_debug("%s: allocated srs: %d, ibs: %d\n",
200 __func__, hwirq / IRQS_PER_MSI_REG, hwirq % IRQS_PER_MSI_REG);
201}
202
203static int fsl_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type)
204{
205 irq_hw_number_t hwirq;
206 int rc;
207 unsigned int virq;
208 struct msi_desc *entry;
209 struct msi_msg msg;
210 struct fsl_msi *msi_data = fsl_msi;
211
212 list_for_each_entry(entry, &pdev->msi_list, list) {
213 hwirq = fsl_msi_alloc_hwirqs(msi_data, 1);
214 if (hwirq < 0) {
215 rc = hwirq;
216 pr_debug("%s: fail allocating msi interrupt\n",
217 __func__);
218 goto out_free;
219 }
220
221 virq = irq_create_mapping(msi_data->irqhost, hwirq);
222
223 if (virq == NO_IRQ) {
224 pr_debug("%s: fail mapping hwirq 0x%lx\n",
225 __func__, hwirq);
226 fsl_msi_free_hwirqs(msi_data, hwirq, 1);
227 rc = -ENOSPC;
228 goto out_free;
229 }
230 set_irq_msi(virq, entry);
231
232 fsl_compose_msi_msg(pdev, hwirq, &msg);
233 write_msi_msg(virq, &msg);
234 }
235 return 0;
236
237out_free:
238 return rc;
239}
240
241void fsl_msi_cascade(unsigned int irq, struct irq_desc *desc)
242{
243 unsigned int cascade_irq;
244 struct fsl_msi *msi_data = fsl_msi;
245 int msir_index = -1;
246 u32 msir_value = 0;
247 u32 intr_index;
248 u32 have_shift = 0;
249
250 spin_lock(&desc->lock);
251 if ((msi_data->feature & FSL_PIC_IP_MASK) == FSL_PIC_IP_IPIC) {
252 if (desc->chip->mask_ack)
253 desc->chip->mask_ack(irq);
254 else {
255 desc->chip->mask(irq);
256 desc->chip->ack(irq);
257 }
258 }
259
260 if (unlikely(desc->status & IRQ_INPROGRESS))
261 goto unlock;
262
263 msir_index = (int)(desc->handler_data);
264
265 if (msir_index >= NR_MSI_REG)
266 cascade_irq = NO_IRQ;
267
268 desc->status |= IRQ_INPROGRESS;
269 switch (fsl_msi->feature & FSL_PIC_IP_MASK) {
270 case FSL_PIC_IP_MPIC:
271 msir_value = fsl_msi_read(msi_data->msi_regs,
272 msir_index * 0x10);
273 break;
274 case FSL_PIC_IP_IPIC:
275 msir_value = fsl_msi_read(msi_data->msi_regs, msir_index * 0x4);
276 break;
277 }
278
279 while (msir_value) {
280 intr_index = ffs(msir_value) - 1;
281
282 cascade_irq = irq_linear_revmap(msi_data->irqhost,
283 (msir_index * IRQS_PER_MSI_REG +
284 intr_index + have_shift));
285 if (cascade_irq != NO_IRQ)
286 generic_handle_irq(cascade_irq);
287 have_shift += (intr_index + 1);
288 msir_value = (msir_value >> (intr_index + 1));
289 }
290 desc->status &= ~IRQ_INPROGRESS;
291
292 switch (msi_data->feature & FSL_PIC_IP_MASK) {
293 case FSL_PIC_IP_MPIC:
294 desc->chip->eoi(irq);
295 break;
296 case FSL_PIC_IP_IPIC:
297 if (!(desc->status & IRQ_DISABLED) && desc->chip->unmask)
298 desc->chip->unmask(irq);
299 break;
300 }
301unlock:
302 spin_unlock(&desc->lock);
303}
304
305static int __devinit fsl_of_msi_probe(struct of_device *dev,
306 const struct of_device_id *match)
307{
308 struct fsl_msi *msi;
309 struct resource res;
310 int err, i, count;
311 int rc;
312 int virt_msir;
313 const u32 *p;
314 struct fsl_msi_feature *tmp_data;
315
316 printk(KERN_DEBUG "Setting up Freescale MSI support\n");
317
318 msi = kzalloc(sizeof(struct fsl_msi), GFP_KERNEL);
319 if (!msi) {
320 dev_err(&dev->dev, "No memory for MSI structure\n");
321 err = -ENOMEM;
322 goto error_out;
323 }
324
325 msi->of_node = of_node_get(dev->node);
326
327 msi->irqhost = irq_alloc_host(of_node_get(dev->node),
328 IRQ_HOST_MAP_LINEAR,
329 NR_MSI_IRQS, &fsl_msi_host_ops, 0);
330 if (msi->irqhost == NULL) {
331 dev_err(&dev->dev, "No memory for MSI irqhost\n");
332 of_node_put(dev->node);
333 err = -ENOMEM;
334 goto error_out;
335 }
336
337 /* Get the MSI reg base */
338 err = of_address_to_resource(dev->node, 0, &res);
339 if (err) {
340 dev_err(&dev->dev, "%s resource error!\n",
341 dev->node->full_name);
342 goto error_out;
343 }
344
345 msi->msi_regs = ioremap(res.start, res.end - res.start + 1);
346 if (!msi->msi_regs) {
347 dev_err(&dev->dev, "ioremap problem failed\n");
348 goto error_out;
349 }
350
351 tmp_data = (struct fsl_msi_feature *)match->data;
352
353 msi->feature = tmp_data->fsl_pic_ip;
354
355 msi->irqhost->host_data = msi;
356
357 msi->msi_addr_hi = 0x0;
358 msi->msi_addr_lo = res.start + tmp_data->msiir_offset;
359
360 rc = fsl_msi_init_allocator(msi);
361 if (rc) {
362 dev_err(&dev->dev, "Error allocating MSI bitmap\n");
363 goto error_out;
364 }
365
366 p = of_get_property(dev->node, "interrupts", &count);
367 if (!p) {
368 dev_err(&dev->dev, "no interrupts property found on %s\n",
369 dev->node->full_name);
370 err = -ENODEV;
371 goto error_out;
372 }
373 if (count % 8 != 0) {
374 dev_err(&dev->dev, "Malformed interrupts property on %s\n",
375 dev->node->full_name);
376 err = -EINVAL;
377 goto error_out;
378 }
379
380 count /= sizeof(u32);
381 for (i = 0; i < count / 2; i++) {
382 if (i > NR_MSI_REG)
383 break;
384 virt_msir = irq_of_parse_and_map(dev->node, i);
385 if (virt_msir != NO_IRQ) {
386 set_irq_data(virt_msir, (void *)i);
387 set_irq_chained_handler(virt_msir, fsl_msi_cascade);
388 }
389 }
390
391 fsl_msi = msi;
392
393 WARN_ON(ppc_md.setup_msi_irqs);
394 ppc_md.setup_msi_irqs = fsl_setup_msi_irqs;
395 ppc_md.teardown_msi_irqs = fsl_teardown_msi_irqs;
396 ppc_md.msi_check_device = fsl_msi_check_device;
397 return 0;
398error_out:
399 kfree(msi);
400 return err;
401}
402
403static const struct fsl_msi_feature mpic_msi_feature = {
404 .fsl_pic_ip = FSL_PIC_IP_MPIC,
405 .msiir_offset = 0x140,
406};
407
408static const struct fsl_msi_feature ipic_msi_feature = {
409 .fsl_pic_ip = FSL_PIC_IP_IPIC,
410 .msiir_offset = 0x38,
411};
412
413static const struct of_device_id fsl_of_msi_ids[] = {
414 {
415 .compatible = "fsl,mpic-msi",
416 .data = (void *)&mpic_msi_feature,
417 },
418 {
419 .compatible = "fsl,ipic-msi",
420 .data = (void *)&ipic_msi_feature,
421 },
422 {}
423};
424
425static struct of_platform_driver fsl_of_msi_driver = {
426 .name = "fsl-msi",
427 .match_table = fsl_of_msi_ids,
428 .probe = fsl_of_msi_probe,
429};
430
431static __init int fsl_of_msi_init(void)
432{
433 return of_register_platform_driver(&fsl_of_msi_driver);
434}
435
436subsys_initcall(fsl_of_msi_init);
diff --git a/arch/powerpc/sysdev/fsl_msi.h b/arch/powerpc/sysdev/fsl_msi.h
new file mode 100644
index 000000000000..a653468521fa
--- /dev/null
+++ b/arch/powerpc/sysdev/fsl_msi.h
@@ -0,0 +1,42 @@
1/*
2 * Copyright (C) 2007-2008 Freescale Semiconductor, Inc. All rights reserved.
3 *
4 * Author: Tony Li <tony.li@freescale.com>
5 * Jason Jin <Jason.jin@freescale.com>
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; version 2 of the
10 * License.
11 *
12 */
13#ifndef _POWERPC_SYSDEV_FSL_MSI_H
14#define _POWERPC_SYSDEV_FSL_MSI_H
15
16#define NR_MSI_REG 8
17#define IRQS_PER_MSI_REG 32
18#define NR_MSI_IRQS (NR_MSI_REG * IRQS_PER_MSI_REG)
19
20#define FSL_PIC_IP_MASK 0x0000000F
21#define FSL_PIC_IP_MPIC 0x00000001
22#define FSL_PIC_IP_IPIC 0x00000002
23
24struct fsl_msi {
25 /* Device node of the MSI interrupt*/
26 struct device_node *of_node;
27
28 struct irq_host *irqhost;
29
30 unsigned long cascade_irq;
31
32 u32 msi_addr_lo;
33 u32 msi_addr_hi;
34 void __iomem *msi_regs;
35 u32 feature;
36
37 unsigned long *fsl_msi_bitmap;
38 spinlock_t bitmap_lock;
39};
40
41#endif /* _POWERPC_SYSDEV_FSL_MSI_H */
42
diff --git a/arch/powerpc/sysdev/fsl_pci.c b/arch/powerpc/sysdev/fsl_pci.c
index bf13c2174a4e..52a5f7f41b4a 100644
--- a/arch/powerpc/sysdev/fsl_pci.c
+++ b/arch/powerpc/sysdev/fsl_pci.c
@@ -106,6 +106,16 @@ void __init setup_pci_cmd(struct pci_controller *hose)
106 } 106 }
107} 107}
108 108
109#ifdef CONFIG_PCI_MSI
110void __init setup_pci_pcsrbar(struct pci_controller *hose)
111{
112 phys_addr_t immr_base;
113
114 immr_base = get_immrbase();
115 early_write_config_dword(hose, 0, 0, PCI_BASE_ADDRESS_0, immr_base);
116}
117#endif
118
109static int fsl_pcie_bus_fixup; 119static int fsl_pcie_bus_fixup;
110 120
111static void __init quirk_fsl_pcie_header(struct pci_dev *dev) 121static void __init quirk_fsl_pcie_header(struct pci_dev *dev)
@@ -211,6 +221,10 @@ int __init fsl_add_bridge(struct device_node *dev, int is_primary)
211 /* Setup PEX window registers */ 221 /* Setup PEX window registers */
212 setup_pci_atmu(hose, &rsrc); 222 setup_pci_atmu(hose, &rsrc);
213 223
224 /* Setup PEXCSRBAR */
225#ifdef CONFIG_PCI_MSI
226 setup_pci_pcsrbar(hose);
227#endif
214 return 0; 228 return 0;
215} 229}
216 230