From: Jiang Lu <lu.ji...@windriver.com>

Extracted from lsi.patch in lsi_acp_linux_6.8.1.18 tarball.

Add support for the PCIe controller on ACP34xx.

[Jiang: The main modifications include:
        * Use dynamic virtual address for PCIe configuration space.
        * Remove the fixup for the pci bridge.
        * Refactor the setting of dma offset of acp pcie in order not
        to mess up the pci-common.c.
        * Cleanup code's indend issues.]

Signed-off-by: Jiang Lu <lu.ji...@windriver.com>
---
 arch/powerpc/sysdev/ppc4xx_pci.c | 954 ++++++++++++++++++++++++++++++++++++++-
 1 file changed, 951 insertions(+), 3 deletions(-)

diff --git a/arch/powerpc/sysdev/ppc4xx_pci.c b/arch/powerpc/sysdev/ppc4xx_pci.c
index 56e8b3c..b6c022d 100644
--- a/arch/powerpc/sysdev/ppc4xx_pci.c
+++ b/arch/powerpc/sysdev/ppc4xx_pci.c
@@ -34,8 +34,20 @@
 #include <mm/mmu_decl.h>
 
 #include "ppc4xx_pci.h"
+#ifdef CONFIG_ACP
+#include <linux/interrupt.h>
+#include <asm/lsi/acp_ncr.h>
+static int acp_plx;
+#endif
+
+#undef PRINT_CONFIG_ACCESSES
+/*#define PRINT_CONFIG_ACCESSES*/
 
 static int dma_offset_set;
+#ifdef CONFIG_ACP
+static u32 last_mpage;
+static u32 last_port;
+#endif
 
 #define U64_TO_U32_LOW(val)    ((u32)((val) & 0x00000000ffffffffULL))
 #define U64_TO_U32_HIGH(val)   ((u32)((val) >> 32))
@@ -45,6 +57,11 @@ static int dma_offset_set;
 #define RES_TO_U32_HIGH(val)   \
        ((sizeof(resource_size_t) > sizeof(u32)) ? U64_TO_U32_HIGH(val) : (0))
 
+
+#define ACPX1_PCIE_MPAGE_UPPER(n) (0x1010 + (n * 8))
+#define ACPX1_PCIE_MPAGE_LOWER(n) (0x1014 + (n * 8))
+
+
 static inline int ppc440spe_revA(void)
 {
        /* Catch both 440SPe variants, with and without RAID6 support */
@@ -87,7 +104,36 @@ static void fixup_ppc4xx_pci_bridge(struct pci_dev *dev)
        printk(KERN_INFO "PCI: Hiding 4xx host bridge resources %s\n",
               pci_name(dev));
 }
+
+
+static void __init
+fixup_acp_pci_bridge(struct pci_dev *dev)
+{
+       /* if we aren't a PCIe don't bother */
+       if (!pci_find_capability(dev, PCI_CAP_ID_EXP))
+               return ;
+
+       /*
+        * Set the class appropriately for a bridge device
+        */
+       printk(KERN_INFO
+              "PCI: Setting PCI Class to PCI_CLASS_BRIDGE_HOST for 
%04x:%04x\n",
+              dev->vendor, dev->device);
+
+
+       dev->class = PCI_CLASS_BRIDGE_HOST << 8;
+
+       /*
+        * Make the bridge transparent
+        */
+       dev->transparent = 1;
+
+       return ;
+}
+
 DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, fixup_ppc4xx_pci_bridge);
+DECLARE_PCI_FIXUP_HEADER(0x1000, 0x5101, fixup_acp_pci_bridge);
+DECLARE_PCI_FIXUP_HEADER(0x1000, 0x5108, fixup_acp_pci_bridge);
 
 static int __init ppc4xx_parse_dma_ranges(struct pci_controller *hose,
                                          void __iomem *reg,
@@ -340,6 +386,10 @@ static void __init ppc4xx_probe_pci_bridge(struct 
device_node *np)
                       np->full_name);
                return;
        }
+
+       printk(KERN_INFO "%s:%d - %s/0x%llx/0x%llx\n",
+              __FILE__, __LINE__, np->type, rsrc_cfg.start, rsrc_cfg.end);
+
        /* Fetch host bridge internal registers address */
        if (of_address_to_resource(np, 3, &rsrc_reg)) {
                printk(KERN_ERR "%s: Can't get PCI internal register base !",
@@ -646,6 +696,9 @@ struct ppc4xx_pciex_port
        struct resource         cfg_space;
        struct resource         utl_regs;
        void __iomem            *utl_base;
+#ifdef CONFIG_ACP
+       int     acpChipType;
+#endif
 };
 
 static struct ppc4xx_pciex_port *ppc4xx_pciex_ports;
@@ -939,6 +992,34 @@ static struct ppc4xx_pciex_hwops ppc440speB_pcie_hwops 
__initdata =
        .check_link     = ppc4xx_pciex_check_link_sdr,
 };
 
