i5400_edac: convert driver to use the new edac ABI
The legacy edac ABI is going to be removed. Port the driver to use and benefit from the new API functionality. Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
This commit is contained in:
parent
d1afaa0a6e
commit
296da591ea
@ -18,6 +18,10 @@
|
|||||||
* Intel 5400 Chipset Memory Controller Hub (MCH) - Datasheet
|
* Intel 5400 Chipset Memory Controller Hub (MCH) - Datasheet
|
||||||
* http://developer.intel.com/design/chipsets/datashts/313070.htm
|
* http://developer.intel.com/design/chipsets/datashts/313070.htm
|
||||||
*
|
*
|
||||||
|
* This Memory Controller manages DDR2 FB-DIMMs. It has 2 branches, each with
|
||||||
|
* 2 channels operating in lockstep no-mirror mode. Each channel can have up to
|
||||||
|
* 4 dimm's, each with up to 8GB.
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
@ -44,12 +48,10 @@
|
|||||||
edac_mc_chipset_printk(mci, level, "i5400", fmt, ##arg)
|
edac_mc_chipset_printk(mci, level, "i5400", fmt, ##arg)
|
||||||
|
|
||||||
/* Limits for i5400 */
|
/* Limits for i5400 */
|
||||||
#define NUM_MTRS_PER_BRANCH 4
|
#define MAX_BRANCHES 2
|
||||||
#define CHANNELS_PER_BRANCH 2
|
#define CHANNELS_PER_BRANCH 2
|
||||||
#define MAX_DIMMS_PER_CHANNEL NUM_MTRS_PER_BRANCH
|
#define DIMMS_PER_CHANNEL 4
|
||||||
#define MAX_CHANNELS 4
|
#define MAX_CHANNELS (MAX_BRANCHES * CHANNELS_PER_BRANCH)
|
||||||
/* max possible csrows per channel */
|
|
||||||
#define MAX_CSROWS (MAX_DIMMS_PER_CHANNEL)
|
|
||||||
|
|
||||||
/* Device 16,
|
/* Device 16,
|
||||||
* Function 0: System Address
|
* Function 0: System Address
|
||||||
@ -347,16 +349,16 @@ struct i5400_pvt {
|
|||||||
|
|
||||||
u16 mir0, mir1;
|
u16 mir0, mir1;
|
||||||
|
|
||||||
u16 b0_mtr[NUM_MTRS_PER_BRANCH]; /* Memory Technlogy Reg */
|
u16 b0_mtr[DIMMS_PER_CHANNEL]; /* Memory Technlogy Reg */
|
||||||
u16 b0_ambpresent0; /* Branch 0, Channel 0 */
|
u16 b0_ambpresent0; /* Branch 0, Channel 0 */
|
||||||
u16 b0_ambpresent1; /* Brnach 0, Channel 1 */
|
u16 b0_ambpresent1; /* Brnach 0, Channel 1 */
|
||||||
|
|
||||||
u16 b1_mtr[NUM_MTRS_PER_BRANCH]; /* Memory Technlogy Reg */
|
u16 b1_mtr[DIMMS_PER_CHANNEL]; /* Memory Technlogy Reg */
|
||||||
u16 b1_ambpresent0; /* Branch 1, Channel 8 */
|
u16 b1_ambpresent0; /* Branch 1, Channel 8 */
|
||||||
u16 b1_ambpresent1; /* Branch 1, Channel 1 */
|
u16 b1_ambpresent1; /* Branch 1, Channel 1 */
|
||||||
|
|
||||||
/* DIMM information matrix, allocating architecture maximums */
|
/* DIMM information matrix, allocating architecture maximums */
|
||||||
struct i5400_dimm_info dimm_info[MAX_CSROWS][MAX_CHANNELS];
|
struct i5400_dimm_info dimm_info[DIMMS_PER_CHANNEL][MAX_CHANNELS];
|
||||||
|
|
||||||
/* Actual values for this controller */
|
/* Actual values for this controller */
|
||||||
int maxch; /* Max channels */
|
int maxch; /* Max channels */
|
||||||
@ -532,13 +534,15 @@ static void i5400_proccess_non_recoverable_info(struct mem_ctl_info *mci,
|
|||||||
int ras, cas;
|
int ras, cas;
|
||||||
int errnum;
|
int errnum;
|
||||||
char *type = NULL;
|
char *type = NULL;
|
||||||
|
enum hw_event_mc_err_type tp_event = HW_EVENT_ERR_UNCORRECTED;
|
||||||
|
|
||||||
if (!allErrors)
|
if (!allErrors)
|
||||||
return; /* if no error, return now */
|
return; /* if no error, return now */
|
||||||
|
|
||||||
if (allErrors & ERROR_FAT_MASK)
|
if (allErrors & ERROR_FAT_MASK) {
|
||||||
type = "FATAL";
|
type = "FATAL";
|
||||||
else if (allErrors & FERR_NF_UNCORRECTABLE)
|
tp_event = HW_EVENT_ERR_FATAL;
|
||||||
|
} else if (allErrors & FERR_NF_UNCORRECTABLE)
|
||||||
type = "NON-FATAL uncorrected";
|
type = "NON-FATAL uncorrected";
|
||||||
else
|
else
|
||||||
type = "NON-FATAL recoverable";
|
type = "NON-FATAL recoverable";
|
||||||
@ -556,7 +560,7 @@ static void i5400_proccess_non_recoverable_info(struct mem_ctl_info *mci,
|
|||||||
ras = nrec_ras(info);
|
ras = nrec_ras(info);
|
||||||
cas = nrec_cas(info);
|
cas = nrec_cas(info);
|
||||||
|
|
||||||
debugf0("\t\tCSROW= %d Channels= %d,%d (Branch= %d "
|
debugf0("\t\tDIMM= %d Channels= %d,%d (Branch= %d "
|
||||||
"DRAM Bank= %d Buffer ID = %d rdwr= %s ras= %d cas= %d)\n",
|
"DRAM Bank= %d Buffer ID = %d rdwr= %s ras= %d cas= %d)\n",
|
||||||
rank, channel, channel + 1, branch >> 1, bank,
|
rank, channel, channel + 1, branch >> 1, bank,
|
||||||
buf_id, rdwr_str(rdwr), ras, cas);
|
buf_id, rdwr_str(rdwr), ras, cas);
|
||||||
@ -566,13 +570,13 @@ static void i5400_proccess_non_recoverable_info(struct mem_ctl_info *mci,
|
|||||||
|
|
||||||
/* Form out message */
|
/* Form out message */
|
||||||
snprintf(msg, sizeof(msg),
|
snprintf(msg, sizeof(msg),
|
||||||
"%s (Branch=%d DRAM-Bank=%d Buffer ID = %d RDWR=%s "
|
"Bank=%d Buffer ID = %d RAS=%d CAS=%d Err=0x%lx (%s)",
|
||||||
"RAS=%d CAS=%d %s Err=0x%lx (%s))",
|
bank, buf_id, ras, cas, allErrors, error_name[errnum]);
|
||||||
type, branch >> 1, bank, buf_id, rdwr_str(rdwr), ras, cas,
|
|
||||||
type, allErrors, error_name[errnum]);
|
|
||||||
|
|
||||||
/* Call the helper to output message */
|
edac_mc_handle_error(tp_event, mci, 0, 0, 0,
|
||||||
edac_mc_handle_fbd_ue(mci, rank, channel, channel + 1, msg);
|
branch >> 1, -1, rank,
|
||||||
|
rdwr ? "Write error" : "Read error",
|
||||||
|
msg, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -630,7 +634,7 @@ static void i5400_process_nonfatal_error_info(struct mem_ctl_info *mci,
|
|||||||
/* Only 1 bit will be on */
|
/* Only 1 bit will be on */
|
||||||
errnum = find_first_bit(&allErrors, ARRAY_SIZE(error_name));
|
errnum = find_first_bit(&allErrors, ARRAY_SIZE(error_name));
|
||||||
|
|
||||||
debugf0("\t\tCSROW= %d Channel= %d (Branch %d "
|
debugf0("\t\tDIMM= %d Channel= %d (Branch %d "
|
||||||
"DRAM Bank= %d rdwr= %s ras= %d cas= %d)\n",
|
"DRAM Bank= %d rdwr= %s ras= %d cas= %d)\n",
|
||||||
rank, channel, branch >> 1, bank,
|
rank, channel, branch >> 1, bank,
|
||||||
rdwr_str(rdwr), ras, cas);
|
rdwr_str(rdwr), ras, cas);
|
||||||
@ -642,8 +646,10 @@ static void i5400_process_nonfatal_error_info(struct mem_ctl_info *mci,
|
|||||||
branch >> 1, bank, rdwr_str(rdwr), ras, cas,
|
branch >> 1, bank, rdwr_str(rdwr), ras, cas,
|
||||||
allErrors, error_name[errnum]);
|
allErrors, error_name[errnum]);
|
||||||
|
|
||||||
/* Call the helper to output message */
|
edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 0, 0, 0,
|
||||||
edac_mc_handle_fbd_ce(mci, rank, channel, msg);
|
branch >> 1, channel % 2, rank,
|
||||||
|
rdwr ? "Write error" : "Read error",
|
||||||
|
msg, NULL);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -831,8 +837,8 @@ static int i5400_get_devices(struct mem_ctl_info *mci, int dev_idx)
|
|||||||
/*
|
/*
|
||||||
* determine_amb_present
|
* determine_amb_present
|
||||||
*
|
*
|
||||||
* the information is contained in NUM_MTRS_PER_BRANCH different
|
* the information is contained in DIMMS_PER_CHANNEL different
|
||||||
* registers determining which of the NUM_MTRS_PER_BRANCH requires
|
* registers determining which of the DIMMS_PER_CHANNEL requires
|
||||||
* knowing which channel is in question
|
* knowing which channel is in question
|
||||||
*
|
*
|
||||||
* 2 branches, each with 2 channels
|
* 2 branches, each with 2 channels
|
||||||
@ -861,11 +867,11 @@ static int determine_amb_present_reg(struct i5400_pvt *pvt, int channel)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* determine_mtr(pvt, csrow, channel)
|
* determine_mtr(pvt, dimm, channel)
|
||||||
*
|
*
|
||||||
* return the proper MTR register as determine by the csrow and desired channel
|
* return the proper MTR register as determine by the dimm and desired channel
|
||||||
*/
|
*/
|
||||||
static int determine_mtr(struct i5400_pvt *pvt, int csrow, int channel)
|
static int determine_mtr(struct i5400_pvt *pvt, int dimm, int channel)
|
||||||
{
|
{
|
||||||
int mtr;
|
int mtr;
|
||||||
int n;
|
int n;
|
||||||
@ -873,11 +879,11 @@ static int determine_mtr(struct i5400_pvt *pvt, int csrow, int channel)
|
|||||||
/* There is one MTR for each slot pair of FB-DIMMs,
|
/* There is one MTR for each slot pair of FB-DIMMs,
|
||||||
Each slot pair may be at branch 0 or branch 1.
|
Each slot pair may be at branch 0 or branch 1.
|
||||||
*/
|
*/
|
||||||
n = csrow;
|
n = dimm;
|
||||||
|
|
||||||
if (n >= NUM_MTRS_PER_BRANCH) {
|
if (n >= DIMMS_PER_CHANNEL) {
|
||||||
debugf0("ERROR: trying to access an invalid csrow: %d\n",
|
debugf0("ERROR: trying to access an invalid dimm: %d\n",
|
||||||
csrow);
|
dimm);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -913,19 +919,19 @@ static void decode_mtr(int slot_row, u16 mtr)
|
|||||||
debugf2("\t\tNUMCOL: %s\n", numcol_toString[MTR_DIMM_COLS(mtr)]);
|
debugf2("\t\tNUMCOL: %s\n", numcol_toString[MTR_DIMM_COLS(mtr)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handle_channel(struct i5400_pvt *pvt, int csrow, int channel,
|
static void handle_channel(struct i5400_pvt *pvt, int dimm, int channel,
|
||||||
struct i5400_dimm_info *dinfo)
|
struct i5400_dimm_info *dinfo)
|
||||||
{
|
{
|
||||||
int mtr;
|
int mtr;
|
||||||
int amb_present_reg;
|
int amb_present_reg;
|
||||||
int addrBits;
|
int addrBits;
|
||||||
|
|
||||||
mtr = determine_mtr(pvt, csrow, channel);
|
mtr = determine_mtr(pvt, dimm, channel);
|
||||||
if (MTR_DIMMS_PRESENT(mtr)) {
|
if (MTR_DIMMS_PRESENT(mtr)) {
|
||||||
amb_present_reg = determine_amb_present_reg(pvt, channel);
|
amb_present_reg = determine_amb_present_reg(pvt, channel);
|
||||||
|
|
||||||
/* Determine if there is a DIMM present in this DIMM slot */
|
/* Determine if there is a DIMM present in this DIMM slot */
|
||||||
if (amb_present_reg & (1 << csrow)) {
|
if (amb_present_reg & (1 << dimm)) {
|
||||||
/* Start with the number of bits for a Bank
|
/* Start with the number of bits for a Bank
|
||||||
* on the DRAM */
|
* on the DRAM */
|
||||||
addrBits = MTR_DRAM_BANKS_ADDR_BITS(mtr);
|
addrBits = MTR_DRAM_BANKS_ADDR_BITS(mtr);
|
||||||
@ -954,7 +960,7 @@ static void handle_channel(struct i5400_pvt *pvt, int csrow, int channel,
|
|||||||
static void calculate_dimm_size(struct i5400_pvt *pvt)
|
static void calculate_dimm_size(struct i5400_pvt *pvt)
|
||||||
{
|
{
|
||||||
struct i5400_dimm_info *dinfo;
|
struct i5400_dimm_info *dinfo;
|
||||||
int csrow, max_csrows;
|
int dimm, max_dimms;
|
||||||
char *p, *mem_buffer;
|
char *p, *mem_buffer;
|
||||||
int space, n;
|
int space, n;
|
||||||
int channel;
|
int channel;
|
||||||
@ -968,32 +974,32 @@ static void calculate_dimm_size(struct i5400_pvt *pvt)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Scan all the actual CSROWS
|
/* Scan all the actual DIMMS
|
||||||
* and calculate the information for each DIMM
|
* and calculate the information for each DIMM
|
||||||
* Start with the highest csrow first, to display it first
|
* Start with the highest dimm first, to display it first
|
||||||
* and work toward the 0th csrow
|
* and work toward the 0th dimm
|
||||||
*/
|
*/
|
||||||
max_csrows = pvt->maxdimmperch;
|
max_dimms = pvt->maxdimmperch;
|
||||||
for (csrow = max_csrows - 1; csrow >= 0; csrow--) {
|
for (dimm = max_dimms - 1; dimm >= 0; dimm--) {
|
||||||
|
|
||||||
/* on an odd csrow, first output a 'boundary' marker,
|
/* on an odd dimm, first output a 'boundary' marker,
|
||||||
* then reset the message buffer */
|
* then reset the message buffer */
|
||||||
if (csrow & 0x1) {
|
if (dimm & 0x1) {
|
||||||
n = snprintf(p, space, "---------------------------"
|
n = snprintf(p, space, "---------------------------"
|
||||||
"--------------------------------");
|
"-------------------------------");
|
||||||
p += n;
|
p += n;
|
||||||
space -= n;
|
space -= n;
|
||||||
debugf2("%s\n", mem_buffer);
|
debugf2("%s\n", mem_buffer);
|
||||||
p = mem_buffer;
|
p = mem_buffer;
|
||||||
space = PAGE_SIZE;
|
space = PAGE_SIZE;
|
||||||
}
|
}
|
||||||
n = snprintf(p, space, "csrow %2d ", csrow);
|
n = snprintf(p, space, "dimm %2d ", dimm);
|
||||||
p += n;
|
p += n;
|
||||||
space -= n;
|
space -= n;
|
||||||
|
|
||||||
for (channel = 0; channel < pvt->maxch; channel++) {
|
for (channel = 0; channel < pvt->maxch; channel++) {
|
||||||
dinfo = &pvt->dimm_info[csrow][channel];
|
dinfo = &pvt->dimm_info[dimm][channel];
|
||||||
handle_channel(pvt, csrow, channel, dinfo);
|
handle_channel(pvt, dimm, channel, dinfo);
|
||||||
n = snprintf(p, space, "%4d MB | ", dinfo->megabytes);
|
n = snprintf(p, space, "%4d MB | ", dinfo->megabytes);
|
||||||
p += n;
|
p += n;
|
||||||
space -= n;
|
space -= n;
|
||||||
@ -1005,7 +1011,7 @@ static void calculate_dimm_size(struct i5400_pvt *pvt)
|
|||||||
|
|
||||||
/* Output the last bottom 'boundary' marker */
|
/* Output the last bottom 'boundary' marker */
|
||||||
n = snprintf(p, space, "---------------------------"
|
n = snprintf(p, space, "---------------------------"
|
||||||
"--------------------------------");
|
"-------------------------------");
|
||||||
p += n;
|
p += n;
|
||||||
space -= n;
|
space -= n;
|
||||||
debugf2("%s\n", mem_buffer);
|
debugf2("%s\n", mem_buffer);
|
||||||
@ -1013,7 +1019,7 @@ static void calculate_dimm_size(struct i5400_pvt *pvt)
|
|||||||
space = PAGE_SIZE;
|
space = PAGE_SIZE;
|
||||||
|
|
||||||
/* now output the 'channel' labels */
|
/* now output the 'channel' labels */
|
||||||
n = snprintf(p, space, " ");
|
n = snprintf(p, space, " ");
|
||||||
p += n;
|
p += n;
|
||||||
space -= n;
|
space -= n;
|
||||||
for (channel = 0; channel < pvt->maxch; channel++) {
|
for (channel = 0; channel < pvt->maxch; channel++) {
|
||||||
@ -1080,7 +1086,7 @@ static void i5400_get_mc_regs(struct mem_ctl_info *mci)
|
|||||||
debugf2("MIR1: limit= 0x%x WAY1= %u WAY0= %x\n", limit, way1, way0);
|
debugf2("MIR1: limit= 0x%x WAY1= %u WAY0= %x\n", limit, way1, way0);
|
||||||
|
|
||||||
/* Get the set of MTR[0-3] regs by each branch */
|
/* Get the set of MTR[0-3] regs by each branch */
|
||||||
for (slot_row = 0; slot_row < NUM_MTRS_PER_BRANCH; slot_row++) {
|
for (slot_row = 0; slot_row < DIMMS_PER_CHANNEL; slot_row++) {
|
||||||
int where = MTR0 + (slot_row * sizeof(u16));
|
int where = MTR0 + (slot_row * sizeof(u16));
|
||||||
|
|
||||||
/* Branch 0 set of MTR registers */
|
/* Branch 0 set of MTR registers */
|
||||||
@ -1105,7 +1111,7 @@ static void i5400_get_mc_regs(struct mem_ctl_info *mci)
|
|||||||
/* Read and dump branch 0's MTRs */
|
/* Read and dump branch 0's MTRs */
|
||||||
debugf2("\nMemory Technology Registers:\n");
|
debugf2("\nMemory Technology Registers:\n");
|
||||||
debugf2(" Branch 0:\n");
|
debugf2(" Branch 0:\n");
|
||||||
for (slot_row = 0; slot_row < NUM_MTRS_PER_BRANCH; slot_row++)
|
for (slot_row = 0; slot_row < DIMMS_PER_CHANNEL; slot_row++)
|
||||||
decode_mtr(slot_row, pvt->b0_mtr[slot_row]);
|
decode_mtr(slot_row, pvt->b0_mtr[slot_row]);
|
||||||
|
|
||||||
pci_read_config_word(pvt->branch_0, AMBPRESENT_0,
|
pci_read_config_word(pvt->branch_0, AMBPRESENT_0,
|
||||||
@ -1122,7 +1128,7 @@ static void i5400_get_mc_regs(struct mem_ctl_info *mci)
|
|||||||
} else {
|
} else {
|
||||||
/* Read and dump branch 1's MTRs */
|
/* Read and dump branch 1's MTRs */
|
||||||
debugf2(" Branch 1:\n");
|
debugf2(" Branch 1:\n");
|
||||||
for (slot_row = 0; slot_row < NUM_MTRS_PER_BRANCH; slot_row++)
|
for (slot_row = 0; slot_row < DIMMS_PER_CHANNEL; slot_row++)
|
||||||
decode_mtr(slot_row, pvt->b1_mtr[slot_row]);
|
decode_mtr(slot_row, pvt->b1_mtr[slot_row]);
|
||||||
|
|
||||||
pci_read_config_word(pvt->branch_1, AMBPRESENT_0,
|
pci_read_config_word(pvt->branch_1, AMBPRESENT_0,
|
||||||
@ -1141,7 +1147,7 @@ static void i5400_get_mc_regs(struct mem_ctl_info *mci)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* i5400_init_csrows Initialize the 'csrows' table within
|
* i5400_init_dimms Initialize the 'dimms' table within
|
||||||
* the mci control structure with the
|
* the mci control structure with the
|
||||||
* addressing of memory.
|
* addressing of memory.
|
||||||
*
|
*
|
||||||
@ -1149,50 +1155,68 @@ static void i5400_get_mc_regs(struct mem_ctl_info *mci)
|
|||||||
* 0 success
|
* 0 success
|
||||||
* 1 no actual memory found on this MC
|
* 1 no actual memory found on this MC
|
||||||
*/
|
*/
|
||||||
static int i5400_init_csrows(struct mem_ctl_info *mci)
|
static int i5400_init_dimms(struct mem_ctl_info *mci)
|
||||||
{
|
{
|
||||||
struct i5400_pvt *pvt;
|
struct i5400_pvt *pvt;
|
||||||
struct csrow_info *p_csrow;
|
struct dimm_info *dimm;
|
||||||
int empty, channel_count;
|
int ndimms, channel_count;
|
||||||
int max_csrows;
|
int max_dimms;
|
||||||
int mtr;
|
int mtr;
|
||||||
int size_mb;
|
int size_mb;
|
||||||
int channel;
|
int channel, slot;
|
||||||
int csrow;
|
|
||||||
struct dimm_info *dimm;
|
|
||||||
|
|
||||||
pvt = mci->pvt_info;
|
pvt = mci->pvt_info;
|
||||||
|
|
||||||
channel_count = pvt->maxch;
|
channel_count = pvt->maxch;
|
||||||
max_csrows = pvt->maxdimmperch;
|
max_dimms = pvt->maxdimmperch;
|
||||||
|
|
||||||
empty = 1; /* Assume NO memory */
|
ndimms = 0;
|
||||||
|
|
||||||
for (csrow = 0; csrow < max_csrows; csrow++) {
|
/*
|
||||||
p_csrow = &mci->csrows[csrow];
|
* FIXME: remove pvt->dimm_info[slot][channel] and use the 3
|
||||||
|
* layers here.
|
||||||
|
*/
|
||||||
|
for (channel = 0; channel < mci->layers[0].size * mci->layers[1].size;
|
||||||
|
channel++) {
|
||||||
|
for (slot = 0; slot < mci->layers[2].size; slot++) {
|
||||||
|
mtr = determine_mtr(pvt, slot, channel);
|
||||||
|
|
||||||
/* use branch 0 for the basis */
|
/* if no DIMMS on this slot, continue */
|
||||||
mtr = determine_mtr(pvt, csrow, 0);
|
if (!MTR_DIMMS_PRESENT(mtr))
|
||||||
|
continue;
|
||||||
|
|
||||||
/* if no DIMMS on this row, continue */
|
dimm = EDAC_DIMM_PTR(mci->layers, mci->dimms, mci->n_layers,
|
||||||
if (!MTR_DIMMS_PRESENT(mtr))
|
channel / 2, channel % 2, slot);
|
||||||
continue;
|
|
||||||
|
|
||||||
for (channel = 0; channel < pvt->maxch; channel++) {
|
size_mb = pvt->dimm_info[slot][channel].megabytes;
|
||||||
size_mb = pvt->dimm_info[csrow][channel].megabytes;
|
|
||||||
|
debugf2("%s: dimm%zd (branch %d channel %d slot %d): %d.%03d GB\n",
|
||||||
|
__func__, dimm - mci->dimms,
|
||||||
|
channel / 2, channel % 2, slot,
|
||||||
|
size_mb / 1000, size_mb % 1000);
|
||||||
|
|
||||||
dimm = p_csrow->channels[channel].dimm;
|
|
||||||
dimm->nr_pages = size_mb << 8;
|
dimm->nr_pages = size_mb << 8;
|
||||||
dimm->grain = 8;
|
dimm->grain = 8;
|
||||||
dimm->dtype = MTR_DRAM_WIDTH(mtr) ? DEV_X8 : DEV_X4;
|
dimm->dtype = MTR_DRAM_WIDTH(mtr) ? DEV_X8 : DEV_X4;
|
||||||
dimm->mtype = MEM_RDDR2;
|
dimm->mtype = MEM_FB_DDR2;
|
||||||
dimm->edac_mode = EDAC_SECDED;
|
/*
|
||||||
|
* The eccc mechanism is SDDC (aka SECC), with
|
||||||
|
* is similar to Chipkill.
|
||||||
|
*/
|
||||||
|
dimm->edac_mode = MTR_DRAM_WIDTH(mtr) ?
|
||||||
|
EDAC_S8ECD8ED : EDAC_S4ECD4ED;
|
||||||
|
ndimms++;
|
||||||
}
|
}
|
||||||
|
|
||||||
empty = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return empty;
|
/*
|
||||||
|
* When just one memory is provided, it should be at location (0,0,0).
|
||||||
|
* With such single-DIMM mode, the SDCC algorithm degrades to SECDEC+.
|
||||||
|
*/
|
||||||
|
if (ndimms == 1)
|
||||||
|
mci->dimms[0].edac_mode = EDAC_SECDED;
|
||||||
|
|
||||||
|
return (ndimms == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1228,9 +1252,7 @@ static int i5400_probe1(struct pci_dev *pdev, int dev_idx)
|
|||||||
{
|
{
|
||||||
struct mem_ctl_info *mci;
|
struct mem_ctl_info *mci;
|
||||||
struct i5400_pvt *pvt;
|
struct i5400_pvt *pvt;
|
||||||
int num_channels;
|
struct edac_mc_layer layers[3];
|
||||||
int num_dimms_per_channel;
|
|
||||||
int num_csrows;
|
|
||||||
|
|
||||||
if (dev_idx >= ARRAY_SIZE(i5400_devs))
|
if (dev_idx >= ARRAY_SIZE(i5400_devs))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
@ -1244,22 +1266,21 @@ static int i5400_probe1(struct pci_dev *pdev, int dev_idx)
|
|||||||
if (PCI_FUNC(pdev->devfn) != 0)
|
if (PCI_FUNC(pdev->devfn) != 0)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
||||||
/* As we don't have a motherboard identification routine to determine
|
/*
|
||||||
* actual number of slots/dimms per channel, we thus utilize the
|
* allocate a new MC control structure
|
||||||
* resource as specified by the chipset. Thus, we might have
|
*
|
||||||
* have more DIMMs per channel than actually on the mobo, but this
|
* This drivers uses the DIMM slot as "csrow" and the rest as "channel".
|
||||||
* allows the driver to support up to the chipset max, without
|
|
||||||
* some fancy mobo determination.
|
|
||||||
*/
|
*/
|
||||||
num_dimms_per_channel = MAX_DIMMS_PER_CHANNEL;
|
layers[0].type = EDAC_MC_LAYER_BRANCH;
|
||||||
num_channels = MAX_CHANNELS;
|
layers[0].size = MAX_BRANCHES;
|
||||||
num_csrows = num_dimms_per_channel;
|
layers[0].is_virt_csrow = false;
|
||||||
|
layers[1].type = EDAC_MC_LAYER_CHANNEL;
|
||||||
debugf0("MC: %s(): Number of - Channels= %d DIMMS= %d CSROWS= %d\n",
|
layers[1].size = CHANNELS_PER_BRANCH;
|
||||||
__func__, num_channels, num_dimms_per_channel, num_csrows);
|
layers[1].is_virt_csrow = false;
|
||||||
|
layers[2].type = EDAC_MC_LAYER_SLOT;
|
||||||
/* allocate a new MC control structure */
|
layers[2].size = DIMMS_PER_CHANNEL;
|
||||||
mci = edac_mc_alloc(sizeof(*pvt), num_csrows, num_channels, 0);
|
layers[2].is_virt_csrow = true;
|
||||||
|
mci = new_edac_mc_alloc(0, ARRAY_SIZE(layers), layers, sizeof(*pvt));
|
||||||
|
|
||||||
if (mci == NULL)
|
if (mci == NULL)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
@ -1270,8 +1291,8 @@ static int i5400_probe1(struct pci_dev *pdev, int dev_idx)
|
|||||||
|
|
||||||
pvt = mci->pvt_info;
|
pvt = mci->pvt_info;
|
||||||
pvt->system_address = pdev; /* Record this device in our private */
|
pvt->system_address = pdev; /* Record this device in our private */
|
||||||
pvt->maxch = num_channels;
|
pvt->maxch = MAX_CHANNELS;
|
||||||
pvt->maxdimmperch = num_dimms_per_channel;
|
pvt->maxdimmperch = DIMMS_PER_CHANNEL;
|
||||||
|
|
||||||
/* 'get' the pci devices we want to reserve for our use */
|
/* 'get' the pci devices we want to reserve for our use */
|
||||||
if (i5400_get_devices(mci, dev_idx))
|
if (i5400_get_devices(mci, dev_idx))
|
||||||
@ -1293,13 +1314,13 @@ static int i5400_probe1(struct pci_dev *pdev, int dev_idx)
|
|||||||
/* Set the function pointer to an actual operation function */
|
/* Set the function pointer to an actual operation function */
|
||||||
mci->edac_check = i5400_check_error;
|
mci->edac_check = i5400_check_error;
|
||||||
|
|
||||||
/* initialize the MC control structure 'csrows' table
|
/* initialize the MC control structure 'dimms' table
|
||||||
* with the mapping and control information */
|
* with the mapping and control information */
|
||||||
if (i5400_init_csrows(mci)) {
|
if (i5400_init_dimms(mci)) {
|
||||||
debugf0("MC: Setting mci->edac_cap to EDAC_FLAG_NONE\n"
|
debugf0("MC: Setting mci->edac_cap to EDAC_FLAG_NONE\n"
|
||||||
" because i5400_init_csrows() returned nonzero "
|
" because i5400_init_dimms() returned nonzero "
|
||||||
"value\n");
|
"value\n");
|
||||||
mci->edac_cap = EDAC_FLAG_NONE; /* no csrows found */
|
mci->edac_cap = EDAC_FLAG_NONE; /* no dimms found */
|
||||||
} else {
|
} else {
|
||||||
debugf1("MC: Enable error reporting now\n");
|
debugf1("MC: Enable error reporting now\n");
|
||||||
i5400_enable_error_reporting(mci);
|
i5400_enable_error_reporting(mci);
|
||||||
|
Loading…
Reference in New Issue
Block a user