this is a slightly hacked up version of the 20020429 code available at ... except i needed it to be compiled directly into the kernel, and not as a module. -dean --- linux/drivers/char/ecc.c.dgecc Thu May 2 19:13:54 2002 +++ linux/drivers/char/ecc.c Thu May 2 19:49:56 2002 @@ -0,0 +1,1465 @@ +/* + * ECC kernel module (C) 1998, 1999 Dan Hollis + * Portions thanks to + * Michael O'Reilly + * Osma Ahvenlampi + * Martin Maney + * Peter Heckert + * Ishikawa + * Jaakko Hyvti + * Christopher Hoover + */ + +#define DEBUG 0 + +#if 0 +#if (LINUX_VERSION_CODE < 0x020401) +#define __KERNEL__ +#define MODULE +#endif +#endif +#include +#include +#if (LINUX_VERSION_CODE < 0x020401) +#ifdef CONFIG_MODVERSIONS +#include +#endif +#endif +#include + +#include +#include +#include +#include +#include +#include +#include + +#define ECC_VER "development" + +static spinlock_t ecc_lock = SPIN_LOCK_UNLOCKED; +static struct timer_list ecc_timer; +static struct pci_dev *bridge = NULL; +static u16 vendor, device; +static int scrub_needed; +static int scrub_row; + +/* memory types */ +#define BANK_EMPTY 0 /* Empty bank */ +#define BANK_RESERVED 1 /* Reserved bank type */ +#define BANK_UNKNOWN 2 /* Unknown bank type */ +#define BANK_FPM 3 /* Fast page mode */ +#define BANK_EDO 4 /* Extended data out */ +#define BANK_BEDO 5 /* Burst Extended data out */ +#define BANK_SDR 6 /* Single data rate SDRAM */ +#define BANK_DDR 7 /* Double data rate SDRAM */ +#define BANK_RDR 8 /* Registered SDRAM */ +#define BANK_RMBS 9 /* Rambus DRAM */ + +/* Memory bank info */ +#define MAX_BANKS 16 /* do any chipsets support more? */ +static struct bankstruct +{ + u32 endaddr; /* bank ending address */ + u32 mbecount; /* total number of MBE errors */ + u32 sbecount; /* total number of SBE errors */ + u8 eccmode; /* ECC enabled for this bank? */ + u8 mtype; /* memory bank type */ +} bank[MAX_BANKS]; + +/* chipset ECC capabilities and mode */ +#define ECC_NONE 0 /* Doesnt support ECC (or is BIOS disabled) */ +#define ECC_RESERVED 1 /* Reserved ECC type */ +#define ECC_PARITY 2 /* Detects parity errors */ +#define ECC_DETECT 3 /* Detects ECC errors */ +#define ECC_CORRECT 4 /* Detects ECC errors and corrects SBE */ +#define ECC_AUTO 5 /* Detects ECC errors and has hardware scrubber */ + +static struct ChipsetInfo +{ + int ecc_cap; /* chipset ECC capabilities */ + int ecc_mode; /* current ECC mode */ + void (*check)(void); /* pointer to ecc checking routine */ + void (*clear_err)(void);/* pointer to error clear routine */ +#if 0 +/* + * I dont think we care about SERR at the moment. + * We may if/when we hook into an NMI handler. + */ + int SERR; /* SERR enabled? */ + int SERR_MBE; /* SERR on multi-bit error? */ + int SERR_SBE; /* SERR on single-bit error? */ +#endif + int MBE_flag_address; /* pci offset for mbe register */ + int MBE_flag_shift; /* bits to shift for mbe flag */ + int MBE_flag_mask; /* mask for mbe flag */ + int MBE_row_shift; /* bits to shift for mbe row flag */ + int MBE_row_mask; /* mask for mbe register (shifted) */ + int SBE_flag_address; /* pci offset for sbe register */ + int SBE_flag_shift; /* bits to shift for sbe flag */ + int SBE_flag_mask; /* mask for sbe flag */ + int SBE_row_shift; /* bits to shift for sbe row flag */ + int SBE_row_mask; /* mask for sbe register (shifted) */ + int MBE_err_address1; /* pci offset for mbe address register */ + int MBE_err_shift1; /* bits to shift for mbe address register */ + int MBE_err_address2; /* pci offset for mbe address register */ + int MBE_err_shift2; /* bits to shift for mbe address register */ + u32 MBE_err_mask; /* mask for mbe address register */ + int MBE_err_flag; /* MBE error flag */ + int MBE_err_row; /* MBE row */ + u32 MBE_addr; /* address of last MBE */ + int SBE_err_address1; /* pci offset for mbe address register */ + int SBE_err_shift1; /* bits to shift for mbe address register */ + int SBE_err_address2; /* pci offset for mbe address register */ + int SBE_err_shift2; /* bits to shift for mbe address register */ + u32 SBE_err_mask; /* mask for mbe address register */ + int SBE_err_flag; /* SBE error flag */ + int SBE_err_row; /* SBE row */ + u32 SBE_addr; /* address of last SBE */ +} cs; + +static inline unsigned int pci_byte(int offset) +{ + u8 value; + pci_read_config_byte(bridge, offset, &value); + return value & 0xFF; +} + +static inline unsigned int pci_word(int offset) +{ + u16 value; + pci_read_config_word(bridge, offset, &value); + return value; +} + +static inline unsigned int pci_dword(int offset) +{ + u32 value; + pci_read_config_dword(bridge, offset, &value); + return value; +} + +/* write all or some bits in a byte-register*/ +static inline void pci_write_bits8(int offset,u8 value,u8 mask) +{ + if (mask != 0xff){ + u8 buf; + pci_read_config_byte(bridge,offset, &buf); + value &=mask; + buf &= ~mask; + value |= buf; + } + pci_write_config_byte(bridge,offset,value); +} + +static int find_row(unsigned long err_addr) +{ + int row = 0, loop; + for(loop=0;loop> cs.MBE_flag_shift) & cs.MBE_flag_mask) + { + int row = (status >> cs.MBE_row_shift) & cs.MBE_row_mask; + printk(KERN_ERR "ECC: MBE detected in DRAM row %d\n", row); + if (cs.MBE_err_address1) + { + cs.MBE_addr = + ( pci_word(cs.MBE_err_address1 << cs.MBE_err_shift1) | + pci_word(cs.MBE_err_address2 << cs.MBE_err_shift2) ) & + cs.MBE_err_mask; + printk(KERN_ERR "ECC: MBE at memory address %lx\n", (long unsigned int)cs.MBE_addr); + } + scrub_needed = 2; + scrub_row = row; + bank[row].mbecount++; + } + if (cs.SBE_flag_address != cs.MBE_flag_address) + status = pci_byte(cs.SBE_flag_address); + if ((status >> cs.SBE_flag_shift) & cs.SBE_flag_mask) + { + int row = (status >> cs.SBE_row_shift) & cs.SBE_row_mask; + printk(KERN_ERR "ECC: SBE detected in DRAM row %d\n", row); + if (cs.SBE_err_address1) + { + cs.SBE_addr = + ( pci_word(cs.SBE_err_address1 << cs.SBE_err_shift1) | + pci_word(cs.SBE_err_address2 << cs.SBE_err_shift2) ) & + cs.SBE_err_mask; + printk(KERN_ERR "ECC: SBE at memory address %lx\n", (long unsigned int)cs.SBE_addr); + } + scrub_needed = 1; + scrub_row = row; + bank[row].sbecount++; + } +} + +/* VIA specific error clearing */ +static void clear_via_err(void) +{ + pci_write_bits8(0x6f,0xff,0xff); + /* as scrubbing is unimplemented, we reset it here */ + scrub_needed = 0; +} + +/* unified VIA probe */ +static void probe_via(void) +{ + int loop, ecc_ctrl, dimmslots = 3, bankshift = 23; + int m_mem[] = { BANK_FPM, BANK_EDO, BANK_DDR, BANK_SDR }; + switch (device) { + case 0x0391: /* VIA VT8371 - KX133 */ + dimmslots = 4; + bankshift = 24; + case 0x0595: /* VIA VT82C595 - VP2,VP2/97 */ + m_mem[2] = BANK_RESERVED; + cs.ecc_cap = ECC_CORRECT; + break; + case 0x0501: /* VIA VT8501 - MVP4 */ + case 0x0597: /* VIA VT82C597 - VP3 */ + case 0x0598: /* VIA VT82C598 - MVP3 */ + cs.ecc_cap = ECC_CORRECT; + break; + case 0x3099: /* VIA VT8366 - KT266/KT266A */ + m_mem[0] = BANK_SDR; + m_mem[1] = BANK_RESERVED; + m_mem[3] = BANK_RESERVED; + case 0x0691: /* VIA VT82C691 - Apollo PRO */ + /* VIA VT82C693A - Apollo Pro133 (rev 40?) */ + /* VIA VT82C694X - Apollo Pro133A (rev 8x,Cx) + has 4 dimm slots */ + case 0x0694: /* VIA VT82C694XDP - Apollo PRO133A smp */ + dimmslots = 4; + case 0x0693: /* VIA VT82C693 - Apollo PRO-Plus */ + bankshift = 24; + cs.ecc_cap = ECC_CORRECT; + break; + case 0x0305: /* VIA VT8363 - KT133 - no ecc!! */ + /* VIA VT8363A - KT133A (rev 8) - no ecc!! */ + bankshift = 24; + cs.ecc_cap = ECC_NONE; + break; + case 0x0585: /* VIA VT82C585 - VP,VPX,VPX/97 */ + default: + cs.ecc_cap = ECC_NONE; + return; + } + ecc_ctrl = pci_byte(0x6E); + cs.ecc_mode = (ecc_ctrl>>7)&1 ? cs.ecc_cap : ECC_NONE; + + cs.check = generic_check; + if (cs.ecc_mode != ECC_NONE) { + cs.clear_err = clear_via_err; + /* clear initial errors */ + cs.clear_err(); + } + cs.MBE_flag_address = 0x6F; + cs.MBE_flag_shift = 7; + cs.MBE_flag_mask = 1; + cs.MBE_row_shift = 4; + cs.MBE_row_mask = 7; + cs.SBE_flag_address = 0x6F; + cs.SBE_flag_shift = 3; + cs.SBE_flag_mask = 1; + cs.SBE_row_shift = 0; + cs.SBE_row_mask = 7; + + for(loop=0;loop<6;loop++) + bank[loop].endaddr = (unsigned long)pci_byte(0x5a+loop)<>(loop*2))&3]; + bank[(loop*2)+1].mtype = m_mem[(pci_byte(0x60)>>(loop*2))&3]; + bank[loop*2].eccmode = (ecc_ctrl>>(loop))&1; + bank[(loop*2)+1].eccmode = (ecc_ctrl>>(loop))&1; + } +} + +static void check_serverworks(void) +{ + unsigned long mesr; + int row; + + mesr = pci_dword(0x94); /* mesr0 */ + row = (int)(mesr>>29)&0x7; + if((mesr>>31)&1) + { + printk(KERN_ERR "ECC: MBE Detected in DRAM row %d\n", row); + scrub_needed |= 2; + bank[row].mbecount++; + } + if((mesr>>30)&1) + { + printk(KERN_ERR "ECC: SBE Detected in DRAM row %d\n", row); + scrub_needed |= 1; + bank[row].sbecount++; + } + if (scrub_needed) + { + /* + * clear error flag bits that were set by writing 1 to them + * we hope the error was a fluke or something :) + */ + unsigned long value = scrub_needed<<30; + pci_write_config_dword(bridge, 0x94, value); + scrub_needed = 0; + } + + mesr = pci_dword(0x98); /* mesr1 */ + row = (int)(mesr>>29)&0x7; + if((mesr>>31)&1) + { + printk(KERN_ERR "ECC: MBE Detected in DRAM row %d\n", row); + scrub_needed |= 2; + bank[row].mbecount++; + } + if((mesr>>30)&1) + { + printk(KERN_ERR "ECC: SBE Detected in DRAM row %d\n", row); + scrub_needed |= 1; + bank[row].sbecount++; + } + if (scrub_needed) + { + /* + * clear error flag bits that were set by writing 1 to them + * we hope the error was a fluke or something :) + */ + unsigned long value = scrub_needed<<30; + pci_write_config_dword(bridge, 0x98, value); + scrub_needed = 0; + } +} + +static void probe_serverworks(void) +{ + int loop, efcr, mccr; + switch (device) { + case 0x0008: /* serverworks iii he */ + case 0x0009: /* serverworks iii le */ + cs.ecc_cap = ECC_AUTO; + break; + default: + cs.ecc_cap = ECC_NONE; + return; + } + efcr = pci_byte(0xE0); + mccr = pci_word(0x92); + cs.ecc_mode = (efcr>>2)&1 ? (mccr&1 ? ECC_AUTO : ECC_CORRECT) : ECC_NONE; + + cs.check = check_serverworks; + + for(loop=0;loop<8;loop++) + { +#if 0 + printk(KERN_ERR "ECC: row %d 0x%x\n", loop, pci_byte(0x7c+loop)&0x0F); + printk(KERN_ERR "ECC: row %d 0x%x\n", loop+1, (pci_byte(0x7c+loop)>>4)&0x0F); +#endif + bank[loop].mtype = BANK_SDR; + bank[loop].eccmode = cs.ecc_mode; + bank[loop].endaddr = pci_byte(0x81+loop*2)<<24; + } +} + +/* + * 450gx probing is buggered at the moment. help me obi-wan. + */ + +static void probe_450gx(void) +{ + int loop, dramc, merrcmd; + u32 nbxcfg; + int m_mem[] = { BANK_EDO, BANK_SDR, BANK_RDR, BANK_RESERVED }; +/* int ddim[] = { ECC_NONE, ECC_DETECT, ECC_CORRECT, ECC_AUTO }; */ + nbxcfg = pci_word(0x50) | (pci_word(0x52)<<16); + dramc = pci_byte(0x57); + merrcmd = pci_word(0xC0); + for(loop=0;loop<8;loop++) + { + bank[loop].endaddr=(unsigned long)pci_byte(0x60+loop)<<23; + /* 450gx doesnt allow mixing memory types. bleah. */ + bank[loop].mtype = m_mem[(dramc>>3)&3]; + /* yes, bit is _zero_ if ecc is _enabled_. */ + bank[loop].eccmode = !((nbxcfg>>(loop+24))&1); + } + cs.ecc_cap = ECC_AUTO; + cs.ecc_mode = (merrcmd>>1)&1 ? ECC_AUTO : ECC_DETECT; + + cs.check = generic_check; + cs.MBE_flag_address = 0xC2; + cs.MBE_flag_shift = 0; + cs.MBE_flag_mask = 1; + cs.MBE_row_shift = 5; + cs.MBE_row_mask = 7; + cs.SBE_flag_address = 0xC2; + cs.SBE_flag_shift = 1; + cs.SBE_flag_mask = 1; + cs.SBE_row_shift = 1; + cs.SBE_row_mask = 7; + + cs.MBE_err_address1 = 0xA8; + cs.MBE_err_shift1 = 0; + cs.MBE_err_address2 = 0xAA; + cs.MBE_err_shift2 = 16; + cs.MBE_err_mask = 0xFFFFFFFC; + + cs.SBE_err_address1 = 0x74; + cs.SBE_err_shift1 = 0; + cs.SBE_err_address2 = 0x76; + cs.SBE_err_shift2 = 16; + cs.SBE_err_mask = 0xFFFFFFFC; +} + +static void check_450nx(void) +{ + u16 mel0 = pci_word(0xB0); + u16 mel1 = pci_word(0xB2); + if (mel0 & 3) { + u8 mea0 = pci_byte(0xA8); + printk(KERN_ERR "ECC: "); + switch (mel0 & 3){ + case 1: printk("SBE"); + bank[(mea0>>3)&0x1F].sbecount++; + break; + case 2: printk("MBE"); + bank[(mea0>>3)&0x1F].mbecount++; + break; + case 3: printk("SBE and MBE"); + bank[(mea0>>3)&0x1F].sbecount++; + bank[(mea0>>3)&0x1F].mbecount++; + break; + } + printk(" Detected, bank %d (card %d bank %d row %d)\n", + (mea0>>3)&0x1F, (mea0>>7)&1, (mea0>>4)&7, (mea0>>3)&1 ); + pci_write_config_word(bridge, 0xB0, 0); + pci_write_config_byte(bridge, 0xA8, 0); + } + if (mel1 & 3) { + u8 mea1 = pci_byte(0xA9); + switch (mel1 & 3){ + case 1: printk(KERN_ERR "ECC: SBE"); + bank[(mea1>>3)&0x1F].sbecount++; + break; + case 2: printk(KERN_ERR "ECC: MBE"); + bank[(mea1>>3)&0x1F].mbecount++; + break; + case 3: printk(KERN_ERR "ECC: SBE and MBE"); + bank[(mea1>>3)&0x1F].sbecount++; + bank[(mea1>>3)&0x1F].mbecount++; + break; + } + printk(" Detected, bank %d (card %d bank %d row %d)\n", + (mea1>>3)&0x1F, (mea1>>7)&1, (mea1>>4)&7, (mea1>>3)&1 ); + pci_write_config_word(bridge, 0xB2, 0); + pci_write_config_byte(bridge, 0xA9, 0); + } +} + +static void probe_450nx(void) +{ + int loop, ecccmd, dbc; + u32 lastaddr; + ecccmd = pci_byte(0xB8); + if (ecccmd & 8) { /* mss */ + cs.ecc_mode = ECC_AUTO; + } else if (ecccmd & 1) { /* mcs */ + cs.ecc_mode = ECC_CORRECT; + } else if (ecccmd & 6) { /* mrs & mrm */ + cs.ecc_mode = ECC_DETECT; + } else { + cs.ecc_mode = ECC_NONE; + } + cs.ecc_cap = ECC_AUTO; + cs.check = check_450nx; + + lastaddr = 0L; + for(loop=0;loop<8;loop++) + { + dbc = pci_word(0x80+loop*2); + if (dbc & 0x2000) /* bank present */ + { + if (dbc & 0x4000) /* single row */ + { + bank[loop*2].endaddr=(dbc & 0x03FF)<<25; + bank[loop*2].mtype = BANK_EDO; + bank[loop*2].eccmode = cs.ecc_mode; + lastaddr=(dbc & 0x03FF)<<25; + } else { /* double row */ + bank[loop*2].endaddr=lastaddr+((((dbc & 0x03FF)<<25)-lastaddr)>>1); + bank[loop*2].mtype = BANK_EDO; + bank[loop*2].eccmode = cs.ecc_mode; + bank[loop*2+1].endaddr=(dbc & 0x03FF)<<25; + bank[loop*2+1].mtype = BANK_EDO; + bank[loop*2+1].eccmode = cs.ecc_mode; + lastaddr=(dbc & 0x03FF)<<25; + } + } + } +} + +/* there seems to be NO WAY to distinguish 440zx from 440bx!! >B( */ +static void probe_440bx(void) +{ + int loop, dramc, errcmd; + u32 nbxcfg; + int m_mem[] = { BANK_EDO, BANK_SDR, BANK_RDR, BANK_RESERVED }; + int ddim[] = { ECC_NONE, ECC_DETECT, ECC_CORRECT, ECC_AUTO }; + nbxcfg = pci_word(0x50) | (pci_word(0x52)<<16); + dramc = pci_byte(0x57); + errcmd = pci_byte(0x90); + cs.ecc_cap = ECC_AUTO; + cs.ecc_mode = ddim[(nbxcfg>>7)&3]; + + cs.check = generic_check; + cs.MBE_flag_address = 0x91; + cs.MBE_flag_shift = 4; + cs.MBE_flag_mask = 1; + cs.MBE_row_shift = 5; + cs.MBE_row_mask = 7; + cs.SBE_flag_address = 0x91; + cs.SBE_flag_shift = 0; + cs.SBE_flag_mask = 1; + cs.SBE_row_shift = 1; + cs.SBE_row_mask = 7; + + cs.MBE_err_address1 = 80; + cs.MBE_err_shift1 = 0; + cs.MBE_err_address2 = 82; + cs.MBE_err_shift2 = 16; + cs.MBE_err_mask = 0xFFFFF000; + + cs.SBE_err_address1 = 80; + cs.SBE_err_shift1 = 0; + cs.SBE_err_address2 = 82; + cs.SBE_err_shift2 = 16; + cs.SBE_err_mask = 0xFFFFF000; + + for(loop=0;loop<8;loop++) + { + bank[loop].endaddr=(unsigned long)pci_byte(0x60+loop)<<23; + /* 440bx doesnt allow mixing memory types. bleah. */ + bank[loop].mtype = m_mem[(dramc>>3)&3]; + /* yes, bit is _zero_ if ecc is _enabled_. */ + bank[loop].eccmode = !((nbxcfg>>(loop+24))&1); + } +} + +/* no way to tell 440ex from 440lx!? grr. */ +static void probe_440lx(void) +{ + int loop, drt, paccfg, errcmd; + int m_mem[] = { BANK_EDO, BANK_RESERVED, BANK_SDR, BANK_EMPTY }; + int ddim[] = { ECC_NONE, ECC_DETECT, ECC_RESERVED, ECC_CORRECT } ; + paccfg = pci_word(0x50); + drt = pci_byte(0x55) | (pci_byte(0x56)<<8); + errcmd = pci_byte(0x90); + /* 440ex doesnt support ecc, but no way to tell if its 440ex! */ + cs.ecc_cap = ECC_CORRECT; + cs.ecc_mode = ddim[(paccfg>>7)&3]; + + cs.check = generic_check; + cs.MBE_flag_address = 0x91; + cs.MBE_flag_shift = 4; + cs.MBE_flag_mask = 1; + cs.MBE_row_shift = 5; + cs.MBE_row_mask = 7; + cs.SBE_flag_address = 0x91; + cs.SBE_flag_shift = 0; + cs.SBE_flag_mask = 1; + cs.SBE_row_shift = 1; + cs.SBE_row_mask = 7; + + for(loop=0;loop<8;loop++) + { + bank[loop].endaddr = (unsigned long)pci_byte(0x60+loop)<<23; + bank[loop].mtype = m_mem[(drt>>(loop*2))&3]; + bank[loop].eccmode = (cs.ecc_mode != 0); + } +} + +static void probe_440fx(void) +{ + int loop, drt, pmccfg, errcmd; + int m_mem[] = { BANK_FPM, BANK_EDO, BANK_BEDO, BANK_EMPTY }; + int ddim[] = { ECC_NONE, ECC_PARITY, ECC_DETECT, ECC_CORRECT }; + pmccfg = pci_word(0x50); + drt = pci_byte(0x55) | (pci_byte(0x56)<<8); + errcmd = pci_byte(0x90); + for(loop=0;loop<8;loop++) + { + bank[loop].endaddr=(unsigned long)pci_byte(0x60+loop)<<23; + bank[loop].mtype = m_mem[(drt>>(loop*2))&3]; + } + cs.ecc_cap = ECC_CORRECT; + cs.ecc_mode = ddim[(pmccfg>>4)&3]; + + cs.check = generic_check; + cs.MBE_flag_address = 0x91; + cs.MBE_flag_shift = 4; + cs.MBE_flag_mask = 1; + cs.MBE_row_shift = 5; + cs.MBE_row_mask = 7; + cs.SBE_flag_address = 0x91; + cs.SBE_flag_shift = 0; + cs.SBE_flag_mask = 1; + cs.SBE_row_shift = 1; + cs.SBE_row_mask = 7; +} + +static void probe_430hx(void) +{ + int pcicmd, pcon, errcmd, drt, loop; + pcicmd = pci_word(0x4); + pcon = pci_byte(0x50); + drt = pci_byte(0x68); + errcmd = pci_byte(0x90); + cs.ecc_cap = ECC_CORRECT; + cs.ecc_mode = (pcon>>7)&1 ? ECC_CORRECT : ECC_PARITY; + + cs.check = generic_check; + cs.MBE_flag_address = 0x91; + cs.MBE_flag_shift = 4; + cs.MBE_flag_mask = 1; + cs.MBE_row_shift = 5; + cs.MBE_row_mask = 7; + cs.SBE_flag_address = 0x91; + cs.SBE_flag_shift = 0; + cs.SBE_flag_mask = 1; + cs.SBE_row_shift = 1; + cs.SBE_row_mask = 7; + + for(loop=0;loop<8;loop++) + { + bank[loop].endaddr=(unsigned long)pci_byte(0x60+loop)<<22; + bank[loop].mtype = (drt>>loop)&1 ? BANK_EDO : BANK_FPM; + bank[loop].eccmode = cs.ecc_mode; + } +} + +static void check_820(void) +{ + int errsts = pci_word(0xC8); + if ((errsts>>1) & 1) + { + u32 eap = pci_dword(0xC4) & 0xFFFFF000; + char errsyn = pci_byte(0xC7); + printk(KERN_ERR "ECC: MBE at memory address %lx\n row %d syndrome %x", + (long unsigned int)eap, find_row(eap), errsyn); + scrub_needed = 2; + scrub_row = find_row(eap); + bank[scrub_row].mbecount++; + } + if ((errsts) & 1) + { + u32 eap = pci_dword(0xC4) & 0xFFFFF000; + char errsyn = pci_byte(0xC7); + printk(KERN_ERR "ECC: SBE at memory address %lx row %d syndrome %x\n", + (long unsigned int)eap, find_row(eap), errsyn); + scrub_needed = 1; + scrub_row = find_row(eap); + bank[scrub_row].sbecount++; + } +} + +void probe_820(void) +{ + int loop, mchcfg; + int ddim[] = { ECC_NONE, ECC_DETECT, ECC_CORRECT, ECC_AUTO }; + mchcfg = pci_word(0xBE); + cs.ecc_cap = ECC_AUTO; + cs.ecc_mode = ddim[(mchcfg>>7)&3]; + + cs.check = check_820; + + for(loop=0;loop<8;loop++) + { + bank[loop].endaddr=(unsigned long)pci_word(0x60+(loop*2))<<23; + bank[loop].mtype = BANK_RMBS; + bank[loop].eccmode = cs.ecc_mode; + } +} + +static void check_840(void) +{ + int errsts = pci_word(0xE2); + if ((errsts>>9) & 1) + { + u32 eap = pci_dword(0xE4) & 0xFFFFF800; + printk(KERN_ERR "ECC: MBE at memory address %lx\n row %d syndrome %x", + (long unsigned int)eap, find_row(eap), errsts&0xFF); + scrub_needed = 2; + scrub_row = find_row(eap); + bank[scrub_row].mbecount++; + } + if ((errsts>>10) & 1) + { + u32 eap = pci_dword(0xE4) & 0xFFFFF800; + printk(KERN_ERR "ECC: SBE at memory address %lx row %d syndrome %x\n", + (long unsigned int)eap, find_row(eap), errsts&0xFF); + scrub_needed = 1; + scrub_row = find_row(eap); + bank[scrub_row].sbecount++; + } +} + +static void probe_840(void) +{ + int loop, mchcfg; + int ddim[] = { ECC_NONE, ECC_RESERVED, ECC_CORRECT, ECC_AUTO }; + mchcfg = pci_word(0x50); + cs.ecc_cap = ECC_AUTO; + cs.ecc_mode = ddim[(mchcfg>>7)&3]; + + cs.check = check_840; + + for(loop=0;loop<16;loop++) + { + bank[loop].endaddr=(unsigned long)pci_word(0x60+(loop*2))<<23; + bank[loop].mtype = BANK_RMBS; + bank[loop].eccmode = cs.ecc_mode; + } +} + +static void check_845(void) +{ + int errsts = pci_word(0xC8); + if ((errsts>>1) & 1) + { + u32 eap = (pci_dword(0x8C) & 0xFFFFFFFE)<<4; + char derrsyn = pci_byte(0x86); + printk(KERN_ERR "ECC: MBE at memory address %lx\n row %d syndrome %x", + (long unsigned int)eap, find_row(eap), derrsyn); + scrub_needed = 2; + scrub_row = find_row(eap); + bank[scrub_row].mbecount++; + } + if (errsts & 1) + { + u32 eap = (pci_dword(0x8C) & 0xFFFFFFFE)<<4; + char derrsyn = pci_byte(0x86); + printk(KERN_ERR "ECC: SBE at memory address %lx row %d syndrome %x\n", + (long unsigned int)eap, find_row(eap), derrsyn); + scrub_needed = 1; + scrub_row = find_row(eap); + bank[scrub_row].sbecount++; + } +} + +static void probe_845(void) +{ + int loop; + u32 drc; + int m_mem[] = { BANK_SDR, BANK_DDR, BANK_RESERVED, BANK_RESERVED }; + int ddim[] = { ECC_NONE, ECC_RESERVED, ECC_CORRECT, ECC_RESERVED }; + drc = pci_dword(0x7C); + cs.ecc_cap = ECC_CORRECT; + cs.ecc_mode = ddim[(drc>>20)&3]; + + cs.check = check_845; + + for(loop=0;loop<8;loop++) + { + bank[loop].endaddr=(unsigned long)pci_byte(0x60+loop)<<24; + bank[loop].mtype = m_mem[drc&3]; + bank[loop].eccmode = cs.ecc_mode; + } +} + +static void check_850(void) +{ + int errsts = pci_word(0xC8); + if ((errsts>>1) & 1) + { + u32 eap = pci_dword(0xE4) & 0xFFFFE000; + char derrsyn = pci_word(0xE2) & 0x7F; + printk(KERN_ERR "ECC: MBE at memory address %lx\n row %d syndrome %x", + (long unsigned int)eap, find_row(eap), derrsyn); + scrub_needed = 2; + scrub_row = find_row(eap); + bank[scrub_row].mbecount++; + } + if (errsts & 1) + { + u32 eap = pci_dword(0xE4) & 0xFFFFE000; + char derrsyn = pci_byte(0xE2) & 0x7F; + printk(KERN_ERR "ECC: SBE at memory address %lx row %d syndrome %x\n", + (long unsigned int)eap, find_row(eap), derrsyn); + scrub_needed = 1; + scrub_row = find_row(eap); + bank[scrub_row].sbecount++; + } +} + +static void probe_850(void) +{ + int loop; + int mchcfg; + int ddim[] = { ECC_NONE, ECC_RESERVED, ECC_CORRECT, ECC_RESERVED }; + mchcfg = pci_word(0x50); + cs.ecc_cap = ECC_CORRECT; + cs.ecc_mode = ddim[(mchcfg>>7)&3]; + + cs.check = check_850; + + for(loop=0;loop<16;loop++) + { + bank[loop].endaddr=(unsigned long)pci_word(0x60+(loop*2))<<24; + bank[loop].mtype = BANK_RMBS; + bank[loop].eccmode = cs.ecc_mode; + } +} + +static void check_860(void) +{ + int errsts = pci_word(0xC8); + if ((errsts>>1) & 1) + { + u32 eap = (pci_dword(0xE4) & 0xFFFFFE00)<<2; + char derrsyn = pci_word(0xE2) & 0x7F; + printk(KERN_ERR "ECC: MBE at memory address %lx\n row %d syndrome %x", + (long unsigned int)eap, find_row(eap), derrsyn); + scrub_needed = 2; + scrub_row = find_row(eap); + bank[scrub_row].mbecount++; + } + if (errsts & 1) + { + u32 eap = (pci_dword(0x8C) & 0xFFFFFE00)<<2; + char derrsyn = pci_byte(0xE2) & 0x7F; + printk(KERN_ERR "ECC: SBE at memory address %lx row %d syndrome %x\n", + (long unsigned int)eap, find_row(eap), derrsyn); + scrub_needed = 1; + scrub_row = find_row(eap); + bank[scrub_row].sbecount++; + } +} + +static void probe_860(void) +{ + int loop; + u32 drc; + int ddim[] = { ECC_NONE, ECC_RESERVED, ECC_CORRECT, ECC_RESERVED }; + drc = pci_dword(0x7C); + cs.ecc_cap = ECC_CORRECT; + cs.ecc_mode = ddim[(drc>>19)&3]; + + cs.check = check_860; + + for(loop=0;loop<16;loop++) + { + bank[loop].endaddr=(unsigned long)pci_word(0x60+(loop*2))<<23; + bank[loop].mtype = BANK_RMBS; + bank[loop].eccmode = cs.ecc_mode; + } +} + +static void check_amd751(void) +{ + int eccstat = pci_word(0x58); + int csbits = eccstat & 0x3F; + int row; + switch (csbits) + { + case 1: row = 0; break; + case 2: row = 1; break; + case 4: row = 2; break; + case 8: row = 3; break; + case 16: row = 4; break; + case 32: row = 5; break; + default: row = 6; + } + switch ((eccstat>>8)&3) + { + case 1: printk(KERN_ERR "ECC: MBE Detected in DRAM row %d\n", row); + scrub_needed=2; + bank[row].mbecount++; + break; + case 2: printk(KERN_ERR "ECC: SBE Detected in DRAM row %d\n", row); + scrub_needed=1; + bank[row].sbecount++; + break; + case 3: printk(KERN_ERR "ECC: SBE and MBE Detected in DRAM row %d\n", row); + scrub_needed=1; + bank[row].sbecount++; + break; + } + if (scrub_needed) + { + /* + * clear error flag bits that were set by writing 0 to them + * we hope the error was a fluke or something :) + */ + int value = eccstat & 0xFCFF; + pci_write_config_word(bridge, 0x58, value); + scrub_needed = 0; + } +} + +static void probe_amd751(void) +{ + int loop; + cs.ecc_cap = ECC_CORRECT; + cs.ecc_mode = (pci_byte(0x5a)>>2)&1 ? ECC_CORRECT : ECC_NONE; + cs.check = check_amd751; + for(loop=0;loop<6;loop++) + { + unsigned flag = pci_byte(0x40+loop*2); + /* bank address mask. */ + unsigned mask = (flag&0x7F)>>1; + /* actually starting address */ + bank[loop].endaddr=(unsigned long)(pci_word(0x40+loop*2)&0xFF80)<<(23-7); + /* only when bank is populated */ + if((flag&1)&&(mask!=0)){ + /* mask+1 * 8mb appears to be bank size */ + bank[loop].endaddr += (mask + 1) * 8 * (1024 * 1024); /* -1? */ + } + bank[loop].mtype = flag&1 ? BANK_SDR : BANK_EMPTY; + bank[loop].eccmode = cs.ecc_mode; + } +} + +static void check_amd76x(void) +{ + unsigned long eccstat = pci_dword(0x48); + if(eccstat & 0x100) + { + int row = (eccstat >> 4) & 0xf; + printk(KERN_ERR "ECC: MBE Detected in DRAM row %d\n", row); + bank[row].mbecount++; + } + if(eccstat & 0x200) + { + int row = eccstat & 0xf; + printk(KERN_ERR "ECC: SBE Detected in DRAM row %d\n", row); + bank[row].sbecount++; + } + if(eccstat & 0x300) /* clear ECC_Status */ + pci_write_config_dword(bridge, 0x48, eccstat); +} + +static void probe_amd76x(void) +{ + static const int modetab[] = {ECC_NONE, ECC_DETECT, ECC_CORRECT, ECC_AUTO}; + int loop; + unsigned long addr = 0; + cs.ecc_cap = ECC_AUTO; + cs.ecc_mode = modetab [(pci_dword(0x48)>>10)&3]; + cs.check = check_amd76x; + + for(loop=0;loop<8;loop++) + { + unsigned long r = pci_dword(0xc0+(loop*4)); + bank[loop].mtype = r & 1 ? BANK_DDR : BANK_EMPTY; + if (r & 1) + addr += ((r & 0xff80) << 16) + 0x800000; + bank[loop].endaddr = addr; + bank[loop].eccmode = cs.ecc_mode != ECC_NONE; + } + pci_write_config_dword(bridge, 0x48, pci_dword(0x48) | 0x300); +} + +static void probe_sis(void) +{ + int loop; + u32 endaddr; + int m_mem[] = { BANK_FPM, BANK_EDO, BANK_RESERVED, BANK_SDR }; + int dramsize[] = { 256, 1024, 4096, 16384, 1024, 2048, 4096, + 8192, 512, 1024, 2048, 0, 0, 0, 0, 0 }; + int sdramsize[] = { 1024, 4096, 4096, 8192, 2048, 8192, + 8192, 16384, 4096, 16384, 16384, 32768, 2048, 0, 0, 0 }; + + cs.ecc_cap = ECC_CORRECT; + + cs.check = generic_check; + + cs.MBE_flag_address = 0x64; + cs.MBE_flag_shift = 4; + cs.MBE_flag_mask = 1; + cs.MBE_row_shift = 5; + cs.MBE_row_mask = 7; + + cs.SBE_flag_address = 0x64; + cs.SBE_flag_shift = 3; + cs.SBE_flag_mask = 1; + cs.SBE_row_shift = 5; + cs.SBE_row_mask = 7; + + cs.MBE_err_address1 = 0x64; + cs.MBE_err_shift1 = 0; + cs.MBE_err_address2 = 0x66; + cs.MBE_err_shift2 = 16; + cs.MBE_err_mask = 0xFFFFF000; + + cs.SBE_err_address1 = 0x64; + cs.SBE_err_shift1 = 0; + cs.SBE_err_address2 = 0x66; + cs.SBE_err_shift2 = 16; + cs.MBE_err_mask = 0xFFFFF000; + + endaddr = 0; + for(loop=0;loop<3;loop++) + { + /* populated bank? */ + if ((pci_byte(0x63)>>loop)&1) + { + u32 banksize; + int mtype = pci_byte(0x60+loop); + + bank[loop*2].mtype = m_mem[(mtype>>6)&3]; + if(bank[loop*2].mtype == BANK_SDR) + { + banksize = sdramsize[mtype&15]*1024; + } else { + banksize = dramsize[mtype&15]*1024; + } + endaddr += banksize; + bank[loop*2].endaddr = endaddr; + /* double sided dimm? */ + if ((mtype>>5)&1) + { + bank[(loop*2)+1].mtype = bank[loop*2].mtype; + endaddr += banksize; + bank[(loop*2)+1].endaddr = endaddr; + } + } else { + bank[loop*2].mtype = BANK_EMPTY; + bank[(loop*2)+1].mtype = BANK_EMPTY; + bank[loop*2].endaddr = endaddr; + bank[(loop*2)+1].endaddr = endaddr; + } + } + cs.ecc_mode = ECC_NONE; + for(loop=0;loop<6;loop++) + { + int eccmode = (pci_byte(0x74)>>loop)&1; + bank[loop].eccmode = eccmode; + if(eccmode) + cs.ecc_mode = ECC_CORRECT; + } +} + +static void probe_aladdin4(void) +{ + int loop; + int m_mem[] = { BANK_FPM, BANK_EDO, BANK_RESERVED, BANK_SDR }; + cs.ecc_cap = ECC_CORRECT; + cs.ecc_mode = pci_byte(0x49)&1 ? ECC_CORRECT : ECC_PARITY; + + cs.check = generic_check; + + cs.MBE_flag_address = 0x4a; + cs.MBE_flag_shift = 4; + cs.MBE_flag_mask = 1; + cs.MBE_row_shift = 5; + cs.MBE_row_mask = 7; + + cs.SBE_flag_address = 0x4a; + cs.SBE_flag_shift = 0; + cs.SBE_flag_mask = 1; + cs.SBE_row_shift = 1; + cs.SBE_row_mask = 7; + + for(loop=0;loop<8;loop++) + { + bank[loop].endaddr = (unsigned long)(pci_byte(0x61+(loop*2))&15)<<27|(pci_byte(0x60+(loop*2))<<20); + bank[loop].mtype = m_mem[(pci_byte(0x61+(loop*2))>>4)&3]; + if (cs.ecc_mode == ECC_CORRECT) { + bank[loop].eccmode = 1; + } else { + bank[loop].eccmode = 0; + } + } +} + +static void probe_aladdin5(void) +{ + int loop; + int m_mem[] = { BANK_FPM, BANK_EDO, BANK_RDR, BANK_SDR }; + cs.ecc_cap = ECC_CORRECT; + cs.ecc_mode = pci_byte(0x50)&1 ? ECC_CORRECT : ECC_PARITY; + + cs.check = generic_check; + + cs.MBE_flag_address = 0x51; + cs.MBE_flag_shift = 4; + cs.MBE_flag_mask = 1; + cs.MBE_row_shift = 5; + cs.MBE_row_mask = 7; + + cs.SBE_flag_address = 0x51; + cs.SBE_flag_shift = 0; + cs.SBE_flag_mask = 1; + cs.SBE_row_shift = 1; + cs.SBE_row_mask = 7; + + for(loop=0;loop<8;loop++) + { + /* DBxCII not disabled address mapping? */ + if(pci_byte(0x61+(loop*2))&0xF0) + { + /* endaddr always 1 unit low, granularity 1mb */ + bank[loop].endaddr = (unsigned long)((pci_byte(0x61+(loop*2))&15)<<27|(pci_byte(0x60+(loop*2))<<20))+1048576; + bank[loop].mtype = m_mem[(pci_byte(0x61+(loop*2))>>4)&3]; + if (cs.ecc_mode == ECC_CORRECT) { + bank[loop].eccmode = 1; + } else { + bank[loop].eccmode = 0; + } + } + } +} + +#if 0 +/* + * memory scrubber routines, not ready to be used yet... + */ +/* start at 16mb */ +static unsigned long start = 4096; +static unsigned long pages = 1; +/* other architectures have different page sizes... */ +static unsigned long step = 4096; + +static char buff[8192] = {0,}; + +/* + * Michael's page scrubber routine + */ +static void scrub_page(unsigned long volatile * p) +{ + int i; + int len, err = 0; + unsigned long *q; + q = (unsigned long *) ((((int)buff)+4095) & ~4095); + + if (((int)p) >= 640 * 1024 && ((int)p) < 1024 * 1024) + return; + + cli(); /* kill interrupts */ + err = pci_byte(0x91); + outb(0x11, PCI_DATA + 1); /* clear the memory error indicator */ + + for (i = 0; i < step / 4 ; ++i) + q[i] = p[i]; + for (i = 0; i < step / 4 ; ++i) + p[i] = q[i]; + err = inb(PCI_DATA + 1); + sti(); + if (err & 0x11) { + printk(KERN_ERR "ECC: Memory error @ %08x (0x%02x)\n", p, err); + return 1; + } + return 0; +} + +static void scrub(void) +{ + int i,j = 0; + for (i = 0; i < pages; ++i) { + j = scrub_page(start); + start += step; + } + if (!j) { + /* + * Hmm... This is probably a very bad situation. + */ + printk(KERN_ERR "ECC: Scrubbed, no errors found?!\n"); + scrub_needed=0; + return; + } + if (scrub_needed==2) { + /* + * TODO: We should determine what process owns the memory + * and send a SIGBUS to it. We should also printk something + * along the lines of + * "ECC: Process (PID) killed with SIGBUS due to uncorrectable memory error at 0xDEADBEEF" + */ + scrub_needed=0; + } +} +#endif + +static void check_ecc(unsigned long); + +/* + * Check ECC status every second. + * SMP safe, doesn't use NMI, and auto-rate-limits. + */ +static void check_ecc(unsigned long dummy) +{ + spin_lock (&ecc_lock); + + if (!scrub_needed) + if (cs.check) + cs.check(); + if (cs.clear_err) + cs.clear_err(); + + ecc_timer.expires = jiffies + HZ; + add_timer(&ecc_timer); + + spin_unlock (&ecc_lock); +} + +#ifdef CONFIG_PROC_FS +static char *ecc_type[] = { + "None", "Reserved", "Parity checking", "ECC detection", + "ECC detection and correction", "ECC with hardware scrubber" +}; + +static char *dram_type[] = { + "Empty", "Reserved", "Unknown", "FPM", "EDO", + "BEDO", "SDR", "DDR", "RDR", "RMBS" +}; + +static int ecc_proc_output(char *buf) +{ + int loop; + u32 last_mem, mem_end; + char *p = buf; + + p += sprintf(p, "Chipset ECC capability : %s\n", ecc_type[cs.ecc_cap]); + p += sprintf(p, "Current ECC mode : %s\n", ecc_type[cs.ecc_mode]); + p += sprintf(p, "Bank\tSize\tType\tECC\tSBE\tMBE\n"); + + mem_end = 0; + for (loop=0;loopmem_end) { + p += sprintf(p, "%d\t", loop); + p += sprintf(p, "%d Mi\t", + (int)((last_mem-mem_end)>>20)); + p += sprintf(p, "%s\t", dram_type[bank[loop].mtype]); + p += sprintf(p, "%s\t", + bank[loop].eccmode ? "Y" : "N"); + p += sprintf(p, "%ld\t", + (unsigned long) bank[loop].sbecount); + p += sprintf(p, "%ld\n", + (unsigned long) bank[loop].mbecount); + mem_end=last_mem; + } + } + p += sprintf(p, "Total\t%d Mi\n", (int)(mem_end>>20)); + + return p - buf; +} + +static int ecc_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len; + + spin_lock (&ecc_lock); + + len = ecc_proc_output (page); + if (len <= off+count) *eof = 1; + *start = page + off; + len -= off; + if (len>count) len = count; + if (len<0) len = 0; + + spin_unlock (&ecc_lock); + + return len; +} +#endif + +struct cs_table_struct { + int vendor; /* pci vendor id */ + int device; /* pci device id */ + void (*check)(void); /* pointer to chipset probing routine */ + char *csname; /* chipset name */ +}; + +static struct cs_table_struct cs_table[] = { + /* AMD */ + { 0x1022, 0x7006, probe_amd751, "amd751" }, + { 0x1022, 0x700c, probe_amd76x, "amd760mp" }, + { 0x1022, 0x700e, probe_amd76x, "amd760" }, + /* Motorola */ + { 0x1057, 0x4802, 0, "falcon" }, /* not yet supported */ + /* Apple */ + { 0x106b, 0x0001, 0, "bandit" }, /* not yet supported */ + /* SiS */ + { 0x1039, 0x0600, probe_sis, "sis600" }, /* programatically same as 5600 */ + { 0x1039, 0x0620, 0, "sis620" }, /* 620 doesnt support ecc */ + { 0x1039, 0x5600, probe_sis, "sis5600" }, + /* ALi */ + { 0x10b9, 0x1531, probe_aladdin4, "m1531" }, + { 0x10b9, 0x1541, probe_aladdin5, "m1541" }, + { 0x10b9, 0x1647, 0, "m1647" }, /* magik1 - no documentation yet */ + /* VIA */ + { 0x1106, 0x0305, probe_via, "vt8363" }, /* kt133/km133 */ + { 0x1106, 0x0391, probe_via, "vt8371" }, /* kx133 */ + { 0x1106, 0x0501, probe_via, "vt8501" }, /* mvp4 */ + { 0x1106, 0x0585, probe_via, "vt82c585" }, /* vp/vpx */ + { 0x1106, 0x0595, probe_via, "vt82c597" }, /* vp2 */ + { 0x1106, 0x0597, probe_via, "vt82c597" }, /* vp3 */ + { 0x1106, 0x0598, probe_via, "vt82c598" }, /* mvp3 */ + { 0x1106, 0x0691, probe_via, "vt82c691" }, /* pro133/pro133a */ + { 0x1106, 0x0693, probe_via, "vt82c693" }, /* pro+ */ + { 0x1106, 0x0694, probe_via, "vt82c694" }, /* pro133a */ + { 0x1106, 0x3099, probe_via, "vt82366" }, /* kt266 */ + /* Serverworks */ + { 0x1166, 0x0008, probe_serverworks, "CNB20HE" }, /* CNB20HE - serverset iii he */ + { 0x1166, 0x0009, probe_serverworks, "CNB20LE" }, /* serverset iii le */ + /* Intel */ + { 0x8086, 0x1130, 0, "i815" }, /* doesnt support ecc */ + { 0x8086, 0x122d, 0, "430fx" }, /* doesnt support ecc */ + { 0x8086, 0x1237, probe_440fx, "440fx" }, + { 0x8086, 0x1250, probe_430hx, "430hx" }, + { 0x8086, 0x2500, probe_820, "i820" }, + { 0x8086, 0x1A21, probe_840, "i840" }, + { 0x8086, 0x1A30, probe_845, "i845" }, + { 0x8086, 0x2530, probe_850, "i850" }, + { 0x8086, 0x2531, probe_860, "i860" }, + { 0x8086, 0x7030, 0, "430vx" }, /* doesnt support ecc */ + { 0x8086, 0x7120, 0, "i810" }, /* 810 doesnt support ecc */ + { 0x8086, 0x7122, 0, "i810dc" }, + { 0x8086, 0x7124, 0, "i810e" }, /* 810e doesnt support ecc */ + { 0x8086, 0x7180, probe_440lx, "440lx/ex" }, + { 0x8086, 0x7190, probe_440bx, "440bx/zx" }, + { 0x8086, 0x7192, probe_440bx, "440bx/zx" }, + { 0x8086, 0x71A0, probe_440bx, "440bx/gx" }, + { 0x8086, 0x71A2, probe_440bx, "440bx/gx" }, + { 0x8086, 0x84C5, probe_450gx, "450gx" }, + { 0x8086, 0x84CA, probe_450nx, "450nx" }, + { 0, 0, 0 } +}; + +static int find_chipset(void) { + + if (!pci_present()) { + printk(KERN_ERR "ECC: No PCI bus.\n"); + return 0; + } + + while ((bridge = pci_find_class(PCI_CLASS_BRIDGE_HOST << 8, bridge))) + { + int loop = 0; + pci_read_config_word(bridge, PCI_VENDOR_ID, &vendor); + pci_read_config_word(bridge, PCI_DEVICE_ID, &device); + while(cs_table[loop].vendor) + { + if( (vendor == cs_table[loop].vendor) && + (device == cs_table[loop].device) ) + { + if(cs_table[loop].check) + { + cs_table[loop].check(); + printk(KERN_ERR "ECC: Found memory controller %x:%x - %s\n", vendor, device, cs_table[loop].csname); + return 1; + } else { + printk(KERN_WARNING "ECC: Unsupported device %x:%x.\n", vendor, device); + return 0; + } + } + loop++; + } + printk(KERN_DEBUG "ECC: Unknown device %x:%x.\n", vendor, device); + } + printk(KERN_ERR "ECC: Can't find host bridge.\n"); + return 0; +} + +#if (LINUX_VERSION_CODE < 0x020401) +static int init_module(void) +#else +static int __init ecc_init(void) +#endif +{ + int loop; +#if (LINUX_VERSION_CODE < 0x020401) +#ifdef CONFIG_PROC_FS + struct proc_dir_entry *ent; +#endif +#endif + spin_lock (&ecc_lock); + + printk(KERN_INFO "ECC: monitor version %s\n", ECC_VER); + + for (loop=0;loopnlink = 1; + ent->read_proc = ecc_read_proc; + } +#else + create_proc_read_entry("ram", 0, 0, ecc_read_proc, 0); +#endif +#endif + + init_timer(&ecc_timer); + ecc_timer.function = check_ecc; + ecc_timer.expires = jiffies + HZ; + add_timer(&ecc_timer); + + spin_unlock (&ecc_lock); + + return 0; +} + +#if (LINUX_VERSION_CODE < 0x020401) +static void cleanup_module(void) +#else +static void __exit ecc_exit(void) +#endif +{ + spin_lock (&ecc_lock); + +#ifdef CONFIG_PROC_FS + remove_proc_entry("ram", 0); +#endif + + del_timer(&ecc_timer); + + printk(KERN_DEBUG "ECC: unloaded.\n"); + + spin_unlock (&ecc_lock); +} + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("Dan Hollis et al"); +MODULE_DESCRIPTION("ECC Module"); +#if (LINUX_VERSION_CODE > 0x020408) +MODULE_LICENSE("GPL"); + +module_init(ecc_init); +module_exit(ecc_exit); +#endif --- linux/drivers/char/Config.in.dgecc Thu May 2 18:21:20 2002 +++ linux/drivers/char/Config.in Thu May 2 19:15:27 2002 @@ -4,6 +4,7 @@ mainmenu_option next_comment comment 'Character devices' +tristate 'ECC memory monitoring' CONFIG_ECC bool 'Virtual terminal' CONFIG_VT if [ "$CONFIG_VT" = "y" ]; then bool ' Support for console on virtual terminal' CONFIG_VT_CONSOLE --- linux/drivers/char/Makefile.dgecc Thu May 2 18:21:20 2002 +++ linux/drivers/char/Makefile Thu May 2 19:15:54 2002 @@ -151,6 +151,7 @@ endif obj-$(CONFIG_MAGIC_SYSRQ) += sysrq.o +obj-$(CONFIG_ECC) += ecc.o obj-$(CONFIG_ATARI_DSP56K) += dsp56k.o obj-$(CONFIG_ROCKETPORT) += rocket.o obj-$(CONFIG_MOXA_SMARTIO) += mxser.o