162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Intel 82443BX/GX (440BX/GX chipset) Memory Controller EDAC kernel 362306a36Sopenharmony_ci * module (C) 2006 Tim Small 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * This file may be distributed under the terms of the GNU General 662306a36Sopenharmony_ci * Public License. 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Written by Tim Small <tim@buttersideup.com>, based on work by Linux 962306a36Sopenharmony_ci * Networx, Thayne Harbaugh, Dan Hollis <goemon at anime dot net> and 1062306a36Sopenharmony_ci * others. 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * 440GX fix by Jason Uhlenkott <juhlenko@akamai.com>. 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * Written with reference to 82443BX Host Bridge Datasheet: 1562306a36Sopenharmony_ci * http://download.intel.com/design/chipsets/datashts/29063301.pdf 1662306a36Sopenharmony_ci * references to this document given in []. 1762306a36Sopenharmony_ci * 1862306a36Sopenharmony_ci * This module doesn't support the 440LX, but it may be possible to 1962306a36Sopenharmony_ci * make it do so (the 440LX's register definitions are different, but 2062306a36Sopenharmony_ci * not completely so - I haven't studied them in enough detail to know 2162306a36Sopenharmony_ci * how easy this would be). 2262306a36Sopenharmony_ci */ 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#include <linux/module.h> 2562306a36Sopenharmony_ci#include <linux/init.h> 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#include <linux/pci.h> 2862306a36Sopenharmony_ci#include <linux/pci_ids.h> 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#include <linux/edac.h> 3262306a36Sopenharmony_ci#include "edac_module.h" 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#define EDAC_MOD_STR "i82443bxgx_edac" 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci/* The 82443BX supports SDRAM, or EDO (EDO for mobile only), "Memory 3762306a36Sopenharmony_ci * Size: 8 MB to 512 MB (1GB with Registered DIMMs) with eight memory 3862306a36Sopenharmony_ci * rows" "The 82443BX supports multiple-bit error detection and 3962306a36Sopenharmony_ci * single-bit error correction when ECC mode is enabled and 4062306a36Sopenharmony_ci * single/multi-bit error detection when correction is disabled. 4162306a36Sopenharmony_ci * During writes to the DRAM, the 82443BX generates ECC for the data 4262306a36Sopenharmony_ci * on a QWord basis. Partial QWord writes require a read-modify-write 4362306a36Sopenharmony_ci * cycle when ECC is enabled." 4462306a36Sopenharmony_ci*/ 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci/* "Additionally, the 82443BX ensures that the data is corrected in 4762306a36Sopenharmony_ci * main memory so that accumulation of errors is prevented. Another 4862306a36Sopenharmony_ci * error within the same QWord would result in a double-bit error 4962306a36Sopenharmony_ci * which is unrecoverable. This is known as hardware scrubbing since 5062306a36Sopenharmony_ci * it requires no software intervention to correct the data in memory." 5162306a36Sopenharmony_ci */ 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci/* [Also see page 100 (section 4.3), "DRAM Interface"] 5462306a36Sopenharmony_ci * [Also see page 112 (section 4.6.1.4), ECC] 5562306a36Sopenharmony_ci */ 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci#define I82443BXGX_NR_CSROWS 8 5862306a36Sopenharmony_ci#define I82443BXGX_NR_CHANS 1 5962306a36Sopenharmony_ci#define I82443BXGX_NR_DIMMS 4 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci/* 82443 PCI Device 0 */ 6262306a36Sopenharmony_ci#define I82443BXGX_NBXCFG 0x50 /* 32bit register starting at this PCI 6362306a36Sopenharmony_ci * config space offset */ 6462306a36Sopenharmony_ci#define I82443BXGX_NBXCFG_OFFSET_NON_ECCROW 24 /* Array of bits, zero if 6562306a36Sopenharmony_ci * row is non-ECC */ 6662306a36Sopenharmony_ci#define I82443BXGX_NBXCFG_OFFSET_DRAM_FREQ 12 /* 2 bits,00=100MHz,10=66 MHz */ 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci#define I82443BXGX_NBXCFG_OFFSET_DRAM_INTEGRITY 7 /* 2 bits: */ 6962306a36Sopenharmony_ci#define I82443BXGX_NBXCFG_INTEGRITY_NONE 0x0 /* 00 = Non-ECC */ 7062306a36Sopenharmony_ci#define I82443BXGX_NBXCFG_INTEGRITY_EC 0x1 /* 01 = EC (only) */ 7162306a36Sopenharmony_ci#define I82443BXGX_NBXCFG_INTEGRITY_ECC 0x2 /* 10 = ECC */ 7262306a36Sopenharmony_ci#define I82443BXGX_NBXCFG_INTEGRITY_SCRUB 0x3 /* 11 = ECC + HW Scrub */ 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci#define I82443BXGX_NBXCFG_OFFSET_ECC_DIAG_ENABLE 6 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci/* 82443 PCI Device 0 */ 7762306a36Sopenharmony_ci#define I82443BXGX_EAP 0x80 /* 32bit register starting at this PCI 7862306a36Sopenharmony_ci * config space offset, Error Address 7962306a36Sopenharmony_ci * Pointer Register */ 8062306a36Sopenharmony_ci#define I82443BXGX_EAP_OFFSET_EAP 12 /* High 20 bits of error address */ 8162306a36Sopenharmony_ci#define I82443BXGX_EAP_OFFSET_MBE BIT(1) /* Err at EAP was multi-bit (W1TC) */ 8262306a36Sopenharmony_ci#define I82443BXGX_EAP_OFFSET_SBE BIT(0) /* Err at EAP was single-bit (W1TC) */ 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci#define I82443BXGX_ERRCMD 0x90 /* 8bit register starting at this PCI 8562306a36Sopenharmony_ci * config space offset. */ 8662306a36Sopenharmony_ci#define I82443BXGX_ERRCMD_OFFSET_SERR_ON_MBE BIT(1) /* 1 = enable */ 8762306a36Sopenharmony_ci#define I82443BXGX_ERRCMD_OFFSET_SERR_ON_SBE BIT(0) /* 1 = enable */ 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci#define I82443BXGX_ERRSTS 0x91 /* 16bit register starting at this PCI 9062306a36Sopenharmony_ci * config space offset. */ 9162306a36Sopenharmony_ci#define I82443BXGX_ERRSTS_OFFSET_MBFRE 5 /* 3 bits - first err row multibit */ 9262306a36Sopenharmony_ci#define I82443BXGX_ERRSTS_OFFSET_MEF BIT(4) /* 1 = MBE occurred */ 9362306a36Sopenharmony_ci#define I82443BXGX_ERRSTS_OFFSET_SBFRE 1 /* 3 bits - first err row singlebit */ 9462306a36Sopenharmony_ci#define I82443BXGX_ERRSTS_OFFSET_SEF BIT(0) /* 1 = SBE occurred */ 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci#define I82443BXGX_DRAMC 0x57 /* 8bit register starting at this PCI 9762306a36Sopenharmony_ci * config space offset. */ 9862306a36Sopenharmony_ci#define I82443BXGX_DRAMC_OFFSET_DT 3 /* 2 bits, DRAM Type */ 9962306a36Sopenharmony_ci#define I82443BXGX_DRAMC_DRAM_IS_EDO 0 /* 00 = EDO */ 10062306a36Sopenharmony_ci#define I82443BXGX_DRAMC_DRAM_IS_SDRAM 1 /* 01 = SDRAM */ 10162306a36Sopenharmony_ci#define I82443BXGX_DRAMC_DRAM_IS_RSDRAM 2 /* 10 = Registered SDRAM */ 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci#define I82443BXGX_DRB 0x60 /* 8x 8bit registers starting at this PCI 10462306a36Sopenharmony_ci * config space offset. */ 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci/* FIXME - don't poll when ECC disabled? */ 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_cistruct i82443bxgx_edacmc_error_info { 10962306a36Sopenharmony_ci u32 eap; 11062306a36Sopenharmony_ci}; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_cistatic struct edac_pci_ctl_info *i82443bxgx_pci; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_cistatic struct pci_dev *mci_pdev; /* init dev: in case that AGP code has 11562306a36Sopenharmony_ci * already registered driver 11662306a36Sopenharmony_ci */ 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_cistatic int i82443bxgx_registered = 1; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_cistatic void i82443bxgx_edacmc_get_error_info(struct mem_ctl_info *mci, 12162306a36Sopenharmony_ci struct i82443bxgx_edacmc_error_info 12262306a36Sopenharmony_ci *info) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci struct pci_dev *pdev; 12562306a36Sopenharmony_ci pdev = to_pci_dev(mci->pdev); 12662306a36Sopenharmony_ci pci_read_config_dword(pdev, I82443BXGX_EAP, &info->eap); 12762306a36Sopenharmony_ci if (info->eap & I82443BXGX_EAP_OFFSET_SBE) 12862306a36Sopenharmony_ci /* Clear error to allow next error to be reported [p.61] */ 12962306a36Sopenharmony_ci pci_write_bits32(pdev, I82443BXGX_EAP, 13062306a36Sopenharmony_ci I82443BXGX_EAP_OFFSET_SBE, 13162306a36Sopenharmony_ci I82443BXGX_EAP_OFFSET_SBE); 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci if (info->eap & I82443BXGX_EAP_OFFSET_MBE) 13462306a36Sopenharmony_ci /* Clear error to allow next error to be reported [p.61] */ 13562306a36Sopenharmony_ci pci_write_bits32(pdev, I82443BXGX_EAP, 13662306a36Sopenharmony_ci I82443BXGX_EAP_OFFSET_MBE, 13762306a36Sopenharmony_ci I82443BXGX_EAP_OFFSET_MBE); 13862306a36Sopenharmony_ci} 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_cistatic int i82443bxgx_edacmc_process_error_info(struct mem_ctl_info *mci, 14162306a36Sopenharmony_ci struct 14262306a36Sopenharmony_ci i82443bxgx_edacmc_error_info 14362306a36Sopenharmony_ci *info, int handle_errors) 14462306a36Sopenharmony_ci{ 14562306a36Sopenharmony_ci int error_found = 0; 14662306a36Sopenharmony_ci u32 eapaddr, page, pageoffset; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci /* bits 30:12 hold the 4kb block in which the error occurred 14962306a36Sopenharmony_ci * [p.61] */ 15062306a36Sopenharmony_ci eapaddr = (info->eap & 0xfffff000); 15162306a36Sopenharmony_ci page = eapaddr >> PAGE_SHIFT; 15262306a36Sopenharmony_ci pageoffset = eapaddr - (page << PAGE_SHIFT); 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci if (info->eap & I82443BXGX_EAP_OFFSET_SBE) { 15562306a36Sopenharmony_ci error_found = 1; 15662306a36Sopenharmony_ci if (handle_errors) 15762306a36Sopenharmony_ci edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1, 15862306a36Sopenharmony_ci page, pageoffset, 0, 15962306a36Sopenharmony_ci edac_mc_find_csrow_by_page(mci, page), 16062306a36Sopenharmony_ci 0, -1, mci->ctl_name, ""); 16162306a36Sopenharmony_ci } 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci if (info->eap & I82443BXGX_EAP_OFFSET_MBE) { 16462306a36Sopenharmony_ci error_found = 1; 16562306a36Sopenharmony_ci if (handle_errors) 16662306a36Sopenharmony_ci edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, 16762306a36Sopenharmony_ci page, pageoffset, 0, 16862306a36Sopenharmony_ci edac_mc_find_csrow_by_page(mci, page), 16962306a36Sopenharmony_ci 0, -1, mci->ctl_name, ""); 17062306a36Sopenharmony_ci } 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci return error_found; 17362306a36Sopenharmony_ci} 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_cistatic void i82443bxgx_edacmc_check(struct mem_ctl_info *mci) 17662306a36Sopenharmony_ci{ 17762306a36Sopenharmony_ci struct i82443bxgx_edacmc_error_info info; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci i82443bxgx_edacmc_get_error_info(mci, &info); 18062306a36Sopenharmony_ci i82443bxgx_edacmc_process_error_info(mci, &info, 1); 18162306a36Sopenharmony_ci} 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_cistatic void i82443bxgx_init_csrows(struct mem_ctl_info *mci, 18462306a36Sopenharmony_ci struct pci_dev *pdev, 18562306a36Sopenharmony_ci enum edac_type edac_mode, 18662306a36Sopenharmony_ci enum mem_type mtype) 18762306a36Sopenharmony_ci{ 18862306a36Sopenharmony_ci struct csrow_info *csrow; 18962306a36Sopenharmony_ci struct dimm_info *dimm; 19062306a36Sopenharmony_ci int index; 19162306a36Sopenharmony_ci u8 drbar, dramc; 19262306a36Sopenharmony_ci u32 row_base, row_high_limit, row_high_limit_last; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci pci_read_config_byte(pdev, I82443BXGX_DRAMC, &dramc); 19562306a36Sopenharmony_ci row_high_limit_last = 0; 19662306a36Sopenharmony_ci for (index = 0; index < mci->nr_csrows; index++) { 19762306a36Sopenharmony_ci csrow = mci->csrows[index]; 19862306a36Sopenharmony_ci dimm = csrow->channels[0]->dimm; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci pci_read_config_byte(pdev, I82443BXGX_DRB + index, &drbar); 20162306a36Sopenharmony_ci edac_dbg(1, "MC%d: Row=%d DRB = %#0x\n", 20262306a36Sopenharmony_ci mci->mc_idx, index, drbar); 20362306a36Sopenharmony_ci row_high_limit = ((u32) drbar << 23); 20462306a36Sopenharmony_ci /* find the DRAM Chip Select Base address and mask */ 20562306a36Sopenharmony_ci edac_dbg(1, "MC%d: Row=%d, Boundary Address=%#0x, Last = %#0x\n", 20662306a36Sopenharmony_ci mci->mc_idx, index, row_high_limit, 20762306a36Sopenharmony_ci row_high_limit_last); 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci /* 440GX goes to 2GB, represented with a DRB of 0. */ 21062306a36Sopenharmony_ci if (row_high_limit_last && !row_high_limit) 21162306a36Sopenharmony_ci row_high_limit = 1UL << 31; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci /* This row is empty [p.49] */ 21462306a36Sopenharmony_ci if (row_high_limit == row_high_limit_last) 21562306a36Sopenharmony_ci continue; 21662306a36Sopenharmony_ci row_base = row_high_limit_last; 21762306a36Sopenharmony_ci csrow->first_page = row_base >> PAGE_SHIFT; 21862306a36Sopenharmony_ci csrow->last_page = (row_high_limit >> PAGE_SHIFT) - 1; 21962306a36Sopenharmony_ci dimm->nr_pages = csrow->last_page - csrow->first_page + 1; 22062306a36Sopenharmony_ci /* EAP reports in 4kilobyte granularity [61] */ 22162306a36Sopenharmony_ci dimm->grain = 1 << 12; 22262306a36Sopenharmony_ci dimm->mtype = mtype; 22362306a36Sopenharmony_ci /* I don't think 440BX can tell you device type? FIXME? */ 22462306a36Sopenharmony_ci dimm->dtype = DEV_UNKNOWN; 22562306a36Sopenharmony_ci /* Mode is global to all rows on 440BX */ 22662306a36Sopenharmony_ci dimm->edac_mode = edac_mode; 22762306a36Sopenharmony_ci row_high_limit_last = row_high_limit; 22862306a36Sopenharmony_ci } 22962306a36Sopenharmony_ci} 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_cistatic int i82443bxgx_edacmc_probe1(struct pci_dev *pdev, int dev_idx) 23262306a36Sopenharmony_ci{ 23362306a36Sopenharmony_ci struct mem_ctl_info *mci; 23462306a36Sopenharmony_ci struct edac_mc_layer layers[2]; 23562306a36Sopenharmony_ci u8 dramc; 23662306a36Sopenharmony_ci u32 nbxcfg, ecc_mode; 23762306a36Sopenharmony_ci enum mem_type mtype; 23862306a36Sopenharmony_ci enum edac_type edac_mode; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci edac_dbg(0, "MC:\n"); 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci /* Something is really hosed if PCI config space reads from 24362306a36Sopenharmony_ci * the MC aren't working. 24462306a36Sopenharmony_ci */ 24562306a36Sopenharmony_ci if (pci_read_config_dword(pdev, I82443BXGX_NBXCFG, &nbxcfg)) 24662306a36Sopenharmony_ci return -EIO; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci layers[0].type = EDAC_MC_LAYER_CHIP_SELECT; 24962306a36Sopenharmony_ci layers[0].size = I82443BXGX_NR_CSROWS; 25062306a36Sopenharmony_ci layers[0].is_virt_csrow = true; 25162306a36Sopenharmony_ci layers[1].type = EDAC_MC_LAYER_CHANNEL; 25262306a36Sopenharmony_ci layers[1].size = I82443BXGX_NR_CHANS; 25362306a36Sopenharmony_ci layers[1].is_virt_csrow = false; 25462306a36Sopenharmony_ci mci = edac_mc_alloc(0, ARRAY_SIZE(layers), layers, 0); 25562306a36Sopenharmony_ci if (mci == NULL) 25662306a36Sopenharmony_ci return -ENOMEM; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci edac_dbg(0, "MC: mci = %p\n", mci); 25962306a36Sopenharmony_ci mci->pdev = &pdev->dev; 26062306a36Sopenharmony_ci mci->mtype_cap = MEM_FLAG_EDO | MEM_FLAG_SDR | MEM_FLAG_RDR; 26162306a36Sopenharmony_ci mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_EC | EDAC_FLAG_SECDED; 26262306a36Sopenharmony_ci pci_read_config_byte(pdev, I82443BXGX_DRAMC, &dramc); 26362306a36Sopenharmony_ci switch ((dramc >> I82443BXGX_DRAMC_OFFSET_DT) & (BIT(0) | BIT(1))) { 26462306a36Sopenharmony_ci case I82443BXGX_DRAMC_DRAM_IS_EDO: 26562306a36Sopenharmony_ci mtype = MEM_EDO; 26662306a36Sopenharmony_ci break; 26762306a36Sopenharmony_ci case I82443BXGX_DRAMC_DRAM_IS_SDRAM: 26862306a36Sopenharmony_ci mtype = MEM_SDR; 26962306a36Sopenharmony_ci break; 27062306a36Sopenharmony_ci case I82443BXGX_DRAMC_DRAM_IS_RSDRAM: 27162306a36Sopenharmony_ci mtype = MEM_RDR; 27262306a36Sopenharmony_ci break; 27362306a36Sopenharmony_ci default: 27462306a36Sopenharmony_ci edac_dbg(0, "Unknown/reserved DRAM type value in DRAMC register!\n"); 27562306a36Sopenharmony_ci mtype = -MEM_UNKNOWN; 27662306a36Sopenharmony_ci } 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci if ((mtype == MEM_SDR) || (mtype == MEM_RDR)) 27962306a36Sopenharmony_ci mci->edac_cap = mci->edac_ctl_cap; 28062306a36Sopenharmony_ci else 28162306a36Sopenharmony_ci mci->edac_cap = EDAC_FLAG_NONE; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci mci->scrub_cap = SCRUB_FLAG_HW_SRC; 28462306a36Sopenharmony_ci pci_read_config_dword(pdev, I82443BXGX_NBXCFG, &nbxcfg); 28562306a36Sopenharmony_ci ecc_mode = ((nbxcfg >> I82443BXGX_NBXCFG_OFFSET_DRAM_INTEGRITY) & 28662306a36Sopenharmony_ci (BIT(0) | BIT(1))); 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci mci->scrub_mode = (ecc_mode == I82443BXGX_NBXCFG_INTEGRITY_SCRUB) 28962306a36Sopenharmony_ci ? SCRUB_HW_SRC : SCRUB_NONE; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci switch (ecc_mode) { 29262306a36Sopenharmony_ci case I82443BXGX_NBXCFG_INTEGRITY_NONE: 29362306a36Sopenharmony_ci edac_mode = EDAC_NONE; 29462306a36Sopenharmony_ci break; 29562306a36Sopenharmony_ci case I82443BXGX_NBXCFG_INTEGRITY_EC: 29662306a36Sopenharmony_ci edac_mode = EDAC_EC; 29762306a36Sopenharmony_ci break; 29862306a36Sopenharmony_ci case I82443BXGX_NBXCFG_INTEGRITY_ECC: 29962306a36Sopenharmony_ci case I82443BXGX_NBXCFG_INTEGRITY_SCRUB: 30062306a36Sopenharmony_ci edac_mode = EDAC_SECDED; 30162306a36Sopenharmony_ci break; 30262306a36Sopenharmony_ci default: 30362306a36Sopenharmony_ci edac_dbg(0, "Unknown/reserved ECC state in NBXCFG register!\n"); 30462306a36Sopenharmony_ci edac_mode = EDAC_UNKNOWN; 30562306a36Sopenharmony_ci break; 30662306a36Sopenharmony_ci } 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci i82443bxgx_init_csrows(mci, pdev, edac_mode, mtype); 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci /* Many BIOSes don't clear error flags on boot, so do this 31162306a36Sopenharmony_ci * here, or we get "phantom" errors occurring at module-load 31262306a36Sopenharmony_ci * time. */ 31362306a36Sopenharmony_ci pci_write_bits32(pdev, I82443BXGX_EAP, 31462306a36Sopenharmony_ci (I82443BXGX_EAP_OFFSET_SBE | 31562306a36Sopenharmony_ci I82443BXGX_EAP_OFFSET_MBE), 31662306a36Sopenharmony_ci (I82443BXGX_EAP_OFFSET_SBE | 31762306a36Sopenharmony_ci I82443BXGX_EAP_OFFSET_MBE)); 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci mci->mod_name = EDAC_MOD_STR; 32062306a36Sopenharmony_ci mci->ctl_name = "I82443BXGX"; 32162306a36Sopenharmony_ci mci->dev_name = pci_name(pdev); 32262306a36Sopenharmony_ci mci->edac_check = i82443bxgx_edacmc_check; 32362306a36Sopenharmony_ci mci->ctl_page_to_phys = NULL; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci if (edac_mc_add_mc(mci)) { 32662306a36Sopenharmony_ci edac_dbg(3, "failed edac_mc_add_mc()\n"); 32762306a36Sopenharmony_ci goto fail; 32862306a36Sopenharmony_ci } 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci /* allocating generic PCI control info */ 33162306a36Sopenharmony_ci i82443bxgx_pci = edac_pci_create_generic_ctl(&pdev->dev, EDAC_MOD_STR); 33262306a36Sopenharmony_ci if (!i82443bxgx_pci) { 33362306a36Sopenharmony_ci printk(KERN_WARNING 33462306a36Sopenharmony_ci "%s(): Unable to create PCI control\n", 33562306a36Sopenharmony_ci __func__); 33662306a36Sopenharmony_ci printk(KERN_WARNING 33762306a36Sopenharmony_ci "%s(): PCI error report via EDAC not setup\n", 33862306a36Sopenharmony_ci __func__); 33962306a36Sopenharmony_ci } 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci edac_dbg(3, "MC: success\n"); 34262306a36Sopenharmony_ci return 0; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_cifail: 34562306a36Sopenharmony_ci edac_mc_free(mci); 34662306a36Sopenharmony_ci return -ENODEV; 34762306a36Sopenharmony_ci} 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci/* returns count (>= 0), or negative on error */ 35062306a36Sopenharmony_cistatic int i82443bxgx_edacmc_init_one(struct pci_dev *pdev, 35162306a36Sopenharmony_ci const struct pci_device_id *ent) 35262306a36Sopenharmony_ci{ 35362306a36Sopenharmony_ci int rc; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci edac_dbg(0, "MC:\n"); 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci /* don't need to call pci_enable_device() */ 35862306a36Sopenharmony_ci rc = i82443bxgx_edacmc_probe1(pdev, ent->driver_data); 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci if (mci_pdev == NULL) 36162306a36Sopenharmony_ci mci_pdev = pci_dev_get(pdev); 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci return rc; 36462306a36Sopenharmony_ci} 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_cistatic void i82443bxgx_edacmc_remove_one(struct pci_dev *pdev) 36762306a36Sopenharmony_ci{ 36862306a36Sopenharmony_ci struct mem_ctl_info *mci; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci edac_dbg(0, "\n"); 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci if (i82443bxgx_pci) 37362306a36Sopenharmony_ci edac_pci_release_generic_ctl(i82443bxgx_pci); 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci if ((mci = edac_mc_del_mc(&pdev->dev)) == NULL) 37662306a36Sopenharmony_ci return; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci edac_mc_free(mci); 37962306a36Sopenharmony_ci} 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_cistatic const struct pci_device_id i82443bxgx_pci_tbl[] = { 38262306a36Sopenharmony_ci {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443BX_0)}, 38362306a36Sopenharmony_ci {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443BX_2)}, 38462306a36Sopenharmony_ci {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443GX_0)}, 38562306a36Sopenharmony_ci {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443GX_2)}, 38662306a36Sopenharmony_ci {0,} /* 0 terminated list. */ 38762306a36Sopenharmony_ci}; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, i82443bxgx_pci_tbl); 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_cistatic struct pci_driver i82443bxgx_edacmc_driver = { 39262306a36Sopenharmony_ci .name = EDAC_MOD_STR, 39362306a36Sopenharmony_ci .probe = i82443bxgx_edacmc_init_one, 39462306a36Sopenharmony_ci .remove = i82443bxgx_edacmc_remove_one, 39562306a36Sopenharmony_ci .id_table = i82443bxgx_pci_tbl, 39662306a36Sopenharmony_ci}; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_cistatic int __init i82443bxgx_edacmc_init(void) 39962306a36Sopenharmony_ci{ 40062306a36Sopenharmony_ci int pci_rc; 40162306a36Sopenharmony_ci /* Ensure that the OPSTATE is set correctly for POLL or NMI */ 40262306a36Sopenharmony_ci opstate_init(); 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci pci_rc = pci_register_driver(&i82443bxgx_edacmc_driver); 40562306a36Sopenharmony_ci if (pci_rc < 0) 40662306a36Sopenharmony_ci goto fail0; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci if (mci_pdev == NULL) { 40962306a36Sopenharmony_ci const struct pci_device_id *id = &i82443bxgx_pci_tbl[0]; 41062306a36Sopenharmony_ci int i = 0; 41162306a36Sopenharmony_ci i82443bxgx_registered = 0; 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci while (mci_pdev == NULL && id->vendor != 0) { 41462306a36Sopenharmony_ci mci_pdev = pci_get_device(id->vendor, 41562306a36Sopenharmony_ci id->device, NULL); 41662306a36Sopenharmony_ci i++; 41762306a36Sopenharmony_ci id = &i82443bxgx_pci_tbl[i]; 41862306a36Sopenharmony_ci } 41962306a36Sopenharmony_ci if (!mci_pdev) { 42062306a36Sopenharmony_ci edac_dbg(0, "i82443bxgx pci_get_device fail\n"); 42162306a36Sopenharmony_ci pci_rc = -ENODEV; 42262306a36Sopenharmony_ci goto fail1; 42362306a36Sopenharmony_ci } 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci pci_rc = i82443bxgx_edacmc_init_one(mci_pdev, i82443bxgx_pci_tbl); 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci if (pci_rc < 0) { 42862306a36Sopenharmony_ci edac_dbg(0, "i82443bxgx init fail\n"); 42962306a36Sopenharmony_ci pci_rc = -ENODEV; 43062306a36Sopenharmony_ci goto fail1; 43162306a36Sopenharmony_ci } 43262306a36Sopenharmony_ci } 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci return 0; 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_cifail1: 43762306a36Sopenharmony_ci pci_unregister_driver(&i82443bxgx_edacmc_driver); 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_cifail0: 44062306a36Sopenharmony_ci pci_dev_put(mci_pdev); 44162306a36Sopenharmony_ci return pci_rc; 44262306a36Sopenharmony_ci} 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_cistatic void __exit i82443bxgx_edacmc_exit(void) 44562306a36Sopenharmony_ci{ 44662306a36Sopenharmony_ci pci_unregister_driver(&i82443bxgx_edacmc_driver); 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci if (!i82443bxgx_registered) 44962306a36Sopenharmony_ci i82443bxgx_edacmc_remove_one(mci_pdev); 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci pci_dev_put(mci_pdev); 45262306a36Sopenharmony_ci} 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_cimodule_init(i82443bxgx_edacmc_init); 45562306a36Sopenharmony_cimodule_exit(i82443bxgx_edacmc_exit); 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 45862306a36Sopenharmony_ciMODULE_AUTHOR("Tim Small <tim@buttersideup.com> - WPAD"); 45962306a36Sopenharmony_ciMODULE_DESCRIPTION("EDAC MC support for Intel 82443BX/GX memory controllers"); 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_cimodule_param(edac_op_state, int, 0444); 46262306a36Sopenharmony_ciMODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI"); 463