forked from Minki/linux
5a0e3ad6af
percpu.h is included by sched.h and module.h and thus ends up being included when building most .c files. percpu.h includes slab.h which in turn includes gfp.h making everything defined by the two files universally available and complicating inclusion dependencies. percpu.h -> slab.h dependency is about to be removed. Prepare for this change by updating users of gfp and slab facilities include those headers directly instead of assuming availability. As this conversion needs to touch large number of source files, the following script is used as the basis of conversion. http://userweb.kernel.org/~tj/misc/slabh-sweep.py The script does the followings. * Scan files for gfp and slab usages and update includes such that only the necessary includes are there. ie. if only gfp is used, gfp.h, if slab is used, slab.h. * When the script inserts a new include, it looks at the include blocks and try to put the new include such that its order conforms to its surrounding. It's put in the include block which contains core kernel includes, in the same order that the rest are ordered - alphabetical, Christmas tree, rev-Xmas-tree or at the end if there doesn't seem to be any matching order. * If the script can't find a place to put a new include (mostly because the file doesn't have fitting include block), it prints out an error message indicating which .h file needs to be added to the file. The conversion was done in the following steps. 1. The initial automatic conversion of all .c files updated slightly over 4000 files, deleting around 700 includes and adding ~480 gfp.h and ~3000 slab.h inclusions. The script emitted errors for ~400 files. 2. Each error was manually checked. Some didn't need the inclusion, some needed manual addition while adding it to implementation .h or embedding .c file was more appropriate for others. This step added inclusions to around 150 files. 3. The script was run again and the output was compared to the edits from #2 to make sure no file was left behind. 4. Several build tests were done and a couple of problems were fixed. e.g. lib/decompress_*.c used malloc/free() wrappers around slab APIs requiring slab.h to be added manually. 5. The script was run on all .h files but without automatically editing them as sprinkling gfp.h and slab.h inclusions around .h files could easily lead to inclusion dependency hell. Most gfp.h inclusion directives were ignored as stuff from gfp.h was usually wildly available and often used in preprocessor macros. Each slab.h inclusion directive was examined and added manually as necessary. 6. percpu.h was updated not to include slab.h. 7. Build test were done on the following configurations and failures were fixed. CONFIG_GCOV_KERNEL was turned off for all tests (as my distributed build env didn't work with gcov compiles) and a few more options had to be turned off depending on archs to make things build (like ipr on powerpc/64 which failed due to missing writeq). * x86 and x86_64 UP and SMP allmodconfig and a custom test config. * powerpc and powerpc64 SMP allmodconfig * sparc and sparc64 SMP allmodconfig * ia64 SMP allmodconfig * s390 SMP allmodconfig * alpha SMP allmodconfig * um on x86_64 SMP allmodconfig 8. percpu.h modifications were reverted so that it could be applied as a separate patch and serve as bisection point. Given the fact that I had only a couple of failures from tests on step 6, I'm fairly confident about the coverage of this conversion patch. If there is a breakage, it's likely to be something in one of the arch headers which should be easily discoverable easily on most builds of the specific arch. Signed-off-by: Tejun Heo <tj@kernel.org> Guess-its-ok-by: Christoph Lameter <cl@linux-foundation.org> Cc: Ingo Molnar <mingo@redhat.com> Cc: Lee Schermerhorn <Lee.Schermerhorn@hp.com>
577 lines
15 KiB
C
577 lines
15 KiB
C
/*
|
|
* Intel e7xxx Memory Controller kernel module
|
|
* (C) 2003 Linux Networx (http://lnxi.com)
|
|
* This file may be distributed under the terms of the
|
|
* GNU General Public License.
|
|
*
|
|
* See "enum e7xxx_chips" below for supported chipsets
|
|
*
|
|
* Written by Thayne Harbaugh
|
|
* Based on work by Dan Hollis <goemon at anime dot net> and others.
|
|
* http://www.anime.net/~goemon/linux-ecc/
|
|
*
|
|
* Contributors:
|
|
* Eric Biederman (Linux Networx)
|
|
* Tom Zimmerman (Linux Networx)
|
|
* Jim Garlick (Lawrence Livermore National Labs)
|
|
* Dave Peterson (Lawrence Livermore National Labs)
|
|
* That One Guy (Some other place)
|
|
* Wang Zhenyu (intel.com)
|
|
*
|
|
* $Id: edac_e7xxx.c,v 1.5.2.9 2005/10/05 00:43:44 dsp_llnl Exp $
|
|
*
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/init.h>
|
|
#include <linux/pci.h>
|
|
#include <linux/pci_ids.h>
|
|
#include <linux/edac.h>
|
|
#include "edac_core.h"
|
|
|
|
#define E7XXX_REVISION " Ver: 2.0.2 " __DATE__
|
|
#define EDAC_MOD_STR "e7xxx_edac"
|
|
|
|
#define e7xxx_printk(level, fmt, arg...) \
|
|
edac_printk(level, "e7xxx", fmt, ##arg)
|
|
|
|
#define e7xxx_mc_printk(mci, level, fmt, arg...) \
|
|
edac_mc_chipset_printk(mci, level, "e7xxx", fmt, ##arg)
|
|
|
|
#ifndef PCI_DEVICE_ID_INTEL_7205_0
|
|
#define PCI_DEVICE_ID_INTEL_7205_0 0x255d
|
|
#endif /* PCI_DEVICE_ID_INTEL_7205_0 */
|
|
|
|
#ifndef PCI_DEVICE_ID_INTEL_7205_1_ERR
|
|
#define PCI_DEVICE_ID_INTEL_7205_1_ERR 0x2551
|
|
#endif /* PCI_DEVICE_ID_INTEL_7205_1_ERR */
|
|
|
|
#ifndef PCI_DEVICE_ID_INTEL_7500_0
|
|
#define PCI_DEVICE_ID_INTEL_7500_0 0x2540
|
|
#endif /* PCI_DEVICE_ID_INTEL_7500_0 */
|
|
|
|
#ifndef PCI_DEVICE_ID_INTEL_7500_1_ERR
|
|
#define PCI_DEVICE_ID_INTEL_7500_1_ERR 0x2541
|
|
#endif /* PCI_DEVICE_ID_INTEL_7500_1_ERR */
|
|
|
|
#ifndef PCI_DEVICE_ID_INTEL_7501_0
|
|
#define PCI_DEVICE_ID_INTEL_7501_0 0x254c
|
|
#endif /* PCI_DEVICE_ID_INTEL_7501_0 */
|
|
|
|
#ifndef PCI_DEVICE_ID_INTEL_7501_1_ERR
|
|
#define PCI_DEVICE_ID_INTEL_7501_1_ERR 0x2541
|
|
#endif /* PCI_DEVICE_ID_INTEL_7501_1_ERR */
|
|
|
|
#ifndef PCI_DEVICE_ID_INTEL_7505_0
|
|
#define PCI_DEVICE_ID_INTEL_7505_0 0x2550
|
|
#endif /* PCI_DEVICE_ID_INTEL_7505_0 */
|
|
|
|
#ifndef PCI_DEVICE_ID_INTEL_7505_1_ERR
|
|
#define PCI_DEVICE_ID_INTEL_7505_1_ERR 0x2551
|
|
#endif /* PCI_DEVICE_ID_INTEL_7505_1_ERR */
|
|
|
|
#define E7XXX_NR_CSROWS 8 /* number of csrows */
|
|
#define E7XXX_NR_DIMMS 8 /* FIXME - is this correct? */
|
|
|
|
/* E7XXX register addresses - device 0 function 0 */
|
|
#define E7XXX_DRB 0x60 /* DRAM row boundary register (8b) */
|
|
#define E7XXX_DRA 0x70 /* DRAM row attribute register (8b) */
|
|
/*
|
|
* 31 Device width row 7 0=x8 1=x4
|
|
* 27 Device width row 6
|
|
* 23 Device width row 5
|
|
* 19 Device width row 4
|
|
* 15 Device width row 3
|
|
* 11 Device width row 2
|
|
* 7 Device width row 1
|
|
* 3 Device width row 0
|
|
*/
|
|
#define E7XXX_DRC 0x7C /* DRAM controller mode reg (32b) */
|
|
/*
|
|
* 22 Number channels 0=1,1=2
|
|
* 19:18 DRB Granularity 32/64MB
|
|
*/
|
|
#define E7XXX_TOLM 0xC4 /* DRAM top of low memory reg (16b) */
|
|
#define E7XXX_REMAPBASE 0xC6 /* DRAM remap base address reg (16b) */
|
|
#define E7XXX_REMAPLIMIT 0xC8 /* DRAM remap limit address reg (16b) */
|
|
|
|
/* E7XXX register addresses - device 0 function 1 */
|
|
#define E7XXX_DRAM_FERR 0x80 /* DRAM first error register (8b) */
|
|
#define E7XXX_DRAM_NERR 0x82 /* DRAM next error register (8b) */
|
|
#define E7XXX_DRAM_CELOG_ADD 0xA0 /* DRAM first correctable memory */
|
|
/* error address register (32b) */
|
|
/*
|
|
* 31:28 Reserved
|
|
* 27:6 CE address (4k block 33:12)
|
|
* 5:0 Reserved
|
|
*/
|
|
#define E7XXX_DRAM_UELOG_ADD 0xB0 /* DRAM first uncorrectable memory */
|
|
/* error address register (32b) */
|
|
/*
|
|
* 31:28 Reserved
|
|
* 27:6 CE address (4k block 33:12)
|
|
* 5:0 Reserved
|
|
*/
|
|
#define E7XXX_DRAM_CELOG_SYNDROME 0xD0 /* DRAM first correctable memory */
|
|
/* error syndrome register (16b) */
|
|
|
|
enum e7xxx_chips {
|
|
E7500 = 0,
|
|
E7501,
|
|
E7505,
|
|
E7205,
|
|
};
|
|
|
|
struct e7xxx_pvt {
|
|
struct pci_dev *bridge_ck;
|
|
u32 tolm;
|
|
u32 remapbase;
|
|
u32 remaplimit;
|
|
const struct e7xxx_dev_info *dev_info;
|
|
};
|
|
|
|
struct e7xxx_dev_info {
|
|
u16 err_dev;
|
|
const char *ctl_name;
|
|
};
|
|
|
|
struct e7xxx_error_info {
|
|
u8 dram_ferr;
|
|
u8 dram_nerr;
|
|
u32 dram_celog_add;
|
|
u16 dram_celog_syndrome;
|
|
u32 dram_uelog_add;
|
|
};
|
|
|
|
static struct edac_pci_ctl_info *e7xxx_pci;
|
|
|
|
static const struct e7xxx_dev_info e7xxx_devs[] = {
|
|
[E7500] = {
|
|
.err_dev = PCI_DEVICE_ID_INTEL_7500_1_ERR,
|
|
.ctl_name = "E7500"},
|
|
[E7501] = {
|
|
.err_dev = PCI_DEVICE_ID_INTEL_7501_1_ERR,
|
|
.ctl_name = "E7501"},
|
|
[E7505] = {
|
|
.err_dev = PCI_DEVICE_ID_INTEL_7505_1_ERR,
|
|
.ctl_name = "E7505"},
|
|
[E7205] = {
|
|
.err_dev = PCI_DEVICE_ID_INTEL_7205_1_ERR,
|
|
.ctl_name = "E7205"},
|
|
};
|
|
|
|
/* FIXME - is this valid for both SECDED and S4ECD4ED? */
|
|
static inline int e7xxx_find_channel(u16 syndrome)
|
|
{
|
|
debugf3("%s()\n", __func__);
|
|
|
|
if ((syndrome & 0xff00) == 0)
|
|
return 0;
|
|
|
|
if ((syndrome & 0x00ff) == 0)
|
|
return 1;
|
|
|
|
if ((syndrome & 0xf000) == 0 || (syndrome & 0x0f00) == 0)
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static unsigned long ctl_page_to_phys(struct mem_ctl_info *mci,
|
|
unsigned long page)
|
|
{
|
|
u32 remap;
|
|
struct e7xxx_pvt *pvt = (struct e7xxx_pvt *)mci->pvt_info;
|
|
|
|
debugf3("%s()\n", __func__);
|
|
|
|
if ((page < pvt->tolm) ||
|
|
((page >= 0x100000) && (page < pvt->remapbase)))
|
|
return page;
|
|
|
|
remap = (page - pvt->tolm) + pvt->remapbase;
|
|
|
|
if (remap < pvt->remaplimit)
|
|
return remap;
|
|
|
|
e7xxx_printk(KERN_ERR, "Invalid page %lx - out of range\n", page);
|
|
return pvt->tolm - 1;
|
|
}
|
|
|
|
static void process_ce(struct mem_ctl_info *mci, struct e7xxx_error_info *info)
|
|
{
|
|
u32 error_1b, page;
|
|
u16 syndrome;
|
|
int row;
|
|
int channel;
|
|
|
|
debugf3("%s()\n", __func__);
|
|
/* read the error address */
|
|
error_1b = info->dram_celog_add;
|
|
/* FIXME - should use PAGE_SHIFT */
|
|
page = error_1b >> 6; /* convert the address to 4k page */
|
|
/* read the syndrome */
|
|
syndrome = info->dram_celog_syndrome;
|
|
/* FIXME - check for -1 */
|
|
row = edac_mc_find_csrow_by_page(mci, page);
|
|
/* convert syndrome to channel */
|
|
channel = e7xxx_find_channel(syndrome);
|
|
edac_mc_handle_ce(mci, page, 0, syndrome, row, channel, "e7xxx CE");
|
|
}
|
|
|
|
static void process_ce_no_info(struct mem_ctl_info *mci)
|
|
{
|
|
debugf3("%s()\n", __func__);
|
|
edac_mc_handle_ce_no_info(mci, "e7xxx CE log register overflow");
|
|
}
|
|
|
|
static void process_ue(struct mem_ctl_info *mci, struct e7xxx_error_info *info)
|
|
{
|
|
u32 error_2b, block_page;
|
|
int row;
|
|
|
|
debugf3("%s()\n", __func__);
|
|
/* read the error address */
|
|
error_2b = info->dram_uelog_add;
|
|
/* FIXME - should use PAGE_SHIFT */
|
|
block_page = error_2b >> 6; /* convert to 4k address */
|
|
row = edac_mc_find_csrow_by_page(mci, block_page);
|
|
edac_mc_handle_ue(mci, block_page, 0, row, "e7xxx UE");
|
|
}
|
|
|
|
static void process_ue_no_info(struct mem_ctl_info *mci)
|
|
{
|
|
debugf3("%s()\n", __func__);
|
|
edac_mc_handle_ue_no_info(mci, "e7xxx UE log register overflow");
|
|
}
|
|
|
|
static void e7xxx_get_error_info(struct mem_ctl_info *mci,
|
|
struct e7xxx_error_info *info)
|
|
{
|
|
struct e7xxx_pvt *pvt;
|
|
|
|
pvt = (struct e7xxx_pvt *)mci->pvt_info;
|
|
pci_read_config_byte(pvt->bridge_ck, E7XXX_DRAM_FERR, &info->dram_ferr);
|
|
pci_read_config_byte(pvt->bridge_ck, E7XXX_DRAM_NERR, &info->dram_nerr);
|
|
|
|
if ((info->dram_ferr & 1) || (info->dram_nerr & 1)) {
|
|
pci_read_config_dword(pvt->bridge_ck, E7XXX_DRAM_CELOG_ADD,
|
|
&info->dram_celog_add);
|
|
pci_read_config_word(pvt->bridge_ck,
|
|
E7XXX_DRAM_CELOG_SYNDROME,
|
|
&info->dram_celog_syndrome);
|
|
}
|
|
|
|
if ((info->dram_ferr & 2) || (info->dram_nerr & 2))
|
|
pci_read_config_dword(pvt->bridge_ck, E7XXX_DRAM_UELOG_ADD,
|
|
&info->dram_uelog_add);
|
|
|
|
if (info->dram_ferr & 3)
|
|
pci_write_bits8(pvt->bridge_ck, E7XXX_DRAM_FERR, 0x03, 0x03);
|
|
|
|
if (info->dram_nerr & 3)
|
|
pci_write_bits8(pvt->bridge_ck, E7XXX_DRAM_NERR, 0x03, 0x03);
|
|
}
|
|
|
|
static int e7xxx_process_error_info(struct mem_ctl_info *mci,
|
|
struct e7xxx_error_info *info,
|
|
int handle_errors)
|
|
{
|
|
int error_found;
|
|
|
|
error_found = 0;
|
|
|
|
/* decode and report errors */
|
|
if (info->dram_ferr & 1) { /* check first error correctable */
|
|
error_found = 1;
|
|
|
|
if (handle_errors)
|
|
process_ce(mci, info);
|
|
}
|
|
|
|
if (info->dram_ferr & 2) { /* check first error uncorrectable */
|
|
error_found = 1;
|
|
|
|
if (handle_errors)
|
|
process_ue(mci, info);
|
|
}
|
|
|
|
if (info->dram_nerr & 1) { /* check next error correctable */
|
|
error_found = 1;
|
|
|
|
if (handle_errors) {
|
|
if (info->dram_ferr & 1)
|
|
process_ce_no_info(mci);
|
|
else
|
|
process_ce(mci, info);
|
|
}
|
|
}
|
|
|
|
if (info->dram_nerr & 2) { /* check next error uncorrectable */
|
|
error_found = 1;
|
|
|
|
if (handle_errors) {
|
|
if (info->dram_ferr & 2)
|
|
process_ue_no_info(mci);
|
|
else
|
|
process_ue(mci, info);
|
|
}
|
|
}
|
|
|
|
return error_found;
|
|
}
|
|
|
|
static void e7xxx_check(struct mem_ctl_info *mci)
|
|
{
|
|
struct e7xxx_error_info info;
|
|
|
|
debugf3("%s()\n", __func__);
|
|
e7xxx_get_error_info(mci, &info);
|
|
e7xxx_process_error_info(mci, &info, 1);
|
|
}
|
|
|
|
/* Return 1 if dual channel mode is active. Else return 0. */
|
|
static inline int dual_channel_active(u32 drc, int dev_idx)
|
|
{
|
|
return (dev_idx == E7501) ? ((drc >> 22) & 0x1) : 1;
|
|
}
|
|
|
|
/* Return DRB granularity (0=32mb, 1=64mb). */
|
|
static inline int drb_granularity(u32 drc, int dev_idx)
|
|
{
|
|
/* only e7501 can be single channel */
|
|
return (dev_idx == E7501) ? ((drc >> 18) & 0x3) : 1;
|
|
}
|
|
|
|
static void e7xxx_init_csrows(struct mem_ctl_info *mci, struct pci_dev *pdev,
|
|
int dev_idx, u32 drc)
|
|
{
|
|
unsigned long last_cumul_size;
|
|
int index;
|
|
u8 value;
|
|
u32 dra, cumul_size;
|
|
int drc_chan, drc_drbg, drc_ddim, mem_dev;
|
|
struct csrow_info *csrow;
|
|
|
|
pci_read_config_dword(pdev, E7XXX_DRA, &dra);
|
|
drc_chan = dual_channel_active(drc, dev_idx);
|
|
drc_drbg = drb_granularity(drc, dev_idx);
|
|
drc_ddim = (drc >> 20) & 0x3;
|
|
last_cumul_size = 0;
|
|
|
|
/* The dram row boundary (DRB) reg values are boundary address
|
|
* for each DRAM row with a granularity of 32 or 64MB (single/dual
|
|
* channel operation). DRB regs are cumulative; therefore DRB7 will
|
|
* contain the total memory contained in all eight rows.
|
|
*/
|
|
for (index = 0; index < mci->nr_csrows; index++) {
|
|
/* mem_dev 0=x8, 1=x4 */
|
|
mem_dev = (dra >> (index * 4 + 3)) & 0x1;
|
|
csrow = &mci->csrows[index];
|
|
|
|
pci_read_config_byte(pdev, E7XXX_DRB + index, &value);
|
|
/* convert a 64 or 32 MiB DRB to a page size. */
|
|
cumul_size = value << (25 + drc_drbg - PAGE_SHIFT);
|
|
debugf3("%s(): (%d) cumul_size 0x%x\n", __func__, index,
|
|
cumul_size);
|
|
if (cumul_size == last_cumul_size)
|
|
continue; /* not populated */
|
|
|
|
csrow->first_page = last_cumul_size;
|
|
csrow->last_page = cumul_size - 1;
|
|
csrow->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;
|
|
}
|
|
}
|
|
|
|
static int e7xxx_probe1(struct pci_dev *pdev, int dev_idx)
|
|
{
|
|
u16 pci_data;
|
|
struct mem_ctl_info *mci = NULL;
|
|
struct e7xxx_pvt *pvt = NULL;
|
|
u32 drc;
|
|
int drc_chan;
|
|
struct e7xxx_error_info discard;
|
|
|
|
debugf0("%s(): mci\n", __func__);
|
|
|
|
pci_read_config_dword(pdev, E7XXX_DRC, &drc);
|
|
|
|
drc_chan = dual_channel_active(drc, dev_idx);
|
|
mci = edac_mc_alloc(sizeof(*pvt), E7XXX_NR_CSROWS, drc_chan + 1, 0);
|
|
|
|
if (mci == NULL)
|
|
return -ENOMEM;
|
|
|
|
debugf3("%s(): init mci\n", __func__);
|
|
mci->mtype_cap = MEM_FLAG_RDDR;
|
|
mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED |
|
|
EDAC_FLAG_S4ECD4ED;
|
|
/* FIXME - what if different memory types are in different csrows? */
|
|
mci->mod_name = EDAC_MOD_STR;
|
|
mci->mod_ver = E7XXX_REVISION;
|
|
mci->dev = &pdev->dev;
|
|
debugf3("%s(): init pvt\n", __func__);
|
|
pvt = (struct e7xxx_pvt *)mci->pvt_info;
|
|
pvt->dev_info = &e7xxx_devs[dev_idx];
|
|
pvt->bridge_ck = pci_get_device(PCI_VENDOR_ID_INTEL,
|
|
pvt->dev_info->err_dev, pvt->bridge_ck);
|
|
|
|
if (!pvt->bridge_ck) {
|
|
e7xxx_printk(KERN_ERR, "error reporting device not found:"
|
|
"vendor %x device 0x%x (broken BIOS?)\n",
|
|
PCI_VENDOR_ID_INTEL, e7xxx_devs[dev_idx].err_dev);
|
|
goto fail0;
|
|
}
|
|
|
|
debugf3("%s(): more mci init\n", __func__);
|
|
mci->ctl_name = pvt->dev_info->ctl_name;
|
|
mci->dev_name = pci_name(pdev);
|
|
mci->edac_check = e7xxx_check;
|
|
mci->ctl_page_to_phys = ctl_page_to_phys;
|
|
e7xxx_init_csrows(mci, pdev, dev_idx, drc);
|
|
mci->edac_cap |= EDAC_FLAG_NONE;
|
|
debugf3("%s(): tolm, remapbase, remaplimit\n", __func__);
|
|
/* load the top of low memory, remap base, and remap limit vars */
|
|
pci_read_config_word(pdev, E7XXX_TOLM, &pci_data);
|
|
pvt->tolm = ((u32) pci_data) << 4;
|
|
pci_read_config_word(pdev, E7XXX_REMAPBASE, &pci_data);
|
|
pvt->remapbase = ((u32) pci_data) << 14;
|
|
pci_read_config_word(pdev, E7XXX_REMAPLIMIT, &pci_data);
|
|
pvt->remaplimit = ((u32) pci_data) << 14;
|
|
e7xxx_printk(KERN_INFO,
|
|
"tolm = %x, remapbase = %x, remaplimit = %x\n", pvt->tolm,
|
|
pvt->remapbase, pvt->remaplimit);
|
|
|
|
/* clear any pending errors, or initial state bits */
|
|
e7xxx_get_error_info(mci, &discard);
|
|
|
|
/* Here we assume that we will never see multiple instances of this
|
|
* type of memory controller. The ID is therefore hardcoded to 0.
|
|
*/
|
|
if (edac_mc_add_mc(mci)) {
|
|
debugf3("%s(): failed edac_mc_add_mc()\n", __func__);
|
|
goto fail1;
|
|
}
|
|
|
|
/* allocating generic PCI control info */
|
|
e7xxx_pci = edac_pci_create_generic_ctl(&pdev->dev, EDAC_MOD_STR);
|
|
if (!e7xxx_pci) {
|
|
printk(KERN_WARNING
|
|
"%s(): Unable to create PCI control\n",
|
|
__func__);
|
|
printk(KERN_WARNING
|
|
"%s(): PCI error report via EDAC not setup\n",
|
|
__func__);
|
|
}
|
|
|
|
/* get this far and it's successful */
|
|
debugf3("%s(): success\n", __func__);
|
|
return 0;
|
|
|
|
fail1:
|
|
pci_dev_put(pvt->bridge_ck);
|
|
|
|
fail0:
|
|
edac_mc_free(mci);
|
|
|
|
return -ENODEV;
|
|
}
|
|
|
|
/* returns count (>= 0), or negative on error */
|
|
static int __devinit e7xxx_init_one(struct pci_dev *pdev,
|
|
const struct pci_device_id *ent)
|
|
{
|
|
debugf0("%s()\n", __func__);
|
|
|
|
/* wake up and enable device */
|
|
return pci_enable_device(pdev) ?
|
|
-EIO : e7xxx_probe1(pdev, ent->driver_data);
|
|
}
|
|
|
|
static void __devexit e7xxx_remove_one(struct pci_dev *pdev)
|
|
{
|
|
struct mem_ctl_info *mci;
|
|
struct e7xxx_pvt *pvt;
|
|
|
|
debugf0("%s()\n", __func__);
|
|
|
|
if (e7xxx_pci)
|
|
edac_pci_release_generic_ctl(e7xxx_pci);
|
|
|
|
if ((mci = edac_mc_del_mc(&pdev->dev)) == NULL)
|
|
return;
|
|
|
|
pvt = (struct e7xxx_pvt *)mci->pvt_info;
|
|
pci_dev_put(pvt->bridge_ck);
|
|
edac_mc_free(mci);
|
|
}
|
|
|
|
static const struct pci_device_id e7xxx_pci_tbl[] __devinitdata = {
|
|
{
|
|
PCI_VEND_DEV(INTEL, 7205_0), PCI_ANY_ID, PCI_ANY_ID, 0, 0,
|
|
E7205},
|
|
{
|
|
PCI_VEND_DEV(INTEL, 7500_0), PCI_ANY_ID, PCI_ANY_ID, 0, 0,
|
|
E7500},
|
|
{
|
|
PCI_VEND_DEV(INTEL, 7501_0), PCI_ANY_ID, PCI_ANY_ID, 0, 0,
|
|
E7501},
|
|
{
|
|
PCI_VEND_DEV(INTEL, 7505_0), PCI_ANY_ID, PCI_ANY_ID, 0, 0,
|
|
E7505},
|
|
{
|
|
0,
|
|
} /* 0 terminated list. */
|
|
};
|
|
|
|
MODULE_DEVICE_TABLE(pci, e7xxx_pci_tbl);
|
|
|
|
static struct pci_driver e7xxx_driver = {
|
|
.name = EDAC_MOD_STR,
|
|
.probe = e7xxx_init_one,
|
|
.remove = __devexit_p(e7xxx_remove_one),
|
|
.id_table = e7xxx_pci_tbl,
|
|
};
|
|
|
|
static int __init e7xxx_init(void)
|
|
{
|
|
/* Ensure that the OPSTATE is set correctly for POLL or NMI */
|
|
opstate_init();
|
|
|
|
return pci_register_driver(&e7xxx_driver);
|
|
}
|
|
|
|
static void __exit e7xxx_exit(void)
|
|
{
|
|
pci_unregister_driver(&e7xxx_driver);
|
|
}
|
|
|
|
module_init(e7xxx_init);
|
|
module_exit(e7xxx_exit);
|
|
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_AUTHOR("Linux Networx (http://lnxi.com) Thayne Harbaugh et al\n"
|
|
"Based on.work by Dan Hollis et al");
|
|
MODULE_DESCRIPTION("MC support for Intel e7xxx memory controllers");
|
|
module_param(edac_op_state, int, 0444);
|
|
MODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI");
|