diff options
Diffstat (limited to 'arch/powerpc/platforms/cell/axon_msi.c')
| -rw-r--r-- | arch/powerpc/platforms/cell/axon_msi.c | 142 |
1 files changed, 84 insertions, 58 deletions
diff --git a/arch/powerpc/platforms/cell/axon_msi.c b/arch/powerpc/platforms/cell/axon_msi.c index 896548ba1ca..85825b5401e 100644 --- a/arch/powerpc/platforms/cell/axon_msi.c +++ b/arch/powerpc/platforms/cell/axon_msi.c @@ -13,8 +13,10 @@ #include <linux/kernel.h> #include <linux/pci.h> #include <linux/msi.h> +#include <linux/export.h> #include <linux/of_platform.h> #include <linux/debugfs.h> +#include <linux/slab.h> #include <asm/dcr.h> #include <asm/machdep.h> @@ -65,7 +67,7 @@ struct axon_msic { - struct irq_host *irq_host; + struct irq_domain *irq_domain; __le32 *fifo_virt; dma_addr_t fifo_phys; dcr_host_t dcr_host; @@ -85,46 +87,72 @@ static inline void axon_msi_debug_setup(struct device_node *dn, static void msic_dcr_write(struct axon_msic *msic, unsigned int dcr_n, u32 val) { - pr_debug("axon_msi: dcr_write(0x%x, 0x%x)\n", val, dcr_n); + pr_devel("axon_msi: dcr_write(0x%x, 0x%x)\n", val, dcr_n); dcr_write(msic->dcr_host, dcr_n, val); } static void axon_msi_cascade(unsigned int irq, struct irq_desc *desc) { - struct axon_msic *msic = get_irq_data(irq); + struct irq_chip *chip = irq_desc_get_chip(desc); + struct axon_msic *msic = irq_get_handler_data(irq); u32 write_offset, msi; int idx; + int retry = 0; write_offset = dcr_read(msic->dcr_host, MSIC_WRITE_OFFSET_REG); - pr_debug("axon_msi: original write_offset 0x%x\n", write_offset); + pr_devel("axon_msi: original write_offset 0x%x\n", write_offset); /* write_offset doesn't wrap properly, so we have to mask it */ write_offset &= MSIC_FIFO_SIZE_MASK; - while (msic->read_offset != write_offset) { + while (msic->read_offset != write_offset && retry < 100) { idx = msic->read_offset / sizeof(__le32); msi = le32_to_cpu(msic->fifo_virt[idx]); msi &= 0xFFFF; - pr_debug("axon_msi: woff %x roff %x msi %x\n", + pr_devel("axon_msi: woff %x roff %x msi %x\n", write_offset, msic->read_offset, msi); + if (msi < nr_irqs && irq_get_chip_data(msi) == msic) { + generic_handle_irq(msi); + msic->fifo_virt[idx] = cpu_to_le32(0xffffffff); + } else { + /* + * Reading the MSIC_WRITE_OFFSET_REG does not + * reliably flush the outstanding DMA to the + * FIFO buffer. Here we were reading stale + * data, so we need to retry. + */ + udelay(1); + retry++; + pr_devel("axon_msi: invalid irq 0x%x!\n", msi); + continue; + } + + if (retry) { + pr_devel("axon_msi: late irq 0x%x, retry %d\n", + msi, retry); + retry = 0; + } + msic->read_offset += MSIC_FIFO_ENTRY_SIZE; msic->read_offset &= MSIC_FIFO_SIZE_MASK; + } - if (msi < NR_IRQS && irq_map[msi].host == msic->irq_host) - generic_handle_irq(msi); - else - pr_debug("axon_msi: invalid irq 0x%x!\n", msi); + if (retry) { + printk(KERN_WARNING "axon_msi: irq timed out\n"); + + msic->read_offset += MSIC_FIFO_ENTRY_SIZE; + msic->read_offset &= MSIC_FIFO_SIZE_MASK; } - desc->chip->eoi(irq); + chip->irq_eoi(&desc->irq_data); } static struct axon_msic *find_msi_translator(struct pci_dev *dev) { - struct irq_host *irq_host; + struct irq_domain *irq_domain; struct device_node *dn, *tmp; const phandle *ph; struct axon_msic *msic = NULL; @@ -156,14 +184,14 @@ static struct axon_msic *find_msi_translator(struct pci_dev *dev) goto out_error; } - irq_host = irq_find_host(dn); - if (!irq_host) { - dev_dbg(&dev->dev, "axon_msi: no irq_host found for node %s\n", + irq_domain = irq_find_host(dn); + if (!irq_domain) { + dev_dbg(&dev->dev, "axon_msi: no irq_domain found for node %s\n", dn->full_name); goto out_error; } - msic = irq_host->host_data; + msic = irq_domain->host_data; out_error: of_node_put(dn); @@ -248,11 +276,8 @@ static int axon_msi_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) if (rc) return rc; - /* We rely on being able to stash a virq in a u16 */ - BUILD_BUG_ON(NR_IRQS > 65536); - list_for_each_entry(entry, &dev->msi_list, list) { - virq = irq_create_direct_mapping(msic->irq_host); + virq = irq_create_direct_mapping(msic->irq_domain); if (virq == NO_IRQ) { dev_warn(&dev->dev, "axon_msi: virq allocation failed!\n"); @@ -260,7 +285,7 @@ static int axon_msi_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) } dev_dbg(&dev->dev, "axon_msi: allocated virq 0x%x\n", virq); - set_irq_msi(virq, entry); + irq_set_msi_desc(virq, entry); msg.data = virq; write_msi_msg(virq, &msg); } @@ -278,53 +303,51 @@ static void axon_msi_teardown_msi_irqs(struct pci_dev *dev) if (entry->irq == NO_IRQ) continue; - set_irq_msi(entry->irq, NULL); + irq_set_msi_desc(entry->irq, NULL); irq_dispose_mapping(entry->irq); } } static struct irq_chip msic_irq_chip = { - .mask = mask_msi_irq, - .unmask = unmask_msi_irq, - .shutdown = unmask_msi_irq, - .typename = "AXON-MSI", + .irq_mask = mask_msi_irq, + .irq_unmask = unmask_msi_irq, + .irq_shutdown = mask_msi_irq, + .name = "AXON-MSI", }; -static int msic_host_map(struct irq_host *h, unsigned int virq, +static int msic_host_map(struct irq_domain *h, unsigned int virq, irq_hw_number_t hw) { - set_irq_chip_and_handler(virq, &msic_irq_chip, handle_simple_irq); + irq_set_chip_data(virq, h->host_data); + irq_set_chip_and_handler(virq, &msic_irq_chip, handle_simple_irq); return 0; } -static struct irq_host_ops msic_host_ops = { +static const struct irq_domain_ops msic_host_ops = { .map = msic_host_map, }; -static int axon_msi_shutdown(struct of_device *device) +static void axon_msi_shutdown(struct platform_device *device) { - struct axon_msic *msic = device->dev.platform_data; + struct axon_msic *msic = dev_get_drvdata(&device->dev); u32 tmp; - pr_debug("axon_msi: disabling %s\n", - msic->irq_host->of_node->full_name); + pr_devel("axon_msi: disabling %s\n", + msic->irq_domain->of_node->full_name); tmp = dcr_read(msic->dcr_host, MSIC_CTRL_REG); tmp &= ~MSIC_CTRL_ENABLE & ~MSIC_CTRL_IRQ_ENABLE; msic_dcr_write(msic, MSIC_CTRL_REG, tmp); - - return 0; } -static int axon_msi_probe(struct of_device *device, - const struct of_device_id *device_id) +static int axon_msi_probe(struct platform_device *device) { - struct device_node *dn = device->node; + struct device_node *dn = device->dev.of_node; struct axon_msic *msic; unsigned int virq; int dcr_base, dcr_len; - pr_debug("axon_msi: setting up dn %s\n", dn->full_name); + pr_devel("axon_msi: setting up dn %s\n", dn->full_name); msic = kzalloc(sizeof(struct axon_msic), GFP_KERNEL); if (!msic) { @@ -340,7 +363,7 @@ static int axon_msi_probe(struct of_device *device, printk(KERN_ERR "axon_msi: couldn't parse dcr properties on %s\n", dn->full_name); - goto out; + goto out_free_msic; } msic->dcr_host = dcr_map(dn, dcr_base, dcr_len); @@ -364,20 +387,19 @@ static int axon_msi_probe(struct of_device *device, dn->full_name); goto out_free_fifo; } + memset(msic->fifo_virt, 0xff, MSIC_FIFO_SIZE_BYTES); - msic->irq_host = irq_alloc_host(dn, IRQ_HOST_MAP_NOMAP, - NR_IRQS, &msic_host_ops, 0); - if (!msic->irq_host) { - printk(KERN_ERR "axon_msi: couldn't allocate irq_host for %s\n", + /* We rely on being able to stash a virq in a u16, so limit irqs to < 65536 */ + msic->irq_domain = irq_domain_add_nomap(dn, 65536, &msic_host_ops, msic); + if (!msic->irq_domain) { + printk(KERN_ERR "axon_msi: couldn't allocate irq_domain for %s\n", dn->full_name); goto out_free_fifo; } - msic->irq_host->host_data = msic; - - set_irq_data(virq, msic); - set_irq_chained_handler(virq, axon_msi_cascade); - pr_debug("axon_msi: irq 0x%x setup for axon_msi\n", virq); + irq_set_handler_data(virq, msic); + irq_set_chained_handler(virq, axon_msi_cascade); + pr_devel("axon_msi: irq 0x%x setup for axon_msi\n", virq); /* Enable the MSIC hardware */ msic_dcr_write(msic, MSIC_BASE_ADDR_HI_REG, msic->fifo_phys >> 32); @@ -387,7 +409,10 @@ static int axon_msi_probe(struct of_device *device, MSIC_CTRL_IRQ_ENABLE | MSIC_CTRL_ENABLE | MSIC_CTRL_FIFO_SIZE); - device->dev.platform_data = msic; + msic->read_offset = dcr_read(msic->dcr_host, MSIC_WRITE_OFFSET_REG) + & MSIC_FIFO_SIZE_MASK; + + dev_set_drvdata(&device->dev, msic); ppc_md.setup_msi_irqs = axon_msi_setup_msi_irqs; ppc_md.teardown_msi_irqs = axon_msi_teardown_msi_irqs; @@ -416,18 +441,19 @@ static const struct of_device_id axon_msi_device_id[] = { {} }; -static struct of_platform_driver axon_msi_driver = { - .match_table = axon_msi_device_id, +static struct platform_driver axon_msi_driver = { .probe = axon_msi_probe, .shutdown = axon_msi_shutdown, - .driver = { - .name = "axon-msi" + .driver = { + .name = "axon-msi", + .owner = THIS_MODULE, + .of_match_table = axon_msi_device_id, }, }; static int __init axon_msi_init(void) { - return of_register_platform_driver(&axon_msi_driver); + return platform_driver_register(&axon_msi_driver); } subsys_initcall(axon_msi_init); @@ -455,13 +481,13 @@ void axon_msi_debug_setup(struct device_node *dn, struct axon_msic *msic) addr = of_translate_address(dn, of_get_property(dn, "reg", NULL)); if (addr == OF_BAD_ADDR) { - pr_debug("axon_msi: couldn't translate reg property\n"); + pr_devel("axon_msi: couldn't translate reg property\n"); return; } msic->trigger = ioremap(addr, 0x4); if (!msic->trigger) { - pr_debug("axon_msi: ioremap failed\n"); + pr_devel("axon_msi: ioremap failed\n"); return; } @@ -469,7 +495,7 @@ void axon_msi_debug_setup(struct device_node *dn, struct axon_msic *msic) if (!debugfs_create_file(name, 0600, powerpc_debugfs_root, msic, &fops_msic)) { - pr_debug("axon_msi: debugfs_create_file failed!\n"); + pr_devel("axon_msi: debugfs_create_file failed!\n"); return; } } |
