diff options
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; |