diff options
author | Tony Breeds <tony@bakeyournoodle.com> | 2011-06-30 16:44:24 -0400 |
---|---|---|
committer | Josh Boyer <jwboyer@linux.vnet.ibm.com> | 2011-07-12 09:03:23 -0400 |
commit | 112d1fe9f7715db423ffeec5ac1beccff6093dc4 (patch) | |
tree | ce89fe15078e2d53e65db8654bd21f58fd78b97b /arch | |
parent | af9719c3062dfe216a0c3de3fa52be6d22b4456c (diff) |
powerpc/4xx: Add check_link to struct ppc4xx_pciex_hwops
All current pcie controllers unconditionally use SDR to check the link and
poll for reset. Refactor the code to include device reset in the
port_init_hw() op and add a new check_link() op.
This will make room fro new controllers that do not use SDR for these
operations.
Tested on 460ex.
Signed-off-by: Tony Breeds <tony@bakeyournoodle.com>
Signed-off-by: Josh Boyer <jwboyer@linux.vnet.ibm.com>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/powerpc/sysdev/ppc4xx_pci.c | 148 |
1 files changed, 83 insertions, 65 deletions
diff --git a/arch/powerpc/sysdev/ppc4xx_pci.c b/arch/powerpc/sysdev/ppc4xx_pci.c index 156aa7d36258..ad330fe69c26 100644 --- a/arch/powerpc/sysdev/ppc4xx_pci.c +++ b/arch/powerpc/sysdev/ppc4xx_pci.c | |||
@@ -650,12 +650,75 @@ struct ppc4xx_pciex_hwops | |||
650 | int (*core_init)(struct device_node *np); | 650 | int (*core_init)(struct device_node *np); |
651 | int (*port_init_hw)(struct ppc4xx_pciex_port *port); | 651 | int (*port_init_hw)(struct ppc4xx_pciex_port *port); |
652 | int (*setup_utl)(struct ppc4xx_pciex_port *port); | 652 | int (*setup_utl)(struct ppc4xx_pciex_port *port); |
653 | void (*check_link)(struct ppc4xx_pciex_port *port); | ||
653 | }; | 654 | }; |
654 | 655 | ||
655 | static struct ppc4xx_pciex_hwops *ppc4xx_pciex_hwops; | 656 | static struct ppc4xx_pciex_hwops *ppc4xx_pciex_hwops; |
656 | 657 | ||
657 | #ifdef CONFIG_44x | 658 | #ifdef CONFIG_44x |
658 | 659 | ||
660 | static int __init ppc4xx_pciex_wait_on_sdr(struct ppc4xx_pciex_port *port, | ||
661 | unsigned int sdr_offset, | ||
662 | unsigned int mask, | ||
663 | unsigned int value, | ||
664 | int timeout_ms) | ||
665 | { | ||
666 | u32 val; | ||
667 | |||
668 | while(timeout_ms--) { | ||
669 | val = mfdcri(SDR0, port->sdr_base + sdr_offset); | ||
670 | if ((val & mask) == value) { | ||
671 | pr_debug("PCIE%d: Wait on SDR %x success with tm %d (%08x)\n", | ||
672 | port->index, sdr_offset, timeout_ms, val); | ||
673 | return 0; | ||
674 | } | ||
675 | msleep(1); | ||
676 | } | ||
677 | return -1; | ||
678 | } | ||
679 | |||
680 | static int __init ppc4xx_pciex_port_reset_sdr(struct ppc4xx_pciex_port *port) | ||
681 | { | ||
682 | printk(KERN_INFO "PCIE%d: Checking link...\n", | ||
683 | port->index); | ||
684 | |||
685 | /* Wait for reset to complete */ | ||
686 | if (ppc4xx_pciex_wait_on_sdr(port, PESDRn_RCSSTS, 1 << 20, 0, 10)) { | ||
687 | printk(KERN_WARNING "PCIE%d: PGRST failed\n", | ||
688 | port->index); | ||
689 | return -1; | ||
690 | } | ||
691 | return 0; | ||
692 | } | ||
693 | |||
694 | static void __init ppc4xx_pciex_check_link_sdr(struct ppc4xx_pciex_port *port) | ||
695 | { | ||
696 | /* Check for card presence detect if supported, if not, just wait for | ||
697 | * link unconditionally. | ||
698 | * | ||
699 | * note that we don't fail if there is no link, we just filter out | ||
700 | * config space accesses. That way, it will be easier to implement | ||
701 | * hotplug later on. | ||
702 | */ | ||
703 | if (!port->has_ibpre || | ||
704 | !ppc4xx_pciex_wait_on_sdr(port, PESDRn_LOOP, | ||
705 | 1 << 28, 1 << 28, 100)) { | ||
706 | printk(KERN_INFO | ||
707 | "PCIE%d: Device detected, waiting for link...\n", | ||
708 | port->index); | ||
709 | if (ppc4xx_pciex_wait_on_sdr(port, PESDRn_LOOP, | ||
710 | 0x1000, 0x1000, 2000)) | ||
711 | printk(KERN_WARNING | ||
712 | "PCIE%d: Link up failed\n", port->index); | ||
713 | else { | ||
714 | printk(KERN_INFO | ||
715 | "PCIE%d: link is up !\n", port->index); | ||
716 | port->link = 1; | ||
717 | } | ||
718 | } else | ||
719 | printk(KERN_INFO "PCIE%d: No device detected.\n", port->index); | ||
720 | } | ||
721 | |||
659 | /* Check various reset bits of the 440SPe PCIe core */ | 722 | /* Check various reset bits of the 440SPe PCIe core */ |
660 | static int __init ppc440spe_pciex_check_reset(struct device_node *np) | 723 | static int __init ppc440spe_pciex_check_reset(struct device_node *np) |
661 | { | 724 | { |
@@ -806,7 +869,7 @@ static int ppc440spe_pciex_init_port_hw(struct ppc4xx_pciex_port *port) | |||
806 | dcri_clrset(SDR0, port->sdr_base + PESDRn_RCSSET, | 869 | dcri_clrset(SDR0, port->sdr_base + PESDRn_RCSSET, |
807 | (1 << 24) | (1 << 16), 1 << 12); | 870 | (1 << 24) | (1 << 16), 1 << 12); |
808 | 871 | ||
809 | return 0; | 872 | return ppc4xx_pciex_port_reset_sdr(port); |
810 | } | 873 | } |
811 | 874 | ||
812 | static int ppc440speA_pciex_init_port_hw(struct ppc4xx_pciex_port *port) | 875 | static int ppc440speA_pciex_init_port_hw(struct ppc4xx_pciex_port *port) |
@@ -856,6 +919,7 @@ static struct ppc4xx_pciex_hwops ppc440speA_pcie_hwops __initdata = | |||
856 | .core_init = ppc440spe_pciex_core_init, | 919 | .core_init = ppc440spe_pciex_core_init, |
857 | .port_init_hw = ppc440speA_pciex_init_port_hw, | 920 | .port_init_hw = ppc440speA_pciex_init_port_hw, |
858 | .setup_utl = ppc440speA_pciex_init_utl, | 921 | .setup_utl = ppc440speA_pciex_init_utl, |
922 | .check_link = ppc4xx_pciex_check_link_sdr, | ||
859 | }; | 923 | }; |
860 | 924 | ||
861 | static struct ppc4xx_pciex_hwops ppc440speB_pcie_hwops __initdata = | 925 | static struct ppc4xx_pciex_hwops ppc440speB_pcie_hwops __initdata = |
@@ -863,6 +927,7 @@ static struct ppc4xx_pciex_hwops ppc440speB_pcie_hwops __initdata = | |||
863 | .core_init = ppc440spe_pciex_core_init, | 927 | .core_init = ppc440spe_pciex_core_init, |
864 | .port_init_hw = ppc440speB_pciex_init_port_hw, | 928 | .port_init_hw = ppc440speB_pciex_init_port_hw, |
865 | .setup_utl = ppc440speB_pciex_init_utl, | 929 | .setup_utl = ppc440speB_pciex_init_utl, |
930 | .check_link = ppc4xx_pciex_check_link_sdr, | ||
866 | }; | 931 | }; |
867 | 932 | ||
868 | static int __init ppc460ex_pciex_core_init(struct device_node *np) | 933 | static int __init ppc460ex_pciex_core_init(struct device_node *np) |
@@ -944,7 +1009,7 @@ static int ppc460ex_pciex_init_port_hw(struct ppc4xx_pciex_port *port) | |||
944 | 1009 | ||
945 | port->has_ibpre = 1; | 1010 | port->has_ibpre = 1; |
946 | 1011 | ||
947 | return 0; | 1012 | return ppc4xx_pciex_port_reset_sdr(port); |
948 | } | 1013 | } |
949 | 1014 | ||
950 | static int ppc460ex_pciex_init_utl(struct ppc4xx_pciex_port *port) | 1015 | static int ppc460ex_pciex_init_utl(struct ppc4xx_pciex_port *port) |
@@ -972,6 +1037,7 @@ static struct ppc4xx_pciex_hwops ppc460ex_pcie_hwops __initdata = | |||
972 | .core_init = ppc460ex_pciex_core_init, | 1037 | .core_init = ppc460ex_pciex_core_init, |
973 | .port_init_hw = ppc460ex_pciex_init_port_hw, | 1038 | .port_init_hw = ppc460ex_pciex_init_port_hw, |
974 | .setup_utl = ppc460ex_pciex_init_utl, | 1039 | .setup_utl = ppc460ex_pciex_init_utl, |
1040 | .check_link = ppc4xx_pciex_check_link_sdr, | ||
975 | }; | 1041 | }; |
976 | 1042 | ||
977 | static int __init ppc460sx_pciex_core_init(struct device_node *np) | 1043 | static int __init ppc460sx_pciex_core_init(struct device_node *np) |
@@ -1075,7 +1141,7 @@ static int ppc460sx_pciex_init_port_hw(struct ppc4xx_pciex_port *port) | |||
1075 | 1141 | ||
1076 | port->has_ibpre = 1; | 1142 | port->has_ibpre = 1; |
1077 | 1143 | ||
1078 | return 0; | 1144 | return ppc4xx_pciex_port_reset_sdr(port); |
1079 | } | 1145 | } |
1080 | 1146 | ||
1081 | static int ppc460sx_pciex_init_utl(struct ppc4xx_pciex_port *port) | 1147 | static int ppc460sx_pciex_init_utl(struct ppc4xx_pciex_port *port) |
@@ -1089,6 +1155,7 @@ static struct ppc4xx_pciex_hwops ppc460sx_pcie_hwops __initdata = { | |||
1089 | .core_init = ppc460sx_pciex_core_init, | 1155 | .core_init = ppc460sx_pciex_core_init, |
1090 | .port_init_hw = ppc460sx_pciex_init_port_hw, | 1156 | .port_init_hw = ppc460sx_pciex_init_port_hw, |
1091 | .setup_utl = ppc460sx_pciex_init_utl, | 1157 | .setup_utl = ppc460sx_pciex_init_utl, |
1158 | .check_link = ppc4xx_pciex_check_link_sdr, | ||
1092 | }; | 1159 | }; |
1093 | 1160 | ||
1094 | #endif /* CONFIG_44x */ | 1161 | #endif /* CONFIG_44x */ |
@@ -1154,7 +1221,7 @@ static int ppc405ex_pciex_init_port_hw(struct ppc4xx_pciex_port *port) | |||
1154 | 1221 | ||
1155 | port->has_ibpre = 1; | 1222 | port->has_ibpre = 1; |
1156 | 1223 | ||
1157 | return 0; | 1224 | return ppc4xx_pciex_port_reset_sdr(port); |
1158 | } | 1225 | } |
1159 | 1226 | ||
1160 | static int ppc405ex_pciex_init_utl(struct ppc4xx_pciex_port *port) | 1227 | static int ppc405ex_pciex_init_utl(struct ppc4xx_pciex_port *port) |
@@ -1183,11 +1250,11 @@ static struct ppc4xx_pciex_hwops ppc405ex_pcie_hwops __initdata = | |||
1183 | .core_init = ppc405ex_pciex_core_init, | 1250 | .core_init = ppc405ex_pciex_core_init, |
1184 | .port_init_hw = ppc405ex_pciex_init_port_hw, | 1251 | .port_init_hw = ppc405ex_pciex_init_port_hw, |
1185 | .setup_utl = ppc405ex_pciex_init_utl, | 1252 | .setup_utl = ppc405ex_pciex_init_utl, |
1253 | .check_link = ppc4xx_pciex_check_link_sdr, | ||
1186 | }; | 1254 | }; |
1187 | 1255 | ||
1188 | #endif /* CONFIG_40x */ | 1256 | #endif /* CONFIG_40x */ |
1189 | 1257 | ||
1190 | |||
1191 | /* Check that the core has been initied and if not, do it */ | 1258 | /* Check that the core has been initied and if not, do it */ |
1192 | static int __init ppc4xx_pciex_check_core_init(struct device_node *np) | 1259 | static int __init ppc4xx_pciex_check_core_init(struct device_node *np) |
1193 | { | 1260 | { |
@@ -1261,26 +1328,6 @@ static void __init ppc4xx_pciex_port_init_mapping(struct ppc4xx_pciex_port *port | |||
1261 | dcr_write(port->dcrs, DCRO_PEGPL_MSGMSK, 0); | 1328 | dcr_write(port->dcrs, DCRO_PEGPL_MSGMSK, 0); |
1262 | } | 1329 | } |
1263 | 1330 | ||
1264 | static int __init ppc4xx_pciex_wait_on_sdr(struct ppc4xx_pciex_port *port, | ||
1265 | unsigned int sdr_offset, | ||
1266 | unsigned int mask, | ||
1267 | unsigned int value, | ||
1268 | int timeout_ms) | ||
1269 | { | ||
1270 | u32 val; | ||
1271 | |||
1272 | while(timeout_ms--) { | ||
1273 | val = mfdcri(SDR0, port->sdr_base + sdr_offset); | ||
1274 | if ((val & mask) == value) { | ||
1275 | pr_debug("PCIE%d: Wait on SDR %x success with tm %d (%08x)\n", | ||
1276 | port->index, sdr_offset, timeout_ms, val); | ||
1277 | return 0; | ||
1278 | } | ||
1279 | msleep(1); | ||
1280 | } | ||
1281 | return -1; | ||
1282 | } | ||
1283 | |||
1284 | static int __init ppc4xx_pciex_port_init(struct ppc4xx_pciex_port *port) | 1331 | static int __init ppc4xx_pciex_port_init(struct ppc4xx_pciex_port *port) |
1285 | { | 1332 | { |
1286 | int rc = 0; | 1333 | int rc = 0; |
@@ -1291,40 +1338,8 @@ static int __init ppc4xx_pciex_port_init(struct ppc4xx_pciex_port *port) | |||
1291 | if (rc != 0) | 1338 | if (rc != 0) |
1292 | return rc; | 1339 | return rc; |
1293 | 1340 | ||
1294 | printk(KERN_INFO "PCIE%d: Checking link...\n", | 1341 | if (ppc4xx_pciex_hwops->check_link) |
1295 | port->index); | 1342 | ppc4xx_pciex_hwops->check_link(port); |
1296 | |||
1297 | /* Wait for reset to complete */ | ||
1298 | if (ppc4xx_pciex_wait_on_sdr(port, PESDRn_RCSSTS, 1 << 20, 0, 10)) { | ||
1299 | printk(KERN_WARNING "PCIE%d: PGRST failed\n", | ||
1300 | port->index); | ||
1301 | return -1; | ||
1302 | } | ||
1303 | |||
1304 | /* Check for card presence detect if supported, if not, just wait for | ||
1305 | * link unconditionally. | ||
1306 | * | ||
1307 | * note that we don't fail if there is no link, we just filter out | ||
1308 | * config space accesses. That way, it will be easier to implement | ||
1309 | * hotplug later on. | ||
1310 | */ | ||
1311 | if (!port->has_ibpre || | ||
1312 | !ppc4xx_pciex_wait_on_sdr(port, PESDRn_LOOP, | ||
1313 | 1 << 28, 1 << 28, 100)) { | ||
1314 | printk(KERN_INFO | ||
1315 | "PCIE%d: Device detected, waiting for link...\n", | ||
1316 | port->index); | ||
1317 | if (ppc4xx_pciex_wait_on_sdr(port, PESDRn_LOOP, | ||
1318 | 0x1000, 0x1000, 2000)) | ||
1319 | printk(KERN_WARNING | ||
1320 | "PCIE%d: Link up failed\n", port->index); | ||
1321 | else { | ||
1322 | printk(KERN_INFO | ||
1323 | "PCIE%d: link is up !\n", port->index); | ||
1324 | port->link = 1; | ||
1325 | } | ||
1326 | } else | ||
1327 | printk(KERN_INFO "PCIE%d: No device detected.\n", port->index); | ||
1328 | 1343 | ||
1329 | /* | 1344 | /* |
1330 | * Initialize mapping: disable all regions and configure | 1345 | * Initialize mapping: disable all regions and configure |
@@ -1347,14 +1362,17 @@ static int __init ppc4xx_pciex_port_init(struct ppc4xx_pciex_port *port) | |||
1347 | /* | 1362 | /* |
1348 | * Check for VC0 active and assert RDY. | 1363 | * Check for VC0 active and assert RDY. |
1349 | */ | 1364 | */ |
1350 | if (port->link && | 1365 | if (port->sdr_base) { |
1351 | ppc4xx_pciex_wait_on_sdr(port, PESDRn_RCSSTS, | 1366 | if (port->link && |
1352 | 1 << 16, 1 << 16, 5000)) { | 1367 | ppc4xx_pciex_wait_on_sdr(port, PESDRn_RCSSTS, |
1353 | printk(KERN_INFO "PCIE%d: VC0 not active\n", port->index); | 1368 | 1 << 16, 1 << 16, 5000)) { |
1354 | port->link = 0; | 1369 | printk(KERN_INFO "PCIE%d: VC0 not active\n", port->index); |
1370 | port->link = 0; | ||
1371 | } | ||
1372 | |||
1373 | dcri_clrset(SDR0, port->sdr_base + PESDRn_RCSSET, 0, 1 << 20); | ||
1355 | } | 1374 | } |
1356 | 1375 | ||
1357 | dcri_clrset(SDR0, port->sdr_base + PESDRn_RCSSET, 0, 1 << 20); | ||
1358 | msleep(100); | 1376 | msleep(100); |
1359 | 1377 | ||
1360 | return 0; | 1378 | return 0; |