Merge branch 'linux_next' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-edac
Pull EDAC fixes from Mauro Carvalho Chehab: "A series of EDAC driver fixes. It also has one core fix at the documentation, and a rename patch, fixing the name of the struct that contains the rank information." * 'linux_next' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-edac: edac: rename channel_info to rank_info i5400_edac: Avoid calling pci_put_device() twice edac: i5100 ack error detection register after each read edac: i5100 fix erroneous define for M1Err edac: sb_edac: Fix a wrong value setting for the previous value edac: sb_edac: Fix a INTERLEAVE_MODE() misuse edac: sb_edac: Let the driver depend on PCI_MMCONFIG edac: Improve the comments to better describe the memory concepts edac/ppc4xx_edac: Fix compilation Fix sb_edac compilation with 32 bits kernels
This commit is contained in:
commit
f0f3680e50
7 changed files with 209 additions and 103 deletions
|
@ -215,7 +215,7 @@ config EDAC_I7300
|
|||
config EDAC_SBRIDGE
|
||||
tristate "Intel Sandy-Bridge Integrated MC"
|
||||
depends on EDAC_MM_EDAC && PCI && X86_64 && X86_MCE_INTEL
|
||||
depends on EXPERIMENTAL
|
||||
depends on PCI_MMCONFIG && EXPERIMENTAL
|
||||
help
|
||||
Support for error detection and correction the Intel
|
||||
Sandy Bridge Integrated Memory Controller.
|
||||
|
|
|
@ -39,7 +39,7 @@ static LIST_HEAD(mc_devices);
|
|||
|
||||
#ifdef CONFIG_EDAC_DEBUG
|
||||
|
||||
static void edac_mc_dump_channel(struct channel_info *chan)
|
||||
static void edac_mc_dump_channel(struct rank_info *chan)
|
||||
{
|
||||
debugf4("\tchannel = %p\n", chan);
|
||||
debugf4("\tchannel->chan_idx = %d\n", chan->chan_idx);
|
||||
|
@ -156,7 +156,7 @@ struct mem_ctl_info *edac_mc_alloc(unsigned sz_pvt, unsigned nr_csrows,
|
|||
{
|
||||
struct mem_ctl_info *mci;
|
||||
struct csrow_info *csi, *csrow;
|
||||
struct channel_info *chi, *chp, *chan;
|
||||
struct rank_info *chi, *chp, *chan;
|
||||
void *pvt;
|
||||
unsigned size;
|
||||
int row, chn;
|
||||
|
@ -181,7 +181,7 @@ struct mem_ctl_info *edac_mc_alloc(unsigned sz_pvt, unsigned nr_csrows,
|
|||
* rather than an imaginary chunk of memory located at address 0.
|
||||
*/
|
||||
csi = (struct csrow_info *)(((char *)mci) + ((unsigned long)csi));
|
||||
chi = (struct channel_info *)(((char *)mci) + ((unsigned long)chi));
|
||||
chi = (struct rank_info *)(((char *)mci) + ((unsigned long)chi));
|
||||
pvt = sz_pvt ? (((char *)mci) + ((unsigned long)pvt)) : NULL;
|
||||
|
||||
/* setup index and various internal pointers */
|
||||
|
|
|
@ -49,7 +49,7 @@
|
|||
#define I5100_FERR_NF_MEM_M6ERR_MASK (1 << 6)
|
||||
#define I5100_FERR_NF_MEM_M5ERR_MASK (1 << 5)
|
||||
#define I5100_FERR_NF_MEM_M4ERR_MASK (1 << 4)
|
||||
#define I5100_FERR_NF_MEM_M1ERR_MASK 1
|
||||
#define I5100_FERR_NF_MEM_M1ERR_MASK (1 << 1)
|
||||
#define I5100_FERR_NF_MEM_ANY_MASK \
|
||||
(I5100_FERR_NF_MEM_M16ERR_MASK | \
|
||||
I5100_FERR_NF_MEM_M15ERR_MASK | \
|
||||
|
@ -535,23 +535,20 @@ static void i5100_read_log(struct mem_ctl_info *mci, int chan,
|
|||
static void i5100_check_error(struct mem_ctl_info *mci)
|
||||
{
|
||||
struct i5100_priv *priv = mci->pvt_info;
|
||||
u32 dw;
|
||||
|
||||
u32 dw, dw2;
|
||||
|
||||
pci_read_config_dword(priv->mc, I5100_FERR_NF_MEM, &dw);
|
||||
if (i5100_ferr_nf_mem_any(dw)) {
|
||||
u32 dw2;
|
||||
|
||||
pci_read_config_dword(priv->mc, I5100_NERR_NF_MEM, &dw2);
|
||||
if (dw2)
|
||||
pci_write_config_dword(priv->mc, I5100_NERR_NF_MEM,
|
||||
dw2);
|
||||
pci_write_config_dword(priv->mc, I5100_FERR_NF_MEM, dw);
|
||||
|
||||
i5100_read_log(mci, i5100_ferr_nf_mem_chan_indx(dw),
|
||||
i5100_ferr_nf_mem_any(dw),
|
||||
i5100_nerr_nf_mem_any(dw2));
|
||||
|
||||
pci_write_config_dword(priv->mc, I5100_NERR_NF_MEM, dw2);
|
||||
}
|
||||
pci_write_config_dword(priv->mc, I5100_FERR_NF_MEM, dw);
|
||||
}
|
||||
|
||||
/* The i5100 chipset will scrub the entire memory once, then
|
||||
|
|
|
@ -735,7 +735,7 @@ static int i5400_get_devices(struct mem_ctl_info *mci, int dev_idx)
|
|||
|
||||
/* Attempt to 'get' the MCH register we want */
|
||||
pdev = NULL;
|
||||
while (!pvt->branchmap_werrors || !pvt->fsb_error_regs) {
|
||||
while (1) {
|
||||
pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
|
||||
PCI_DEVICE_ID_INTEL_5400_ERR, pdev);
|
||||
if (!pdev) {
|
||||
|
@ -743,23 +743,42 @@ static int i5400_get_devices(struct mem_ctl_info *mci, int dev_idx)
|
|||
i5400_printk(KERN_ERR,
|
||||
"'system address,Process Bus' "
|
||||
"device not found:"
|
||||
"vendor 0x%x device 0x%x ERR funcs "
|
||||
"vendor 0x%x device 0x%x ERR func 1 "
|
||||
"(broken BIOS?)\n",
|
||||
PCI_VENDOR_ID_INTEL,
|
||||
PCI_DEVICE_ID_INTEL_5400_ERR);
|
||||
goto error;
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* Store device 16 funcs 1 and 2 */
|
||||
switch (PCI_FUNC(pdev->devfn)) {
|
||||
case 1:
|
||||
pvt->branchmap_werrors = pdev;
|
||||
/* Store device 16 func 1 */
|
||||
if (PCI_FUNC(pdev->devfn) == 1)
|
||||
break;
|
||||
case 2:
|
||||
pvt->fsb_error_regs = pdev;
|
||||
break;
|
||||
}
|
||||
}
|
||||
pvt->branchmap_werrors = pdev;
|
||||
|
||||
pdev = NULL;
|
||||
while (1) {
|
||||
pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
|
||||
PCI_DEVICE_ID_INTEL_5400_ERR, pdev);
|
||||
if (!pdev) {
|
||||
/* End of list, leave */
|
||||
i5400_printk(KERN_ERR,
|
||||
"'system address,Process Bus' "
|
||||
"device not found:"
|
||||
"vendor 0x%x device 0x%x ERR func 2 "
|
||||
"(broken BIOS?)\n",
|
||||
PCI_VENDOR_ID_INTEL,
|
||||
PCI_DEVICE_ID_INTEL_5400_ERR);
|
||||
|
||||
pci_dev_put(pvt->branchmap_werrors);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* Store device 16 func 2 */
|
||||
if (PCI_FUNC(pdev->devfn) == 2)
|
||||
break;
|
||||
}
|
||||
pvt->fsb_error_regs = pdev;
|
||||
|
||||
debugf1("System Address, processor bus- PCI Bus ID: %s %x:%x\n",
|
||||
pci_name(pvt->system_address),
|
||||
|
@ -778,7 +797,10 @@ static int i5400_get_devices(struct mem_ctl_info *mci, int dev_idx)
|
|||
"MC: 'BRANCH 0' device not found:"
|
||||
"vendor 0x%x device 0x%x Func 0 (broken BIOS?)\n",
|
||||
PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_5400_FBD0);
|
||||
goto error;
|
||||
|
||||
pci_dev_put(pvt->fsb_error_regs);
|
||||
pci_dev_put(pvt->branchmap_werrors);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* If this device claims to have more than 2 channels then
|
||||
|
@ -796,14 +818,14 @@ static int i5400_get_devices(struct mem_ctl_info *mci, int dev_idx)
|
|||
"(broken BIOS?)\n",
|
||||
PCI_VENDOR_ID_INTEL,
|
||||
PCI_DEVICE_ID_INTEL_5400_FBD1);
|
||||
goto error;
|
||||
|
||||
pci_dev_put(pvt->branch_0);
|
||||
pci_dev_put(pvt->fsb_error_regs);
|
||||
pci_dev_put(pvt->branchmap_werrors);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
i5400_put_devices(mci);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -184,7 +184,7 @@ struct ppc4xx_ecc_status {
|
|||
|
||||
/* Function Prototypes */
|
||||
|
||||
static int ppc4xx_edac_probe(struct platform_device *device)
|
||||
static int ppc4xx_edac_probe(struct platform_device *device);
|
||||
static int ppc4xx_edac_remove(struct platform_device *device);
|
||||
|
||||
/* Global Variables */
|
||||
|
@ -1068,7 +1068,7 @@ ppc4xx_edac_mc_init(struct mem_ctl_info *mci,
|
|||
|
||||
mci->mod_name = PPC4XX_EDAC_MODULE_NAME;
|
||||
mci->mod_ver = PPC4XX_EDAC_MODULE_REVISION;
|
||||
mci->ctl_name = match->compatible,
|
||||
mci->ctl_name = ppc4xx_edac_match->compatible,
|
||||
mci->dev_name = np->full_name;
|
||||
|
||||
/* Initialize callbacks */
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include <linux/mmzone.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/bitmap.h>
|
||||
#include <linux/math64.h>
|
||||
#include <asm/processor.h>
|
||||
#include <asm/mce.h>
|
||||
|
||||
|
@ -670,6 +671,7 @@ static void get_memory_layout(const struct mem_ctl_info *mci)
|
|||
u32 reg;
|
||||
u64 limit, prv = 0;
|
||||
u64 tmp_mb;
|
||||
u32 mb, kb;
|
||||
u32 rir_way;
|
||||
|
||||
/*
|
||||
|
@ -682,8 +684,9 @@ static void get_memory_layout(const struct mem_ctl_info *mci)
|
|||
pvt->tolm = GET_TOLM(reg);
|
||||
tmp_mb = (1 + pvt->tolm) >> 20;
|
||||
|
||||
debugf0("TOLM: %Lu.%03Lu GB (0x%016Lx)\n",
|
||||
tmp_mb / 1000, tmp_mb % 1000, (u64)pvt->tolm);
|
||||
mb = div_u64_rem(tmp_mb, 1000, &kb);
|
||||
debugf0("TOLM: %u.%03u GB (0x%016Lx)\n",
|
||||
mb, kb, (u64)pvt->tolm);
|
||||
|
||||
/* Address range is already 45:25 */
|
||||
pci_read_config_dword(pvt->pci_sad1, TOHM,
|
||||
|
@ -691,8 +694,9 @@ static void get_memory_layout(const struct mem_ctl_info *mci)
|
|||
pvt->tohm = GET_TOHM(reg);
|
||||
tmp_mb = (1 + pvt->tohm) >> 20;
|
||||
|
||||
debugf0("TOHM: %Lu.%03Lu GB (0x%016Lx)",
|
||||
tmp_mb / 1000, tmp_mb % 1000, (u64)pvt->tohm);
|
||||
mb = div_u64_rem(tmp_mb, 1000, &kb);
|
||||
debugf0("TOHM: %u.%03u GB (0x%016Lx)",
|
||||
mb, kb, (u64)pvt->tohm);
|
||||
|
||||
/*
|
||||
* Step 2) Get SAD range and SAD Interleave list
|
||||
|
@ -714,10 +718,11 @@ static void get_memory_layout(const struct mem_ctl_info *mci)
|
|||
break;
|
||||
|
||||
tmp_mb = (limit + 1) >> 20;
|
||||
debugf0("SAD#%d %s up to %Lu.%03Lu GB (0x%016Lx) %s reg=0x%08x\n",
|
||||
mb = div_u64_rem(tmp_mb, 1000, &kb);
|
||||
debugf0("SAD#%d %s up to %u.%03u GB (0x%016Lx) %s reg=0x%08x\n",
|
||||
n_sads,
|
||||
get_dram_attr(reg),
|
||||
tmp_mb / 1000, tmp_mb % 1000,
|
||||
mb, kb,
|
||||
((u64)tmp_mb) << 20L,
|
||||
INTERLEAVE_MODE(reg) ? "Interleave: 8:6" : "Interleave: [8:6]XOR[18:16]",
|
||||
reg);
|
||||
|
@ -747,8 +752,9 @@ static void get_memory_layout(const struct mem_ctl_info *mci)
|
|||
break;
|
||||
tmp_mb = (limit + 1) >> 20;
|
||||
|
||||
debugf0("TAD#%d: up to %Lu.%03Lu GB (0x%016Lx), socket interleave %d, memory interleave %d, TGT: %d, %d, %d, %d, reg=0x%08x\n",
|
||||
n_tads, tmp_mb / 1000, tmp_mb % 1000,
|
||||
mb = div_u64_rem(tmp_mb, 1000, &kb);
|
||||
debugf0("TAD#%d: up to %u.%03u GB (0x%016Lx), socket interleave %d, memory interleave %d, TGT: %d, %d, %d, %d, reg=0x%08x\n",
|
||||
n_tads, mb, kb,
|
||||
((u64)tmp_mb) << 20L,
|
||||
(u32)TAD_SOCK(reg),
|
||||
(u32)TAD_CH(reg),
|
||||
|
@ -757,7 +763,7 @@ static void get_memory_layout(const struct mem_ctl_info *mci)
|
|||
(u32)TAD_TGT2(reg),
|
||||
(u32)TAD_TGT3(reg),
|
||||
reg);
|
||||
prv = tmp_mb;
|
||||
prv = limit;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -771,9 +777,10 @@ static void get_memory_layout(const struct mem_ctl_info *mci)
|
|||
tad_ch_nilv_offset[j],
|
||||
®);
|
||||
tmp_mb = TAD_OFFSET(reg) >> 20;
|
||||
debugf0("TAD CH#%d, offset #%d: %Lu.%03Lu GB (0x%016Lx), reg=0x%08x\n",
|
||||
mb = div_u64_rem(tmp_mb, 1000, &kb);
|
||||
debugf0("TAD CH#%d, offset #%d: %u.%03u GB (0x%016Lx), reg=0x%08x\n",
|
||||
i, j,
|
||||
tmp_mb / 1000, tmp_mb % 1000,
|
||||
mb, kb,
|
||||
((u64)tmp_mb) << 20L,
|
||||
reg);
|
||||
}
|
||||
|
@ -795,9 +802,10 @@ static void get_memory_layout(const struct mem_ctl_info *mci)
|
|||
|
||||
tmp_mb = RIR_LIMIT(reg) >> 20;
|
||||
rir_way = 1 << RIR_WAY(reg);
|
||||
debugf0("CH#%d RIR#%d, limit: %Lu.%03Lu GB (0x%016Lx), way: %d, reg=0x%08x\n",
|
||||
mb = div_u64_rem(tmp_mb, 1000, &kb);
|
||||
debugf0("CH#%d RIR#%d, limit: %u.%03u GB (0x%016Lx), way: %d, reg=0x%08x\n",
|
||||
i, j,
|
||||
tmp_mb / 1000, tmp_mb % 1000,
|
||||
mb, kb,
|
||||
((u64)tmp_mb) << 20L,
|
||||
rir_way,
|
||||
reg);
|
||||
|
@ -808,9 +816,10 @@ static void get_memory_layout(const struct mem_ctl_info *mci)
|
|||
®);
|
||||
tmp_mb = RIR_OFFSET(reg) << 6;
|
||||
|
||||
debugf0("CH#%d RIR#%d INTL#%d, offset %Lu.%03Lu GB (0x%016Lx), tgt: %d, reg=0x%08x\n",
|
||||
mb = div_u64_rem(tmp_mb, 1000, &kb);
|
||||
debugf0("CH#%d RIR#%d INTL#%d, offset %u.%03u GB (0x%016Lx), tgt: %d, reg=0x%08x\n",
|
||||
i, j, k,
|
||||
tmp_mb / 1000, tmp_mb % 1000,
|
||||
mb, kb,
|
||||
((u64)tmp_mb) << 20L,
|
||||
(u32)RIR_RNK_TGT(reg),
|
||||
reg);
|
||||
|
@ -848,6 +857,7 @@ static int get_memory_error_data(struct mem_ctl_info *mci,
|
|||
u8 ch_way,sck_way;
|
||||
u32 tad_offset;
|
||||
u32 rir_way;
|
||||
u32 mb, kb;
|
||||
u64 ch_addr, offset, limit, prv = 0;
|
||||
|
||||
|
||||
|
@ -858,7 +868,7 @@ static int get_memory_error_data(struct mem_ctl_info *mci,
|
|||
* range (e. g. VGA addresses). It is unlikely, however, that the
|
||||
* memory controller would generate an error on that range.
|
||||
*/
|
||||
if ((addr > (u64) pvt->tolm) && (addr < (1L << 32))) {
|
||||
if ((addr > (u64) pvt->tolm) && (addr < (1LL << 32))) {
|
||||
sprintf(msg, "Error at TOLM area, on addr 0x%08Lx", addr);
|
||||
edac_mc_handle_ce_no_info(mci, msg);
|
||||
return -EINVAL;
|
||||
|
@ -913,7 +923,7 @@ static int get_memory_error_data(struct mem_ctl_info *mci,
|
|||
addr,
|
||||
limit,
|
||||
sad_way + 7,
|
||||
INTERLEAVE_MODE(reg) ? "" : "XOR[18:16]");
|
||||
interleave_mode ? "" : "XOR[18:16]");
|
||||
if (interleave_mode)
|
||||
idx = ((addr >> 6) ^ (addr >> 16)) & 7;
|
||||
else
|
||||
|
@ -1053,7 +1063,7 @@ static int get_memory_error_data(struct mem_ctl_info *mci,
|
|||
ch_addr = addr & 0x7f;
|
||||
/* Remove socket wayness and remove 6 bits */
|
||||
addr >>= 6;
|
||||
addr /= sck_xch;
|
||||
addr = div_u64(addr, sck_xch);
|
||||
#if 0
|
||||
/* Divide by channel way */
|
||||
addr = addr / ch_way;
|
||||
|
@ -1073,10 +1083,10 @@ static int get_memory_error_data(struct mem_ctl_info *mci,
|
|||
continue;
|
||||
|
||||
limit = RIR_LIMIT(reg);
|
||||
|
||||
debugf0("RIR#%d, limit: %Lu.%03Lu GB (0x%016Lx), way: %d\n",
|
||||
mb = div_u64_rem(limit >> 20, 1000, &kb);
|
||||
debugf0("RIR#%d, limit: %u.%03u GB (0x%016Lx), way: %d\n",
|
||||
n_rir,
|
||||
(limit >> 20) / 1000, (limit >> 20) % 1000,
|
||||
mb, kb,
|
||||
limit,
|
||||
1 << RIR_WAY(reg));
|
||||
if (ch_addr <= limit)
|
||||
|
|
|
@ -70,25 +70,64 @@ enum dev_type {
|
|||
#define DEV_FLAG_X32 BIT(DEV_X32)
|
||||
#define DEV_FLAG_X64 BIT(DEV_X64)
|
||||
|
||||
/* memory types */
|
||||
/**
|
||||
* enum mem_type - memory types. For a more detailed reference, please see
|
||||
* http://en.wikipedia.org/wiki/DRAM
|
||||
*
|
||||
* @MEM_EMPTY Empty csrow
|
||||
* @MEM_RESERVED: Reserved csrow type
|
||||
* @MEM_UNKNOWN: Unknown csrow type
|
||||
* @MEM_FPM: FPM - Fast Page Mode, used on systems up to 1995.
|
||||
* @MEM_EDO: EDO - Extended data out, used on systems up to 1998.
|
||||
* @MEM_BEDO: BEDO - Burst Extended data out, an EDO variant.
|
||||
* @MEM_SDR: SDR - Single data rate SDRAM
|
||||
* http://en.wikipedia.org/wiki/Synchronous_dynamic_random-access_memory
|
||||
* They use 3 pins for chip select: Pins 0 and 2 are
|
||||
* for rank 0; pins 1 and 3 are for rank 1, if the memory
|
||||
* is dual-rank.
|
||||
* @MEM_RDR: Registered SDR SDRAM
|
||||
* @MEM_DDR: Double data rate SDRAM
|
||||
* http://en.wikipedia.org/wiki/DDR_SDRAM
|
||||
* @MEM_RDDR: Registered Double data rate SDRAM
|
||||
* This is a variant of the DDR memories.
|
||||
* A registered memory has a buffer inside it, hiding
|
||||
* part of the memory details to the memory controller.
|
||||
* @MEM_RMBS: Rambus DRAM, used on a few Pentium III/IV controllers.
|
||||
* @MEM_DDR2: DDR2 RAM, as described at JEDEC JESD79-2F.
|
||||
* Those memories are labed as "PC2-" instead of "PC" to
|
||||
* differenciate from DDR.
|
||||
* @MEM_FB_DDR2: Fully-Buffered DDR2, as described at JEDEC Std No. 205
|
||||
* and JESD206.
|
||||
* Those memories are accessed per DIMM slot, and not by
|
||||
* a chip select signal.
|
||||
* @MEM_RDDR2: Registered DDR2 RAM
|
||||
* This is a variant of the DDR2 memories.
|
||||
* @MEM_XDR: Rambus XDR
|
||||
* It is an evolution of the original RAMBUS memories,
|
||||
* created to compete with DDR2. Weren't used on any
|
||||
* x86 arch, but cell_edac PPC memory controller uses it.
|
||||
* @MEM_DDR3: DDR3 RAM
|
||||
* @MEM_RDDR3: Registered DDR3 RAM
|
||||
* This is a variant of the DDR3 memories.
|
||||
*/
|
||||
enum mem_type {
|
||||
MEM_EMPTY = 0, /* Empty csrow */
|
||||
MEM_RESERVED, /* Reserved csrow type */
|
||||
MEM_UNKNOWN, /* Unknown csrow type */
|
||||
MEM_FPM, /* Fast page mode */
|
||||
MEM_EDO, /* Extended data out */
|
||||
MEM_BEDO, /* Burst Extended data out */
|
||||
MEM_SDR, /* Single data rate SDRAM */
|
||||
MEM_RDR, /* Registered single data rate SDRAM */
|
||||
MEM_DDR, /* Double data rate SDRAM */
|
||||
MEM_RDDR, /* Registered Double data rate SDRAM */
|
||||
MEM_RMBS, /* Rambus DRAM */
|
||||
MEM_DDR2, /* DDR2 RAM */
|
||||
MEM_FB_DDR2, /* fully buffered DDR2 */
|
||||
MEM_RDDR2, /* Registered DDR2 RAM */
|
||||
MEM_XDR, /* Rambus XDR */
|
||||
MEM_DDR3, /* DDR3 RAM */
|
||||
MEM_RDDR3, /* Registered DDR3 RAM */
|
||||
MEM_EMPTY = 0,
|
||||
MEM_RESERVED,
|
||||
MEM_UNKNOWN,
|
||||
MEM_FPM,
|
||||
MEM_EDO,
|
||||
MEM_BEDO,
|
||||
MEM_SDR,
|
||||
MEM_RDR,
|
||||
MEM_DDR,
|
||||
MEM_RDDR,
|
||||
MEM_RMBS,
|
||||
MEM_DDR2,
|
||||
MEM_FB_DDR2,
|
||||
MEM_RDDR2,
|
||||
MEM_XDR,
|
||||
MEM_DDR3,
|
||||
MEM_RDDR3,
|
||||
};
|
||||
|
||||
#define MEM_FLAG_EMPTY BIT(MEM_EMPTY)
|
||||
|
@ -166,8 +205,9 @@ enum scrub_type {
|
|||
#define OP_OFFLINE 0x300
|
||||
|
||||
/*
|
||||
* There are several things to be aware of that aren't at all obvious:
|
||||
* Concepts used at the EDAC subsystem
|
||||
*
|
||||
* There are several things to be aware of that aren't at all obvious:
|
||||
*
|
||||
* SOCKETS, SOCKET SETS, BANKS, ROWS, CHIP-SELECT ROWS, CHANNELS, etc..
|
||||
*
|
||||
|
@ -176,36 +216,61 @@ enum scrub_type {
|
|||
* creating a common ground for discussion, terms and their definitions
|
||||
* will be established.
|
||||
*
|
||||
* Memory devices: The individual chip on a memory stick. These devices
|
||||
* commonly output 4 and 8 bits each. Grouping several
|
||||
* of these in parallel provides 64 bits which is common
|
||||
* for a memory stick.
|
||||
* Memory devices: The individual DRAM chips on a memory stick. These
|
||||
* devices commonly output 4 and 8 bits each (x4, x8).
|
||||
* Grouping several of these in parallel provides the
|
||||
* number of bits that the memory controller expects:
|
||||
* typically 72 bits, in order to provide 64 bits +
|
||||
* 8 bits of ECC data.
|
||||
*
|
||||
* Memory Stick: A printed circuit board that aggregates multiple
|
||||
* memory devices in parallel. This is the atomic
|
||||
* memory component that is purchaseable by Joe consumer
|
||||
* and loaded into a memory socket.
|
||||
* memory devices in parallel. In general, this is the
|
||||
* Field Replaceable Unit (FRU) which gets replaced, in
|
||||
* the case of excessive errors. Most often it is also
|
||||
* called DIMM (Dual Inline Memory Module).
|
||||
*
|
||||
* Socket: A physical connector on the motherboard that accepts
|
||||
* a single memory stick.
|
||||
* Memory Socket: A physical connector on the motherboard that accepts
|
||||
* a single memory stick. Also called as "slot" on several
|
||||
* datasheets.
|
||||
*
|
||||
* Channel: Set of memory devices on a memory stick that must be
|
||||
* grouped in parallel with one or more additional
|
||||
* channels from other memory sticks. This parallel
|
||||
* grouping of the output from multiple channels are
|
||||
* necessary for the smallest granularity of memory access.
|
||||
* Some memory controllers are capable of single channel -
|
||||
* which means that memory sticks can be loaded
|
||||
* individually. Other memory controllers are only
|
||||
* capable of dual channel - which means that memory
|
||||
* sticks must be loaded as pairs (see "socket set").
|
||||
* Channel: A memory controller channel, responsible to communicate
|
||||
* with a group of DIMMs. Each channel has its own
|
||||
* independent control (command) and data bus, and can
|
||||
* be used independently or grouped with other channels.
|
||||
*
|
||||
* Chip-select row: All of the memory devices that are selected together.
|
||||
* for a single, minimum grain of memory access.
|
||||
* This selects all of the parallel memory devices across
|
||||
* all of the parallel channels. Common chip-select rows
|
||||
* for single channel are 64 bits, for dual channel 128
|
||||
* bits.
|
||||
* Branch: It is typically the highest hierarchy on a
|
||||
* Fully-Buffered DIMM memory controller.
|
||||
* Typically, it contains two channels.
|
||||
* Two channels at the same branch can be used in single
|
||||
* mode or in lockstep mode.
|
||||
* When lockstep is enabled, the cacheline is doubled,
|
||||
* but it generally brings some performance penalty.
|
||||
* Also, it is generally not possible to point to just one
|
||||
* memory stick when an error occurs, as the error
|
||||
* correction code is calculated using two DIMMs instead
|
||||
* of one. Due to that, it is capable of correcting more
|
||||
* errors than on single mode.
|
||||
*
|
||||
* Single-channel: The data accessed by the memory controller is contained
|
||||
* into one dimm only. E. g. if the data is 64 bits-wide,
|
||||
* the data flows to the CPU using one 64 bits parallel
|
||||
* access.
|
||||
* Typically used with SDR, DDR, DDR2 and DDR3 memories.
|
||||
* FB-DIMM and RAMBUS use a different concept for channel,
|
||||
* so this concept doesn't apply there.
|
||||
*
|
||||
* Double-channel: The data size accessed by the memory controller is
|
||||
* interlaced into two dimms, accessed at the same time.
|
||||
* E. g. if the DIMM is 64 bits-wide (72 bits with ECC),
|
||||
* the data flows to the CPU using a 128 bits parallel
|
||||
* access.
|
||||
*
|
||||
* Chip-select row: This is the name of the DRAM signal used to select the
|
||||
* DRAM ranks to be accessed. Common chip-select rows for
|
||||
* single channel are 64 bits, for dual channel 128 bits.
|
||||
* It may not be visible by the memory controller, as some
|
||||
* DIMM types have a memory buffer that can hide direct
|
||||
* access to it from the Memory Controller.
|
||||
*
|
||||
* Single-Ranked stick: A Single-ranked stick has 1 chip-select row of memory.
|
||||
* Motherboards commonly drive two chip-select pins to
|
||||
|
@ -218,8 +283,8 @@ enum scrub_type {
|
|||
*
|
||||
* Double-sided stick: DEPRECATED TERM, see Double-Ranked stick.
|
||||
* A double-sided stick has two chip-select rows which
|
||||
* access different sets of memory devices. The two
|
||||
* rows cannot be accessed concurrently. "Double-sided"
|
||||
* access different sets of memory devices. The two
|
||||
* rows cannot be accessed concurrently. "Double-sided"
|
||||
* is irrespective of the memory devices being mounted
|
||||
* on both sides of the memory stick.
|
||||
*
|
||||
|
@ -247,10 +312,22 @@ enum scrub_type {
|
|||
* PS - I enjoyed writing all that about as much as you enjoyed reading it.
|
||||
*/
|
||||
|
||||
struct channel_info {
|
||||
int chan_idx; /* channel index */
|
||||
u32 ce_count; /* Correctable Errors for this CHANNEL */
|
||||
char label[EDAC_MC_LABEL_LEN + 1]; /* DIMM label on motherboard */
|
||||
/**
|
||||
* struct rank_info - contains the information for one DIMM rank
|
||||
*
|
||||
* @chan_idx: channel number where the rank is (typically, 0 or 1)
|
||||
* @ce_count: number of correctable errors for this rank
|
||||
* @label: DIMM label. Different ranks for the same DIMM should be
|
||||
* filled, on userspace, with the same label.
|
||||
* FIXME: The core currently won't enforce it.
|
||||
* @csrow: A pointer to the chip select row structure (the parent
|
||||
* structure). The location of the rank is given by
|
||||
* the (csrow->csrow_idx, chan_idx) vector.
|
||||
*/
|
||||
struct rank_info {
|
||||
int chan_idx;
|
||||
u32 ce_count;
|
||||
char label[EDAC_MC_LABEL_LEN + 1];
|
||||
struct csrow_info *csrow; /* the parent */
|
||||
};
|
||||
|
||||
|
@ -274,7 +351,7 @@ struct csrow_info {
|
|||
|
||||
/* channel information for this csrow */
|
||||
u32 nr_channels;
|
||||
struct channel_info *channels;
|
||||
struct rank_info *channels;
|
||||
};
|
||||
|
||||
struct mcidev_sysfs_group {
|
||||
|
|
Loading…
Reference in a new issue