Creation of Cybook 2416 (actually Gen4) repository

This commit is contained in:
mlt
2009-12-18 17:10:00 +00:00
committed by godzil
commit 76f20f4d40
13791 changed files with 6812321 additions and 0 deletions

113
drivers/edac/Kconfig Normal file
View File

@@ -0,0 +1,113 @@
#
# EDAC Kconfig
# Copyright (c) 2003 Linux Networx
# Licensed and distributed under the GPL
#
# $Id: Kconfig,v 1.1.1.1 2007/06/12 07:27:09 eyryu Exp $
#
menu 'EDAC - error detection and reporting (RAS) (EXPERIMENTAL)'
config EDAC
tristate "EDAC core system error reporting (EXPERIMENTAL)"
depends on X86 && EXPERIMENTAL
help
EDAC is designed to report errors in the core system.
These are low-level errors that are reported in the CPU or
supporting chipset: memory errors, cache errors, PCI errors,
thermal throttling, etc.. If unsure, select 'Y'.
If this code is reporting problems on your system, please
see the EDAC project web pages for more information at:
<http://bluesmoke.sourceforge.net/>
and:
<http://buttersideup.com/edacwiki>
There is also a mailing list for the EDAC project, which can
be found via the sourceforge page.
comment "Reporting subsystems"
depends on EDAC
config EDAC_DEBUG
bool "Debugging"
depends on EDAC
help
This turns on debugging information for the entire EDAC
sub-system. You can insert module with "debug_level=x", current
there're four debug levels (x=0,1,2,3 from low to high).
Usually you should select 'N'.
config EDAC_MM_EDAC
tristate "Main Memory EDAC (Error Detection And Correction) reporting"
depends on EDAC
default y
help
Some systems are able to detect and correct errors in main
memory. EDAC can report statistics on memory error
detection and correction (EDAC - or commonly referred to ECC
errors). EDAC will also try to decode where these errors
occurred so that a particular failing memory module can be
replaced. If unsure, select 'Y'.
config EDAC_AMD76X
tristate "AMD 76x (760, 762, 768)"
depends on EDAC_MM_EDAC && PCI && X86_32
help
Support for error detection and correction on the AMD 76x
series of chipsets used with the Athlon processor.
config EDAC_E7XXX
tristate "Intel e7xxx (e7205, e7500, e7501, e7505)"
depends on EDAC_MM_EDAC && PCI && X86_32
help
Support for error detection and correction on the Intel
E7205, E7500, E7501 and E7505 server chipsets.
config EDAC_E752X
tristate "Intel e752x (e7520, e7525, e7320)"
depends on EDAC_MM_EDAC && PCI && X86 && HOTPLUG
help
Support for error detection and correction on the Intel
E7520, E7525, E7320 server chipsets.
config EDAC_I82875P
tristate "Intel 82875p (D82875P, E7210)"
depends on EDAC_MM_EDAC && PCI && X86_32
help
Support for error detection and correction on the Intel
DP82785P and E7210 server chipsets.
config EDAC_I82860
tristate "Intel 82860"
depends on EDAC_MM_EDAC && PCI && X86_32
help
Support for error detection and correction on the Intel
82860 chipset.
config EDAC_R82600
tristate "Radisys 82600 embedded chipset"
depends on EDAC_MM_EDAC && PCI && X86_32
help
Support for error detection and correction on the Radisys
82600 embedded chipset.
choice
prompt "Error detecting method"
depends on EDAC
default EDAC_POLL
config EDAC_POLL
bool "Poll for errors"
depends on EDAC
help
Poll the chipset periodically to detect errors.
endchoice
endmenu

18
drivers/edac/Makefile Normal file
View File

@@ -0,0 +1,18 @@
#
# Makefile for the Linux kernel EDAC drivers.
#
# Copyright 02 Jul 2003, Linux Networx (http://lnxi.com)
# This file may be distributed under the terms of the
# GNU General Public License.
#
# $Id: Makefile,v 1.1.1.1 2007/06/12 07:27:09 eyryu Exp $
obj-$(CONFIG_EDAC_MM_EDAC) += edac_mc.o
obj-$(CONFIG_EDAC_AMD76X) += amd76x_edac.o
obj-$(CONFIG_EDAC_E7XXX) += e7xxx_edac.o
obj-$(CONFIG_EDAC_E752X) += e752x_edac.o
obj-$(CONFIG_EDAC_I82875P) += i82875p_edac.o
obj-$(CONFIG_EDAC_I82860) += i82860_edac.o
obj-$(CONFIG_EDAC_R82600) += r82600_edac.o

347
drivers/edac/amd76x_edac.c Normal file
View File

