diff options
author | Michael Ellerman <michael@ellerman.id.au> | 2008-01-25 00:59:14 -0500 |
---|---|---|
committer | Paul Mackerras <paulus@samba.org> | 2008-02-06 00:30:00 -0500 |
commit | de4c928b843063b7079c35c950c1d38c559fa0cb (patch) | |
tree | 59c3f70d0047bef6c68bc7d514175c9d6378ec2c /arch/powerpc/platforms/cell | |
parent | e4347dfb580e0172185cb38529266ecf3b4d0baa (diff) |
[POWERPC] Avoid DMA exception when using axon_msi with IOMMU
There's a brown-paper-bag bug in axon_msi, we pass the address of our
FIFO directly to the hardware, without DMA mapping it. This leads to
DMA exceptions if you enable MSI & the IOMMU.
The fix is to correctly DMA map the fifo, dma_alloc_coherent() does
what we want - and we need to track the virt & phys addresses.
Signed-off-by: Michael Ellerman <michael@ellerman.id.au>
Acked-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: Paul Mackerras <paulus@samba.org>
Diffstat (limited to 'arch/powerpc/platforms/cell')
-rw-r--r-- | arch/powerpc/platforms/cell/axon_msi.c | 21 |
1 files changed, 10 insertions, 11 deletions
diff --git a/arch/powerpc/platforms/cell/axon_msi.c b/arch/powerpc/platforms/cell/axon_msi.c index ea3dc8c64a38..b9a97c4ae15a 100644 --- a/arch/powerpc/platforms/cell/axon_msi.c +++ b/arch/powerpc/platforms/cell/axon_msi.c | |||
@@ -65,7 +65,8 @@ | |||
65 | 65 | ||
66 | struct axon_msic { | 66 | struct axon_msic { |
67 | struct irq_host *irq_host; | 67 | struct irq_host *irq_host; |
68 | __le32 *fifo; | 68 | __le32 *fifo_virt; |
69 | dma_addr_t fifo_phys; | ||
69 | dcr_host_t dcr_host; | 70 | dcr_host_t dcr_host; |
70 | u32 read_offset; | 71 | u32 read_offset; |
71 | }; | 72 | }; |
@@ -91,7 +92,7 @@ static void axon_msi_cascade(unsigned int irq, struct irq_desc *desc) | |||
91 | 92 | ||
92 | while (msic->read_offset != write_offset) { | 93 | while (msic->read_offset != write_offset) { |
93 | idx = msic->read_offset / sizeof(__le32); | 94 | idx = msic->read_offset / sizeof(__le32); |
94 | msi = le32_to_cpu(msic->fifo[idx]); | 95 | msi = le32_to_cpu(msic->fifo_virt[idx]); |
95 | msi &= 0xFFFF; | 96 | msi &= 0xFFFF; |
96 | 97 | ||
97 | pr_debug("axon_msi: woff %x roff %x msi %x\n", | 98 | pr_debug("axon_msi: woff %x roff %x msi %x\n", |
@@ -306,7 +307,6 @@ static int axon_msi_shutdown(struct of_device *device) | |||
306 | static int axon_msi_probe(struct of_device *device, | 307 | static int axon_msi_probe(struct of_device *device, |
307 | const struct of_device_id *device_id) | 308 | const struct of_device_id *device_id) |
308 | { | 309 | { |
309 | struct page *page; | ||
310 | struct device_node *dn = device->node; | 310 | struct device_node *dn = device->node; |
311 | struct axon_msic *msic; | 311 | struct axon_msic *msic; |
312 | unsigned int virq; | 312 | unsigned int virq; |
@@ -338,16 +338,14 @@ static int axon_msi_probe(struct of_device *device, | |||
338 | goto out_free_msic; | 338 | goto out_free_msic; |
339 | } | 339 | } |
340 | 340 | ||
341 | page = alloc_pages_node(of_node_to_nid(dn), GFP_KERNEL, | 341 | msic->fifo_virt = dma_alloc_coherent(&device->dev, MSIC_FIFO_SIZE_BYTES, |
342 | get_order(MSIC_FIFO_SIZE_BYTES)); | 342 | &msic->fifo_phys, GFP_KERNEL); |
343 | if (!page) { | 343 | if (!msic->fifo_virt) { |
344 | printk(KERN_ERR "axon_msi: couldn't allocate fifo for %s\n", | 344 | printk(KERN_ERR "axon_msi: couldn't allocate fifo for %s\n", |
345 | dn->full_name); | 345 | dn->full_name); |
346 | goto out_free_msic; | 346 | goto out_free_msic; |
347 | } | 347 | } |
348 | 348 | ||
349 | msic->fifo = page_address(page); | ||
350 | |||
351 | msic->irq_host = irq_alloc_host(of_node_get(dn), IRQ_HOST_MAP_NOMAP, | 349 | msic->irq_host = irq_alloc_host(of_node_get(dn), IRQ_HOST_MAP_NOMAP, |
352 | NR_IRQS, &msic_host_ops, 0); | 350 | NR_IRQS, &msic_host_ops, 0); |
353 | if (!msic->irq_host) { | 351 | if (!msic->irq_host) { |
@@ -370,9 +368,9 @@ static int axon_msi_probe(struct of_device *device, | |||
370 | pr_debug("axon_msi: irq 0x%x setup for axon_msi\n", virq); | 368 | pr_debug("axon_msi: irq 0x%x setup for axon_msi\n", virq); |
371 | 369 | ||
372 | /* Enable the MSIC hardware */ | 370 | /* Enable the MSIC hardware */ |
373 | msic_dcr_write(msic, MSIC_BASE_ADDR_HI_REG, (u64)msic->fifo >> 32); | 371 | msic_dcr_write(msic, MSIC_BASE_ADDR_HI_REG, msic->fifo_phys >> 32); |
374 | msic_dcr_write(msic, MSIC_BASE_ADDR_LO_REG, | 372 | msic_dcr_write(msic, MSIC_BASE_ADDR_LO_REG, |
375 | (u64)msic->fifo & 0xFFFFFFFF); | 373 | msic->fifo_phys & 0xFFFFFFFF); |
376 | msic_dcr_write(msic, MSIC_CTRL_REG, | 374 | msic_dcr_write(msic, MSIC_CTRL_REG, |
377 | MSIC_CTRL_IRQ_ENABLE | MSIC_CTRL_ENABLE | | 375 | MSIC_CTRL_IRQ_ENABLE | MSIC_CTRL_ENABLE | |
378 | MSIC_CTRL_FIFO_SIZE); | 376 | MSIC_CTRL_FIFO_SIZE); |
@@ -390,7 +388,8 @@ static int axon_msi_probe(struct of_device *device, | |||
390 | out_free_host: | 388 | out_free_host: |
391 | kfree(msic->irq_host); | 389 | kfree(msic->irq_host); |
392 | out_free_fifo: | 390 | out_free_fifo: |
393 | __free_pages(virt_to_page(msic->fifo), get_order(MSIC_FIFO_SIZE_BYTES)); | 391 | dma_free_coherent(&device->dev, MSIC_FIFO_SIZE_BYTES, msic->fifo_virt, |
392 | msic->fifo_phys); | ||
394 | out_free_msic: | 393 | out_free_msic: |
395 | kfree(msic); | 394 | kfree(msic); |
396 | out: | 395 | out: |