2007-07-19 12:49:42 +04:00
/*
* Intel 82443 BX / GX ( 440 BX / GX chipset ) Memory Controller EDAC kernel
* module ( C ) 2006 Tim Small
*
* 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 Linux
* Networx , Thayne Harbaugh , Dan Hollis < goemon at anime dot net > and
* others .
*
* 440 GX fix by Jason Uhlenkott < juhlenko @ akamai . com > .
*
* Written with reference to 82443 BX Host Bridge Datasheet :
2012-01-28 01:38:08 +04:00
* http : //download.intel.com/design/chipsets/datashts/29063301.pdf
2007-07-19 12:49:42 +04:00
* references to this document given in [ ] .
*
* This module doesn ' t support the 440L X , but it may be possible to
* make it do so ( the 440L X ' s register definitions are different , but
* not completely so - I haven ' t studied them in enough detail to know
* how easy this would be ) .
*/
# include <linux/module.h>
# include <linux/init.h>
# include <linux/pci.h>
# include <linux/pci_ids.h>
2008-04-29 12:03:18 +04:00
# include <linux/edac.h>
2007-07-19 12:49:47 +04:00
# include "edac_core.h"
2007-07-19 12:49:42 +04:00
# define I82443_REVISION "0.1"
# define EDAC_MOD_STR "i82443bxgx_edac"
/* The 82443BX supports SDRAM, or EDO (EDO for mobile only), "Memory
* Size : 8 MB to 512 MB ( 1 GB with Registered DIMMs ) with eight memory
* rows " " The 82443 BX supports multiple - bit error detection and
* single - bit error correction when ECC mode is enabled and
* single / multi - bit error detection when correction is disabled .
* During writes to the DRAM , the 82443 BX generates ECC for the data
* on a QWord basis . Partial QWord writes require a read - modify - write
* cycle when ECC is enabled . "
*/
/* "Additionally, the 82443BX ensures that the data is corrected in
* main memory so that accumulation of errors is prevented . Another
* error within the same QWord would result in a double - bit error
* which is unrecoverable . This is known as hardware scrubbing since
* it requires no software intervention to correct the data in memory . "
*/
/* [Also see page 100 (section 4.3), "DRAM Interface"]
* [ Also see page 112 ( section 4.6 .1 .4 ) , ECC ]
*/
# define I82443BXGX_NR_CSROWS 8
# define I82443BXGX_NR_CHANS 1
# define I82443BXGX_NR_DIMMS 4
/* 82443 PCI Device 0 */
2007-07-19 12:50:07 +04:00
# define I82443BXGX_NBXCFG 0x50 / * 32bit register starting at this PCI
* config space offset */
# define I82443BXGX_NBXCFG_OFFSET_NON_ECCROW 24 / * Array of bits, zero if
* row is non - ECC */
# define I82443BXGX_NBXCFG_OFFSET_DRAM_FREQ 12 /* 2 bits,00=100MHz,10=66 MHz */
# define I82443BXGX_NBXCFG_OFFSET_DRAM_INTEGRITY 7 /* 2 bits: */
# define I82443BXGX_NBXCFG_INTEGRITY_NONE 0x0 /* 00 = Non-ECC */
# define I82443BXGX_NBXCFG_INTEGRITY_EC 0x1 /* 01 = EC (only) */
# define I82443BXGX_NBXCFG_INTEGRITY_ECC 0x2 /* 10 = ECC */
# define I82443BXGX_NBXCFG_INTEGRITY_SCRUB 0x3 /* 11 = ECC + HW Scrub */
2007-07-19 12:49:42 +04:00
# define I82443BXGX_NBXCFG_OFFSET_ECC_DIAG_ENABLE 6
/* 82443 PCI Device 0 */
2007-07-19 12:50:07 +04:00
# define I82443BXGX_EAP 0x80 / * 32bit register starting at this PCI
* config space offset , Error Address
* Pointer Register */
# define I82443BXGX_EAP_OFFSET_EAP 12 /* High 20 bits of error address */
# define I82443BXGX_EAP_OFFSET_MBE BIT(1) /* Err at EAP was multi-bit (W1TC) */
# define I82443BXGX_EAP_OFFSET_SBE BIT(0) /* Err at EAP was single-bit (W1TC) */
# define I82443BXGX_ERRCMD 0x90 / * 8bit register starting at this PCI
2007-07-19 12:49:42 +04:00
* config space offset . */
2007-07-19 12:50:07 +04:00
# define I82443BXGX_ERRCMD_OFFSET_SERR_ON_MBE BIT(1) /* 1 = enable */
# define I82443BXGX_ERRCMD_OFFSET_SERR_ON_SBE BIT(0) /* 1 = enable */
2007-07-19 12:49:42 +04:00
2007-07-19 12:50:07 +04:00
# define I82443BXGX_ERRSTS 0x91 / * 16bit register starting at this PCI
2007-07-19 12:49:42 +04:00
* config space offset . */
2007-07-19 12:50:07 +04:00
# define I82443BXGX_ERRSTS_OFFSET_MBFRE 5 /* 3 bits - first err row multibit */
# define I82443BXGX_ERRSTS_OFFSET_MEF BIT(4) /* 1 = MBE occurred */
# define I82443BXGX_ERRSTS_OFFSET_SBFRE 1 /* 3 bits - first err row singlebit */
# define I82443BXGX_ERRSTS_OFFSET_SEF BIT(0) /* 1 = SBE occurred */
2007-07-19 12:49:42 +04:00
2007-07-19 12:50:07 +04:00
# define I82443BXGX_DRAMC 0x57 / * 8bit register starting at this PCI
* config space offset . */
# define I82443BXGX_DRAMC_OFFSET_DT 3 /* 2 bits, DRAM Type */
# define I82443BXGX_DRAMC_DRAM_IS_EDO 0 /* 00 = EDO */
2007-07-19 12:49:42 +04:00
# define I82443BXGX_DRAMC_DRAM_IS_SDRAM 1 /* 01 = SDRAM */
2007-07-19 12:50:07 +04:00
# define I82443BXGX_DRAMC_DRAM_IS_RSDRAM 2 /* 10 = Registered SDRAM */
2007-07-19 12:49:42 +04:00
2007-07-19 12:50:07 +04:00
# define I82443BXGX_DRB 0x60 / * 8x 8bit registers starting at this PCI
* config space offset . */
2007-07-19 12:49:42 +04:00
/* FIXME - don't poll when ECC disabled? */
struct i82443bxgx_edacmc_error_info {
u32 eap ;
} ;
2007-07-19 12:50:10 +04:00
static struct edac_pci_ctl_info * i82443bxgx_pci ;
2008-10-16 09:04:26 +04:00
static struct pci_dev * mci_pdev ; /* init dev: in case that AGP code has
* already registered driver
*/
static int i82443bxgx_registered = 1 ;
2007-07-19 12:50:07 +04:00
static void i82443bxgx_edacmc_get_error_info ( struct mem_ctl_info * mci ,
2007-07-19 12:50:13 +04:00
struct i82443bxgx_edacmc_error_info
* info )
2007-07-19 12:49:42 +04:00
{
struct pci_dev * pdev ;
2012-03-16 14:44:18 +04:00
pdev = to_pci_dev ( mci - > pdev ) ;
2007-07-19 12:49:42 +04:00
pci_read_config_dword ( pdev , I82443BXGX_EAP , & info - > eap ) ;
if ( info - > eap & I82443BXGX_EAP_OFFSET_SBE )
/* Clear error to allow next error to be reported [p.61] */
pci_write_bits32 ( pdev , I82443BXGX_EAP ,
I82443BXGX_EAP_OFFSET_SBE ,
I82443BXGX_EAP_OFFSET_SBE ) ;
if ( info - > eap & I82443BXGX_EAP_OFFSET_MBE )
/* Clear error to allow next error to be reported [p.61] */
pci_write_bits32 ( pdev , I82443BXGX_EAP ,
I82443BXGX_EAP_OFFSET_MBE ,
I82443BXGX_EAP_OFFSET_MBE ) ;
}
2007-07-19 12:50:07 +04:00
static int i82443bxgx_edacmc_process_error_info ( struct mem_ctl_info * mci ,
struct
i82443bxgx_edacmc_error_info
* info , int handle_errors )
2007-07-19 12:49:42 +04:00
{
int error_found = 0 ;
u32 eapaddr , page , pageoffset ;
/* bits 30:12 hold the 4kb block in which the error occurred
* [ p .61 ] */
eapaddr = ( info - > eap & 0xfffff000 ) ;
page = eapaddr > > PAGE_SHIFT ;
pageoffset = eapaddr - ( page < < PAGE_SHIFT ) ;
2007-07-19 12:50:07 +04:00
if ( info - > eap & I82443BXGX_EAP_OFFSET_SBE ) {
2007-07-19 12:49:42 +04:00
error_found = 1 ;
if ( handle_errors )
2012-06-04 20:27:43 +04:00
edac_mc_handle_error ( HW_EVENT_ERR_CORRECTED , mci , 1 ,
2012-04-16 22:10:18 +04:00
page , pageoffset , 0 ,
edac_mc_find_csrow_by_page ( mci , page ) ,
2012-06-04 18:29:25 +04:00
0 , - 1 , mci - > ctl_name , " " ) ;
2007-07-19 12:49:42 +04:00
}
2007-07-19 12:50:07 +04:00
if ( info - > eap & I82443BXGX_EAP_OFFSET_MBE ) {
2007-07-19 12:49:42 +04:00
error_found = 1 ;
if ( handle_errors )
2012-06-04 20:27:43 +04:00
edac_mc_handle_error ( HW_EVENT_ERR_UNCORRECTED , mci , 1 ,
2012-04-16 22:10:18 +04:00
page , pageoffset , 0 ,
edac_mc_find_csrow_by_page ( mci , page ) ,
2012-06-04 18:29:25 +04:00
0 , - 1 , mci - > ctl_name , " " ) ;
2007-07-19 12:49:42 +04:00
}
return error_found ;
}
static void i82443bxgx_edacmc_check ( struct mem_ctl_info * mci )
{
struct i82443bxgx_edacmc_error_info info ;
2012-04-30 00:08:39 +04:00
edac_dbg ( 1 , " MC%d \n " , mci - > mc_idx ) ;
2007-07-19 12:49:42 +04:00
i82443bxgx_edacmc_get_error_info ( mci , & info ) ;
i82443bxgx_edacmc_process_error_info ( mci , & info , 1 ) ;
}
static void i82443bxgx_init_csrows ( struct mem_ctl_info * mci ,
2007-07-19 12:50:13 +04:00
struct pci_dev * pdev ,
enum edac_type edac_mode ,
enum mem_type mtype )
2007-07-19 12:49:42 +04:00
{
struct csrow_info * csrow ;
2012-01-28 01:38:08 +04:00
struct dimm_info * dimm ;
2007-07-19 12:49:42 +04:00
int index ;
u8 drbar , dramc ;
u32 row_base , row_high_limit , row_high_limit_last ;
pci_read_config_byte ( pdev , I82443BXGX_DRAMC , & dramc ) ;
row_high_limit_last = 0 ;
for ( index = 0 ; index < mci - > nr_csrows ; index + + ) {
2012-04-24 22:05:43 +04:00
csrow = mci - > csrows [ index ] ;
dimm = csrow - > channels [ 0 ] - > dimm ;
2012-01-28 01:38:08 +04:00
2007-07-19 12:49:42 +04:00
pci_read_config_byte ( pdev , I82443BXGX_DRB + index , & drbar ) ;
2012-04-30 00:08:39 +04:00
edac_dbg ( 1 , " MC%d: Row=%d DRB = %#0x \n " ,
mci - > mc_idx , index , drbar ) ;
2007-07-19 12:49:42 +04:00
row_high_limit = ( ( u32 ) drbar < < 23 ) ;
/* find the DRAM Chip Select Base address and mask */
2012-04-30 00:08:39 +04:00
edac_dbg ( 1 , " MC%d: Row=%d, Boundary Address=%#0x, Last = %#0x \n " ,
mci - > mc_idx , index , row_high_limit ,
row_high_limit_last ) ;
2007-07-19 12:49:42 +04:00
/* 440GX goes to 2GB, represented with a DRB of 0. */
if ( row_high_limit_last & & ! row_high_limit )
row_high_limit = 1UL < < 31 ;
/* This row is empty [p.49] */
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 ;
2012-01-28 16:09:38 +04:00
dimm - > nr_pages = csrow - > last_page - csrow - > first_page + 1 ;
2007-07-19 12:49:42 +04:00
/* EAP reports in 4kilobyte granularity [61] */
2012-01-28 01:38:08 +04:00
dimm - > grain = 1 < < 12 ;
dimm - > mtype = mtype ;
2007-07-19 12:49:42 +04:00
/* I don't think 440BX can tell you device type? FIXME? */
2012-01-28 01:38:08 +04:00
dimm - > dtype = DEV_UNKNOWN ;
2007-07-19 12:49:42 +04:00
/* Mode is global to all rows on 440BX */
2012-01-28 01:38:08 +04:00
dimm - > edac_mode = edac_mode ;
2007-07-19 12:49:42 +04:00
row_high_limit_last = row_high_limit ;
}
}
2007-07-19 12:50:07 +04:00
static int i82443bxgx_edacmc_probe1 ( struct pci_dev * pdev , int dev_idx )
2007-07-19 12:49:42 +04:00
{
struct mem_ctl_info * mci ;
2012-04-16 22:10:18 +04:00
struct edac_mc_layer layers [ 2 ] ;
2007-07-19 12:49:42 +04:00
u8 dramc ;
u32 nbxcfg , ecc_mode ;
enum mem_type mtype ;
enum edac_type edac_mode ;
2012-04-30 00:08:39 +04:00
edac_dbg ( 0 , " MC: \n " ) ;
2007-07-19 12:49:42 +04:00
/* Something is really hosed if PCI config space reads from
2007-07-19 12:50:13 +04:00
* the MC aren ' t working .
*/
2007-07-19 12:49:42 +04:00
if ( pci_read_config_dword ( pdev , I82443BXGX_NBXCFG , & nbxcfg ) )
return - EIO ;
2012-04-16 22:10:18 +04:00
layers [ 0 ] . type = EDAC_MC_LAYER_CHIP_SELECT ;
layers [ 0 ] . size = I82443BXGX_NR_CSROWS ;
layers [ 0 ] . is_virt_csrow = true ;
layers [ 1 ] . type = EDAC_MC_LAYER_CHANNEL ;
layers [ 1 ] . size = I82443BXGX_NR_CHANS ;
layers [ 1 ] . is_virt_csrow = false ;
2012-05-02 21:37:00 +04:00
mci = edac_mc_alloc ( 0 , ARRAY_SIZE ( layers ) , layers , 0 ) ;
2007-07-19 12:49:42 +04:00
if ( mci = = NULL )
return - ENOMEM ;
2012-04-30 00:08:39 +04:00
edac_dbg ( 0 , " MC: mci = %p \n " , mci ) ;
2012-03-16 14:44:18 +04:00
mci - > pdev = & pdev - > dev ;
2007-07-19 12:49:42 +04:00
mci - > mtype_cap = MEM_FLAG_EDO | MEM_FLAG_SDR | MEM_FLAG_RDR ;
mci - > edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_EC | EDAC_FLAG_SECDED ;
pci_read_config_byte ( pdev , I82443BXGX_DRAMC , & dramc ) ;
switch ( ( dramc > > I82443BXGX_DRAMC_OFFSET_DT ) & ( BIT ( 0 ) | BIT ( 1 ) ) ) {
2007-07-19 12:50:07 +04:00
case I82443BXGX_DRAMC_DRAM_IS_EDO :
2007-07-19 12:49:42 +04:00
mtype = MEM_EDO ;
break ;
case I82443BXGX_DRAMC_DRAM_IS_SDRAM :
mtype = MEM_SDR ;
break ;
case I82443BXGX_DRAMC_DRAM_IS_RSDRAM :
mtype = MEM_RDR ;
break ;
default :
2012-04-30 00:08:39 +04:00
edac_dbg ( 0 , " Unknown/reserved DRAM type value in DRAMC register! \n " ) ;
2007-07-19 12:49:42 +04:00
mtype = - MEM_UNKNOWN ;
}
if ( ( mtype = = MEM_SDR ) | | ( mtype = = MEM_RDR ) )
mci - > edac_cap = mci - > edac_ctl_cap ;
else
mci - > edac_cap = EDAC_FLAG_NONE ;
mci - > scrub_cap = SCRUB_FLAG_HW_SRC ;
pci_read_config_dword ( pdev , I82443BXGX_NBXCFG , & nbxcfg ) ;
ecc_mode = ( ( nbxcfg > > I82443BXGX_NBXCFG_OFFSET_DRAM_INTEGRITY ) &
2007-07-19 12:50:13 +04:00
( BIT ( 0 ) | BIT ( 1 ) ) ) ;
2007-07-19 12:49:42 +04:00
mci - > scrub_mode = ( ecc_mode = = I82443BXGX_NBXCFG_INTEGRITY_SCRUB )
2007-07-19 12:50:13 +04:00
? SCRUB_HW_SRC : SCRUB_NONE ;
2007-07-19 12:49:42 +04:00
2007-07-19 12:50:07 +04:00
switch ( ecc_mode ) {
2007-07-19 12:49:42 +04:00
case I82443BXGX_NBXCFG_INTEGRITY_NONE :
edac_mode = EDAC_NONE ;
break ;
case I82443BXGX_NBXCFG_INTEGRITY_EC :
edac_mode = EDAC_EC ;
break ;
case I82443BXGX_NBXCFG_INTEGRITY_ECC :
case I82443BXGX_NBXCFG_INTEGRITY_SCRUB :
edac_mode = EDAC_SECDED ;
break ;
default :
2012-04-30 00:08:39 +04:00
edac_dbg ( 0 , " Unknown/reserved ECC state in NBXCFG register! \n " ) ;
2007-07-19 12:49:42 +04:00
edac_mode = EDAC_UNKNOWN ;
break ;
}
i82443bxgx_init_csrows ( mci , pdev , edac_mode , mtype ) ;
/* Many BIOSes don't clear error flags on boot, so do this
2011-03-31 05:57:33 +04:00
* here , or we get " phantom " errors occurring at module - load
2007-07-19 12:49:42 +04:00
* time . */
pci_write_bits32 ( pdev , I82443BXGX_EAP ,
2007-07-19 12:50:13 +04:00
( I82443BXGX_EAP_OFFSET_SBE |
I82443BXGX_EAP_OFFSET_MBE ) ,
( I82443BXGX_EAP_OFFSET_SBE |
I82443BXGX_EAP_OFFSET_MBE ) ) ;
2007-07-19 12:49:42 +04:00
mci - > mod_name = EDAC_MOD_STR ;
mci - > mod_ver = I82443_REVISION ;
mci - > ctl_name = " I82443BXGX " ;
2007-07-19 12:49:47 +04:00
mci - > dev_name = pci_name ( pdev ) ;
2007-07-19 12:49:42 +04:00
mci - > edac_check = i82443bxgx_edacmc_check ;
mci - > ctl_page_to_phys = NULL ;
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 " ) ;
2007-07-19 12:49:42 +04:00
goto fail ;
}
2007-07-19 12:50:10 +04:00
/* allocating generic PCI control info */
i82443bxgx_pci = edac_pci_create_generic_ctl ( & pdev - > dev , EDAC_MOD_STR ) ;
if ( ! i82443bxgx_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__ ) ;
}
2012-04-30 00:08:39 +04:00
edac_dbg ( 3 , " MC: success \n " ) ;
2007-07-19 12:49:42 +04:00
return 0 ;
2007-07-19 12:50:13 +04:00
fail :
2007-07-19 12:49:42 +04:00
edac_mc_free ( mci ) ;
return - ENODEV ;
}
2007-07-19 12:50:07 +04:00
2007-07-19 12:49:42 +04:00
EXPORT_SYMBOL_GPL ( i82443bxgx_edacmc_probe1 ) ;
/* returns count (>= 0), or negative on error */
2012-12-22 01:23:51 +04:00
static int i82443bxgx_edacmc_init_one ( struct pci_dev * pdev ,
const struct pci_device_id * ent )
2007-07-19 12:49:42 +04:00
{
2008-10-16 09:04:26 +04:00
int rc ;
2012-04-30 00:08:39 +04:00
edac_dbg ( 0 , " MC: \n " ) ;
2007-07-19 12:49:42 +04:00
2010-05-18 16:45:47 +04:00
/* don't need to call pci_enable_device() */
2008-10-16 09:04:26 +04:00
rc = i82443bxgx_edacmc_probe1 ( pdev , ent - > driver_data ) ;
if ( mci_pdev = = NULL )
mci_pdev = pci_dev_get ( pdev ) ;
return rc ;
2007-07-19 12:49:42 +04:00
}
2012-12-22 01:23:51 +04:00
static void i82443bxgx_edacmc_remove_one ( struct pci_dev * pdev )
2007-07-19 12:49:42 +04:00
{
struct mem_ctl_info * mci ;
2012-04-30 00:08:39 +04:00
edac_dbg ( 0 , " \n " ) ;
2007-07-19 12:49:42 +04:00
2007-07-19 12:50:10 +04:00
if ( i82443bxgx_pci )
edac_pci_release_generic_ctl ( i82443bxgx_pci ) ;
2007-07-19 12:50:07 +04:00
if ( ( mci = edac_mc_del_mc ( & pdev - > dev ) ) = = NULL )
2007-07-19 12:49:42 +04:00
return ;
edac_mc_free ( mci ) ;
}
2007-07-19 12:50:07 +04:00
EXPORT_SYMBOL_GPL ( i82443bxgx_edacmc_remove_one ) ;
2007-07-19 12:49:42 +04:00
2012-02-27 10:41:47 +04:00
static DEFINE_PCI_DEVICE_TABLE ( i82443bxgx_pci_tbl ) = {
2007-07-19 12:49:42 +04:00
{ PCI_DEVICE ( PCI_VENDOR_ID_INTEL , PCI_DEVICE_ID_INTEL_82443BX_0 ) } ,
{ PCI_DEVICE ( PCI_VENDOR_ID_INTEL , PCI_DEVICE_ID_INTEL_82443BX_2 ) } ,
{ PCI_DEVICE ( PCI_VENDOR_ID_INTEL , PCI_DEVICE_ID_INTEL_82443GX_0 ) } ,
{ PCI_DEVICE ( PCI_VENDOR_ID_INTEL , PCI_DEVICE_ID_INTEL_82443GX_2 ) } ,
{ 0 , } /* 0 terminated list. */
} ;
MODULE_DEVICE_TABLE ( pci , i82443bxgx_pci_tbl ) ;
static struct pci_driver i82443bxgx_edacmc_driver = {
. name = EDAC_MOD_STR ,
. probe = i82443bxgx_edacmc_init_one ,
2012-12-22 01:23:51 +04:00
. remove = i82443bxgx_edacmc_remove_one ,
2007-07-19 12:49:42 +04:00
. id_table = i82443bxgx_pci_tbl ,
} ;
static int __init i82443bxgx_edacmc_init ( void )
{
2008-10-16 09:04:26 +04:00
int pci_rc ;
2008-04-29 12:03:18 +04:00
/* Ensure that the OPSTATE is set correctly for POLL or NMI */
opstate_init ( ) ;
2008-10-16 09:04:26 +04:00
pci_rc = pci_register_driver ( & i82443bxgx_edacmc_driver ) ;
if ( pci_rc < 0 )
goto fail0 ;
if ( mci_pdev = = NULL ) {
const struct pci_device_id * id = & i82443bxgx_pci_tbl [ 0 ] ;
int i = 0 ;
i82443bxgx_registered = 0 ;
while ( mci_pdev = = NULL & & id - > vendor ! = 0 ) {
mci_pdev = pci_get_device ( id - > vendor ,
id - > device , NULL ) ;
i + + ;
id = & i82443bxgx_pci_tbl [ i ] ;
}
if ( ! mci_pdev ) {
2012-04-30 00:08:39 +04:00
edac_dbg ( 0 , " i82443bxgx pci_get_device fail \n " ) ;
2008-10-16 09:04:26 +04:00
pci_rc = - ENODEV ;
goto fail1 ;
}
pci_rc = i82443bxgx_edacmc_init_one ( mci_pdev , i82443bxgx_pci_tbl ) ;
if ( pci_rc < 0 ) {
2012-04-30 00:08:39 +04:00
edac_dbg ( 0 , " i82443bxgx init fail \n " ) ;
2008-10-16 09:04:26 +04:00
pci_rc = - ENODEV ;
goto fail1 ;
}
}
return 0 ;
fail1 :
pci_unregister_driver ( & i82443bxgx_edacmc_driver ) ;
fail0 :
if ( mci_pdev ! = NULL )
pci_dev_put ( mci_pdev ) ;
return pci_rc ;
2007-07-19 12:49:42 +04:00
}
static void __exit i82443bxgx_edacmc_exit ( void )
{
pci_unregister_driver ( & i82443bxgx_edacmc_driver ) ;
2008-10-16 09:04:26 +04:00
if ( ! i82443bxgx_registered )
i82443bxgx_edacmc_remove_one ( mci_pdev ) ;
if ( mci_pdev )
pci_dev_put ( mci_pdev ) ;
2007-07-19 12:49:42 +04:00
}
module_init ( i82443bxgx_edacmc_init ) ;
module_exit ( i82443bxgx_edacmc_exit ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " Tim Small <tim@buttersideup.com> - WPAD " ) ;
MODULE_DESCRIPTION ( " EDAC MC support for Intel 82443BX/GX memory controllers " ) ;
2008-04-29 12:03:18 +04:00
module_param ( edac_op_state , int , 0444 ) ;
MODULE_PARM_DESC ( edac_op_state , " EDAC Error Reporting state: 0=Poll,1=NMI " ) ;