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