aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/edac/i3200_edac.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2013-02-28 20:42:33 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2013-02-28 20:42:33 -0800
commitad6c2c2eb34f234d6253292b9b3c047614fbfe7e (patch)
tree8ceb00db9874c09f3002b5ca579f1f9146b30a28 /drivers/edac/i3200_edac.c
parentFix mis-merge of intel_powerclamp.c resulting in compile error (diff)
parenti5100_edac: convert to use simple_open() (diff)
downloadlinux-dev-ad6c2c2eb34f234d6253292b9b3c047614fbfe7e.tar.xz
linux-dev-ad6c2c2eb34f234d6253292b9b3c047614fbfe7e.zip
Merge branch 'linux_next' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-edac
Pull EDAC fixes and ghes-edac from Mauro Carvalho Chehab: "For: - Some fixes at edac drivers (i7core_edac, sb_edac, i3200_edac); - error injection support for i5100, when EDAC debug is enabled; - fix edac when it is loaded builtin (early init for the subsystem); - a "Firmware First" EDAC driver, allowing ghes to report errors via EDAC (ghes-edac). With regards to ghes-edac, this fixes a longstanding BZ at Red Hat that happens with Nehalem and Sandy Bridge CPUs: when both GHES and i7core_edac or sb_edac are running, the error reports are unpredictable, as both BIOS and OS race to access the registers. With ghes-edac, the EDAC core will refuse to register any other concurrent memory error driver. This patchset moves the ghes struct definitions to a separate header file (include/acpi/ghes.h) and adds 3 hooks at apei/ghes.c to register/unregister and to report errors via ghes-edac. Those changes were acked by ghes driver maintainer (Huang)." * 'linux_next' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-edac: (30 commits) i5100_edac: convert to use simple_open() ghes_edac: fix to use list_for_each_entry_safe() when delete list items ghes_edac: Fix RAS tracing ghes_edac: Make it compliant with UEFI spec 2.3.1 ghes_edac: Improve driver's printk messages ghes_edac: Don't credit the same memory dimm twice ghes_edac: do a better job of filling EDAC DIMM info ghes_edac: add support for reporting errors via EDAC ghes_edac: Register at EDAC core the BIOS report ghes: add the needed hooks for EDAC error report ghes: move structures/enum to a header file edac: add support for error type "Info" edac: add support for raw error reports edac: reduce stack pressure by using a pre-allocated buffer edac: lock module owner to avoid error report conflicts edac: remove proc_name from mci structure edac: add a new memory layer type edac: initialize the core earlier edac: better report error conditions in debug mode i5100_edac: Remove two checkpatch warnings ...
Diffstat (limited to 'drivers/edac/i3200_edac.c')
-rw-r--r--drivers/edac/i3200_edac.c37
1 files changed, 26 insertions, 11 deletions
diff --git a/drivers/edac/i3200_edac.c b/drivers/edac/i3200_edac.c
index 4e8337602e78..aa44c1718f50 100644
--- a/drivers/edac/i3200_edac.c
+++ b/drivers/edac/i3200_edac.c
@@ -106,16 +106,26 @@ static int nr_channels;
static int how_many_channels(struct pci_dev *pdev)
{
+ int n_channels;
+
unsigned char capid0_8b; /* 8th byte of CAPID0 */
pci_read_config_byte(pdev, I3200_CAPID0 + 8, &capid0_8b);
+
if (capid0_8b & 0x20) { /* check DCD: Dual Channel Disable */
edac_dbg(0, "In single channel mode\n");
- return 1;
+ n_channels = 1;
} else {
edac_dbg(0, "In dual channel mode\n");
- return 2;
+ n_channels = 2;
}
+
+ if (capid0_8b & 0x10) /* check if both channels are filled */
+ edac_dbg(0, "2 DIMMS per channel disabled\n");
+ else
+ edac_dbg(0, "2 DIMMS per channel enabled\n");
+
+ return n_channels;
}
static unsigned long eccerrlog_syndrome(u64 log)
@@ -290,6 +300,8 @@ static void i3200_get_drbs(void __iomem *window,
for (i = 0; i < I3200_RANKS_PER_CHANNEL; i++) {
drbs[0][i] = readw(window + I3200_C0DRB + 2*i) & I3200_DRB_MASK;
drbs[1][i] = readw(window + I3200_C1DRB + 2*i) & I3200_DRB_MASK;
+
+ edac_dbg(0, "drb[0][%d] = %d, drb[1][%d] = %d\n", i, drbs[0][i], i, drbs[1][i]);
}
}
@@ -311,6 +323,9 @@ static unsigned long drb_to_nr_pages(
int n;
n = drbs[channel][rank];
+ if (!n)
+ return 0;
+
if (rank > 0)
n -= drbs[channel][rank - 1];
if (stacked && (channel == 1) &&
@@ -377,19 +392,19 @@ static int i3200_probe1(struct pci_dev *pdev, int dev_idx)
* cumulative; the last one will contain the total memory
* contained in all ranks.
*/
- for (i = 0; i < mci->nr_csrows; i++) {
+ for (i = 0; i < I3200_DIMMS; i++) {
unsigned long nr_pages;
- struct csrow_info *csrow = mci->csrows[i];
- nr_pages = drb_to_nr_pages(drbs, stacked,
- i / I3200_RANKS_PER_CHANNEL,
- i % I3200_RANKS_PER_CHANNEL);
+ for (j = 0; j < nr_channels; j++) {
+ struct dimm_info *dimm = EDAC_DIMM_PTR(mci->layers, mci->dimms,
+ mci->n_layers, i, j, 0);
- if (nr_pages == 0)
- continue;
+ nr_pages = drb_to_nr_pages(drbs, stacked, j, i);
+ if (nr_pages == 0)
+ continue;
- for (j = 0; j < nr_channels; j++) {
- struct dimm_info *dimm = csrow->channels[j]->dimm;
+ edac_dbg(0, "csrow %d, channel %d%s, size = %ld Mb\n", i, j,
+ stacked ? " (stacked)" : "", PAGES_TO_MiB(nr_pages));
dimm->nr_pages = nr_pages;
dimm->grain = nr_pages << PAGE_SHIFT;