@@ -0,0 +1,347 @@
/*
* AMD 76x 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.
*
* Written by Thayne Harbaugh
* Based on work by Dan Hollis <goemon at anime dot net> and others.
* http://www.anime.net/~goemon/linux-ecc/
*
* $Id: amd76x_edac.c,v 1.1.1.1 2007/06/12 07:27:09 eyryu Exp $
*
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/pci_ids.h>
#include <linux/slab.h>
#include "edac_mc.h"
#define AMD76X_REVISION " Ver: 2.0.1 " __DATE__
#define EDAC_MOD_STR "amd76x_edac"
#define amd76x_printk(level, fmt, arg...) \
edac_printk(level, "amd76x", fmt, ##arg)
#define amd76x_mc_printk(mci, level, fmt, arg...) \
edac_mc_chipset_printk(mci, level, "amd76x", fmt, ##arg)
#define AMD76X_NR_CSROWS 8
#define AMD76X_NR_CHANS 1
#define AMD76X_NR_DIMMS 4
/* AMD 76x register addresses - device 0 function 0 - PCI bridge */
#define AMD76X_ECC_MODE_STATUS 0x48 /* Mode and status of ECC (32b)
*
* 31:16 reserved
* 15:14 SERR enabled: x1=ue 1x=ce
* 13 reserved
* 12 diag: disabled, enabled
* 11:10 mode: dis, EC, ECC, ECC+scrub
* 9:8 status: x1=ue 1x=ce
* 7:4 UE cs row
* 3:0 CE cs row
*/
#define AMD76X_DRAM_MODE_STATUS 0x58 /* DRAM Mode and status (32b)
*
* 31:26 clock disable 5 - 0
* 25 SDRAM init
* 24 reserved
* 23 mode register service
* 22:21 suspend to RAM
* 20 burst refresh enable
* 19 refresh disable
* 18 reserved
* 17:16 cycles-per-refresh
* 15:8 reserved
* 7:0 x4 mode enable 7 - 0
*/
#define AMD76X_MEM_BASE_ADDR 0xC0 /* Memory base address (8 x 32b)
*
* 31:23 chip-select base
* 22:16 reserved
* 15:7 chip-select mask
* 6:3 reserved
* 2:1 address mode
* 0 chip-select enable
*/
struct amd76x_error_info {
u32 ecc_mode_status;
};
enum amd76x_chips {
AMD761 = 0,
AMD762
};
struct amd76x_dev_info {
const char *ctl_name;
};
static const struct amd76x_dev_info amd76x_devs[] = {
[AMD761] = {
.ctl_name = "AMD761"
},
[AMD762] = {
.ctl_name = "AMD762"
},
};
/**
* amd76x_get_error_info - fetch error information
* @mci: Memory controller
* @info: Info to fill in
*
* Fetch and store the AMD76x ECC status. Clear pending status
* on the chip so that further errors will be reported
*/
static void amd76x_get_error_info(struct mem_ctl_info *mci,
struct amd76x_error_info *info)
{
struct pci_dev *pdev;
pdev = to_pci_dev(mci->dev);
pci_read_config_dword(pdev, AMD76X_ECC_MODE_STATUS,
&info->ecc_mode_status);
if (info->ecc_mode_status & BIT(8))
pci_write_bits32(pdev, AMD76X_ECC_MODE_STATUS,
(u32) BIT(8), (u32) BIT(8));
if (info->ecc_mode_status & BIT(9))
pci_write_bits32(pdev, AMD76X_ECC_MODE_STATUS,
(u32) BIT(9), (u32) BIT(9));
}
/**
* amd76x_process_error_info - Error check
* @mci: Memory controller
* @info: Previously fetched information from chip
* @handle_errors: 1 if we should do recovery
*
* Process the chip state and decide if an error has occurred.
* A return of 1 indicates an error. Also if handle_errors is true
* then attempt to handle and clean up after the error
*/
static int amd76x_process_error_info(struct mem_ctl_info *mci,
struct amd76x_error_info *info, int handle_errors)
{
int error_found;
u32 row;
error_found = 0;
/*
* Check for an uncorrectable error
*/
if (info->ecc_mode_status & BIT(8)) {
error_found = 1;
if (handle_errors) {
row = (info->ecc_mode_status >> 4) & 0xf;
edac_mc_handle_ue(mci, mci->csrows[row].first_page, 0,
row, mci->ctl_name);
}
}
/*
* Check for a correctable error
*/
if (info->ecc_mode_status & BIT(9)) {
error_found = 1;
if (handle_errors) {
row = info->ecc_mode_status & 0xf;
edac_mc_handle_ce(mci, mci->csrows[row].first_page, 0,
0, row, 0, mci->ctl_name);
}
}
return error_found;
}
/**
* amd76x_check - Poll the controller
* @mci: Memory controller
*
* Called by the poll handlers this function reads the status
* from the controller and checks for errors.
*/
static void amd76x_check(struct mem_ctl_info *mci)
{
struct amd76x_error_info info;
debugf3("%s()\n", __func__);
amd76x_get_error_info(mci, &info);
amd76x_process_error_info(mci, &info, 1);
}
static void amd76x_init_csrows(struct mem_ctl_info *mci, struct pci_dev *pdev,
enum edac_type edac_mode)
{
struct csrow_info *csrow;
u32 mba, mba_base, mba_mask, dms;
int index;
for (index = 0; index < mci->nr_csrows; index++) {
csrow = &mci->csrows[index];
/* find the DRAM Chip Select Base address and mask */
pci_read_config_dword(pdev,
AMD76X_MEM_BASE_ADDR + (index * 4),
&mba);
if (!(mba & BIT(0)))
continue;
mba_base = mba & 0xff800000UL;
mba_mask = ((mba & 0xff80) << 16) | 0x7fffffUL;
pci_read_config_dword(pdev, AMD76X_DRAM_MODE_STATUS, &dms);
csrow->first_page = mba_base >> PAGE_SHIFT;
csrow->nr_pages = (mba_mask + 1) >> PAGE_SHIFT;
csrow->last_page = csrow->first_page + csrow->nr_pages - 1;
csrow->page_mask = mba_mask >> PAGE_SHIFT;
csrow->grain = csrow->nr_pages << PAGE_SHIFT;
csrow->mtype = MEM_RDDR;
csrow->dtype = ((dms >> index) & 0x1) ? DEV_X4 : DEV_UNKNOWN;
csrow->edac_mode = edac_mode;
}
}
/**
* amd76x_probe1 - Perform set up for detected device
* @pdev; PCI device detected
* @dev_idx: Device type index
*
* We have found an AMD76x and now need to set up the memory
* controller status reporting. We configure and set up the
* memory controller reporting and claim the device.
*/
static int amd76x_probe1(struct pci_dev *pdev, int dev_idx)
{
static const enum edac_type ems_modes[] = {
EDAC_NONE,
EDAC_EC,
EDAC_SECDED,
EDAC_SECDED
};
struct mem_ctl_info *mci = NULL;
u32 ems;
u32 ems_mode;
struct amd76x_error_info discard;
debugf0("%s()\n", __func__);
pci_read_config_dword(pdev, AMD76X_ECC_MODE_STATUS, &ems);
ems_mode = (ems >> 10) & 0x3;
mci = edac_mc_alloc(0, AMD76X_NR_CSROWS, AMD76X_NR_CHANS);
if (mci == NULL) {
return -ENOMEM;
}
debugf0("%s(): mci = %p\n", __func__, mci);
mci->dev = &pdev->dev;
mci->mtype_cap = MEM_FLAG_RDDR;
mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_EC | EDAC_FLAG_SECDED;
mci->edac_cap = ems_mode ?
(EDAC_FLAG_EC | EDAC_FLAG_SECDED) : EDAC_FLAG_NONE;
mci->mod_name = EDAC_MOD_STR;
mci->mod_ver = AMD76X_REVISION;
mci->ctl_name = amd76x_devs[dev_idx].ctl_name;
mci->edac_check = amd76x_check;
mci->ctl_page_to_phys = NULL;
amd76x_init_csrows(mci, pdev, ems_modes[ems_mode]);
amd76x_get_error_info(mci, &discard); /* clear counters */
/* 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,0)) {
debugf3("%s(): failed edac_mc_add_mc()\n", __func__);
goto fail;
}
/* get this far and it's successful */
debugf3("%s(): success\n", __func__);
return 0;
fail:
edac_mc_free(mci);
return -ENODEV;
}
/* returns count (>= 0), or negative on error */
static int __devinit amd76x_init_one(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
debugf0("%s()\n", __func__);
/* don't need to call pci_device_enable() */
return amd76x_probe1(pdev, ent->driver_data);
}
/**
* amd76x_remove_one - driver shutdown
* @pdev: PCI device being handed back
*
* Called when the driver is unloaded. Find the matching mci
* structure for the device then delete the mci and free the
* resources.
*/
static void __devexit amd76x_remove_one(struct pci_dev *pdev)
{
struct mem_ctl_info *mci;
debugf0("%s()\n", __func__);
if ((mci = edac_mc_del_mc(&pdev->dev)) == NULL)
return;
edac_mc_free(mci);
}
static const struct pci_device_id amd76x_pci_tbl[] __devinitdata = {
{
PCI_VEND_DEV(AMD, FE_GATE_700C), PCI_ANY_ID, PCI_ANY_ID, 0, 0,
AMD762
},
{
PCI_VEND_DEV(AMD, FE_GATE_700E), PCI_ANY_ID, PCI_ANY_ID, 0, 0,
AMD761
},
{
0,
} /* 0 terminated list. */
};
MODULE_DEVICE_TABLE(pci, amd76x_pci_tbl);
static struct pci_driver amd76x_driver = {
.name = EDAC_MOD_STR,
.probe = amd76x_init_one,
.remove = __devexit_p(amd76x_remove_one),
.id_table = amd76x_pci_tbl,
};
static int __init amd76x_init(void)
{
return pci_register_driver(&amd76x_driver);
}
static void __exit amd76x_exit(void)
{
pci_unregister_driver(&amd76x_driver);
}
module_init(amd76x_init);
module_exit(amd76x_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Linux Networx (http://lnxi.com) Thayne Harbaugh");
MODULE_DESCRIPTION("MC support for AMD 76x memory controllers");

1126
drivers/edac/e752x_edac.c Normal file

File diff suppressed because it is too large Load Diff

567
drivers/edac/e7xxx_edac.c Normal file
View File

@@ -0,0 +1,567 @@
/*
* 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: e7xxx_edac.c,v 1.1.1.1 2007/06/12 07:27:09 eyryu Exp $
*
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/pci_ids.h>
#include <linux/slab.h>
#include "edac_mc.h"
#define E7XXX_REVISION " Ver: 2.0.1 " __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 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);
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->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,0)) {
debugf3("%s(): failed edac_mc_add_mc()\n", __func__);
goto fail1;
}
/* 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 ((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)
{
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");

2004
drivers/edac/edac_mc.c Normal file

File diff suppressed because it is too large Load Diff

481
drivers/edac/edac_mc.h Normal file
View File

@@ -0,0 +1,481 @@
/*
* MC kernel module
* (C) 2003 Linux Networx (http://lnxi.com)
* This file may be distributed under the terms of the
* GNU General Public License.
*
* Written by Thayne Harbaugh
* Based on work by Dan Hollis <goemon at anime dot net> and others.
* http://www.anime.net/~goemon/linux-ecc/
*
* NMI handling support added by
* Dave Peterson <dsp@llnl.gov> <dave_peterson@pobox.com>
*
* $Id: edac_mc.h,v 1.1.1.1 2007/06/12 07:27:09 eyryu Exp $
*
*/
#ifndef _EDAC_MC_H_
#define _EDAC_MC_H_
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/module.h>
#include <linux/spinlock.h>
#include <linux/smp.h>
#include <linux/pci.h>
#include <linux/time.h>
#include <linux/nmi.h>
#include <linux/rcupdate.h>
#include <linux/completion.h>
#include <linux/kobject.h>
#include <linux/platform_device.h>
#define EDAC_MC_LABEL_LEN 31
#define MC_PROC_NAME_MAX_LEN 7
#if PAGE_SHIFT < 20
#define PAGES_TO_MiB( pages ) ( ( pages ) >> ( 20 - PAGE_SHIFT ) )
#else /* PAGE_SHIFT > 20 */
#define PAGES_TO_MiB( pages ) ( ( pages ) << ( PAGE_SHIFT - 20 ) )
#endif
#define edac_printk(level, prefix, fmt, arg...) \
printk(level "EDAC " prefix ": " fmt, ##arg)
#define edac_mc_printk(mci, level, fmt, arg...) \
printk(level "EDAC MC%d: " fmt, mci->mc_idx, ##arg)
#define edac_mc_chipset_printk(mci, level, prefix, fmt, arg...) \
printk(level "EDAC " prefix " MC%d: " fmt, mci->mc_idx, ##arg)
/* prefixes for edac_printk() and edac_mc_printk() */
#define EDAC_MC "MC"
#define EDAC_PCI "PCI"
#define EDAC_DEBUG "DEBUG"
#ifdef CONFIG_EDAC_DEBUG
extern int edac_debug_level;
#define edac_debug_printk(level, fmt, arg...) \
do { \
if (level <= edac_debug_level) \
edac_printk(KERN_DEBUG, EDAC_DEBUG, fmt, ##arg); \
} while(0)
#define debugf0( ... ) edac_debug_printk(0, __VA_ARGS__ )
#define debugf1( ... ) edac_debug_printk(1, __VA_ARGS__ )
#define debugf2( ... ) edac_debug_printk(2, __VA_ARGS__ )
#define debugf3( ... ) edac_debug_printk(3, __VA_ARGS__ )
#define debugf4( ... ) edac_debug_printk(4, __VA_ARGS__ )
#else /* !CONFIG_EDAC_DEBUG */
#define debugf0( ... )
#define debugf1( ... )
#define debugf2( ... )
#define debugf3( ... )
#define debugf4( ... )
#endif /* !CONFIG_EDAC_DEBUG */
#define BIT(x) (1 << (x))
#define PCI_VEND_DEV(vend, dev) PCI_VENDOR_ID_ ## vend, \
PCI_DEVICE_ID_ ## vend ## _ ## dev
#if defined(CONFIG_X86) && defined(CONFIG_PCI)
#define dev_name(dev) pci_name(to_pci_dev(dev))
#else
#define dev_name(dev) to_platform_device(dev)->name
#endif
/* memory devices */
enum dev_type {
DEV_UNKNOWN = 0,
DEV_X1,
DEV_X2,
DEV_X4,
DEV_X8,
DEV_X16,
DEV_X32, /* Do these parts exist? */
DEV_X64 /* Do these parts exist? */
};
#define DEV_FLAG_UNKNOWN BIT(DEV_UNKNOWN)
#define DEV_FLAG_X1 BIT(DEV_X1)
#define DEV_FLAG_X2 BIT(DEV_X2)
#define DEV_FLAG_X4 BIT(DEV_X4)
#define DEV_FLAG_X8 BIT(DEV_X8)
#define DEV_FLAG_X16 BIT(DEV_X16)
#define DEV_FLAG_X32 BIT(DEV_X32)
#define DEV_FLAG_X64 BIT(DEV_X64)
/* memory types */
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 */
};
#define MEM_FLAG_EMPTY BIT(MEM_EMPTY)
#define MEM_FLAG_RESERVED BIT(MEM_RESERVED)
#define MEM_FLAG_UNKNOWN BIT(MEM_UNKNOWN)
#define MEM_FLAG_FPM BIT(MEM_FPM)
#define MEM_FLAG_EDO BIT(MEM_EDO)
#define MEM_FLAG_BEDO BIT(MEM_BEDO)
#define MEM_FLAG_SDR BIT(MEM_SDR)
#define MEM_FLAG_RDR BIT(MEM_RDR)
#define MEM_FLAG_DDR BIT(MEM_DDR)
#define MEM_FLAG_RDDR BIT(MEM_RDDR)
#define MEM_FLAG_RMBS BIT(MEM_RMBS)
#define MEM_FLAG_DDR2 BIT(MEM_DDR2)
#define MEM_FLAG_FB_DDR2 BIT(MEM_FB_DDR2)
/* chipset Error Detection and Correction capabilities and mode */
enum edac_type {
EDAC_UNKNOWN = 0, /* Unknown if ECC is available */
EDAC_NONE, /* Doesnt support ECC */
EDAC_RESERVED, /* Reserved ECC type */
EDAC_PARITY, /* Detects parity errors */
EDAC_EC, /* Error Checking - no correction */
EDAC_SECDED, /* Single bit error correction, Double detection */
EDAC_S2ECD2ED, /* Chipkill x2 devices - do these exist? */
EDAC_S4ECD4ED, /* Chipkill x4 devices */
EDAC_S8ECD8ED, /* Chipkill x8 devices */
EDAC_S16ECD16ED, /* Chipkill x16 devices */
};
#define EDAC_FLAG_UNKNOWN BIT(EDAC_UNKNOWN)
#define EDAC_FLAG_NONE BIT(EDAC_NONE)
#define EDAC_FLAG_PARITY BIT(EDAC_PARITY)
#define EDAC_FLAG_EC BIT(EDAC_EC)
#define EDAC_FLAG_SECDED BIT(EDAC_SECDED)
#define EDAC_FLAG_S2ECD2ED BIT(EDAC_S2ECD2ED)
#define EDAC_FLAG_S4ECD4ED BIT(EDAC_S4ECD4ED)
#define EDAC_FLAG_S8ECD8ED BIT(EDAC_S8ECD8ED)
#define EDAC_FLAG_S16ECD16ED BIT(EDAC_S16ECD16ED)
/* scrubbing capabilities */
enum scrub_type {
SCRUB_UNKNOWN = 0, /* Unknown if scrubber is available */
SCRUB_NONE, /* No scrubber */
SCRUB_SW_PROG, /* SW progressive (sequential) scrubbing */
SCRUB_SW_SRC, /* Software scrub only errors */
SCRUB_SW_PROG_SRC, /* Progressive software scrub from an error */
SCRUB_SW_TUNABLE, /* Software scrub frequency is tunable */
SCRUB_HW_PROG, /* HW progressive (sequential) scrubbing */
SCRUB_HW_SRC, /* Hardware scrub only errors */
SCRUB_HW_PROG_SRC, /* Progressive hardware scrub from an error */
SCRUB_HW_TUNABLE /* Hardware scrub frequency is tunable */
};
#define SCRUB_FLAG_SW_PROG BIT(SCRUB_SW_PROG)
#define SCRUB_FLAG_SW_SRC BIT(SCRUB_SW_SRC_CORR)
#define SCRUB_FLAG_SW_PROG_SRC BIT(SCRUB_SW_PROG_SRC_CORR)
#define SCRUB_FLAG_SW_TUN BIT(SCRUB_SW_SCRUB_TUNABLE)
#define SCRUB_FLAG_HW_PROG BIT(SCRUB_HW_PROG)
#define SCRUB_FLAG_HW_SRC BIT(SCRUB_HW_SRC_CORR)
#define SCRUB_FLAG_HW_PROG_SRC BIT(SCRUB_HW_PROG_SRC_CORR)
#define SCRUB_FLAG_HW_TUN BIT(SCRUB_HW_TUNABLE)
/* FIXME - should have notify capabilities: NMI, LOG, PROC, etc */
/*
* There are several things to be aware of that aren't at all obvious:
*
*
* SOCKETS, SOCKET SETS, BANKS, ROWS, CHIP-SELECT ROWS, CHANNELS, etc..
*
* These are some of the many terms that are thrown about that don't always
* mean what people think they mean (Inconceivable!). In the interest of
* 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 Stick: A printed circuit board that agregates multiple
* memory devices in parallel. This is the atomic
* memory component that is purchaseable by Joe consumer
* and loaded into a memory socket.
*
* Socket: A physical connector on the motherboard that accepts
* a single memory stick.
*
* 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").
*
* 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.
*
* Single-Ranked stick: A Single-ranked stick has 1 chip-select row of memmory.
* Motherboards commonly drive two chip-select pins to
* a memory stick. A single-ranked stick, will occupy
* only one of those rows. The other will be unused.
*
* Double-Ranked stick: A double-ranked stick has two chip-select rows which
* access different sets of memory devices. The two
* rows cannot be accessed concurrently.
*
* 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"
* is irrespective of the memory devices being mounted
* on both sides of the memory stick.
*
* Socket set: All of the memory sticks that are required for for
* a single memory access or all of the memory sticks
* spanned by a chip-select row. A single socket set
* has two chip-select rows and if double-sided sticks
* are used these will occupy those chip-select rows.
*
* Bank: This term is avoided because it is unclear when
* needing to distinguish between chip-select rows and
* socket sets.
*
* Controller pages:
*
* Physical pages:
*
* Virtual pages:
*
*
* STRUCTURE ORGANIZATION AND CHOICES
*
*
*
* 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 csrow_info *csrow; /* the parent */
};
struct csrow_info {
unsigned long first_page; /* first page number in dimm */
unsigned long last_page; /* last page number in dimm */
unsigned long page_mask; /* used for interleaving -
* 0UL for non intlv
*/
u32 nr_pages; /* number of pages in csrow */
u32 grain; /* granularity of reported error in bytes */
int csrow_idx; /* the chip-select row */
enum dev_type dtype; /* memory device type */
u32 ue_count; /* Uncorrectable Errors for this csrow */
u32 ce_count; /* Correctable Errors for this csrow */
enum mem_type mtype; /* memory csrow type */
enum edac_type edac_mode; /* EDAC mode for this csrow */
struct mem_ctl_info *mci; /* the parent */
struct kobject kobj; /* sysfs kobject for this csrow */
struct completion kobj_complete;
/* FIXME the number of CHANNELs might need to become dynamic */
u32 nr_channels;
struct channel_info *channels;
};
struct mem_ctl_info {
struct list_head link; /* for global list of mem_ctl_info structs */
unsigned long mtype_cap; /* memory types supported by mc */
unsigned long edac_ctl_cap; /* Mem controller EDAC capabilities */
unsigned long edac_cap; /* configuration capabilities - this is
* closely related to edac_ctl_cap. The
* difference is that the controller may be
* capable of s4ecd4ed which would be listed
* in edac_ctl_cap, but if channels aren't
* capable of s4ecd4ed then the edac_cap would
* not have that capability.
*/
unsigned long scrub_cap; /* chipset scrub capabilities */
enum scrub_type scrub_mode; /* current scrub mode */
/* Translates sdram memory scrub rate given in bytes/sec to the
internal representation and configures whatever else needs
to be configured.
*/
int (*set_sdram_scrub_rate) (struct mem_ctl_info *mci, u32 *bw);
/* Get the current sdram memory scrub rate from the internal
representation and converts it to the closest matching
bandwith in bytes/sec.
*/
int (*get_sdram_scrub_rate) (struct mem_ctl_info *mci, u32 *bw);
/* pointer to edac checking routine */
void (*edac_check) (struct mem_ctl_info * mci);
/*
* Remaps memory pages: controller pages to physical pages.
* For most MC's, this will be NULL.
*/
/* FIXME - why not send the phys page to begin with? */
unsigned long (*ctl_page_to_phys) (struct mem_ctl_info * mci,
unsigned long page);
int mc_idx;
int nr_csrows;
struct csrow_info *csrows;
/*
* FIXME - what about controllers on other busses? - IDs must be
* unique. dev pointer should be sufficiently unique, but
* BUS:SLOT.FUNC numbers may not be unique.
*/
struct device *dev;
const char *mod_name;
const char *mod_ver;
const char *ctl_name;
char proc_name[MC_PROC_NAME_MAX_LEN + 1];
void *pvt_info;
u32 ue_noinfo_count; /* Uncorrectable Errors w/o info */
u32 ce_noinfo_count; /* Correctable Errors w/o info */
u32 ue_count; /* Total Uncorrectable Errors for this MC */
u32 ce_count; /* Total Correctable Errors for this MC */
unsigned long start_time; /* mci load start time (in jiffies) */
/* this stuff is for safe removal of mc devices from global list while
* NMI handlers may be traversing list
*/
struct rcu_head rcu;
struct completion complete;
/* edac sysfs device control */
struct kobject edac_mci_kobj;
struct completion kobj_complete;
};
#ifdef CONFIG_PCI
/* write all or some bits in a byte-register*/
static inline void pci_write_bits8(struct pci_dev *pdev, int offset, u8 value,
u8 mask)
{
if (mask != 0xff) {
u8 buf;
pci_read_config_byte(pdev, offset, &buf);
value &= mask;
buf &= ~mask;
value |= buf;
}
pci_write_config_byte(pdev, offset, value);
}
/* write all or some bits in a word-register*/
static inline void pci_write_bits16(struct pci_dev *pdev, int offset,
u16 value, u16 mask)
{
if (mask != 0xffff) {
u16 buf;
pci_read_config_word(pdev, offset, &buf);
value &= mask;
buf &= ~mask;
value |= buf;
}
pci_write_config_word(pdev, offset, value);
}
/* write all or some bits in a dword-register*/
static inline void pci_write_bits32(struct pci_dev *pdev, int offset,
u32 value, u32 mask)
{
if (mask != 0xffff) {
u32 buf;
pci_read_config_dword(pdev, offset, &buf);
value &= mask;
buf &= ~mask;
value |= buf;
}
pci_write_config_dword(pdev, offset, value);
}
#endif /* CONFIG_PCI */
#ifdef CONFIG_EDAC_DEBUG
void edac_mc_dump_channel(struct channel_info *chan);
void edac_mc_dump_mci(struct mem_ctl_info *mci);
void edac_mc_dump_csrow(struct csrow_info *csrow);
#endif /* CONFIG_EDAC_DEBUG */
extern int edac_mc_add_mc(struct mem_ctl_info *mci,int mc_idx);
extern struct mem_ctl_info * edac_mc_del_mc(struct device *dev);
extern int edac_mc_find_csrow_by_page(struct mem_ctl_info *mci,
unsigned long page);
extern void edac_mc_scrub_block(unsigned long page, unsigned long offset,
u32 size);
/*
* The no info errors are used when error overflows are reported.
* There are a limited number of error logging registers that can
* be exausted. When all registers are exhausted and an additional
* error occurs then an error overflow register records that an
* error occured and the type of error, but doesn't have any
* further information. The ce/ue versions make for cleaner
* reporting logic and function interface - reduces conditional
* statement clutter and extra function arguments.
*/
extern void edac_mc_handle_ce(struct mem_ctl_info *mci,
unsigned long page_frame_number, unsigned long offset_in_page,
unsigned long syndrome, int row, int channel,
const char *msg);
extern void edac_mc_handle_ce_no_info(struct mem_ctl_info *mci,
const char *msg);
extern void edac_mc_handle_ue(struct mem_ctl_info *mci,
unsigned long page_frame_number, unsigned long offset_in_page,
int row, const char *msg);
extern void edac_mc_handle_ue_no_info(struct mem_ctl_info *mci,
const char *msg);
extern void edac_mc_handle_fbd_ue(struct mem_ctl_info *mci,
unsigned int csrow,
unsigned int channel0,
unsigned int channel1,
char *msg);
extern void edac_mc_handle_fbd_ce(struct mem_ctl_info *mci,
unsigned int csrow,
unsigned int channel,
char *msg);
/*
* This kmalloc's and initializes all the structures.
* Can't be used if all structures don't have the same lifetime.
*/
extern struct mem_ctl_info *edac_mc_alloc(unsigned sz_pvt, unsigned nr_csrows,
unsigned nr_chans);
/* Free an mc previously allocated by edac_mc_alloc() */
extern void edac_mc_free(struct mem_ctl_info *mci);
#endif /* _EDAC_MC_H_ */

333
drivers/edac/i82860_edac.c Normal file
View File

@@ -0,0 +1,333 @@
/*
* Intel 82860 Memory Controller kernel module
* (C) 2005 Red Hat (http://www.redhat.com)
* This file may be distributed under the terms of the
* GNU General Public License.
*
* Written by Ben Woodard <woodard@redhat.com>
* shamelessly copied from and based upon the edac_i82875 driver
* by Thayne Harbaugh of Linux Networx. (http://lnxi.com)
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/pci_ids.h>
#include <linux/slab.h>
#include "edac_mc.h"
#define I82860_REVISION " Ver: 2.0.1 " __DATE__
#define EDAC_MOD_STR "i82860_edac"
#define i82860_printk(level, fmt, arg...) \
edac_printk(level, "i82860", fmt, ##arg)
#define i82860_mc_printk(mci, level, fmt, arg...) \
edac_mc_chipset_printk(mci, level, "i82860", fmt, ##arg)
#ifndef PCI_DEVICE_ID_INTEL_82860_0
#define PCI_DEVICE_ID_INTEL_82860_0 0x2531
#endif /* PCI_DEVICE_ID_INTEL_82860_0 */
#define I82860_MCHCFG 0x50
#define I82860_GBA 0x60
#define I82860_GBA_MASK 0x7FF
#define I82860_GBA_SHIFT 24
#define I82860_ERRSTS 0xC8
#define I82860_EAP 0xE4
#define I82860_DERRCTL_STS 0xE2
enum i82860_chips {
I82860 = 0,
};
struct i82860_dev_info {
const char *ctl_name;
};
struct i82860_error_info {
u16 errsts;
u32 eap;
u16 derrsyn;
u16 errsts2;
};
static const struct i82860_dev_info i82860_devs[] = {
[I82860] = {
.ctl_name = "i82860"
},
};
static struct pci_dev *mci_pdev = NULL; /* init dev: in case that AGP code
* has already registered driver
*/
static void i82860_get_error_info(struct mem_ctl_info *mci,
struct i82860_error_info *info)
{
struct pci_dev *pdev;
pdev = to_pci_dev(mci->dev);
/*
* This is a mess because there is no atomic way to read all the
* registers at once and the registers can transition from CE being
* overwritten by UE.
*/
pci_read_config_word(pdev, I82860_ERRSTS, &info->errsts);
pci_read_config_dword(pdev, I82860_EAP, &info->eap);
pci_read_config_word(pdev, I82860_DERRCTL_STS, &info->derrsyn);
pci_read_config_word(pdev, I82860_ERRSTS, &info->errsts2);
pci_write_bits16(pdev, I82860_ERRSTS, 0x0003, 0x0003);
/*
* If the error is the same for both reads then the first set of reads
* is valid. If there is a change then there is a CE no info and the
* second set of reads is valid and should be UE info.
*/
if (!(info->errsts2 & 0x0003))
return;
if ((info->errsts ^ info->errsts2) & 0x0003) {
pci_read_config_dword(pdev, I82860_EAP, &info->eap);
pci_read_config_word(pdev, I82860_DERRCTL_STS,
&info->derrsyn);
}
}
static int i82860_process_error_info(struct mem_ctl_info *mci,
struct i82860_error_info *info, int handle_errors)
{
int row;
if (!(info->errsts2 & 0x0003))
return 0;
if (!handle_errors)
return 1;
if ((info->errsts ^ info->errsts2) & 0x0003) {
edac_mc_handle_ce_no_info(mci, "UE overwrote CE");
info->errsts = info->errsts2;
}
info->eap >>= PAGE_SHIFT;
row = edac_mc_find_csrow_by_page(mci, info->eap);
if (info->errsts & 0x0002)
edac_mc_handle_ue(mci, info->eap, 0, row, "i82860 UE");
else
edac_mc_handle_ce(mci, info->eap, 0, info->derrsyn, row, 0,
"i82860 UE");
return 1;
}
static void i82860_check(struct mem_ctl_info *mci)
{
struct i82860_error_info info;
debugf1("MC%d: %s()\n", mci->mc_idx, __func__);
i82860_get_error_info(mci, &info);
i82860_process_error_info(mci, &info, 1);
}
static void i82860_init_csrows(struct mem_ctl_info *mci, struct pci_dev *pdev)
{
unsigned long last_cumul_size;
u16 mchcfg_ddim; /* DRAM Data Integrity Mode 0=none, 2=edac */
u16 value;
u32 cumul_size;
struct csrow_info *csrow;
int index;
pci_read_config_word(pdev, I82860_MCHCFG, &mchcfg_ddim);
mchcfg_ddim = mchcfg_ddim & 0x180;
last_cumul_size = 0;
/* The group row boundary (GRA) reg values are boundary address
* for each DRAM row with a granularity of 16MB. GRA regs are
* cumulative; therefore GRA15 will contain the total memory contained
* in all eight rows.
*/
for (index = 0; index < mci->nr_csrows; index++) {
csrow = &mci->csrows[index];
pci_read_config_word(pdev, I82860_GBA + index * 2, &value);
cumul_size = (value & I82860_GBA_MASK) <<
(I82860_GBA_SHIFT - 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; /* I82860_EAP has 4KiB reolution */
csrow->mtype = MEM_RMBS;
csrow->dtype = DEV_UNKNOWN;
csrow->edac_mode = mchcfg_ddim ? EDAC_SECDED : EDAC_NONE;
}
}
static int i82860_probe1(struct pci_dev *pdev, int dev_idx)
{
struct mem_ctl_info *mci;
struct i82860_error_info discard;
/* RDRAM has channels but these don't map onto the abstractions that
edac uses.
The device groups from the GRA registers seem to map reasonably
well onto the notion of a chip select row.
There are 16 GRA registers and since the name is associated with
the channel and the GRA registers map to physical devices so we are
going to make 1 channel for group.
*/
mci = edac_mc_alloc(0, 16, 1);
if (!mci)
return -ENOMEM;
debugf3("%s(): init mci\n", __func__);
mci->dev = &pdev->dev;
mci->mtype_cap = MEM_FLAG_DDR;
mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED;
/* I"m not sure about this but I think that all RDRAM is SECDED */
mci->edac_cap = EDAC_FLAG_SECDED;
mci->mod_name = EDAC_MOD_STR;
mci->mod_ver = I82860_REVISION;
mci->ctl_name = i82860_devs[dev_idx].ctl_name;
mci->edac_check = i82860_check;
mci->ctl_page_to_phys = NULL;
i82860_init_csrows(mci, pdev);
i82860_get_error_info(mci, &discard); /* clear counters */
/* 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,0)) {
debugf3("%s(): failed edac_mc_add_mc()\n", __func__);
goto fail;
}
/* get this far and it's successful */
debugf3("%s(): success\n", __func__);
return 0;
fail:
edac_mc_free(mci);
return -ENODEV;
}
/* returns count (>= 0), or negative on error */
static int __devinit i82860_init_one(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
int rc;
debugf0("%s()\n", __func__);
i82860_printk(KERN_INFO, "i82860 init one\n");
if (pci_enable_device(pdev) < 0)
return -EIO;
rc = i82860_probe1(pdev, ent->driver_data);
if (rc == 0)
mci_pdev = pci_dev_get(pdev);
return rc;
}
static void __devexit i82860_remove_one(struct pci_dev *pdev)
{
struct mem_ctl_info *mci;
debugf0("%s()\n", __func__);
if ((mci = edac_mc_del_mc(&pdev->dev)) == NULL)
return;
edac_mc_free(mci);
}
static const struct pci_device_id i82860_pci_tbl[] __devinitdata = {
{
PCI_VEND_DEV(INTEL, 82860_0), PCI_ANY_ID, PCI_ANY_ID, 0, 0,
I82860
},
{
0,
} /* 0 terminated list. */
};
MODULE_DEVICE_TABLE(pci, i82860_pci_tbl);
static struct pci_driver i82860_driver = {
.name = EDAC_MOD_STR,
.probe = i82860_init_one,
.remove = __devexit_p(i82860_remove_one),
.id_table = i82860_pci_tbl,
};
static int __init i82860_init(void)
{
int pci_rc;
debugf3("%s()\n", __func__);
if ((pci_rc = pci_register_driver(&i82860_driver)) < 0)
goto fail0;
if (!mci_pdev) {
mci_pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
PCI_DEVICE_ID_INTEL_82860_0, NULL);
if (mci_pdev == NULL) {
debugf0("860 pci_get_device fail\n");
pci_rc = -ENODEV;
goto fail1;
}
pci_rc = i82860_init_one(mci_pdev, i82860_pci_tbl);
if (pci_rc < 0) {
debugf0("860 init fail\n");
pci_rc = -ENODEV;
goto fail1;
}
}
return 0;
fail1:
pci_unregister_driver(&i82860_driver);
fail0:
if (mci_pdev != NULL)
pci_dev_put(mci_pdev);
return pci_rc;
}
static void __exit i82860_exit(void)
{
debugf3("%s()\n", __func__);
pci_unregister_driver(&i82860_driver);
if (mci_pdev != NULL)
pci_dev_put(mci_pdev);
}
module_init(i82860_init);
module_exit(i82860_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Red Hat Inc. (http://www.redhat.com) "
"Ben Woodard <woodard@redhat.com>");
MODULE_DESCRIPTION("ECC support for Intel 82860 memory hub controllers");

575
drivers/edac/i82875p_edac.c Normal file
View File

@@ -0,0 +1,575 @@
/*
* Intel D82875P 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.
*
* Written by Thayne Harbaugh
* Contributors:
* Wang Zhenyu at intel.com
*
* $Id: i82875p_edac.c,v 1.1.1.1 2007/06/12 07:27:09 eyryu Exp $
*
* Note: E7210 appears same as D82875P - zhenyu.z.wang at intel.com
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/pci_ids.h>
#include <linux/slab.h>
#include "edac_mc.h"
#define I82875P_REVISION " Ver: 2.0.1 " __DATE__
#define EDAC_MOD_STR "i82875p_edac"
#define i82875p_printk(level, fmt, arg...) \
edac_printk(level, "i82875p", fmt, ##arg)
#define i82875p_mc_printk(mci, level, fmt, arg...) \
edac_mc_chipset_printk(mci, level, "i82875p", fmt, ##arg)
#ifndef PCI_DEVICE_ID_INTEL_82875_0
#define PCI_DEVICE_ID_INTEL_82875_0 0x2578
#endif /* PCI_DEVICE_ID_INTEL_82875_0 */
#ifndef PCI_DEVICE_ID_INTEL_82875_6
#define PCI_DEVICE_ID_INTEL_82875_6 0x257e
#endif /* PCI_DEVICE_ID_INTEL_82875_6 */
/* four csrows in dual channel, eight in single channel */
#define I82875P_NR_CSROWS(nr_chans) (8/(nr_chans))
/* Intel 82875p register addresses - device 0 function 0 - DRAM Controller */
#define I82875P_EAP 0x58 /* Error Address Pointer (32b)
*
* 31:12 block address
* 11:0 reserved
*/
#define I82875P_DERRSYN 0x5c /* DRAM Error Syndrome (8b)
*
* 7:0 DRAM ECC Syndrome
*/
#define I82875P_DES 0x5d /* DRAM Error Status (8b)
*
* 7:1 reserved
* 0 Error channel 0/1
*/
#define I82875P_ERRSTS 0xc8 /* Error Status Register (16b)
*
* 15:10 reserved
* 9 non-DRAM lock error (ndlock)
* 8 Sftwr Generated SMI
* 7 ECC UE
* 6 reserved
* 5 MCH detects unimplemented cycle
* 4 AGP access outside GA
* 3 Invalid AGP access
* 2 Invalid GA translation table
* 1 Unsupported AGP command
* 0 ECC CE
*/
#define I82875P_ERRCMD 0xca /* Error Command (16b)
*
* 15:10 reserved
* 9 SERR on non-DRAM lock
* 8 SERR on ECC UE
* 7 SERR on ECC CE
* 6 target abort on high exception
* 5 detect unimplemented cyc
* 4 AGP access outside of GA
* 3 SERR on invalid AGP access
* 2 invalid translation table
* 1 SERR on unsupported AGP command
* 0 reserved
*/
/* Intel 82875p register addresses - device 6 function 0 - DRAM Controller */
#define I82875P_PCICMD6 0x04 /* PCI Command Register (16b)
*
* 15:10 reserved
* 9 fast back-to-back - ro 0
* 8 SERR enable - ro 0
* 7 addr/data stepping - ro 0
* 6 parity err enable - ro 0
* 5 VGA palette snoop - ro 0
* 4 mem wr & invalidate - ro 0
* 3 special cycle - ro 0
* 2 bus master - ro 0
* 1 mem access dev6 - 0(dis),1(en)
* 0 IO access dev3 - 0(dis),1(en)
*/
#define I82875P_BAR6 0x10 /* Mem Delays Base ADDR Reg (32b)
*
* 31:12 mem base addr [31:12]
* 11:4 address mask - ro 0
* 3 prefetchable - ro 0(non),1(pre)
* 2:1 mem type - ro 0
* 0 mem space - ro 0
*/
/* Intel 82875p MMIO register space - device 0 function 0 - MMR space */
#define I82875P_DRB_SHIFT 26 /* 64MiB grain */
#define I82875P_DRB 0x00 /* DRAM Row Boundary (8b x 8)
*
* 7 reserved
* 6:0 64MiB row boundary addr
*/
#define I82875P_DRA 0x10 /* DRAM Row Attribute (4b x 8)
*
* 7 reserved
* 6:4 row attr row 1
* 3 reserved
* 2:0 row attr row 0
*
* 000 = 4KiB
* 001 = 8KiB
* 010 = 16KiB
* 011 = 32KiB
*/
#define I82875P_DRC 0x68 /* DRAM Controller Mode (32b)
*
* 31:30 reserved
* 29 init complete
* 28:23 reserved
* 22:21 nr chan 00=1,01=2
* 20 reserved
* 19:18 Data Integ Mode 00=none,01=ecc
* 17:11 reserved
* 10:8 refresh mode
* 7 reserved
* 6:4 mode select
* 3:2 reserved
* 1:0 DRAM type 01=DDR
*/
enum i82875p_chips {
I82875P = 0,
};
struct i82875p_pvt {
struct pci_dev *ovrfl_pdev;
void __iomem *ovrfl_window;
};
struct i82875p_dev_info {
const char *ctl_name;
};
struct i82875p_error_info {
u16 errsts;
u32 eap;
u8 des;
u8 derrsyn;
u16 errsts2;
};
static const struct i82875p_dev_info i82875p_devs[] = {
[I82875P] = {
.ctl_name = "i82875p"
},
};
static struct pci_dev *mci_pdev = NULL; /* init dev: in case that AGP code has
* already registered driver
*/
static int i82875p_registered = 1;
static void i82875p_get_error_info(struct mem_ctl_info *mci,
struct i82875p_error_info *info)
{
struct pci_dev *pdev;
pdev = to_pci_dev(mci->dev);
/*
* This is a mess because there is no atomic way to read all the
* registers at once and the registers can transition from CE being
* overwritten by UE.
*/
pci_read_config_word(pdev, I82875P_ERRSTS, &info->errsts);
pci_read_config_dword(pdev, I82875P_EAP, &info->eap);
pci_read_config_byte(pdev, I82875P_DES, &info->des);
pci_read_config_byte(pdev, I82875P_DERRSYN, &info->derrsyn);
pci_read_config_word(pdev, I82875P_ERRSTS, &info->errsts2);
pci_write_bits16(pdev, I82875P_ERRSTS, 0x0081, 0x0081);
/*
* If the error is the same then we can for both reads then
* the first set of reads is valid. If there is a change then
* there is a CE no info and the second set of reads is valid
* and should be UE info.
*/
if (!(info->errsts2 & 0x0081))
return;
if ((info->errsts ^ info->errsts2) & 0x0081) {
pci_read_config_dword(pdev, I82875P_EAP, &info->eap);
pci_read_config_byte(pdev, I82875P_DES, &info->des);
pci_read_config_byte(pdev, I82875P_DERRSYN,
&info->derrsyn);
}
}
static int i82875p_process_error_info(struct mem_ctl_info *mci,
struct i82875p_error_info *info, int handle_errors)
{
int row, multi_chan;
multi_chan = mci->csrows[0].nr_channels - 1;
if (!(info->errsts2 & 0x0081))
return 0;
if (!handle_errors)
return 1;
if ((info->errsts ^ info->errsts2) & 0x0081) {
edac_mc_handle_ce_no_info(mci, "UE overwrote CE");
info->errsts = info->errsts2;
}
info->eap >>= PAGE_SHIFT;
row = edac_mc_find_csrow_by_page(mci, info->eap);
if (info->errsts & 0x0080)
edac_mc_handle_ue(mci, info->eap, 0, row, "i82875p UE");
else
edac_mc_handle_ce(mci, info->eap, 0, info->derrsyn, row,
multi_chan ? (info->des & 0x1) : 0,
"i82875p CE");
return 1;
}
static void i82875p_check(struct mem_ctl_info *mci)
{
struct i82875p_error_info info;
debugf1("MC%d: %s()\n", mci->mc_idx, __func__);
i82875p_get_error_info(mci, &info);
i82875p_process_error_info(mci, &info, 1);
}
#ifdef CONFIG_PROC_FS
extern int pci_proc_attach_device(struct pci_dev *);
#endif
/* Return 0 on success or 1 on failure. */
static int i82875p_setup_overfl_dev(struct pci_dev *pdev,
struct pci_dev **ovrfl_pdev, void __iomem **ovrfl_window)
{
struct pci_dev *dev;
void __iomem *window;
*ovrfl_pdev = NULL;
*ovrfl_window = NULL;
dev = pci_get_device(PCI_VEND_DEV(INTEL, 82875_6), NULL);
if (dev == NULL) {
/* Intel tells BIOS developers to hide device 6 which
* configures the overflow device access containing
* the DRBs - this is where we expose device 6.
* http://www.x86-secret.com/articles/tweak/pat/patsecrets-2.htm
*/
pci_write_bits8(pdev, 0xf4, 0x2, 0x2);
dev = pci_scan_single_device(pdev->bus, PCI_DEVFN(6, 0));
if (dev == NULL)
return 1;
}
*ovrfl_pdev = dev;
#ifdef CONFIG_PROC_FS
if ((dev->procent == NULL) && pci_proc_attach_device(dev)) {
i82875p_printk(KERN_ERR, "%s(): Failed to attach overflow "
"device\n", __func__);
return 1;
}
#endif /* CONFIG_PROC_FS */
if (pci_enable_device(dev)) {
i82875p_printk(KERN_ERR, "%s(): Failed to enable overflow "
"device\n", __func__);
return 1;
}
if (pci_request_regions(dev, pci_name(dev))) {
#ifdef CORRECT_BIOS
goto fail0;
#endif
}
/* cache is irrelevant for PCI bus reads/writes */
window = ioremap_nocache(pci_resource_start(dev, 0),
pci_resource_len(dev, 0));
if (window == NULL) {
i82875p_printk(KERN_ERR, "%s(): Failed to ioremap bar6\n",
__func__);
goto fail1;
}
*ovrfl_window = window;
return 0;
fail1:
pci_release_regions(dev);
#ifdef CORRECT_BIOS
fail0:
pci_disable_device(dev);
#endif
/* NOTE: the ovrfl proc entry and pci_dev are intentionally left */
return 1;
}
/* Return 1 if dual channel mode is active. Else return 0. */
static inline int dual_channel_active(u32 drc)
{
return (drc >> 21) & 0x1;
}
static void i82875p_init_csrows(struct mem_ctl_info *mci,
struct pci_dev *pdev, void __iomem *ovrfl_window, u32 drc)
{
struct csrow_info *csrow;
unsigned long last_cumul_size;
u8 value;
u32 drc_ddim; /* DRAM Data Integrity Mode 0=none,2=edac */
u32 cumul_size;
int index;
drc_ddim = (drc >> 18) & 0x1;
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++) {
csrow = &mci->csrows[index];
value = readb(ovrfl_window + I82875P_DRB + index);
cumul_size = value << (I82875P_DRB_SHIFT - 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; /* I82875P_EAP has 4KiB reolution */
csrow->mtype = MEM_DDR;
csrow->dtype = DEV_UNKNOWN;
csrow->edac_mode = drc_ddim ? EDAC_SECDED : EDAC_NONE;
}
}
static int i82875p_probe1(struct pci_dev *pdev, int dev_idx)
{
int rc = -ENODEV;
struct mem_ctl_info *mci;
struct i82875p_pvt *pvt;
struct pci_dev *ovrfl_pdev;
void __iomem *ovrfl_window;
u32 drc;
u32 nr_chans;
struct i82875p_error_info discard;
debugf0("%s()\n", __func__);
ovrfl_pdev = pci_get_device(PCI_VEND_DEV(INTEL, 82875_6), NULL);
if (i82875p_setup_overfl_dev(pdev, &ovrfl_pdev, &ovrfl_window))
return -ENODEV;
drc = readl(ovrfl_window + I82875P_DRC);
nr_chans = dual_channel_active(drc) + 1;
mci = edac_mc_alloc(sizeof(*pvt), I82875P_NR_CSROWS(nr_chans),
nr_chans);
if (!mci) {
rc = -ENOMEM;
goto fail0;
}
debugf3("%s(): init mci\n", __func__);
mci->dev = &pdev->dev;
mci->mtype_cap = MEM_FLAG_DDR;
mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED;
mci->edac_cap = EDAC_FLAG_UNKNOWN;
mci->mod_name = EDAC_MOD_STR;
mci->mod_ver = I82875P_REVISION;
mci->ctl_name = i82875p_devs[dev_idx].ctl_name;
mci->edac_check = i82875p_check;
mci->ctl_page_to_phys = NULL;
debugf3("%s(): init pvt\n", __func__);
pvt = (struct i82875p_pvt *) mci->pvt_info;
pvt->ovrfl_pdev = ovrfl_pdev;
pvt->ovrfl_window = ovrfl_window;
i82875p_init_csrows(mci, pdev, ovrfl_window, drc);
i82875p_get_error_info(mci, &discard); /* clear counters */
/* 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,0)) {
debugf3("%s(): failed edac_mc_add_mc()\n", __func__);
goto fail1;
}
/* get this far and it's successful */
debugf3("%s(): success\n", __func__);
return 0;
fail1:
edac_mc_free(mci);
fail0:
iounmap(ovrfl_window);
pci_release_regions(ovrfl_pdev);
pci_disable_device(ovrfl_pdev);
/* NOTE: the ovrfl proc entry and pci_dev are intentionally left */
return rc;
}
/* returns count (>= 0), or negative on error */
static int __devinit i82875p_init_one(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
int rc;
debugf0("%s()\n", __func__);
i82875p_printk(KERN_INFO, "i82875p init one\n");
if (pci_enable_device(pdev) < 0)
return -EIO;
rc = i82875p_probe1(pdev, ent->driver_data);
if (mci_pdev == NULL)
mci_pdev = pci_dev_get(pdev);
return rc;
}
static void __devexit i82875p_remove_one(struct pci_dev *pdev)
{
struct mem_ctl_info *mci;
struct i82875p_pvt *pvt = NULL;
debugf0("%s()\n", __func__);
if ((mci = edac_mc_del_mc(&pdev->dev)) == NULL)
return;
pvt = (struct i82875p_pvt *) mci->pvt_info;
if (pvt->ovrfl_window)
iounmap(pvt->ovrfl_window);
if (pvt->ovrfl_pdev) {
#ifdef CORRECT_BIOS
pci_release_regions(pvt->ovrfl_pdev);
#endif /*CORRECT_BIOS */
pci_disable_device(pvt->ovrfl_pdev);
pci_dev_put(pvt->ovrfl_pdev);
}
edac_mc_free(mci);
}
static const struct pci_device_id i82875p_pci_tbl[] __devinitdata = {
{
PCI_VEND_DEV(INTEL, 82875_0), PCI_ANY_ID, PCI_ANY_ID, 0, 0,
I82875P
},
{
0,
} /* 0 terminated list. */
};
MODULE_DEVICE_TABLE(pci, i82875p_pci_tbl);
static struct pci_driver i82875p_driver = {
.name = EDAC_MOD_STR,
.probe = i82875p_init_one,
.remove = __devexit_p(i82875p_remove_one),
.id_table = i82875p_pci_tbl,
};
static int __init i82875p_init(void)
{
int pci_rc;
debugf3("%s()\n", __func__);
pci_rc = pci_register_driver(&i82875p_driver);
if (pci_rc < 0)
goto fail0;
if (mci_pdev == NULL) {
mci_pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
PCI_DEVICE_ID_INTEL_82875_0, NULL);
if (!mci_pdev) {
debugf0("875p pci_get_device fail\n");
pci_rc = -ENODEV;
goto fail1;
}
pci_rc = i82875p_init_one(mci_pdev, i82875p_pci_tbl);
if (pci_rc < 0) {
debugf0("875p init fail\n");
pci_rc = -ENODEV;
goto fail1;
}
}
return 0;
fail1:
pci_unregister_driver(&i82875p_driver);
fail0:
if (mci_pdev != NULL)
pci_dev_put(mci_pdev);
return pci_rc;
}
static void __exit i82875p_exit(void)
{
debugf3("%s()\n", __func__);
pci_unregister_driver(&i82875p_driver);
if (!i82875p_registered) {
i82875p_remove_one(mci_pdev);
pci_dev_put(mci_pdev);
}
}
module_init(i82875p_init);
module_exit(i82875p_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Linux Networx (http://lnxi.com) Thayne Harbaugh");
MODULE_DESCRIPTION("MC support for Intel 82875 memory hub controllers");

397
drivers/edac/r82600_edac.c Normal file
View File

@@ -0,0 +1,397 @@
/*
* Radisys 82600 Embedded chipset Memory Controller kernel module
* (C) 2005 EADS Astrium
* This file may be distributed under the terms of the
* GNU General Public License.
*
* Written by Tim Small <tim@buttersideup.com>, based on work by Thayne
* Harbaugh, Dan Hollis <goemon at anime dot net> and others.
*
* $Id: r82600_edac.c,v 1.1.1.1 2007/06/12 07:27:09 eyryu Exp $
*
* Written with reference to 82600 High Integration Dual PCI System
* Controller Data Book:
* http://www.radisys.com/files/support_downloads/007-01277-0002.82600DataBook.pdf
* references to this document given in []
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/pci_ids.h>
#include <linux/slab.h>
#include "edac_mc.h"
#define R82600_REVISION " Ver: 2.0.1 " __DATE__
#define EDAC_MOD_STR "r82600_edac"
#define r82600_printk(level, fmt, arg...) \
edac_printk(level, "r82600", fmt, ##arg)
#define r82600_mc_printk(mci, level, fmt, arg...) \
edac_mc_chipset_printk(mci, level, "r82600", fmt, ##arg)
/* Radisys say "The 82600 integrates a main memory SDRAM controller that
* supports up to four banks of memory. The four banks can support a mix of
* sizes of 64 bit wide (72 bits with ECC) Synchronous DRAM (SDRAM) DIMMs,
* each of which can be any size from 16MB to 512MB. Both registered (control
* signals buffered) and unbuffered DIMM types are supported. Mixing of
* registered and unbuffered DIMMs as well as mixing of ECC and non-ECC DIMMs
* is not allowed. The 82600 SDRAM interface operates at the same frequency as
* the CPU bus, 66MHz, 100MHz or 133MHz."
*/
#define R82600_NR_CSROWS 4
#define R82600_NR_CHANS 1
#define R82600_NR_DIMMS 4
#define R82600_BRIDGE_ID 0x8200
/* Radisys 82600 register addresses - device 0 function 0 - PCI bridge */
#define R82600_DRAMC 0x57 /* Various SDRAM related control bits
* all bits are R/W
*
* 7 SDRAM ISA Hole Enable
* 6 Flash Page Mode Enable
* 5 ECC Enable: 1=ECC 0=noECC
* 4 DRAM DIMM Type: 1=
* 3 BIOS Alias Disable
* 2 SDRAM BIOS Flash Write Enable
* 1:0 SDRAM Refresh Rate: 00=Disabled
* 01=7.8usec (256Mbit SDRAMs)
* 10=15.6us 11=125usec
*/
#define R82600_SDRAMC 0x76 /* "SDRAM Control Register"
* More SDRAM related control bits
* all bits are R/W
*
* 15:8 Reserved.
*
* 7:5 Special SDRAM Mode Select
*
* 4 Force ECC
*
* 1=Drive ECC bits to 0 during
* write cycles (i.e. ECC test mode)
*
* 0=Normal ECC functioning
*
* 3 Enhanced Paging Enable
*
* 2 CAS# Latency 0=3clks 1=2clks
*
* 1 RAS# to CAS# Delay 0=3 1=2
*
* 0 RAS# Precharge 0=3 1=2
*/
#define R82600_EAP 0x80 /* ECC Error Address Pointer Register
*
* 31 Disable Hardware Scrubbing (RW)
* 0=Scrub on corrected read
* 1=Don't scrub on corrected read
*
* 30:12 Error Address Pointer (RO)
* Upper 19 bits of error address
*
* 11:4 Syndrome Bits (RO)
*
* 3 BSERR# on multibit error (RW)
* 1=enable 0=disable
*
* 2 NMI on Single Bit Eror (RW)
* 1=NMI triggered by SBE n.b. other
* prerequeists
* 0=NMI not triggered
*
* 1 MBE (R/WC)
* read 1=MBE at EAP (see above)
* read 0=no MBE, or SBE occurred first
* write 1=Clear MBE status (must also
* clear SBE)
* write 0=NOP
*
* 1 SBE (R/WC)
* read 1=SBE at EAP (see above)
* read 0=no SBE, or MBE occurred first
* write 1=Clear SBE status (must also
* clear MBE)
* write 0=NOP
*/
#define R82600_DRBA 0x60 /* + 0x60..0x63 SDRAM Row Boundry Address
* Registers
*
* 7:0 Address lines 30:24 - upper limit of
* each row [p57]
*/
struct r82600_error_info {
u32 eapr;
};
static unsigned int disable_hardware_scrub = 0;
static void r82600_get_error_info (struct mem_ctl_info *mci,
struct r82600_error_info *info)
{
struct pci_dev *pdev;
pdev = to_pci_dev(mci->dev);
pci_read_config_dword(pdev, R82600_EAP, &info->eapr);
if (info->eapr & BIT(0))
/* Clear error to allow next error to be reported [p.62] */
pci_write_bits32(pdev, R82600_EAP,
((u32) BIT(0) & (u32) BIT(1)),
((u32) BIT(0) & (u32) BIT(1)));
if (info->eapr & BIT(1))
/* Clear error to allow next error to be reported [p.62] */
pci_write_bits32(pdev, R82600_EAP,
((u32) BIT(0) & (u32) BIT(1)),
((u32) BIT(0) & (u32) BIT(1)));
}
static int r82600_process_error_info (struct mem_ctl_info *mci,
struct r82600_error_info *info, int handle_errors)
{
int error_found;
u32 eapaddr, page;
u32 syndrome;
error_found = 0;
/* bits 30:12 store the upper 19 bits of the 32 bit error address */
eapaddr = ((info->eapr >> 12) & 0x7FFF) << 13;
/* Syndrome in bits 11:4 [p.62] */
syndrome = (info->eapr >> 4) & 0xFF;
/* the R82600 reports at less than page *
* granularity (upper 19 bits only) */
page = eapaddr >> PAGE_SHIFT;
if (info->eapr & BIT(0)) { /* CE? */
error_found = 1;
if (handle_errors)
edac_mc_handle_ce(mci, page, 0, /* not avail */
syndrome,
edac_mc_find_csrow_by_page(mci, page),
0, /* channel */
mci->ctl_name);
}
if (info->eapr & BIT(1)) { /* UE? */
error_found = 1;
if (handle_errors)
/* 82600 doesn't give enough info */
edac_mc_handle_ue(mci, page, 0,
edac_mc_find_csrow_by_page(mci, page),
mci->ctl_name);
}
return error_found;
}
static void r82600_check(struct mem_ctl_info *mci)
{
struct r82600_error_info info;
debugf1("MC%d: %s()\n", mci->mc_idx, __func__);
r82600_get_error_info(mci, &info);
r82600_process_error_info(mci, &info, 1);
}
static inline int ecc_enabled(u8 dramcr)
{
return dramcr & BIT(5);
}
static void r82600_init_csrows(struct mem_ctl_info *mci, struct pci_dev *pdev,
u8 dramcr)
{
struct csrow_info *csrow;
int index;
u8 drbar; /* SDRAM Row Boundry Address Register */
u32 row_high_limit, row_high_limit_last;
u32 reg_sdram, ecc_on, row_base;
ecc_on = ecc_enabled(dramcr);
reg_sdram = dramcr & BIT(4);
row_high_limit_last = 0;
for (index = 0; index < mci->nr_csrows; index++) {
csrow = &mci->csrows[index];
/* find the DRAM Chip Select Base address and mask */
pci_read_config_byte(pdev, R82600_DRBA + index, &drbar);
debugf1("%s() Row=%d DRBA = %#0x\n", __func__, index, drbar);
row_high_limit = ((u32) drbar << 24);
/* row_high_limit = ((u32)drbar << 24) | 0xffffffUL; */
debugf1("%s() Row=%d, Boundry Address=%#0x, Last = %#0x\n",
__func__, index, row_high_limit, row_high_limit_last);
/* Empty row [p.57] */
if (row_high_limit == row_high_limit_last)
continue;
row_base = row_high_limit_last;
csrow->first_page = row_base >> PAGE_SHIFT;
csrow->last_page = (row_high_limit >> PAGE_SHIFT) - 1;
csrow->nr_pages = csrow->last_page - csrow->first_page + 1;
/* Error address is top 19 bits - so granularity is *
* 14 bits */
csrow->grain = 1 << 14;
csrow->mtype = reg_sdram ? MEM_RDDR : MEM_DDR;
/* FIXME - check that this is unknowable with this chipset */
csrow->dtype = DEV_UNKNOWN;
/* Mode is global on 82600 */
csrow->edac_mode = ecc_on ? EDAC_SECDED : EDAC_NONE;
row_high_limit_last = row_high_limit;
}
}
static int r82600_probe1(struct pci_dev *pdev, int dev_idx)
{
struct mem_ctl_info *mci;
u8 dramcr;
u32 eapr;
u32 scrub_disabled;
u32 sdram_refresh_rate;
struct r82600_error_info discard;
debugf0("%s()\n", __func__);
pci_read_config_byte(pdev, R82600_DRAMC, &dramcr);
pci_read_config_dword(pdev, R82600_EAP, &eapr);
scrub_disabled = eapr & BIT(31);
sdram_refresh_rate = dramcr & (BIT(0) | BIT(1));
debugf2("%s(): sdram refresh rate = %#0x\n", __func__,
sdram_refresh_rate);
debugf2("%s(): DRAMC register = %#0x\n", __func__, dramcr);
mci = edac_mc_alloc(0, R82600_NR_CSROWS, R82600_NR_CHANS);
if (mci == NULL)
return -ENOMEM;
debugf0("%s(): mci = %p\n", __func__, mci);
mci->dev = &pdev->dev;
mci->mtype_cap = MEM_FLAG_RDDR | MEM_FLAG_DDR;
mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_EC | EDAC_FLAG_SECDED;
/* FIXME try to work out if the chip leads have been used for COM2
* instead on this board? [MA6?] MAYBE:
*/
/* On the R82600, the pins for memory bits 72:65 - i.e. the *
* EC bits are shared with the pins for COM2 (!), so if COM2 *
* is enabled, we assume COM2 is wired up, and thus no EDAC *
* is possible. */
mci->edac_cap = EDAC_FLAG_NONE | EDAC_FLAG_EC | EDAC_FLAG_SECDED;
if (ecc_enabled(dramcr)) {
if (scrub_disabled)
debugf3("%s(): mci = %p - Scrubbing disabled! EAP: "
"%#0x\n", __func__, mci, eapr);
} else
mci->edac_cap = EDAC_FLAG_NONE;
mci->mod_name = EDAC_MOD_STR;
mci->mod_ver = R82600_REVISION;
mci->ctl_name = "R82600";
mci->edac_check = r82600_check;
mci->ctl_page_to_phys = NULL;
r82600_init_csrows(mci, pdev, dramcr);
r82600_get_error_info(mci, &discard); /* clear counters */
/* 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,0)) {
debugf3("%s(): failed edac_mc_add_mc()\n", __func__);
goto fail;
}
/* get this far and it's successful */
if (disable_hardware_scrub) {
debugf3("%s(): Disabling Hardware Scrub (scrub on error)\n",
__func__);
pci_write_bits32(pdev, R82600_EAP, BIT(31), BIT(31));
}
debugf3("%s(): success\n", __func__);
return 0;
fail:
edac_mc_free(mci);
return -ENODEV;
}
/* returns count (>= 0), or negative on error */
static int __devinit r82600_init_one(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
debugf0("%s()\n", __func__);
/* don't need to call pci_device_enable() */
return r82600_probe1(pdev, ent->driver_data);
}
static void __devexit r82600_remove_one(struct pci_dev *pdev)
{
struct mem_ctl_info *mci;
debugf0("%s()\n", __func__);
if ((mci = edac_mc_del_mc(&pdev->dev)) == NULL)
return;
edac_mc_free(mci);
}
static const struct pci_device_id r82600_pci_tbl[] __devinitdata = {
{
PCI_DEVICE(PCI_VENDOR_ID_RADISYS, R82600_BRIDGE_ID)
},
{
0,
} /* 0 terminated list. */
};
MODULE_DEVICE_TABLE(pci, r82600_pci_tbl);
static struct pci_driver r82600_driver = {
.name = EDAC_MOD_STR,
.probe = r82600_init_one,
.remove = __devexit_p(r82600_remove_one),
.id_table = r82600_pci_tbl,
};
static int __init r82600_init(void)
{
return pci_register_driver(&r82600_driver);
}
static void __exit r82600_exit(void)
{
pci_unregister_driver(&r82600_driver);
}
module_init(r82600_init);
module_exit(r82600_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Tim Small <tim@buttersideup.com> - WPAD Ltd. "
"on behalf of EADS Astrium");
MODULE_DESCRIPTION("MC support for Radisys 82600 memory controllers");
module_param(disable_hardware_scrub, bool, 0644);
MODULE_PARM_DESC(disable_hardware_scrub,
"If set, disable the chipset's automatic scrub for CEs");