diff options
Diffstat (limited to 'arch/powerpc/sysdev/ppc4xx_pci.c')
-rw-r--r-- | arch/powerpc/sysdev/ppc4xx_pci.c | 250 |
1 files changed, 243 insertions, 7 deletions
diff --git a/arch/powerpc/sysdev/ppc4xx_pci.c b/arch/powerpc/sysdev/ppc4xx_pci.c index 6ff9d71b4c0..716b57f1e03 100644 --- a/arch/powerpc/sysdev/ppc4xx_pci.c +++ b/arch/powerpc/sysdev/ppc4xx_pci.c @@ -34,6 +34,8 @@ #include "ppc4xx_pci.h" +int pcie_used; + static int dma_offset_set; #define U64_TO_U32_LOW(val) ((u32)((val) & 0x00000000ffffffffULL)) @@ -44,6 +46,169 @@ static int dma_offset_set; #define RES_TO_U32_HIGH(val) \ ((sizeof(resource_size_t) > sizeof(u32)) ? U64_TO_U32_HIGH(val) : (0)) +#if defined (CONFIG_APM82181) && defined(CONFIG_PCIE_MAX_PAYLOAD_SIZE_256) +static int __init get_pci_exp_ptr(struct pci_controller *hose, int bus, u8 *ptr, int *devfn) +{ + u8 cap_id; + u8 next; + int j; + + u8 devfn_trace[256]; + memset(devfn_trace, 0, 256); + + for (j = 0; j<=0xff; j++){ + if (early_read_config_byte(hose, bus, j, PCI_CAPABILITY_LIST, ptr) < 0) { + printk(KERN_ERR "ppc440_init_pcie:couldn't read Capabliity" + " List Register\n"); + return -1; + } + + if (*ptr != 0xff) + break; + } + + if (*ptr == 0xff) + return 0; + else + *devfn = j; + + while (*ptr){ + if (early_read_config_byte(hose, bus, j, *ptr + PCI_CAP_LIST_ID, &cap_id) < 0) { + printk(KERN_ERR "ppc440_init_pcie:couldn't read" + " Capability ID\n"); + return -1; + } + + if (early_read_config_byte(hose, bus, j, *ptr + PCI_CAP_LIST_NEXT, &next) < 0) { + printk(KERN_ERR "ppc440_init_pcie:couldn't read" + " Capability List Next\n"); + return -1; + } + + if (devfn_trace[*ptr]++){ + printk("<chain looped>\n"); + return -1; + } + if (cap_id == 0xff){ + printk("<chain broken>\n"); + return -1; + } + /* + * We only interested in PCI Express capability + * which have payload size etc + */ + if (cap_id == PCI_CAP_ID_EXP) + return 0; + else{ + *ptr = next; + } + } + + return -1; +} + +void ppc440_config_pcie_mps(struct pci_controller *hose) +{ + int i; + int devfn; + u32 mps; + u32 ecdevcppa; + u32 ecdevctl; + u32 mps_supported; + u8 ptr; + int adjust_mps=1; + + /* + * Check supported max payload size for all devices + * If all devices on this hose supported mps > 256, then we will set the max payload + * size of the root complex and each device's mps to 256bytes + */ + for (i = hose->first_busno +1; i<=hose->last_busno; i++){ + if (get_pci_exp_ptr(hose, i, &ptr, &devfn) < 0){ + printk(KERN_ERR "ppc440_init_pcie:couldn't read Capabliity" + " List Register on bus:%d\n", i); + continue; + } + + if (ptr == 0xff){ + continue; + } + + if (early_read_config_dword(hose, hose->first_busno, 0, 0x00, &ecdevctl)<0){ + printk(KERN_ERR "ppc440_init_pcie:couldn't read Device" + " Control Register\n"); + return; + } + + if (early_read_config_dword(hose, i, devfn, ptr + PCI_EXP_DEVCAP, &mps_supported) < 0) { + printk(KERN_ERR "ppc440_init_pcie:couldn't read Device" + " Capability Register\n"); + return; + } + + if (early_read_config_dword(hose, i, devfn, ptr + PCI_EXP_DEVCTL, &mps) < 0) { + printk(KERN_ERR "ppc440_init_pcie:couldn't read Device" + " Capability Register\n"); + return; + } + printk("Max pay load supported =0x%x max pay load prog 0x%x \n",mps_supported, mps); + if (!(mps_supported & PCI_EXP_DEVCAP_PAYLOAD)){ + adjust_mps = 0; + break; + } + } + + if (adjust_mps){ + /* we already know all devices on this hose have supported mps > 128bytes + * we will set mps for each the devices at 256byte + */ + + printk("hose->first_busno = %x, hose= %x\n",hose->first_busno, (u32)hose); + + if (early_read_config_dword(hose, hose->first_busno, 0, 0x00, &ecdevctl)<0){ + printk(KERN_ERR "ppc440_init_pcie:couldn't read Device" + " Control Register\n"); + return; + } + + /* Set supported MPS and MPS to 256byte for the root complex */ + if (early_read_config_dword(hose, hose->first_busno, 0, PECFG_ECDEVCTL, &ecdevctl)<0){ + printk(KERN_ERR "ppc440_init_pcie:couldn't read Device" + " Control Register\n"); + return; + } + + early_write_config_dword(hose, hose->first_busno, 0, PECFG_ECDEVCTL, (ecdevctl & 0xffffff1f) | 0x00000020); + if (early_read_config_dword(hose, hose->first_busno, 0, PECFG_ECDEVCAPPA, &ecdevcppa)<0){ + printk(KERN_ERR "ppc440_init_pcie:couldn't read Device" + " Capability Register\n"); + return; + } + + early_write_config_dword(hose, hose->first_busno, 0, PECFG_ECDEVCAPPA, (ecdevcppa & 0xfffffff8) | 0x00000001); + + for (i = hose->first_busno +1; i<=hose->last_busno; i++){ + if (get_pci_exp_ptr(hose, i, &ptr, &devfn) < 0){ + printk(KERN_ERR "ppc440_init_pcie:couldn't read Capabliity" + " List Register on bus:%d \n", i); + return; + } + + if (ptr != 0xff) { + + if (early_read_config_dword(hose, i, devfn, ptr + PCI_EXP_DEVCTL, &mps) < 0) { + printk(KERN_ERR "ppc440_init_pcie:couldn't read Device" + " Capability Register\n"); + return; + } + + early_write_config_dword(hose, i, devfn, ptr + PCI_EXP_DEVCTL, (mps & 0xffffff1f) | 0x00000020); + } + } +} +} +#endif + static inline int ppc440spe_revA(void) { /* Catch both 440SPe variants, with and without RAID6 support */ @@ -866,6 +1031,9 @@ static struct ppc4xx_pciex_hwops ppc440speB_pcie_hwops __initdata = static int __init ppc460ex_pciex_core_init(struct device_node *np) { /* Nothing to do, return 2 ports */ +#if defined(CONFIG_APM82181) + return 1; +#endif return 2; } @@ -874,6 +1042,27 @@ static int ppc460ex_pciex_init_port_hw(struct ppc4xx_pciex_port *port) u32 val; u32 utlset1; + /* + * Do a software reset on PCIe ports. + * This code is to fix the issue that pci drivers doesn't re-assign + * bus number for PCIE devices after Uboot + * scanned and configured all the busses (eg. PCIE NIC IntelPro/1000 + * PT quad port, SAS LSI 1064E) + */ + switch (port->index) + { + case 0: + mtdcri(SDR0, PESDR0_460EX_PHY_CTL_RST, 0x0); + mdelay(10); + break; + case 1: + mtdcri(SDR0, PESDR1_460EX_PHY_CTL_RST, 0x0); + mdelay(10); + break; + default: + break; + } + if (port->endpoint) val = PTYPE_LEGACY_ENDPOINT << 20; else @@ -881,7 +1070,15 @@ static int ppc460ex_pciex_init_port_hw(struct ppc4xx_pciex_port *port) if (port->index == 0) { val |= LNKW_X1 << 12; +#if defined(CONFIG_APM82181) +#if defined(CONFIG_PCIE_MAX_PAYLOAD_SIZE_256) + utlset1 = 0x10000000; +#else + utlset1 = 0x00000000; +#endif +#else utlset1 = 0x20000000; +#endif } else { val |= LNKW_X4 << 12; utlset1 = 0x20101101; @@ -889,15 +1086,28 @@ static int ppc460ex_pciex_init_port_hw(struct ppc4xx_pciex_port *port) mtdcri(SDR0, port->sdr_base + PESDRn_DLPSET, val); mtdcri(SDR0, port->sdr_base + PESDRn_UTLSET1, utlset1); +#if defined(CONFIG_APM82181) +#if defined(CONFIG_PCIE_MAX_PAYLOAD_SIZE_256) + mtdcri(SDR0, port->sdr_base + PESDRn_UTLSET2, 0x01110000); +#else + mtdcri(SDR0, port->sdr_base + PESDRn_UTLSET2, 0x01010000); +#endif +#else mtdcri(SDR0, port->sdr_base + PESDRn_UTLSET2, 0x01210000); +#endif switch (port->index) { case 0: mtdcri(SDR0, PESDR0_460EX_L0CDRCTL, 0x00003230); mtdcri(SDR0, PESDR0_460EX_L0DRV, 0x00000130); mtdcri(SDR0, PESDR0_460EX_L0CLK, 0x00000006); - +#if defined(CONFIG_APM82181) mtdcri(SDR0, PESDR0_460EX_PHY_CTL_RST,0x10000000); + mdelay(50); + mtdcri(SDR0, PESDR0_460EX_PHY_CTL_RST,0x30000000); +#else + mtdcri(SDR0, PESDR0_460EX_PHY_CTL_RST,0x10000000); +#endif break; case 1: @@ -953,13 +1163,23 @@ static int ppc460ex_pciex_init_utl(struct ppc4xx_pciex_port *port) * Set buffer allocations and then assert VRB and TXE. */ out_be32(port->utl_base + PEUTL_PBCTL, 0x0800000c); +#if defined (CONFIG_APM82181) && defined(CONFIG_PCIE_MAX_PAYLOAD_SIZE_256) + out_be32(port->utl_base + PEUTL_OUTTR, 0x02000000); + out_be32(port->utl_base + PEUTL_INTR, 0x02000000); + +#else out_be32(port->utl_base + PEUTL_OUTTR, 0x08000000); out_be32(port->utl_base + PEUTL_INTR, 0x02000000); +#endif out_be32(port->utl_base + PEUTL_OPDBSZ, 0x04000000); out_be32(port->utl_base + PEUTL_PBBSZ, 0x00000000); out_be32(port->utl_base + PEUTL_IPHBSZ, 0x02000000); out_be32(port->utl_base + PEUTL_IPDBSZ, 0x04000000); out_be32(port->utl_base + PEUTL_RCIRQEN,0x00f00000); +#if defined (CONFIG_APM82181) && defined(CONFIG_PCIE_MAX_PAYLOAD_SIZE_256) + out_be32(port->utl_base + PEUTL_PCTL, 0x80841066); + udelay(100); +#endif out_be32(port->utl_base + PEUTL_PCTL, 0x80800066); return 0; @@ -974,7 +1194,7 @@ static struct ppc4xx_pciex_hwops ppc460ex_pcie_hwops __initdata = #endif /* CONFIG_44x */ -#ifdef CONFIG_40x +#if defined(CONFIG_40x) static int __init ppc405ex_pciex_core_init(struct device_node *np) { @@ -1088,7 +1308,7 @@ static int __init ppc4xx_pciex_check_core_init(struct device_node *np) if (of_device_is_compatible(np, "ibm,plb-pciex-460ex")) ppc4xx_pciex_hwops = &ppc460ex_pcie_hwops; #endif /* CONFIG_44x */ -#ifdef CONFIG_40x +#if defined(CONFIG_40x) if (of_device_is_compatible(np, "ibm,plb-pciex-405ex")) ppc4xx_pciex_hwops = &ppc405ex_pcie_hwops; #endif @@ -1456,7 +1676,6 @@ static int __init ppc4xx_setup_one_pciex_POM(struct ppc4xx_pciex_port *port, dcr_write(port->dcrs, DCRO_PEGPL_OMR3MSKL, sa | 3); break; } - return 0; } @@ -1561,9 +1780,9 @@ static void __init ppc4xx_configure_pciex_PIMs(struct ppc4xx_pciex_port *port, * if it works */ out_le32(mbase + PECFG_PIM0LAL, 0x00000000); - out_le32(mbase + PECFG_PIM0LAH, 0x00000000); + out_le32(mbase + PECFG_PIM0LAH, 0x00000008); /* Moving on HB*/ out_le32(mbase + PECFG_PIM1LAL, 0x00000000); - out_le32(mbase + PECFG_PIM1LAH, 0x00000000); + out_le32(mbase + PECFG_PIM1LAH, 0x0000000c); /* Moving on HB */ out_le32(mbase + PECFG_PIM01SAH, 0xffff0000); out_le32(mbase + PECFG_PIM01SAL, 0x00000000); @@ -1718,17 +1937,31 @@ static void __init ppc4xx_pciex_port_setup_hose(struct ppc4xx_pciex_port *port) if (!port->endpoint) { /* Set Class Code to PCI-PCI bridge and Revision Id to 1 */ out_le32(mbase + 0x208, 0x06040001); +#ifdef CONFIG_PCI_MSI + printk("Disabling INTX for MSI testing.\n"); + out_be32((mbase + 0x23C), 0x00000000); + out_be32((mbase + 0x4), 0x07040000); + + printk("Setting for 64-bit MSI and 4 MSI messages.\n"); + out_be32((mbase + 0x048),in_be32((mbase + 0x048)) | 0x00a50000); +#endif printk(KERN_INFO "PCIE%d: successfully set as root-complex\n", port->index); } else { /* Set Class Code to Processor/PPC */ out_le32(mbase + 0x208, 0x0b200001); +#ifdef CONFIG_PCI_MSI + printk("Setting for 64-bit MSI and 4 MSI messages.\n"); + out_be32((mbase + 0x048),in_be32((mbase + 0x048)) | 0x00a50000); +#endif printk(KERN_INFO "PCIE%d: successfully set as endpoint\n", port->index); } - +#if defined (CONFIG_APM82181) && defined(CONFIG_PCIE_MAX_PAYLOAD_SIZE_256) + ppc440_config_pcie_mps(hose); +#endif return; fail: if (hose) @@ -1772,8 +2005,11 @@ static void __init ppc4xx_probe_pciex_bridge(struct device_node *np) /* * Check if device is enabled */ + + pcie_used = 1; if (!of_device_is_available(np)) { printk(KERN_INFO "PCIE%d: Port disabled via device-tree\n", port->index); + pcie_used = 0; return; } |