diff options
author | Mauro Carvalho Chehab <mchehab@redhat.com> | 2012-02-24 07:34:54 -0500 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2012-05-28 18:13:52 -0400 |
commit | 64e1fdaf55d61eb79ae970f78bb00edf6aa696fb (patch) | |
tree | 0b19580b6d34717e750aefb7833d08d369ddb4e6 /drivers/edac/i5000_edac.c | |
parent | 68d086f89b8064d5576f8c1e47fa7ecb4fd6f141 (diff) |
i5000_edac: Fix the logic that retrieves memory information
The logic there is broken: it basically creates two csrows for
each DIMM and assumes that all DIMM's are dual rank. Only one of
the csrows will contain the entire DIMM size. If single rank
memories are found, they'll be marked with 0 bytes.
The check if the AMB is present were also wrong.
Yet, as the error reports don't use the memory size in order to
credit an error to the right DIMM, that part of the driver seems
to work. That's why probably nobody detected the issue yet.
After this patch, the memory layout is now properly reported,
when debug mode is enabled, and the number of ranks per dimm is
now shown:
calculate_dimm_size: ----------------------------------------------------------
calculate_dimm_size: slot 3 0 MB | 0 MB | 0 MB | 0 MB |
calculate_dimm_size: slot 2 0 MB | 0 MB | 0 MB | 0 MB |
calculate_dimm_size: ----------------------------------------------------------
calculate_dimm_size: slot 1 0 MB | 0 MB | 0 MB | 0 MB |
calculate_dimm_size: slot 0 512 MB 1R| 512 MB 1R| 512 MB 1R| 512 MB 1R|
calculate_dimm_size: ----------------------------------------------------------
calculate_dimm_size: channel 0 | channel 1 | channel 2 | channel 3 |
calculate_dimm_size: branch 0 | branch 1 |
(1R above means that all memories on my test machine are single-ranked)
Reviewed-by: Aristeu Rozanski <arozansk@redhat.com>
Cc: Doug Thompson <norsk5@yahoo.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/edac/i5000_edac.c')
-rw-r--r-- | drivers/edac/i5000_edac.c | 150 |
1 files changed, 79 insertions, 71 deletions
diff --git a/drivers/edac/i5000_edac.c b/drivers/edac/i5000_edac.c index 82f3f4d2e7b8..1c1aa7a23c3c 100644 --- a/drivers/edac/i5000_edac.c +++ b/drivers/edac/i5000_edac.c | |||
@@ -270,7 +270,8 @@ | |||
270 | #define MTR3 0x8C | 270 | #define MTR3 0x8C |
271 | 271 | ||
272 | #define NUM_MTRS 4 | 272 | #define NUM_MTRS 4 |
273 | #define CHANNELS_PER_BRANCH (2) | 273 | #define CHANNELS_PER_BRANCH 2 |
274 | #define MAX_BRANCHES 2 | ||
274 | 275 | ||
275 | /* Defines to extract the vaious fields from the | 276 | /* Defines to extract the vaious fields from the |
276 | * MTRx - Memory Technology Registers | 277 | * MTRx - Memory Technology Registers |
@@ -962,14 +963,14 @@ static int determine_amb_present_reg(struct i5000_pvt *pvt, int channel) | |||
962 | * | 963 | * |
963 | * return the proper MTR register as determine by the csrow and channel desired | 964 | * return the proper MTR register as determine by the csrow and channel desired |
964 | */ | 965 | */ |
965 | static int determine_mtr(struct i5000_pvt *pvt, int csrow, int channel) | 966 | static int determine_mtr(struct i5000_pvt *pvt, int slot, int channel) |
966 | { | 967 | { |
967 | int mtr; | 968 | int mtr; |
968 | 969 | ||
969 | if (channel < CHANNELS_PER_BRANCH) | 970 | if (channel < CHANNELS_PER_BRANCH) |
970 | mtr = pvt->b0_mtr[csrow >> 1]; | 971 | mtr = pvt->b0_mtr[slot]; |
971 | else | 972 | else |
972 | mtr = pvt->b1_mtr[csrow >> 1]; | 973 | mtr = pvt->b1_mtr[slot]; |
973 | 974 | ||
974 | return mtr; | 975 | return mtr; |
975 | } | 976 | } |
@@ -994,37 +995,34 @@ static void decode_mtr(int slot_row, u16 mtr) | |||
994 | debugf2("\t\tNUMCOL: %s\n", numcol_toString[MTR_DIMM_COLS(mtr)]); | 995 | debugf2("\t\tNUMCOL: %s\n", numcol_toString[MTR_DIMM_COLS(mtr)]); |
995 | } | 996 | } |
996 | 997 | ||
997 | static void handle_channel(struct i5000_pvt *pvt, int csrow, int channel, | 998 | static void handle_channel(struct i5000_pvt *pvt, int slot, int channel, |
998 | struct i5000_dimm_info *dinfo) | 999 | struct i5000_dimm_info *dinfo) |
999 | { | 1000 | { |
1000 | int mtr; | 1001 | int mtr; |
1001 | int amb_present_reg; | 1002 | int amb_present_reg; |
1002 | int addrBits; | 1003 | int addrBits; |
1003 | 1004 | ||
1004 | mtr = determine_mtr(pvt, csrow, channel); | 1005 | mtr = determine_mtr(pvt, slot, channel); |
1005 | if (MTR_DIMMS_PRESENT(mtr)) { | 1006 | if (MTR_DIMMS_PRESENT(mtr)) { |
1006 | amb_present_reg = determine_amb_present_reg(pvt, channel); | 1007 | amb_present_reg = determine_amb_present_reg(pvt, channel); |
1007 | 1008 | ||
1008 | /* Determine if there is a DIMM present in this DIMM slot */ | 1009 | /* Determine if there is a DIMM present in this DIMM slot */ |
1009 | if (amb_present_reg & (1 << (csrow >> 1))) { | 1010 | if (amb_present_reg) { |
1010 | dinfo->dual_rank = MTR_DIMM_RANK(mtr); | 1011 | dinfo->dual_rank = MTR_DIMM_RANK(mtr); |
1011 | 1012 | ||
1012 | if (!((dinfo->dual_rank == 0) && | 1013 | /* Start with the number of bits for a Bank |
1013 | ((csrow & 0x1) == 0x1))) { | 1014 | * on the DRAM */ |
1014 | /* Start with the number of bits for a Bank | 1015 | addrBits = MTR_DRAM_BANKS_ADDR_BITS(mtr); |
1015 | * on the DRAM */ | 1016 | /* Add the number of ROW bits */ |
1016 | addrBits = MTR_DRAM_BANKS_ADDR_BITS(mtr); | 1017 | addrBits += MTR_DIMM_ROWS_ADDR_BITS(mtr); |
1017 | /* Add thenumber of ROW bits */ | 1018 | /* add the number of COLUMN bits */ |
1018 | addrBits += MTR_DIMM_ROWS_ADDR_BITS(mtr); | 1019 | addrBits += MTR_DIMM_COLS_ADDR_BITS(mtr); |
1019 | /* add the number of COLUMN bits */ | 1020 | |
1020 | addrBits += MTR_DIMM_COLS_ADDR_BITS(mtr); | 1021 | addrBits += 6; /* add 64 bits per DIMM */ |
1021 | 1022 | addrBits -= 20; /* divide by 2^^20 */ | |
1022 | addrBits += 6; /* add 64 bits per DIMM */ | 1023 | addrBits -= 3; /* 8 bits per bytes */ |
1023 | addrBits -= 20; /* divide by 2^^20 */ | 1024 | |
1024 | addrBits -= 3; /* 8 bits per bytes */ | 1025 | dinfo->megabytes = 1 << addrBits; |
1025 | |||
1026 | dinfo->megabytes = 1 << addrBits; | ||
1027 | } | ||
1028 | } | 1026 | } |
1029 | } | 1027 | } |
1030 | } | 1028 | } |
@@ -1038,10 +1036,9 @@ static void handle_channel(struct i5000_pvt *pvt, int csrow, int channel, | |||
1038 | static void calculate_dimm_size(struct i5000_pvt *pvt) | 1036 | static void calculate_dimm_size(struct i5000_pvt *pvt) |
1039 | { | 1037 | { |
1040 | struct i5000_dimm_info *dinfo; | 1038 | struct i5000_dimm_info *dinfo; |
1041 | int csrow, max_csrows; | 1039 | int slot, channel, branch; |
1042 | char *p, *mem_buffer; | 1040 | char *p, *mem_buffer; |
1043 | int space, n; | 1041 | int space, n; |
1044 | int channel; | ||
1045 | 1042 | ||
1046 | /* ================= Generate some debug output ================= */ | 1043 | /* ================= Generate some debug output ================= */ |
1047 | space = PAGE_SIZE; | 1044 | space = PAGE_SIZE; |
@@ -1052,22 +1049,17 @@ static void calculate_dimm_size(struct i5000_pvt *pvt) | |||
1052 | return; | 1049 | return; |
1053 | } | 1050 | } |
1054 | 1051 | ||
1055 | n = snprintf(p, space, "\n"); | 1052 | /* Scan all the actual slots |
1056 | p += n; | ||
1057 | space -= n; | ||
1058 | |||
1059 | /* Scan all the actual CSROWS (which is # of DIMMS * 2) | ||
1060 | * and calculate the information for each DIMM | 1053 | * and calculate the information for each DIMM |
1061 | * Start with the highest csrow first, to display it first | 1054 | * Start with the highest slot first, to display it first |
1062 | * and work toward the 0th csrow | 1055 | * and work toward the 0th slot |
1063 | */ | 1056 | */ |
1064 | max_csrows = pvt->maxdimmperch * 2; | 1057 | for (slot = pvt->maxdimmperch - 1; slot >= 0; slot--) { |
1065 | for (csrow = max_csrows - 1; csrow >= 0; csrow--) { | ||
1066 | 1058 | ||
1067 | /* on an odd csrow, first output a 'boundary' marker, | 1059 | /* on an odd slot, first output a 'boundary' marker, |
1068 | * then reset the message buffer */ | 1060 | * then reset the message buffer */ |
1069 | if (csrow & 0x1) { | 1061 | if (slot & 0x1) { |
1070 | n = snprintf(p, space, "---------------------------" | 1062 | n = snprintf(p, space, "--------------------------" |
1071 | "--------------------------------"); | 1063 | "--------------------------------"); |
1072 | p += n; | 1064 | p += n; |
1073 | space -= n; | 1065 | space -= n; |
@@ -1075,30 +1067,39 @@ static void calculate_dimm_size(struct i5000_pvt *pvt) | |||
1075 | p = mem_buffer; | 1067 | p = mem_buffer; |
1076 | space = PAGE_SIZE; | 1068 | space = PAGE_SIZE; |
1077 | } | 1069 | } |
1078 | n = snprintf(p, space, "csrow %2d ", csrow); | 1070 | n = snprintf(p, space, "slot %2d ", slot); |
1079 | p += n; | 1071 | p += n; |
1080 | space -= n; | 1072 | space -= n; |
1081 | 1073 | ||
1082 | for (channel = 0; channel < pvt->maxch; channel++) { | 1074 | for (channel = 0; channel < pvt->maxch; channel++) { |
1083 | dinfo = &pvt->dimm_info[csrow][channel]; | 1075 | dinfo = &pvt->dimm_info[slot][channel]; |
1084 | handle_channel(pvt, csrow, channel, dinfo); | 1076 | handle_channel(pvt, slot, channel, dinfo); |
1085 | n = snprintf(p, space, "%4d MB | ", dinfo->megabytes); | 1077 | if (dinfo->megabytes) |
1078 | n = snprintf(p, space, "%4d MB %dR| ", | ||
1079 | dinfo->megabytes, dinfo->dual_rank + 1); | ||
1080 | else | ||
1081 | n = snprintf(p, space, "%4d MB | ", 0); | ||
1086 | p += n; | 1082 | p += n; |
1087 | space -= n; | 1083 | space -= n; |
1088 | } | 1084 | } |
1089 | n = snprintf(p, space, "\n"); | ||
1090 | p += n; | 1085 | p += n; |
1091 | space -= n; | 1086 | space -= n; |
1087 | debugf2("%s\n", mem_buffer); | ||
1088 | p = mem_buffer; | ||
1089 | space = PAGE_SIZE; | ||
1092 | } | 1090 | } |
1093 | 1091 | ||
1094 | /* Output the last bottom 'boundary' marker */ | 1092 | /* Output the last bottom 'boundary' marker */ |
1095 | n = snprintf(p, space, "---------------------------" | 1093 | n = snprintf(p, space, "--------------------------" |
1096 | "--------------------------------\n"); | 1094 | "--------------------------------"); |
1097 | p += n; | 1095 | p += n; |
1098 | space -= n; | 1096 | space -= n; |
1097 | debugf2("%s\n", mem_buffer); | ||
1098 | p = mem_buffer; | ||
1099 | space = PAGE_SIZE; | ||
1099 | 1100 | ||
1100 | /* now output the 'channel' labels */ | 1101 | /* now output the 'channel' labels */ |
1101 | n = snprintf(p, space, " "); | 1102 | n = snprintf(p, space, " "); |
1102 | p += n; | 1103 | p += n; |
1103 | space -= n; | 1104 | space -= n; |
1104 | for (channel = 0; channel < pvt->maxch; channel++) { | 1105 | for (channel = 0; channel < pvt->maxch; channel++) { |
@@ -1106,9 +1107,17 @@ static void calculate_dimm_size(struct i5000_pvt *pvt) | |||
1106 | p += n; | 1107 | p += n; |
1107 | space -= n; | 1108 | space -= n; |
1108 | } | 1109 | } |
1109 | n = snprintf(p, space, "\n"); | 1110 | debugf2("%s\n", mem_buffer); |
1111 | p = mem_buffer; | ||
1112 | space = PAGE_SIZE; | ||
1113 | |||
1114 | n = snprintf(p, space, " "); | ||
1110 | p += n; | 1115 | p += n; |
1111 | space -= n; | 1116 | for (branch = 0; branch < MAX_BRANCHES; branch++) { |
1117 | n = snprintf(p, space, " branch %d | ", branch); | ||
1118 | p += n; | ||
1119 | space -= n; | ||
1120 | } | ||
1112 | 1121 | ||
1113 | /* output the last message and free buffer */ | 1122 | /* output the last message and free buffer */ |
1114 | debugf2("%s\n", mem_buffer); | 1123 | debugf2("%s\n", mem_buffer); |
@@ -1241,14 +1250,13 @@ static void i5000_get_mc_regs(struct mem_ctl_info *mci) | |||
1241 | static int i5000_init_csrows(struct mem_ctl_info *mci) | 1250 | static int i5000_init_csrows(struct mem_ctl_info *mci) |
1242 | { | 1251 | { |
1243 | struct i5000_pvt *pvt; | 1252 | struct i5000_pvt *pvt; |
1244 | struct csrow_info *p_csrow; | ||
1245 | struct dimm_info *dimm; | 1253 | struct dimm_info *dimm; |
1246 | int empty, channel_count; | 1254 | int empty, channel_count; |
1247 | int max_csrows; | 1255 | int max_csrows; |
1248 | int mtr, mtr1; | 1256 | int mtr; |
1249 | int csrow_megs; | 1257 | int csrow_megs; |
1250 | int channel; | 1258 | int channel; |
1251 | int csrow; | 1259 | int slot; |
1252 | 1260 | ||
1253 | pvt = mci->pvt_info; | 1261 | pvt = mci->pvt_info; |
1254 | 1262 | ||
@@ -1258,26 +1266,25 @@ static int i5000_init_csrows(struct mem_ctl_info *mci) | |||
1258 | empty = 1; /* Assume NO memory */ | 1266 | empty = 1; /* Assume NO memory */ |
1259 | 1267 | ||
1260 | /* | 1268 | /* |
1261 | * TODO: it would be better to not use csrow here, filling | 1269 | * FIXME: The memory layout used to map slot/channel into the |
1262 | * directly the dimm_info structs, based on branch, channel, dim number | 1270 | * real memory architecture is weird: branch+slot are "csrows" |
1271 | * and channel is channel. That required an extra array (dimm_info) | ||
1272 | * to map the dimms. A good cleanup would be to remove this array, | ||
1273 | * and do a loop here with branch, channel, slot | ||
1263 | */ | 1274 | */ |
1264 | for (csrow = 0; csrow < max_csrows; csrow++) { | 1275 | for (slot = 0; slot < max_csrows; slot++) { |
1265 | p_csrow = &mci->csrows[csrow]; | 1276 | for (channel = 0; channel < pvt->maxch; channel++) { |
1266 | 1277 | ||
1267 | p_csrow->csrow_idx = csrow; | 1278 | mtr = determine_mtr(pvt, slot, channel); |
1268 | 1279 | ||
1269 | /* use branch 0 for the basis */ | 1280 | if (!MTR_DIMMS_PRESENT(mtr)) |
1270 | mtr = pvt->b0_mtr[csrow >> 1]; | 1281 | continue; |
1271 | mtr1 = pvt->b1_mtr[csrow >> 1]; | ||
1272 | 1282 | ||
1273 | /* if no DIMMS on this row, continue */ | 1283 | dimm = EDAC_DIMM_PTR(mci->layers, mci->dimms, mci->n_layers, |
1274 | if (!MTR_DIMMS_PRESENT(mtr) && !MTR_DIMMS_PRESENT(mtr1)) | 1284 | channel / MAX_BRANCHES, |
1275 | continue; | 1285 | channel % MAX_BRANCHES, slot); |
1276 | 1286 | ||
1277 | csrow_megs = 0; | 1287 | csrow_megs = pvt->dimm_info[slot][channel].megabytes; |
1278 | for (channel = 0; channel < pvt->maxch; channel++) { | ||
1279 | dimm = p_csrow->channels[channel].dimm; | ||
1280 | csrow_megs += pvt->dimm_info[csrow][channel].megabytes; | ||
1281 | dimm->grain = 8; | 1288 | dimm->grain = 8; |
1282 | 1289 | ||
1283 | /* Assume DDR2 for now */ | 1290 | /* Assume DDR2 for now */ |
@@ -1290,7 +1297,7 @@ static int i5000_init_csrows(struct mem_ctl_info *mci) | |||
1290 | dimm->dtype = DEV_X4; | 1297 | dimm->dtype = DEV_X4; |
1291 | 1298 | ||
1292 | dimm->edac_mode = EDAC_S8ECD8ED; | 1299 | dimm->edac_mode = EDAC_S8ECD8ED; |
1293 | dimm->nr_pages = (csrow_megs << 8) / pvt->maxch; | 1300 | dimm->nr_pages = csrow_megs << 8; |
1294 | } | 1301 | } |
1295 | 1302 | ||
1296 | empty = 0; | 1303 | empty = 0; |
@@ -1337,7 +1344,7 @@ static void i5000_get_dimm_and_channel_counts(struct pci_dev *pdev, | |||
1337 | * supported on this memory controller | 1344 | * supported on this memory controller |
1338 | */ | 1345 | */ |
1339 | pci_read_config_byte(pdev, MAXDIMMPERCH, &value); | 1346 | pci_read_config_byte(pdev, MAXDIMMPERCH, &value); |
1340 | *num_dimms_per_channel = (int)value *2; | 1347 | *num_dimms_per_channel = (int)value; |
1341 | 1348 | ||
1342 | pci_read_config_byte(pdev, MAXCH, &value); | 1349 | pci_read_config_byte(pdev, MAXCH, &value); |
1343 | *num_channels = (int)value; | 1350 | *num_channels = (int)value; |
@@ -1387,11 +1394,12 @@ static int i5000_probe1(struct pci_dev *pdev, int dev_idx) | |||
1387 | __func__, num_channels, num_dimms_per_channel); | 1394 | __func__, num_channels, num_dimms_per_channel); |
1388 | 1395 | ||
1389 | /* allocate a new MC control structure */ | 1396 | /* allocate a new MC control structure */ |
1397 | |||
1390 | layers[0].type = EDAC_MC_LAYER_BRANCH; | 1398 | layers[0].type = EDAC_MC_LAYER_BRANCH; |
1391 | layers[0].size = 2; | 1399 | layers[0].size = MAX_BRANCHES; |
1392 | layers[0].is_virt_csrow = true; | 1400 | layers[0].is_virt_csrow = false; |
1393 | layers[1].type = EDAC_MC_LAYER_CHANNEL; | 1401 | layers[1].type = EDAC_MC_LAYER_CHANNEL; |
1394 | layers[1].size = num_channels; | 1402 | layers[1].size = num_channels / MAX_BRANCHES; |
1395 | layers[1].is_virt_csrow = false; | 1403 | layers[1].is_virt_csrow = false; |
1396 | layers[2].type = EDAC_MC_LAYER_SLOT; | 1404 | layers[2].type = EDAC_MC_LAYER_SLOT; |
1397 | layers[2].size = num_dimms_per_channel; | 1405 | layers[2].size = num_dimms_per_channel; |