diff options
Diffstat (limited to 'arch/powerpc/sysdev/fsl_msi.c')
| -rw-r--r-- | arch/powerpc/sysdev/fsl_msi.c | 178 |
1 files changed, 119 insertions, 59 deletions
diff --git a/arch/powerpc/sysdev/fsl_msi.c b/arch/powerpc/sysdev/fsl_msi.c index ecb5c1946d2..77efbaec7b9 100644 --- a/arch/powerpc/sysdev/fsl_msi.c +++ b/arch/powerpc/sysdev/fsl_msi.c @@ -28,7 +28,19 @@ #include "fsl_msi.h" #include "fsl_pci.h" -LIST_HEAD(msi_head); +#define MSIIR_OFFSET_MASK 0xfffff +#define MSIIR_IBS_SHIFT 0 +#define MSIIR_SRS_SHIFT 5 +#define MSIIR1_IBS_SHIFT 4 +#define MSIIR1_SRS_SHIFT 0 +#define MSI_SRS_MASK 0xf +#define MSI_IBS_MASK 0x1f + +#define msi_hwirq(msi, msir_index, intr_index) \ + ((msir_index) << (msi)->srs_shift | \ + ((intr_index) << (msi)->ibs_shift)) + +static LIST_HEAD(msi_head); struct fsl_msi_feature { u32 fsl_pic_ip; @@ -60,7 +72,7 @@ static struct irq_chip fsl_msi_chip = { .name = "FSL-MSI", }; -static int fsl_msi_host_map(struct irq_host *h, unsigned int virq, +static int fsl_msi_host_map(struct irq_domain *h, unsigned int virq, irq_hw_number_t hw) { struct fsl_msi *msi_data = h->host_data; @@ -74,24 +86,25 @@ static int fsl_msi_host_map(struct irq_host *h, unsigned int virq, return 0; } -static struct irq_host_ops fsl_msi_host_ops = { +static const struct irq_domain_ops fsl_msi_host_ops = { .map = fsl_msi_host_map, }; static int fsl_msi_init_allocator(struct fsl_msi *msi_data) { - int rc; + int rc, hwirq; - rc = msi_bitmap_alloc(&msi_data->bitmap, NR_MSI_IRQS, + rc = msi_bitmap_alloc(&msi_data->bitmap, NR_MSI_IRQS_MAX, msi_data->irqhost->of_node); if (rc) return rc; - rc = msi_bitmap_reserve_dt_hwirqs(&msi_data->bitmap); - if (rc < 0) { - msi_bitmap_free(&msi_data->bitmap); - return rc; - } + /* + * Reserve all the hwirqs + * The available hwirqs will be released in fsl_msi_setup_hwirq() + */ + for (hwirq = 0; hwirq < NR_MSI_IRQS_MAX; hwirq++) + msi_bitmap_reserve_hwirq(&msi_data->bitmap, hwirq); return 0; } @@ -130,7 +143,7 @@ static void fsl_compose_msi_msg(struct pci_dev *pdev, int hwirq, struct pci_controller *hose = pci_bus_to_host(pdev->bus); u64 address; /* Physical address of the MSIIR */ int len; - const u64 *reg; + const __be64 *reg; /* If the msi-address-64 property exists, then use it */ reg = of_get_property(hose->dn, "msi-address-64", &len); @@ -144,8 +157,9 @@ static void fsl_compose_msi_msg(struct pci_dev *pdev, int hwirq, msg->data = hwirq; - pr_debug("%s: allocated srs: %d, ibs: %d\n", - __func__, hwirq / IRQS_PER_MSI_REG, hwirq % IRQS_PER_MSI_REG); + pr_debug("%s: allocated srs: %d, ibs: %d\n", __func__, + (hwirq >> msi_data->srs_shift) & MSI_SRS_MASK, + (hwirq >> msi_data->ibs_shift) & MSI_IBS_MASK); } static int fsl_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type) @@ -236,7 +250,6 @@ static void fsl_msi_cascade(unsigned int irq, struct irq_desc *desc) u32 intr_index; u32 have_shift = 0; struct fsl_msi_cascade_data *cascade_data; - unsigned int ret; cascade_data = irq_get_handler_data(irq); msi_data = cascade_data->msi_data; @@ -256,7 +269,7 @@ static void fsl_msi_cascade(unsigned int irq, struct irq_desc *desc) msir_index = cascade_data->index; - if (msir_index >= NR_MSI_REG) + if (msir_index >= NR_MSI_REG_MAX) cascade_irq = NO_IRQ; irqd_set_chained_irq_inprogress(idata); @@ -268,7 +281,9 @@ static void fsl_msi_cascade(unsigned int irq, struct irq_desc *desc) case FSL_PIC_IP_IPIC: msir_value = fsl_msi_read(msi_data->msi_regs, msir_index * 0x4); break; - case FSL_PIC_IP_VMPIC: +#ifdef CONFIG_EPAPR_PARAVIRT + case FSL_PIC_IP_VMPIC: { + unsigned int ret; ret = fh_vmpic_get_msir(virq_to_hw(irq), &msir_value); if (ret) { pr_err("fsl-msi: fh_vmpic_get_msir() failed for " @@ -277,13 +292,15 @@ static void fsl_msi_cascade(unsigned int irq, struct irq_desc *desc) } break; } +#endif + } while (msir_value) { intr_index = ffs(msir_value) - 1; cascade_irq = irq_linear_revmap(msi_data->irqhost, - msir_index * IRQS_PER_MSI_REG + - intr_index + have_shift); + msi_hwirq(msi_data, msir_index, + intr_index + have_shift)); if (cascade_irq != NO_IRQ) generic_handle_irq(cascade_irq); have_shift += intr_index + 1; @@ -313,7 +330,7 @@ static int fsl_of_msi_remove(struct platform_device *ofdev) if (msi->list.prev != NULL) list_del(&msi->list); - for (i = 0; i < NR_MSI_REG; i++) { + for (i = 0; i < NR_MSI_REG_MAX; i++) { virq = msi->msi_virqs[i]; if (virq != NO_IRQ) { cascade_data = irq_get_handler_data(virq); @@ -330,12 +347,13 @@ 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) +static struct lock_class_key fsl_msi_irq_class; + +static int 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; + int virt_msir, i; virt_msir = irq_of_parse_and_map(dev->dev.of_node, irq_index); if (virt_msir == NO_IRQ) { @@ -349,29 +367,32 @@ static int __devinit fsl_msi_setup_hwirq(struct fsl_msi *msi, dev_err(&dev->dev, "No memory for MSI cascade data\n"); return -ENOMEM; } - + irq_set_lockdep_class(virt_msir, &fsl_msi_irq_class); msi->msi_virqs[irq_index] = virt_msir; cascade_data->index = offset; cascade_data->msi_data = msi; irq_set_handler_data(virt_msir, cascade_data); irq_set_chained_handler(virt_msir, fsl_msi_cascade); + /* Release the hwirqs corresponding to this MSI register */ + for (i = 0; i < IRQS_PER_MSI_REG; i++) + msi_bitmap_free_hwirqs(&msi->bitmap, + msi_hwirq(msi, offset, i), 1); + return 0; } static const struct of_device_id fsl_of_msi_ids[]; -static int __devinit fsl_of_msi_probe(struct platform_device *dev) +static int fsl_of_msi_probe(struct platform_device *dev) { const struct of_device_id *match; struct fsl_msi *msi; - struct resource res; + struct resource res, msiir; int err, i, j, irq_index, count; - int rc; const u32 *p; - struct fsl_msi_feature *features; + const struct fsl_msi_feature *features; int len; u32 offset; - static const u32 all_avail[] = { 0, NR_MSI_IRQS }; match = of_match_device(fsl_of_msi_ids, &dev->dev); if (!match) @@ -387,8 +408,8 @@ static int __devinit fsl_of_msi_probe(struct platform_device *dev) } platform_set_drvdata(dev, msi); - msi->irqhost = irq_alloc_host(dev->dev.of_node, IRQ_HOST_MAP_LINEAR, - NR_MSI_IRQS, &fsl_msi_host_ops, 0); + msi->irqhost = irq_domain_add_linear(dev->dev.of_node, + NR_MSI_IRQS_MAX, &fsl_msi_host_ops, msi); if (msi->irqhost == NULL) { dev_err(&dev->dev, "No memory for MSI irqhost\n"); @@ -410,60 +431,93 @@ static int __devinit fsl_of_msi_probe(struct platform_device *dev) msi->msi_regs = ioremap(res.start, resource_size(&res)); if (!msi->msi_regs) { + err = -ENOMEM; dev_err(&dev->dev, "could not map node %s\n", dev->dev.of_node->full_name); goto error_out; } msi->msiir_offset = features->msiir_offset + (res.start & 0xfffff); + + /* + * First read the MSIIR/MSIIR1 offset from dts + * On failure use the hardcode MSIIR offset + */ + if (of_address_to_resource(dev->dev.of_node, 1, &msiir)) + msi->msiir_offset = features->msiir_offset + + (res.start & MSIIR_OFFSET_MASK); + else + msi->msiir_offset = msiir.start & MSIIR_OFFSET_MASK; } msi->feature = features->fsl_pic_ip; - msi->irqhost->host_data = msi; - /* * Remember the phandle, so that we can match with any PCI nodes * that have an "fsl,msi" property. */ msi->phandle = dev->dev.of_node->phandle; - rc = fsl_msi_init_allocator(msi); - if (rc) { + err = fsl_msi_init_allocator(msi); + if (err) { dev_err(&dev->dev, "Error allocating MSI bitmap\n"); goto error_out; } 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; - } - if (!p) { - p = all_avail; - len = sizeof(all_avail); - } + if (of_device_is_compatible(dev->dev.of_node, "fsl,mpic-msi-v4.3")) { + msi->srs_shift = MSIIR1_SRS_SHIFT; + msi->ibs_shift = MSIIR1_IBS_SHIFT; + if (p) + dev_warn(&dev->dev, "%s: dose not support msi-available-ranges property\n", + __func__); + + for (irq_index = 0; irq_index < NR_MSI_REG_MSIIR1; + irq_index++) { + err = fsl_msi_setup_hwirq(msi, dev, + irq_index, irq_index); + if (err) + goto error_out; + } + } else { + static const u32 all_avail[] = + { 0, NR_MSI_REG_MSIIR * IRQS_PER_MSI_REG }; - 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]); + msi->srs_shift = MSIIR_SRS_SHIFT; + msi->ibs_shift = MSIIR_IBS_SHIFT; + + 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 = p[i * 2] / IRQS_PER_MSI_REG; - count = p[i * 2 + 1] / IRQS_PER_MSI_REG; + if (!p) { + p = all_avail; + len = sizeof(all_avail); + } - for (j = 0; j < count; j++, irq_index++) { - err = fsl_msi_setup_hwirq(msi, dev, offset + j, irq_index); - if (err) + 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) { + pr_warn("%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 + j, + irq_index); + if (err) + goto error_out; + } } } @@ -503,16 +557,22 @@ static const struct fsl_msi_feature vmpic_msi_feature = { static const struct of_device_id fsl_of_msi_ids[] = { { .compatible = "fsl,mpic-msi", - .data = (void *)&mpic_msi_feature, + .data = &mpic_msi_feature, + }, + { + .compatible = "fsl,mpic-msi-v4.3", + .data = &mpic_msi_feature, }, { .compatible = "fsl,ipic-msi", - .data = (void *)&ipic_msi_feature, + .data = &ipic_msi_feature, }, +#ifdef CONFIG_EPAPR_PARAVIRT { .compatible = "fsl,vmpic-msi", - .data = (void *)&vmpic_msi_feature, + .data = &vmpic_msi_feature, }, +#endif {} }; |
