aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/edac/i5000_edac.c
diff options
context:
space:
mode:
authorMauro Carvalho Chehab <mchehab@redhat.com>2012-02-24 07:34:54 -0500
committerMauro Carvalho Chehab <mchehab@redhat.com>2012-05-28 18:13:52 -0400
commit64e1fdaf55d61eb79ae970f78bb00edf6aa696fb (patch)
tree0b19580b6d34717e750aefb7833d08d369ddb4e6 /drivers/edac/i5000_edac.c
parent68d086f89b8064d5576f8c1e47fa7ecb4fd6f141 (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.c150
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 */
965static int determine_mtr(struct i5000_pvt *pvt, int csrow, int channel) 966static 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
997static void handle_channel(struct i5000_pvt *pvt, int csrow, int channel, 998static 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,
1038static void calculate_dimm_size(struct i5000_pvt *pvt) 1036static 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)
1241static int i5000_init_csrows(struct mem_ctl_info *mci) 1250static 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;