From 8081881327d4791f26ebf56cf304992673503ad4 Mon Sep 17 00:00:00 2001 From: Lan Chunhe-B25806 Date: Mon, 15 Mar 2010 06:38:33 +0000 Subject: powerpc/fsl_msi: Add multiple MSI bank support Freescale QorIQ P4080 has three MSI banks and the original code can not work well. This patch adds multiple MSI banks support for Freescale processor. Signed-off-by: Lan Chunhe-B25806 Signed-off-by: Roy Zang Signed-off-by: Kumar Gala --- arch/powerpc/sysdev/fsl_msi.c | 40 +++++++++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 15 deletions(-) (limited to 'arch/powerpc/sysdev') diff --git a/arch/powerpc/sysdev/fsl_msi.c b/arch/powerpc/sysdev/fsl_msi.c index 3482e3fd89c0..c84fbb1c1b42 100644 --- a/arch/powerpc/sysdev/fsl_msi.c +++ b/arch/powerpc/sysdev/fsl_msi.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007-2008 Freescale Semiconductor, Inc. All rights reserved. + * Copyright (C) 2007-2010 Freescale Semiconductor, Inc. * * Author: Tony Li * Jason Jin @@ -29,7 +29,6 @@ struct fsl_msi_feature { u32 msiir_offset; }; -static struct fsl_msi *fsl_msi; static inline u32 fsl_msi_read(u32 __iomem *base, unsigned int reg) { @@ -54,10 +53,12 @@ static struct irq_chip fsl_msi_chip = { static int fsl_msi_host_map(struct irq_host *h, unsigned int virq, irq_hw_number_t hw) { + struct fsl_msi *msi_data = h->host_data; struct irq_chip *chip = &fsl_msi_chip; irq_to_desc(virq)->status |= IRQ_TYPE_EDGE_FALLING; + set_irq_chip_data(virq, msi_data); set_irq_chip_and_handler(virq, chip, handle_edge_irq); return 0; @@ -96,11 +97,12 @@ static int fsl_msi_check_device(struct pci_dev *pdev, int nvec, int type) static void fsl_teardown_msi_irqs(struct pci_dev *pdev) { struct msi_desc *entry; - struct fsl_msi *msi_data = fsl_msi; + struct fsl_msi *msi_data; list_for_each_entry(entry, &pdev->msi_list, list) { if (entry->irq == NO_IRQ) continue; + msi_data = get_irq_chip_data(entry->irq); set_irq_msi(entry->irq, NULL); msi_bitmap_free_hwirqs(&msi_data->bitmap, virq_to_hw(entry->irq), 1); @@ -111,9 +113,10 @@ static void fsl_teardown_msi_irqs(struct pci_dev *pdev) } static void fsl_compose_msi_msg(struct pci_dev *pdev, int hwirq, - struct msi_msg *msg) + struct msi_msg *msg, + struct fsl_msi *fsl_msi_data) { - struct fsl_msi *msi_data = fsl_msi; + struct fsl_msi *msi_data = fsl_msi_data; struct pci_controller *hose = pci_bus_to_host(pdev->bus); u32 base = 0; @@ -134,9 +137,11 @@ static int fsl_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type) unsigned int virq; struct msi_desc *entry; struct msi_msg msg; - struct fsl_msi *msi_data = fsl_msi; + struct fsl_msi *msi_data; list_for_each_entry(entry, &pdev->msi_list, list) { + msi_data = get_irq_chip_data(entry->irq); + hwirq = msi_bitmap_alloc_hwirqs(&msi_data->bitmap, 1); if (hwirq < 0) { rc = hwirq; @@ -156,7 +161,7 @@ static int fsl_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type) } set_irq_msi(virq, entry); - fsl_compose_msi_msg(pdev, hwirq, &msg); + fsl_compose_msi_msg(pdev, hwirq, &msg, msi_data); write_msi_msg(virq, &msg); } return 0; @@ -168,7 +173,7 @@ out_free: static void fsl_msi_cascade(unsigned int irq, struct irq_desc *desc) { unsigned int cascade_irq; - struct fsl_msi *msi_data = fsl_msi; + struct fsl_msi *msi_data = get_irq_chip_data(irq); int msir_index = -1; u32 msir_value = 0; u32 intr_index; @@ -193,7 +198,7 @@ static void fsl_msi_cascade(unsigned int irq, struct irq_desc *desc) cascade_irq = NO_IRQ; desc->status |= IRQ_INPROGRESS; - switch (fsl_msi->feature & FSL_PIC_IP_MASK) { + switch (msi_data->feature & FSL_PIC_IP_MASK) { case FSL_PIC_IP_MPIC: msir_value = fsl_msi_read(msi_data->msi_regs, msir_index * 0x10); @@ -307,15 +312,20 @@ static int __devinit fsl_of_msi_probe(struct of_device *dev, if (virt_msir != NO_IRQ) { set_irq_data(virt_msir, (void *)i); set_irq_chained_handler(virt_msir, fsl_msi_cascade); + set_irq_chip_data(virt_msir, msi); } } - fsl_msi = msi; - - WARN_ON(ppc_md.setup_msi_irqs); - ppc_md.setup_msi_irqs = fsl_setup_msi_irqs; - ppc_md.teardown_msi_irqs = fsl_teardown_msi_irqs; - ppc_md.msi_check_device = fsl_msi_check_device; + /* The multiple setting ppc_md.setup_msi_irqs will not harm things */ + if (!ppc_md.setup_msi_irqs) { + ppc_md.setup_msi_irqs = fsl_setup_msi_irqs; + ppc_md.teardown_msi_irqs = fsl_teardown_msi_irqs; + ppc_md.msi_check_device = fsl_msi_check_device; + } else if (ppc_md.setup_msi_irqs != fsl_setup_msi_irqs) { + dev_err(&dev->dev, "Different MSI driver already installed!\n"); + err = -ENODEV; + goto error_out; + } return 0; error_out: kfree(msi); -- cgit v1.2.2 From 02adac6051b0ff8df3877ae3d94e0e68063c6a30 Mon Sep 17 00:00:00 2001 From: Li Yang Date: Thu, 22 Apr 2010 16:31:35 +0800 Subject: powerpc/fsl_msi: fix the conflict of virt_msir's chip_data In fsl_of_msi_probe(), the virt_msir's chip_data have been stored the pointer to struct mpic. We add a struct fsl_msi_cascade_data to store the pointer to struct fsl_msi and msir_index in hanler_data. Otherwise, the pointer to struct mpic will be over-written, and will cause problem when calling eoi() of the irq. Signed-off-by: Zhao Chenhui Signed-off-by: Li Yang Signed-off-by: Kumar Gala --- arch/powerpc/sysdev/fsl_msi.c | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) (limited to 'arch/powerpc/sysdev') diff --git a/arch/powerpc/sysdev/fsl_msi.c b/arch/powerpc/sysdev/fsl_msi.c index c84fbb1c1b42..b769982612a0 100644 --- a/arch/powerpc/sysdev/fsl_msi.c +++ b/arch/powerpc/sysdev/fsl_msi.c @@ -22,6 +22,7 @@ #include #include #include +#include #include "fsl_msi.h" struct fsl_msi_feature { @@ -29,6 +30,10 @@ struct fsl_msi_feature { u32 msiir_offset; }; +struct fsl_msi_cascade_data { + struct fsl_msi *msi_data; + int index; +}; static inline u32 fsl_msi_read(u32 __iomem *base, unsigned int reg) { @@ -102,7 +107,7 @@ static void fsl_teardown_msi_irqs(struct pci_dev *pdev) list_for_each_entry(entry, &pdev->msi_list, list) { if (entry->irq == NO_IRQ) continue; - msi_data = get_irq_chip_data(entry->irq); + msi_data = get_irq_data(entry->irq); set_irq_msi(entry->irq, NULL); msi_bitmap_free_hwirqs(&msi_data->bitmap, virq_to_hw(entry->irq), 1); @@ -133,7 +138,7 @@ static void fsl_compose_msi_msg(struct pci_dev *pdev, int hwirq, static int fsl_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type) { - int rc, hwirq; + int rc, hwirq = NO_IRQ; unsigned int virq; struct msi_desc *entry; struct msi_msg msg; @@ -159,6 +164,7 @@ static int fsl_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type) rc = -ENOSPC; goto out_free; } + set_irq_data(virq, msi_data); set_irq_msi(virq, entry); fsl_compose_msi_msg(pdev, hwirq, &msg, msi_data); @@ -173,11 +179,15 @@ out_free: static void fsl_msi_cascade(unsigned int irq, struct irq_desc *desc) { unsigned int cascade_irq; - struct fsl_msi *msi_data = get_irq_chip_data(irq); + struct fsl_msi *msi_data; int msir_index = -1; u32 msir_value = 0; u32 intr_index; u32 have_shift = 0; + struct fsl_msi_cascade_data *cascade_data; + + cascade_data = (struct fsl_msi_cascade_data *)get_irq_data(irq); + msi_data = cascade_data->msi_data; raw_spin_lock(&desc->lock); if ((msi_data->feature & FSL_PIC_IP_MASK) == FSL_PIC_IP_IPIC) { @@ -192,7 +202,7 @@ static void fsl_msi_cascade(unsigned int irq, struct irq_desc *desc) if (unlikely(desc->status & IRQ_INPROGRESS)) goto unlock; - msir_index = (int)desc->handler_data; + msir_index = cascade_data->index; if (msir_index >= NR_MSI_REG) cascade_irq = NO_IRQ; @@ -244,6 +254,7 @@ static int __devinit fsl_of_msi_probe(struct of_device *dev, int virt_msir; const u32 *p; struct fsl_msi_feature *features = match->data; + struct fsl_msi_cascade_data *cascade_data = NULL; printk(KERN_DEBUG "Setting up Freescale MSI support\n"); @@ -310,9 +321,19 @@ static int __devinit fsl_of_msi_probe(struct of_device *dev, break; virt_msir = irq_of_parse_and_map(dev->node, i); if (virt_msir != NO_IRQ) { - set_irq_data(virt_msir, (void *)i); + 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; + goto error_out; + } + cascade_data->index = i; + cascade_data->msi_data = msi; + set_irq_data(virt_msir, (void *)cascade_data); set_irq_chained_handler(virt_msir, fsl_msi_cascade); - set_irq_chip_data(virt_msir, msi); } } -- cgit v1.2.2 From 694a7a3611a1c0e28d99b4955151c6ce68e89752 Mon Sep 17 00:00:00 2001 From: Li Yang Date: Thu, 22 Apr 2010 16:31:36 +0800 Subject: powerpc/fsl_msi: enable msi allocation in all banks Put all fsl_msi banks in a linked list. The list of banks then can be traversed when allocating new msi interrupts. Also fix failing path of fsl_setup_msi_irqs(). Signed-off-by: Zhao Chenhui Signed-off-by: Li Yang Signed-off-by: Kumar Gala --- arch/powerpc/sysdev/fsl_msi.c | 14 +++++++++++--- arch/powerpc/sysdev/fsl_msi.h | 2 ++ 2 files changed, 13 insertions(+), 3 deletions(-) (limited to 'arch/powerpc/sysdev') diff --git a/arch/powerpc/sysdev/fsl_msi.c b/arch/powerpc/sysdev/fsl_msi.c index b769982612a0..efffcbd190a1 100644 --- a/arch/powerpc/sysdev/fsl_msi.c +++ b/arch/powerpc/sysdev/fsl_msi.c @@ -25,6 +25,8 @@ #include #include "fsl_msi.h" +LIST_HEAD(msi_head); + struct fsl_msi_feature { u32 fsl_pic_ip; u32 msiir_offset; @@ -138,16 +140,19 @@ static void fsl_compose_msi_msg(struct pci_dev *pdev, int hwirq, static int fsl_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type) { - int rc, hwirq = NO_IRQ; + int rc, hwirq = -ENOMEM; unsigned int virq; struct msi_desc *entry; struct msi_msg msg; struct fsl_msi *msi_data; list_for_each_entry(entry, &pdev->msi_list, list) { - msi_data = get_irq_chip_data(entry->irq); + list_for_each_entry(msi_data, &msi_head, list) { + hwirq = msi_bitmap_alloc_hwirqs(&msi_data->bitmap, 1); + if (hwirq >= 0) + break; + } - hwirq = msi_bitmap_alloc_hwirqs(&msi_data->bitmap, 1); if (hwirq < 0) { rc = hwirq; pr_debug("%s: fail allocating msi interrupt\n", @@ -173,6 +178,7 @@ static int fsl_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type) return 0; out_free: + /* free by the caller of this function */ return rc; } @@ -337,6 +343,8 @@ static int __devinit fsl_of_msi_probe(struct of_device *dev, } } + list_add_tail(&msi->list, &msi_head); + /* The multiple setting ppc_md.setup_msi_irqs will not harm things */ if (!ppc_md.setup_msi_irqs) { ppc_md.setup_msi_irqs = fsl_setup_msi_irqs; diff --git a/arch/powerpc/sysdev/fsl_msi.h b/arch/powerpc/sysdev/fsl_msi.h index 331c7e7025b7..8fc5523d6ee3 100644 --- a/arch/powerpc/sysdev/fsl_msi.h +++ b/arch/powerpc/sysdev/fsl_msi.h @@ -34,6 +34,8 @@ struct fsl_msi { u32 feature; struct msi_bitmap bitmap; + + struct list_head list; /* support multiple MSI banks */ }; #endif /* _POWERPC_SYSDEV_FSL_MSI_H */ -- cgit v1.2.2 From 061ca4adfb2e3c986a182fd30f7e939a1ff8d29d Mon Sep 17 00:00:00 2001 From: Li Yang Date: Thu, 22 Apr 2010 16:31:37 +0800 Subject: powerpc/fsl_msi: enable msi sharing through AMP OSes Make a single PCIe MSI bank shareable through CAMP OSes. The number of MSI used by each core can be configured by dts file. Signed-off-by: Zhao Chenhui Signed-off-by: Li Yang Signed-off-by: Kumar Gala --- arch/powerpc/sysdev/fsl_msi.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'arch/powerpc/sysdev') diff --git a/arch/powerpc/sysdev/fsl_msi.c b/arch/powerpc/sysdev/fsl_msi.c index efffcbd190a1..1ab703915972 100644 --- a/arch/powerpc/sysdev/fsl_msi.c +++ b/arch/powerpc/sysdev/fsl_msi.c @@ -261,6 +261,8 @@ static int __devinit fsl_of_msi_probe(struct of_device *dev, const u32 *p; struct fsl_msi_feature *features = match->data; struct fsl_msi_cascade_data *cascade_data = NULL; + int len; + u32 offset; printk(KERN_DEBUG "Setting up Freescale MSI support\n"); @@ -320,6 +322,10 @@ static int __devinit fsl_of_msi_probe(struct of_device *dev, err = -EINVAL; goto error_out; } + offset = 0; + p = of_get_property(dev->node, "msi-available-ranges", &len); + if (p) + offset = *p / IRQS_PER_MSI_REG; count /= sizeof(u32); for (i = 0; i < count / 2; i++) { @@ -336,7 +342,7 @@ static int __devinit fsl_of_msi_probe(struct of_device *dev, err = -ENOMEM; goto error_out; } - cascade_data->index = i; + 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); -- cgit v1.2.2 From 480599933a69a8a8dc1390c2164e49e20a1593b9 Mon Sep 17 00:00:00 2001 From: Li Yang Date: Thu, 22 Apr 2010 16:31:39 +0800 Subject: powerpc/fsl_msi: add removal path and probe failing path Also cleanup the probe function. Signed-off-by: Li Yang Signed-off-by: Kumar Gala --- arch/powerpc/sysdev/fsl_msi.c | 36 ++++++++++++++++++++++++++++++------ arch/powerpc/sysdev/fsl_msi.h | 1 + 2 files changed, 31 insertions(+), 6 deletions(-) (limited to 'arch/powerpc/sysdev') diff --git a/arch/powerpc/sysdev/fsl_msi.c b/arch/powerpc/sysdev/fsl_msi.c index 1ab703915972..f7ca9e6fa5f0 100644 --- a/arch/powerpc/sysdev/fsl_msi.c +++ b/arch/powerpc/sysdev/fsl_msi.c @@ -250,6 +250,30 @@ unlock: raw_spin_unlock(&desc->lock); } +static int fsl_of_msi_remove(struct of_device *ofdev) +{ + struct fsl_msi *msi = ofdev->dev.platform_data; + int virq, i; + struct fsl_msi_cascade_data *cascade_data; + + if (msi->list.prev != NULL) + list_del(&msi->list); + for (i = 0; i < NR_MSI_REG; i++) { + virq = msi->msi_virqs[i]; + if (virq != NO_IRQ) { + cascade_data = get_irq_data(virq); + kfree(cascade_data); + irq_dispose_mapping(virq); + } + } + if (msi->bitmap.bitmap) + msi_bitmap_free(&msi->bitmap); + iounmap(msi->msi_regs); + kfree(msi); + + return 0; +} + static int __devinit fsl_of_msi_probe(struct of_device *dev, const struct of_device_id *match) { @@ -269,9 +293,9 @@ static int __devinit fsl_of_msi_probe(struct of_device *dev, msi = kzalloc(sizeof(struct fsl_msi), GFP_KERNEL); if (!msi) { dev_err(&dev->dev, "No memory for MSI structure\n"); - err = -ENOMEM; - goto error_out; + return -ENOMEM; } + dev->dev.platform_data = msi; msi->irqhost = irq_alloc_host(dev->node, IRQ_HOST_MAP_LINEAR, NR_MSI_IRQS, &fsl_msi_host_ops, 0); @@ -328,9 +352,7 @@ static int __devinit fsl_of_msi_probe(struct of_device *dev, offset = *p / IRQS_PER_MSI_REG; count /= sizeof(u32); - for (i = 0; i < count / 2; i++) { - if (i > NR_MSI_REG) - break; + for (i = 0; i < min(count / 2, NR_MSI_REG); i++) { virt_msir = irq_of_parse_and_map(dev->node, i); if (virt_msir != NO_IRQ) { cascade_data = kzalloc( @@ -342,6 +364,7 @@ static int __devinit fsl_of_msi_probe(struct of_device *dev, err = -ENOMEM; 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); @@ -363,7 +386,7 @@ static int __devinit fsl_of_msi_probe(struct of_device *dev, } return 0; error_out: - kfree(msi); + fsl_of_msi_remove(dev); return err; } @@ -393,6 +416,7 @@ static struct of_platform_driver fsl_of_msi_driver = { .name = "fsl-msi", .match_table = fsl_of_msi_ids, .probe = fsl_of_msi_probe, + .remove = fsl_of_msi_remove, }; static __init int fsl_of_msi_init(void) diff --git a/arch/powerpc/sysdev/fsl_msi.h b/arch/powerpc/sysdev/fsl_msi.h index 8fc5523d6ee3..624580c252d7 100644 --- a/arch/powerpc/sysdev/fsl_msi.h +++ b/arch/powerpc/sysdev/fsl_msi.h @@ -32,6 +32,7 @@ struct fsl_msi { u32 msi_addr_hi; void __iomem *msi_regs; u32 feature; + int msi_virqs[NR_MSI_REG]; struct msi_bitmap bitmap; -- cgit v1.2.2