2009-06-23 05:41:15 +04:00
/* Intel 7 core Memory Controller kernel module (Nehalem)
*
* This file may be distributed under the terms of the
* GNU General Public License version 2 only .
*
* Copyright ( c ) 2009 by :
* Mauro Carvalho Chehab < mchehab @ redhat . com >
*
* Red Hat Inc . http : //www.redhat.com
*
* Forked and adapted from the i5400_edac driver
*
* Based on the following public Intel datasheets :
* Intel Core i7 Processor Extreme Edition and Intel Core i7 Processor
* Datasheet , Volume 2 :
* http : //download.intel.com/design/processor/datashts/320835.pdf
* Intel Xeon Processor 5500 Series Datasheet Volume 2
* http : //www.intel.com/Assets/PDF/datasheet/321322.pdf
* also available at :
* http : //www.arrownac.com/manufacturers/intel/s/nehalem/5500-datasheet-v2.pdf
*/
# include <linux/module.h>
# include <linux/init.h>
# include <linux/pci.h>
# include <linux/pci_ids.h>
# include <linux/slab.h>
# include <linux/edac.h>
# include <linux/mmzone.h>
2009-07-10 05:06:41 +04:00
# include <linux/edac_mce.h>
# include <linux/spinlock.h>
2009-06-23 05:41:15 +04:00
# include "edac_core.h"
2009-06-23 05:48:29 +04:00
/* To use the new pci_[read/write]_config_qword instead of two dword */
2009-07-10 05:14:35 +04:00
# define USE_QWORD 0
2009-06-23 05:41:15 +04:00
/*
* Alter this version for the module when modifications are made
*/
# define I7CORE_REVISION " Ver: 1.0.0 " __DATE__
# define EDAC_MOD_STR "i7core_edac"
/* HACK: temporary, just to enable all logs, for now */
# undef debugf0
# define debugf0(fmt, arg...) edac_printk(KERN_INFO, "i7core", fmt, ##arg)
/*
* Debug macros
*/
# define i7core_printk(level, fmt, arg...) \
edac_printk ( level , " i7core " , fmt , # # arg )
# define i7core_mc_printk(mci, level, fmt, arg...) \
edac_mc_chipset_printk ( mci , level , " i7core " , fmt , # # arg )
/*
* i7core Memory Controller Registers
*/
2009-07-10 05:14:35 +04:00
/* OFFSETS for Device 0 Function 0 */
# define MC_CFG_CONTROL 0x90
2009-06-23 05:41:15 +04:00
/* OFFSETS for Device 3 Function 0 */
# define MC_CONTROL 0x48
# define MC_STATUS 0x4c
# define MC_MAX_DOD 0x64
2009-06-23 05:48:29 +04:00
/*
* OFFSETS for Device 3 Function 4 , as inicated on Xeon 5500 datasheet :
* http : //www.arrownac.com/manufacturers/intel/s/nehalem/5500-datasheet-v2.pdf
*/
# define MC_TEST_ERR_RCV1 0x60
# define DIMM2_COR_ERR(r) ((r) & 0x7fff)
# define MC_TEST_ERR_RCV0 0x64
# define DIMM1_COR_ERR(r) (((r) >> 16) & 0x7fff)
# define DIMM0_COR_ERR(r) ((r) & 0x7fff)
2009-06-23 05:41:15 +04:00
/* OFFSETS for Devices 4,5 and 6 Function 0 */
2009-06-23 05:48:29 +04:00
# define MC_CHANNEL_DIMM_INIT_PARAMS 0x58
# define THREE_DIMMS_PRESENT (1 << 24)
# define SINGLE_QUAD_RANK_PRESENT (1 << 23)
# define QUAD_RANK_PRESENT (1 << 22)
# define REGISTERED_DIMM (1 << 15)
2009-06-23 05:48:29 +04:00
# define MC_CHANNEL_MAPPER 0x60
# define RDLCH(r, ch) ((((r) >> (3 + (ch * 6))) & 0x07) - 1)
# define WRLCH(r, ch) ((((r) >> (ch * 6)) & 0x07) - 1)
2009-06-23 05:48:29 +04:00
# define MC_CHANNEL_RANK_PRESENT 0x7c
# define RANK_PRESENT_MASK 0xffff
2009-06-23 05:41:15 +04:00
# define MC_CHANNEL_ADDR_MATCH 0xf0
2009-06-23 05:48:28 +04:00
# define MC_CHANNEL_ERROR_MASK 0xf8
# define MC_CHANNEL_ERROR_INJECT 0xfc
# define INJECT_ADDR_PARITY 0x10
# define INJECT_ECC 0x08
# define MASK_CACHELINE 0x06
# define MASK_FULL_CACHELINE 0x06
# define MASK_MSB32_CACHELINE 0x04
# define MASK_LSB32_CACHELINE 0x02
# define NO_MASK_CACHELINE 0x00
# define REPEAT_EN 0x01
2009-06-23 05:41:15 +04:00
2009-06-23 05:48:29 +04:00
/* OFFSETS for Devices 4,5 and 6 Function 1 */
# define MC_DOD_CH_DIMM0 0x48
# define MC_DOD_CH_DIMM1 0x4c
# define MC_DOD_CH_DIMM2 0x50
# define RANKOFFSET_MASK ((1 << 12) | (1 << 11) | (1 << 10))
# define RANKOFFSET(x) ((x & RANKOFFSET_MASK) >> 10)
# define DIMM_PRESENT_MASK (1 << 9)
# define DIMM_PRESENT(x) (((x) & DIMM_PRESENT_MASK) >> 9)
2009-06-23 05:48:30 +04:00
# define MC_DOD_NUMBANK_MASK ((1 << 8) | (1 << 7))
# define MC_DOD_NUMBANK(x) (((x) & MC_DOD_NUMBANK_MASK) >> 7)
# define MC_DOD_NUMRANK_MASK ((1 << 6) | (1 << 5))
# define MC_DOD_NUMRANK(x) (((x) & MC_DOD_NUMRANK_MASK) >> 5)
2009-06-23 05:48:31 +04:00
# define MC_DOD_NUMROW_MASK ((1 << 4) | (1 << 3) | (1 << 2))
i7core_edac: Memory info fixes and preparation for properly filling cswrow data
Now, memory size is properly displayed:
EDAC i7core: DOD Max limits: DIMMS: 2, 1-ranked, 8-banked
EDAC i7core: DOD Max rows x colums = 0x4000 x 0x400
EDAC i7core: Memory channel configuration:
EDAC i7core: Ch0 phy rd0, wr0 (0x063f7c31): 2 ranks, UDIMMs
EDAC i7core: dimm 0 (0x00000288) 1024 Mb offset: 0, numbank: 8,
numrank: 1, numrow: 0x4000, numcol: 0x400
EDAC i7core: dimm 1 (0x00001288) 1024 Mb offset: 4, numbank: 8,
numrank: 1, numrow: 0x4000, numcol: 0x400
EDAC i7core: Ch1 phy rd1, wr1 (0x063f7c31): 2 ranks, UDIMMs
EDAC i7core: dimm 0 (0x00000288) 1024 Mb offset: 0, numbank: 8,
numrank: 1, numrow: 0x4000, numcol: 0x400
EDAC i7core: Ch2 phy rd3, wr3 (0x063f7c31): 2 ranks, UDIMMs
EDAC i7core: dimm 0 (0x00000288) 1024 Mb offset: 0, numbank: 8,
numrank: 1, numrow: 0x4000, numcol: 0x400
Still, as the way to retrieve csrows info is not known, it does a
mapping of what's available to csrows basic unit at edac core.
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
2009-06-23 05:48:31 +04:00
# define MC_DOD_NUMROW(x) (((x) & MC_DOD_NUMROW_MASK) >> 2)
2009-06-23 05:48:30 +04:00
# define MC_DOD_NUMCOL_MASK 3
# define MC_DOD_NUMCOL(x) ((x) & MC_DOD_NUMCOL_MASK)
2009-06-23 05:48:29 +04:00
2009-06-23 05:48:29 +04:00
# define MC_RANK_PRESENT 0x7c
2009-06-23 05:48:29 +04:00
# define MC_SAG_CH_0 0x80
# define MC_SAG_CH_1 0x84
# define MC_SAG_CH_2 0x88
# define MC_SAG_CH_3 0x8c
# define MC_SAG_CH_4 0x90
# define MC_SAG_CH_5 0x94
# define MC_SAG_CH_6 0x98
# define MC_SAG_CH_7 0x9c
# define MC_RIR_LIMIT_CH_0 0x40
# define MC_RIR_LIMIT_CH_1 0x44
# define MC_RIR_LIMIT_CH_2 0x48
# define MC_RIR_LIMIT_CH_3 0x4C
# define MC_RIR_LIMIT_CH_4 0x50
# define MC_RIR_LIMIT_CH_5 0x54
# define MC_RIR_LIMIT_CH_6 0x58
# define MC_RIR_LIMIT_CH_7 0x5C
# define MC_RIR_LIMIT_MASK ((1 << 10) - 1)
# define MC_RIR_WAY_CH 0x80
# define MC_RIR_WAY_OFFSET_MASK (((1 << 14) - 1) & ~0x7)
# define MC_RIR_WAY_RANK_MASK 0x7
2009-06-23 05:41:15 +04:00
/*
* i7core structs
*/
# define NUM_CHANS 3
2009-06-23 05:48:29 +04:00
# define MAX_DIMMS 3 /* Max DIMMS per channel */
2009-07-15 13:56:23 +04:00
# define NUM_SOCKETS 2 /* Max number of MC sockets */
2009-06-23 05:48:29 +04:00
# define MAX_MCR_FUNC 4
# define MAX_CHAN_FUNC 3
2009-06-23 05:41:15 +04:00
struct i7core_info {
u32 mc_control ;
u32 mc_status ;
u32 max_dod ;
2009-06-23 05:48:29 +04:00
u32 ch_map ;
2009-06-23 05:41:15 +04:00
} ;
2009-06-23 05:48:28 +04:00
struct i7core_inject {
int enable ;
2009-07-15 13:56:23 +04:00
u8 socket ;
2009-06-23 05:48:28 +04:00
u32 section ;
u32 type ;
u32 eccmask ;
/* Error address mask */
int channel , dimm , rank , bank , page , col ;
} ;
2009-06-23 05:48:29 +04:00
struct i7core_channel {
2009-06-23 05:48:29 +04:00
u32 ranks ;
u32 dimms ;
2009-06-23 05:48:29 +04:00
} ;
2009-06-23 05:48:29 +04:00
struct pci_id_descr {
int dev ;
int func ;
int dev_id ;
2009-07-15 13:56:23 +04:00
struct pci_dev * pdev [ NUM_SOCKETS ] ;
2009-06-23 05:48:29 +04:00
} ;
2009-06-23 05:41:15 +04:00
struct i7core_pvt {
2009-07-15 13:56:23 +04:00
struct pci_dev * pci_noncore [ NUM_SOCKETS ] ;
struct pci_dev * pci_mcr [ NUM_SOCKETS ] [ MAX_MCR_FUNC + 1 ] ;
struct pci_dev * pci_ch [ NUM_SOCKETS ] [ NUM_CHANS ] [ MAX_CHAN_FUNC + 1 ] ;
2009-06-23 05:41:15 +04:00
struct i7core_info info ;
2009-06-23 05:48:28 +04:00
struct i7core_inject inject ;
2009-07-15 13:56:23 +04:00
struct i7core_channel channel [ NUM_SOCKETS ] [ NUM_CHANS ] ;
int sockets ; /* Number of sockets */
2009-06-23 05:48:30 +04:00
int channels ; /* Number of active channels */
2009-06-23 05:48:29 +04:00
2009-07-15 13:56:23 +04:00
int ce_count_available [ NUM_SOCKETS ] ;
/* ECC corrected errors counts per dimm */
unsigned long ce_count [ NUM_SOCKETS ] [ MAX_DIMMS ] ;
int last_ce_count [ NUM_SOCKETS ] [ MAX_DIMMS ] ;
2009-06-23 05:48:29 +04:00
2009-07-10 05:06:41 +04:00
/* mcelog glue */
struct edac_mce edac_mce ;
struct mce mce_entry [ MCE_LOG_LEN ] ;
unsigned mce_count ;
spinlock_t mce_lock ;
2009-06-23 05:41:15 +04:00
} ;
/* Device name and register DID (Device ID) */
struct i7core_dev_info {
const char * ctl_name ; /* name for this device */
u16 fsb_mapping_errors ; /* DID for the branchmap,control */
} ;
2009-06-23 05:48:29 +04:00
# define PCI_DESCR(device, function, device_id) \
. dev = ( device ) , \
. func = ( function ) , \
. dev_id = ( device_id )
struct pci_id_descr pci_devs [ ] = {
2009-07-11 01:39:53 +04:00
/* Generic Non-core registers */
{ PCI_DESCR ( 0 , 0 , PCI_DEVICE_ID_INTEL_I7_NOCORE ) } ,
2009-06-23 05:48:29 +04:00
/* Memory controller */
{ PCI_DESCR ( 3 , 0 , PCI_DEVICE_ID_INTEL_I7_MCR ) } ,
{ PCI_DESCR ( 3 , 1 , PCI_DEVICE_ID_INTEL_I7_MC_TAD ) } ,
{ PCI_DESCR ( 3 , 2 , PCI_DEVICE_ID_INTEL_I7_MC_RAS ) } , /* if RDIMM is supported */
{ PCI_DESCR ( 3 , 4 , PCI_DEVICE_ID_INTEL_I7_MC_TEST ) } ,
/* Channel 0 */
{ PCI_DESCR ( 4 , 0 , PCI_DEVICE_ID_INTEL_I7_MC_CH0_CTRL ) } ,
{ PCI_DESCR ( 4 , 1 , PCI_DEVICE_ID_INTEL_I7_MC_CH0_ADDR ) } ,
{ PCI_DESCR ( 4 , 2 , PCI_DEVICE_ID_INTEL_I7_MC_CH0_RANK ) } ,
{ PCI_DESCR ( 4 , 3 , PCI_DEVICE_ID_INTEL_I7_MC_CH0_TC ) } ,
/* Channel 1 */
{ PCI_DESCR ( 5 , 0 , PCI_DEVICE_ID_INTEL_I7_MC_CH1_CTRL ) } ,
{ PCI_DESCR ( 5 , 1 , PCI_DEVICE_ID_INTEL_I7_MC_CH1_ADDR ) } ,
{ PCI_DESCR ( 5 , 2 , PCI_DEVICE_ID_INTEL_I7_MC_CH1_RANK ) } ,
{ PCI_DESCR ( 5 , 3 , PCI_DEVICE_ID_INTEL_I7_MC_CH1_TC ) } ,
/* Channel 2 */
{ PCI_DESCR ( 6 , 0 , PCI_DEVICE_ID_INTEL_I7_MC_CH2_CTRL ) } ,
{ PCI_DESCR ( 6 , 1 , PCI_DEVICE_ID_INTEL_I7_MC_CH2_ADDR ) } ,
{ PCI_DESCR ( 6 , 2 , PCI_DEVICE_ID_INTEL_I7_MC_CH2_RANK ) } ,
{ PCI_DESCR ( 6 , 3 , PCI_DEVICE_ID_INTEL_I7_MC_CH2_TC ) } ,
2009-06-23 05:41:15 +04:00
} ;
2009-06-23 05:48:29 +04:00
# define N_DEVS ARRAY_SIZE(pci_devs)
/*
* pci_device_id table for which devices we are looking for
* This should match the first device at pci_devs table
*/
static const struct pci_device_id i7core_pci_tbl [ ] __devinitdata = {
2009-07-11 01:39:53 +04:00
{ PCI_DEVICE ( PCI_VENDOR_ID_INTEL , PCI_DEVICE_ID_INTEL_X58_HUB_MGMT ) } ,
2009-06-23 05:48:29 +04:00
{ 0 , } /* 0 terminated list. */
} ;
2009-06-23 05:41:15 +04:00
/* Table of devices attributes supported by this driver */
static const struct i7core_dev_info i7core_devs [ ] = {
{
. ctl_name = " i7 Core " ,
. fsb_mapping_errors = PCI_DEVICE_ID_INTEL_I7_MCR ,
} ,
} ;
static struct edac_pci_ctl_info * i7core_pci ;
/****************************************************************************
Anciliary status routines
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* MC_CONTROL bits */
2009-06-23 05:48:30 +04:00
# define CH_ACTIVE(pvt, ch) ((pvt)->info.mc_control & (1 << (8 + ch)))
# define ECCx8(pvt) ((pvt)->info.mc_control & (1 << 1))
2009-06-23 05:41:15 +04:00
/* MC_STATUS bits */
2009-06-23 05:48:30 +04:00
# define ECC_ENABLED(pvt) ((pvt)->info.mc_status & (1 << 3))
# define CH_DISABLED(pvt, ch) ((pvt)->info.mc_status & (1 << ch))
2009-06-23 05:41:15 +04:00
/* MC_MAX_DOD read functions */
2009-06-23 05:48:30 +04:00
static inline int numdimms ( u32 dimms )
2009-06-23 05:41:15 +04:00
{
2009-06-23 05:48:30 +04:00
return ( dimms & 0x3 ) + 1 ;
2009-06-23 05:41:15 +04:00
}
2009-06-23 05:48:30 +04:00
static inline int numrank ( u32 rank )
2009-06-23 05:41:15 +04:00
{
static int ranks [ 4 ] = { 1 , 2 , 4 , - EINVAL } ;
2009-06-23 05:48:30 +04:00
return ranks [ rank & 0x3 ] ;
2009-06-23 05:41:15 +04:00
}
2009-06-23 05:48:30 +04:00
static inline int numbank ( u32 bank )
2009-06-23 05:41:15 +04:00
{
static int banks [ 4 ] = { 4 , 8 , 16 , - EINVAL } ;
2009-06-23 05:48:30 +04:00
return banks [ bank & 0x3 ] ;
2009-06-23 05:41:15 +04:00
}
2009-06-23 05:48:30 +04:00
static inline int numrow ( u32 row )
2009-06-23 05:41:15 +04:00
{
static int rows [ 8 ] = {
1 < < 12 , 1 < < 13 , 1 < < 14 , 1 < < 15 ,
1 < < 16 , - EINVAL , - EINVAL , - EINVAL ,
} ;
2009-06-23 05:48:30 +04:00
return rows [ row & 0x7 ] ;
2009-06-23 05:41:15 +04:00
}
2009-06-23 05:48:30 +04:00
static inline int numcol ( u32 col )
2009-06-23 05:41:15 +04:00
{
static int cols [ 8 ] = {
1 < < 10 , 1 < < 11 , 1 < < 12 , - EINVAL ,
} ;
2009-06-23 05:48:30 +04:00
return cols [ col & 0x3 ] ;
2009-06-23 05:41:15 +04:00
}
2009-06-23 05:48:28 +04:00
2009-06-23 05:41:15 +04:00
/****************************************************************************
Memory check routines
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2009-07-15 13:56:23 +04:00
static struct pci_dev * get_pdev_slot_func ( u8 socket , unsigned slot ,
unsigned func )
2009-06-23 05:48:30 +04:00
{
int i ;
for ( i = 0 ; i < N_DEVS ; i + + ) {
2009-07-15 13:56:23 +04:00
if ( ! pci_devs [ i ] . pdev [ socket ] )
2009-06-23 05:48:30 +04:00
continue ;
2009-07-15 13:56:23 +04:00
if ( PCI_SLOT ( pci_devs [ i ] . pdev [ socket ] - > devfn ) = = slot & &
PCI_FUNC ( pci_devs [ i ] . pdev [ socket ] - > devfn ) = = func ) {
return pci_devs [ i ] . pdev [ socket ] ;
2009-06-23 05:48:30 +04:00
}
}
2009-06-23 05:48:31 +04:00
return NULL ;
}
2009-07-15 13:56:23 +04:00
static int i7core_get_active_channels ( u8 socket , unsigned * channels ,
unsigned * csrows )
2009-06-23 05:48:31 +04:00
{
struct pci_dev * pdev = NULL ;
int i , j ;
u32 status , control ;
* channels = 0 ;
* csrows = 0 ;
2009-07-15 13:56:23 +04:00
pdev = get_pdev_slot_func ( socket , 3 , 0 ) ;
2009-06-23 05:48:30 +04:00
if ( ! pdev ) {
2009-07-15 13:56:23 +04:00
i7core_printk ( KERN_ERR , " Couldn't find socket %d fn 3.0!!! \n " ,
socket ) ;
2009-06-23 05:48:30 +04:00
return - ENODEV ;
2009-06-23 05:48:30 +04:00
}
2009-06-23 05:48:30 +04:00
/* Device 3 function 0 reads */
pci_read_config_dword ( pdev , MC_STATUS , & status ) ;
pci_read_config_dword ( pdev , MC_CONTROL , & control ) ;
for ( i = 0 ; i < NUM_CHANS ; i + + ) {
2009-06-23 05:48:31 +04:00
u32 dimm_dod [ 3 ] ;
2009-06-23 05:48:30 +04:00
/* Check if the channel is active */
if ( ! ( control & ( 1 < < ( 8 + i ) ) ) )
continue ;
/* Check if the channel is disabled */
2009-06-23 05:48:31 +04:00
if ( status & ( 1 < < i ) )
2009-06-23 05:48:30 +04:00
continue ;
2009-07-15 13:56:23 +04:00
pdev = get_pdev_slot_func ( socket , i + 4 , 1 ) ;
2009-06-23 05:48:31 +04:00
if ( ! pdev ) {
2009-07-15 13:56:23 +04:00
i7core_printk ( KERN_ERR , " Couldn't find socket %d "
" fn %d.%d!!! \n " ,
socket , i + 4 , 1 ) ;
2009-06-23 05:48:31 +04:00
return - ENODEV ;
}
/* Devices 4-6 function 1 */
pci_read_config_dword ( pdev ,
MC_DOD_CH_DIMM0 , & dimm_dod [ 0 ] ) ;
pci_read_config_dword ( pdev ,
MC_DOD_CH_DIMM1 , & dimm_dod [ 1 ] ) ;
pci_read_config_dword ( pdev ,
MC_DOD_CH_DIMM2 , & dimm_dod [ 2 ] ) ;
2009-06-23 05:48:30 +04:00
( * channels ) + + ;
2009-06-23 05:48:31 +04:00
for ( j = 0 ; j < 3 ; j + + ) {
if ( ! DIMM_PRESENT ( dimm_dod [ j ] ) )
continue ;
( * csrows ) + + ;
}
2009-06-23 05:48:30 +04:00
}
2009-07-15 13:56:23 +04:00
debugf0 ( " Number of active channels on socked %d: %d \n " ,
socket , * channels ) ;
2009-06-23 05:48:30 +04:00
2009-06-23 05:48:30 +04:00
return 0 ;
}
2009-07-15 16:02:32 +04:00
static int get_dimm_config ( struct mem_ctl_info * mci , int * csrow , u8 socket )
2009-06-23 05:41:15 +04:00
{
struct i7core_pvt * pvt = mci - > pvt_info ;
2009-06-23 05:48:30 +04:00
struct csrow_info * csr ;
2009-06-23 05:48:30 +04:00
struct pci_dev * pdev ;
2009-07-15 16:02:32 +04:00
int i , j ;
i7core_edac: Memory info fixes and preparation for properly filling cswrow data
Now, memory size is properly displayed:
EDAC i7core: DOD Max limits: DIMMS: 2, 1-ranked, 8-banked
EDAC i7core: DOD Max rows x colums = 0x4000 x 0x400
EDAC i7core: Memory channel configuration:
EDAC i7core: Ch0 phy rd0, wr0 (0x063f7c31): 2 ranks, UDIMMs
EDAC i7core: dimm 0 (0x00000288) 1024 Mb offset: 0, numbank: 8,
numrank: 1, numrow: 0x4000, numcol: 0x400
EDAC i7core: dimm 1 (0x00001288) 1024 Mb offset: 4, numbank: 8,
numrank: 1, numrow: 0x4000, numcol: 0x400
EDAC i7core: Ch1 phy rd1, wr1 (0x063f7c31): 2 ranks, UDIMMs
EDAC i7core: dimm 0 (0x00000288) 1024 Mb offset: 0, numbank: 8,
numrank: 1, numrow: 0x4000, numcol: 0x400
EDAC i7core: Ch2 phy rd3, wr3 (0x063f7c31): 2 ranks, UDIMMs
EDAC i7core: dimm 0 (0x00000288) 1024 Mb offset: 0, numbank: 8,
numrank: 1, numrow: 0x4000, numcol: 0x400
Still, as the way to retrieve csrows info is not known, it does a
mapping of what's available to csrows basic unit at edac core.
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
2009-06-23 05:48:31 +04:00
unsigned long last_page = 0 ;
2009-06-23 05:48:30 +04:00
enum edac_type mode ;
2009-06-23 05:48:30 +04:00
enum mem_type mtype ;
2009-06-23 05:41:15 +04:00
2009-06-23 05:48:30 +04:00
/* Get data from the MC register, function 0 */
2009-07-15 13:56:23 +04:00
pdev = pvt - > pci_mcr [ socket ] [ 0 ] ;
2009-06-23 05:48:30 +04:00
if ( ! pdev )
2009-06-23 05:48:29 +04:00
return - ENODEV ;
2009-06-23 05:48:29 +04:00
/* Device 3 function 0 reads */
2009-06-23 05:48:30 +04:00
pci_read_config_dword ( pdev , MC_CONTROL , & pvt - > info . mc_control ) ;
pci_read_config_dword ( pdev , MC_STATUS , & pvt - > info . mc_status ) ;
pci_read_config_dword ( pdev , MC_MAX_DOD , & pvt - > info . max_dod ) ;
pci_read_config_dword ( pdev , MC_CHANNEL_MAPPER , & pvt - > info . ch_map ) ;
2009-06-23 05:48:29 +04:00
debugf0 ( " MC control=0x%08x status=0x%08x dod=0x%08x map=0x%08x \n " ,
pvt - > info . mc_control , pvt - > info . mc_status ,
pvt - > info . max_dod , pvt - > info . ch_map ) ;
2009-06-23 05:41:15 +04:00
2009-06-23 05:48:30 +04:00
if ( ECC_ENABLED ( pvt ) ) {
2009-06-23 05:48:31 +04:00
debugf0 ( " ECC enabled with x%d SDCC \n " , ECCx8 ( pvt ) ? 8 : 4 ) ;
2009-06-23 05:48:30 +04:00
if ( ECCx8 ( pvt ) )
mode = EDAC_S8ECD8ED ;
else
mode = EDAC_S4ECD4ED ;
} else {
2009-06-23 05:41:15 +04:00
debugf0 ( " ECC disabled \n " ) ;
2009-06-23 05:48:30 +04:00
mode = EDAC_NONE ;
}
2009-06-23 05:41:15 +04:00
/* FIXME: need to handle the error codes */
2009-06-23 05:48:30 +04:00
debugf0 ( " DOD Max limits: DIMMS: %d, %d-ranked, %d-banked \n " ,
numdimms ( pvt - > info . max_dod ) ,
numrank ( pvt - > info . max_dod > > 2 ) ,
numbank ( pvt - > info . max_dod > > 4 ) ) ;
debugf0 ( " DOD Max rows x colums = 0x%x x 0x%x \n " ,
numrow ( pvt - > info . max_dod > > 6 ) ,
numcol ( pvt - > info . max_dod > > 9 ) ) ;
2009-06-23 05:41:15 +04:00
2009-06-23 05:48:29 +04:00
debugf0 ( " Memory channel configuration: \n " ) ;
for ( i = 0 ; i < NUM_CHANS ; i + + ) {
2009-06-23 05:48:30 +04:00
u32 data , dimm_dod [ 3 ] , value [ 8 ] ;
2009-06-23 05:48:29 +04:00
if ( ! CH_ACTIVE ( pvt , i ) ) {
debugf0 ( " Channel %i is not active \n " , i ) ;
continue ;
}
if ( CH_DISABLED ( pvt , i ) ) {
debugf0 ( " Channel %i is disabled \n " , i ) ;
continue ;
}
2009-06-23 05:48:29 +04:00
/* Devices 4-6 function 0 */
2009-07-15 13:56:23 +04:00
pci_read_config_dword ( pvt - > pci_ch [ socket ] [ i ] [ 0 ] ,
2009-06-23 05:48:29 +04:00
MC_CHANNEL_DIMM_INIT_PARAMS , & data ) ;
2009-07-15 13:56:23 +04:00
pvt - > channel [ socket ] [ i ] . ranks = ( data & QUAD_RANK_PRESENT ) ?
4 : 2 ;
2009-06-23 05:48:29 +04:00
2009-06-23 05:48:30 +04:00
if ( data & REGISTERED_DIMM )
mtype = MEM_RDDR3 ;
else
mtype = MEM_DDR3 ;
#if 0
2009-06-23 05:48:29 +04:00
if ( data & THREE_DIMMS_PRESENT )
pvt - > channel [ i ] . dimms = 3 ;
else if ( data & SINGLE_QUAD_RANK_PRESENT )
pvt - > channel [ i ] . dimms = 1 ;
else
pvt - > channel [ i ] . dimms = 2 ;
2009-06-23 05:48:30 +04:00
# endif
/* Devices 4-6 function 1 */
2009-07-15 13:56:23 +04:00
pci_read_config_dword ( pvt - > pci_ch [ socket ] [ i ] [ 1 ] ,
2009-06-23 05:48:30 +04:00
MC_DOD_CH_DIMM0 , & dimm_dod [ 0 ] ) ;
2009-07-15 13:56:23 +04:00
pci_read_config_dword ( pvt - > pci_ch [ socket ] [ i ] [ 1 ] ,
2009-06-23 05:48:30 +04:00
MC_DOD_CH_DIMM1 , & dimm_dod [ 1 ] ) ;
2009-07-15 13:56:23 +04:00
pci_read_config_dword ( pvt - > pci_ch [ socket ] [ i ] [ 1 ] ,
2009-06-23 05:48:30 +04:00
MC_DOD_CH_DIMM2 , & dimm_dod [ 2 ] ) ;
2009-06-23 05:48:29 +04:00
2009-06-23 05:48:30 +04:00
debugf0 ( " Ch%d phy rd%d, wr%d (0x%08x): "
2009-06-23 05:48:30 +04:00
" %d ranks, %cDIMMs \n " ,
2009-06-23 05:48:30 +04:00
i ,
RDLCH ( pvt - > info . ch_map , i ) , WRLCH ( pvt - > info . ch_map , i ) ,
data ,
2009-07-15 13:56:23 +04:00
pvt - > channel [ socket ] [ i ] . ranks ,
2009-06-23 05:48:31 +04:00
( data & REGISTERED_DIMM ) ? ' R ' : ' U ' ) ;
2009-06-23 05:48:30 +04:00
for ( j = 0 ; j < 3 ; j + + ) {
u32 banks , ranks , rows , cols ;
i7core_edac: Memory info fixes and preparation for properly filling cswrow data
Now, memory size is properly displayed:
EDAC i7core: DOD Max limits: DIMMS: 2, 1-ranked, 8-banked
EDAC i7core: DOD Max rows x colums = 0x4000 x 0x400
EDAC i7core: Memory channel configuration:
EDAC i7core: Ch0 phy rd0, wr0 (0x063f7c31): 2 ranks, UDIMMs
EDAC i7core: dimm 0 (0x00000288) 1024 Mb offset: 0, numbank: 8,
numrank: 1, numrow: 0x4000, numcol: 0x400
EDAC i7core: dimm 1 (0x00001288) 1024 Mb offset: 4, numbank: 8,
numrank: 1, numrow: 0x4000, numcol: 0x400
EDAC i7core: Ch1 phy rd1, wr1 (0x063f7c31): 2 ranks, UDIMMs
EDAC i7core: dimm 0 (0x00000288) 1024 Mb offset: 0, numbank: 8,
numrank: 1, numrow: 0x4000, numcol: 0x400
EDAC i7core: Ch2 phy rd3, wr3 (0x063f7c31): 2 ranks, UDIMMs
EDAC i7core: dimm 0 (0x00000288) 1024 Mb offset: 0, numbank: 8,
numrank: 1, numrow: 0x4000, numcol: 0x400
Still, as the way to retrieve csrows info is not known, it does a
mapping of what's available to csrows basic unit at edac core.
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
2009-06-23 05:48:31 +04:00
u32 size , npages ;
2009-06-23 05:48:30 +04:00
if ( ! DIMM_PRESENT ( dimm_dod [ j ] ) )
continue ;
banks = numbank ( MC_DOD_NUMBANK ( dimm_dod [ j ] ) ) ;
ranks = numrank ( MC_DOD_NUMRANK ( dimm_dod [ j ] ) ) ;
rows = numrow ( MC_DOD_NUMROW ( dimm_dod [ j ] ) ) ;
cols = numcol ( MC_DOD_NUMCOL ( dimm_dod [ j ] ) ) ;
i7core_edac: Memory info fixes and preparation for properly filling cswrow data
Now, memory size is properly displayed:
EDAC i7core: DOD Max limits: DIMMS: 2, 1-ranked, 8-banked
EDAC i7core: DOD Max rows x colums = 0x4000 x 0x400
EDAC i7core: Memory channel configuration:
EDAC i7core: Ch0 phy rd0, wr0 (0x063f7c31): 2 ranks, UDIMMs
EDAC i7core: dimm 0 (0x00000288) 1024 Mb offset: 0, numbank: 8,
numrank: 1, numrow: 0x4000, numcol: 0x400
EDAC i7core: dimm 1 (0x00001288) 1024 Mb offset: 4, numbank: 8,
numrank: 1, numrow: 0x4000, numcol: 0x400
EDAC i7core: Ch1 phy rd1, wr1 (0x063f7c31): 2 ranks, UDIMMs
EDAC i7core: dimm 0 (0x00000288) 1024 Mb offset: 0, numbank: 8,
numrank: 1, numrow: 0x4000, numcol: 0x400
EDAC i7core: Ch2 phy rd3, wr3 (0x063f7c31): 2 ranks, UDIMMs
EDAC i7core: dimm 0 (0x00000288) 1024 Mb offset: 0, numbank: 8,
numrank: 1, numrow: 0x4000, numcol: 0x400
Still, as the way to retrieve csrows info is not known, it does a
mapping of what's available to csrows basic unit at edac core.
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
2009-06-23 05:48:31 +04:00
/* DDR3 has 8 I/O banks */
size = ( rows * cols * banks * ranks ) > > ( 20 - 3 ) ;
2009-07-15 13:56:23 +04:00
pvt - > channel [ socket ] [ i ] . dimms + + ;
2009-06-23 05:48:30 +04:00
i7core_edac: Memory info fixes and preparation for properly filling cswrow data
Now, memory size is properly displayed:
EDAC i7core: DOD Max limits: DIMMS: 2, 1-ranked, 8-banked
EDAC i7core: DOD Max rows x colums = 0x4000 x 0x400
EDAC i7core: Memory channel configuration:
EDAC i7core: Ch0 phy rd0, wr0 (0x063f7c31): 2 ranks, UDIMMs
EDAC i7core: dimm 0 (0x00000288) 1024 Mb offset: 0, numbank: 8,
numrank: 1, numrow: 0x4000, numcol: 0x400
EDAC i7core: dimm 1 (0x00001288) 1024 Mb offset: 4, numbank: 8,
numrank: 1, numrow: 0x4000, numcol: 0x400
EDAC i7core: Ch1 phy rd1, wr1 (0x063f7c31): 2 ranks, UDIMMs
EDAC i7core: dimm 0 (0x00000288) 1024 Mb offset: 0, numbank: 8,
numrank: 1, numrow: 0x4000, numcol: 0x400
EDAC i7core: Ch2 phy rd3, wr3 (0x063f7c31): 2 ranks, UDIMMs
EDAC i7core: dimm 0 (0x00000288) 1024 Mb offset: 0, numbank: 8,
numrank: 1, numrow: 0x4000, numcol: 0x400
Still, as the way to retrieve csrows info is not known, it does a
mapping of what's available to csrows basic unit at edac core.
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
2009-06-23 05:48:31 +04:00
debugf0 ( " \t dimm %d (0x%08x) %d Mb offset: %x, "
" numbank: %d, \n \t \t "
" numrank: %d, numrow: %#x, numcol: %#x \n " ,
j , dimm_dod [ j ] , size ,
2009-06-23 05:48:30 +04:00
RANKOFFSET ( dimm_dod [ j ] ) ,
banks , ranks , rows , cols ) ;
2009-06-23 05:48:31 +04:00
# if PAGE_SHIFT > 20
npages = size > > ( PAGE_SHIFT - 20 ) ;
# else
npages = size < < ( 20 - PAGE_SHIFT ) ;
# endif
i7core_edac: Memory info fixes and preparation for properly filling cswrow data
Now, memory size is properly displayed:
EDAC i7core: DOD Max limits: DIMMS: 2, 1-ranked, 8-banked
EDAC i7core: DOD Max rows x colums = 0x4000 x 0x400
EDAC i7core: Memory channel configuration:
EDAC i7core: Ch0 phy rd0, wr0 (0x063f7c31): 2 ranks, UDIMMs
EDAC i7core: dimm 0 (0x00000288) 1024 Mb offset: 0, numbank: 8,
numrank: 1, numrow: 0x4000, numcol: 0x400
EDAC i7core: dimm 1 (0x00001288) 1024 Mb offset: 4, numbank: 8,
numrank: 1, numrow: 0x4000, numcol: 0x400
EDAC i7core: Ch1 phy rd1, wr1 (0x063f7c31): 2 ranks, UDIMMs
EDAC i7core: dimm 0 (0x00000288) 1024 Mb offset: 0, numbank: 8,
numrank: 1, numrow: 0x4000, numcol: 0x400
EDAC i7core: Ch2 phy rd3, wr3 (0x063f7c31): 2 ranks, UDIMMs
EDAC i7core: dimm 0 (0x00000288) 1024 Mb offset: 0, numbank: 8,
numrank: 1, numrow: 0x4000, numcol: 0x400
Still, as the way to retrieve csrows info is not known, it does a
mapping of what's available to csrows basic unit at edac core.
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
2009-06-23 05:48:31 +04:00
2009-07-15 16:02:32 +04:00
csr = & mci - > csrows [ * csrow ] ;
i7core_edac: Memory info fixes and preparation for properly filling cswrow data
Now, memory size is properly displayed:
EDAC i7core: DOD Max limits: DIMMS: 2, 1-ranked, 8-banked
EDAC i7core: DOD Max rows x colums = 0x4000 x 0x400
EDAC i7core: Memory channel configuration:
EDAC i7core: Ch0 phy rd0, wr0 (0x063f7c31): 2 ranks, UDIMMs
EDAC i7core: dimm 0 (0x00000288) 1024 Mb offset: 0, numbank: 8,
numrank: 1, numrow: 0x4000, numcol: 0x400
EDAC i7core: dimm 1 (0x00001288) 1024 Mb offset: 4, numbank: 8,
numrank: 1, numrow: 0x4000, numcol: 0x400
EDAC i7core: Ch1 phy rd1, wr1 (0x063f7c31): 2 ranks, UDIMMs
EDAC i7core: dimm 0 (0x00000288) 1024 Mb offset: 0, numbank: 8,
numrank: 1, numrow: 0x4000, numcol: 0x400
EDAC i7core: Ch2 phy rd3, wr3 (0x063f7c31): 2 ranks, UDIMMs
EDAC i7core: dimm 0 (0x00000288) 1024 Mb offset: 0, numbank: 8,
numrank: 1, numrow: 0x4000, numcol: 0x400
Still, as the way to retrieve csrows info is not known, it does a
mapping of what's available to csrows basic unit at edac core.
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
2009-06-23 05:48:31 +04:00
csr - > first_page = last_page + 1 ;
last_page + = npages ;
csr - > last_page = last_page ;
csr - > nr_pages = npages ;
2009-06-23 05:48:30 +04:00
csr - > page_mask = 0 ;
2009-06-23 05:48:31 +04:00
csr - > grain = 8 ;
2009-07-15 16:02:32 +04:00
csr - > csrow_idx = * csrow ;
2009-06-23 05:48:31 +04:00
csr - > nr_channels = 1 ;
csr - > channels [ 0 ] . chan_idx = i ;
csr - > channels [ 0 ] . ce_count = 0 ;
2009-06-23 05:48:30 +04:00
switch ( banks ) {
case 4 :
csr - > dtype = DEV_X4 ;
break ;
case 8 :
csr - > dtype = DEV_X8 ;
break ;
case 16 :
csr - > dtype = DEV_X16 ;
break ;
default :
csr - > dtype = DEV_UNKNOWN ;
}
csr - > edac_mode = mode ;
csr - > mtype = mtype ;
2009-07-15 16:02:32 +04:00
( * csrow ) + + ;
2009-06-23 05:48:30 +04:00
}
2009-06-23 05:48:30 +04:00
2009-06-23 05:48:30 +04:00
pci_read_config_dword ( pdev , MC_SAG_CH_0 , & value [ 0 ] ) ;
pci_read_config_dword ( pdev , MC_SAG_CH_1 , & value [ 1 ] ) ;
pci_read_config_dword ( pdev , MC_SAG_CH_2 , & value [ 2 ] ) ;
pci_read_config_dword ( pdev , MC_SAG_CH_3 , & value [ 3 ] ) ;
pci_read_config_dword ( pdev , MC_SAG_CH_4 , & value [ 4 ] ) ;
pci_read_config_dword ( pdev , MC_SAG_CH_5 , & value [ 5 ] ) ;
pci_read_config_dword ( pdev , MC_SAG_CH_6 , & value [ 6 ] ) ;
pci_read_config_dword ( pdev , MC_SAG_CH_7 , & value [ 7 ] ) ;
2009-06-23 05:48:31 +04:00
debugf0 ( " \t [%i] DIVBY3 \t REMOVED \t OFFSET \n " , i ) ;
2009-06-23 05:48:30 +04:00
for ( j = 0 ; j < 8 ; j + + )
2009-06-23 05:48:31 +04:00
debugf0 ( " \t \t %#x \t %#x \t %#x \n " ,
2009-06-23 05:48:30 +04:00
( value [ j ] > > 27 ) & 0x1 ,
( value [ j ] > > 24 ) & 0x7 ,
( value [ j ] & & ( ( 1 < < 24 ) - 1 ) ) ) ;
2009-06-23 05:48:29 +04:00
}
2009-06-23 05:41:15 +04:00
return 0 ;
}
2009-06-23 05:48:28 +04:00
/****************************************************************************
Error insertion routines
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* The i7core has independent error injection features per channel.
However , to have a simpler code , we don ' t allow enabling error injection
on more than one channel .
Also , since a change at an inject parameter will be applied only at enable ,
we ' re disabling error injection on all write calls to the sysfs nodes that
controls the error code injection .
*/
2009-06-23 05:48:29 +04:00
static int disable_inject ( struct mem_ctl_info * mci )
2009-06-23 05:48:28 +04:00
{
struct i7core_pvt * pvt = mci - > pvt_info ;
pvt - > inject . enable = 0 ;
2009-07-15 13:56:23 +04:00
if ( ! pvt - > pci_ch [ pvt - > inject . socket ] [ pvt - > inject . channel ] [ 0 ] )
2009-06-23 05:48:29 +04:00
return - ENODEV ;
2009-07-15 13:56:23 +04:00
pci_write_config_dword ( pvt - > pci_ch [ pvt - > inject . socket ] [ pvt - > inject . channel ] [ 0 ] ,
2009-06-23 05:48:28 +04:00
MC_CHANNEL_ERROR_MASK , 0 ) ;
2009-06-23 05:48:29 +04:00
return 0 ;
2009-06-23 05:48:28 +04:00
}
2009-07-15 13:56:23 +04:00
/*
* i7core inject inject . socket
*
* accept and store error injection inject . socket value
*/
static ssize_t i7core_inject_socket_store ( struct mem_ctl_info * mci ,
const char * data , size_t count )
{
struct i7core_pvt * pvt = mci - > pvt_info ;
unsigned long value ;
int rc ;
rc = strict_strtoul ( data , 10 , & value ) ;
if ( ( rc < 0 ) | | ( value > pvt - > sockets ) )
return 0 ;
pvt - > inject . section = ( u32 ) value ;
return count ;
}
static ssize_t i7core_inject_socket_show ( struct mem_ctl_info * mci ,
char * data )
{
struct i7core_pvt * pvt = mci - > pvt_info ;
return sprintf ( data , " %d \n " , pvt - > inject . socket ) ;
}
2009-06-23 05:48:28 +04:00
/*
* i7core inject inject . section
*
* accept and store error injection inject . section value
* bit 0 - refers to the lower 32 - byte half cacheline
* bit 1 - refers to the upper 32 - byte half cacheline
*/
static ssize_t i7core_inject_section_store ( struct mem_ctl_info * mci ,
const char * data , size_t count )
{
struct i7core_pvt * pvt = mci - > pvt_info ;
unsigned long value ;
int rc ;
if ( pvt - > inject . enable )
2009-06-23 05:48:31 +04:00
disable_inject ( mci ) ;
2009-06-23 05:48:28 +04:00
rc = strict_strtoul ( data , 10 , & value ) ;
if ( ( rc < 0 ) | | ( value > 3 ) )
return 0 ;
pvt - > inject . section = ( u32 ) value ;
return count ;
}
static ssize_t i7core_inject_section_show ( struct mem_ctl_info * mci ,
char * data )
{
struct i7core_pvt * pvt = mci - > pvt_info ;
return sprintf ( data , " 0x%08x \n " , pvt - > inject . section ) ;
}
/*
* i7core inject . type
*
* accept and store error injection inject . section value
* bit 0 - repeat enable - Enable error repetition
* bit 1 - inject ECC error
* bit 2 - inject parity error
*/
static ssize_t i7core_inject_type_store ( struct mem_ctl_info * mci ,
const char * data , size_t count )
{
struct i7core_pvt * pvt = mci - > pvt_info ;
unsigned long value ;
int rc ;
if ( pvt - > inject . enable )
2009-06-23 05:48:31 +04:00
disable_inject ( mci ) ;
2009-06-23 05:48:28 +04:00
rc = strict_strtoul ( data , 10 , & value ) ;
if ( ( rc < 0 ) | | ( value > 7 ) )
return 0 ;
pvt - > inject . type = ( u32 ) value ;
return count ;
}
static ssize_t i7core_inject_type_show ( struct mem_ctl_info * mci ,
char * data )
{
struct i7core_pvt * pvt = mci - > pvt_info ;
return sprintf ( data , " 0x%08x \n " , pvt - > inject . type ) ;
}
/*
* i7core_inject_inject . eccmask_store
*
* The type of error ( UE / CE ) will depend on the inject . eccmask value :
* Any bits set to a 1 will flip the corresponding ECC bit
* Correctable errors can be injected by flipping 1 bit or the bits within
* a symbol pair ( 2 consecutive aligned 8 - bit pairs - i . e . 7 : 0 and 15 : 8 or
* 23 : 16 and 31 : 24 ) . Flipping bits in two symbol pairs will cause an
* uncorrectable error to be injected .
*/
static ssize_t i7core_inject_eccmask_store ( struct mem_ctl_info * mci ,
const char * data , size_t count )
{
struct i7core_pvt * pvt = mci - > pvt_info ;
unsigned long value ;
int rc ;
if ( pvt - > inject . enable )
2009-06-23 05:48:31 +04:00
disable_inject ( mci ) ;
2009-06-23 05:48:28 +04:00
rc = strict_strtoul ( data , 10 , & value ) ;
if ( rc < 0 )
return 0 ;
pvt - > inject . eccmask = ( u32 ) value ;
return count ;
}
static ssize_t i7core_inject_eccmask_show ( struct mem_ctl_info * mci ,
char * data )
{
struct i7core_pvt * pvt = mci - > pvt_info ;
return sprintf ( data , " 0x%08x \n " , pvt - > inject . eccmask ) ;
}
/*
* i7core_addrmatch
*
* The type of error ( UE / CE ) will depend on the inject . eccmask value :
* Any bits set to a 1 will flip the corresponding ECC bit
* Correctable errors can be injected by flipping 1 bit or the bits within
* a symbol pair ( 2 consecutive aligned 8 - bit pairs - i . e . 7 : 0 and 15 : 8 or
* 23 : 16 and 31 : 24 ) . Flipping bits in two symbol pairs will cause an
* uncorrectable error to be injected .
*/
static ssize_t i7core_inject_addrmatch_store ( struct mem_ctl_info * mci ,
const char * data , size_t count )
{
struct i7core_pvt * pvt = mci - > pvt_info ;
char * cmd , * val ;
long value ;
int rc ;
if ( pvt - > inject . enable )
2009-06-23 05:48:31 +04:00
disable_inject ( mci ) ;
2009-06-23 05:48:28 +04:00
do {
cmd = strsep ( ( char * * ) & data , " : " ) ;
if ( ! cmd )
break ;
val = strsep ( ( char * * ) & data , " \n \t " ) ;
if ( ! val )
return cmd - data ;
2009-06-23 05:48:31 +04:00
if ( ! strcasecmp ( val , " any " ) )
2009-06-23 05:48:28 +04:00
value = - 1 ;
else {
rc = strict_strtol ( val , 10 , & value ) ;
if ( ( rc < 0 ) | | ( value < 0 ) )
return cmd - data ;
}
2009-06-23 05:48:31 +04:00
if ( ! strcasecmp ( cmd , " channel " ) ) {
2009-06-23 05:48:28 +04:00
if ( value < 3 )
pvt - > inject . channel = value ;
else
return cmd - data ;
2009-06-23 05:48:31 +04:00
} else if ( ! strcasecmp ( cmd , " dimm " ) ) {
2009-06-23 05:48:28 +04:00
if ( value < 4 )
pvt - > inject . dimm = value ;
else
return cmd - data ;
2009-06-23 05:48:31 +04:00
} else if ( ! strcasecmp ( cmd , " rank " ) ) {
2009-06-23 05:48:28 +04:00
if ( value < 4 )
pvt - > inject . rank = value ;
else
return cmd - data ;
2009-06-23 05:48:31 +04:00
} else if ( ! strcasecmp ( cmd , " bank " ) ) {
2009-06-23 05:48:28 +04:00
if ( value < 4 )
pvt - > inject . bank = value ;
else
return cmd - data ;
2009-06-23 05:48:31 +04:00
} else if ( ! strcasecmp ( cmd , " page " ) ) {
2009-06-23 05:48:28 +04:00
if ( value < = 0xffff )
pvt - > inject . page = value ;
else
return cmd - data ;
2009-06-23 05:48:31 +04:00
} else if ( ! strcasecmp ( cmd , " col " ) | |
! strcasecmp ( cmd , " column " ) ) {
2009-06-23 05:48:28 +04:00
if ( value < = 0x3fff )
pvt - > inject . col = value ;
else
return cmd - data ;
}
} while ( 1 ) ;
return count ;
}
static ssize_t i7core_inject_addrmatch_show ( struct mem_ctl_info * mci ,
char * data )
{
struct i7core_pvt * pvt = mci - > pvt_info ;
char channel [ 4 ] , dimm [ 4 ] , bank [ 4 ] , rank [ 4 ] , page [ 7 ] , col [ 7 ] ;
if ( pvt - > inject . channel < 0 )
sprintf ( channel , " any " ) ;
else
sprintf ( channel , " %d " , pvt - > inject . channel ) ;
if ( pvt - > inject . dimm < 0 )
sprintf ( dimm , " any " ) ;
else
sprintf ( dimm , " %d " , pvt - > inject . dimm ) ;
if ( pvt - > inject . bank < 0 )
sprintf ( bank , " any " ) ;
else
sprintf ( bank , " %d " , pvt - > inject . bank ) ;
if ( pvt - > inject . rank < 0 )
sprintf ( rank , " any " ) ;
else
sprintf ( rank , " %d " , pvt - > inject . rank ) ;
if ( pvt - > inject . page < 0 )
sprintf ( page , " any " ) ;
else
sprintf ( page , " 0x%04x " , pvt - > inject . page ) ;
if ( pvt - > inject . col < 0 )
sprintf ( col , " any " ) ;
else
sprintf ( col , " 0x%04x " , pvt - > inject . col ) ;
return sprintf ( data , " channel: %s \n dimm: %s \n bank: %s \n "
" rank: %s \n page: %s \n column: %s \n " ,
channel , dimm , bank , rank , page , col ) ;
}
/*
* This routine prepares the Memory Controller for error injection .
* The error will be injected when some process tries to write to the
* memory that matches the given criteria .
* The criteria can be set in terms of a mask where dimm , rank , bank , page
* and col can be specified .
* A - 1 value for any of the mask items will make the MCU to ignore
* that matching criteria for error injection .
*
* It should be noticed that the error will only happen after a write operation
* on a memory that matches the condition . if REPEAT_EN is not enabled at
* inject mask , then it will produce just one error . Otherwise , it will repeat
* until the injectmask would be cleaned .
*
* FIXME : This routine assumes that MAXNUMDIMMS value of MC_MAX_DOD
* is reliable enough to check if the MC is using the
* three channels . However , this is not clear at the datasheet .
*/
static ssize_t i7core_inject_enable_store ( struct mem_ctl_info * mci ,
const char * data , size_t count )
{
struct i7core_pvt * pvt = mci - > pvt_info ;
u32 injectmask ;
u64 mask = 0 ;
int rc ;
long enable ;
2009-07-15 13:56:23 +04:00
if ( ! pvt - > pci_ch [ pvt - > inject . socket ] [ pvt - > inject . channel ] [ 0 ] )
2009-06-23 05:48:29 +04:00
return 0 ;
2009-06-23 05:48:28 +04:00
rc = strict_strtoul ( data , 10 , & enable ) ;
if ( ( rc < 0 ) )
return 0 ;
if ( enable ) {
pvt - > inject . enable = 1 ;
} else {
disable_inject ( mci ) ;
return count ;
}
/* Sets pvt->inject.dimm mask */
if ( pvt - > inject . dimm < 0 )
2009-06-23 05:48:29 +04:00
mask | = 1L < < 41 ;
2009-06-23 05:48:28 +04:00
else {
2009-07-15 13:56:23 +04:00
if ( pvt - > channel [ pvt - > inject . socket ] [ pvt - > inject . channel ] . dimms > 2 )
2009-06-23 05:48:29 +04:00
mask | = ( pvt - > inject . dimm & 0x3L ) < < 35 ;
2009-06-23 05:48:28 +04:00
else
2009-06-23 05:48:29 +04:00
mask | = ( pvt - > inject . dimm & 0x1L ) < < 36 ;
2009-06-23 05:48:28 +04:00
}
/* Sets pvt->inject.rank mask */
if ( pvt - > inject . rank < 0 )
2009-06-23 05:48:29 +04:00
mask | = 1L < < 40 ;
2009-06-23 05:48:28 +04:00
else {
2009-07-15 13:56:23 +04:00
if ( pvt - > channel [ pvt - > inject . socket ] [ pvt - > inject . channel ] . dimms > 2 )
2009-06-23 05:48:29 +04:00
mask | = ( pvt - > inject . rank & 0x1L ) < < 34 ;
2009-06-23 05:48:28 +04:00
else
2009-06-23 05:48:29 +04:00
mask | = ( pvt - > inject . rank & 0x3L ) < < 34 ;
2009-06-23 05:48:28 +04:00
}
/* Sets pvt->inject.bank mask */
if ( pvt - > inject . bank < 0 )
2009-06-23 05:48:29 +04:00
mask | = 1L < < 39 ;
2009-06-23 05:48:28 +04:00
else
2009-06-23 05:48:29 +04:00
mask | = ( pvt - > inject . bank & 0x15L ) < < 30 ;
2009-06-23 05:48:28 +04:00
/* Sets pvt->inject.page mask */
if ( pvt - > inject . page < 0 )
2009-06-23 05:48:29 +04:00
mask | = 1L < < 38 ;
2009-06-23 05:48:28 +04:00
else
2009-06-23 05:48:29 +04:00
mask | = ( pvt - > inject . page & 0xffffL ) < < 14 ;
2009-06-23 05:48:28 +04:00
/* Sets pvt->inject.column mask */
if ( pvt - > inject . col < 0 )
2009-06-23 05:48:29 +04:00
mask | = 1L < < 37 ;
2009-06-23 05:48:28 +04:00
else
2009-06-23 05:48:29 +04:00
mask | = ( pvt - > inject . col & 0x3fffL ) ;
2009-06-23 05:48:28 +04:00
2009-07-10 05:14:35 +04:00
/* Unlock writes to registers */
2009-07-15 13:56:23 +04:00
pci_write_config_dword ( pvt - > pci_noncore [ pvt - > inject . socket ] ,
MC_CFG_CONTROL , 0x2 ) ;
2009-07-10 05:14:35 +04:00
msleep ( 100 ) ;
/* Zeroes error count registers */
2009-07-15 13:56:23 +04:00
pci_write_config_dword ( pvt - > pci_mcr [ pvt - > inject . socket ] [ 4 ] ,
MC_TEST_ERR_RCV1 , 0 ) ;
pci_write_config_dword ( pvt - > pci_mcr [ pvt - > inject . socket ] [ 4 ] ,
MC_TEST_ERR_RCV0 , 0 ) ;
pvt - > ce_count_available [ pvt - > inject . socket ] = 0 ;
2009-07-10 05:14:35 +04:00
2009-06-23 05:48:29 +04:00
# if USE_QWORD
2009-07-15 13:56:23 +04:00
pci_write_config_qword ( pvt - > pci_ch [ pvt - > inject . socket ] [ pvt - > inject . channel ] [ 0 ] ,
2009-06-23 05:48:28 +04:00
MC_CHANNEL_ADDR_MATCH , mask ) ;
2009-06-23 05:48:29 +04:00
# else
2009-07-15 13:56:23 +04:00
pci_write_config_dword ( pvt - > pci_ch [ pvt - > inject . socket ] [ pvt - > inject . channel ] [ 0 ] ,
2009-06-23 05:48:29 +04:00
MC_CHANNEL_ADDR_MATCH , mask ) ;
2009-07-15 13:56:23 +04:00
pci_write_config_dword ( pvt - > pci_ch [ pvt - > inject . socket ] [ pvt - > inject . channel ] [ 0 ] ,
2009-06-23 05:48:29 +04:00
MC_CHANNEL_ADDR_MATCH + 4 , mask > > 32L ) ;
# endif
# if 1
# if USE_QWORD
u64 rdmask ;
2009-07-15 13:56:23 +04:00
pci_read_config_qword ( pvt - > pci_ch [ pvt - > inject . socket ] [ pvt - > inject . channel ] [ 0 ] ,
2009-06-23 05:48:29 +04:00
MC_CHANNEL_ADDR_MATCH , & rdmask ) ;
debugf0 ( " Inject addr match write 0x%016llx, read: 0x%016llx \n " ,
mask , rdmask ) ;
# else
u32 rdmask1 , rdmask2 ;
2009-07-15 13:56:23 +04:00
pci_read_config_dword ( pvt - > pci_ch [ pvt - > inject . socket ] [ pvt - > inject . channel ] [ 0 ] ,
2009-06-23 05:48:29 +04:00
MC_CHANNEL_ADDR_MATCH , & rdmask1 ) ;
2009-07-15 13:56:23 +04:00
pci_read_config_dword ( pvt - > pci_ch [ pvt - > inject . socket ] [ pvt - > inject . channel ] [ 0 ] ,
2009-06-23 05:48:29 +04:00
MC_CHANNEL_ADDR_MATCH + 4 , & rdmask2 ) ;
2009-07-10 05:06:41 +04:00
debugf0 ( " Inject addr match write 0x%016llx, read: 0x%08x 0x%08x \n " ,
2009-06-23 05:48:29 +04:00
mask , rdmask1 , rdmask2 ) ;
# endif
# endif
2009-06-23 05:48:28 +04:00
2009-07-15 13:56:23 +04:00
pci_write_config_dword ( pvt - > pci_ch [ pvt - > inject . socket ] [ pvt - > inject . channel ] [ 0 ] ,
2009-06-23 05:48:28 +04:00
MC_CHANNEL_ERROR_MASK , pvt - > inject . eccmask ) ;
/*
* bit 0 : REPEAT_EN
* bits 1 - 2 : MASK_HALF_CACHELINE
* bit 3 : INJECT_ECC
* bit 4 : INJECT_ADDR_PARITY
*/
2009-06-23 05:48:29 +04:00
injectmask = ( pvt - > inject . type & 1 ) |
( pvt - > inject . section & 0x3 ) < < 1 |
2009-06-23 05:48:28 +04:00
( pvt - > inject . type & 0x6 ) < < ( 3 - 1 ) ;
2009-07-15 13:56:23 +04:00
pci_write_config_dword ( pvt - > pci_ch [ pvt - > inject . socket ] [ pvt - > inject . channel ] [ 0 ] ,
2009-06-23 05:48:28 +04:00
MC_CHANNEL_ERROR_MASK , injectmask ) ;
2009-07-10 05:14:35 +04:00
#if 0
/* lock writes to registers */
pci_write_config_dword ( pvt - > pci_noncore , MC_CFG_CONTROL , 0 ) ;
# endif
2009-06-23 05:48:31 +04:00
debugf0 ( " Error inject addr match 0x%016llx, ecc 0x%08x, "
" inject 0x%08x \n " ,
2009-06-23 05:48:28 +04:00
mask , pvt - > inject . eccmask , injectmask ) ;
2009-06-23 05:48:29 +04:00
2009-06-23 05:48:28 +04:00
return count ;
}
static ssize_t i7core_inject_enable_show ( struct mem_ctl_info * mci ,
char * data )
{
struct i7core_pvt * pvt = mci - > pvt_info ;
2009-06-23 05:48:29 +04:00
u32 injectmask ;
2009-07-15 13:56:23 +04:00
pci_read_config_dword ( pvt - > pci_ch [ pvt - > inject . socket ] [ pvt - > inject . channel ] [ 0 ] ,
2009-06-23 05:48:29 +04:00
MC_CHANNEL_ERROR_MASK , & injectmask ) ;
debugf0 ( " Inject error read: 0x%018x \n " , injectmask ) ;
if ( injectmask & 0x0c )
pvt - > inject . enable = 1 ;
2009-06-23 05:48:28 +04:00
return sprintf ( data , " %d \n " , pvt - > inject . enable ) ;
}
2009-06-23 05:48:29 +04:00
static ssize_t i7core_ce_regs_show ( struct mem_ctl_info * mci , char * data )
{
2009-07-15 13:56:23 +04:00
unsigned i , count , total = 0 ;
2009-06-23 05:48:29 +04:00
struct i7core_pvt * pvt = mci - > pvt_info ;
2009-07-15 13:56:23 +04:00
for ( i = 0 ; i < pvt - > sockets ; i + + ) {
if ( ! pvt - > ce_count_available [ i ] )
count = sprintf ( data , " socket 0 data unavailable \n " ) ;
else
count = sprintf ( data , " socket %d, dimm0: %lu \n "
" dimm1: %lu \n dimm2: %lu \n " ,
i ,
pvt - > ce_count [ i ] [ 0 ] ,
pvt - > ce_count [ i ] [ 1 ] ,
pvt - > ce_count [ i ] [ 2 ] ) ;
data + = count ;
total + = count ;
}
2009-06-23 05:48:29 +04:00
2009-07-15 13:56:23 +04:00
return total ;
2009-06-23 05:48:29 +04:00
}
2009-06-23 05:48:28 +04:00
/*
* Sysfs struct
*/
static struct mcidev_sysfs_attribute i7core_inj_attrs [ ] = {
{
2009-07-15 13:56:23 +04:00
. attr = {
. name = " inject_socket " ,
. mode = ( S_IRUGO | S_IWUSR )
} ,
. show = i7core_inject_socket_show ,
. store = i7core_inject_socket_store ,
} , {
2009-06-23 05:48:28 +04:00
. attr = {
. name = " inject_section " ,
. mode = ( S_IRUGO | S_IWUSR )
} ,
. show = i7core_inject_section_show ,
. store = i7core_inject_section_store ,
} , {
. attr = {
. name = " inject_type " ,
. mode = ( S_IRUGO | S_IWUSR )
} ,
. show = i7core_inject_type_show ,
. store = i7core_inject_type_store ,
} , {
. attr = {
. name = " inject_eccmask " ,
. mode = ( S_IRUGO | S_IWUSR )
} ,
. show = i7core_inject_eccmask_show ,
. store = i7core_inject_eccmask_store ,
} , {
. attr = {
. name = " inject_addrmatch " ,
. mode = ( S_IRUGO | S_IWUSR )
} ,
. show = i7core_inject_addrmatch_show ,
. store = i7core_inject_addrmatch_store ,
} , {
. attr = {
. name = " inject_enable " ,
. mode = ( S_IRUGO | S_IWUSR )
} ,
. show = i7core_inject_enable_show ,
. store = i7core_inject_enable_store ,
2009-06-23 05:48:29 +04:00
} , {
. attr = {
. name = " corrected_error_counts " ,
. mode = ( S_IRUGO | S_IWUSR )
} ,
. show = i7core_ce_regs_show ,
. store = NULL ,
2009-06-23 05:48:28 +04:00
} ,
} ;
2009-06-23 05:41:15 +04:00
/****************************************************************************
Device initialization routines : put / get , init / exit
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*
* i7core_put_devices ' put ' all the devices that we have
* reserved via ' get '
*/
2009-06-23 05:48:29 +04:00
static void i7core_put_devices ( void )
2009-06-23 05:41:15 +04:00
{
2009-07-15 13:56:23 +04:00
int i , j ;
2009-06-23 05:41:15 +04:00
2009-07-15 13:56:23 +04:00
for ( i = 0 ; i < NUM_SOCKETS ; i + + )
for ( j = 0 ; j < N_DEVS ; j + + )
pci_dev_put ( pci_devs [ j ] . pdev [ i ] ) ;
2009-06-23 05:41:15 +04:00
}
/*
* i7core_get_devices Find and perform ' get ' operation on the MCH ' s
* device / functions we want to reference for this driver
*
* Need to ' get ' device 16 func 1 and func 2
*/
2009-06-23 05:48:30 +04:00
static int i7core_get_devices ( void )
2009-06-23 05:41:15 +04:00
{
2009-06-23 05:48:30 +04:00
int rc , i ;
2009-06-23 05:48:29 +04:00
struct pci_dev * pdev = NULL ;
2009-07-15 13:56:23 +04:00
u8 bus = 0 ;
u8 socket = 0 ;
2009-06-23 05:41:15 +04:00
2009-06-23 05:48:29 +04:00
for ( i = 0 ; i < N_DEVS ; i + + ) {
pdev = pci_get_device ( PCI_VENDOR_ID_INTEL ,
pci_devs [ i ] . dev_id , NULL ) ;
2009-07-11 01:39:53 +04:00
if ( ! pdev & & ! i ) {
pcibios_scan_specific_bus ( 254 ) ;
pcibios_scan_specific_bus ( 255 ) ;
pdev = pci_get_device ( PCI_VENDOR_ID_INTEL ,
pci_devs [ i ] . dev_id , NULL ) ;
}
2009-07-15 13:56:23 +04:00
if ( likely ( pdev ) ) {
bus = pdev - > bus - > number ;
if ( bus = = 0x3f )
socket = 0 ;
else
socket = 255 - bus ;
if ( socket > = NUM_SOCKETS ) {
i7core_printk ( KERN_ERR ,
" Found unexpected socket for "
" dev %02x:%02x.%d PCI ID %04x:%04x \n " ,
bus , pci_devs [ i ] . dev , pci_devs [ i ] . func ,
PCI_VENDOR_ID_INTEL , pci_devs [ i ] . dev_id ) ;
rc = - ENODEV ;
goto error ;
}
pci_devs [ i ] . pdev [ socket ] = pdev ;
} else {
2009-06-23 05:48:29 +04:00
i7core_printk ( KERN_ERR ,
2009-07-15 13:56:23 +04:00
" Device not found: "
" dev %02x:%02x.%d PCI ID %04x:%04x \n " ,
bus , pci_devs [ i ] . dev , pci_devs [ i ] . func ,
PCI_VENDOR_ID_INTEL , pci_devs [ i ] . dev_id ) ;
2009-06-23 05:48:30 +04:00
/* Dev 3 function 2 only exists on chips with RDIMMs */
2009-06-23 05:48:29 +04:00
if ( ( pci_devs [ i ] . dev = = 3 ) & & ( pci_devs [ i ] . func = = 2 ) )
2009-06-23 05:48:30 +04:00
continue ;
/* End of list, leave */
rc = - ENODEV ;
goto error ;
2009-06-23 05:48:29 +04:00
}
2009-06-23 05:48:30 +04:00
/* Sanity check */
if ( unlikely ( PCI_SLOT ( pdev - > devfn ) ! = pci_devs [ i ] . dev | |
PCI_FUNC ( pdev - > devfn ) ! = pci_devs [ i ] . func ) ) {
2009-06-23 05:48:29 +04:00
i7core_printk ( KERN_ERR ,
2009-06-23 05:48:30 +04:00
" Device PCI ID %04x:%04x "
" has fn %d.%d instead of fn %d.%d \n " ,
2009-06-23 05:48:29 +04:00
PCI_VENDOR_ID_INTEL , pci_devs [ i ] . dev_id ,
2009-06-23 05:48:30 +04:00
PCI_SLOT ( pdev - > devfn ) , PCI_FUNC ( pdev - > devfn ) ,
2009-06-23 05:48:29 +04:00
pci_devs [ i ] . dev , pci_devs [ i ] . func ) ;
2009-06-23 05:48:30 +04:00
rc = - EINVAL ;
goto error ;
2009-06-23 05:48:29 +04:00
}
2009-06-23 05:48:30 +04:00
/* Be sure that the device is enabled */
rc = pci_enable_device ( pdev ) ;
if ( unlikely ( rc < 0 ) ) {
2009-06-23 05:48:29 +04:00
i7core_printk ( KERN_ERR ,
2009-06-23 05:48:30 +04:00
" Couldn't enable PCI ID %04x:%04x "
" fn %d.%d \n " ,
2009-06-23 05:48:29 +04:00
PCI_VENDOR_ID_INTEL , pci_devs [ i ] . dev_id ,
2009-06-23 05:48:30 +04:00
PCI_SLOT ( pdev - > devfn ) , PCI_FUNC ( pdev - > devfn ) ) ;
goto error ;
2009-06-23 05:48:29 +04:00
}
2009-06-23 05:41:15 +04:00
2009-06-23 05:48:29 +04:00
i7core_printk ( KERN_INFO ,
2009-07-15 13:56:23 +04:00
" Registered socket %d "
" dev %02x:%02x.%d PCI ID %04x:%04x \n " ,
socket , bus , pci_devs [ i ] . dev , pci_devs [ i ] . func ,
PCI_VENDOR_ID_INTEL , pci_devs [ i ] . dev_id ) ;
2009-06-23 05:48:30 +04:00
}
return 0 ;
error :
i7core_put_devices ( ) ;
return - EINVAL ;
}
static int mci_bind_devs ( struct mem_ctl_info * mci )
{
struct i7core_pvt * pvt = mci - > pvt_info ;
struct pci_dev * pdev ;
2009-07-15 13:56:23 +04:00
int i , j , func , slot ;
2009-06-23 05:48:30 +04:00
2009-07-15 13:56:23 +04:00
for ( i = 0 ; i < pvt - > sockets ; i + + ) {
for ( j = 0 ; j < N_DEVS ; j + + ) {
pdev = pci_devs [ j ] . pdev [ i ] ;
if ( ! pdev )
continue ;
2009-06-23 05:48:29 +04:00
2009-07-15 13:56:23 +04:00
func = PCI_FUNC ( pdev - > devfn ) ;
slot = PCI_SLOT ( pdev - > devfn ) ;
if ( slot = = 3 ) {
if ( unlikely ( func > MAX_MCR_FUNC ) )
goto error ;
pvt - > pci_mcr [ i ] [ func ] = pdev ;
} else if ( likely ( slot > = 4 & & slot < 4 + NUM_CHANS ) ) {
if ( unlikely ( func > MAX_CHAN_FUNC ) )
goto error ;
pvt - > pci_ch [ i ] [ slot - 4 ] [ func ] = pdev ;
} else if ( ! slot & & ! func )
pvt - > pci_noncore [ i ] = pdev ;
else
2009-06-23 05:48:30 +04:00
goto error ;
2009-07-15 13:56:23 +04:00
debugf0 ( " Associated fn %d.%d, dev = %p, socket %d \n " ,
PCI_SLOT ( pdev - > devfn ) , PCI_FUNC ( pdev - > devfn ) ,
pdev , i ) ;
}
2009-06-23 05:41:15 +04:00
}
2009-07-10 05:14:35 +04:00
2009-06-23 05:41:15 +04:00
return 0 ;
2009-06-23 05:48:30 +04:00
error :
i7core_printk ( KERN_ERR , " Device %d, function %d "
" is out of the expected range \n " ,
slot , func ) ;
return - EINVAL ;
2009-06-23 05:41:15 +04:00
}
2009-06-23 05:48:29 +04:00
/****************************************************************************
Error check routines
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* This function is based on the device 3 function 4 registers as described on:
* Intel Xeon Processor 5500 Series Datasheet Volume 2
* http : //www.intel.com/Assets/PDF/datasheet/321322.pdf
* also available at :
* http : //www.arrownac.com/manufacturers/intel/s/nehalem/5500-datasheet-v2.pdf
*/
2009-07-15 13:56:23 +04:00
static void check_mc_test_err ( struct mem_ctl_info * mci , u8 socket )
2009-06-23 05:48:29 +04:00
{
struct i7core_pvt * pvt = mci - > pvt_info ;
u32 rcv1 , rcv0 ;
int new0 , new1 , new2 ;
2009-07-15 13:56:23 +04:00
if ( ! pvt - > pci_mcr [ socket ] [ 4 ] ) {
2009-06-23 05:48:29 +04:00
debugf0 ( " %s MCR registers not found \n " , __func__ ) ;
return ;
}
/* Corrected error reads */
2009-07-15 13:56:23 +04:00
pci_read_config_dword ( pvt - > pci_mcr [ socket ] [ 4 ] , MC_TEST_ERR_RCV1 , & rcv1 ) ;
pci_read_config_dword ( pvt - > pci_mcr [ socket ] [ 4 ] , MC_TEST_ERR_RCV0 , & rcv0 ) ;
2009-06-23 05:48:29 +04:00
/* Store the new values */
new2 = DIMM2_COR_ERR ( rcv1 ) ;
new1 = DIMM1_COR_ERR ( rcv0 ) ;
new0 = DIMM0_COR_ERR ( rcv0 ) ;
2009-07-10 05:06:41 +04:00
#if 0
2009-06-23 05:48:29 +04:00
debugf2 ( " %s CE rcv1=0x%08x rcv0=0x%08x, %d %d %d \n " ,
( pvt - > ce_count_available ? " UPDATE " : " READ " ) ,
rcv1 , rcv0 , new0 , new1 , new2 ) ;
2009-07-10 05:06:41 +04:00
# endif
2009-06-23 05:48:29 +04:00
/* Updates CE counters if it is not the first time here */
2009-07-15 13:56:23 +04:00
if ( pvt - > ce_count_available [ socket ] ) {
2009-06-23 05:48:29 +04:00
/* Updates CE counters */
int add0 , add1 , add2 ;
2009-07-15 13:56:23 +04:00
add2 = new2 - pvt - > last_ce_count [ socket ] [ 2 ] ;
add1 = new1 - pvt - > last_ce_count [ socket ] [ 1 ] ;
add0 = new0 - pvt - > last_ce_count [ socket ] [ 0 ] ;
2009-06-23 05:48:29 +04:00
if ( add2 < 0 )
add2 + = 0x7fff ;
2009-07-15 13:56:23 +04:00
pvt - > ce_count [ socket ] [ 2 ] + = add2 ;
2009-06-23 05:48:29 +04:00
if ( add1 < 0 )
add1 + = 0x7fff ;
2009-07-15 13:56:23 +04:00
pvt - > ce_count [ socket ] [ 1 ] + = add1 ;
2009-06-23 05:48:29 +04:00
if ( add0 < 0 )
add0 + = 0x7fff ;
2009-07-15 13:56:23 +04:00
pvt - > ce_count [ socket ] [ 0 ] + = add0 ;
2009-06-23 05:48:29 +04:00
} else
2009-07-15 13:56:23 +04:00
pvt - > ce_count_available [ socket ] = 1 ;
2009-06-23 05:48:29 +04:00
/* Store the new values */
2009-07-15 13:56:23 +04:00
pvt - > last_ce_count [ socket ] [ 2 ] = new2 ;
pvt - > last_ce_count [ socket ] [ 1 ] = new1 ;
pvt - > last_ce_count [ socket ] [ 0 ] = new0 ;
2009-06-23 05:48:29 +04:00
}
2009-07-16 02:01:08 +04:00
/*
* According with tables E - 11 and E - 12 of chapter E .3 .3 of Intel 64 and IA - 32
* Architectures Software Developer ’ s Manual Volume 3 B .
2009-07-16 02:53:24 +04:00
* Nehalem are defined as family 0x06 , model 0x1a
*
* The MCA registers used here are the following ones :
2009-07-16 02:01:08 +04:00
* struct mce field MCA Register
2009-07-16 02:53:24 +04:00
* m - > status MSR_IA32_MC8_STATUS
* m - > addr MSR_IA32_MC8_ADDR
* m - > misc MSR_IA32_MC8_MISC
2009-07-16 02:01:08 +04:00
* In the case of Nehalem , the error information is masked at . status and . misc
* fields
*/
2009-07-10 05:06:41 +04:00
static void i7core_mce_output_error ( struct mem_ctl_info * mci ,
struct mce * m )
{
2009-07-16 02:01:08 +04:00
char * type = " NON-FATAL " ;
char * err , * msg ;
unsigned long error = m - > status & 0x1ff0000l ;
u32 core_err_cnt = ( m - > status > > 38 ) & & 0x7fff ;
u32 dimm = ( m - > misc > > 16 ) & 0x3 ;
u32 channel = ( m - > misc > > 18 ) & 0x3 ;
u32 syndrome = m - > misc > > 32 ;
u32 errnum = find_first_bit ( & error , 32 ) ;
switch ( errnum ) {
case 16 :
err = " read ECC error " ;
break ;
case 17 :
err = " RAS ECC error " ;
break ;
case 18 :
err = " write parity error " ;
break ;
case 19 :
err = " redundacy loss " ;
break ;
case 20 :
err = " reserved " ;
break ;
case 21 :
err = " memory range error " ;
break ;
case 22 :
err = " RTID out of range " ;
break ;
case 23 :
err = " address parity error " ;
break ;
case 24 :
err = " byte enable parity error " ;
break ;
default :
err = " unknown " ;
2009-07-10 05:06:41 +04:00
}
2009-07-16 02:53:24 +04:00
/* FIXME: should convert addr into bank and rank information */
2009-07-16 02:01:08 +04:00
msg = kasprintf ( GFP_ATOMIC ,
2009-07-16 02:53:24 +04:00
" %s (addr = 0x%08llx Dimm=%d, Channel=%d, "
" syndrome=0x%08x, count=%d Err=%d (%s)) \n " ,
type , ( long long ) m - > addr , dimm , channel ,
2009-07-16 02:01:08 +04:00
syndrome , core_err_cnt , errnum , err ) ;
debugf0 ( " %s " , msg ) ;
2009-07-10 05:06:41 +04:00
/* Call the helper to output message */
2009-07-16 02:01:08 +04:00
edac_mc_handle_fbd_ue ( mci , 0 /* FIXME: should be rank here */ ,
0 , 0 /* FIXME: should be channel here */ , msg ) ;
kfree ( msg ) ;
2009-07-10 05:06:41 +04:00
}
2009-06-23 05:48:29 +04:00
/*
* i7core_check_error Retrieve and process errors reported by the
* hardware . Called by the Core module .
*/
static void i7core_check_error ( struct mem_ctl_info * mci )
{
2009-07-10 05:06:41 +04:00
struct i7core_pvt * pvt = mci - > pvt_info ;
int i ;
unsigned count = 0 ;
struct mce * m = NULL ;
unsigned long flags ;
debugf0 ( __FILE__ " : %s() \n " , __func__ ) ;
/* Copy all mce errors into a temporary buffer */
spin_lock_irqsave ( & pvt - > mce_lock , flags ) ;
if ( pvt - > mce_count ) {
m = kmalloc ( sizeof ( * m ) * pvt - > mce_count , GFP_ATOMIC ) ;
if ( m ) {
count = pvt - > mce_count ;
memcpy ( m , & pvt - > mce_entry , sizeof ( * m ) * count ) ;
}
pvt - > mce_count = 0 ;
}
spin_unlock_irqrestore ( & pvt - > mce_lock , flags ) ;
/* proccess mcelog errors */
for ( i = 0 ; i < count ; i + + )
i7core_mce_output_error ( mci , & m [ i ] ) ;
kfree ( m ) ;
/* check memory count errors */
2009-07-15 13:56:23 +04:00
for ( i = 0 ; i < pvt - > sockets ; i + + )
check_mc_test_err ( mci , i ) ;
2009-06-23 05:48:29 +04:00
}
2009-07-10 05:06:41 +04:00
/*
* i7core_mce_check_error Replicates mcelog routine to get errors
* This routine simply queues mcelog errors , and
* return . The error itself should be handled later
* by i7core_check_error .
*/
static int i7core_mce_check_error ( void * priv , struct mce * mce )
{
struct i7core_pvt * pvt = priv ;
unsigned long flags ;
debugf0 ( __FILE__ " : %s() \n " , __func__ ) ;
2009-07-16 02:01:08 +04:00
/*
* Just let mcelog handle it if the error is
* outside the memory controller
*/
if ( ( ( mce - > status & 0xffff ) > > 7 ) ! = 1 )
return 0 ;
2009-07-16 02:53:24 +04:00
/* Bank 8 registers are the only ones that we know how to handle */
if ( mce - > bank ! = 8 )
return 0 ;
2009-07-10 05:06:41 +04:00
spin_lock_irqsave ( & pvt - > mce_lock , flags ) ;
if ( pvt - > mce_count < MCE_LOG_LEN ) {
memcpy ( & pvt - > mce_entry [ pvt - > mce_count ] , mce , sizeof ( * mce ) ) ;
pvt - > mce_count + + ;
}
spin_unlock_irqrestore ( & pvt - > mce_lock , flags ) ;
/* Advice mcelog that the error were handled */
2009-07-16 02:01:08 +04:00
return 1 ;
2009-07-10 05:06:41 +04:00
}
2009-06-23 05:41:15 +04:00
/*
* i7core_probe Probe for ONE instance of device to see if it is
* present .
* return :
* 0 for FOUND a device
* < 0 for error code
*/
static int __devinit i7core_probe ( struct pci_dev * pdev ,
const struct pci_device_id * id )
{
struct mem_ctl_info * mci ;
struct i7core_pvt * pvt ;
2009-07-15 13:56:23 +04:00
int num_channels = 0 ;
int num_csrows = 0 ;
2009-07-15 16:02:32 +04:00
int csrow = 0 ;
2009-06-23 05:41:15 +04:00
int dev_idx = id - > driver_data ;
2009-07-15 13:56:23 +04:00
int rc , i ;
u8 sockets ;
2009-06-23 05:41:15 +04:00
2009-06-23 05:48:30 +04:00
if ( unlikely ( dev_idx > = ARRAY_SIZE ( i7core_devs ) ) )
2009-06-23 05:41:15 +04:00
return - EINVAL ;
2009-06-23 05:48:30 +04:00
/* get the pci devices we want to reserve for our use */
2009-06-23 05:48:30 +04:00
rc = i7core_get_devices ( ) ;
if ( unlikely ( rc < 0 ) )
return rc ;
2009-06-23 05:48:30 +04:00
2009-07-15 13:56:23 +04:00
sockets = 1 ;
for ( i = NUM_SOCKETS - 1 ; i > 0 ; i - - )
if ( pci_devs [ 0 ] . pdev [ i ] ) {
sockets = i + 1 ;
break ;
}
for ( i = 0 ; i < sockets ; i + + ) {
int channels ;
int csrows ;
/* Check the number of active and not disabled channels */
rc = i7core_get_active_channels ( i , & channels , & csrows ) ;
if ( unlikely ( rc < 0 ) )
goto fail0 ;
num_channels + = channels ;
num_csrows + = csrows ;
}
2009-06-23 05:41:15 +04:00
/* allocate a new MC control structure */
mci = edac_mc_alloc ( sizeof ( * pvt ) , num_csrows , num_channels , 0 ) ;
2009-06-23 05:48:31 +04:00
if ( unlikely ( ! mci ) ) {
2009-06-23 05:48:30 +04:00
rc = - ENOMEM ;
goto fail0 ;
}
2009-06-23 05:41:15 +04:00
debugf0 ( " MC: " __FILE__ " : %s(): mci = %p \n " , __func__ , mci ) ;
2009-06-23 05:48:28 +04:00
mci - > dev = & pdev - > dev ; /* record ptr to the generic device */
2009-06-23 05:41:15 +04:00
pvt = mci - > pvt_info ;
2009-06-23 05:48:30 +04:00
memset ( pvt , 0 , sizeof ( * pvt ) ) ;
2009-07-15 13:56:23 +04:00
pvt - > sockets = sockets ;
2009-06-23 05:41:15 +04:00
mci - > mc_idx = 0 ;
2009-07-15 13:56:23 +04:00
2009-06-23 05:48:31 +04:00
/*
* FIXME : how to handle RDDR3 at MCI level ? It is possible to have
* Mixed RDDR3 / UDDR3 with Nehalem , provided that they are on different
* memory channels
*/
mci - > mtype_cap = MEM_FLAG_DDR3 ;
2009-06-23 05:41:15 +04:00
mci - > edac_ctl_cap = EDAC_FLAG_NONE ;
mci - > edac_cap = EDAC_FLAG_NONE ;
mci - > mod_name = " i7core_edac.c " ;
mci - > mod_ver = I7CORE_REVISION ;
mci - > ctl_name = i7core_devs [ dev_idx ] . ctl_name ;
mci - > dev_name = pci_name ( pdev ) ;
mci - > ctl_page_to_phys = NULL ;
2009-06-23 05:48:28 +04:00
mci - > mc_driver_sysfs_attributes = i7core_inj_attrs ;
2009-06-23 05:48:29 +04:00
/* Set the function pointer to an actual operation function */
mci - > edac_check = i7core_check_error ;
2009-06-23 05:48:29 +04:00
2009-06-23 05:48:30 +04:00
/* Store pci devices at mci for faster access */
2009-06-23 05:48:30 +04:00
rc = mci_bind_devs ( mci ) ;
2009-06-23 05:48:31 +04:00
if ( unlikely ( rc < 0 ) )
2009-06-23 05:48:30 +04:00
goto fail1 ;
/* Get dimm basic config */
2009-07-15 13:56:23 +04:00
for ( i = 0 ; i < sockets ; i + + )
2009-07-15 16:02:32 +04:00
get_dimm_config ( mci , & csrow , i ) ;
2009-06-23 05:48:30 +04:00
2009-06-23 05:41:15 +04:00
/* add this new MC control structure to EDAC's list of MCs */
2009-06-23 05:48:30 +04:00
if ( unlikely ( edac_mc_add_mc ( mci ) ) ) {
2009-06-23 05:41:15 +04:00
debugf0 ( " MC: " __FILE__
" : %s(): failed edac_mc_add_mc() \n " , __func__ ) ;
/* FIXME: perhaps some code should go here that disables error
* reporting if we just enabled it
*/
2009-06-23 05:48:30 +04:00
rc = - EINVAL ;
2009-06-23 05:41:15 +04:00
goto fail1 ;
}
/* allocating generic PCI control info */
i7core_pci = edac_pci_create_generic_ctl ( & pdev - > dev , EDAC_MOD_STR ) ;
2009-06-23 05:48:31 +04:00
if ( unlikely ( ! i7core_pci ) ) {
2009-06-23 05:41:15 +04:00
printk ( KERN_WARNING
" %s(): Unable to create PCI control \n " ,
__func__ ) ;
printk ( KERN_WARNING
" %s(): PCI error report via EDAC not setup \n " ,
__func__ ) ;
}
2009-06-23 05:48:28 +04:00
/* Default error mask is any memory */
2009-06-23 05:48:30 +04:00
pvt - > inject . channel = 0 ;
2009-06-23 05:48:28 +04:00
pvt - > inject . dimm = - 1 ;
pvt - > inject . rank = - 1 ;
pvt - > inject . bank = - 1 ;
pvt - > inject . page = - 1 ;
pvt - > inject . col = - 1 ;
2009-07-10 05:06:41 +04:00
/* Registers on edac_mce in order to receive memory errors */
pvt - > edac_mce . priv = pvt ;
pvt - > edac_mce . check_error = i7core_mce_check_error ;
spin_lock_init ( & pvt - > mce_lock ) ;
rc = edac_mce_register ( & pvt - > edac_mce ) ;
if ( unlikely ( rc < 0 ) ) {
debugf0 ( " MC: " __FILE__
" : %s(): failed edac_mce_register() \n " , __func__ ) ;
goto fail1 ;
}
2009-06-23 05:48:30 +04:00
i7core_printk ( KERN_INFO , " Driver loaded. \n " ) ;
2009-06-23 05:48:29 +04:00
2009-06-23 05:41:15 +04:00
return 0 ;
fail1 :
2009-06-23 05:48:30 +04:00
edac_mc_free ( mci ) ;
2009-06-23 05:41:15 +04:00
fail0 :
2009-06-23 05:48:30 +04:00
i7core_put_devices ( ) ;
return rc ;
2009-06-23 05:41:15 +04:00
}
/*
* i7core_remove destructor for one instance of device
*
*/
static void __devexit i7core_remove ( struct pci_dev * pdev )
{
struct mem_ctl_info * mci ;
2009-07-10 05:06:41 +04:00
struct i7core_pvt * pvt ;
2009-06-23 05:41:15 +04:00
debugf0 ( __FILE__ " : %s() \n " , __func__ ) ;
if ( i7core_pci )
edac_pci_release_generic_ctl ( i7core_pci ) ;
2009-06-23 05:48:29 +04:00
2009-07-10 05:06:41 +04:00
mci = edac_mc_del_mc ( & pdev - > dev ) ;
2009-06-23 05:41:15 +04:00
if ( ! mci )
return ;
2009-07-10 05:06:41 +04:00
/* Unregisters on edac_mce in order to receive memory errors */
pvt = mci - > pvt_info ;
edac_mce_unregister ( & pvt - > edac_mce ) ;
2009-06-23 05:41:15 +04:00
/* retrieve references to resources, and free those resources */
2009-06-23 05:48:29 +04:00
i7core_put_devices ( ) ;
2009-06-23 05:41:15 +04:00
edac_mc_free ( mci ) ;
}
MODULE_DEVICE_TABLE ( pci , i7core_pci_tbl ) ;
/*
* i7core_driver pci_driver structure for this module
*
*/
static struct pci_driver i7core_driver = {
. name = " i7core_edac " ,
. probe = i7core_probe ,
. remove = __devexit_p ( i7core_remove ) ,
. id_table = i7core_pci_tbl ,
} ;
/*
* i7core_init Module entry function
* Try to initialize this module for its devices
*/
static int __init i7core_init ( void )
{
int pci_rc ;
debugf2 ( " MC: " __FILE__ " : %s() \n " , __func__ ) ;
/* Ensure that the OPSTATE is set correctly for POLL or NMI */
opstate_init ( ) ;
pci_rc = pci_register_driver ( & i7core_driver ) ;
return ( pci_rc < 0 ) ? pci_rc : 0 ;
}
/*
* i7core_exit ( ) Module exit function
* Unregister the driver
*/
static void __exit i7core_exit ( void )
{
debugf2 ( " MC: " __FILE__ " : %s() \n " , __func__ ) ;
pci_unregister_driver ( & i7core_driver ) ;
}
module_init ( i7core_init ) ;
module_exit ( i7core_exit ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " Mauro Carvalho Chehab <mchehab@redhat.com> " ) ;
MODULE_AUTHOR ( " Red Hat Inc. (http://www.redhat.com) " ) ;
MODULE_DESCRIPTION ( " MC Driver for Intel i7 Core memory controllers - "
I7CORE_REVISION ) ;
module_param ( edac_op_state , int , 0444 ) ;
MODULE_PARM_DESC ( edac_op_state , " EDAC Error Reporting state: 0=Poll,1=NMI " ) ;