aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc/sysdev/ppc4xx_pci.c
diff options
context:
space:
mode:
authorBenjamin Herrenschmidt <benh@kernel.crashing.org>2007-12-20 23:39:36 -0500
committerJosh Boyer <jwboyer@linux.vnet.ibm.com>2007-12-23 14:18:49 -0500
commit035ee4282dc5ad19f0141821511346b8de1839af (patch)
tree6627691bd5b40363c71a41bed5d4add234b6c348 /arch/powerpc/sysdev/ppc4xx_pci.c
parent5be9419ac613b9e6ce29fbddd0c4429f8d0acc08 (diff)
[POWERPC] 4xx: PCI-E Link setup improvements
This improves the way the 4xx PCI-E code handles checking for a link and adds explicit testing of CRS result codes on config space accesses. This should make it more reliable. Also, bridges with no link are now still created, though config space accesses beyond the root complex are filtered. This is one step toward eventually supporting hotplug. Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org> Signed-off-by: Josh Boyer <jwboyer@linux.vnet.ibm.com>
Diffstat (limited to 'arch/powerpc/sysdev/ppc4xx_pci.c')
-rw-r--r--arch/powerpc/sysdev/ppc4xx_pci.c220
1 files changed, 131 insertions, 89 deletions
diff --git a/arch/powerpc/sysdev/ppc4xx_pci.c b/arch/powerpc/sysdev/ppc4xx_pci.c
index b986eff09f6b..0538980ef89c 100644
--- a/arch/powerpc/sysdev/ppc4xx_pci.c
+++ b/arch/powerpc/sysdev/ppc4xx_pci.c
@@ -16,6 +16,8 @@
16 * 16 *
17 */ 17 */
18 18
19#undef DEBUG
20
19#include <linux/kernel.h> 21#include <linux/kernel.h>
20#include <linux/pci.h> 22#include <linux/pci.h>
21#include <linux/init.h> 23#include <linux/init.h>
@@ -531,10 +533,13 @@ struct ppc4xx_pciex_port
531 struct device_node *node; 533 struct device_node *node;
532 unsigned int index; 534 unsigned int index;
533 int endpoint; 535 int endpoint;
536 int link;
537 int has_ibpre;
534 unsigned int sdr_base; 538 unsigned int sdr_base;
535 dcr_host_t dcrs; 539 dcr_host_t dcrs;
536 struct resource cfg_space; 540 struct resource cfg_space;
537 struct resource utl_regs; 541 struct resource utl_regs;
542 void __iomem *utl_base;
538}; 543};
539 544
540static struct ppc4xx_pciex_port *ppc4xx_pciex_ports; 545static struct ppc4xx_pciex_port *ppc4xx_pciex_ports;
@@ -706,29 +711,44 @@ static int ppc440spe_pciex_init_port_hw(struct ppc4xx_pciex_port *port)
706 return 0; 711 return 0;
707} 712}
708 713
709static int ppc440speA_pciex_init_utl(struct ppc4xx_pciex_port *port) 714static int ppc440speA_pciex_init_port_hw(struct ppc4xx_pciex_port *port)
715{
716 return ppc440spe_pciex_init_port_hw(port);
717}
718
719static int ppc440speB_pciex_init_port_hw(struct ppc4xx_pciex_port *port)
710{ 720{
711 void __iomem *utl_base; 721 int rc = ppc440spe_pciex_init_port_hw(port);
722
723 port->has_ibpre = 1;
724
725 return rc;
726}
712 727
728static int ppc440speA_pciex_init_utl(struct ppc4xx_pciex_port *port)
729{
713 /* XXX Check what that value means... I hate magic */ 730 /* XXX Check what that value means... I hate magic */
714 dcr_write(port->dcrs, DCRO_PEGPL_SPECIAL, 0x68782800); 731 dcr_write(port->dcrs, DCRO_PEGPL_SPECIAL, 0x68782800);
715 732
716 utl_base = ioremap(port->utl_regs.start, 0x100);
717 BUG_ON(utl_base == NULL);
718
719 /* 733 /*
720 * Set buffer allocations and then assert VRB and TXE. 734 * Set buffer allocations and then assert VRB and TXE.
721 */ 735 */
722 out_be32(utl_base + PEUTL_OUTTR, 0x08000000); 736 out_be32(port->utl_base + PEUTL_OUTTR, 0x08000000);
723 out_be32(utl_base + PEUTL_INTR, 0x02000000); 737 out_be32(port->utl_base + PEUTL_INTR, 0x02000000);
724 out_be32(utl_base + PEUTL_OPDBSZ, 0x10000000); 738 out_be32(port->utl_base + PEUTL_OPDBSZ, 0x10000000);
725 out_be32(utl_base + PEUTL_PBBSZ, 0x53000000); 739 out_be32(port->utl_base + PEUTL_PBBSZ, 0x53000000);
726 out_be32(utl_base + PEUTL_IPHBSZ, 0x08000000); 740 out_be32(port->utl_base + PEUTL_IPHBSZ, 0x08000000);
727 out_be32(utl_base + PEUTL_IPDBSZ, 0x10000000); 741 out_be32(port->utl_base + PEUTL_IPDBSZ, 0x10000000);
728 out_be32(utl_base + PEUTL_RCIRQEN, 0x00f00000); 742 out_be32(port->utl_base + PEUTL_RCIRQEN, 0x00f00000);
729 out_be32(utl_base + PEUTL_PCTL, 0x80800066); 743 out_be32(port->utl_base + PEUTL_PCTL, 0x80800066);
730 744
731 iounmap(utl_base); 745 return 0;
746}
747
748static int ppc440speB_pciex_init_utl(struct ppc4xx_pciex_port *port)
749{
750 /* Report CRS to the operating system */
751 out_be32(port->utl_base + PEUTL_PBCTL, 0x08000000);
732 752
733 return 0; 753 return 0;
734} 754}
@@ -736,14 +756,15 @@ static int ppc440speA_pciex_init_utl(struct ppc4xx_pciex_port *port)
736static struct ppc4xx_pciex_hwops ppc440speA_pcie_hwops __initdata = 756static struct ppc4xx_pciex_hwops ppc440speA_pcie_hwops __initdata =
737{ 757{
738 .core_init = ppc440spe_pciex_core_init, 758 .core_init = ppc440spe_pciex_core_init,
739 .port_init_hw = ppc440spe_pciex_init_port_hw, 759 .port_init_hw = ppc440speA_pciex_init_port_hw,
740 .setup_utl = ppc440speA_pciex_init_utl, 760 .setup_utl = ppc440speA_pciex_init_utl,
741}; 761};
742 762
743static struct ppc4xx_pciex_hwops ppc440speB_pcie_hwops __initdata = 763static struct ppc4xx_pciex_hwops ppc440speB_pcie_hwops __initdata =
744{ 764{
745 .core_init = ppc440spe_pciex_core_init, 765 .core_init = ppc440spe_pciex_core_init,
746 .port_init_hw = ppc440spe_pciex_init_port_hw, 766 .port_init_hw = ppc440speB_pciex_init_port_hw,
767 .setup_utl = ppc440speB_pciex_init_utl,
747}; 768};
748 769
749 770
@@ -821,30 +842,21 @@ static int ppc405ex_pciex_init_port_hw(struct ppc4xx_pciex_port *port)
821 842
822static int ppc405ex_pciex_init_utl(struct ppc4xx_pciex_port *port) 843static int ppc405ex_pciex_init_utl(struct ppc4xx_pciex_port *port)
823{ 844{
824 void __iomem *utl_base;
825
826 dcr_write(port->dcrs, DCRO_PEGPL_SPECIAL, 0x0); 845 dcr_write(port->dcrs, DCRO_PEGPL_SPECIAL, 0x0);
827 846
828 utl_base = ioremap(port->utl_regs.start, 0x100);
829 BUG_ON(utl_base == NULL);
830
831 /* 847 /*
832 * Set buffer allocations and then assert VRB and TXE. 848 * Set buffer allocations and then assert VRB and TXE.
833 */ 849 */
834 out_be32(utl_base + PEUTL_OUTTR, 0x02000000); 850 out_be32(port->utl_base + PEUTL_OUTTR, 0x02000000);
835 out_be32(utl_base + PEUTL_INTR, 0x02000000); 851 out_be32(port->utl_base + PEUTL_INTR, 0x02000000);
836 out_be32(utl_base + PEUTL_OPDBSZ, 0x04000000); 852 out_be32(port->utl_base + PEUTL_OPDBSZ, 0x04000000);
837 out_be32(utl_base + PEUTL_PBBSZ, 0x21000000); 853 out_be32(port->utl_base + PEUTL_PBBSZ, 0x21000000);
838 out_be32(utl_base + PEUTL_IPHBSZ, 0x02000000); 854 out_be32(port->utl_base + PEUTL_IPHBSZ, 0x02000000);
839 out_be32(utl_base + PEUTL_IPDBSZ, 0x04000000); 855 out_be32(port->utl_base + PEUTL_IPDBSZ, 0x04000000);
840 out_be32(utl_base + PEUTL_RCIRQEN, 0x00f00000); 856 out_be32(port->utl_base + PEUTL_RCIRQEN, 0x00f00000);
841 out_be32(utl_base + PEUTL_PCTL, 0x80800066); 857 out_be32(port->utl_base + PEUTL_PCTL, 0x80800066);
842 858
843 out_be32(utl_base + PEUTL_PBCTL, 0x0800000c); 859 out_be32(port->utl_base + PEUTL_PBCTL, 0x08000000);
844 out_be32(utl_base + PEUTL_RCSTA,
845 in_be32(utl_base + PEUTL_RCSTA) | 0x000040000);
846
847 iounmap(utl_base);
848 860
849 return 0; 861 return 0;
850} 862}
@@ -926,17 +938,29 @@ static void __init ppc4xx_pciex_port_init_mapping(struct ppc4xx_pciex_port *port
926 dcr_write(port->dcrs, DCRO_PEGPL_MSGMSK, 0); 938 dcr_write(port->dcrs, DCRO_PEGPL_MSGMSK, 0);
927} 939}
928 940
929static int __init ppc4xx_pciex_port_init(struct ppc4xx_pciex_port *port) 941static int __init ppc4xx_pciex_wait_on_sdr(struct ppc4xx_pciex_port *port,
942 unsigned int sdr_offset,
943 unsigned int mask,
944 unsigned int value,
945 int timeout_ms)
930{ 946{
931 int attempts, rc = 0;
932 u32 val; 947 u32 val;
933 948
934 /* Check if it's endpoint or root complex 949 while(timeout_ms--) {
935 * 950 val = mfdcri(SDR0, port->sdr_base + sdr_offset);
936 * XXX Do we want to use the device-tree instead ? --BenH. 951 if ((val & mask) == value) {
937 */ 952 pr_debug("PCIE%d: Wait on SDR %x success with tm %d (%08x)\n",
938 val = mfdcri(SDR0, port->sdr_base + PESDRn_DLPSET); 953 port->index, sdr_offset, timeout_ms, val);
939 port->endpoint = (((val >> 20) & 0xf) != PTYPE_ROOT_PORT); 954 return 0;
955 }
956 msleep(1);
957 }
958 return -1;
959}
960
961static int __init ppc4xx_pciex_port_init(struct ppc4xx_pciex_port *port)
962{
963 int rc = 0;
940 964
941 /* Init HW */ 965 /* Init HW */
942 if (ppc4xx_pciex_hwops->port_init_hw) 966 if (ppc4xx_pciex_hwops->port_init_hw)
@@ -944,44 +968,40 @@ static int __init ppc4xx_pciex_port_init(struct ppc4xx_pciex_port *port)
944 if (rc != 0) 968 if (rc != 0)
945 return rc; 969 return rc;
946 970
947 /* 971 printk(KERN_INFO "PCIE%d: Checking link...\n",
948 * Notice: the following delay has critical impact on device
949 * initialization - if too short (<50ms) the link doesn't get up.
950 *
951 * XXX FIXME: There are various issues with that link up thingy,
952 * we could just wait for the link with a timeout but Stefan says
953 * some cards need more time even after the link is up. I'll
954 * investigate. For now, we keep a fixed 1s delay.
955 *
956 * Ultimately, it should be made asynchronous so all ports are
957 * brought up simultaneously though.
958 */
959 printk(KERN_INFO "PCIE%d: Waiting for link to go up...\n",
960 port->index); 972 port->index);
961 msleep(1000);
962 973
963 /* 974 /* Wait for reset to complete */
964 * Check that we exited the reset state properly 975 if (ppc4xx_pciex_wait_on_sdr(port, PESDRn_RCSSTS, 1 << 20, 0, 10)) {
965 */ 976 printk(KERN_WARNING "PCIE%d: PGRST failed\n",
966 val = mfdcri(SDR0, port->sdr_base + PESDRn_RCSSTS); 977 port->index);
967 if (val & (1 << 20)) {
968 printk(KERN_WARNING "PCIE%d: PGRST failed %08x\n",
969 port->index, val);
970 return -1; 978 return -1;
971 } 979 }
972 980
973 /* 981 /* Check for card presence detect if supported, if not, just wait for
974 * Verify link is up 982 * link unconditionally.
983 *
984 * note that we don't fail if there is no link, we just filter out
985 * config space accesses. That way, it will be easier to implement
986 * hotplug later on.
975 */ 987 */
976 val = mfdcri(SDR0, port->sdr_base + PESDRn_LOOP); 988 if (!port->has_ibpre ||
977 if (!(val & 0x00001000)) { 989 !ppc4xx_pciex_wait_on_sdr(port, PESDRn_LOOP,
978 printk(KERN_INFO "PCIE%d: link is not up !\n", 990 1 << 28, 1 << 28, 100)) {
991 printk(KERN_INFO
992 "PCIE%d: Device detected, waiting for link...\n",
979 port->index); 993 port->index);
980 return -1; 994 if (ppc4xx_pciex_wait_on_sdr(port, PESDRn_LOOP,
981 } 995 0x1000, 0x1000, 2000))
982 996 printk(KERN_WARNING
983 printk(KERN_INFO "PCIE%d: link is up !\n", 997 "PCIE%d: Link up failed\n", port->index);
984 port->index); 998 else {
999 printk(KERN_INFO
1000 "PCIE%d: link is up !\n", port->index);
1001 port->link = 1;
1002 }
1003 } else
1004 printk(KERN_INFO "PCIE%d: No device detected.\n", port->index);
985 1005
986 /* 1006 /*
987 * Initialize mapping: disable all regions and configure 1007 * Initialize mapping: disable all regions and configure
@@ -990,12 +1010,13 @@ static int __init ppc4xx_pciex_port_init(struct ppc4xx_pciex_port *port)
990 ppc4xx_pciex_port_init_mapping(port); 1010 ppc4xx_pciex_port_init_mapping(port);
991 1011
992 /* 1012 /*
993 * Setup UTL registers - but only on revA! 1013 * Map UTL
994 * We use default settings for revB chip. 1014 */
995 * 1015 port->utl_base = ioremap(port->utl_regs.start, 0x100);
996 * To be reworked. We may also be able to move that to 1016 BUG_ON(port->utl_base == NULL);
997 * before the link wait 1017
998 * --BenH. 1018 /*
1019 * Setup UTL registers --BenH.
999 */ 1020 */
1000 if (ppc4xx_pciex_hwops->setup_utl) 1021 if (ppc4xx_pciex_hwops->setup_utl)
1001 ppc4xx_pciex_hwops->setup_utl(port); 1022 ppc4xx_pciex_hwops->setup_utl(port);
@@ -1003,15 +1024,13 @@ static int __init ppc4xx_pciex_port_init(struct ppc4xx_pciex_port *port)
1003 /* 1024 /*
1004 * Check for VC0 active and assert RDY. 1025 * Check for VC0 active and assert RDY.
1005 */ 1026 */
1006 attempts = 10; 1027 if (port->link &&
1007 while (!(mfdcri(SDR0, port->sdr_base + PESDRn_RCSSTS) & (1 << 16))) { 1028 ppc4xx_pciex_wait_on_sdr(port, PESDRn_RCSSTS,
1008 if (!(attempts--)) { 1029 1 << 16, 1 << 16, 5000)) {
1009 printk(KERN_INFO "PCIE%d: VC0 not active\n", 1030 printk(KERN_INFO "PCIE%d: VC0 not active\n", port->index);
1010 port->index); 1031 port->link = 0;
1011 return -1;
1012 }
1013 msleep(1000);
1014 } 1032 }
1033
1015 mtdcri(SDR0, port->sdr_base + PESDRn_RCSSET, 1034 mtdcri(SDR0, port->sdr_base + PESDRn_RCSSET,
1016 mfdcri(SDR0, port->sdr_base + PESDRn_RCSSET) | 1 << 20); 1035 mfdcri(SDR0, port->sdr_base + PESDRn_RCSSET) | 1 << 20);
1017 msleep(100); 1036 msleep(100);
@@ -1048,6 +1067,10 @@ static int ppc4xx_pciex_validate_bdf(struct ppc4xx_pciex_port *port,
1048 PCI_SLOT(devfn) != 0) 1067 PCI_SLOT(devfn) != 0)
1049 return PCIBIOS_DEVICE_NOT_FOUND; 1068 return PCIBIOS_DEVICE_NOT_FOUND;
1050 1069
1070 /* Check if we have a link */
1071 if ((bus->number != port->hose->first_busno) && !port->link)
1072 return PCIBIOS_DEVICE_NOT_FOUND;
1073
1051 return 0; 1074 return 0;
1052} 1075}
1053 1076
@@ -1092,6 +1115,9 @@ static int ppc4xx_pciex_read_config(struct pci_bus *bus, unsigned int devfn,
1092 gpl_cfg = dcr_read(port->dcrs, DCRO_PEGPL_CFG); 1115 gpl_cfg = dcr_read(port->dcrs, DCRO_PEGPL_CFG);
1093 dcr_write(port->dcrs, DCRO_PEGPL_CFG, gpl_cfg | GPL_DMER_MASK_DISA); 1116 dcr_write(port->dcrs, DCRO_PEGPL_CFG, gpl_cfg | GPL_DMER_MASK_DISA);
1094 1117
1118 /* Make sure no CRS is recorded */
1119 out_be32(port->utl_base + PEUTL_RCSTA, 0x00040000);
1120
1095 switch (len) { 1121 switch (len) {
1096 case 1: 1122 case 1:
1097 *val = in_8((u8 *)(addr + offset)); 1123 *val = in_8((u8 *)(addr + offset));
@@ -1109,6 +1135,14 @@ static int ppc4xx_pciex_read_config(struct pci_bus *bus, unsigned int devfn,
1109 bus->number, hose->first_busno, hose->last_busno, 1135 bus->number, hose->first_busno, hose->last_busno,
1110 devfn, offset, len, addr + offset, *val); 1136 devfn, offset, len, addr + offset, *val);
1111 1137
1138 /* Check for CRS (440SPe rev B does that for us but heh ..) */
1139 if (in_be32(port->utl_base + PEUTL_RCSTA) & 0x00040000) {
1140 pr_debug("Got CRS !\n");
1141 if (len != 4 || offset != 0)
1142 return PCIBIOS_DEVICE_NOT_FOUND;
1143 *val = 0xffff0001;
1144 }
1145
1112 dcr_write(port->dcrs, DCRO_PEGPL_CFG, gpl_cfg); 1146 dcr_write(port->dcrs, DCRO_PEGPL_CFG, gpl_cfg);
1113 1147
1114 return PCIBIOS_SUCCESSFUL; 1148 return PCIBIOS_SUCCESSFUL;
@@ -1278,8 +1312,11 @@ static void __init ppc4xx_pciex_port_setup_hose(struct ppc4xx_pciex_port *port)
1278 void __iomem *mbase = NULL, *cfg_data = NULL; 1312 void __iomem *mbase = NULL, *cfg_data = NULL;
1279 1313
1280 /* XXX FIXME: Handle endpoint mode properly */ 1314 /* XXX FIXME: Handle endpoint mode properly */
1281 if (port->endpoint) 1315 if (port->endpoint) {
1316 printk(KERN_WARNING "PCIE%d: Port in endpoint mode !\n",
1317 port->index);
1282 return; 1318 return;
1319 }
1283 1320
1284 /* Check if primary bridge */ 1321 /* Check if primary bridge */
1285 if (of_get_property(port->node, "primary", NULL)) 1322 if (of_get_property(port->node, "primary", NULL))
@@ -1424,6 +1461,9 @@ static void __init ppc4xx_probe_pciex_bridge(struct device_node *np)
1424 } 1461 }
1425 port->sdr_base = *pval; 1462 port->sdr_base = *pval;
1426 1463
1464 /* XXX Currently, we only support root complex mode */
1465 port->endpoint = 0;
1466
1427 /* Fetch config space registers address */ 1467 /* Fetch config space registers address */
1428 if (of_address_to_resource(np, 0, &port->cfg_space)) { 1468 if (of_address_to_resource(np, 0, &port->cfg_space)) {
1429 printk(KERN_ERR "%s: Can't get PCI-E config space !", 1469 printk(KERN_ERR "%s: Can't get PCI-E config space !",
@@ -1447,8 +1487,10 @@ static void __init ppc4xx_probe_pciex_bridge(struct device_node *np)
1447 port->dcrs = dcr_map(np, dcrs, dcr_resource_len(np, 0)); 1487 port->dcrs = dcr_map(np, dcrs, dcr_resource_len(np, 0));
1448 1488
1449 /* Initialize the port specific registers */ 1489 /* Initialize the port specific registers */
1450 if (ppc4xx_pciex_port_init(port)) 1490 if (ppc4xx_pciex_port_init(port)) {
1491 printk(KERN_WARNING "PCIE%d: Port init failed\n", port->index);
1451 return; 1492 return;
1493 }
1452 1494
1453 /* Setup the linux hose data structure */ 1495 /* Setup the linux hose data structure */
1454 ppc4xx_pciex_port_setup_hose(port); 1496 ppc4xx_pciex_port_setup_hose(port);