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