in i7core_edac.c [487:645]
static int get_dimm_config(struct mem_ctl_info *mci)
{
struct i7core_pvt *pvt = mci->pvt_info;
struct pci_dev *pdev;
int i, j;
enum edac_type mode;
enum mem_type mtype;
struct dimm_info *dimm;
/* Get data from the MC register, function 0 */
pdev = pvt->pci_mcr[0];
if (!pdev)
return -ENODEV;
/* Device 3 function 0 reads */
pci_read_config_dword(pdev, MC_CONTROL, &pvt->info.mc_control);
pci_read_config_dword(pdev, MC_STATUS, &pvt->info.mc_status);
pci_read_config_dword(pdev, MC_MAX_DOD, &pvt->info.max_dod);
pci_read_config_dword(pdev, MC_CHANNEL_MAPPER, &pvt->info.ch_map);
edac_dbg(0, "QPI %d control=0x%08x status=0x%08x dod=0x%08x map=0x%08x\n",
pvt->i7core_dev->socket, pvt->info.mc_control,
pvt->info.mc_status, pvt->info.max_dod, pvt->info.ch_map);
if (ECC_ENABLED(pvt)) {
edac_dbg(0, "ECC enabled with x%d SDCC\n", ECCx8(pvt) ? 8 : 4);
if (ECCx8(pvt))
mode = EDAC_S8ECD8ED;
else
mode = EDAC_S4ECD4ED;
} else {
edac_dbg(0, "ECC disabled\n");
mode = EDAC_NONE;
}
/* FIXME: need to handle the error codes */
edac_dbg(0, "DOD Max limits: DIMMS: %d, %d-ranked, %d-banked x%x x 0x%x\n",
numdimms(pvt->info.max_dod),
numrank(pvt->info.max_dod >> 2),
numbank(pvt->info.max_dod >> 4),
numrow(pvt->info.max_dod >> 6),
numcol(pvt->info.max_dod >> 9));
for (i = 0; i < NUM_CHANS; i++) {
u32 data, dimm_dod[3], value[8];
if (!pvt->pci_ch[i][0])
continue;
if (!CH_ACTIVE(pvt, i)) {
edac_dbg(0, "Channel %i is not active\n", i);
continue;
}
if (CH_DISABLED(pvt, i)) {
edac_dbg(0, "Channel %i is disabled\n", i);
continue;
}
/* Devices 4-6 function 0 */
pci_read_config_dword(pvt->pci_ch[i][0],
MC_CHANNEL_DIMM_INIT_PARAMS, &data);
if (data & THREE_DIMMS_PRESENT)
pvt->channel[i].is_3dimms_present = true;
if (data & SINGLE_QUAD_RANK_PRESENT)
pvt->channel[i].is_single_4rank = true;
if (data & QUAD_RANK_PRESENT)
pvt->channel[i].has_4rank = true;
if (data & REGISTERED_DIMM)
mtype = MEM_RDDR3;
else
mtype = MEM_DDR3;
/* Devices 4-6 function 1 */
pci_read_config_dword(pvt->pci_ch[i][1],
MC_DOD_CH_DIMM0, &dimm_dod[0]);
pci_read_config_dword(pvt->pci_ch[i][1],
MC_DOD_CH_DIMM1, &dimm_dod[1]);
pci_read_config_dword(pvt->pci_ch[i][1],
MC_DOD_CH_DIMM2, &dimm_dod[2]);
edac_dbg(0, "Ch%d phy rd%d, wr%d (0x%08x): %s%s%s%cDIMMs\n",
i,
RDLCH(pvt->info.ch_map, i), WRLCH(pvt->info.ch_map, i),
data,
pvt->channel[i].is_3dimms_present ? "3DIMMS " : "",
pvt->channel[i].is_3dimms_present ? "SINGLE_4R " : "",
pvt->channel[i].has_4rank ? "HAS_4R " : "",
(data & REGISTERED_DIMM) ? 'R' : 'U');
for (j = 0; j < 3; j++) {
u32 banks, ranks, rows, cols;
u32 size, npages;
if (!DIMM_PRESENT(dimm_dod[j]))
continue;
dimm = edac_get_dimm(mci, i, j, 0);
banks = numbank(MC_DOD_NUMBANK(dimm_dod[j]));
ranks = numrank(MC_DOD_NUMRANK(dimm_dod[j]));
rows = numrow(MC_DOD_NUMROW(dimm_dod[j]));
cols = numcol(MC_DOD_NUMCOL(dimm_dod[j]));
/* DDR3 has 8 I/O banks */
size = (rows * cols * banks * ranks) >> (20 - 3);
edac_dbg(0, "\tdimm %d %d MiB offset: %x, bank: %d, rank: %d, row: %#x, col: %#x\n",
j, size,
RANKOFFSET(dimm_dod[j]),
banks, ranks, rows, cols);
npages = MiB_TO_PAGES(size);
dimm->nr_pages = npages;
switch (banks) {
case 4:
dimm->dtype = DEV_X4;
break;
case 8:
dimm->dtype = DEV_X8;
break;
case 16:
dimm->dtype = DEV_X16;
break;
default:
dimm->dtype = DEV_UNKNOWN;
}
snprintf(dimm->label, sizeof(dimm->label),
"CPU#%uChannel#%u_DIMM#%u",
pvt->i7core_dev->socket, i, j);
dimm->grain = 8;
dimm->edac_mode = mode;
dimm->mtype = mtype;
}
pci_read_config_dword(pdev, MC_SAG_CH_0, &value[0]);
pci_read_config_dword(pdev, MC_SAG_CH_1, &value[1]);
pci_read_config_dword(pdev, MC_SAG_CH_2, &value[2]);
pci_read_config_dword(pdev, MC_SAG_CH_3, &value[3]);
pci_read_config_dword(pdev, MC_SAG_CH_4, &value[4]);
pci_read_config_dword(pdev, MC_SAG_CH_5, &value[5]);
pci_read_config_dword(pdev, MC_SAG_CH_6, &value[6]);
pci_read_config_dword(pdev, MC_SAG_CH_7, &value[7]);
edac_dbg(1, "\t[%i] DIVBY3\tREMOVED\tOFFSET\n", i);
for (j = 0; j < 8; j++)
edac_dbg(1, "\t\t%#x\t%#x\t%#x\n",
(value[j] >> 27) & 0x1,
(value[j] >> 24) & 0x7,
(value[j] & ((1 << 24) - 1)));
}
return 0;
}