162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Intel 3000/3010 Memory Controller kernel module 362306a36Sopenharmony_ci * Copyright (C) 2007 Akamai Technologies, Inc. 462306a36Sopenharmony_ci * Shamelessly copied from: 562306a36Sopenharmony_ci * Intel D82875P Memory Controller kernel module 662306a36Sopenharmony_ci * (C) 2003 Linux Networx (http://lnxi.com) 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * This file may be distributed under the terms of the 962306a36Sopenharmony_ci * GNU General Public License. 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci#include <linux/init.h> 1462306a36Sopenharmony_ci#include <linux/pci.h> 1562306a36Sopenharmony_ci#include <linux/pci_ids.h> 1662306a36Sopenharmony_ci#include <linux/edac.h> 1762306a36Sopenharmony_ci#include "edac_module.h" 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#define EDAC_MOD_STR "i3000_edac" 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#define I3000_RANKS 8 2262306a36Sopenharmony_ci#define I3000_RANKS_PER_CHANNEL 4 2362306a36Sopenharmony_ci#define I3000_CHANNELS 2 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci/* Intel 3000 register addresses - device 0 function 0 - DRAM Controller */ 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#define I3000_MCHBAR 0x44 /* MCH Memory Mapped Register BAR */ 2862306a36Sopenharmony_ci#define I3000_MCHBAR_MASK 0xffffc000 2962306a36Sopenharmony_ci#define I3000_MMR_WINDOW_SIZE 16384 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#define I3000_EDEAP 0x70 /* Extended DRAM Error Address Pointer (8b) 3262306a36Sopenharmony_ci * 3362306a36Sopenharmony_ci * 7:1 reserved 3462306a36Sopenharmony_ci * 0 bit 32 of address 3562306a36Sopenharmony_ci */ 3662306a36Sopenharmony_ci#define I3000_DEAP 0x58 /* DRAM Error Address Pointer (32b) 3762306a36Sopenharmony_ci * 3862306a36Sopenharmony_ci * 31:7 address 3962306a36Sopenharmony_ci * 6:1 reserved 4062306a36Sopenharmony_ci * 0 Error channel 0/1 4162306a36Sopenharmony_ci */ 4262306a36Sopenharmony_ci#define I3000_DEAP_GRAIN (1 << 7) 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci/* 4562306a36Sopenharmony_ci * Helper functions to decode the DEAP/EDEAP hardware registers. 4662306a36Sopenharmony_ci * 4762306a36Sopenharmony_ci * The type promotion here is deliberate; we're deriving an 4862306a36Sopenharmony_ci * unsigned long pfn and offset from hardware regs which are u8/u32. 4962306a36Sopenharmony_ci */ 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_cistatic inline unsigned long deap_pfn(u8 edeap, u32 deap) 5262306a36Sopenharmony_ci{ 5362306a36Sopenharmony_ci deap >>= PAGE_SHIFT; 5462306a36Sopenharmony_ci deap |= (edeap & 1) << (32 - PAGE_SHIFT); 5562306a36Sopenharmony_ci return deap; 5662306a36Sopenharmony_ci} 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_cistatic inline unsigned long deap_offset(u32 deap) 5962306a36Sopenharmony_ci{ 6062306a36Sopenharmony_ci return deap & ~(I3000_DEAP_GRAIN - 1) & ~PAGE_MASK; 6162306a36Sopenharmony_ci} 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistatic inline int deap_channel(u32 deap) 6462306a36Sopenharmony_ci{ 6562306a36Sopenharmony_ci return deap & 1; 6662306a36Sopenharmony_ci} 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci#define I3000_DERRSYN 0x5c /* DRAM Error Syndrome (8b) 6962306a36Sopenharmony_ci * 7062306a36Sopenharmony_ci * 7:0 DRAM ECC Syndrome 7162306a36Sopenharmony_ci */ 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci#define I3000_ERRSTS 0xc8 /* Error Status Register (16b) 7462306a36Sopenharmony_ci * 7562306a36Sopenharmony_ci * 15:12 reserved 7662306a36Sopenharmony_ci * 11 MCH Thermal Sensor Event 7762306a36Sopenharmony_ci * for SMI/SCI/SERR 7862306a36Sopenharmony_ci * 10 reserved 7962306a36Sopenharmony_ci * 9 LOCK to non-DRAM Memory Flag (LCKF) 8062306a36Sopenharmony_ci * 8 Received Refresh Timeout Flag (RRTOF) 8162306a36Sopenharmony_ci * 7:2 reserved 8262306a36Sopenharmony_ci * 1 Multi-bit DRAM ECC Error Flag (DMERR) 8362306a36Sopenharmony_ci * 0 Single-bit DRAM ECC Error Flag (DSERR) 8462306a36Sopenharmony_ci */ 8562306a36Sopenharmony_ci#define I3000_ERRSTS_BITS 0x0b03 /* bits which indicate errors */ 8662306a36Sopenharmony_ci#define I3000_ERRSTS_UE 0x0002 8762306a36Sopenharmony_ci#define I3000_ERRSTS_CE 0x0001 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci#define I3000_ERRCMD 0xca /* Error Command (16b) 9062306a36Sopenharmony_ci * 9162306a36Sopenharmony_ci * 15:12 reserved 9262306a36Sopenharmony_ci * 11 SERR on MCH Thermal Sensor Event 9362306a36Sopenharmony_ci * (TSESERR) 9462306a36Sopenharmony_ci * 10 reserved 9562306a36Sopenharmony_ci * 9 SERR on LOCK to non-DRAM Memory 9662306a36Sopenharmony_ci * (LCKERR) 9762306a36Sopenharmony_ci * 8 SERR on DRAM Refresh Timeout 9862306a36Sopenharmony_ci * (DRTOERR) 9962306a36Sopenharmony_ci * 7:2 reserved 10062306a36Sopenharmony_ci * 1 SERR Multi-Bit DRAM ECC Error 10162306a36Sopenharmony_ci * (DMERR) 10262306a36Sopenharmony_ci * 0 SERR on Single-Bit ECC Error 10362306a36Sopenharmony_ci * (DSERR) 10462306a36Sopenharmony_ci */ 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci/* Intel MMIO register space - device 0 function 0 - MMR space */ 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci#define I3000_DRB_SHIFT 25 /* 32MiB grain */ 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci#define I3000_C0DRB 0x100 /* Channel 0 DRAM Rank Boundary (8b x 4) 11162306a36Sopenharmony_ci * 11262306a36Sopenharmony_ci * 7:0 Channel 0 DRAM Rank Boundary Address 11362306a36Sopenharmony_ci */ 11462306a36Sopenharmony_ci#define I3000_C1DRB 0x180 /* Channel 1 DRAM Rank Boundary (8b x 4) 11562306a36Sopenharmony_ci * 11662306a36Sopenharmony_ci * 7:0 Channel 1 DRAM Rank Boundary Address 11762306a36Sopenharmony_ci */ 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci#define I3000_C0DRA 0x108 /* Channel 0 DRAM Rank Attribute (8b x 2) 12062306a36Sopenharmony_ci * 12162306a36Sopenharmony_ci * 7 reserved 12262306a36Sopenharmony_ci * 6:4 DRAM odd Rank Attribute 12362306a36Sopenharmony_ci * 3 reserved 12462306a36Sopenharmony_ci * 2:0 DRAM even Rank Attribute 12562306a36Sopenharmony_ci * 12662306a36Sopenharmony_ci * Each attribute defines the page 12762306a36Sopenharmony_ci * size of the corresponding rank: 12862306a36Sopenharmony_ci * 000: unpopulated 12962306a36Sopenharmony_ci * 001: reserved 13062306a36Sopenharmony_ci * 010: 4 KB 13162306a36Sopenharmony_ci * 011: 8 KB 13262306a36Sopenharmony_ci * 100: 16 KB 13362306a36Sopenharmony_ci * Others: reserved 13462306a36Sopenharmony_ci */ 13562306a36Sopenharmony_ci#define I3000_C1DRA 0x188 /* Channel 1 DRAM Rank Attribute (8b x 2) */ 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_cistatic inline unsigned char odd_rank_attrib(unsigned char dra) 13862306a36Sopenharmony_ci{ 13962306a36Sopenharmony_ci return (dra & 0x70) >> 4; 14062306a36Sopenharmony_ci} 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_cistatic inline unsigned char even_rank_attrib(unsigned char dra) 14362306a36Sopenharmony_ci{ 14462306a36Sopenharmony_ci return dra & 0x07; 14562306a36Sopenharmony_ci} 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci#define I3000_C0DRC0 0x120 /* DRAM Controller Mode 0 (32b) 14862306a36Sopenharmony_ci * 14962306a36Sopenharmony_ci * 31:30 reserved 15062306a36Sopenharmony_ci * 29 Initialization Complete (IC) 15162306a36Sopenharmony_ci * 28:11 reserved 15262306a36Sopenharmony_ci * 10:8 Refresh Mode Select (RMS) 15362306a36Sopenharmony_ci * 7 reserved 15462306a36Sopenharmony_ci * 6:4 Mode Select (SMS) 15562306a36Sopenharmony_ci * 3:2 reserved 15662306a36Sopenharmony_ci * 1:0 DRAM Type (DT) 15762306a36Sopenharmony_ci */ 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci#define I3000_C0DRC1 0x124 /* DRAM Controller Mode 1 (32b) 16062306a36Sopenharmony_ci * 16162306a36Sopenharmony_ci * 31 Enhanced Addressing Enable (ENHADE) 16262306a36Sopenharmony_ci * 30:0 reserved 16362306a36Sopenharmony_ci */ 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_cienum i3000p_chips { 16662306a36Sopenharmony_ci I3000 = 0, 16762306a36Sopenharmony_ci}; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_cistruct i3000_dev_info { 17062306a36Sopenharmony_ci const char *ctl_name; 17162306a36Sopenharmony_ci}; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_cistruct i3000_error_info { 17462306a36Sopenharmony_ci u16 errsts; 17562306a36Sopenharmony_ci u8 derrsyn; 17662306a36Sopenharmony_ci u8 edeap; 17762306a36Sopenharmony_ci u32 deap; 17862306a36Sopenharmony_ci u16 errsts2; 17962306a36Sopenharmony_ci}; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_cistatic const struct i3000_dev_info i3000_devs[] = { 18262306a36Sopenharmony_ci [I3000] = { 18362306a36Sopenharmony_ci .ctl_name = "i3000"}, 18462306a36Sopenharmony_ci}; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_cistatic struct pci_dev *mci_pdev; 18762306a36Sopenharmony_cistatic int i3000_registered = 1; 18862306a36Sopenharmony_cistatic struct edac_pci_ctl_info *i3000_pci; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_cistatic void i3000_get_error_info(struct mem_ctl_info *mci, 19162306a36Sopenharmony_ci struct i3000_error_info *info) 19262306a36Sopenharmony_ci{ 19362306a36Sopenharmony_ci struct pci_dev *pdev; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci pdev = to_pci_dev(mci->pdev); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci /* 19862306a36Sopenharmony_ci * This is a mess because there is no atomic way to read all the 19962306a36Sopenharmony_ci * registers at once and the registers can transition from CE being 20062306a36Sopenharmony_ci * overwritten by UE. 20162306a36Sopenharmony_ci */ 20262306a36Sopenharmony_ci pci_read_config_word(pdev, I3000_ERRSTS, &info->errsts); 20362306a36Sopenharmony_ci if (!(info->errsts & I3000_ERRSTS_BITS)) 20462306a36Sopenharmony_ci return; 20562306a36Sopenharmony_ci pci_read_config_byte(pdev, I3000_EDEAP, &info->edeap); 20662306a36Sopenharmony_ci pci_read_config_dword(pdev, I3000_DEAP, &info->deap); 20762306a36Sopenharmony_ci pci_read_config_byte(pdev, I3000_DERRSYN, &info->derrsyn); 20862306a36Sopenharmony_ci pci_read_config_word(pdev, I3000_ERRSTS, &info->errsts2); 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci /* 21162306a36Sopenharmony_ci * If the error is the same for both reads then the first set 21262306a36Sopenharmony_ci * of reads is valid. If there is a change then there is a CE 21362306a36Sopenharmony_ci * with no info and the second set of reads is valid and 21462306a36Sopenharmony_ci * should be UE info. 21562306a36Sopenharmony_ci */ 21662306a36Sopenharmony_ci if ((info->errsts ^ info->errsts2) & I3000_ERRSTS_BITS) { 21762306a36Sopenharmony_ci pci_read_config_byte(pdev, I3000_EDEAP, &info->edeap); 21862306a36Sopenharmony_ci pci_read_config_dword(pdev, I3000_DEAP, &info->deap); 21962306a36Sopenharmony_ci pci_read_config_byte(pdev, I3000_DERRSYN, &info->derrsyn); 22062306a36Sopenharmony_ci } 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci /* 22362306a36Sopenharmony_ci * Clear any error bits. 22462306a36Sopenharmony_ci * (Yes, we really clear bits by writing 1 to them.) 22562306a36Sopenharmony_ci */ 22662306a36Sopenharmony_ci pci_write_bits16(pdev, I3000_ERRSTS, I3000_ERRSTS_BITS, 22762306a36Sopenharmony_ci I3000_ERRSTS_BITS); 22862306a36Sopenharmony_ci} 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_cistatic int i3000_process_error_info(struct mem_ctl_info *mci, 23162306a36Sopenharmony_ci struct i3000_error_info *info, 23262306a36Sopenharmony_ci int handle_errors) 23362306a36Sopenharmony_ci{ 23462306a36Sopenharmony_ci int row, multi_chan, channel; 23562306a36Sopenharmony_ci unsigned long pfn, offset; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci multi_chan = mci->csrows[0]->nr_channels - 1; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci if (!(info->errsts & I3000_ERRSTS_BITS)) 24062306a36Sopenharmony_ci return 0; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci if (!handle_errors) 24362306a36Sopenharmony_ci return 1; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci if ((info->errsts ^ info->errsts2) & I3000_ERRSTS_BITS) { 24662306a36Sopenharmony_ci edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, 0, 0, 0, 24762306a36Sopenharmony_ci -1, -1, -1, 24862306a36Sopenharmony_ci "UE overwrote CE", ""); 24962306a36Sopenharmony_ci info->errsts = info->errsts2; 25062306a36Sopenharmony_ci } 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci pfn = deap_pfn(info->edeap, info->deap); 25362306a36Sopenharmony_ci offset = deap_offset(info->deap); 25462306a36Sopenharmony_ci channel = deap_channel(info->deap); 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci row = edac_mc_find_csrow_by_page(mci, pfn); 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci if (info->errsts & I3000_ERRSTS_UE) 25962306a36Sopenharmony_ci edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, 26062306a36Sopenharmony_ci pfn, offset, 0, 26162306a36Sopenharmony_ci row, -1, -1, 26262306a36Sopenharmony_ci "i3000 UE", ""); 26362306a36Sopenharmony_ci else 26462306a36Sopenharmony_ci edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1, 26562306a36Sopenharmony_ci pfn, offset, info->derrsyn, 26662306a36Sopenharmony_ci row, multi_chan ? channel : 0, -1, 26762306a36Sopenharmony_ci "i3000 CE", ""); 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci return 1; 27062306a36Sopenharmony_ci} 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_cistatic void i3000_check(struct mem_ctl_info *mci) 27362306a36Sopenharmony_ci{ 27462306a36Sopenharmony_ci struct i3000_error_info info; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci i3000_get_error_info(mci, &info); 27762306a36Sopenharmony_ci i3000_process_error_info(mci, &info, 1); 27862306a36Sopenharmony_ci} 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_cistatic int i3000_is_interleaved(const unsigned char *c0dra, 28162306a36Sopenharmony_ci const unsigned char *c1dra, 28262306a36Sopenharmony_ci const unsigned char *c0drb, 28362306a36Sopenharmony_ci const unsigned char *c1drb) 28462306a36Sopenharmony_ci{ 28562306a36Sopenharmony_ci int i; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci /* 28862306a36Sopenharmony_ci * If the channels aren't populated identically then 28962306a36Sopenharmony_ci * we're not interleaved. 29062306a36Sopenharmony_ci */ 29162306a36Sopenharmony_ci for (i = 0; i < I3000_RANKS_PER_CHANNEL / 2; i++) 29262306a36Sopenharmony_ci if (odd_rank_attrib(c0dra[i]) != odd_rank_attrib(c1dra[i]) || 29362306a36Sopenharmony_ci even_rank_attrib(c0dra[i]) != 29462306a36Sopenharmony_ci even_rank_attrib(c1dra[i])) 29562306a36Sopenharmony_ci return 0; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci /* 29862306a36Sopenharmony_ci * If the rank boundaries for the two channels are different 29962306a36Sopenharmony_ci * then we're not interleaved. 30062306a36Sopenharmony_ci */ 30162306a36Sopenharmony_ci for (i = 0; i < I3000_RANKS_PER_CHANNEL; i++) 30262306a36Sopenharmony_ci if (c0drb[i] != c1drb[i]) 30362306a36Sopenharmony_ci return 0; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci return 1; 30662306a36Sopenharmony_ci} 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_cistatic int i3000_probe1(struct pci_dev *pdev, int dev_idx) 30962306a36Sopenharmony_ci{ 31062306a36Sopenharmony_ci int rc; 31162306a36Sopenharmony_ci int i, j; 31262306a36Sopenharmony_ci struct mem_ctl_info *mci = NULL; 31362306a36Sopenharmony_ci struct edac_mc_layer layers[2]; 31462306a36Sopenharmony_ci unsigned long last_cumul_size, nr_pages; 31562306a36Sopenharmony_ci int interleaved, nr_channels; 31662306a36Sopenharmony_ci unsigned char dra[I3000_RANKS / 2], drb[I3000_RANKS]; 31762306a36Sopenharmony_ci unsigned char *c0dra = dra, *c1dra = &dra[I3000_RANKS_PER_CHANNEL / 2]; 31862306a36Sopenharmony_ci unsigned char *c0drb = drb, *c1drb = &drb[I3000_RANKS_PER_CHANNEL]; 31962306a36Sopenharmony_ci unsigned long mchbar; 32062306a36Sopenharmony_ci void __iomem *window; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci edac_dbg(0, "MC:\n"); 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci pci_read_config_dword(pdev, I3000_MCHBAR, (u32 *) & mchbar); 32562306a36Sopenharmony_ci mchbar &= I3000_MCHBAR_MASK; 32662306a36Sopenharmony_ci window = ioremap(mchbar, I3000_MMR_WINDOW_SIZE); 32762306a36Sopenharmony_ci if (!window) { 32862306a36Sopenharmony_ci printk(KERN_ERR "i3000: cannot map mmio space at 0x%lx\n", 32962306a36Sopenharmony_ci mchbar); 33062306a36Sopenharmony_ci return -ENODEV; 33162306a36Sopenharmony_ci } 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci c0dra[0] = readb(window + I3000_C0DRA + 0); /* ranks 0,1 */ 33462306a36Sopenharmony_ci c0dra[1] = readb(window + I3000_C0DRA + 1); /* ranks 2,3 */ 33562306a36Sopenharmony_ci c1dra[0] = readb(window + I3000_C1DRA + 0); /* ranks 0,1 */ 33662306a36Sopenharmony_ci c1dra[1] = readb(window + I3000_C1DRA + 1); /* ranks 2,3 */ 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci for (i = 0; i < I3000_RANKS_PER_CHANNEL; i++) { 33962306a36Sopenharmony_ci c0drb[i] = readb(window + I3000_C0DRB + i); 34062306a36Sopenharmony_ci c1drb[i] = readb(window + I3000_C1DRB + i); 34162306a36Sopenharmony_ci } 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci iounmap(window); 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci /* 34662306a36Sopenharmony_ci * Figure out how many channels we have. 34762306a36Sopenharmony_ci * 34862306a36Sopenharmony_ci * If we have what the datasheet calls "asymmetric channels" 34962306a36Sopenharmony_ci * (essentially the same as what was called "virtual single 35062306a36Sopenharmony_ci * channel mode" in the i82875) then it's a single channel as 35162306a36Sopenharmony_ci * far as EDAC is concerned. 35262306a36Sopenharmony_ci */ 35362306a36Sopenharmony_ci interleaved = i3000_is_interleaved(c0dra, c1dra, c0drb, c1drb); 35462306a36Sopenharmony_ci nr_channels = interleaved ? 2 : 1; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci layers[0].type = EDAC_MC_LAYER_CHIP_SELECT; 35762306a36Sopenharmony_ci layers[0].size = I3000_RANKS / nr_channels; 35862306a36Sopenharmony_ci layers[0].is_virt_csrow = true; 35962306a36Sopenharmony_ci layers[1].type = EDAC_MC_LAYER_CHANNEL; 36062306a36Sopenharmony_ci layers[1].size = nr_channels; 36162306a36Sopenharmony_ci layers[1].is_virt_csrow = false; 36262306a36Sopenharmony_ci mci = edac_mc_alloc(0, ARRAY_SIZE(layers), layers, 0); 36362306a36Sopenharmony_ci if (!mci) 36462306a36Sopenharmony_ci return -ENOMEM; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci edac_dbg(3, "MC: init mci\n"); 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci mci->pdev = &pdev->dev; 36962306a36Sopenharmony_ci mci->mtype_cap = MEM_FLAG_DDR2; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci mci->edac_ctl_cap = EDAC_FLAG_SECDED; 37262306a36Sopenharmony_ci mci->edac_cap = EDAC_FLAG_SECDED; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci mci->mod_name = EDAC_MOD_STR; 37562306a36Sopenharmony_ci mci->ctl_name = i3000_devs[dev_idx].ctl_name; 37662306a36Sopenharmony_ci mci->dev_name = pci_name(pdev); 37762306a36Sopenharmony_ci mci->edac_check = i3000_check; 37862306a36Sopenharmony_ci mci->ctl_page_to_phys = NULL; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci /* 38162306a36Sopenharmony_ci * The dram rank boundary (DRB) reg values are boundary addresses 38262306a36Sopenharmony_ci * for each DRAM rank with a granularity of 32MB. DRB regs are 38362306a36Sopenharmony_ci * cumulative; the last one will contain the total memory 38462306a36Sopenharmony_ci * contained in all ranks. 38562306a36Sopenharmony_ci * 38662306a36Sopenharmony_ci * If we're in interleaved mode then we're only walking through 38762306a36Sopenharmony_ci * the ranks of controller 0, so we double all the values we see. 38862306a36Sopenharmony_ci */ 38962306a36Sopenharmony_ci for (last_cumul_size = i = 0; i < mci->nr_csrows; i++) { 39062306a36Sopenharmony_ci u8 value; 39162306a36Sopenharmony_ci u32 cumul_size; 39262306a36Sopenharmony_ci struct csrow_info *csrow = mci->csrows[i]; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci value = drb[i]; 39562306a36Sopenharmony_ci cumul_size = value << (I3000_DRB_SHIFT - PAGE_SHIFT); 39662306a36Sopenharmony_ci if (interleaved) 39762306a36Sopenharmony_ci cumul_size <<= 1; 39862306a36Sopenharmony_ci edac_dbg(3, "MC: (%d) cumul_size 0x%x\n", i, cumul_size); 39962306a36Sopenharmony_ci if (cumul_size == last_cumul_size) 40062306a36Sopenharmony_ci continue; 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci csrow->first_page = last_cumul_size; 40362306a36Sopenharmony_ci csrow->last_page = cumul_size - 1; 40462306a36Sopenharmony_ci nr_pages = cumul_size - last_cumul_size; 40562306a36Sopenharmony_ci last_cumul_size = cumul_size; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci for (j = 0; j < nr_channels; j++) { 40862306a36Sopenharmony_ci struct dimm_info *dimm = csrow->channels[j]->dimm; 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci dimm->nr_pages = nr_pages / nr_channels; 41162306a36Sopenharmony_ci dimm->grain = I3000_DEAP_GRAIN; 41262306a36Sopenharmony_ci dimm->mtype = MEM_DDR2; 41362306a36Sopenharmony_ci dimm->dtype = DEV_UNKNOWN; 41462306a36Sopenharmony_ci dimm->edac_mode = EDAC_UNKNOWN; 41562306a36Sopenharmony_ci } 41662306a36Sopenharmony_ci } 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci /* 41962306a36Sopenharmony_ci * Clear any error bits. 42062306a36Sopenharmony_ci * (Yes, we really clear bits by writing 1 to them.) 42162306a36Sopenharmony_ci */ 42262306a36Sopenharmony_ci pci_write_bits16(pdev, I3000_ERRSTS, I3000_ERRSTS_BITS, 42362306a36Sopenharmony_ci I3000_ERRSTS_BITS); 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci rc = -ENODEV; 42662306a36Sopenharmony_ci if (edac_mc_add_mc(mci)) { 42762306a36Sopenharmony_ci edac_dbg(3, "MC: failed edac_mc_add_mc()\n"); 42862306a36Sopenharmony_ci goto fail; 42962306a36Sopenharmony_ci } 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci /* allocating generic PCI control info */ 43262306a36Sopenharmony_ci i3000_pci = edac_pci_create_generic_ctl(&pdev->dev, EDAC_MOD_STR); 43362306a36Sopenharmony_ci if (!i3000_pci) { 43462306a36Sopenharmony_ci printk(KERN_WARNING 43562306a36Sopenharmony_ci "%s(): Unable to create PCI control\n", 43662306a36Sopenharmony_ci __func__); 43762306a36Sopenharmony_ci printk(KERN_WARNING 43862306a36Sopenharmony_ci "%s(): PCI error report via EDAC not setup\n", 43962306a36Sopenharmony_ci __func__); 44062306a36Sopenharmony_ci } 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci /* get this far and it's successful */ 44362306a36Sopenharmony_ci edac_dbg(3, "MC: success\n"); 44462306a36Sopenharmony_ci return 0; 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_cifail: 44762306a36Sopenharmony_ci if (mci) 44862306a36Sopenharmony_ci edac_mc_free(mci); 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci return rc; 45162306a36Sopenharmony_ci} 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci/* returns count (>= 0), or negative on error */ 45462306a36Sopenharmony_cistatic int i3000_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) 45562306a36Sopenharmony_ci{ 45662306a36Sopenharmony_ci int rc; 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci edac_dbg(0, "MC:\n"); 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci if (pci_enable_device(pdev) < 0) 46162306a36Sopenharmony_ci return -EIO; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci rc = i3000_probe1(pdev, ent->driver_data); 46462306a36Sopenharmony_ci if (!mci_pdev) 46562306a36Sopenharmony_ci mci_pdev = pci_dev_get(pdev); 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci return rc; 46862306a36Sopenharmony_ci} 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_cistatic void i3000_remove_one(struct pci_dev *pdev) 47162306a36Sopenharmony_ci{ 47262306a36Sopenharmony_ci struct mem_ctl_info *mci; 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci edac_dbg(0, "\n"); 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci if (i3000_pci) 47762306a36Sopenharmony_ci edac_pci_release_generic_ctl(i3000_pci); 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci mci = edac_mc_del_mc(&pdev->dev); 48062306a36Sopenharmony_ci if (!mci) 48162306a36Sopenharmony_ci return; 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci edac_mc_free(mci); 48462306a36Sopenharmony_ci} 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_cistatic const struct pci_device_id i3000_pci_tbl[] = { 48762306a36Sopenharmony_ci { 48862306a36Sopenharmony_ci PCI_VEND_DEV(INTEL, 3000_HB), PCI_ANY_ID, PCI_ANY_ID, 0, 0, 48962306a36Sopenharmony_ci I3000}, 49062306a36Sopenharmony_ci { 49162306a36Sopenharmony_ci 0, 49262306a36Sopenharmony_ci } /* 0 terminated list. */ 49362306a36Sopenharmony_ci}; 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, i3000_pci_tbl); 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_cistatic struct pci_driver i3000_driver = { 49862306a36Sopenharmony_ci .name = EDAC_MOD_STR, 49962306a36Sopenharmony_ci .probe = i3000_init_one, 50062306a36Sopenharmony_ci .remove = i3000_remove_one, 50162306a36Sopenharmony_ci .id_table = i3000_pci_tbl, 50262306a36Sopenharmony_ci}; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_cistatic int __init i3000_init(void) 50562306a36Sopenharmony_ci{ 50662306a36Sopenharmony_ci int pci_rc; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci edac_dbg(3, "MC:\n"); 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci /* Ensure that the OPSTATE is set correctly for POLL or NMI */ 51162306a36Sopenharmony_ci opstate_init(); 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci pci_rc = pci_register_driver(&i3000_driver); 51462306a36Sopenharmony_ci if (pci_rc < 0) 51562306a36Sopenharmony_ci goto fail0; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci if (!mci_pdev) { 51862306a36Sopenharmony_ci i3000_registered = 0; 51962306a36Sopenharmony_ci mci_pdev = pci_get_device(PCI_VENDOR_ID_INTEL, 52062306a36Sopenharmony_ci PCI_DEVICE_ID_INTEL_3000_HB, NULL); 52162306a36Sopenharmony_ci if (!mci_pdev) { 52262306a36Sopenharmony_ci edac_dbg(0, "i3000 pci_get_device fail\n"); 52362306a36Sopenharmony_ci pci_rc = -ENODEV; 52462306a36Sopenharmony_ci goto fail1; 52562306a36Sopenharmony_ci } 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci pci_rc = i3000_init_one(mci_pdev, i3000_pci_tbl); 52862306a36Sopenharmony_ci if (pci_rc < 0) { 52962306a36Sopenharmony_ci edac_dbg(0, "i3000 init fail\n"); 53062306a36Sopenharmony_ci pci_rc = -ENODEV; 53162306a36Sopenharmony_ci goto fail1; 53262306a36Sopenharmony_ci } 53362306a36Sopenharmony_ci } 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci return 0; 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_cifail1: 53862306a36Sopenharmony_ci pci_unregister_driver(&i3000_driver); 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_cifail0: 54162306a36Sopenharmony_ci pci_dev_put(mci_pdev); 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci return pci_rc; 54462306a36Sopenharmony_ci} 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_cistatic void __exit i3000_exit(void) 54762306a36Sopenharmony_ci{ 54862306a36Sopenharmony_ci edac_dbg(3, "MC:\n"); 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci pci_unregister_driver(&i3000_driver); 55162306a36Sopenharmony_ci if (!i3000_registered) { 55262306a36Sopenharmony_ci i3000_remove_one(mci_pdev); 55362306a36Sopenharmony_ci pci_dev_put(mci_pdev); 55462306a36Sopenharmony_ci } 55562306a36Sopenharmony_ci} 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_cimodule_init(i3000_init); 55862306a36Sopenharmony_cimodule_exit(i3000_exit); 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 56162306a36Sopenharmony_ciMODULE_AUTHOR("Akamai Technologies Arthur Ulfeldt/Jason Uhlenkott"); 56262306a36Sopenharmony_ciMODULE_DESCRIPTION("MC support for Intel 3000 memory hub controllers"); 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_cimodule_param(edac_op_state, int, 0444); 56562306a36Sopenharmony_ciMODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI"); 566