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