From 895d603f945baf7d1680b7657561212890e3a803 Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Mon, 31 Oct 2011 17:06:35 -0500 Subject: powerpc/fsl_msi: add support for the fsl, msi property in PCI nodes On Freescale parts with multiple MSI controllers, the controllers are combined into one "pool" of interrupts. Whenever a device requests an MSI interrupt, the next available interrupt from the pool is selected, regardless of which MSI controller the interrupt is from. This works because each PCI bus has an ATMU to all of CCSR, so any PCI device can access any MSI interrupt register. The fsl,msi property is used to specify that a given PCI bus should only use a specific MSI device. This is necessary, for example, with the Freescale hypervisor, because the MSI devices are assigned to specific partitions. Ideally, we'd like to be able to assign MSI devices to PCI busses within the MSI or PCI layers. However, there does not appear to be a mechanism to do that. Whenever the MSI layer wants to allocate an MSI interrupt to a PCI device, it just calls arch_setup_msi_irqs(). It would be nice if we could register an MSI device with a specific PCI bus. So instead we remember the phandles of each MSI device, and we use that to limit our search for an available interrupt. Whenever we are asked to allocate a new interrupt for a PCI device, we check the fsl,msi property of the PCI bus for that device. If it exists, then as we are looping over all MSI devices, we skip the ones that don't have a matching phandle. Signed-off-by: Timur Tabi Signed-off-by: Kumar Gala --- arch/powerpc/sysdev/fsl_msi.c | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) (limited to 'arch/powerpc/sysdev/fsl_msi.c') diff --git a/arch/powerpc/sysdev/fsl_msi.c b/arch/powerpc/sysdev/fsl_msi.c index e5c344d336ea..89548e07ddeb 100644 --- a/arch/powerpc/sysdev/fsl_msi.c +++ b/arch/powerpc/sysdev/fsl_msi.c @@ -148,14 +148,47 @@ 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) { + struct pci_controller *hose = pci_bus_to_host(pdev->bus); + struct device_node *np; + phandle phandle = 0; int rc, hwirq = -ENOMEM; unsigned int virq; struct msi_desc *entry; struct msi_msg msg; struct fsl_msi *msi_data; + /* + * If the PCI node has an fsl,msi property, then we need to use it + * to find the specific MSI. + */ + np = of_parse_phandle(hose->dn, "fsl,msi", 0); + if (np) { + if (of_device_is_compatible(np, "fsl,mpic-msi")) + phandle = np->phandle; + else { + dev_err(&pdev->dev, "node %s has an invalid fsl,msi" + " phandle\n", hose->dn->full_name); + return -EINVAL; + } + } + list_for_each_entry(entry, &pdev->msi_list, list) { + /* + * Loop over all the MSI devices until we find one that has an + * available interrupt. + */ list_for_each_entry(msi_data, &msi_head, list) { + /* + * If the PCI node has an fsl,msi property, then we + * restrict our search to the corresponding MSI node. + * The simplest way is to skip over MSI nodes with the + * wrong phandle. Under the Freescale hypervisor, this + * has the additional benefit of skipping over MSI + * nodes that are not mapped in the PAMU. + */ + if (phandle && (phandle != msi_data->phandle)) + continue; + hwirq = msi_bitmap_alloc_hwirqs(&msi_data->bitmap, 1); if (hwirq >= 0) break; @@ -370,6 +403,12 @@ static int __devinit fsl_of_msi_probe(struct platform_device *dev) msi->msiir_offset = features->msiir_offset + (res.start & 0xfffff); + /* + * 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) { dev_err(&dev->dev, "Error allocating MSI bitmap\n"); -- cgit From 446bc1ffe4f2cac228909fe0ac48884d12700d81 Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Tue, 13 Dec 2011 14:51:59 -0600 Subject: powerpc/fsl: add MSI support for the Freescale hypervisor Add support for vmpic-msi nodes to the fsl_msi driver. The MSI is virtualized by the hypervisor, so the vmpic-msi does not contain a 'reg' property. Instead, the driver uses hcalls. Add support for the "msi-address-64" property to the fsl_pci driver. The Freescale hypervisor typically puts the virtualized MSIIR register in the page after the end of DDR, so we extend the DDR ATMU to cover it. Any other location for MSIIR is not supported, for now. Signed-off-by: Timur Tabi Signed-off-by: Kumar Gala --- arch/powerpc/sysdev/fsl_msi.c | 68 ++++++++++++++++++++++++++++++------------- arch/powerpc/sysdev/fsl_msi.h | 7 +++-- arch/powerpc/sysdev/fsl_pci.c | 29 ++++++++++++++++++ 3 files changed, 81 insertions(+), 23 deletions(-) (limited to 'arch/powerpc/sysdev/fsl_msi.c') diff --git a/arch/powerpc/sysdev/fsl_msi.c b/arch/powerpc/sysdev/fsl_msi.c index 89548e07ddeb..ecb5c1946d22 100644 --- a/arch/powerpc/sysdev/fsl_msi.c +++ b/arch/powerpc/sysdev/fsl_msi.c @@ -23,6 +23,8 @@ #include #include #include +#include + #include "fsl_msi.h" #include "fsl_pci.h" @@ -163,11 +165,13 @@ static int fsl_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type) */ np = of_parse_phandle(hose->dn, "fsl,msi", 0); if (np) { - if (of_device_is_compatible(np, "fsl,mpic-msi")) + if (of_device_is_compatible(np, "fsl,mpic-msi") || + of_device_is_compatible(np, "fsl,vmpic-msi")) phandle = np->phandle; else { - dev_err(&pdev->dev, "node %s has an invalid fsl,msi" - " phandle\n", hose->dn->full_name); + dev_err(&pdev->dev, + "node %s has an invalid fsl,msi phandle %u\n", + hose->dn->full_name, np->phandle); return -EINVAL; } } @@ -196,16 +200,14 @@ static int fsl_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type) if (hwirq < 0) { rc = hwirq; - pr_debug("%s: fail allocating msi interrupt\n", - __func__); + dev_err(&pdev->dev, "could not allocate MSI interrupt\n"); goto out_free; } virq = irq_create_mapping(msi_data->irqhost, hwirq); if (virq == NO_IRQ) { - pr_debug("%s: fail mapping hwirq 0x%x\n", - __func__, hwirq); + dev_err(&pdev->dev, "fail mapping hwirq %i\n", hwirq); msi_bitmap_free_hwirqs(&msi_data->bitmap, hwirq, 1); rc = -ENOSPC; goto out_free; @@ -234,6 +236,7 @@ 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; @@ -265,6 +268,14 @@ 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: + ret = fh_vmpic_get_msir(virq_to_hw(irq), &msir_value); + if (ret) { + pr_err("fsl-msi: fh_vmpic_get_msir() failed for " + "irq %u (ret=%u)\n", irq, ret); + msir_value = 0; + } + break; } while (msir_value) { @@ -282,6 +293,7 @@ static void fsl_msi_cascade(unsigned int irq, struct irq_desc *desc) switch (msi_data->feature & FSL_PIC_IP_MASK) { case FSL_PIC_IP_MPIC: + case FSL_PIC_IP_VMPIC: chip->irq_eoi(idata); break; case FSL_PIC_IP_IPIC: @@ -311,7 +323,8 @@ static int fsl_of_msi_remove(struct platform_device *ofdev) } if (msi->bitmap.bitmap) msi_bitmap_free(&msi->bitmap); - iounmap(msi->msi_regs); + if ((msi->feature & FSL_PIC_IP_MASK) != FSL_PIC_IP_VMPIC) + iounmap(msi->msi_regs); kfree(msi); return 0; @@ -383,26 +396,32 @@ static int __devinit fsl_of_msi_probe(struct platform_device *dev) goto error_out; } - /* Get the MSI reg base */ - err = of_address_to_resource(dev->dev.of_node, 0, &res); - if (err) { - dev_err(&dev->dev, "%s resource error!\n", + /* + * Under the Freescale hypervisor, the msi nodes don't have a 'reg' + * property. Instead, we use hypercalls to access the MSI. + */ + if ((features->fsl_pic_ip & FSL_PIC_IP_MASK) != FSL_PIC_IP_VMPIC) { + err = of_address_to_resource(dev->dev.of_node, 0, &res); + if (err) { + dev_err(&dev->dev, "invalid resource for node %s\n", dev->dev.of_node->full_name); - goto error_out; - } + goto error_out; + } - msi->msi_regs = ioremap(res.start, resource_size(&res)); - if (!msi->msi_regs) { - dev_err(&dev->dev, "ioremap problem failed\n"); - goto error_out; + msi->msi_regs = ioremap(res.start, resource_size(&res)); + if (!msi->msi_regs) { + 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); } msi->feature = features->fsl_pic_ip; msi->irqhost->host_data = msi; - msi->msiir_offset = features->msiir_offset + (res.start & 0xfffff); - /* * Remember the phandle, so that we can match with any PCI nodes * that have an "fsl,msi" property. @@ -476,6 +495,11 @@ static const struct fsl_msi_feature ipic_msi_feature = { .msiir_offset = 0x38, }; +static const struct fsl_msi_feature vmpic_msi_feature = { + .fsl_pic_ip = FSL_PIC_IP_VMPIC, + .msiir_offset = 0, +}; + static const struct of_device_id fsl_of_msi_ids[] = { { .compatible = "fsl,mpic-msi", @@ -485,6 +509,10 @@ static const struct of_device_id fsl_of_msi_ids[] = { .compatible = "fsl,ipic-msi", .data = (void *)&ipic_msi_feature, }, + { + .compatible = "fsl,vmpic-msi", + .data = (void *)&vmpic_msi_feature, + }, {} }; diff --git a/arch/powerpc/sysdev/fsl_msi.h b/arch/powerpc/sysdev/fsl_msi.h index b5d25ba51311..f6c646a52541 100644 --- a/arch/powerpc/sysdev/fsl_msi.h +++ b/arch/powerpc/sysdev/fsl_msi.h @@ -20,9 +20,10 @@ #define IRQS_PER_MSI_REG 32 #define NR_MSI_IRQS (NR_MSI_REG * IRQS_PER_MSI_REG) -#define FSL_PIC_IP_MASK 0x0000000F -#define FSL_PIC_IP_MPIC 0x00000001 -#define FSL_PIC_IP_IPIC 0x00000002 +#define FSL_PIC_IP_MASK 0x0000000F +#define FSL_PIC_IP_MPIC 0x00000001 +#define FSL_PIC_IP_IPIC 0x00000002 +#define FSL_PIC_IP_VMPIC 0x00000003 struct fsl_msi { struct irq_host *irqhost; diff --git a/arch/powerpc/sysdev/fsl_pci.c b/arch/powerpc/sysdev/fsl_pci.c index 8f92446ff0ae..3b61e8cf3421 100644 --- a/arch/powerpc/sysdev/fsl_pci.c +++ b/arch/powerpc/sysdev/fsl_pci.c @@ -137,6 +137,8 @@ static void __init setup_pci_atmu(struct pci_controller *hose, u32 piwar = PIWAR_EN | PIWAR_PF | PIWAR_TGI_LOCAL | PIWAR_READ_SNOOP | PIWAR_WRITE_SNOOP; char *name = hose->dn->full_name; + const u64 *reg; + int len; pr_debug("PCI memory map start 0x%016llx, size 0x%016llx\n", (u64)rsrc->start, (u64)resource_size(rsrc)); @@ -229,6 +231,33 @@ static void __init setup_pci_atmu(struct pci_controller *hose, /* Setup inbound mem window */ mem = memblock_end_of_DRAM(); + + /* + * The msi-address-64 property, if it exists, indicates the physical + * address of the MSIIR register. Normally, this register is located + * inside CCSR, so the ATMU that covers all of CCSR is used. But if + * this property exists, then we normally need to create a new ATMU + * for it. For now, however, we cheat. The only entity that creates + * this property is the Freescale hypervisor, and the address is + * specified in the partition configuration. Typically, the address + * is located in the page immediately after the end of DDR. If so, we + * can avoid allocating a new ATMU by extending the DDR ATMU by one + * page. + */ + reg = of_get_property(hose->dn, "msi-address-64", &len); + if (reg && (len == sizeof(u64))) { + u64 address = be64_to_cpup(reg); + + if ((address >= mem) && (address < (mem + PAGE_SIZE))) { + pr_info("%s: extending DDR ATMU to cover MSIIR", name); + mem += PAGE_SIZE; + } else { + /* TODO: Create a new ATMU for MSIIR */ + pr_warn("%s: msi-address-64 address of %llx is " + "unsupported\n", name, address); + } + } + sz = min(mem, paddr_lo); mem_log = __ilog2_u64(sz); -- cgit