Message ID | 1261471791-476-1-git-send-email-tmarri@amcc.com (mailing list archive) |
---|---|
State | Changes Requested |
Headers | show |
On Tue, Dec 22, 2009 at 12:49:51AM -0800, tmarri@amcc.com wrote: >From: Tirumala Marri <tmarri@amcc.com> > > >Signed-off-by: Tirumala Marri <tmarri@amcc.com> >--- >Kernel version: 2.6.33-rc1 >Testing: > When 460SX configured as root as a end point E1000(Intell Ethernet card) > was plugged into the one of the PCI-E ports. I was able to run the traffic > with MSI interrupts. >--- > arch/powerpc/boot/dts/redwood.dts | 15 ++ > arch/powerpc/configs/44x/redwood_defconfig | 5 +- > arch/powerpc/platforms/44x/Kconfig | 1 + > arch/powerpc/sysdev/Kconfig | 7 + > arch/powerpc/sysdev/Makefile | 1 + > arch/powerpc/sysdev/ppc4xx_msi.c | 335 ++++++++++++++++++++++++++++ > arch/powerpc/sysdev/ppc4xx_msi.h | 49 ++++ > 7 files changed, 411 insertions(+), 2 deletions(-) > create mode 100644 arch/powerpc/sysdev/ppc4xx_msi.c > create mode 100644 arch/powerpc/sysdev/ppc4xx_msi.h > >diff --git a/arch/powerpc/boot/dts/redwood.dts b/arch/powerpc/boot/dts/redwood.dts >index 81636c0..412d5f9 100644 >--- a/arch/powerpc/boot/dts/redwood.dts >+++ b/arch/powerpc/boot/dts/redwood.dts >@@ -357,6 +357,21 @@ > 0x0 0x0 0x0 0x3 &UIC3 0xa 0x4 /* swizzled int C */ > 0x0 0x0 0x0 0x4 &UIC3 0xb 0x4 /* swizzled int D */>; > }; >+ MSI: ppc4xx-msi@400300000 { >+ compatible = "amcc,ppc4xx-msi", "ppc4xx-msi"; >+ reg = < 0x4 0x00300000 0x100 >+ 0x4 0x00300000 0x100>; >+ sdr-base = <0x3B0>; >+ interrupts =<0 1 2 3>; >+ interrupt-parent = <&MSI>; >+ #interrupt-cells = <1>; >+ #address-cells = <0>; >+ #size-cells = <0>; >+ interrupt-map = <0 &UIC0 0xC 1 >+ 1 &UIC0 0x0D 1 >+ 2 &UIC0 0x0E 1 >+ 3 &UIC0 0x0F 1>; >+ }; > > }; > >diff --git a/arch/powerpc/sysdev/Kconfig b/arch/powerpc/sysdev/Kconfig >index 3965828..32f5a40 100644 >--- a/arch/powerpc/sysdev/Kconfig >+++ b/arch/powerpc/sysdev/Kconfig >@@ -7,8 +7,15 @@ config PPC4xx_PCI_EXPRESS > depends on PCI && 4xx > default n > >+config 4xx_MSI This should probably be named PPC4xx_MSI, similar to how PPC4xx_PCI_EXPRESS is named. >+ bool >+ depends on PCI_MSI >+ depends on PCI && 4xx >+ default n >+ > config PPC_MSI_BITMAP > bool > depends on PCI_MSI > default y if MPIC > default y if FSL_PCI >+ default y if 4xx_MSI >diff --git a/arch/powerpc/sysdev/ppc4xx_msi.c b/arch/powerpc/sysdev/ppc4xx_msi.c >new file mode 100644 >index 0000000..752da4b >--- /dev/null >+++ b/arch/powerpc/sysdev/ppc4xx_msi.c >@@ -0,0 +1,335 @@ >+/* >+ * Copyright (C) 2009 Applied Micro Circuits corporation, >+ * All rights reserved. Please don't add the 'All rights reserved.' to new files. It is inaccurate and confusing given that it's a GPLv2 file. >+ * >+ * Author: Feng Kan <fkan@amcc.com> >+ * Tirumala Marri <tmarri@amcc.com> >+ * This program is free software; you can redistribute it and/or >+ * modify it under the terms of the GNU General Public License >+ * as published by the Free Software Foundation; version 2 of the >+ * License. >+ */ >+#include <linux/irq.h> >+#include <linux/bootmem.h> >+#include <linux/pci.h> >+#include <linux/msi.h> >+#include <linux/of_platform.h> >+#include <linux/interrupt.h> >+#include <linux/device.h> >+#include <asm/prom.h> >+#include <asm/hw_irq.h> >+#include <asm/ppc-pci.h> >+#include <asm/dcr.h> >+#include <asm/dcr-regs.h> >+#include "ppc4xx_msi.h" >+ >+ >+static struct ppc4xx_msi *ppc4xx_msi; >+ >+struct ppc4xx_msi_feature { >+ u32 ppc4xx_pic_ip; >+ u32 msiir_offset; >+}; >+ >+static int ppc4xx_msi_init_allocator(struct ppc4xx_msi *msi_data) >+{ >+ int rc; >+ >+ rc = msi_bitmap_alloc(&msi_data->bitmap, NR_MSI_IRQS, >+ 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; >+ } >+ return 0; >+} >+ >+ >+static void ppc4xx_msi_cascade(unsigned int irq, struct irq_desc *desc) >+{ >+ unsigned int cascade_irq; >+ struct ppc4xx_msi *msi_data = ppc4xx_msi; >+ int msir_index = -1; >+ >+ raw_spin_lock(&desc->lock); >+ if (desc->chip->mask_ack) { >+ desc->chip->mask_ack(irq); >+ } else { >+ desc->chip->mask(irq); >+ desc->chip->ack(irq); >+ } >+ >+ if (unlikely(desc->status & IRQ_INPROGRESS)) >+ goto unlock; >+ >+ msir_index = (int)desc->handler_data; >+ >+ if (msir_index >= NR_MSI_IRQS) >+ cascade_irq = NO_IRQ; >+ >+ desc->status |= IRQ_INPROGRESS; >+ >+ cascade_irq = irq_linear_revmap(msi_data->irqhost, msir_index); >+ if (cascade_irq != NO_IRQ) >+ generic_handle_irq(cascade_irq); >+ desc->status &= ~IRQ_INPROGRESS; >+ >+ if (!(desc->status & IRQ_DISABLED) && desc->chip->unmask) >+ desc->chip->unmask(irq); >+unlock: >+ raw_spin_unlock(&desc->lock); >+} >+static void ppc4xx_compose_msi_msg(struct pci_dev *pdev, int hwirq, >+ struct msi_msg *msg) >+{ >+ struct ppc4xx_msi *msi_data = ppc4xx_msi; >+ >+ msg->address_lo = msi_data->msi_addr_lo; >+ msg->address_hi = msi_data->msi_addr_hi; >+ msg->data = hwirq; >+} >+ >+ >+int ppc4xx_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) >+{ >+ struct msi_desc *entry; >+ int rc, hwirq; >+ unsigned int virq; >+ struct msi_msg msg; >+ struct ppc4xx_msi *msi_data = ppc4xx_msi; >+ >+ >+ list_for_each_entry(entry, &dev->msi_list, list) { >+ hwirq = msi_bitmap_alloc_hwirqs(&msi_data->bitmap, 1); >+ if (hwirq < 0) { >+ rc = hwirq; >+ dev_err(&dev->dev, "%s: fail allocating msi\ >+ interrupt\n", __func__); >+ goto out_free; >+ } >+ >+ pr_debug(KERN_INFO"mis is %p\n", msi_data->irqhost); >+ virq = irq_create_mapping(msi_data->irqhost, hwirq); >+ if (virq == NO_IRQ) { >+ dev_err(&dev->dev, "%s: fail mapping irq\n", __func__); >+ rc = -ENOSPC; >+ goto out_free; >+ } >+ >+ set_irq_msi(virq, entry); >+ ppc4xx_compose_msi_msg(dev, hwirq, &msg); >+ write_msi_msg(virq, &msg); >+ } >+ >+ return 0; >+out_free: >+ return rc; >+} >+ >+void ppc4xx_teardown_msi_irqs(struct pci_dev *dev) >+{ >+ struct msi_desc *entry; >+ struct ppc4xx_msi *msi_data = ppc4xx_msi; >+ dev_dbg(&dev->dev, "PCIE-MSI: tearing down msi irqs\n"); >+ >+ list_for_each_entry(entry, &dev->msi_list, list) { >+ if (entry->irq == NO_IRQ) >+ continue; >+ set_irq_msi(entry->irq, NULL); >+ msi_bitmap_free_hwirqs(&msi_data->bitmap, >+ virq_to_hw(entry->irq), 1); >+ irq_dispose_mapping(entry->irq); >+ >+ } >+ >+ return; >+} >+ >+static int ppc4xx_msi_check_device(struct pci_dev *pdev, int nvec, int type) >+{ >+ pr_debug(KERN_INFO"PCIE-MSI:%s called. vec %x type %d\n", >+ __func__, nvec, type); >+ return 0; >+} >+ >+/* >+ * We do not need this actually. The MSIR register has been read once >+ * in the cascade interrupt. So, this MSI interrupt has been acked >+*/ >+static void ppc4xx_msi_end_irq(unsigned int virq) >+{ >+} >+ >+ >+static struct irq_chip ppc4xx_msi_chip = { >+ .mask = mask_msi_irq, >+ .unmask = unmask_msi_irq, >+ .ack = ppc4xx_msi_end_irq, >+ .name = " UIC", >+}; >+ >+static int ppc4xx_msi_host_map(struct irq_host *h, unsigned int virq, >+ irq_hw_number_t hw) >+{ >+ struct irq_chip *chip = &ppc4xx_msi_chip; >+ >+ irq_to_desc(virq)->status |= IRQ_TYPE_EDGE_RISING; >+ >+ set_irq_chip_and_handler(virq, chip, handle_edge_irq); >+ >+ return 0; >+} >+ >+static struct irq_host_ops ppc4xx_msi_host_ops = { >+ .map = ppc4xx_msi_host_map, >+}; >+ >+ >+static int __devinit ppc4xx_msi_probe(struct of_device *dev, >+ const struct of_device_id *match) >+{ >+ struct ppc4xx_msi *msi; >+ struct resource res, rmsi; >+ int i, count; >+ int rc; >+ int virt_msir; >+ const u32 *p; >+ u32 *msi_virt = NULL; >+ dma_addr_t msi_phys; >+ >+ >+ msi = kzalloc(sizeof(struct ppc4xx_msi), GFP_KERNEL); >+ if (!msi) { >+ dev_err(&dev->dev, "No memory for MSI structure\n"); >+ rc = -ENOMEM; >+ goto error_out; >+ } >+ >+ msi->irqhost = irq_alloc_host(dev->node, IRQ_HOST_MAP_LINEAR, >+ NR_MSI_IRQS, &ppc4xx_msi_host_ops, 0); >+ if (msi->irqhost == NULL) { >+ dev_err(&dev->dev, "No memory for MSI irqhost\n"); >+ rc = -ENOMEM; >+ goto error_out; >+ } >+ >+ >+ /* Get MSI ranges */ >+ rc = of_address_to_resource(dev->node, 0, &rmsi); >+ if (rc) { >+ dev_err(&dev->dev, "%s resource error!\n", >+ dev->node->full_name); >+ goto error_out; >+ } >+ >+ >+ /* Get the MSI reg base */ >+ rc = of_address_to_resource(dev->node, 1, &res); >+ if (rc) { >+ dev_err(&dev->dev, "%s resource error!\n", >+ dev->node->full_name); >+ goto error_out; >+ } >+#if defined(CONFIG_460SX) >+ mtdcri(SDR0, SDR0_PCIEH_H, PCIE_MSI_REG_BASE_H); >+ mtdcri(SDR0, SDR0_PCIEH_L, PCIE_MSI_REG_BASE_L); >+ msi->msi_regs = ioremap(((u64)PCIE_MSI_REG_BASE_H << 32) | res.start, >+ res.end - res.start + 1); You defined sdr-base in the device tree. Please use it instead of the hard coding. >+#else >+ dev_err(&dev->dev, " Invalid Device \n"); >+ goto error_out; >+#endif >+ if (!msi->msi_regs) { >+ dev_err(&dev->dev, "ioremap problem failed\n"); >+ goto error_out; >+ } >+ /* MSI region always mapped in 4GB region*/ >+ msi->msi_addr_hi = 0x0; >+ msi_virt = dma_alloc_coherent(&dev->dev, 64, &msi_phys, >+ GFP_KERNEL); >+ if (msi_virt == NULL) { >+ dev_err(&dev->dev, "No memory for MSI mem space\n"); >+ rc = -ENOMEM; >+ goto error_out; >+ } >+ msi->msi_addr_lo = (u32)msi_phys; >+ >+ /* Progam the Interrupt handler Termination addr registers */ >+ out_be32(msi->msi_regs + PEIH_TERMADH, msi->msi_addr_hi); >+ out_be32(msi->msi_regs + PEIH_TERMADL, msi->msi_addr_lo); >+ >+ /* Program MSI Expected data and Mask bits */ >+ out_be32(msi->msi_regs + PEIH_MSIED, MSI_DATA_PATTERN); >+ out_be32(msi->msi_regs + PEIH_MSIMK, MSI_DATA_PATTERN); >+ >+ msi->irqhost->host_data = msi; >+ >+ if (ppc4xx_msi_init_allocator(msi)) { >+ dev_err(&dev->dev, "Error allocating MSI bitmap\n"); >+ goto error_out; >+ } >+ >+ p = of_get_property(dev->node, "interrupts", &count); >+ if (!p) { >+ dev_err(&dev->dev, "no interrupts property found on %s\n", >+ dev->node->full_name); >+ rc = -ENODEV; >+ goto error_out; >+ } >+ if (count == 0) { >+ dev_err(&dev->dev, "Malformed interrupts property on %s\n", >+ dev->node->full_name); >+ rc = -EINVAL; >+ goto error_out; >+ } >+ >+ for (i = 0; i < NR_MSI_IRQS; i++) { >+ virt_msir = irq_of_parse_and_map(dev->node, i); >+ if (virt_msir != NO_IRQ) { >+ set_irq_data(virt_msir, (void *)i); >+ set_irq_chained_handler(virt_msir, ppc4xx_msi_cascade); >+ } >+ } >+ >+ ppc4xx_msi = msi; >+ >+ WARN_ON(ppc_md.setup_msi_irqs); >+ ppc_md.setup_msi_irqs = ppc4xx_setup_msi_irqs; >+ ppc_md.teardown_msi_irqs = ppc4xx_teardown_msi_irqs; >+ ppc_md.msi_check_device = ppc4xx_msi_check_device; >+ return 0; >+error_out: >+ if (msi_virt) >+ dma_free_coherent(&dev->dev, 64, msi_virt, msi_phys); >+ kfree(msi); >+ return rc; >+} >+ >+static const struct ppc4xx_msi_feature ppc4xx_msi_feature = { >+ .ppc4xx_pic_ip = 0, >+ .msiir_offset = 0x140, >+}; >+ >+static const struct of_device_id ppc4xx_msi_ids[] = { >+ { >+ .compatible = "amcc,ppc4xx-msi", >+ .data = (void *)&ppc4xx_msi_feature, >+ }, >+ {} >+}; >+ >+static struct of_platform_driver ppc4xx_msi_driver = { >+ .name = "ppc4xx-msi", >+ .match_table = ppc4xx_msi_ids, >+ .probe = ppc4xx_msi_probe, >+}; >+ >+static __init int ppc4xx_msi_init(void) >+{ >+ return of_register_platform_driver(&ppc4xx_msi_driver); >+} >+ >+subsys_initcall(ppc4xx_msi_init); >diff --git a/arch/powerpc/sysdev/ppc4xx_msi.h b/arch/powerpc/sysdev/ppc4xx_msi.h >new file mode 100644 >index 0000000..7b8ac5c >--- /dev/null >+++ b/arch/powerpc/sysdev/ppc4xx_msi.h >@@ -0,0 +1,49 @@ >+/* >+ * Copyright (C) 2009 Applied Micro Circuits Corporation, >+ * All rights reserved. Same comment as above. >+ * >+ * Author: T irumala Marri <tmarri@amcc.com> >+ * Feng Kan <fkan@amcc.com> >+ * This program is free software; you can redistribute it and/or >+ * modify it under the terms of the GNU General Public License >+ * as published by the Free Software Foundation; version 2 of the >+ * License. >+ */ >+#ifndef __PPC4XX_MSI_H__ >+#define __PPC4XX_MSI_H__ >+ >+#include <asm/msi_bitmap.h> >+ >+#define PEIH_TERMADH 0x00 >+#define PEIH_TERMADL 0x08 >+#define PEIH_MSIED 0x10 >+#define PEIH_MSIMK 0x18 >+#define PEIH_MSIASS 0x20 >+#define PEIH_FLUSH0 0x30 >+#define PEIH_FLUSH1 0x38 >+#define PEIH_CNTRST 0x48 >+ >+#define MSI_DATA_PATTERN 0x44440000 >+ >+#if defined(CONFIG_405Ex) >+#define SDR0_PCIEH 0x4B1 >+#define PCIE_MSI_REG_BASE 0xef620000 >+#elif defined(CONFIG_440SPe) || defined(CONFIG_460SX) >+#define SDR0_PCIEH_H 0x3B0 >+#define SDR0_PCIEH_L 0x3B1 >+#define PCIE_MSI_REG_BASE_L 0x00300000 >+#define PCIE_MSI_REG_BASE_H 0x00000004 >+#endif sdr-base covers quite a bit of this. >+ >+struct ppc4xx_msi { >+ struct irq_host *irqhost; >+ unsigned long cascade_irq; >+ u32 msi_addr_lo; >+ u32 msi_addr_hi; >+ void __iomem *msi_regs; >+ u32 feature; >+ struct msi_bitmap bitmap; >+}; Perhaps add an sdr_base member to this struct, similar to how the pci port structure looks. josh
Josh, Thanks for the comments. I will fix them re-submit it. Regards, Marri -----Original Message----- From: Josh Boyer [mailto:jwboyer@gmail.com] On Behalf Of Josh Boyer Sent: Tuesday, December 22, 2009 4:08 AM To: Tirumala Reddy Marri Cc: linuxppc-dev@lists.ozlabs.org; writetomarri@yahoo.com Subject: Re: [PATCH 2/2] Adding PCI-E MSI support for PowerPC 460SX SOC. On Tue, Dec 22, 2009 at 12:49:51AM -0800, tmarri@amcc.com wrote: >From: Tirumala Marri <tmarri@amcc.com> > > >Signed-off-by: Tirumala Marri <tmarri@amcc.com> >--- >Kernel version: 2.6.33-rc1 >Testing: > When 460SX configured as root as a end point E1000(Intell Ethernet card) > was plugged into the one of the PCI-E ports. I was able to run the traffic > with MSI interrupts. >--- > arch/powerpc/boot/dts/redwood.dts | 15 ++ > arch/powerpc/configs/44x/redwood_defconfig | 5 +- > arch/powerpc/platforms/44x/Kconfig | 1 + > arch/powerpc/sysdev/Kconfig | 7 + > arch/powerpc/sysdev/Makefile | 1 + > arch/powerpc/sysdev/ppc4xx_msi.c | 335 ++++++++++++++++++++++++++++ > arch/powerpc/sysdev/ppc4xx_msi.h | 49 ++++ > 7 files changed, 411 insertions(+), 2 deletions(-) > create mode 100644 arch/powerpc/sysdev/ppc4xx_msi.c > create mode 100644 arch/powerpc/sysdev/ppc4xx_msi.h > >diff --git a/arch/powerpc/boot/dts/redwood.dts b/arch/powerpc/boot/dts/redwood.dts >index 81636c0..412d5f9 100644 >--- a/arch/powerpc/boot/dts/redwood.dts >+++ b/arch/powerpc/boot/dts/redwood.dts >@@ -357,6 +357,21 @@ > 0x0 0x0 0x0 0x3 &UIC3 0xa 0x4 /* swizzled int C */ > 0x0 0x0 0x0 0x4 &UIC3 0xb 0x4 /* swizzled int D */>; > }; >+ MSI: ppc4xx-msi@400300000 { >+ compatible = "amcc,ppc4xx-msi", "ppc4xx-msi"; >+ reg = < 0x4 0x00300000 0x100 >+ 0x4 0x00300000 0x100>; >+ sdr-base = <0x3B0>; >+ interrupts =<0 1 2 3>; >+ interrupt-parent = <&MSI>; >+ #interrupt-cells = <1>; >+ #address-cells = <0>; >+ #size-cells = <0>; >+ interrupt-map = <0 &UIC0 0xC 1 >+ 1 &UIC0 0x0D 1 >+ 2 &UIC0 0x0E 1 >+ 3 &UIC0 0x0F 1>; >+ }; > > }; > >diff --git a/arch/powerpc/sysdev/Kconfig b/arch/powerpc/sysdev/Kconfig >index 3965828..32f5a40 100644 >--- a/arch/powerpc/sysdev/Kconfig >+++ b/arch/powerpc/sysdev/Kconfig >@@ -7,8 +7,15 @@ config PPC4xx_PCI_EXPRESS > depends on PCI && 4xx > default n > >+config 4xx_MSI This should probably be named PPC4xx_MSI, similar to how PPC4xx_PCI_EXPRESS is named. >+ bool >+ depends on PCI_MSI >+ depends on PCI && 4xx >+ default n >+ > config PPC_MSI_BITMAP > bool > depends on PCI_MSI > default y if MPIC > default y if FSL_PCI >+ default y if 4xx_MSI >diff --git a/arch/powerpc/sysdev/ppc4xx_msi.c b/arch/powerpc/sysdev/ppc4xx_msi.c >new file mode 100644 >index 0000000..752da4b >--- /dev/null >+++ b/arch/powerpc/sysdev/ppc4xx_msi.c >@@ -0,0 +1,335 @@ >+/* >+ * Copyright (C) 2009 Applied Micro Circuits corporation, >+ * All rights reserved. Please don't add the 'All rights reserved.' to new files. It is inaccurate and confusing given that it's a GPLv2 file. >+ * >+ * Author: Feng Kan <fkan@amcc.com> >+ * Tirumala Marri <tmarri@amcc.com> >+ * This program is free software; you can redistribute it and/or >+ * modify it under the terms of the GNU General Public License >+ * as published by the Free Software Foundation; version 2 of the >+ * License. >+ */ >+#include <linux/irq.h> >+#include <linux/bootmem.h> >+#include <linux/pci.h> >+#include <linux/msi.h> >+#include <linux/of_platform.h> >+#include <linux/interrupt.h> >+#include <linux/device.h> >+#include <asm/prom.h> >+#include <asm/hw_irq.h> >+#include <asm/ppc-pci.h> >+#include <asm/dcr.h> >+#include <asm/dcr-regs.h> >+#include "ppc4xx_msi.h" >+ >+ >+static struct ppc4xx_msi *ppc4xx_msi; >+ >+struct ppc4xx_msi_feature { >+ u32 ppc4xx_pic_ip; >+ u32 msiir_offset; >+}; >+ >+static int ppc4xx_msi_init_allocator(struct ppc4xx_msi *msi_data) >+{ >+ int rc; >+ >+ rc = msi_bitmap_alloc(&msi_data->bitmap, NR_MSI_IRQS, >+ 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; >+ } >+ return 0; >+} >+ >+ >+static void ppc4xx_msi_cascade(unsigned int irq, struct irq_desc *desc) >+{ >+ unsigned int cascade_irq; >+ struct ppc4xx_msi *msi_data = ppc4xx_msi; >+ int msir_index = -1; >+ >+ raw_spin_lock(&desc->lock); >+ if (desc->chip->mask_ack) { >+ desc->chip->mask_ack(irq); >+ } else { >+ desc->chip->mask(irq); >+ desc->chip->ack(irq); >+ } >+ >+ if (unlikely(desc->status & IRQ_INPROGRESS)) >+ goto unlock; >+ >+ msir_index = (int)desc->handler_data; >+ >+ if (msir_index >= NR_MSI_IRQS) >+ cascade_irq = NO_IRQ; >+ >+ desc->status |= IRQ_INPROGRESS; >+ >+ cascade_irq = irq_linear_revmap(msi_data->irqhost, msir_index); >+ if (cascade_irq != NO_IRQ) >+ generic_handle_irq(cascade_irq); >+ desc->status &= ~IRQ_INPROGRESS; >+ >+ if (!(desc->status & IRQ_DISABLED) && desc->chip->unmask) >+ desc->chip->unmask(irq); >+unlock: >+ raw_spin_unlock(&desc->lock); >+} >+static void ppc4xx_compose_msi_msg(struct pci_dev *pdev, int hwirq, >+ struct msi_msg *msg) >+{ >+ struct ppc4xx_msi *msi_data = ppc4xx_msi; >+ >+ msg->address_lo = msi_data->msi_addr_lo; >+ msg->address_hi = msi_data->msi_addr_hi; >+ msg->data = hwirq; >+} >+ >+ >+int ppc4xx_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) >+{ >+ struct msi_desc *entry; >+ int rc, hwirq; >+ unsigned int virq; >+ struct msi_msg msg; >+ struct ppc4xx_msi *msi_data = ppc4xx_msi; >+ >+ >+ list_for_each_entry(entry, &dev->msi_list, list) { >+ hwirq = msi_bitmap_alloc_hwirqs(&msi_data->bitmap, 1); >+ if (hwirq < 0) { >+ rc = hwirq; >+ dev_err(&dev->dev, "%s: fail allocating msi\ >+ interrupt\n", __func__); >+ goto out_free; >+ } >+ >+ pr_debug(KERN_INFO"mis is %p\n", msi_data->irqhost); >+ virq = irq_create_mapping(msi_data->irqhost, hwirq); >+ if (virq == NO_IRQ) { >+ dev_err(&dev->dev, "%s: fail mapping irq\n", __func__); >+ rc = -ENOSPC; >+ goto out_free; >+ } >+ >+ set_irq_msi(virq, entry); >+ ppc4xx_compose_msi_msg(dev, hwirq, &msg); >+ write_msi_msg(virq, &msg); >+ } >+ >+ return 0; >+out_free: >+ return rc; >+} >+ >+void ppc4xx_teardown_msi_irqs(struct pci_dev *dev) >+{ >+ struct msi_desc *entry; >+ struct ppc4xx_msi *msi_data = ppc4xx_msi; >+ dev_dbg(&dev->dev, "PCIE-MSI: tearing down msi irqs\n"); >+ >+ list_for_each_entry(entry, &dev->msi_list, list) { >+ if (entry->irq == NO_IRQ) >+ continue; >+ set_irq_msi(entry->irq, NULL); >+ msi_bitmap_free_hwirqs(&msi_data->bitmap, >+ virq_to_hw(entry->irq), 1); >+ irq_dispose_mapping(entry->irq); >+ >+ } >+ >+ return; >+} >+ >+static int ppc4xx_msi_check_device(struct pci_dev *pdev, int nvec, int type) >+{ >+ pr_debug(KERN_INFO"PCIE-MSI:%s called. vec %x type %d\n", >+ __func__, nvec, type); >+ return 0; >+} >+ >+/* >+ * We do not need this actually. The MSIR register has been read once >+ * in the cascade interrupt. So, this MSI interrupt has been acked >+*/ >+static void ppc4xx_msi_end_irq(unsigned int virq) >+{ >+} >+ >+ >+static struct irq_chip ppc4xx_msi_chip = { >+ .mask = mask_msi_irq, >+ .unmask = unmask_msi_irq, >+ .ack = ppc4xx_msi_end_irq, >+ .name = " UIC", >+}; >+ >+static int ppc4xx_msi_host_map(struct irq_host *h, unsigned int virq, >+ irq_hw_number_t hw) >+{ >+ struct irq_chip *chip = &ppc4xx_msi_chip; >+ >+ irq_to_desc(virq)->status |= IRQ_TYPE_EDGE_RISING; >+ >+ set_irq_chip_and_handler(virq, chip, handle_edge_irq); >+ >+ return 0; >+} >+ >+static struct irq_host_ops ppc4xx_msi_host_ops = { >+ .map = ppc4xx_msi_host_map, >+}; >+ >+ >+static int __devinit ppc4xx_msi_probe(struct of_device *dev, >+ const struct of_device_id *match) >+{ >+ struct ppc4xx_msi *msi; >+ struct resource res, rmsi; >+ int i, count; >+ int rc; >+ int virt_msir; >+ const u32 *p; >+ u32 *msi_virt = NULL; >+ dma_addr_t msi_phys; >+ >+ >+ msi = kzalloc(sizeof(struct ppc4xx_msi), GFP_KERNEL); >+ if (!msi) { >+ dev_err(&dev->dev, "No memory for MSI structure\n"); >+ rc = -ENOMEM; >+ goto error_out; >+ } >+ >+ msi->irqhost = irq_alloc_host(dev->node, IRQ_HOST_MAP_LINEAR, >+ NR_MSI_IRQS, &ppc4xx_msi_host_ops, 0); >+ if (msi->irqhost == NULL) { >+ dev_err(&dev->dev, "No memory for MSI irqhost\n"); >+ rc = -ENOMEM; >+ goto error_out; >+ } >+ >+ >+ /* Get MSI ranges */ >+ rc = of_address_to_resource(dev->node, 0, &rmsi); >+ if (rc) { >+ dev_err(&dev->dev, "%s resource error!\n", >+ dev->node->full_name); >+ goto error_out; >+ } >+ >+ >+ /* Get the MSI reg base */ >+ rc = of_address_to_resource(dev->node, 1, &res); >+ if (rc) { >+ dev_err(&dev->dev, "%s resource error!\n", >+ dev->node->full_name); >+ goto error_out; >+ } >+#if defined(CONFIG_460SX) >+ mtdcri(SDR0, SDR0_PCIEH_H, PCIE_MSI_REG_BASE_H); >+ mtdcri(SDR0, SDR0_PCIEH_L, PCIE_MSI_REG_BASE_L); >+ msi->msi_regs = ioremap(((u64)PCIE_MSI_REG_BASE_H << 32) | res.start, >+ res.end - res.start + 1); You defined sdr-base in the device tree. Please use it instead of the hard coding. >+#else >+ dev_err(&dev->dev, " Invalid Device \n"); >+ goto error_out; >+#endif >+ if (!msi->msi_regs) { >+ dev_err(&dev->dev, "ioremap problem failed\n"); >+ goto error_out; >+ } >+ /* MSI region always mapped in 4GB region*/ >+ msi->msi_addr_hi = 0x0; >+ msi_virt = dma_alloc_coherent(&dev->dev, 64, &msi_phys, >+ GFP_KERNEL); >+ if (msi_virt == NULL) { >+ dev_err(&dev->dev, "No memory for MSI mem space\n"); >+ rc = -ENOMEM; >+ goto error_out; >+ } >+ msi->msi_addr_lo = (u32)msi_phys; >+ >+ /* Progam the Interrupt handler Termination addr registers */ >+ out_be32(msi->msi_regs + PEIH_TERMADH, msi->msi_addr_hi); >+ out_be32(msi->msi_regs + PEIH_TERMADL, msi->msi_addr_lo); >+ >+ /* Program MSI Expected data and Mask bits */ >+ out_be32(msi->msi_regs + PEIH_MSIED, MSI_DATA_PATTERN); >+ out_be32(msi->msi_regs + PEIH_MSIMK, MSI_DATA_PATTERN); >+ >+ msi->irqhost->host_data = msi; >+ >+ if (ppc4xx_msi_init_allocator(msi)) { >+ dev_err(&dev->dev, "Error allocating MSI bitmap\n"); >+ goto error_out; >+ } >+ >+ p = of_get_property(dev->node, "interrupts", &count); >+ if (!p) { >+ dev_err(&dev->dev, "no interrupts property found on %s\n", >+ dev->node->full_name); >+ rc = -ENODEV; >+ goto error_out; >+ } >+ if (count == 0) { >+ dev_err(&dev->dev, "Malformed interrupts property on %s\n", >+ dev->node->full_name); >+ rc = -EINVAL; >+ goto error_out; >+ } >+ >+ for (i = 0; i < NR_MSI_IRQS; i++) { >+ virt_msir = irq_of_parse_and_map(dev->node, i); >+ if (virt_msir != NO_IRQ) { >+ set_irq_data(virt_msir, (void *)i); >+ set_irq_chained_handler(virt_msir, ppc4xx_msi_cascade); >+ } >+ } >+ >+ ppc4xx_msi = msi; >+ >+ WARN_ON(ppc_md.setup_msi_irqs); >+ ppc_md.setup_msi_irqs = ppc4xx_setup_msi_irqs; >+ ppc_md.teardown_msi_irqs = ppc4xx_teardown_msi_irqs; >+ ppc_md.msi_check_device = ppc4xx_msi_check_device; >+ return 0; >+error_out: >+ if (msi_virt) >+ dma_free_coherent(&dev->dev, 64, msi_virt, msi_phys); >+ kfree(msi); >+ return rc; >+} >+ >+static const struct ppc4xx_msi_feature ppc4xx_msi_feature = { >+ .ppc4xx_pic_ip = 0, >+ .msiir_offset = 0x140, >+}; >+ >+static const struct of_device_id ppc4xx_msi_ids[] = { >+ { >+ .compatible = "amcc,ppc4xx-msi", >+ .data = (void *)&ppc4xx_msi_feature, >+ }, >+ {} >+}; >+ >+static struct of_platform_driver ppc4xx_msi_driver = { >+ .name = "ppc4xx-msi", >+ .match_table = ppc4xx_msi_ids, >+ .probe = ppc4xx_msi_probe, >+}; >+ >+static __init int ppc4xx_msi_init(void) >+{ >+ return of_register_platform_driver(&ppc4xx_msi_driver); >+} >+ >+subsys_initcall(ppc4xx_msi_init); >diff --git a/arch/powerpc/sysdev/ppc4xx_msi.h b/arch/powerpc/sysdev/ppc4xx_msi.h >new file mode 100644 >index 0000000..7b8ac5c >--- /dev/null >+++ b/arch/powerpc/sysdev/ppc4xx_msi.h >@@ -0,0 +1,49 @@ >+/* >+ * Copyright (C) 2009 Applied Micro Circuits Corporation, >+ * All rights reserved. Same comment as above. >+ * >+ * Author: T irumala Marri <tmarri@amcc.com> >+ * Feng Kan <fkan@amcc.com> >+ * This program is free software; you can redistribute it and/or >+ * modify it under the terms of the GNU General Public License >+ * as published by the Free Software Foundation; version 2 of the >+ * License. >+ */ >+#ifndef __PPC4XX_MSI_H__ >+#define __PPC4XX_MSI_H__ >+ >+#include <asm/msi_bitmap.h> >+ >+#define PEIH_TERMADH 0x00 >+#define PEIH_TERMADL 0x08 >+#define PEIH_MSIED 0x10 >+#define PEIH_MSIMK 0x18 >+#define PEIH_MSIASS 0x20 >+#define PEIH_FLUSH0 0x30 >+#define PEIH_FLUSH1 0x38 >+#define PEIH_CNTRST 0x48 >+ >+#define MSI_DATA_PATTERN 0x44440000 >+ >+#if defined(CONFIG_405Ex) >+#define SDR0_PCIEH 0x4B1 >+#define PCIE_MSI_REG_BASE 0xef620000 >+#elif defined(CONFIG_440SPe) || defined(CONFIG_460SX) >+#define SDR0_PCIEH_H 0x3B0 >+#define SDR0_PCIEH_L 0x3B1 >+#define PCIE_MSI_REG_BASE_L 0x00300000 >+#define PCIE_MSI_REG_BASE_H 0x00000004 >+#endif sdr-base covers quite a bit of this. >+ >+struct ppc4xx_msi { >+ struct irq_host *irqhost; >+ unsigned long cascade_irq; >+ u32 msi_addr_lo; >+ u32 msi_addr_hi; >+ void __iomem *msi_regs; >+ u32 feature; >+ struct msi_bitmap bitmap; >+}; Perhaps add an sdr_base member to this struct, similar to how the pci port structure looks. josh
diff --git a/arch/powerpc/boot/dts/redwood.dts b/arch/powerpc/boot/dts/redwood.dts index 81636c0..412d5f9 100644 --- a/arch/powerpc/boot/dts/redwood.dts +++ b/arch/powerpc/boot/dts/redwood.dts @@ -357,6 +357,21 @@ 0x0 0x0 0x0 0x3 &UIC3 0xa 0x4 /* swizzled int C */ 0x0 0x0 0x0 0x4 &UIC3 0xb 0x4 /* swizzled int D */>; }; + MSI: ppc4xx-msi@400300000 { + compatible = "amcc,ppc4xx-msi", "ppc4xx-msi"; + reg = < 0x4 0x00300000 0x100 + 0x4 0x00300000 0x100>; + sdr-base = <0x3B0>; + interrupts =<0 1 2 3>; + interrupt-parent = <&MSI>; + #interrupt-cells = <1>; + #address-cells = <0>; + #size-cells = <0>; + interrupt-map = <0 &UIC0 0xC 1 + 1 &UIC0 0x0D 1 + 2 &UIC0 0x0E 1 + 3 &UIC0 0x0F 1>; + }; }; diff --git a/arch/powerpc/configs/44x/redwood_defconfig b/arch/powerpc/configs/44x/redwood_defconfig index ed31d4f..5d16c88 100644 --- a/arch/powerpc/configs/44x/redwood_defconfig +++ b/arch/powerpc/configs/44x/redwood_defconfig @@ -158,6 +158,7 @@ CONFIG_DEFAULT_AS=y CONFIG_DEFAULT_IOSCHED="anticipatory" # CONFIG_FREEZER is not set CONFIG_PPC4xx_PCI_EXPRESS=y +CONFIG_PPC_MSI_BITMAP=y # # Platform support @@ -264,7 +265,7 @@ CONFIG_PCIEPORTBUS=y CONFIG_PCIEAER=y # CONFIG_PCIEASPM is not set CONFIG_ARCH_SUPPORTS_MSI=y -# CONFIG_PCI_MSI is not set +CONFIG_PCI_MSI=y # CONFIG_PCI_LEGACY is not set # CONFIG_PCI_DEBUG is not set # CONFIG_PCI_STUB is not set @@ -1062,7 +1063,7 @@ CONFIG_PRINT_STACK_DEPTH=64 # CONFIG_DEBUG_PAGEALLOC is not set # CONFIG_CODE_PATCHING_SELFTEST is not set # CONFIG_FTR_FIXUP_SELFTEST is not set -# CONFIG_MSI_BITMAP_SELFTEST is not set +CONFIG_MSI_BITMAP_SELFTEST=y # CONFIG_XMON is not set # CONFIG_IRQSTACKS is not set # CONFIG_VIRQ_DEBUG is not set diff --git a/arch/powerpc/platforms/44x/Kconfig b/arch/powerpc/platforms/44x/Kconfig index 7486bff..85b9c33 100644 --- a/arch/powerpc/platforms/44x/Kconfig +++ b/arch/powerpc/platforms/44x/Kconfig @@ -126,6 +126,7 @@ config REDWOOD select 460SX select PCI select PPC4xx_PCI_EXPRESS + select 4xx_MSI help This option enables support for the AMCC PPC460SX Redwood board. diff --git a/arch/powerpc/sysdev/Kconfig b/arch/powerpc/sysdev/Kconfig index 3965828..32f5a40 100644 --- a/arch/powerpc/sysdev/Kconfig +++ b/arch/powerpc/sysdev/Kconfig @@ -7,8 +7,15 @@ config PPC4xx_PCI_EXPRESS depends on PCI && 4xx default n +config 4xx_MSI + bool + depends on PCI_MSI + depends on PCI && 4xx + default n + config PPC_MSI_BITMAP bool depends on PCI_MSI default y if MPIC default y if FSL_PCI + default y if 4xx_MSI diff --git a/arch/powerpc/sysdev/Makefile b/arch/powerpc/sysdev/Makefile index 5642924..d60c33b 100644 --- a/arch/powerpc/sysdev/Makefile +++ b/arch/powerpc/sysdev/Makefile @@ -36,6 +36,7 @@ obj-$(CONFIG_PPC_I8259) += i8259.o obj-$(CONFIG_IPIC) += ipic.o obj-$(CONFIG_4xx) += uic.o obj-$(CONFIG_4xx_SOC) += ppc4xx_soc.o +obj-$(CONFIG_4xx_MSI) += ppc4xx_msi.o obj-$(CONFIG_XILINX_VIRTEX) += xilinx_intc.o obj-$(CONFIG_XILINX_PCI) += xilinx_pci.o obj-$(CONFIG_OF_RTC) += of_rtc.o diff --git a/arch/powerpc/sysdev/ppc4xx_msi.c b/arch/powerpc/sysdev/ppc4xx_msi.c new file mode 100644 index 0000000..752da4b --- /dev/null +++ b/arch/powerpc/sysdev/ppc4xx_msi.c @@ -0,0 +1,335 @@ +/* + * Copyright (C) 2009 Applied Micro Circuits corporation, + * All rights reserved. + * + * Author: Feng Kan <fkan@amcc.com> + * Tirumala Marri <tmarri@amcc.com> + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 of the + * License. + */ +#include <linux/irq.h> +#include <linux/bootmem.h> +#include <linux/pci.h> +#include <linux/msi.h> +#include <linux/of_platform.h> +#include <linux/interrupt.h> +#include <linux/device.h> +#include <asm/prom.h> +#include <asm/hw_irq.h> +#include <asm/ppc-pci.h> +#include <asm/dcr.h> +#include <asm/dcr-regs.h> +#include "ppc4xx_msi.h" + + +static struct ppc4xx_msi *ppc4xx_msi; + +struct ppc4xx_msi_feature { + u32 ppc4xx_pic_ip; + u32 msiir_offset; +}; + +static int ppc4xx_msi_init_allocator(struct ppc4xx_msi *msi_data) +{ + int rc; + + rc = msi_bitmap_alloc(&msi_data->bitmap, NR_MSI_IRQS, + 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; + } + return 0; +} + + +static void ppc4xx_msi_cascade(unsigned int irq, struct irq_desc *desc) +{ + unsigned int cascade_irq; + struct ppc4xx_msi *msi_data = ppc4xx_msi; + int msir_index = -1; + + raw_spin_lock(&desc->lock); + if (desc->chip->mask_ack) { + desc->chip->mask_ack(irq); + } else { + desc->chip->mask(irq); + desc->chip->ack(irq); + } + + if (unlikely(desc->status & IRQ_INPROGRESS)) + goto unlock; + + msir_index = (int)desc->handler_data; + + if (msir_index >= NR_MSI_IRQS) + cascade_irq = NO_IRQ; + + desc->status |= IRQ_INPROGRESS; + + cascade_irq = irq_linear_revmap(msi_data->irqhost, msir_index); + if (cascade_irq != NO_IRQ) + generic_handle_irq(cascade_irq); + desc->status &= ~IRQ_INPROGRESS; + + if (!(desc->status & IRQ_DISABLED) && desc->chip->unmask) + desc->chip->unmask(irq); +unlock: + raw_spin_unlock(&desc->lock); +} +static void ppc4xx_compose_msi_msg(struct pci_dev *pdev, int hwirq, + struct msi_msg *msg) +{ + struct ppc4xx_msi *msi_data = ppc4xx_msi; + + msg->address_lo = msi_data->msi_addr_lo; + msg->address_hi = msi_data->msi_addr_hi; + msg->data = hwirq; +} + + +int ppc4xx_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) +{ + struct msi_desc *entry; + int rc, hwirq; + unsigned int virq; + struct msi_msg msg; + struct ppc4xx_msi *msi_data = ppc4xx_msi; + + + list_for_each_entry(entry, &dev->msi_list, list) { + hwirq = msi_bitmap_alloc_hwirqs(&msi_data->bitmap, 1); + if (hwirq < 0) { + rc = hwirq; + dev_err(&dev->dev, "%s: fail allocating msi\ + interrupt\n", __func__); + goto out_free; + } + + pr_debug(KERN_INFO"mis is %p\n", msi_data->irqhost); + virq = irq_create_mapping(msi_data->irqhost, hwirq); + if (virq == NO_IRQ) { + dev_err(&dev->dev, "%s: fail mapping irq\n", __func__); + rc = -ENOSPC; + goto out_free; + } + + set_irq_msi(virq, entry); + ppc4xx_compose_msi_msg(dev, hwirq, &msg); + write_msi_msg(virq, &msg); + } + + return 0; +out_free: + return rc; +} + +void ppc4xx_teardown_msi_irqs(struct pci_dev *dev) +{ + struct msi_desc *entry; + struct ppc4xx_msi *msi_data = ppc4xx_msi; + dev_dbg(&dev->dev, "PCIE-MSI: tearing down msi irqs\n"); + + list_for_each_entry(entry, &dev->msi_list, list) { + if (entry->irq == NO_IRQ) + continue; + set_irq_msi(entry->irq, NULL); + msi_bitmap_free_hwirqs(&msi_data->bitmap, + virq_to_hw(entry->irq), 1); + irq_dispose_mapping(entry->irq); + + } + + return; +} + +static int ppc4xx_msi_check_device(struct pci_dev *pdev, int nvec, int type) +{ + pr_debug(KERN_INFO"PCIE-MSI:%s called. vec %x type %d\n", + __func__, nvec, type); + return 0; +} + +/* + * We do not need this actually. The MSIR register has been read once + * in the cascade interrupt. So, this MSI interrupt has been acked +*/ +static void ppc4xx_msi_end_irq(unsigned int virq) +{ +} + + +static struct irq_chip ppc4xx_msi_chip = { + .mask = mask_msi_irq, + .unmask = unmask_msi_irq, + .ack = ppc4xx_msi_end_irq, + .name = " UIC", +}; + +static int ppc4xx_msi_host_map(struct irq_host *h, unsigned int virq, + irq_hw_number_t hw) +{ + struct irq_chip *chip = &ppc4xx_msi_chip; + + irq_to_desc(virq)->status |= IRQ_TYPE_EDGE_RISING; + + set_irq_chip_and_handler(virq, chip, handle_edge_irq); + + return 0; +} + +static struct irq_host_ops ppc4xx_msi_host_ops = { + .map = ppc4xx_msi_host_map, +}; + + +static int __devinit ppc4xx_msi_probe(struct of_device *dev, + const struct of_device_id *match) +{ + struct ppc4xx_msi *msi; + struct resource res, rmsi; + int i, count; + int rc; + int virt_msir; + const u32 *p; + u32 *msi_virt = NULL; + dma_addr_t msi_phys; + + + msi = kzalloc(sizeof(struct ppc4xx_msi), GFP_KERNEL); + if (!msi) { + dev_err(&dev->dev, "No memory for MSI structure\n"); + rc = -ENOMEM; + goto error_out; + } + + msi->irqhost = irq_alloc_host(dev->node, IRQ_HOST_MAP_LINEAR, + NR_MSI_IRQS, &ppc4xx_msi_host_ops, 0); + if (msi->irqhost == NULL) { + dev_err(&dev->dev, "No memory for MSI irqhost\n"); + rc = -ENOMEM; + goto error_out; + } + + + /* Get MSI ranges */ + rc = of_address_to_resource(dev->node, 0, &rmsi); + if (rc) { + dev_err(&dev->dev, "%s resource error!\n", + dev->node->full_name); + goto error_out; + } + + + /* Get the MSI reg base */ + rc = of_address_to_resource(dev->node, 1, &res); + if (rc) { + dev_err(&dev->dev, "%s resource error!\n", + dev->node->full_name); + goto error_out; + } +#if defined(CONFIG_460SX) + mtdcri(SDR0, SDR0_PCIEH_H, PCIE_MSI_REG_BASE_H); + mtdcri(SDR0, SDR0_PCIEH_L, PCIE_MSI_REG_BASE_L); + msi->msi_regs = ioremap(((u64)PCIE_MSI_REG_BASE_H << 32) | res.start, + res.end - res.start + 1); +#else + dev_err(&dev->dev, " Invalid Device \n"); + goto error_out; +#endif + if (!msi->msi_regs) { + dev_err(&dev->dev, "ioremap problem failed\n"); + goto error_out; + } + /* MSI region always mapped in 4GB region*/ + msi->msi_addr_hi = 0x0; + msi_virt = dma_alloc_coherent(&dev->dev, 64, &msi_phys, + GFP_KERNEL); + if (msi_virt == NULL) { + dev_err(&dev->dev, "No memory for MSI mem space\n"); + rc = -ENOMEM; + goto error_out; + } + msi->msi_addr_lo = (u32)msi_phys; + + /* Progam the Interrupt handler Termination addr registers */ + out_be32(msi->msi_regs + PEIH_TERMADH, msi->msi_addr_hi); + out_be32(msi->msi_regs + PEIH_TERMADL, msi->msi_addr_lo); + + /* Program MSI Expected data and Mask bits */ + out_be32(msi->msi_regs + PEIH_MSIED, MSI_DATA_PATTERN); + out_be32(msi->msi_regs + PEIH_MSIMK, MSI_DATA_PATTERN); + + msi->irqhost->host_data = msi; + + if (ppc4xx_msi_init_allocator(msi)) { + dev_err(&dev->dev, "Error allocating MSI bitmap\n"); + goto error_out; + } + + p = of_get_property(dev->node, "interrupts", &count); + if (!p) { + dev_err(&dev->dev, "no interrupts property found on %s\n", + dev->node->full_name); + rc = -ENODEV; + goto error_out; + } + if (count == 0) { + dev_err(&dev->dev, "Malformed interrupts property on %s\n", + dev->node->full_name); + rc = -EINVAL; + goto error_out; + } + + for (i = 0; i < NR_MSI_IRQS; i++) { + virt_msir = irq_of_parse_and_map(dev->node, i); + if (virt_msir != NO_IRQ) { + set_irq_data(virt_msir, (void *)i); + set_irq_chained_handler(virt_msir, ppc4xx_msi_cascade); + } + } + + ppc4xx_msi = msi; + + WARN_ON(ppc_md.setup_msi_irqs); + ppc_md.setup_msi_irqs = ppc4xx_setup_msi_irqs; + ppc_md.teardown_msi_irqs = ppc4xx_teardown_msi_irqs; + ppc_md.msi_check_device = ppc4xx_msi_check_device; + return 0; +error_out: + if (msi_virt) + dma_free_coherent(&dev->dev, 64, msi_virt, msi_phys); + kfree(msi); + return rc; +} + +static const struct ppc4xx_msi_feature ppc4xx_msi_feature = { + .ppc4xx_pic_ip = 0, + .msiir_offset = 0x140, +}; + +static const struct of_device_id ppc4xx_msi_ids[] = { + { + .compatible = "amcc,ppc4xx-msi", + .data = (void *)&ppc4xx_msi_feature, + }, + {} +}; + +static struct of_platform_driver ppc4xx_msi_driver = { + .name = "ppc4xx-msi", + .match_table = ppc4xx_msi_ids, + .probe = ppc4xx_msi_probe, +}; + +static __init int ppc4xx_msi_init(void) +{ + return of_register_platform_driver(&ppc4xx_msi_driver); +} + +subsys_initcall(ppc4xx_msi_init); diff --git a/arch/powerpc/sysdev/ppc4xx_msi.h b/arch/powerpc/sysdev/ppc4xx_msi.h new file mode 100644 index 0000000..7b8ac5c --- /dev/null +++ b/arch/powerpc/sysdev/ppc4xx_msi.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2009 Applied Micro Circuits Corporation, + * All rights reserved. + * + * Author: T irumala Marri <tmarri@amcc.com> + * Feng Kan <fkan@amcc.com> + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 of the + * License. + */ +#ifndef __PPC4XX_MSI_H__ +#define __PPC4XX_MSI_H__ + +#include <asm/msi_bitmap.h> + +#define PEIH_TERMADH 0x00 +#define PEIH_TERMADL 0x08 +#define PEIH_MSIED 0x10 +#define PEIH_MSIMK 0x18 +#define PEIH_MSIASS 0x20 +#define PEIH_FLUSH0 0x30 +#define PEIH_FLUSH1 0x38 +#define PEIH_CNTRST 0x48 + +#define MSI_DATA_PATTERN 0x44440000 + +#if defined(CONFIG_405Ex) +#define SDR0_PCIEH 0x4B1 +#define PCIE_MSI_REG_BASE 0xef620000 +#elif defined(CONFIG_440SPe) || defined(CONFIG_460SX) +#define SDR0_PCIEH_H 0x3B0 +#define SDR0_PCIEH_L 0x3B1 +#define PCIE_MSI_REG_BASE_L 0x00300000 +#define PCIE_MSI_REG_BASE_H 0x00000004 +#endif + +struct ppc4xx_msi { + struct irq_host *irqhost; + unsigned long cascade_irq; + u32 msi_addr_lo; + u32 msi_addr_hi; + void __iomem *msi_regs; + u32 feature; + struct msi_bitmap bitmap; +}; + +#define NR_MSI_IRQS 4 +#endif /* __PPC4XX_MSI_H__ */