diff options
Diffstat (limited to 'drivers/edac')
-rw-r--r-- | drivers/edac/i7core_edac.c | 326 |
1 files changed, 213 insertions, 113 deletions
diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c index eec0c13c0205..69eacc1b50d7 100644 --- a/drivers/edac/i7core_edac.c +++ b/drivers/edac/i7core_edac.c | |||
@@ -155,6 +155,7 @@ | |||
155 | 155 | ||
156 | #define NUM_CHANS 3 | 156 | #define NUM_CHANS 3 |
157 | #define MAX_DIMMS 3 /* Max DIMMS per channel */ | 157 | #define MAX_DIMMS 3 /* Max DIMMS per channel */ |
158 | #define NUM_SOCKETS 2 /* Max number of MC sockets */ | ||
158 | #define MAX_MCR_FUNC 4 | 159 | #define MAX_MCR_FUNC 4 |
159 | #define MAX_CHAN_FUNC 3 | 160 | #define MAX_CHAN_FUNC 3 |
160 | 161 | ||
@@ -169,6 +170,7 @@ struct i7core_info { | |||
169 | struct i7core_inject { | 170 | struct i7core_inject { |
170 | int enable; | 171 | int enable; |
171 | 172 | ||
173 | u8 socket; | ||
172 | u32 section; | 174 | u32 section; |
173 | u32 type; | 175 | u32 type; |
174 | u32 eccmask; | 176 | u32 eccmask; |
@@ -186,21 +188,25 @@ struct pci_id_descr { | |||
186 | int dev; | 188 | int dev; |
187 | int func; | 189 | int func; |
188 | int dev_id; | 190 | int dev_id; |
189 | struct pci_dev *pdev; | 191 | struct pci_dev *pdev[NUM_SOCKETS]; |
190 | }; | 192 | }; |
191 | 193 | ||
192 | struct i7core_pvt { | 194 | struct i7core_pvt { |
193 | struct pci_dev *pci_noncore; | 195 | struct pci_dev *pci_noncore[NUM_SOCKETS]; |
194 | struct pci_dev *pci_mcr[MAX_MCR_FUNC + 1]; | 196 | struct pci_dev *pci_mcr[NUM_SOCKETS][MAX_MCR_FUNC + 1]; |
195 | struct pci_dev *pci_ch[NUM_CHANS][MAX_CHAN_FUNC + 1]; | 197 | struct pci_dev *pci_ch[NUM_SOCKETS][NUM_CHANS][MAX_CHAN_FUNC + 1]; |
198 | |||
196 | struct i7core_info info; | 199 | struct i7core_info info; |
197 | struct i7core_inject inject; | 200 | struct i7core_inject inject; |
198 | struct i7core_channel channel[NUM_CHANS]; | 201 | struct i7core_channel channel[NUM_SOCKETS][NUM_CHANS]; |
202 | |||
203 | int sockets; /* Number of sockets */ | ||
199 | int channels; /* Number of active channels */ | 204 | int channels; /* Number of active channels */ |
200 | 205 | ||
201 | int ce_count_available; | 206 | int ce_count_available[NUM_SOCKETS]; |
202 | unsigned long ce_count[MAX_DIMMS]; /* ECC corrected errors counts per dimm */ | 207 | /* ECC corrected errors counts per dimm */ |
203 | int last_ce_count[MAX_DIMMS]; | 208 | unsigned long ce_count[NUM_SOCKETS][MAX_DIMMS]; |
209 | int last_ce_count[NUM_SOCKETS][MAX_DIMMS]; | ||
204 | 210 | ||
205 | /* mcelog glue */ | 211 | /* mcelog glue */ |
206 | struct edac_mce edac_mce; | 212 | struct edac_mce edac_mce; |
@@ -324,24 +330,26 @@ static inline int numcol(u32 col) | |||
324 | /**************************************************************************** | 330 | /**************************************************************************** |
325 | Memory check routines | 331 | Memory check routines |
326 | ****************************************************************************/ | 332 | ****************************************************************************/ |
327 | static struct pci_dev *get_pdev_slot_func(int slot, int func) | 333 | static struct pci_dev *get_pdev_slot_func(u8 socket, unsigned slot, |
334 | unsigned func) | ||
328 | { | 335 | { |
329 | int i; | 336 | int i; |
330 | 337 | ||
331 | for (i = 0; i < N_DEVS; i++) { | 338 | for (i = 0; i < N_DEVS; i++) { |
332 | if (!pci_devs[i].pdev) | 339 | if (!pci_devs[i].pdev[socket]) |
333 | continue; | 340 | continue; |
334 | 341 | ||
335 | if (PCI_SLOT(pci_devs[i].pdev->devfn) == slot && | 342 | if (PCI_SLOT(pci_devs[i].pdev[socket]->devfn) == slot && |
336 | PCI_FUNC(pci_devs[i].pdev->devfn) == func) { | 343 | PCI_FUNC(pci_devs[i].pdev[socket]->devfn) == func) { |
337 | return pci_devs[i].pdev; | 344 | return pci_devs[i].pdev[socket]; |
338 | } | 345 | } |
339 | } | 346 | } |
340 | 347 | ||
341 | return NULL; | 348 | return NULL; |
342 | } | 349 | } |
343 | 350 | ||
344 | static int i7core_get_active_channels(int *channels, int *csrows) | 351 | static int i7core_get_active_channels(u8 socket, unsigned *channels, |
352 | unsigned *csrows) | ||
345 | { | 353 | { |
346 | struct pci_dev *pdev = NULL; | 354 | struct pci_dev *pdev = NULL; |
347 | int i, j; | 355 | int i, j; |
@@ -350,9 +358,10 @@ static int i7core_get_active_channels(int *channels, int *csrows) | |||
350 | *channels = 0; | 358 | *channels = 0; |
351 | *csrows = 0; | 359 | *csrows = 0; |
352 | 360 | ||
353 | pdev = get_pdev_slot_func(3, 0); | 361 | pdev = get_pdev_slot_func(socket, 3, 0); |
354 | if (!pdev) { | 362 | if (!pdev) { |
355 | i7core_printk(KERN_ERR, "Couldn't find fn 3.0!!!\n"); | 363 | i7core_printk(KERN_ERR, "Couldn't find socket %d fn 3.0!!!\n", |
364 | socket); | ||
356 | return -ENODEV; | 365 | return -ENODEV; |
357 | } | 366 | } |
358 | 367 | ||
@@ -370,10 +379,11 @@ static int i7core_get_active_channels(int *channels, int *csrows) | |||
370 | if (status & (1 << i)) | 379 | if (status & (1 << i)) |
371 | continue; | 380 | continue; |
372 | 381 | ||
373 | pdev = get_pdev_slot_func(i + 4, 1); | 382 | pdev = get_pdev_slot_func(socket, i + 4, 1); |
374 | if (!pdev) { | 383 | if (!pdev) { |
375 | i7core_printk(KERN_ERR, "Couldn't find fn %d.%d!!!\n", | 384 | i7core_printk(KERN_ERR, "Couldn't find socket %d " |
376 | i + 4, 1); | 385 | "fn %d.%d!!!\n", |
386 | socket, i + 4, 1); | ||
377 | return -ENODEV; | 387 | return -ENODEV; |
378 | } | 388 | } |
379 | /* Devices 4-6 function 1 */ | 389 | /* Devices 4-6 function 1 */ |
@@ -393,12 +403,13 @@ static int i7core_get_active_channels(int *channels, int *csrows) | |||
393 | } | 403 | } |
394 | } | 404 | } |
395 | 405 | ||
396 | debugf0("Number of active channels: %d\n", *channels); | 406 | debugf0("Number of active channels on socked %d: %d\n", |
407 | socket, *channels); | ||
397 | 408 | ||
398 | return 0; | 409 | return 0; |
399 | } | 410 | } |
400 | 411 | ||
401 | static int get_dimm_config(struct mem_ctl_info *mci) | 412 | static int get_dimm_config(struct mem_ctl_info *mci, u8 socket) |
402 | { | 413 | { |
403 | struct i7core_pvt *pvt = mci->pvt_info; | 414 | struct i7core_pvt *pvt = mci->pvt_info; |
404 | struct csrow_info *csr; | 415 | struct csrow_info *csr; |
@@ -409,7 +420,7 @@ static int get_dimm_config(struct mem_ctl_info *mci) | |||
409 | enum mem_type mtype; | 420 | enum mem_type mtype; |
410 | 421 | ||
411 | /* Get data from the MC register, function 0 */ | 422 | /* Get data from the MC register, function 0 */ |
412 | pdev = pvt->pci_mcr[0]; | 423 | pdev = pvt->pci_mcr[socket][0]; |
413 | if (!pdev) | 424 | if (!pdev) |
414 | return -ENODEV; | 425 | return -ENODEV; |
415 | 426 | ||
@@ -458,10 +469,11 @@ static int get_dimm_config(struct mem_ctl_info *mci) | |||
458 | } | 469 | } |
459 | 470 | ||
460 | /* Devices 4-6 function 0 */ | 471 | /* Devices 4-6 function 0 */ |
461 | pci_read_config_dword(pvt->pci_ch[i][0], | 472 | pci_read_config_dword(pvt->pci_ch[socket][i][0], |
462 | MC_CHANNEL_DIMM_INIT_PARAMS, &data); | 473 | MC_CHANNEL_DIMM_INIT_PARAMS, &data); |
463 | 474 | ||
464 | pvt->channel[i].ranks = (data & QUAD_RANK_PRESENT) ? 4 : 2; | 475 | pvt->channel[socket][i].ranks = (data & QUAD_RANK_PRESENT) ? |
476 | 4 : 2; | ||
465 | 477 | ||
466 | if (data & REGISTERED_DIMM) | 478 | if (data & REGISTERED_DIMM) |
467 | mtype = MEM_RDDR3; | 479 | mtype = MEM_RDDR3; |
@@ -477,11 +489,11 @@ static int get_dimm_config(struct mem_ctl_info *mci) | |||
477 | #endif | 489 | #endif |
478 | 490 | ||
479 | /* Devices 4-6 function 1 */ | 491 | /* Devices 4-6 function 1 */ |
480 | pci_read_config_dword(pvt->pci_ch[i][1], | 492 | pci_read_config_dword(pvt->pci_ch[socket][i][1], |
481 | MC_DOD_CH_DIMM0, &dimm_dod[0]); | 493 | MC_DOD_CH_DIMM0, &dimm_dod[0]); |
482 | pci_read_config_dword(pvt->pci_ch[i][1], | 494 | pci_read_config_dword(pvt->pci_ch[socket][i][1], |
483 | MC_DOD_CH_DIMM1, &dimm_dod[1]); | 495 | MC_DOD_CH_DIMM1, &dimm_dod[1]); |
484 | pci_read_config_dword(pvt->pci_ch[i][1], | 496 | pci_read_config_dword(pvt->pci_ch[socket][i][1], |
485 | MC_DOD_CH_DIMM2, &dimm_dod[2]); | 497 | MC_DOD_CH_DIMM2, &dimm_dod[2]); |
486 | 498 | ||
487 | debugf0("Ch%d phy rd%d, wr%d (0x%08x): " | 499 | debugf0("Ch%d phy rd%d, wr%d (0x%08x): " |
@@ -489,7 +501,7 @@ static int get_dimm_config(struct mem_ctl_info *mci) | |||
489 | i, | 501 | i, |
490 | RDLCH(pvt->info.ch_map, i), WRLCH(pvt->info.ch_map, i), | 502 | RDLCH(pvt->info.ch_map, i), WRLCH(pvt->info.ch_map, i), |
491 | data, | 503 | data, |
492 | pvt->channel[i].ranks, | 504 | pvt->channel[socket][i].ranks, |
493 | (data & REGISTERED_DIMM) ? 'R' : 'U'); | 505 | (data & REGISTERED_DIMM) ? 'R' : 'U'); |
494 | 506 | ||
495 | for (j = 0; j < 3; j++) { | 507 | for (j = 0; j < 3; j++) { |
@@ -507,7 +519,7 @@ static int get_dimm_config(struct mem_ctl_info *mci) | |||
507 | /* DDR3 has 8 I/O banks */ | 519 | /* DDR3 has 8 I/O banks */ |
508 | size = (rows * cols * banks * ranks) >> (20 - 3); | 520 | size = (rows * cols * banks * ranks) >> (20 - 3); |
509 | 521 | ||
510 | pvt->channel[i].dimms++; | 522 | pvt->channel[socket][i].dimms++; |
511 | 523 | ||
512 | debugf0("\tdimm %d (0x%08x) %d Mb offset: %x, " | 524 | debugf0("\tdimm %d (0x%08x) %d Mb offset: %x, " |
513 | "numbank: %d,\n\t\t" | 525 | "numbank: %d,\n\t\t" |
@@ -592,16 +604,43 @@ static int disable_inject(struct mem_ctl_info *mci) | |||
592 | 604 | ||
593 | pvt->inject.enable = 0; | 605 | pvt->inject.enable = 0; |
594 | 606 | ||
595 | if (!pvt->pci_ch[pvt->inject.channel][0]) | 607 | if (!pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0]) |
596 | return -ENODEV; | 608 | return -ENODEV; |
597 | 609 | ||
598 | pci_write_config_dword(pvt->pci_ch[pvt->inject.channel][0], | 610 | pci_write_config_dword(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0], |
599 | MC_CHANNEL_ERROR_MASK, 0); | 611 | MC_CHANNEL_ERROR_MASK, 0); |
600 | 612 | ||
601 | return 0; | 613 | return 0; |
602 | } | 614 | } |
603 | 615 | ||
604 | /* | 616 | /* |
617 | * i7core inject inject.socket | ||
618 | * | ||
619 | * accept and store error injection inject.socket value | ||
620 | */ | ||
621 | static ssize_t i7core_inject_socket_store(struct mem_ctl_info *mci, | ||
622 | const char *data, size_t count) | ||
623 | { | ||
624 | struct i7core_pvt *pvt = mci->pvt_info; | ||
625 | unsigned long value; | ||
626 | int rc; | ||
627 | |||
628 | rc = strict_strtoul(data, 10, &value); | ||
629 | if ((rc < 0) || (value > pvt->sockets)) | ||
630 | return 0; | ||
631 | |||
632 | pvt->inject.section = (u32) value; | ||
633 | return count; | ||
634 | } | ||
635 | |||
636 | static ssize_t i7core_inject_socket_show(struct mem_ctl_info *mci, | ||
637 | char *data) | ||
638 | { | ||
639 | struct i7core_pvt *pvt = mci->pvt_info; | ||
640 | return sprintf(data, "%d\n", pvt->inject.socket); | ||
641 | } | ||
642 | |||
643 | /* | ||
605 | * i7core inject inject.section | 644 | * i7core inject inject.section |
606 | * | 645 | * |
607 | * accept and store error injection inject.section value | 646 | * accept and store error injection inject.section value |
@@ -838,7 +877,7 @@ static ssize_t i7core_inject_enable_store(struct mem_ctl_info *mci, | |||
838 | int rc; | 877 | int rc; |
839 | long enable; | 878 | long enable; |
840 | 879 | ||
841 | if (!pvt->pci_ch[pvt->inject.channel][0]) | 880 | if (!pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0]) |
842 | return 0; | 881 | return 0; |
843 | 882 | ||
844 | rc = strict_strtoul(data, 10, &enable); | 883 | rc = strict_strtoul(data, 10, &enable); |
@@ -856,7 +895,7 @@ static ssize_t i7core_inject_enable_store(struct mem_ctl_info *mci, | |||
856 | if (pvt->inject.dimm < 0) | 895 | if (pvt->inject.dimm < 0) |
857 | mask |= 1L << 41; | 896 | mask |= 1L << 41; |
858 | else { | 897 | else { |
859 | if (pvt->channel[pvt->inject.channel].dimms > 2) | 898 | if (pvt->channel[pvt->inject.socket][pvt->inject.channel].dimms > 2) |
860 | mask |= (pvt->inject.dimm & 0x3L) << 35; | 899 | mask |= (pvt->inject.dimm & 0x3L) << 35; |
861 | else | 900 | else |
862 | mask |= (pvt->inject.dimm & 0x1L) << 36; | 901 | mask |= (pvt->inject.dimm & 0x1L) << 36; |
@@ -866,7 +905,7 @@ static ssize_t i7core_inject_enable_store(struct mem_ctl_info *mci, | |||
866 | if (pvt->inject.rank < 0) | 905 | if (pvt->inject.rank < 0) |
867 | mask |= 1L << 40; | 906 | mask |= 1L << 40; |
868 | else { | 907 | else { |
869 | if (pvt->channel[pvt->inject.channel].dimms > 2) | 908 | if (pvt->channel[pvt->inject.socket][pvt->inject.channel].dimms > 2) |
870 | mask |= (pvt->inject.rank & 0x1L) << 34; | 909 | mask |= (pvt->inject.rank & 0x1L) << 34; |
871 | else | 910 | else |
872 | mask |= (pvt->inject.rank & 0x3L) << 34; | 911 | mask |= (pvt->inject.rank & 0x3L) << 34; |
@@ -891,38 +930,41 @@ static ssize_t i7core_inject_enable_store(struct mem_ctl_info *mci, | |||
891 | mask |= (pvt->inject.col & 0x3fffL); | 930 | mask |= (pvt->inject.col & 0x3fffL); |
892 | 931 | ||
893 | /* Unlock writes to registers */ | 932 | /* Unlock writes to registers */ |
894 | pci_write_config_dword(pvt->pci_noncore, MC_CFG_CONTROL, 0x2); | 933 | pci_write_config_dword(pvt->pci_noncore[pvt->inject.socket], |
934 | MC_CFG_CONTROL, 0x2); | ||
895 | msleep(100); | 935 | msleep(100); |
896 | 936 | ||
897 | /* Zeroes error count registers */ | 937 | /* Zeroes error count registers */ |
898 | pci_write_config_dword(pvt->pci_mcr[4], MC_TEST_ERR_RCV1, 0); | 938 | pci_write_config_dword(pvt->pci_mcr[pvt->inject.socket][4], |
899 | pci_write_config_dword(pvt->pci_mcr[4], MC_TEST_ERR_RCV0, 0); | 939 | MC_TEST_ERR_RCV1, 0); |
900 | pvt->ce_count_available = 0; | 940 | pci_write_config_dword(pvt->pci_mcr[pvt->inject.socket][4], |
941 | MC_TEST_ERR_RCV0, 0); | ||
942 | pvt->ce_count_available[pvt->inject.socket] = 0; | ||
901 | 943 | ||
902 | 944 | ||
903 | #if USE_QWORD | 945 | #if USE_QWORD |
904 | pci_write_config_qword(pvt->pci_ch[pvt->inject.channel][0], | 946 | pci_write_config_qword(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0], |
905 | MC_CHANNEL_ADDR_MATCH, mask); | 947 | MC_CHANNEL_ADDR_MATCH, mask); |
906 | #else | 948 | #else |
907 | pci_write_config_dword(pvt->pci_ch[pvt->inject.channel][0], | 949 | pci_write_config_dword(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0], |
908 | MC_CHANNEL_ADDR_MATCH, mask); | 950 | MC_CHANNEL_ADDR_MATCH, mask); |
909 | pci_write_config_dword(pvt->pci_ch[pvt->inject.channel][0], | 951 | pci_write_config_dword(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0], |
910 | MC_CHANNEL_ADDR_MATCH + 4, mask >> 32L); | 952 | MC_CHANNEL_ADDR_MATCH + 4, mask >> 32L); |
911 | #endif | 953 | #endif |
912 | 954 | ||
913 | #if 1 | 955 | #if 1 |
914 | #if USE_QWORD | 956 | #if USE_QWORD |
915 | u64 rdmask; | 957 | u64 rdmask; |
916 | pci_read_config_qword(pvt->pci_ch[pvt->inject.channel][0], | 958 | pci_read_config_qword(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0], |
917 | MC_CHANNEL_ADDR_MATCH, &rdmask); | 959 | MC_CHANNEL_ADDR_MATCH, &rdmask); |
918 | debugf0("Inject addr match write 0x%016llx, read: 0x%016llx\n", | 960 | debugf0("Inject addr match write 0x%016llx, read: 0x%016llx\n", |
919 | mask, rdmask); | 961 | mask, rdmask); |
920 | #else | 962 | #else |
921 | u32 rdmask1, rdmask2; | 963 | u32 rdmask1, rdmask2; |
922 | 964 | ||
923 | pci_read_config_dword(pvt->pci_ch[pvt->inject.channel][0], | 965 | pci_read_config_dword(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0], |
924 | MC_CHANNEL_ADDR_MATCH, &rdmask1); | 966 | MC_CHANNEL_ADDR_MATCH, &rdmask1); |
925 | pci_read_config_dword(pvt->pci_ch[pvt->inject.channel][0], | 967 | pci_read_config_dword(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0], |
926 | MC_CHANNEL_ADDR_MATCH + 4, &rdmask2); | 968 | MC_CHANNEL_ADDR_MATCH + 4, &rdmask2); |
927 | 969 | ||
928 | debugf0("Inject addr match write 0x%016llx, read: 0x%08x 0x%08x\n", | 970 | debugf0("Inject addr match write 0x%016llx, read: 0x%08x 0x%08x\n", |
@@ -930,7 +972,7 @@ static ssize_t i7core_inject_enable_store(struct mem_ctl_info *mci, | |||
930 | #endif | 972 | #endif |
931 | #endif | 973 | #endif |
932 | 974 | ||
933 | pci_write_config_dword(pvt->pci_ch[pvt->inject.channel][0], | 975 | pci_write_config_dword(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0], |
934 | MC_CHANNEL_ERROR_MASK, pvt->inject.eccmask); | 976 | MC_CHANNEL_ERROR_MASK, pvt->inject.eccmask); |
935 | 977 | ||
936 | /* | 978 | /* |
@@ -944,7 +986,7 @@ static ssize_t i7core_inject_enable_store(struct mem_ctl_info *mci, | |||
944 | (pvt->inject.section & 0x3) << 1 | | 986 | (pvt->inject.section & 0x3) << 1 | |
945 | (pvt->inject.type & 0x6) << (3 - 1); | 987 | (pvt->inject.type & 0x6) << (3 - 1); |
946 | 988 | ||
947 | pci_write_config_dword(pvt->pci_ch[pvt->inject.channel][0], | 989 | pci_write_config_dword(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0], |
948 | MC_CHANNEL_ERROR_MASK, injectmask); | 990 | MC_CHANNEL_ERROR_MASK, injectmask); |
949 | 991 | ||
950 | #if 0 | 992 | #if 0 |
@@ -965,7 +1007,7 @@ static ssize_t i7core_inject_enable_show(struct mem_ctl_info *mci, | |||
965 | struct i7core_pvt *pvt = mci->pvt_info; | 1007 | struct i7core_pvt *pvt = mci->pvt_info; |
966 | u32 injectmask; | 1008 | u32 injectmask; |
967 | 1009 | ||
968 | pci_read_config_dword(pvt->pci_ch[pvt->inject.channel][0], | 1010 | pci_read_config_dword(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0], |
969 | MC_CHANNEL_ERROR_MASK, &injectmask); | 1011 | MC_CHANNEL_ERROR_MASK, &injectmask); |
970 | 1012 | ||
971 | debugf0("Inject error read: 0x%018x\n", injectmask); | 1013 | debugf0("Inject error read: 0x%018x\n", injectmask); |
@@ -978,24 +1020,39 @@ static ssize_t i7core_inject_enable_show(struct mem_ctl_info *mci, | |||
978 | 1020 | ||
979 | static ssize_t i7core_ce_regs_show(struct mem_ctl_info *mci, char *data) | 1021 | static ssize_t i7core_ce_regs_show(struct mem_ctl_info *mci, char *data) |
980 | { | 1022 | { |
1023 | unsigned i, count, total = 0; | ||
981 | struct i7core_pvt *pvt = mci->pvt_info; | 1024 | struct i7core_pvt *pvt = mci->pvt_info; |
982 | 1025 | ||
983 | if (!pvt->ce_count_available) | 1026 | for (i = 0; i < pvt->sockets; i++) { |
984 | return sprintf(data, "unavailable\n"); | 1027 | if (!pvt->ce_count_available[i]) |
1028 | count = sprintf(data, "socket 0 data unavailable\n"); | ||
1029 | else | ||
1030 | count = sprintf(data, "socket %d, dimm0: %lu\n" | ||
1031 | "dimm1: %lu\ndimm2: %lu\n", | ||
1032 | i, | ||
1033 | pvt->ce_count[i][0], | ||
1034 | pvt->ce_count[i][1], | ||
1035 | pvt->ce_count[i][2]); | ||
1036 | data += count; | ||
1037 | total += count; | ||
1038 | } | ||
985 | 1039 | ||
986 | return sprintf(data, "dimm0: %lu\ndimm1: %lu\ndimm2: %lu\n", | 1040 | return total; |
987 | pvt->ce_count[0], | ||
988 | pvt->ce_count[1], | ||
989 | pvt->ce_count[2]); | ||
990 | } | 1041 | } |
991 | 1042 | ||
992 | /* | 1043 | /* |
993 | * Sysfs struct | 1044 | * Sysfs struct |
994 | */ | 1045 | */ |
995 | static struct mcidev_sysfs_attribute i7core_inj_attrs[] = { | 1046 | static struct mcidev_sysfs_attribute i7core_inj_attrs[] = { |
996 | |||
997 | { | 1047 | { |
998 | .attr = { | 1048 | .attr = { |
1049 | .name = "inject_socket", | ||
1050 | .mode = (S_IRUGO | S_IWUSR) | ||
1051 | }, | ||
1052 | .show = i7core_inject_socket_show, | ||
1053 | .store = i7core_inject_socket_store, | ||
1054 | }, { | ||
1055 | .attr = { | ||
999 | .name = "inject_section", | 1056 | .name = "inject_section", |
1000 | .mode = (S_IRUGO | S_IWUSR) | 1057 | .mode = (S_IRUGO | S_IWUSR) |
1001 | }, | 1058 | }, |
@@ -1049,10 +1106,11 @@ static struct mcidev_sysfs_attribute i7core_inj_attrs[] = { | |||
1049 | */ | 1106 | */ |
1050 | static void i7core_put_devices(void) | 1107 | static void i7core_put_devices(void) |
1051 | { | 1108 | { |
1052 | int i; | 1109 | int i, j; |
1053 | 1110 | ||
1054 | for (i = 0; i < N_DEVS; i++) | 1111 | for (i = 0; i < NUM_SOCKETS; i++) |
1055 | pci_dev_put(pci_devs[i].pdev); | 1112 | for (j = 0; j < N_DEVS; j++) |
1113 | pci_dev_put(pci_devs[j].pdev[i]); | ||
1056 | } | 1114 | } |
1057 | 1115 | ||
1058 | /* | 1116 | /* |
@@ -1065,6 +1123,8 @@ static int i7core_get_devices(void) | |||
1065 | { | 1123 | { |
1066 | int rc, i; | 1124 | int rc, i; |
1067 | struct pci_dev *pdev = NULL; | 1125 | struct pci_dev *pdev = NULL; |
1126 | u8 bus = 0; | ||
1127 | u8 socket = 0; | ||
1068 | 1128 | ||
1069 | for (i = 0; i < N_DEVS; i++) { | 1129 | for (i = 0; i < N_DEVS; i++) { |
1070 | pdev = pci_get_device(PCI_VENDOR_ID_INTEL, | 1130 | pdev = pci_get_device(PCI_VENDOR_ID_INTEL, |
@@ -1078,14 +1138,32 @@ static int i7core_get_devices(void) | |||
1078 | pci_devs[i].dev_id, NULL); | 1138 | pci_devs[i].dev_id, NULL); |
1079 | } | 1139 | } |
1080 | 1140 | ||
1081 | if (likely(pdev)) | 1141 | if (likely(pdev)) { |
1082 | pci_devs[i].pdev = pdev; | 1142 | bus = pdev->bus->number; |
1083 | else { | 1143 | |
1144 | if (bus == 0x3f) | ||
1145 | socket = 0; | ||
1146 | else | ||
1147 | socket = 255 - bus; | ||
1148 | |||
1149 | if (socket >= NUM_SOCKETS) { | ||
1150 | i7core_printk(KERN_ERR, | ||
1151 | "Found unexpected socket for " | ||
1152 | "dev %02x:%02x.%d PCI ID %04x:%04x\n", | ||
1153 | bus, pci_devs[i].dev, pci_devs[i].func, | ||
1154 | PCI_VENDOR_ID_INTEL, pci_devs[i].dev_id); | ||
1155 | |||
1156 | rc = -ENODEV; | ||
1157 | goto error; | ||
1158 | } | ||
1159 | |||
1160 | pci_devs[i].pdev[socket] = pdev; | ||
1161 | } else { | ||
1084 | i7core_printk(KERN_ERR, | 1162 | i7core_printk(KERN_ERR, |
1085 | "Device not found: PCI ID %04x:%04x " | 1163 | "Device not found: " |
1086 | "(dev %d, func %d)\n", | 1164 | "dev %02x:%02x.%d PCI ID %04x:%04x\n", |
1087 | PCI_VENDOR_ID_INTEL, pci_devs[i].dev_id, | 1165 | bus, pci_devs[i].dev, pci_devs[i].func, |
1088 | pci_devs[i].dev, pci_devs[i].func); | 1166 | PCI_VENDOR_ID_INTEL, pci_devs[i].dev_id); |
1089 | 1167 | ||
1090 | /* Dev 3 function 2 only exists on chips with RDIMMs */ | 1168 | /* Dev 3 function 2 only exists on chips with RDIMMs */ |
1091 | if ((pci_devs[i].dev == 3) && (pci_devs[i].func == 2)) | 1169 | if ((pci_devs[i].dev == 3) && (pci_devs[i].func == 2)) |
@@ -1121,9 +1199,10 @@ static int i7core_get_devices(void) | |||
1121 | } | 1199 | } |
1122 | 1200 | ||
1123 | i7core_printk(KERN_INFO, | 1201 | i7core_printk(KERN_INFO, |
1124 | "Registered device %0x:%0x fn %d.%d\n", | 1202 | "Registered socket %d " |
1125 | PCI_VENDOR_ID_INTEL, pci_devs[i].dev_id, | 1203 | "dev %02x:%02x.%d PCI ID %04x:%04x\n", |
1126 | PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn)); | 1204 | socket, bus, pci_devs[i].dev, pci_devs[i].func, |
1205 | PCI_VENDOR_ID_INTEL, pci_devs[i].dev_id); | ||
1127 | } | 1206 | } |
1128 | 1207 | ||
1129 | return 0; | 1208 | return 0; |
@@ -1137,30 +1216,33 @@ static int mci_bind_devs(struct mem_ctl_info *mci) | |||
1137 | { | 1216 | { |
1138 | struct i7core_pvt *pvt = mci->pvt_info; | 1217 | struct i7core_pvt *pvt = mci->pvt_info; |
1139 | struct pci_dev *pdev; | 1218 | struct pci_dev *pdev; |
1140 | int i, func, slot; | 1219 | int i, j, func, slot; |
1141 | 1220 | ||
1142 | for (i = 0; i < N_DEVS; i++) { | 1221 | for (i = 0; i < pvt->sockets; i++) { |
1143 | pdev = pci_devs[i].pdev; | 1222 | for (j = 0; j < N_DEVS; j++) { |
1144 | if (!pdev) | 1223 | pdev = pci_devs[j].pdev[i]; |
1145 | continue; | 1224 | if (!pdev) |
1225 | continue; | ||
1146 | 1226 | ||
1147 | func = PCI_FUNC(pdev->devfn); | 1227 | func = PCI_FUNC(pdev->devfn); |
1148 | slot = PCI_SLOT(pdev->devfn); | 1228 | slot = PCI_SLOT(pdev->devfn); |
1149 | if (slot == 3) { | 1229 | if (slot == 3) { |
1150 | if (unlikely(func > MAX_MCR_FUNC)) | 1230 | if (unlikely(func > MAX_MCR_FUNC)) |
1151 | goto error; | 1231 | goto error; |
1152 | pvt->pci_mcr[func] = pdev; | 1232 | pvt->pci_mcr[i][func] = pdev; |
1153 | } else if (likely(slot >= 4 && slot < 4 + NUM_CHANS)) { | 1233 | } else if (likely(slot >= 4 && slot < 4 + NUM_CHANS)) { |
1154 | if (unlikely(func > MAX_CHAN_FUNC)) | 1234 | if (unlikely(func > MAX_CHAN_FUNC)) |
1235 | goto error; | ||
1236 | pvt->pci_ch[i][slot - 4][func] = pdev; | ||
1237 | } else if (!slot && !func) | ||
1238 | pvt->pci_noncore[i] = pdev; | ||
1239 | else | ||
1155 | goto error; | 1240 | goto error; |
1156 | pvt->pci_ch[slot - 4][func] = pdev; | ||
1157 | } else if (!slot && !func) | ||
1158 | pvt->pci_noncore = pdev; | ||
1159 | else | ||
1160 | goto error; | ||
1161 | 1241 | ||
1162 | debugf0("Associated fn %d.%d, dev = %p\n", | 1242 | debugf0("Associated fn %d.%d, dev = %p, socket %d\n", |
1163 | PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn), pdev); | 1243 | PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn), |
1244 | pdev, i); | ||
1245 | } | ||
1164 | } | 1246 | } |
1165 | 1247 | ||
1166 | return 0; | 1248 | return 0; |
@@ -1182,20 +1264,20 @@ error: | |||
1182 | * also available at: | 1264 | * also available at: |
1183 | * http://www.arrownac.com/manufacturers/intel/s/nehalem/5500-datasheet-v2.pdf | 1265 | * http://www.arrownac.com/manufacturers/intel/s/nehalem/5500-datasheet-v2.pdf |
1184 | */ | 1266 | */ |
1185 | static void check_mc_test_err(struct mem_ctl_info *mci) | 1267 | static void check_mc_test_err(struct mem_ctl_info *mci, u8 socket) |
1186 | { | 1268 | { |
1187 | struct i7core_pvt *pvt = mci->pvt_info; | 1269 | struct i7core_pvt *pvt = mci->pvt_info; |
1188 | u32 rcv1, rcv0; | 1270 | u32 rcv1, rcv0; |
1189 | int new0, new1, new2; | 1271 | int new0, new1, new2; |
1190 | 1272 | ||
1191 | if (!pvt->pci_mcr[4]) { | 1273 | if (!pvt->pci_mcr[socket][4]) { |
1192 | debugf0("%s MCR registers not found\n",__func__); | 1274 | debugf0("%s MCR registers not found\n",__func__); |
1193 | return; | 1275 | return; |
1194 | } | 1276 | } |
1195 | 1277 | ||
1196 | /* Corrected error reads */ | 1278 | /* Corrected error reads */ |
1197 | pci_read_config_dword(pvt->pci_mcr[4], MC_TEST_ERR_RCV1, &rcv1); | 1279 | pci_read_config_dword(pvt->pci_mcr[socket][4], MC_TEST_ERR_RCV1, &rcv1); |
1198 | pci_read_config_dword(pvt->pci_mcr[4], MC_TEST_ERR_RCV0, &rcv0); | 1280 | pci_read_config_dword(pvt->pci_mcr[socket][4], MC_TEST_ERR_RCV0, &rcv0); |
1199 | 1281 | ||
1200 | /* Store the new values */ | 1282 | /* Store the new values */ |
1201 | new2 = DIMM2_COR_ERR(rcv1); | 1283 | new2 = DIMM2_COR_ERR(rcv1); |
@@ -1209,32 +1291,32 @@ static void check_mc_test_err(struct mem_ctl_info *mci) | |||
1209 | #endif | 1291 | #endif |
1210 | 1292 | ||
1211 | /* Updates CE counters if it is not the first time here */ | 1293 | /* Updates CE counters if it is not the first time here */ |
1212 | if (pvt->ce_count_available) { | 1294 | if (pvt->ce_count_available[socket]) { |
1213 | /* Updates CE counters */ | 1295 | /* Updates CE counters */ |
1214 | int add0, add1, add2; | 1296 | int add0, add1, add2; |
1215 | 1297 | ||
1216 | add2 = new2 - pvt->last_ce_count[2]; | 1298 | add2 = new2 - pvt->last_ce_count[socket][2]; |
1217 | add1 = new1 - pvt->last_ce_count[1]; | 1299 | add1 = new1 - pvt->last_ce_count[socket][1]; |
1218 | add0 = new0 - pvt->last_ce_count[0]; | 1300 | add0 = new0 - pvt->last_ce_count[socket][0]; |
1219 | 1301 | ||
1220 | if (add2 < 0) | 1302 | if (add2 < 0) |
1221 | add2 += 0x7fff; | 1303 | add2 += 0x7fff; |
1222 | pvt->ce_count[2] += add2; | 1304 | pvt->ce_count[socket][2] += add2; |
1223 | 1305 | ||
1224 | if (add1 < 0) | 1306 | if (add1 < 0) |
1225 | add1 += 0x7fff; | 1307 | add1 += 0x7fff; |
1226 | pvt->ce_count[1] += add1; | 1308 | pvt->ce_count[socket][1] += add1; |
1227 | 1309 | ||
1228 | if (add0 < 0) | 1310 | if (add0 < 0) |
1229 | add0 += 0x7fff; | 1311 | add0 += 0x7fff; |
1230 | pvt->ce_count[0] += add0; | 1312 | pvt->ce_count[socket][0] += add0; |
1231 | } else | 1313 | } else |
1232 | pvt->ce_count_available = 1; | 1314 | pvt->ce_count_available[socket] = 1; |
1233 | 1315 | ||
1234 | /* Store the new values */ | 1316 | /* Store the new values */ |
1235 | pvt->last_ce_count[2] = new2; | 1317 | pvt->last_ce_count[socket][2] = new2; |
1236 | pvt->last_ce_count[1] = new1; | 1318 | pvt->last_ce_count[socket][1] = new1; |
1237 | pvt->last_ce_count[0] = new0; | 1319 | pvt->last_ce_count[socket][0] = new0; |
1238 | } | 1320 | } |
1239 | 1321 | ||
1240 | static void i7core_mce_output_error(struct mem_ctl_info *mci, | 1322 | static void i7core_mce_output_error(struct mem_ctl_info *mci, |
@@ -1299,7 +1381,8 @@ static void i7core_check_error(struct mem_ctl_info *mci) | |||
1299 | kfree(m); | 1381 | kfree(m); |
1300 | 1382 | ||
1301 | /* check memory count errors */ | 1383 | /* check memory count errors */ |
1302 | check_mc_test_err(mci); | 1384 | for (i = 0; i < pvt->sockets; i++) |
1385 | check_mc_test_err(mci, i); | ||
1303 | } | 1386 | } |
1304 | 1387 | ||
1305 | /* | 1388 | /* |
@@ -1339,10 +1422,11 @@ static int __devinit i7core_probe(struct pci_dev *pdev, | |||
1339 | { | 1422 | { |
1340 | struct mem_ctl_info *mci; | 1423 | struct mem_ctl_info *mci; |
1341 | struct i7core_pvt *pvt; | 1424 | struct i7core_pvt *pvt; |
1342 | int num_channels; | 1425 | int num_channels = 0; |
1343 | int num_csrows; | 1426 | int num_csrows = 0; |
1344 | int dev_idx = id->driver_data; | 1427 | int dev_idx = id->driver_data; |
1345 | int rc; | 1428 | int rc, i; |
1429 | u8 sockets; | ||
1346 | 1430 | ||
1347 | if (unlikely(dev_idx >= ARRAY_SIZE(i7core_devs))) | 1431 | if (unlikely(dev_idx >= ARRAY_SIZE(i7core_devs))) |
1348 | return -EINVAL; | 1432 | return -EINVAL; |
@@ -1352,10 +1436,25 @@ static int __devinit i7core_probe(struct pci_dev *pdev, | |||
1352 | if (unlikely(rc < 0)) | 1436 | if (unlikely(rc < 0)) |
1353 | return rc; | 1437 | return rc; |
1354 | 1438 | ||
1355 | /* Check the number of active and not disabled channels */ | 1439 | sockets = 1; |
1356 | rc = i7core_get_active_channels(&num_channels, &num_csrows); | 1440 | for (i = NUM_SOCKETS - 1; i > 0; i--) |
1357 | if (unlikely(rc < 0)) | 1441 | if (pci_devs[0].pdev[i]) { |
1358 | goto fail0; | 1442 | sockets = i + 1; |
1443 | break; | ||
1444 | } | ||
1445 | |||
1446 | for (i = 0; i < sockets; i++) { | ||
1447 | int channels; | ||
1448 | int csrows; | ||
1449 | |||
1450 | /* Check the number of active and not disabled channels */ | ||
1451 | rc = i7core_get_active_channels(i, &channels, &csrows); | ||
1452 | if (unlikely(rc < 0)) | ||
1453 | goto fail0; | ||
1454 | |||
1455 | num_channels += channels; | ||
1456 | num_csrows += csrows; | ||
1457 | } | ||
1359 | 1458 | ||
1360 | /* allocate a new MC control structure */ | 1459 | /* allocate a new MC control structure */ |
1361 | mci = edac_mc_alloc(sizeof(*pvt), num_csrows, num_channels, 0); | 1460 | mci = edac_mc_alloc(sizeof(*pvt), num_csrows, num_channels, 0); |
@@ -1367,11 +1466,11 @@ static int __devinit i7core_probe(struct pci_dev *pdev, | |||
1367 | debugf0("MC: " __FILE__ ": %s(): mci = %p\n", __func__, mci); | 1466 | debugf0("MC: " __FILE__ ": %s(): mci = %p\n", __func__, mci); |
1368 | 1467 | ||
1369 | mci->dev = &pdev->dev; /* record ptr to the generic device */ | 1468 | mci->dev = &pdev->dev; /* record ptr to the generic device */ |
1370 | |||
1371 | pvt = mci->pvt_info; | 1469 | pvt = mci->pvt_info; |
1372 | memset(pvt, 0, sizeof(*pvt)); | 1470 | memset(pvt, 0, sizeof(*pvt)); |
1373 | 1471 | pvt->sockets = sockets; | |
1374 | mci->mc_idx = 0; | 1472 | mci->mc_idx = 0; |
1473 | |||
1375 | /* | 1474 | /* |
1376 | * FIXME: how to handle RDDR3 at MCI level? It is possible to have | 1475 | * FIXME: how to handle RDDR3 at MCI level? It is possible to have |
1377 | * Mixed RDDR3/UDDR3 with Nehalem, provided that they are on different | 1476 | * Mixed RDDR3/UDDR3 with Nehalem, provided that they are on different |
@@ -1395,7 +1494,8 @@ static int __devinit i7core_probe(struct pci_dev *pdev, | |||
1395 | goto fail1; | 1494 | goto fail1; |
1396 | 1495 | ||
1397 | /* Get dimm basic config */ | 1496 | /* Get dimm basic config */ |
1398 | get_dimm_config(mci); | 1497 | for (i = 0; i < sockets; i++) |
1498 | get_dimm_config(mci, i); | ||
1399 | 1499 | ||
1400 | /* add this new MC control structure to EDAC's list of MCs */ | 1500 | /* add this new MC control structure to EDAC's list of MCs */ |
1401 | if (unlikely(edac_mc_add_mc(mci))) { | 1501 | if (unlikely(edac_mc_add_mc(mci))) { |