diff options
Diffstat (limited to 'arch/powerpc')
-rw-r--r-- | arch/powerpc/sysdev/fsl_msi.c | 92 |
1 files changed, 56 insertions, 36 deletions
diff --git a/arch/powerpc/sysdev/fsl_msi.c b/arch/powerpc/sysdev/fsl_msi.c index f6051d7de3e..8d6b074bb3e 100644 --- a/arch/powerpc/sysdev/fsl_msi.c +++ b/arch/powerpc/sysdev/fsl_msi.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007-2010 Freescale Semiconductor, Inc. + * Copyright (C) 2007-2011 Freescale Semiconductor, Inc. * * Author: Tony Li <tony.li@freescale.com> * Jason Jin <Jason.jin@freescale.com> @@ -274,19 +274,47 @@ static int fsl_of_msi_remove(struct platform_device *ofdev) return 0; } +static int __devinit fsl_msi_setup_hwirq(struct fsl_msi *msi, + struct platform_device *dev, + int offset, int irq_index) +{ + struct fsl_msi_cascade_data *cascade_data = NULL; + int virt_msir; + + virt_msir = irq_of_parse_and_map(dev->dev.of_node, irq_index); + if (virt_msir == NO_IRQ) { + dev_err(&dev->dev, "%s: Cannot translate IRQ index %d\n", + __func__, irq_index); + return 0; + } + + cascade_data = kzalloc(sizeof(struct fsl_msi_cascade_data), GFP_KERNEL); + if (!cascade_data) { + dev_err(&dev->dev, "No memory for MSI cascade data\n"); + return -ENOMEM; + } + + msi->msi_virqs[irq_index] = virt_msir; + cascade_data->index = offset + irq_index; + cascade_data->msi_data = msi; + set_irq_data(virt_msir, cascade_data); + set_irq_chained_handler(virt_msir, fsl_msi_cascade); + + return 0; +} + static int __devinit fsl_of_msi_probe(struct platform_device *dev, const struct of_device_id *match) { struct fsl_msi *msi; struct resource res; - int err, i, count; + int err, i, j, irq_index, count; int rc; - int virt_msir; const u32 *p; struct fsl_msi_feature *features = match->data; - struct fsl_msi_cascade_data *cascade_data = NULL; int len; u32 offset; + static const u32 all_avail[] = { 0, NR_MSI_IRQS }; printk(KERN_DEBUG "Setting up Freescale MSI support\n"); @@ -333,42 +361,34 @@ static int __devinit fsl_of_msi_probe(struct platform_device *dev, goto error_out; } - p = of_get_property(dev->dev.of_node, "interrupts", &count); - if (!p) { - dev_err(&dev->dev, "no interrupts property found on %s\n", - dev->dev.of_node->full_name); - err = -ENODEV; - goto error_out; - } - if (count % 8 != 0) { - dev_err(&dev->dev, "Malformed interrupts property on %s\n", - dev->dev.of_node->full_name); + p = of_get_property(dev->dev.of_node, "msi-available-ranges", &len); + if (p && len % (2 * sizeof(u32)) != 0) { + dev_err(&dev->dev, "%s: Malformed msi-available-ranges property\n", + __func__); err = -EINVAL; goto error_out; } - offset = 0; - p = of_get_property(dev->dev.of_node, "msi-available-ranges", &len); - if (p) - offset = *p / IRQS_PER_MSI_REG; - - count /= sizeof(u32); - for (i = 0; i < min(count / 2, NR_MSI_REG); i++) { - virt_msir = irq_of_parse_and_map(dev->dev.of_node, i); - if (virt_msir != NO_IRQ) { - cascade_data = kzalloc( - sizeof(struct fsl_msi_cascade_data), - GFP_KERNEL); - if (!cascade_data) { - dev_err(&dev->dev, - "No memory for MSI cascade data\n"); - err = -ENOMEM; + + if (!p) + p = all_avail; + + for (irq_index = 0, i = 0; i < len / (2 * sizeof(u32)); i++) { + if (p[i * 2] % IRQS_PER_MSI_REG || + p[i * 2 + 1] % IRQS_PER_MSI_REG) { + printk(KERN_WARNING "%s: %s: msi available range of %u at %u is not IRQ-aligned\n", + __func__, dev->dev.of_node->full_name, + p[i * 2 + 1], p[i * 2]); + err = -EINVAL; + goto error_out; + } + + offset = p[i * 2] / IRQS_PER_MSI_REG; + count = p[i * 2 + 1] / IRQS_PER_MSI_REG; + + for (j = 0; j < count; j++, irq_index++) { + err = fsl_msi_setup_hwirq(msi, dev, offset, irq_index); + if (err) goto error_out; - } - msi->msi_virqs[i] = virt_msir; - cascade_data->index = i + offset; - cascade_data->msi_data = msi; - set_irq_data(virt_msir, (void *)cascade_data); - set_irq_chained_handler(virt_msir, fsl_msi_cascade); } } |