diff options
Diffstat (limited to 'drivers/edac/e752x_edac.c')
-rw-r--r-- | drivers/edac/e752x_edac.c | 116 |
1 files changed, 79 insertions, 37 deletions
diff --git a/drivers/edac/e752x_edac.c b/drivers/edac/e752x_edac.c index 41223261ede..3186512c973 100644 --- a/drivers/edac/e752x_edac.c +++ b/drivers/edac/e752x_edac.c @@ -4,7 +4,11 @@ * This file may be distributed under the terms of the * GNU General Public License. * - * See "enum e752x_chips" below for supported chipsets + * Implement support for the e7520, E7525, e7320 and i3100 memory controllers. + * + * Datasheets: + * http://www.intel.in/content/www/in/en/chipsets/e7525-memory-controller-hub-datasheet.html + * ftp://download.intel.com/design/intarch/datashts/31345803.pdf * * Written by Tom Zimmerman * @@ -13,8 +17,6 @@ * Wang Zhenyu at intel.com * Dave Jiang at mvista.com * - * $Id: edac_e752x.c,v 1.5.2.11 2005/10/05 00:43:44 dsp_llnl Exp $ - * */ #include <linux/module.h> @@ -187,6 +189,25 @@ enum e752x_chips { I3100 = 3 }; +/* + * Those chips Support single-rank and dual-rank memories only. + * + * On e752x chips, the odd rows are present only on dual-rank memories. + * Dividing the rank by two will provide the dimm# + * + * i3100 MC has a different mapping: it supports only 4 ranks. + * + * The mapping is (from 1 to n): + * slot single-ranked double-ranked + * dimm #1 -> rank #4 NA + * dimm #2 -> rank #3 NA + * dimm #3 -> rank #2 Ranks 2 and 3 + * dimm #4 -> rank $1 Ranks 1 and 4 + * + * FIXME: The current mapping for i3100 considers that it supports up to 8 + * ranks/chanel, but datasheet says that the MC supports only 4 ranks. + */ + struct e752x_pvt { struct pci_dev *bridge_ck; struct pci_dev *dev_d0f0; @@ -350,8 +371,10 @@ static void do_process_ce(struct mem_ctl_info *mci, u16 error_one, channel = !(error_one & 1); /* e752x mc reads 34:6 of the DRAM linear address */ - edac_mc_handle_ce(mci, page, offset_in_page(sec1_add << 4), - sec1_syndrome, row, channel, "e752x CE"); + edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, + page, offset_in_page(sec1_add << 4), sec1_syndrome, + row, channel, -1, + "e752x CE", "", NULL); } static inline void process_ce(struct mem_ctl_info *mci, u16 error_one, @@ -385,9 +408,12 @@ static void do_process_ue(struct mem_ctl_info *mci, u16 error_one, edac_mc_find_csrow_by_page(mci, block_page); /* e752x mc reads 34:6 of the DRAM linear address */ - edac_mc_handle_ue(mci, block_page, - offset_in_page(error_2b << 4), - row, "e752x UE from Read"); + edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, + block_page, + offset_in_page(error_2b << 4), 0, + row, -1, -1, + "e752x UE from Read", "", NULL); + } if (error_one & 0x0404) { error_2b = scrb_add; @@ -401,9 +427,11 @@ static void do_process_ue(struct mem_ctl_info *mci, u16 error_one, edac_mc_find_csrow_by_page(mci, block_page); /* e752x mc reads 34:6 of the DRAM linear address */ - edac_mc_handle_ue(mci, block_page, - offset_in_page(error_2b << 4), - row, "e752x UE from Scruber"); + edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, + block_page, + offset_in_page(error_2b << 4), 0, + row, -1, -1, + "e752x UE from Scruber", "", NULL); } } @@ -426,7 +454,9 @@ static inline void process_ue_no_info_wr(struct mem_ctl_info *mci, return; debugf3("%s()\n", __func__); - edac_mc_handle_ue_no_info(mci, "e752x UE log memory write"); + edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 0, 0, 0, + -1, -1, -1, + "e752x UE log memory write", "", NULL); } static void do_process_ded_retry(struct mem_ctl_info *mci, u16 error, @@ -1044,7 +1074,7 @@ static void e752x_init_csrows(struct mem_ctl_info *mci, struct pci_dev *pdev, int drc_drbg; /* DRB granularity 0=64mb, 1=128mb */ int drc_ddim; /* DRAM Data Integrity Mode 0=none, 2=edac */ u8 value; - u32 dra, drc, cumul_size; + u32 dra, drc, cumul_size, i, nr_pages; dra = 0; for (index = 0; index < 4; index++) { @@ -1053,7 +1083,7 @@ static void e752x_init_csrows(struct mem_ctl_info *mci, struct pci_dev *pdev, dra |= dra_reg << (index * 8); } pci_read_config_dword(pdev, E752X_DRC, &drc); - drc_chan = dual_channel_active(ddrcsr); + drc_chan = dual_channel_active(ddrcsr) ? 1 : 0; drc_drbg = drc_chan + 1; /* 128 in dual mode, 64 in single */ drc_ddim = (drc >> 20) & 0x3; @@ -1078,26 +1108,33 @@ static void e752x_init_csrows(struct mem_ctl_info *mci, struct pci_dev *pdev, csrow->first_page = last_cumul_size; csrow->last_page = cumul_size - 1; - csrow->nr_pages = cumul_size - last_cumul_size; + nr_pages = cumul_size - last_cumul_size; last_cumul_size = cumul_size; - csrow->grain = 1 << 12; /* 4KiB - resolution of CELOG */ - csrow->mtype = MEM_RDDR; /* only one type supported */ - csrow->dtype = mem_dev ? DEV_X4 : DEV_X8; - - /* - * if single channel or x8 devices then SECDED - * if dual channel and x4 then S4ECD4ED - */ - if (drc_ddim) { - if (drc_chan && mem_dev) { - csrow->edac_mode = EDAC_S4ECD4ED; - mci->edac_cap |= EDAC_FLAG_S4ECD4ED; - } else { - csrow->edac_mode = EDAC_SECDED; - mci->edac_cap |= EDAC_FLAG_SECDED; - } - } else - csrow->edac_mode = EDAC_NONE; + + for (i = 0; i < csrow->nr_channels; i++) { + struct dimm_info *dimm = csrow->channels[i].dimm; + + debugf3("Initializing rank at (%i,%i)\n", index, i); + dimm->nr_pages = nr_pages / csrow->nr_channels; + dimm->grain = 1 << 12; /* 4KiB - resolution of CELOG */ + dimm->mtype = MEM_RDDR; /* only one type supported */ + dimm->dtype = mem_dev ? DEV_X4 : DEV_X8; + + /* + * if single channel or x8 devices then SECDED + * if dual channel and x4 then S4ECD4ED + */ + if (drc_ddim) { + if (drc_chan && mem_dev) { + dimm->edac_mode = EDAC_S4ECD4ED; + mci->edac_cap |= EDAC_FLAG_S4ECD4ED; + } else { + dimm->edac_mode = EDAC_SECDED; + mci->edac_cap |= EDAC_FLAG_SECDED; + } + } else + dimm->edac_mode = EDAC_NONE; + } } } @@ -1226,6 +1263,7 @@ static int e752x_probe1(struct pci_dev *pdev, int dev_idx) u16 pci_data; u8 stat8; struct mem_ctl_info *mci; + struct edac_mc_layer layers[2]; struct e752x_pvt *pvt; u16 ddrcsr; int drc_chan; /* Number of channels 0=1chan,1=2chan */ @@ -1252,11 +1290,15 @@ static int e752x_probe1(struct pci_dev *pdev, int dev_idx) /* Dual channel = 1, Single channel = 0 */ drc_chan = dual_channel_active(ddrcsr); - mci = edac_mc_alloc(sizeof(*pvt), E752X_NR_CSROWS, drc_chan + 1, 0); - - if (mci == NULL) { + layers[0].type = EDAC_MC_LAYER_CHIP_SELECT; + layers[0].size = E752X_NR_CSROWS; + layers[0].is_virt_csrow = true; + layers[1].type = EDAC_MC_LAYER_CHANNEL; + layers[1].size = drc_chan + 1; + layers[1].is_virt_csrow = false; + mci = edac_mc_alloc(0, ARRAY_SIZE(layers), layers, sizeof(*pvt)); + if (mci == NULL) return -ENOMEM; - } debugf3("%s(): init mci\n", __func__); mci->mtype_cap = MEM_FLAG_RDDR; |