+#if defined(CONFIG_ACP)
+
+static int __init
+acp_pciex_core_init(struct device_node *np)
+{
+       return 3;
+}
+
+static int
+acp_pciex_port_init_hw(struct ppc4xx_pciex_port *port)
+{
+       return 0;
+}
+
+static int
+acp_pciex_setup_utl(struct ppc4xx_pciex_port *port)
+{
+       return 0;
+}
+
+static struct ppc4xx_pciex_hwops acp_pcie_hwops __initdata = {
+       .core_init      = acp_pciex_core_init,
+       .port_init_hw   = acp_pciex_port_init_hw,
+       .setup_utl      = acp_pciex_setup_utl
+};
+
+#endif
+
 static int __init ppc460ex_pciex_core_init(struct device_node *np)
 {
        /* Nothing to do, return 2 ports */
@@ -1419,6 +1500,11 @@ static int __init ppc4xx_pciex_check_core_init(struct 
device_node *np)
        if (core_init++)
                return 0;
 
+#if defined(CONFIG_ACP)
+       if (of_device_is_compatible(np, "lsi,plb-pciex"))
+               ppc4xx_pciex_hwops = &acp_pcie_hwops;
+#endif
+
 #ifdef CONFIG_44x
        if (of_device_is_compatible(np, "ibm,plb-pciex-440spe")) {
                if (ppc440spe_revA())
@@ -1578,9 +1664,11 @@ static int ppc4xx_pciex_validate_bdf(struct 
ppc4xx_pciex_port *port,
            PCI_SLOT(devfn) != 0)
                return PCIBIOS_DEVICE_NOT_FOUND;
 
+#ifndef CONFIG_ACP
        /* Check if we have a link */
        if ((bus->number != port->hose->first_busno) && !port->link)
                return PCIBIOS_DEVICE_NOT_FOUND;
+#endif
 
        return 0;
 }
@@ -1598,10 +1686,72 @@ static void __iomem 
*ppc4xx_pciex_get_config_base(struct ppc4xx_pciex_port *port
                return (void __iomem *)port->hose->cfg_addr;
 
        relbus = bus->number - (port->hose->first_busno + 1);
+
+#ifndef CONFIG_ACP
        return (void __iomem *)port->hose->cfg_data +
                ((relbus  << 20) | (devfn << 12));
+#else
+       {
+       unsigned mpage;
+       u32 addr;
+       int dev, fn;
+       int cfg_type;
+
+       /*
+        * Set MPAGE0 to map config access for this BDF
+        */
+
+       dev = ((devfn & 0xf8) >> 3);
+       fn  = devfn & 0x7;
+
+       if (dev > 31)
+               return NULL;
+
+#ifdef CONFIG_ACP_X1V1
+       /* v1 only supports fn=0 */
+       if (fn)
+               return NULL;
+#else
+       /* v2 only supports fn0-3 and bus0-63 */
+       if (port->acpChipType == 1)
+               if ((fn > 3) || (bus->number > 63))
+                               return NULL;
+#endif
+       if (relbus && (bus->number != bus->primary))
+               cfg_type = 1;
+       else
+               cfg_type = 0;
+
+       /* build the mpage register */
+       mpage = (bus->number << 11) |
+               (dev << 6) |
+               (cfg_type << 5);
+
+       mpage |= 0x10;   /* enable MPAGE for configuration access */
+
+       if (5 > port->acpChipType)
+               mpage |= 1;
+
+       /* the function number moved for X2 */
+       if (port->acpChipType < 2)
+               mpage |= (fn << 17);
+       else
+               mpage |= (fn << 19);
+
+       if ((mpage != last_mpage) || (port->index != last_port)) {
+               addr = ((u32) port->hose->cfg_addr) +
+                      ACPX1_PCIE_MPAGE_LOWER(7);
+               out_le32((u32 *) addr, mpage);
+               last_mpage = mpage;
+               last_port = port->index;
+       }
+
+       return (void __iomem *)port->hose->cfg_data;
+       }
+#endif
 }
 
+#ifndef CONFIG_ACP
 static int ppc4xx_pciex_read_config(struct pci_bus *bus, unsigned int devfn,
                                    int offset, int len, u32 *val)
 {
@@ -1703,10 +1853,227 @@ static int ppc4xx_pciex_write_config(struct pci_bus 
*bus, unsigned int devfn,
        return PCIBIOS_SUCCESSFUL;
 }
 
+#else
+static int
+ppc4xx_pciex_acp_read_config(struct pci_bus *bus, unsigned int devfn,
+                            int offset, int len, u32 *val)
+{
+       struct pci_controller *hose = (struct pci_controller *) bus->sysdata;
+       struct ppc4xx_pciex_port *port =
+               &ppc4xx_pciex_ports[hose->indirect_type];
+       void __iomem *addr;
+       u32 bus_addr;
+       u32 val32;
+       u32 mcsr;
+       int bo = offset & 0x3;
+       int rc = PCIBIOS_SUCCESSFUL;
+
+       BUG_ON(hose != port->hose);
+
+       if (ppc4xx_pciex_validate_bdf(port, bus, devfn) != 0)
+               return PCIBIOS_DEVICE_NOT_FOUND;
+
+       addr = ppc4xx_pciex_get_config_base(port, bus, devfn);
+
+       if (!addr) {
+               *val = 0;
+               return PCIBIOS_DEVICE_NOT_FOUND;
+       }
+
+       /*
+        * Reading from configuration space of non-existing device can
+        * generate transaction errors. For the read duration we suppress
+        * assertion of machine check exceptions to avoid those.
+        */
+       mtmsr(mfmsr() & ~(MSR_ME));
+       __asm__ __volatile__("msync");
+
+       /*
+        * addressing is different for local config access vs.
+        * access through the mapped config space.
+        */
+       if (bus->number == port->hose->first_busno) {
+               int wo = offset & 0xfffffffc;
+               bus_addr = (u32) addr + wo;
+
+       } else {
+
+               /*
+                * mapped config space only supports 32-bit access
+                *
+                *  AXI address [3:0] is not used at all.
+                *  AXI address[9:4] becomes register number.
+                *  AXI address[13:10] becomes Ext. register number
+                *  AXI address[17:14] becomes 1st DWBE
+                *      for configuration read only.
+                *  AXI address[29:27] is used to select
+                *      one of 8 Mpage registers.
+                */
+
+               bus_addr = (u32) addr + (offset << 2);
+
+               switch (len) {
+               case 1:
+                       bus_addr |=  ((1 << bo)) << 14;
+                       break;
+               case 2:
+                       bus_addr |=  ((3 << bo)) << 14;
+                       break;
+               default:
+                       bus_addr |=  (0xf) << 14;
+                       break;
+               }
+
+       }
+
+       /*
+        * do the read
+        */
+
+       val32 = in_le32((u32 *)bus_addr);
+
+       switch (len) {
+       case 1:
+               *val = (val32 >> (bo * 8)) & 0xff;
+               break;
+       case 2:
+               *val = (val32 >> (bo * 8)) & 0xffff;
+               break;
+       default:
+               *val = val32;
+               break;
+       }
+
+       __asm__ __volatile__("msync");
+
+       mcsr = mfspr(SPRN_MCSR);
+       if (mcsr != 0) {
+               mtspr(SPRN_MCSR, 0);
+               __asm__ __volatile__("msync");
+
+#ifdef PRINT_CONFIG_ACCESSES
+               printk(KERN_INFO
+                      "acp_read_config : %3d [%3d..%3d] fn=0x%04x o=0x%04x 
l=%d a=0x%08x machine check!! 0x%08x\n",
+                      bus->number, hose->first_busno, hose->last_busno,
+                      devfn, offset, len, bus_addr, mcsr);
+#endif
+               pr_debug(
+                        "acp_read_config : bus=%3d [%3d..%3d] devfn=0x%04x 
offset=0x%04x len=%d, addr=0x%08x machine check!! 0x%08x\n",
+                        bus->number, hose->first_busno, hose->last_busno,
+                        devfn, offset, len, bus_addr, mcsr);
+               *val = 0;
+               rc =  PCIBIOS_DEVICE_NOT_FOUND;
+       } else {
+#ifdef PRINT_CONFIG_ACCESSES
+               printk(KERN_INFO
+                      "acp_read_config : %3d [%3d..%3d] fn=0x%04x o=0x%04x 
l=%d a=0x%08x v=0x%08x\n",
+                      bus->number, hose->first_busno, hose->last_busno,
+                      devfn, offset, len, bus_addr, *val);
+#endif
+               pr_debug(
+                        "acp_read_config : bus=%3d [%3d..%3d] devfn=0x%04x 
offset=0x%04x len=%d, addr=0x%08x val=0x%08x\n",
+                        bus->number, hose->first_busno, hose->last_busno,
+                        devfn, offset, len, bus_addr, *val);
+       }
+
+       /* re-enable machine checks */
+       mtmsr(mfmsr() | (MSR_ME));
+       __asm__ __volatile__("msync");
+
+       return rc;
+}
+
+static int
+ppc4xx_pciex_acp_write_config(struct pci_bus *bus,
+                             unsigned int devfn,
+                             int offset, int len, u32 val)
+{
+       struct pci_controller *hose = (struct pci_controller *) bus->sysdata;
+       struct ppc4xx_pciex_port *port =
+               &ppc4xx_pciex_ports[hose->indirect_type];
+       void __iomem *addr;
+       u32 bus_addr;
+       u32 val32;
+
+       if (ppc4xx_pciex_validate_bdf(port, bus, devfn) != 0)
+               return PCIBIOS_DEVICE_NOT_FOUND;
+
+       addr = ppc4xx_pciex_get_config_base(port, bus, devfn);
+
+       if (!addr)
+               return PCIBIOS_DEVICE_NOT_FOUND;
+
+       /*
+        * addressing is different for local config access vs.
+        * access through the mapped config space. We need to
+        * translate the offset for mapped config access
+        */
+       if (bus->number == port->hose->first_busno) {
+               /* the local ACP RC only supports 32-bit dword config access,
+                * so if this is a byte or 16-bit word access we need to
+                * perform a read-modify write
+                */
+               if (len == 4) {
+                       bus_addr = (u32) addr + offset;
+               } else {
+                       int bs = ((offset & 0x3) * 8);
+
+                       bus_addr = (u32) addr + (offset & 0xfffffffc);
+                       val32 = in_le32((u32 *)bus_addr);
+
+                       if (len == 2) {
+                               val32 = (val32 & ~(0xffff << bs)) |
+                                       ((val & 0xffff) << bs);
+                       } else {
+                               val32 = (val32 & ~(0xff << bs)) |
+                                       ((val & 0xff) << bs);
+                       }
+
+                       val = val32;
+                       len = 4;
+               }
+
+       } else
+               bus_addr = (u32) addr + (offset << 2) + (offset & 0x3);
+
+#ifdef PRINT_CONFIG_ACCESSES
+       printk(KERN_INFO
+              "acp_write_config: %3d [%3d..%3d] fn=0x%04x o=0x%04x l=%d 
a=0x%08x v=0x%08x\n",
+              bus->number, hose->first_busno, hose->last_busno,
+              devfn, offset, len, bus_addr, val);
+#endif
+       pr_debug(
+                "acp_write_config: bus=%3d [%3d..%3d] devfn=0x%04x 
offset=0x%04x len=%d, addr=0x%08x val=0x%08x\n",
+                bus->number, hose->first_busno, hose->last_busno,
+                devfn, offset, len, bus_addr, val);
+
+
+       switch (len) {
+       case 1:
+               out_8((u8 *)(bus_addr), val);
+               break;
+       case 2:
+               out_le16((u16 *)(bus_addr), val);
+               break;
+       default:
+               out_le32((u32 *)(bus_addr), val);
+               break;
+       }
+
+       return PCIBIOS_SUCCESSFUL;
+}
+
+#endif
+
 static struct pci_ops ppc4xx_pciex_pci_ops =
 {
+#ifndef CONFIG_ACP
        .read  = ppc4xx_pciex_read_config,
        .write = ppc4xx_pciex_write_config,
+#else
+       .read  = ppc4xx_pciex_acp_read_config,
+       .write = ppc4xx_pciex_acp_write_config,
+#endif
 };
 
 static int __init ppc4xx_setup_one_pciex_POM(struct ppc4xx_pciex_port  *port,
@@ -1718,7 +2085,10 @@ static int __init ppc4xx_setup_one_pciex_POM(struct 
ppc4xx_pciex_port    *port,
                                             unsigned int               flags,
                                             int                        index)
 {
-       u32 lah, lal, pciah, pcial, sa;
+       u32 pciah, pcial;
+#ifndef CONFIG_ACP
+       u32 lah, lal, sa;
+#endif
 
        if (!is_power_of_2(size) ||
            (index < 2 && size < 0x100000) ||
@@ -1730,10 +2100,11 @@ static int __init ppc4xx_setup_one_pciex_POM(struct 
ppc4xx_pciex_port   *port,
        }
 
        /* Calculate register values */
-       lah = RES_TO_U32_HIGH(plb_addr);
-       lal = RES_TO_U32_LOW(plb_addr);
        pciah = RES_TO_U32_HIGH(pci_addr);
        pcial = RES_TO_U32_LOW(pci_addr);
+#ifndef CONFIG_ACP
+       lah = RES_TO_U32_HIGH(plb_addr);
+       lal = RES_TO_U32_LOW(plb_addr);
        sa = (0xffffffffu << ilog2(size)) | 0x1;
 
        /* Program register values */
@@ -1780,6 +2151,43 @@ static int __init ppc4xx_setup_one_pciex_POM(struct 
ppc4xx_pciex_port    *port,
                break;
        }
 
+#else
+       {
+               /* ACP X1 setup MPAGE registers */
+
+               int i, num_pages;
+               u32 mpage_lower;
+
+               printk(KERN_INFO
+                      "setting outbound window %d with plb_add=0x%012llx, 
pci_addr=0x%012llx, size=0x%012llx\n",
+                      index, plb_addr, pci_addr, size);
+
+               /*
+                * MPAGE7 is dedicated to config access, so we only
+                *  have 7 128MB pages available for memory i/o.
+                *  Calculate how many pages we need
+                */
+               if (size > (7 * 0x08000000)) {
+                       printk(KERN_WARNING "%s: Resource size 0x%012llx out of 
range\n",
+                       hose->dn->full_name, size);
+                       return -1;
+               }
+
+               num_pages = ((size - 1) >> 27) + 1;
+               for (i = 0; i < num_pages; i++) {
+                       mpage_lower = (pcial & 0xf8000000);
+
+                       if (5 > port->acpChipType)
+                               mpage_lower |= 1;
+
+                       out_le32(mbase + ACPX1_PCIE_MPAGE_UPPER(i), pciah);
+                       out_le32(mbase + ACPX1_PCIE_MPAGE_LOWER(i),
+                                mpage_lower);
+                       pcial += 0x08000000;
+               }
+       }
+#endif
+
        return 0;
 }
 
@@ -1907,6 +2315,112 @@ static void __init ppc4xx_configure_pciex_PIMs(struct 
ppc4xx_pciex_port *port,
                 PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER);
 }
 
+#if defined(CONFIG_ACP)
+
+static void __init
+ppc4xx_configure_acp_pciex_PIMs(struct ppc4xx_pciex_port *port,
+                               struct pci_controller *hose,
+                               void __iomem *mbase,
+                               struct resource *res)
+{
+       resource_size_t size = res->end - res->start + 1;
+       u64 sa;
+       u32 value = 0;
+       void __iomem *tpage_base = mbase + 0x1050;
+
+       if (port->endpoint) {
+               resource_size_t ep_addr = 0;
+               resource_size_t ep_size = 32 << 20;
+
+               /* Currently we map a fixed 64MByte window to PLB address
+                * 0 (SDRAM). This should probably be configurable via a dts
+                * property.
+                */
+
+               /* Calculate window size */
+               sa = (0xffffffffffffffffull << ilog2(ep_size));
+
+               /* TODO: */
+
+               out_le32(mbase + PCI_BASE_ADDRESS_0, RES_TO_U32_LOW(ep_addr));
+               out_le32(mbase + PCI_BASE_ADDRESS_1, RES_TO_U32_HIGH(ep_addr));
+       } else {
+               /* Calculate window size */
+               sa = (0xffffffffffffffffull << ilog2(size));
+
+               if (res->flags & IORESOURCE_PREFETCH)
+                       sa |= 0x8;
+
+               printk(KERN_INFO
+                      "configure inbound mapping from 0x%012llx-0x%012llx "
+                      "(0x%08llx bytes)\n", res->start, res->end, size);
+
+               if (0 != ncr_read(NCP_REGION_ID(0xa, 0x10), 0x2c, 4, &value))
+                       printk(KERN_WARNING "** Unable to get ACP type!\n");
+
+               /*
+                 HACK!! Since PCI legacy support is disabled
+                 in our config, we reusethe isa_mem_size
+                 field to save the size of our inbound
+                 window.  We use this elsewhere to set up the
+                 dma_base.
+               */
+
+               pci_dram_offset = size;
+               hose->dma_window_base_cur = size;
+
+               out_le32(mbase + PCI_BASE_ADDRESS_0, RES_TO_U32_LOW(size));
+               out_le32(mbase + PCI_BASE_ADDRESS_1, RES_TO_U32_HIGH(size));
+
+               if (5 == port->acpChipType) {
+                       printk(KERN_WARNING "Setting SIZE for 2500\n");
+                       out_le32(mbase + 0x11f4, 0xf0000000UL);
+               }
+
+               /*
+                * set up the TPAGE registers
+                *
+                * We set the MSB of each TPAGE to select 128-bit AXI access.
+                * For the address field we simply program an incrementing value
+                * to map consecutive pages
+                */
+               if (acp_plx) {
+                       int i;
+
+                       for (i = 0; i < 8; i++) {
+                               out_le32(tpage_base, (0x80000000 + i));
+                               tpage_base += 4;
+                       }
+               } else {
+                       out_le32(tpage_base, 0x0);  /* tpg 0  0x0, not used */
+                       tpage_base += 4;
+                       out_le32(tpage_base, 0x0);  /* tpg 1  0x0, not used */
+                       tpage_base += 4;
+                       out_le32(tpage_base, 0x0);  /* tpg 2  0x0, not used */
+                       tpage_base += 4;
+                       out_le32(tpage_base, 0x0);  /* tpg 3  0x0, not used */
+                       tpage_base += 4;
+                       out_le32(tpage_base, 0x0);  /* tpg 4  0x0, not used */
+                       tpage_base += 4;
+                       out_le32(tpage_base, 0x0);  /* tpg 5  0x0, not used */
+                       tpage_base += 4;
+                       out_le32(tpage_base, 0x0);  /* tpg 6  0x0 for dyn map */
+                       tpage_base += 4;
+                       /*
+                         tpage 7
+                         point to 0x20,0000,0000
+                         tpage size = 512MB, 32bit AXI bus access
+                       */
+                       out_le32(tpage_base, 0x00000800);
+                       printk(KERN_INFO
+                              "configure inbound mapping tpage 7 to "
+                              "0x00000800\n");
+               }
+       }
+}
+
+#endif
+
 static void __init ppc4xx_pciex_port_setup_hose(struct ppc4xx_pciex_port *port)
 {
        struct resource dma_window;
@@ -2070,6 +2584,435 @@ static void __init ppc4xx_pciex_port_setup_hose(struct 
ppc4xx_pciex_port *port)
                iounmap(mbase);
 }
 
+#if defined(CONFIG_ACP)
+
+static irqreturn_t
+acp_pcie_isr(int irq, void *arg)
+{
+       struct pci_controller *hose = (struct pci_controller *) arg;
+       void __iomem *mbase = (void __iomem *)hose->cfg_addr;
+
+       u32 intr_status;
+       u32 msg_fifo_stat;
+       u32 msg_fifo_info;
+       u8  externalPciIntr = 0;
+
+
+       /* read the PEI interrupt status register */
+       intr_status = in_le32(mbase + 0x10c0);
+       in_le32(mbase + 0x10c4);
+
+       /* check if this is a PCIe message from an external device */
+       if (intr_status & 0x00000010) {
+               externalPciIntr = 1;
+
+               msg_fifo_stat = in_le32(mbase + 0x10b4);
+
+               /* loop until the message fifo is empty */
+               while ((msg_fifo_stat & 0x01) == 0)  {
+                       u8 bus, dev, fn;
+                       u8 msg_type;
+                       msg_fifo_info = in_le32(mbase + 0x10b0);
+
+                       bus = (msg_fifo_info >> 16) & 0xff;
+                       dev = (msg_fifo_info >> 11) & 0x1f;
+                       fn  = (msg_fifo_info >>  8) & 0x07;
+                       msg_type = msg_fifo_info & 0xff;
+
+                       /* print out the BDF and message type.
+                        * We ignore the common message types.
+                        */
+                       switch (msg_type) {
+                       case 0x20:  /*    assert_INTa */
+                       case 0x21:  /*    assert_INTb */
+                       case 0x22:  /*    assert_INTc */
+                       case 0x23:  /*    assert_INTd */
+                       case 0x24:  /* de-assert_INTa */
+                       case 0x25:  /* de-assert_INTb */
+                       case 0x26:  /* de-assert_INTc */
+                       case 0x27:  /* de-assert_INTd */
+                               /* do nothing */
+                               break;
+                       default:
+                               printk(KERN_INFO
+                                      "BDF %02x:%02x.%x sent msgtype 0x%02x\n",
+                                      bus, dev, fn, msg_type);
+                               break;
+                       }
+
+                       /* re-read fifo status */
+                       msg_fifo_stat = in_le32(mbase + 0x10b4);
+               }
+       } else {
+               /*
+                * Ignore the common interrupts, still need to figure out what
+                * they all mean.
+                */
+               if (intr_status & 0xf3ffffab) {
+                       u32 t2a_err_stat;
+                       u32 t2a_other_err_stat;
+                       u32 int_enb;
+                       u32 linkStatus;
+                       u32 offset;
+
+                       printk(KERN_ERR
+                              "ACP_PCIE_ISR: got PEI error interrupt 0x%08x\n",
+                              intr_status);
+
+                       linkStatus = in_le32(mbase + 0x117c);
+                       printk(KERN_ERR
+                              "link_status (0x117c) = 0x%08x\n",
+                              linkStatus);
+
+                       if (intr_status & 0x00020000) {
+                               t2a_err_stat = in_le32(mbase + 0x1170);
+                               printk(KERN_ERR
+                                      "t2a_fn_indp_err_stat = 0x%08x\n",
+                                      t2a_err_stat);
+                               int_enb = in_le32(mbase + 0x10c4);
+                               int_enb &= 0xfffdffff;
+                               out_le32(mbase + 0x10c4, int_enb);
+                       }
+
+                       if (intr_status & 0x00040000) {
+                               t2a_other_err_stat = in_le32(mbase + 0x1174);
+                               printk(KERN_ERR
+                                      "t2a_fn_indp_other_err_stat = 0x%08x\n",
+                                      t2a_other_err_stat);
+                               int_enb = in_le32(mbase + 0x10c4);
+                               int_enb &= 0xfffbffff;
+                               out_le32(mbase + 0x10c4, int_enb);
+                       }
+
+                       if (intr_status & 0x00000800) {
+                               printk(KERN_INFO
+                                      "pci_config = 0x%08x\n",
+                                      in_le32(mbase + 0x1000));
+                               printk(KERN_INFO
+                                      "pci_status = 0x%08x\n",
+                                      in_le32(mbase + 0x1004));
+
+                               int_enb = in_le32(mbase + 0x10c4);
+                               int_enb &= 0xfffff7ff;
+                               out_le32(mbase + 0x10c4, int_enb);
+                       }
+
+                       /*
+                        * dump all the potentially interesting PEI registers
+                        */
+                       for (offset = 0x114c; offset <= 0x1180; offset += 4) {
+                               printk(KERN_INFO
+                                      "  0x%04x : 0x%08x\n",
+                                      offset, in_le32(mbase + offset));
+                       }
+               }
+       }
+
+       /*
+        *  We clear all the interrupts in the PEI status, even though
+        *  interrupts from external devices have not yet been handled.
+        *  That should be okay, since the PCI IRQ in the MPIC won't be
+        *  re-enabled until all external handlers have been called.
+        */
+       out_le32(mbase + 0x10c0, intr_status);
+
+       return externalPciIntr ? IRQ_NONE : IRQ_HANDLED;
+}
+
+static void __init
+ppc4xx_acp_pciex_port_setup_hose(struct ppc4xx_pciex_port *port)
+{
+       struct resource dma_window;
+       struct pci_controller *hose = NULL;
+       const int *bus_range;
+       int primary = 0, busses;
+       void __iomem *mbase = NULL, *cfg_data = NULL;
+       int mappedIrq;
+       int err;
+       u32 pci_status;
+       u32 link_state;
+       u32 pci_config;
+       u32 version;
+
+       /* Check if primary bridge */
+       if (of_get_property(port->node, "primary", NULL))
+               primary = 1;
+
+       /* Get bus range if any */
+       bus_range = of_get_property(port->node, "bus-range", NULL);
+
+       /* Allocate the host controller data structure */
+       hose = pcibios_alloc_controller(port->node);
+       if (!hose)
+               goto fail;
+
+       /* We stick the port number in "indirect_type" so the config space
+        * ops can retrieve the port data structure easily
+        */
+       hose->indirect_type = port->index;
+
+       /* Get bus range */
+       hose->first_busno = bus_range ? bus_range[0] : 0x0;
+       hose->last_busno = bus_range ? bus_range[1] : 0xff;
+
+       /* Because of how big mapping the config space is (1M per bus), we
+        * limit how many busses we support. In the long run, we could replace
+        * that with something akin to kmap_atomic instead. We set aside 1 bus
+        * for the host itself too.
+        */
+       busses = hose->last_busno - hose->first_busno; /* This is off by 1 */
+       if (busses > MAX_PCIE_BUS_MAPPED) {
+               busses = MAX_PCIE_BUS_MAPPED;
+               hose->last_busno = hose->first_busno + busses;
+       }
+
+       if (!port->endpoint) {
+               /*
+                * map the bottom page of PCIe memory for config space access
+               */
+               cfg_data = ioremap(port->cfg_space.start, 0x100000);
+               if (cfg_data == NULL) {
+                       printk(KERN_ERR "%s: Can't map external config space !",
+                              port->node->full_name);
+                       goto fail;
+               }
+               hose->cfg_data = cfg_data;
+       }
+
+       /*
+        * The internal config space has already been mapped, so
+        * just re-use that virtual address.
+        */
+       hose->cfg_addr = port->utl_base;
+
+       pr_debug("PCIE %s, bus %d..%d\n", port->node->full_name,
+                hose->first_busno, hose->last_busno);
+       pr_debug("     config space mapped at: root @0x%p, other @0x%p\n",
+                hose->cfg_addr, hose->cfg_data);
+
+       /* Setup config space */
+       hose->ops = &ppc4xx_pciex_pci_ops;
+       port->hose = hose;
+       mbase = (void __iomem *)hose->cfg_addr;
+
+       if (port->endpoint) {
+               /* if we're an endpoint don't do anything else */
+               printk(KERN_INFO
+                      "PCIE%d: successfully set as endpoint\n",
+                      port->index);
+               return;
+       }
+
+       /* setting up as root complex */
+       pci_config = in_le32(mbase + 0x1000);
+
+       pci_status = in_le32(mbase + 0x1004);
+       link_state = (pci_status & 0x3f00) >> 8;
+       printk("PCIE%d status = 0x%08x : PCI link state = 0x%x\n",
+       port->index, pci_status, link_state);
+
+       /* make sure the ACP device is configured as PCI Root Complex */
+       if ((pci_status & 0x18) != 0x18) {
+               printk(KERN_ERR
+                      "ACP device is not PCI Root Complex! status = 0x%08x\n",
+                      pci_status);
+               goto fail;
+       }
+
+       /* make sure the link is up */
+       if (link_state != 0xb) {
+               /* reset */
+               printk(KERN_WARNING "PCI link in bad state - resetting\n");
+               pci_config |= 1;
+               out_le32(mbase + 0x1000, pci_config);
+               msleep(1000);
+
+               pci_status = in_le32(mbase + 0x1004);
+               link_state = (pci_status & 0x3f00) >> 8;
+
+               printk(KERN_INFO "PCI link state now = 0x%x\n", link_state);
+
+               if (link_state != 0xb) {
+                       printk(KERN_ERR
+                              "PCI link still in bad state - giving up!\n");
+                       goto fail;
+               }
+       }
+
+       /* get the device version */
+       if (0 != ncr_read(NCP_REGION_ID(0x16, 0xff), 0x0, 4, &version)) {
+               printk(KERN_ERR "Unable to detect ACP revision!\n");
+               goto fail;
+       }
+
+       port->acpChipType = (version & 0xff);
+       printk(KERN_INFO "Using PEI register set for ACP chipType %d\n",
+       port->acpChipType);
+
+       /*
+        * Set bus numbers on our root port
+       */
+       out_8(mbase + PCI_PRIMARY_BUS, hose->first_busno);
+       out_8(mbase + PCI_SECONDARY_BUS, hose->first_busno + 1);
+       out_8(mbase + PCI_SUBORDINATE_BUS, hose->last_busno);
+
+
+       /* Parse outbound mapping resources */
+       pci_process_bridge_OF_ranges(hose, port->node, primary);
+
+       /* Parse inbound mapping resources */
+       if (ppc4xx_parse_dma_ranges(hose, mbase, &dma_window) != 0)
+               goto fail;
+
+       /* Configure outbound ranges POMs */
+       ppc4xx_configure_pciex_POMs(port, hose, mbase);
+
+       /* Configure inbound ranges PIMs */
+       ppc4xx_configure_acp_pciex_PIMs(port, hose, mbase, &dma_window);
+
+       /*
+        * hook up an interrupt handler
+       */
+       printk(KERN_INFO "PCIE%d mapping interrupt\n", port->index);
+       mappedIrq = irq_of_parse_and_map(port->node, 0);
+
+       err = request_irq(mappedIrq, acp_pcie_isr,
+                         IRQF_SHARED, "acp_pcie", hose);
+       if (err) {
+               printk(KERN_ERR "request_irq failed!!!!\n");
+               goto fail;
+       }
+
+       /* unmask PEI interrupts */
+       /* for now ignore retry requests, and INT assert/deassert */
+       out_le32(mbase + 0x10c4, 0xf3fffffd);
+
+       if (port->acpChipType == 1) {
+               /*
+                * for v2 we need to set the 'axi_interface_rdy' bit
+                * this bit didn't exist in X1V1, and means something
+                * else for X2...
+               */
+               pci_config = in_le32(mbase + 0x1000);
+               pci_config |= 0x00040000;
+               out_le32(mbase + 0x1000, pci_config);
+       }
+
+       if (!port->endpoint) {
+               printk(KERN_INFO "PCIE%d: successfully set as root-complex\n",
+                      port->index);
+       } else {
+       }
+
+       return;
+ fail:
+       if (hose)
+               pcibios_free_controller(hose);
+       if (cfg_data)
+               iounmap(cfg_data);
+}
+
+static void __init ppc4xx_probe_acp_pciex_bridge(struct device_node *np)
+{
+       struct ppc4xx_pciex_port *port;
+       const u32 *pval;
+       int portno;
+       const char *val;
+       const u32 *field;
+
+       /* First, proceed to core initialization as we assume there's
+        * only one PCIe core in the system
+        */
+       if (ppc4xx_pciex_check_core_init(np))
+               return;
+
+       /* Get the port number from the device-tree */
+       pval = of_get_property(np, "port", NULL);
+       if (pval == NULL) {
+               printk(KERN_ERR "PCIE: Can't find port number for %s\n",
+                      np->full_name);
+               return;
+       }
+       portno = *pval;
+       if (portno >= ppc4xx_pciex_port_count) {
+               printk(KERN_ERR "PCIE: port number out of range for %s\n",
+                      np->full_name);
+               return;
+       }
+       port = &ppc4xx_pciex_ports[portno];
+       port->index = portno;
+
+       /*
+        * Check if device is enabled
+        */
+       if (!of_device_is_available(np)) {
+               printk(KERN_INFO "PCIE%d: Port disabled via device-tree\n",
+                      port->index);
+               return;
+       }
+
+       /* Make sure PCIe is enabled in the device tree. */
+       field = of_get_property(np, "enabled", NULL);
+
+       if (!field || (field && (0 == *field))) {
+               printk(KERN_INFO "%s: Port disabled via device-tree\n",
+                      np->full_name);
+               return;
+       }
+
+       /* Check for the PLX work-around. */
+       field = of_get_property(np, "plx", NULL);
+
+       if (field && (0 != *field))
+               acp_plx = 1;
+       else
+               acp_plx = 0;
+
+       port->node = of_node_get(np);
+
+       /* Check if device_type property is set to "pci" or "pci-endpoint".
+        * Resulting from this setup this PCIe port will be configured
+        * as root-complex or as endpoint.
+        */
+       val = of_get_property(port->node, "device_type", NULL);
+       if (!strcmp(val, "pci-endpoint")) {
+               port->endpoint = 1;
+       } else if (!strcmp(val, "pci")) {
+               port->endpoint = 0;
+       } else {
+               printk(KERN_ERR "PCIE%d: missing or incorrect device_type for 
%s\n",
+                      portno, np->full_name);
+               return;
+       }
+
+       /* Fetch config space registers address */
+       if (of_address_to_resource(np, 0, &port->cfg_space)) {
+               printk(KERN_ERR "%s: Can't get PCI-E config space !",
+                      np->full_name);
+               return;
+       }
+
+       /* Fetch host bridge internal registers address */
+       if (of_address_to_resource(np, 1, &port->utl_regs)) {
+               printk(KERN_ERR "%s: Can't get UTL register base !",
+                      np->full_name);
+               return;
+       }
+
+       port->utl_base = ioremap(port->utl_regs.start,
+               resource_size(&port->utl_regs));
+
+       printk(KERN_INFO
+              "%s PCIE%d config base = 0x%012llx (0x%08x virtual)\n",
+              np->full_name, port->index, port->utl_regs.start,
+              (u32) port->utl_base);
+
+       /* Setup the linux hose data structure */
+       ppc4xx_acp_pciex_port_setup_hose(port);
+}
+
+#endif /* CONFIG_ACP */
+
 static void __init ppc4xx_probe_pciex_bridge(struct device_node *np)
 {
        struct ppc4xx_pciex_port *port;
@@ -2174,6 +3117,11 @@ static int __init ppc4xx_pci_find_bridges(void)
 
        pci_add_flags(PCI_ENABLE_PROC_DOMAINS | PCI_COMPAT_DOMAIN_0);
 
+#if defined(CONFIG_ACP)
+               for_each_compatible_node(np, NULL, "lsi,plb-pciex")
+                       ppc4xx_probe_acp_pciex_bridge(np);
+#endif
+
 #ifdef CONFIG_PPC4xx_PCI_EXPRESS
        for_each_compatible_node(np, NULL, "ibm,plb-pciex")
                ppc4xx_probe_pciex_bridge(np);
-- 
1.8.3

_______________________________________________
linux-yocto mailing list
linux-yocto@yoctoproject.org
https://lists.yoctoproject.org/listinfo/linux-yocto

Reply via email to