[PATCH] Add AMCC 4XX PCIe MSI support
fkan at amcc.com
fkan at amcc.com
Fri Aug 22 14:42:24 EST 2008
From: Preetesh Parekh <pparekh at amcc.com>
Signed-off-by: Preetesh Parekh <pparekh at amcc.com>
---
arch/powerpc/platforms/40x/kilauea.c | 13 ++++
arch/powerpc/platforms/44x/canyonlands.c | 14 ++++
arch/powerpc/sysdev/ppc4xx_pci.c | 115 ++++++++++++++++++++++++++++++
arch/powerpc/sysdev/ppc4xx_pci.h | 45 ++++++++++++
include/asm-powerpc/dcr-native.h | 12 +++
5 files changed, 199 insertions(+), 0 deletions(-)
diff --git a/arch/powerpc/platforms/40x/kilauea.c b/arch/powerpc/platforms/40x/kilauea.c
index 1dd24ff..3610be2 100644
--- a/arch/powerpc/platforms/40x/kilauea.c
+++ b/arch/powerpc/platforms/40x/kilauea.c
@@ -22,6 +22,11 @@
#include <asm/pci-bridge.h>
#include <asm/ppc4xx.h>
+#ifdef CONFIG_PCI_MSI
+extern int ppc4xx_setup_msi_irqs(struct pci_dev *dev, int nvec, int type);
+extern void ppc4xx_teardown_msi_irqs(struct pci_dev *dev);
+#endif
+
static __initdata struct of_device_id kilauea_of_bus[] = {
{ .compatible = "ibm,plb4", },
{ .compatible = "ibm,opb", },
@@ -46,6 +51,14 @@ static int __init kilauea_probe(void)
ppc_pci_flags = PPC_PCI_REASSIGN_ALL_RSRC;
+#ifdef CONFIG_PCI_MSI
+ /*
+ * Setting callback functions for MSI support
+ */
+ ppc_md.setup_msi_irqs = ppc4xx_setup_msi_irqs;
+ ppc_md.teardown_msi_irqs = ppc4xx_teardown_msi_irqs;
+#endif
+
return 1;
}
diff --git a/arch/powerpc/platforms/44x/canyonlands.c b/arch/powerpc/platforms/44x/canyonlands.c
index 3949289..ee38fb7 100644
--- a/arch/powerpc/platforms/44x/canyonlands.c
+++ b/arch/powerpc/platforms/44x/canyonlands.c
@@ -25,6 +25,12 @@
#include <asm/pci-bridge.h>
#include <asm/ppc4xx.h>
+
+#ifdef CONFIG_PCI_MSI
+extern int ppc4xx_setup_msi_irqs(struct pci_dev *dev, int nvec, int type);
+extern void ppc4xx_teardown_msi_irqs(struct pci_dev *dev);
+#endif
+
static __initdata struct of_device_id canyonlands_of_bus[] = {
{ .compatible = "ibm,plb4", },
{ .compatible = "ibm,opb", },
@@ -49,6 +55,14 @@ static int __init canyonlands_probe(void)
ppc_pci_flags = PPC_PCI_REASSIGN_ALL_RSRC;
+#ifdef CONFIG_PCI_MSI
+ /*
+ * Setting callback functions for MSI support
+ */
+ ppc_md.setup_msi_irqs = ppc4xx_setup_msi_irqs;
+ ppc_md.teardown_msi_irqs = ppc4xx_teardown_msi_irqs;
+#endif
+
return 1;
}
diff --git a/arch/powerpc/sysdev/ppc4xx_pci.c b/arch/powerpc/sysdev/ppc4xx_pci.c
index fb368df..bdb3663 100644
--- a/arch/powerpc/sysdev/ppc4xx_pci.c
+++ b/arch/powerpc/sysdev/ppc4xx_pci.c
@@ -35,6 +35,11 @@
static int dma_offset_set;
+#ifdef CONFIG_PCI_MSI
+#include <linux/msi.h>
+#include "../../../drivers/pci/msi.h"
+#endif
+
/* Move that to a useable header */
extern unsigned long total_memory;
@@ -1587,12 +1592,26 @@ static void __init ppc4xx_pciex_port_setup_hose(struct ppc4xx_pciex_port *port)
out_le16(mbase + 0x202, val);
if (!port->endpoint) {
+#ifdef CONFIG_PCI_MSI
+ /* Set MSI enable, multiple msg cap = 4 */
+ /* 4 messages allocated, 64 bit capability */
+ out_le32(mbase + 0x048, in_le32(mbase + 0x048) | 0x00a50000);
+
+ /* Enable interrupts in BCR */
+ out_le32(mbase + 0x03C, in_le32(mbase + 0x03C) | 0xFF000000);
+#endif
+
/* Set Class Code to PCI-PCI bridge and Revision Id to 1 */
out_le32(mbase + 0x208, 0x06040001);
printk(KERN_INFO "PCIE%d: successfully set as root-complex\n",
port->index);
} else {
+#ifdef CONFIG_PCI_MSI
+ /* Enable MSI for End-Point */
+ out_le32(mbase + 0x048, (in_le32(mbase + 0x048) | 0x00a50000));
+#endif
+
/* Set Class Code to Processor/PPC */
out_le32(mbase + 0x208, 0x0b200001);
@@ -1704,6 +1723,97 @@ static void __init ppc4xx_probe_pciex_bridge(struct device_node *np)
ppc4xx_pciex_port_setup_hose(port);
}
+#ifdef CONFIG_PCI_MSI
+int ppc4xx_setup_peih(void)
+{
+ void __iomem *peih_base;
+
+ printk(KERN_INFO " %s \n",__FUNCTION__);
+ /* Set base address for PEIH and enable PEIH */
+ SDR_WRITE(PESDR0_4XX_IHS1, PPC4XX_PEIH_REGBASE_HADDR);
+ SDR_WRITE(PESDR0_4XX_IHS2, PPC4XX_PEIH_REGBASE_LADDR);
+
+ /* Map in PCI-E Interrupt Handler Registers */
+ peih_base = ioremap(PPC4XX_PEIH_REGBASE, PPC4XX_PEIH_REGSIZE);
+ if(!peih_base) {
+ printk(KERN_ERR "%s: ioremap64 failed for addr 0x%08x\n",
+ __FUNCTION__, PPC4XX_PEIH_REGBASE);
+ return -ENODEV;
+ }
+
+ /* Progam the Interrupt handler Termination addr registers */
+ out_be32(peih_base + PEIH_TERMADH, PPC4XX_PCIE_MSI_HADDR);
+ out_be32(peih_base + PEIH_TERMADL, PPC4XX_PCIE_MSI_LADDR);
+
+ /* Program MSI Expected data and Mask bits */
+ out_be32(peih_base + PEIH_MSIED, PPC4XX_MSI_DATA_PTRN);
+ out_be32(peih_base + PEIH_MSIMK, PPC4XX_MSI_DATA_VALD);
+
+ iounmap(peih_base);
+
+ return 0; /* success */
+}
+
+
+int arch_setup_msi_irq(struct pci_dev *dev, struct msi_desc *desc)
+{
+ /* Setup MSI Capabilities structure for dev func */
+ int pos;
+ u16 control, ctrl;
+
+ printk(KERN_INFO " %s \n",__FUNCTION__);
+ pos = desc->msi_attrib.pos;
+ pci_read_config_word(dev, msi_control_reg(pos), &control);
+
+ /* Multiple msg enable */
+ ctrl = multi_msi_enable(control, 4);
+ pci_write_config_dword(dev, msi_control_reg(pos), ctrl);
+
+ /* setup MSI address registers */
+ if(is_64bit_address(control))
+ pci_write_config_dword(dev, msi_upper_address_reg(pos), PPC4XX_PCIE_MSI_HADDR);
+
+ pci_write_config_dword(dev, msi_lower_address_reg(pos),
+ PPC4XX_PCIE_MSI_LADDR);
+
+ if(is_64bit_address(control)) {
+ /* setup MSI data register */
+ pci_write_config_dword(dev, msi_data_reg(pos, 1),
+ PPC4XX_MSI_DATA_PTRN_EP);
+ /* setup MSI mask bits reg */
+ pci_write_config_dword(dev, msi_mask_bits_reg(pos, 1), 0x00000000);
+ }
+ else {
+ pci_write_config_dword(dev, msi_data_reg(pos, 0),
+ PPC4XX_MSI_DATA_PTRN_EP);
+ pci_write_config_dword(dev, msi_mask_bits_reg(pos, 0), 0x00000000);
+ }
+
+ return 0;
+}
+
+int ppc4xx_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
+{
+ struct msi_desc *entry;
+ int ret;
+
+ list_for_each_entry(entry, &dev->msi_list, list) {
+ ret = arch_setup_msi_irq(dev, entry);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+void ppc4xx_teardown_msi_irqs(struct pci_dev *dev)
+{
+ return;
+}
+
+
+#endif /* CONFIG_PCI_MSI */
+
#endif /* CONFIG_PPC4xx_PCI_EXPRESS */
static int __init ppc4xx_pci_find_bridges(void)
@@ -1713,6 +1823,11 @@ static int __init ppc4xx_pci_find_bridges(void)
#ifdef CONFIG_PPC4xx_PCI_EXPRESS
for_each_compatible_node(np, NULL, "ibm,plb-pciex")
ppc4xx_probe_pciex_bridge(np);
+
+#ifdef CONFIG_PCI_MSI
+ ppc4xx_setup_peih();
+#endif
+
#endif
for_each_compatible_node(np, NULL, "ibm,plb-pcix")
ppc4xx_probe_pcix_bridge(np);
diff --git a/arch/powerpc/sysdev/ppc4xx_pci.h b/arch/powerpc/sysdev/ppc4xx_pci.h
index d04e40b..d5fcaa9 100644
--- a/arch/powerpc/sysdev/ppc4xx_pci.h
+++ b/arch/powerpc/sysdev/ppc4xx_pci.h
@@ -158,6 +158,51 @@
#define GPL_DMER_MASK_DISA 0x02000000
/*
+ * 4xx PCIe IRQ Handler register definitions
+ */
+#ifdef CONFIG_PCI_MSI
+
+#if defined(CONFIG_405EX)
+#define PESDR0_4XX_IHS1 0x04B0
+#define PESDR0_4XX_IHS2 0x04B1
+#define PPC4XX_PEIH_REGBASE 0x0EF620000
+#define PPC4XX_PEIH_REGBASE_HADDR 0x0
+#define PPC4XX_PEIH_REGBASE_LADDR 0xEF620000
+#define PPC4XX_PCIE_MSI_ADDR 0x080000000
+#define PPC4XX_PCIE_MSI_HADDR 0x0
+#define PPC4XX_PCIE_MSI_LADDR 0x80000000
+
+#elif defined(CONFIG_460EX)
+#define PESDR0_4XX_IHS1 0x036C
+#define PESDR0_4XX_IHS2 0x036D
+#define PPC4XX_PEIH_REGBASE 0xC10000000
+#define PPC4XX_PEIH_REGBASE_HADDR 0xC
+#define PPC4XX_PEIH_REGBASE_LADDR 0x10000000
+#define PPC4XX_PCIE_MSI_ADDR 0xe80000000
+#define PPC4XX_PCIE_MSI_HADDR 0xe
+#define PPC4XX_PCIE_MSI_LADDR 0x80000000
+#endif
+
+#define PPC4XX_PEIH_REGSIZE 0x100
+#define PPC4XX_MSI_DATA_PTRN 0x44440000
+#define PPC4XX_MSI_DATA_VALD 0xFFFF0000
+#define PPC4XX_MSI_DATA_PTRN_EP 0x00004444
+
+#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
+
+
+#endif /* CONFIG_PCI_MSI */
+
+
+
+/*
* System DCRs (SDRs)
*/
#define PESDR0_PLLLCT1 0x03a0
diff --git a/include/asm-powerpc/dcr-native.h b/include/asm-powerpc/dcr-native.h
index 72d2b72..357ba36 100644
--- a/include/asm-powerpc/dcr-native.h
+++ b/include/asm-powerpc/dcr-native.h
@@ -111,6 +111,18 @@ static inline void __dcri_clrset(int base_addr, int base_data, int reg,
DCRN_ ## base ## _CONFIG_DATA, \
reg, clr, set)
+/* SDR read/write helper macros */
+
+#define DCRN_SDR_CONFIG_ADDR 0x00E
+#define DCRN_SDR_CONFIG_DATA 0x00F
+
+#define SDR_READ(offset) ({ \
+ mtdcr(DCRN_SDR_CONFIG_ADDR, offset); \
+ mfdcr(DCRN_SDR_CONFIG_DATA);})
+#define SDR_WRITE(offset, data) ({ \
+ mtdcr(DCRN_SDR_CONFIG_ADDR, offset); \
+ mtdcr(DCRN_SDR_CONFIG_DATA, data);})
+
#endif /* __ASSEMBLY__ */
#endif /* __KERNEL__ */
#endif /* _ASM_POWERPC_DCR_NATIVE_H */
--
1.5.5
More information about the Linuxppc-embedded
mailing list