aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc/platforms
diff options
context:
space:
mode:
authorMichael Ellerman <michael@ellerman.id.au>2007-07-20 15:39:28 -0400
committerArnd Bergmann <arnd@klappe.arndb.de>2007-07-20 15:41:45 -0400
commitce21b3c9648ae55181787bf25ee00cf91dfd5c91 (patch)
tree9d2d22d9eab0a2621e29bc237fd1be7f3ea1f46c /arch/powerpc/platforms
parent8d2655e621bfc3c3f925016f881a36739d479f69 (diff)
[CELL] add support for MSI on Axon-based Cell systems
This patch adds support for the setup and decoding of MSIs on Axon-based Cell systems, using the MSIC mechanism. This involves setting up an area of BE memory which the Axon then uses as a FIFO for MSI messages. When one or more MSIs are decoded by the MSIC we receive an interrupt on the MPIC, and the MSI messages are written into the FIFO. At the moment we use a 64KB FIFO, one per MSIC/BE. Signed-off-by: Michael Ellerman <michael@ellerman.id.au> Signed-off-by: Arnd Bergmann <arnd.bergmann@de.ibm.com>
Diffstat (limited to 'arch/powerpc/platforms')
-rw-r--r--arch/powerpc/platforms/cell/Makefile2
-rw-r--r--arch/powerpc/platforms/cell/axon_msi.c445
2 files changed, 447 insertions, 0 deletions
diff --git a/arch/powerpc/platforms/cell/Makefile b/arch/powerpc/platforms/cell/Makefile
index be059718becd..f88a7c76f296 100644
--- a/arch/powerpc/platforms/cell/Makefile
+++ b/arch/powerpc/platforms/cell/Makefile
@@ -25,3 +25,5 @@ obj-$(CONFIG_SPU_BASE) += spu_callbacks.o spu_base.o \
25 $(spu-priv1-y) \ 25 $(spu-priv1-y) \
26 $(spu-manage-y) \ 26 $(spu-manage-y) \
27 spufs/ 27 spufs/
28
29obj-$(CONFIG_PCI_MSI) += axon_msi.o
diff --git a/arch/powerpc/platforms/cell/axon_msi.c b/arch/powerpc/platforms/cell/axon_msi.c
new file mode 100644
index 000000000000..4c9ab5b70bae
--- /dev/null
+++ b/arch/powerpc/platforms/cell/axon_msi.c
@@ -0,0 +1,445 @@
1/*
2 * Copyright 2007, Michael Ellerman, IBM Corporation.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version
7 * 2 of the License, or (at your option) any later version.
8 */
9
10
11#include <linux/interrupt.h>
12#include <linux/irq.h>
13#include <linux/kernel.h>
14#include <linux/pci.h>
15#include <linux/msi.h>
16#include <linux/reboot.h>
17
18#include <asm/dcr.h>
19#include <asm/machdep.h>
20#include <asm/prom.h>
21
22
23/*
24 * MSIC registers, specified as offsets from dcr_base
25 */
26#define MSIC_CTRL_REG 0x0
27
28/* Base Address registers specify FIFO location in BE memory */
29#define MSIC_BASE_ADDR_HI_REG 0x3
30#define MSIC_BASE_ADDR_LO_REG 0x4
31
32/* Hold the read/write offsets into the FIFO */
33#define MSIC_READ_OFFSET_REG 0x5
34#define MSIC_WRITE_OFFSET_REG 0x6
35
36
37/* MSIC control register flags */
38#define MSIC_CTRL_ENABLE 0x0001
39#define MSIC_CTRL_FIFO_FULL_ENABLE 0x0002
40#define MSIC_CTRL_IRQ_ENABLE 0x0008
41#define MSIC_CTRL_FULL_STOP_ENABLE 0x0010
42
43/*
44 * The MSIC can be configured to use a FIFO of 32KB, 64KB, 128KB or 256KB.
45 * Currently we're using a 64KB FIFO size.
46 */
47#define MSIC_FIFO_SIZE_SHIFT 16
48#define MSIC_FIFO_SIZE_BYTES (1 << MSIC_FIFO_SIZE_SHIFT)
49
50/*
51 * To configure the FIFO size as (1 << n) bytes, we write (n - 15) into bits
52 * 8-9 of the MSIC control reg.
53 */
54#define MSIC_CTRL_FIFO_SIZE (((MSIC_FIFO_SIZE_SHIFT - 15) << 8) & 0x300)
55
56/*
57 * We need to mask the read/write offsets to make sure they stay within
58 * the bounds of the FIFO. Also they should always be 16-byte aligned.
59 */
60#define MSIC_FIFO_SIZE_MASK ((MSIC_FIFO_SIZE_BYTES - 1) & ~0xFu)
61
62/* Each entry in the FIFO is 16 bytes, the first 4 bytes hold the irq # */
63#define MSIC_FIFO_ENTRY_SIZE 0x10
64
65
66struct axon_msic {
67 struct device_node *dn;
68 struct irq_host *irq_host;
69 __le32 *fifo;
70 dcr_host_t dcr_host;
71 struct list_head list;
72 u32 read_offset;
73 u32 dcr_base;
74};
75
76static LIST_HEAD(axon_msic_list);
77
78static void msic_dcr_write(struct axon_msic *msic, unsigned int dcr_n, u32 val)
79{
80 pr_debug("axon_msi: dcr_write(0x%x, 0x%x)\n", val, dcr_n);
81
82 dcr_write(msic->dcr_host, msic->dcr_base + dcr_n, val);
83}
84
85static u32 msic_dcr_read(struct axon_msic *msic, unsigned int dcr_n)
86{
87 return dcr_read(msic->dcr_host, msic->dcr_base + dcr_n);
88}
89
90static void axon_msi_cascade(unsigned int irq, struct irq_desc *desc)
91{
92 struct axon_msic *msic = get_irq_data(irq);
93 u32 write_offset, msi;
94 int idx;
95
96 write_offset = msic_dcr_read(msic, MSIC_WRITE_OFFSET_REG);
97 pr_debug("axon_msi: original write_offset 0x%x\n", write_offset);
98
99 /* write_offset doesn't wrap properly, so we have to mask it */
100 write_offset &= MSIC_FIFO_SIZE_MASK;
101
102 while (msic->read_offset != write_offset) {
103 idx = msic->read_offset / sizeof(__le32);
104 msi = le32_to_cpu(msic->fifo[idx]);
105 msi &= 0xFFFF;
106
107 pr_debug("axon_msi: woff %x roff %x msi %x\n",
108 write_offset, msic->read_offset, msi);
109
110 msic->read_offset += MSIC_FIFO_ENTRY_SIZE;
111 msic->read_offset &= MSIC_FIFO_SIZE_MASK;
112
113 if (msi < NR_IRQS && irq_map[msi].host == msic->irq_host)
114 generic_handle_irq(msi);
115 else
116 pr_debug("axon_msi: invalid irq 0x%x!\n", msi);
117 }
118
119 desc->chip->eoi(irq);
120}
121
122static struct axon_msic *find_msi_translator(struct pci_dev *dev)
123{
124 struct irq_host *irq_host;
125 struct device_node *dn, *tmp;
126 const phandle *ph;
127 struct axon_msic *msic = NULL;
128
129 dn = pci_device_to_OF_node(dev);
130 if (!dn) {
131 dev_dbg(&dev->dev, "axon_msi: no pci_dn found\n");
132 return NULL;
133 }
134
135 for (; dn; tmp = of_get_parent(dn), of_node_put(dn), dn = tmp) {
136 ph = of_get_property(dn, "msi-translator", NULL);
137 if (ph)
138 break;
139 }
140
141 if (!ph) {
142 dev_dbg(&dev->dev,
143 "axon_msi: no msi-translator property found\n");
144 goto out_error;
145 }
146
147 tmp = dn;
148 dn = of_find_node_by_phandle(*ph);
149 if (!dn) {
150 dev_dbg(&dev->dev,
151 "axon_msi: msi-translator doesn't point to a node\n");
152 goto out_error;
153 }
154
155 irq_host = irq_find_host(dn);
156 if (!irq_host) {
157 dev_dbg(&dev->dev, "axon_msi: no irq_host found for node %s\n",
158 dn->full_name);
159 goto out_error;
160 }
161
162 msic = irq_host->host_data;
163
164out_error:
165 of_node_put(dn);
166 of_node_put(tmp);
167
168 return msic;
169}
170
171static int axon_msi_check_device(struct pci_dev *dev, int nvec, int type)
172{
173 if (!find_msi_translator(dev))
174 return -ENODEV;
175
176 return 0;
177}
178
179static int setup_msi_msg_address(struct pci_dev *dev, struct msi_msg *msg)
180{
181 struct device_node *dn, *tmp;
182 struct msi_desc *entry;
183 int len;
184 const u32 *prop;
185
186 dn = pci_device_to_OF_node(dev);
187 if (!dn) {
188 dev_dbg(&dev->dev, "axon_msi: no pci_dn found\n");
189 return -ENODEV;
190 }
191
192 entry = list_first_entry(&dev->msi_list, struct msi_desc, list);
193
194 for (; dn; tmp = of_get_parent(dn), of_node_put(dn), dn = tmp) {
195 if (entry->msi_attrib.is_64) {
196 prop = of_get_property(dn, "msi-address-64", &len);
197 if (prop)
198 break;
199 }
200
201 prop = of_get_property(dn, "msi-address-32", &len);
202 if (prop)
203 break;
204 }
205
206 if (!prop) {
207 dev_dbg(&dev->dev,
208 "axon_msi: no msi-address-(32|64) properties found\n");
209 return -ENOENT;
210 }
211
212 switch (len) {
213 case 8:
214 msg->address_hi = prop[0];
215 msg->address_lo = prop[1];
216 break;
217 case 4:
218 msg->address_hi = 0;
219 msg->address_lo = prop[0];
220 break;
221 default:
222 dev_dbg(&dev->dev,
223 "axon_msi: malformed msi-address-(32|64) property\n");
224 of_node_put(dn);
225 return -EINVAL;
226 }
227
228 of_node_put(dn);
229
230 return 0;
231}
232
233static int axon_msi_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
234{
235 unsigned int virq, rc;
236 struct msi_desc *entry;
237 struct msi_msg msg;
238 struct axon_msic *msic;
239
240 msic = find_msi_translator(dev);
241 if (!msic)
242 return -ENODEV;
243
244 rc = setup_msi_msg_address(dev, &msg);
245 if (rc)
246 return rc;
247
248 /* We rely on being able to stash a virq in a u16 */
249 BUILD_BUG_ON(NR_IRQS > 65536);
250
251 list_for_each_entry(entry, &dev->msi_list, list) {
252 virq = irq_create_direct_mapping(msic->irq_host);
253 if (virq == NO_IRQ) {
254 dev_warn(&dev->dev,
255 "axon_msi: virq allocation failed!\n");
256 return -1;
257 }
258 dev_dbg(&dev->dev, "axon_msi: allocated virq 0x%x\n", virq);
259
260 set_irq_msi(virq, entry);
261 msg.data = virq;
262 write_msi_msg(virq, &msg);
263 }
264
265 return 0;
266}
267
268static void axon_msi_teardown_msi_irqs(struct pci_dev *dev)
269{
270 struct msi_desc *entry;
271
272 dev_dbg(&dev->dev, "axon_msi: tearing down msi irqs\n");
273
274 list_for_each_entry(entry, &dev->msi_list, list) {
275 if (entry->irq == NO_IRQ)
276 continue;
277
278 set_irq_msi(entry->irq, NULL);
279 irq_dispose_mapping(entry->irq);
280 }
281}
282
283static struct irq_chip msic_irq_chip = {
284 .mask = mask_msi_irq,
285 .unmask = unmask_msi_irq,
286 .shutdown = unmask_msi_irq,
287 .typename = "AXON-MSI",
288};
289
290static int msic_host_map(struct irq_host *h, unsigned int virq,
291 irq_hw_number_t hw)
292{
293 set_irq_chip_and_handler(virq, &msic_irq_chip, handle_simple_irq);
294
295 return 0;
296}
297
298static int msic_host_match(struct irq_host *host, struct device_node *dn)
299{
300 struct axon_msic *msic = host->host_data;
301
302 return msic->dn == dn;
303}
304
305static struct irq_host_ops msic_host_ops = {
306 .match = msic_host_match,
307 .map = msic_host_map,
308};
309
310static int axon_msi_notify_reboot(struct notifier_block *nb,
311 unsigned long code, void *data)
312{
313 struct axon_msic *msic;
314 u32 tmp;
315
316 list_for_each_entry(msic, &axon_msic_list, list) {
317 pr_debug("axon_msi: disabling %s\n", msic->dn->full_name);
318 tmp = msic_dcr_read(msic, MSIC_CTRL_REG);
319 tmp &= ~MSIC_CTRL_ENABLE & ~MSIC_CTRL_IRQ_ENABLE;
320 msic_dcr_write(msic, MSIC_CTRL_REG, tmp);
321 }
322
323 return 0;
324}
325
326static struct notifier_block axon_msi_reboot_notifier = {
327 .notifier_call = axon_msi_notify_reboot
328};
329
330static int axon_msi_setup_one(struct device_node *dn)
331{
332 struct page *page;
333 struct axon_msic *msic;
334 unsigned int virq;
335 int dcr_len;
336
337 pr_debug("axon_msi: setting up dn %s\n", dn->full_name);
338
339 msic = kzalloc(sizeof(struct axon_msic), GFP_KERNEL);
340 if (!msic) {
341 printk(KERN_ERR "axon_msi: couldn't allocate msic for %s\n",
342 dn->full_name);
343 goto out;
344 }
345
346 msic->dcr_base = dcr_resource_start(dn, 0);
347 dcr_len = dcr_resource_len(dn, 0);
348
349 if (msic->dcr_base == 0 || dcr_len == 0) {
350 printk(KERN_ERR
351 "axon_msi: couldn't parse dcr properties on %s\n",
352 dn->full_name);
353 goto out;
354 }
355
356 msic->dcr_host = dcr_map(dn, msic->dcr_base, dcr_len);
357 if (!DCR_MAP_OK(msic->dcr_host)) {
358 printk(KERN_ERR "axon_msi: dcr_map failed for %s\n",
359 dn->full_name);
360 goto out_free_msic;
361 }
362
363 page = alloc_pages_node(of_node_to_nid(dn), GFP_KERNEL,
364 get_order(MSIC_FIFO_SIZE_BYTES));
365 if (!page) {
366 printk(KERN_ERR "axon_msi: couldn't allocate fifo for %s\n",
367 dn->full_name);
368 goto out_free_msic;
369 }
370
371 msic->fifo = page_address(page);
372
373 msic->irq_host = irq_alloc_host(IRQ_HOST_MAP_NOMAP, NR_IRQS,
374 &msic_host_ops, 0);
375 if (!msic->irq_host) {
376 printk(KERN_ERR "axon_msi: couldn't allocate irq_host for %s\n",
377 dn->full_name);
378 goto out_free_fifo;
379 }
380
381 msic->irq_host->host_data = msic;
382
383 virq = irq_of_parse_and_map(dn, 0);
384 if (virq == NO_IRQ) {
385 printk(KERN_ERR "axon_msi: irq parse and map failed for %s\n",
386 dn->full_name);
387 goto out_free_host;
388 }
389
390 msic->dn = of_node_get(dn);
391
392 set_irq_data(virq, msic);
393 set_irq_chained_handler(virq, axon_msi_cascade);
394 pr_debug("axon_msi: irq 0x%x setup for axon_msi\n", virq);
395
396 /* Enable the MSIC hardware */
397 msic_dcr_write(msic, MSIC_BASE_ADDR_HI_REG, (u64)msic->fifo >> 32);
398 msic_dcr_write(msic, MSIC_BASE_ADDR_LO_REG,
399 (u64)msic->fifo & 0xFFFFFFFF);
400 msic_dcr_write(msic, MSIC_CTRL_REG,
401 MSIC_CTRL_IRQ_ENABLE | MSIC_CTRL_ENABLE |
402 MSIC_CTRL_FIFO_SIZE);
403
404 list_add(&msic->list, &axon_msic_list);
405
406 printk(KERN_DEBUG "axon_msi: setup MSIC on %s\n", dn->full_name);
407
408 return 0;
409
410out_free_host:
411 kfree(msic->irq_host);
412out_free_fifo:
413 __free_pages(virt_to_page(msic->fifo), get_order(MSIC_FIFO_SIZE_BYTES));
414out_free_msic:
415 kfree(msic);
416out:
417
418 return -1;
419}
420
421static int axon_msi_init(void)
422{
423 struct device_node *dn;
424 int found = 0;
425
426 pr_debug("axon_msi: initialising ...\n");
427
428 for_each_compatible_node(dn, NULL, "ibm,axon-msic") {
429 if (axon_msi_setup_one(dn) == 0)
430 found++;
431 }
432
433 if (found) {
434 ppc_md.setup_msi_irqs = axon_msi_setup_msi_irqs;
435 ppc_md.teardown_msi_irqs = axon_msi_teardown_msi_irqs;
436 ppc_md.msi_check_device = axon_msi_check_device;
437
438 register_reboot_notifier(&axon_msi_reboot_notifier);
439
440 pr_debug("axon_msi: registered callbacks!\n");
441 }
442
443 return 0;
444}
445arch_initcall(axon_msi_init);