2006-01-19 04:44:08 +03:00
/*
* 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/
*
2012-04-16 22:07:09 +04:00
* Datasheet :
* http : //www.intel.com/content/www/us/en/chipsets/e7501-chipset-memory-controller-hub-datasheet.html
*
2006-01-19 04:44:08 +03:00
* Contributors :
2006-03-26 13:38:52 +04:00
* Eric Biederman ( Linux Networx )
* Tom Zimmerman ( Linux Networx )
* Jim Garlick ( Lawrence Livermore National Labs )
2006-01-19 04:44:08 +03:00
* 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>
2007-07-19 12:49:46 +04:00
# include <linux/edac.h>
2016-10-29 20:16:34 +03:00
# include "edac_module.h"
2006-01-19 04:44:08 +03:00
2006-07-01 15:35:45 +04:00
# define EDAC_MOD_STR "e7xxx_edac"
2006-06-30 12:56:07 +04:00
2006-03-26 13:38:40 +04:00
# define e7xxx_printk(level, fmt, arg...) \
2006-03-26 13:38:52 +04:00
edac_printk ( level , " e7xxx " , fmt , # # arg )
2006-03-26 13:38:40 +04:00
# define e7xxx_mc_printk(mci, level, fmt, arg...) \
2006-03-26 13:38:52 +04:00
edac_mc_chipset_printk ( mci , level , " e7xxx " , fmt , # # arg )
2006-03-26 13:38:40 +04:00
2006-01-19 04:44:08 +03:00
# 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 */
2012-04-16 22:07:09 +04:00
# define E7XXX_NR_DIMMS 8 /* 2 channels, 4 dimms/channel */
2006-01-19 04:44:08 +03:00
/* 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 / 64 MB
*/
# 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 ( 4 k 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 ( 4 k 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 ;
} ;
2007-07-19 12:50:10 +04:00
static struct edac_pci_ctl_info * e7xxx_pci ;
2006-01-19 04:44:08 +03:00
static const struct e7xxx_dev_info e7xxx_devs [ ] = {
[ E7500 ] = {
2007-07-19 12:50:13 +04:00
. err_dev = PCI_DEVICE_ID_INTEL_7500_1_ERR ,
. ctl_name = " E7500 " } ,
2006-01-19 04:44:08 +03:00
[ E7501 ] = {
2007-07-19 12:50:13 +04:00
. err_dev = PCI_DEVICE_ID_INTEL_7501_1_ERR ,
. ctl_name = " E7501 " } ,
2006-01-19 04:44:08 +03:00
[ E7505 ] = {
2007-07-19 12:50:13 +04:00
. err_dev = PCI_DEVICE_ID_INTEL_7505_1_ERR ,
. ctl_name = " E7505 " } ,
2006-01-19 04:44:08 +03:00
[ E7205 ] = {
2007-07-19 12:50:13 +04:00
. err_dev = PCI_DEVICE_ID_INTEL_7205_1_ERR ,
. ctl_name = " E7205 " } ,
2006-01-19 04:44:08 +03:00
} ;
/* FIXME - is this valid for both SECDED and S4ECD4ED? */
static inline int e7xxx_find_channel ( u16 syndrome )
{
2012-04-30 00:08:39 +04:00
edac_dbg ( 3 , " \n " ) ;
2006-01-19 04:44:08 +03:00
if ( ( syndrome & 0xff00 ) = = 0 )
return 0 ;
2006-03-26 13:38:52 +04:00
2006-01-19 04:44:08 +03:00
if ( ( syndrome & 0x00ff ) = = 0 )
return 1 ;
2006-03-26 13:38:52 +04:00
2006-01-19 04:44:08 +03:00
if ( ( syndrome & 0xf000 ) = = 0 | | ( syndrome & 0x0f00 ) = = 0 )
return 0 ;
2006-03-26 13:38:52 +04:00
2006-01-19 04:44:08 +03:00
return 1 ;
}
2006-03-26 13:38:52 +04:00
static unsigned long ctl_page_to_phys ( struct mem_ctl_info * mci ,
2007-07-19 12:50:13 +04:00
unsigned long page )
2006-01-19 04:44:08 +03:00
{
u32 remap ;
2007-07-19 12:50:03 +04:00
struct e7xxx_pvt * pvt = ( struct e7xxx_pvt * ) mci - > pvt_info ;
2006-01-19 04:44:08 +03:00
2012-04-30 00:08:39 +04:00
edac_dbg ( 3 , " \n " ) ;
2006-01-19 04:44:08 +03:00
if ( ( page < pvt - > tolm ) | |
2007-07-19 12:50:13 +04:00
( ( page > = 0x100000 ) & & ( page < pvt - > remapbase ) ) )
2006-01-19 04:44:08 +03:00
return page ;
2006-03-26 13:38:52 +04:00
2006-01-19 04:44:08 +03:00
remap = ( page - pvt - > tolm ) + pvt - > remapbase ;
2006-03-26 13:38:52 +04:00
2006-01-19 04:44:08 +03:00
if ( remap < pvt - > remaplimit )
return remap ;
2006-03-26 13:38:52 +04:00
2006-03-26 13:38:40 +04:00
e7xxx_printk ( KERN_ERR , " Invalid page %lx - out of range \n " , page ) ;
2006-01-19 04:44:08 +03:00
return pvt - > tolm - 1 ;
}
2007-07-19 12:50:03 +04:00
static void process_ce ( struct mem_ctl_info * mci , struct e7xxx_error_info * info )
2006-01-19 04:44:08 +03:00
{
u32 error_1b , page ;
u16 syndrome ;
int row ;
int channel ;
2012-04-30 00:08:39 +04:00
edac_dbg ( 3 , " \n " ) ;
2006-01-19 04:44:08 +03:00
/* read the error address */
error_1b = info - > dram_celog_add ;
/* FIXME - should use PAGE_SHIFT */
2007-07-19 12:50:03 +04:00
page = error_1b > > 6 ; /* convert the address to 4k page */
2006-01-19 04:44:08 +03:00
/* 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 ) ;
2012-06-04 20:27:43 +04:00
edac_mc_handle_error ( HW_EVENT_ERR_CORRECTED , mci , 1 , page , 0 , syndrome ,
2012-06-04 18:29:25 +04:00
row , channel , - 1 , " e7xxx CE " , " " ) ;
2006-01-19 04:44:08 +03:00
}
static void process_ce_no_info ( struct mem_ctl_info * mci )
{
2012-04-30 00:08:39 +04:00
edac_dbg ( 3 , " \n " ) ;
2014-10-18 18:06:32 +04:00
edac_mc_handle_error ( HW_EVENT_ERR_CORRECTED , mci , 1 , 0 , 0 , 0 , - 1 , - 1 , - 1 ,
2012-06-04 18:29:25 +04:00
" e7xxx CE log register overflow " , " " ) ;
2006-01-19 04:44:08 +03:00
}
2007-07-19 12:50:03 +04:00
static void process_ue ( struct mem_ctl_info * mci , struct e7xxx_error_info * info )
2006-01-19 04:44:08 +03:00
{
u32 error_2b , block_page ;
int row ;
2012-04-30 00:08:39 +04:00
edac_dbg ( 3 , " \n " ) ;
2006-01-19 04:44:08 +03:00
/* read the error address */
error_2b = info - > dram_uelog_add ;
/* FIXME - should use PAGE_SHIFT */
2007-07-19 12:50:03 +04:00
block_page = error_2b > > 6 ; /* convert to 4k address */
2006-01-19 04:44:08 +03:00
row = edac_mc_find_csrow_by_page ( mci , block_page ) ;
2012-04-16 22:07:09 +04:00
2012-06-04 20:27:43 +04:00
edac_mc_handle_error ( HW_EVENT_ERR_UNCORRECTED , mci , 1 , block_page , 0 , 0 ,
2012-06-04 18:29:25 +04:00
row , - 1 , - 1 , " e7xxx UE " , " " ) ;
2006-01-19 04:44:08 +03:00
}
static void process_ue_no_info ( struct mem_ctl_info * mci )
{
2012-04-30 00:08:39 +04:00
edac_dbg ( 3 , " \n " ) ;
2012-04-16 22:07:09 +04:00
2012-06-04 20:27:43 +04:00
edac_mc_handle_error ( HW_EVENT_ERR_UNCORRECTED , mci , 1 , 0 , 0 , 0 , - 1 , - 1 , - 1 ,
2012-06-04 18:29:25 +04:00
" e7xxx UE log register overflow " , " " ) ;
2006-01-19 04:44:08 +03:00
}
2007-07-19 12:50:03 +04:00
static void e7xxx_get_error_info ( struct mem_ctl_info * mci ,
struct e7xxx_error_info * info )
2006-01-19 04:44:08 +03:00
{
struct e7xxx_pvt * pvt ;
2007-07-19 12:50:03 +04:00
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 ) ;
2006-01-19 04:44:08 +03:00
if ( ( info - > dram_ferr & 1 ) | | ( info - > dram_nerr & 1 ) ) {
pci_read_config_dword ( pvt - > bridge_ck , E7XXX_DRAM_CELOG_ADD ,
2007-07-19 12:50:13 +04:00
& info - > dram_celog_add ) ;
2006-01-19 04:44:08 +03:00
pci_read_config_word ( pvt - > bridge_ck ,
2007-07-19 12:50:13 +04:00
E7XXX_DRAM_CELOG_SYNDROME ,
& info - > dram_celog_syndrome ) ;
2006-01-19 04:44:08 +03:00
}
if ( ( info - > dram_ferr & 2 ) | | ( info - > dram_nerr & 2 ) )
pci_read_config_dword ( pvt - > bridge_ck , E7XXX_DRAM_UELOG_ADD ,
2007-07-19 12:50:13 +04:00
& info - > dram_uelog_add ) ;
2006-01-19 04:44:08 +03:00
if ( info - > dram_ferr & 3 )
2006-03-26 13:38:52 +04:00
pci_write_bits8 ( pvt - > bridge_ck , E7XXX_DRAM_FERR , 0x03 , 0x03 ) ;
2006-01-19 04:44:08 +03:00
if ( info - > dram_nerr & 3 )
2006-03-26 13:38:52 +04:00
pci_write_bits8 ( pvt - > bridge_ck , E7XXX_DRAM_NERR , 0x03 , 0x03 ) ;
2006-01-19 04:44:08 +03:00
}
2007-07-19 12:50:03 +04:00
static int e7xxx_process_error_info ( struct mem_ctl_info * mci ,
2007-07-19 12:50:13 +04:00
struct e7xxx_error_info * info ,
int handle_errors )
2006-01-19 04:44:08 +03:00
{
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 ;
e7xxx_get_error_info ( mci , & info ) ;
e7xxx_process_error_info ( mci , & info , 1 ) ;
}
2006-06-30 12:56:08 +04:00
/* Return 1 if dual channel mode is active. Else return 0. */
static inline int dual_channel_active ( u32 drc , int dev_idx )
2006-01-19 04:44:08 +03:00
{
2006-06-30 12:56:08 +04:00
return ( dev_idx = = E7501 ) ? ( ( drc > > 22 ) & 0x1 ) : 1 ;
}
2006-01-19 04:44:08 +03:00
2006-06-30 12:56:08 +04:00
/* Return DRB granularity (0=32mb, 1=64mb). */
static inline int drb_granularity ( u32 drc , int dev_idx )
{
2006-01-19 04:44:08 +03:00
/* only e7501 can be single channel */
2006-06-30 12:56:08 +04:00
return ( dev_idx = = E7501 ) ? ( ( drc > > 18 ) & 0x3 ) : 1 ;
}
2006-03-26 13:38:52 +04:00
2006-06-30 12:56:08 +04:00
static void e7xxx_init_csrows ( struct mem_ctl_info * mci , struct pci_dev * pdev ,
2007-07-19 12:50:13 +04:00
int dev_idx , u32 drc )
2006-06-30 12:56:08 +04:00
{
unsigned long last_cumul_size ;
2012-01-28 01:38:08 +04:00
int index , j ;
2006-06-30 12:56:08 +04:00
u8 value ;
2012-01-28 16:09:38 +04:00
u32 dra , cumul_size , nr_pages ;
2006-06-30 12:56:08 +04:00
int drc_chan , drc_drbg , drc_ddim , mem_dev ;
struct csrow_info * csrow ;
2012-01-28 01:38:08 +04:00
struct dimm_info * dimm ;
2012-03-29 02:37:59 +04:00
enum edac_type edac_mode ;
2006-01-19 04:44:08 +03:00
pci_read_config_dword ( pdev , E7XXX_DRA , & dra ) ;
2006-06-30 12:56:08 +04:00
drc_chan = dual_channel_active ( drc , dev_idx ) ;
drc_drbg = drb_granularity ( drc , dev_idx ) ;
drc_ddim = ( drc > > 20 ) & 0x3 ;
last_cumul_size = 0 ;
2006-01-19 04:44:08 +03:00
2006-06-30 12:56:08 +04:00
/* The dram row boundary (DRB) reg values are boundary address
2006-01-19 04:44:08 +03:00
* for each DRAM row with a granularity of 32 or 64 MB ( single / dual
* channel operation ) . DRB regs are cumulative ; therefore DRB7 will
* contain the total memory contained in all eight rows .
*/
2006-06-30 12:56:08 +04:00
for ( index = 0 ; index < mci - > nr_csrows ; index + + ) {
2006-01-19 04:44:08 +03:00
/* mem_dev 0=x8, 1=x4 */
2006-06-30 12:56:08 +04:00
mem_dev = ( dra > > ( index * 4 + 3 ) ) & 0x1 ;
2012-04-24 22:05:43 +04:00
csrow = mci - > csrows [ index ] ;
2006-01-19 04:44:08 +03:00
2006-06-30 12:56:07 +04:00
pci_read_config_byte ( pdev , E7XXX_DRB + index , & value ) ;
2006-01-19 04:44:08 +03:00
/* convert a 64 or 32 MiB DRB to a page size. */
cumul_size = value < < ( 25 + drc_drbg - PAGE_SHIFT ) ;
2012-04-30 00:08:39 +04:00
edac_dbg ( 3 , " (%d) cumul_size 0x%x \n " , index , cumul_size ) ;
2006-01-19 04:44:08 +03:00
if ( cumul_size = = last_cumul_size )
2006-06-30 12:56:08 +04:00
continue ; /* not populated */
2006-01-19 04:44:08 +03:00
csrow - > first_page = last_cumul_size ;
csrow - > last_page = cumul_size - 1 ;
2012-01-28 16:09:38 +04:00
nr_pages = cumul_size - last_cumul_size ;
2006-01-19 04:44:08 +03:00
last_cumul_size = cumul_size ;
2012-01-28 01:38:08 +04:00
2012-03-29 02:37:59 +04:00
/*
* if single channel or x8 devices then SECDED
* if dual channel and x4 then S4ECD4ED
*/
if ( drc_ddim ) {
if ( drc_chan & & mem_dev ) {
edac_mode = EDAC_S4ECD4ED ;
mci - > edac_cap | = EDAC_FLAG_S4ECD4ED ;
} else {
edac_mode = EDAC_SECDED ;
mci - > edac_cap | = EDAC_FLAG_SECDED ;
}
} else
edac_mode = EDAC_NONE ;
2012-01-28 01:38:08 +04:00
for ( j = 0 ; j < drc_chan + 1 ; j + + ) {
2012-04-24 22:05:43 +04:00
dimm = csrow - > channels [ j ] - > dimm ;
2012-01-28 01:38:08 +04:00
2012-01-28 16:09:38 +04:00
dimm - > nr_pages = nr_pages / ( drc_chan + 1 ) ;
2012-01-28 01:38:08 +04:00
dimm - > grain = 1 < < 12 ; /* 4KiB - resolution of CELOG */
dimm - > mtype = MEM_RDDR ; /* only one type supported */
dimm - > dtype = mem_dev ? DEV_X4 : DEV_X8 ;
2012-03-29 02:37:59 +04:00
dimm - > edac_mode = edac_mode ;
2012-01-28 01:38:08 +04:00
}
2006-01-19 04:44:08 +03:00
}
2006-06-30 12:56:08 +04:00
}
2006-01-19 04:44:08 +03:00
2006-06-30 12:56:08 +04:00
static int e7xxx_probe1 ( struct pci_dev * pdev , int dev_idx )
{
u16 pci_data ;
struct mem_ctl_info * mci = NULL ;
2012-04-16 22:07:09 +04:00
struct edac_mc_layer layers [ 2 ] ;
2006-06-30 12:56:08 +04:00
struct e7xxx_pvt * pvt = NULL ;
u32 drc ;
int drc_chan ;
struct e7xxx_error_info discard ;
2012-04-30 00:08:39 +04:00
edac_dbg ( 0 , " mci \n " ) ;
2007-07-19 12:49:46 +04:00
2006-06-30 12:56:08 +04:00
pci_read_config_dword ( pdev , E7XXX_DRC , & drc ) ;
drc_chan = dual_channel_active ( drc , dev_idx ) ;
2012-04-16 22:07:09 +04:00
/*
* According with the datasheet , this device has a maximum of
* 4 DIMMS per channel , either single - rank or dual - rank . So , the
* total amount of dimms is 8 ( E7XXX_NR_DIMMS ) .
* That means that the DIMM is mapped as CSROWs , and the channel
* will map the rank . So , an error to either channel should be
* attributed to the same dimm .
*/
layers [ 0 ] . type = EDAC_MC_LAYER_CHIP_SELECT ;
layers [ 0 ] . size = E7XXX_NR_CSROWS ;
layers [ 0 ] . is_virt_csrow = true ;
layers [ 1 ] . type = EDAC_MC_LAYER_CHANNEL ;
layers [ 1 ] . size = drc_chan + 1 ;
layers [ 1 ] . is_virt_csrow = false ;
2012-05-02 21:37:00 +04:00
mci = edac_mc_alloc ( 0 , ARRAY_SIZE ( layers ) , layers , sizeof ( * pvt ) ) ;
2006-06-30 12:56:08 +04:00
if ( mci = = NULL )
return - ENOMEM ;
2006-01-19 04:44:08 +03:00
2012-04-30 00:08:39 +04:00
edac_dbg ( 3 , " init mci \n " ) ;
2006-06-30 12:56:08 +04:00
mci - > mtype_cap = MEM_FLAG_RDDR ;
mci - > edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED |
2007-07-19 12:50:13 +04:00
EDAC_FLAG_S4ECD4ED ;
2006-06-30 12:56:08 +04:00
/* FIXME - what if different memory types are in different csrows? */
mci - > mod_name = EDAC_MOD_STR ;
2012-03-16 14:44:18 +04:00
mci - > pdev = & pdev - > dev ;
2012-04-30 00:08:39 +04:00
edac_dbg ( 3 , " init pvt \n " ) ;
2007-07-19 12:50:03 +04:00
pvt = ( struct e7xxx_pvt * ) mci - > pvt_info ;
2006-06-30 12:56:08 +04:00
pvt - > dev_info = & e7xxx_devs [ dev_idx ] ;
pvt - > bridge_ck = pci_get_device ( PCI_VENDOR_ID_INTEL ,
2007-07-19 12:50:03 +04:00
pvt - > dev_info - > err_dev , pvt - > bridge_ck ) ;
2006-06-30 12:56:08 +04:00
if ( ! pvt - > bridge_ck ) {
e7xxx_printk ( KERN_ERR , " error reporting device not found: "
2007-07-19 12:50:13 +04:00
" vendor %x device 0x%x (broken BIOS?) \n " ,
PCI_VENDOR_ID_INTEL , e7xxx_devs [ dev_idx ] . err_dev ) ;
2006-06-30 12:56:08 +04:00
goto fail0 ;
}
2012-04-30 00:08:39 +04:00
edac_dbg ( 3 , " more mci init \n " ) ;
2006-06-30 12:56:08 +04:00
mci - > ctl_name = pvt - > dev_info - > ctl_name ;
2007-07-19 12:49:47 +04:00
mci - > dev_name = pci_name ( pdev ) ;
2006-06-30 12:56:08 +04:00
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 ;
2012-04-30 00:08:39 +04:00
edac_dbg ( 3 , " tolm, remapbase, remaplimit \n " ) ;
2006-01-19 04:44:08 +03:00
/* load the top of low memory, remap base, and remap limit vars */
2006-06-30 12:56:07 +04:00
pci_read_config_word ( pdev , E7XXX_TOLM , & pci_data ) ;
2006-01-19 04:44:08 +03:00
pvt - > tolm = ( ( u32 ) pci_data ) < < 4 ;
2006-06-30 12:56:07 +04:00
pci_read_config_word ( pdev , E7XXX_REMAPBASE , & pci_data ) ;
2006-01-19 04:44:08 +03:00
pvt - > remapbase = ( ( u32 ) pci_data ) < < 14 ;
2006-06-30 12:56:07 +04:00
pci_read_config_word ( pdev , E7XXX_REMAPLIMIT , & pci_data ) ;
2006-01-19 04:44:08 +03:00
pvt - > remaplimit = ( ( u32 ) pci_data ) < < 14 ;
2006-03-26 13:38:40 +04:00
e7xxx_printk ( KERN_INFO ,
2007-07-19 12:50:13 +04:00
" tolm = %x, remapbase = %x, remaplimit = %x \n " , pvt - > tolm ,
pvt - > remapbase , pvt - > remaplimit ) ;
2006-01-19 04:44:08 +03:00
/* clear any pending errors, or initial state bits */
2006-03-26 13:38:45 +04:00
e7xxx_get_error_info ( mci , & discard ) ;
2006-01-19 04:44:08 +03:00
2006-06-30 12:56:08 +04:00
/* Here we assume that we will never see multiple instances of this
* type of memory controller . The ID is therefore hardcoded to 0.
*/
2007-07-19 12:50:26 +04:00
if ( edac_mc_add_mc ( mci ) ) {
2012-04-30 00:08:39 +04:00
edac_dbg ( 3 , " failed edac_mc_add_mc() \n " ) ;
2006-06-30 12:56:08 +04:00
goto fail1 ;
2006-01-19 04:44:08 +03:00
}
2007-07-19 12:50:10 +04:00
/* 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__ ) ;
}
2006-01-19 04:44:08 +03:00
/* get this far and it's successful */
2012-04-30 00:08:39 +04:00
edac_dbg ( 3 , " success \n " ) ;
2006-01-19 04:44:08 +03:00
return 0 ;
2007-07-19 12:50:13 +04:00
fail1 :
2006-06-30 12:56:08 +04:00
pci_dev_put ( pvt - > bridge_ck ) ;
2007-07-19 12:50:13 +04:00
fail0 :
2006-06-30 12:56:08 +04:00
edac_mc_free ( mci ) ;
2006-01-19 04:44:08 +03:00
2006-06-30 12:56:08 +04:00
return - ENODEV ;
2006-01-19 04:44:08 +03:00
}
/* returns count (>= 0), or negative on error */
2012-12-22 01:23:51 +04:00
static int e7xxx_init_one ( struct pci_dev * pdev , const struct pci_device_id * ent )
2006-01-19 04:44:08 +03:00
{
2012-04-30 00:08:39 +04:00
edac_dbg ( 0 , " \n " ) ;
2006-01-19 04:44:08 +03:00
/* wake up and enable device */
return pci_enable_device ( pdev ) ?
2007-07-19 12:50:13 +04:00
- EIO : e7xxx_probe1 ( pdev , ent - > driver_data ) ;
2006-01-19 04:44:08 +03:00
}
2012-12-22 01:23:51 +04:00
static void e7xxx_remove_one ( struct pci_dev * pdev )
2006-01-19 04:44:08 +03:00
{
struct mem_ctl_info * mci ;
struct e7xxx_pvt * pvt ;
2012-04-30 00:08:39 +04:00
edac_dbg ( 0 , " \n " ) ;
2006-01-19 04:44:08 +03:00
2007-07-19 12:50:10 +04:00
if ( e7xxx_pci )
edac_pci_release_generic_ctl ( e7xxx_pci ) ;
2006-06-30 12:56:07 +04:00
if ( ( mci = edac_mc_del_mc ( & pdev - > dev ) ) = = NULL )
2006-03-26 13:38:50 +04:00
return ;
2007-07-19 12:50:03 +04:00
pvt = ( struct e7xxx_pvt * ) mci - > pvt_info ;
2006-03-26 13:38:50 +04:00
pci_dev_put ( pvt - > bridge_ck ) ;
edac_mc_free ( mci ) ;
2006-01-19 04:44:08 +03:00
}
2013-12-06 13:23:08 +04:00
static const struct pci_device_id e7xxx_pci_tbl [ ] = {
2006-03-26 13:38:52 +04:00
{
2007-07-19 12:50:03 +04:00
PCI_VEND_DEV ( INTEL , 7205 _0 ) , PCI_ANY_ID , PCI_ANY_ID , 0 , 0 ,
E7205 } ,
2006-03-26 13:38:52 +04:00
{
2007-07-19 12:50:03 +04:00
PCI_VEND_DEV ( INTEL , 7500 _0 ) , PCI_ANY_ID , PCI_ANY_ID , 0 , 0 ,
E7500 } ,
2006-03-26 13:38:52 +04:00
{
2007-07-19 12:50:03 +04:00
PCI_VEND_DEV ( INTEL , 7501 _0 ) , PCI_ANY_ID , PCI_ANY_ID , 0 , 0 ,
E7501 } ,
2006-03-26 13:38:52 +04:00
{
2007-07-19 12:50:03 +04:00
PCI_VEND_DEV ( INTEL , 7505 _0 ) , PCI_ANY_ID , PCI_ANY_ID , 0 , 0 ,
E7505 } ,
2006-03-26 13:38:52 +04:00
{
2007-07-19 12:50:03 +04:00
0 ,
} /* 0 terminated list. */
2006-01-19 04:44:08 +03:00
} ;
MODULE_DEVICE_TABLE ( pci , e7xxx_pci_tbl ) ;
static struct pci_driver e7xxx_driver = {
2006-03-26 13:38:41 +04:00
. name = EDAC_MOD_STR ,
2006-01-19 04:44:08 +03:00
. probe = e7xxx_init_one ,
2012-12-22 01:23:51 +04:00
. remove = e7xxx_remove_one ,
2006-01-19 04:44:08 +03:00
. id_table = e7xxx_pci_tbl ,
} ;
2006-01-19 04:44:13 +03:00
static int __init e7xxx_init ( void )
2006-01-19 04:44:08 +03:00
{
2008-04-29 12:03:18 +04:00
/* Ensure that the OPSTATE is set correctly for POLL or NMI */
opstate_init ( ) ;
2006-01-19 04:44:08 +03:00
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 "
2007-07-19 12:50:13 +04:00
" Based on.work by Dan Hollis et al " ) ;
2006-01-19 04:44:08 +03:00
MODULE_DESCRIPTION ( " MC support for Intel e7xxx memory controllers " ) ;
2007-07-19 12:49:46 +04:00
module_param ( edac_op_state , int , 0444 ) ;
MODULE_PARM_DESC ( edac_op_state , " EDAC Error Reporting state: 0=Poll,1=NMI " ) ;