Blame view
drivers/edac/i7core_edac.c
64.1 KB
52707f918 i7core_edac: Bett... |
1 2 |
/* Intel i7 core/Nehalem Memory Controller kernel module * |
e7bf068aa i7core_edac: fix ... |
3 |
* This driver supports the memory controllers found on the Intel |
52707f918 i7core_edac: Bett... |
4 5 6 |
* processor families i7core, i7core 7xx/8xx, i5core, Xeon 35xx, * Xeon 55xx and Xeon 56xx also known as Nehalem, Nehalem-EP, Lynnfield * and Westmere-EP. |
a0c36a1f0 i7core_edac: Add ... |
7 8 9 10 |
* * This file may be distributed under the terms of the * GNU General Public License version 2 only. * |
52707f918 i7core_edac: Bett... |
11 |
* Copyright (c) 2009-2010 by: |
a0c36a1f0 i7core_edac: Add ... |
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
* Mauro Carvalho Chehab <mchehab@redhat.com> * * Red Hat Inc. http://www.redhat.com * * Forked and adapted from the i5400_edac driver * * Based on the following public Intel datasheets: * Intel Core i7 Processor Extreme Edition and Intel Core i7 Processor * Datasheet, Volume 2: * http://download.intel.com/design/processor/datashts/320835.pdf * Intel Xeon Processor 5500 Series Datasheet Volume 2 * http://www.intel.com/Assets/PDF/datasheet/321322.pdf * also available at: * http://www.arrownac.com/manufacturers/intel/s/nehalem/5500-datasheet-v2.pdf */ |
a0c36a1f0 i7core_edac: Add ... |
27 28 29 30 31 |
#include <linux/module.h> #include <linux/init.h> #include <linux/pci.h> #include <linux/pci_ids.h> #include <linux/slab.h> |
3b918c12d edac: fix i7core ... |
32 |
#include <linux/delay.h> |
535e9c78e i7core_edac: scru... |
33 |
#include <linux/dmi.h> |
a0c36a1f0 i7core_edac: Add ... |
34 35 |
#include <linux/edac.h> #include <linux/mmzone.h> |
f47429494 i7core_edac: crea... |
36 |
#include <linux/smp.h> |
4140c5426 i7core_edac: Drop... |
37 |
#include <asm/mce.h> |
14d2c0834 i7core: Use regis... |
38 |
#include <asm/processor.h> |
4fad8098b i7core_edac: Fix ... |
39 |
#include <asm/div64.h> |
a0c36a1f0 i7core_edac: Add ... |
40 41 |
#include "edac_core.h" |
18c29002f i7core_edac: move... |
42 43 44 45 |
/* Static vars */ static LIST_HEAD(i7core_edac_list); static DEFINE_MUTEX(i7core_edac_lock); static int probed; |
54a08ab15 i7core_edac: Don'... |
46 47 48 |
static int use_pci_fixup; module_param(use_pci_fixup, int, 0444); MODULE_PARM_DESC(use_pci_fixup, "Enable PCI fixup to seek for hidden devices"); |
a0c36a1f0 i7core_edac: Add ... |
49 |
/* |
f47429494 i7core_edac: crea... |
50 51 52 53 54 55 56 57 58 |
* This is used for Nehalem-EP and Nehalem-EX devices, where the non-core * registers start at bus 255, and are not reported by BIOS. * We currently find devices with only 2 sockets. In order to support more QPI * Quick Path Interconnect, just increment this number. */ #define MAX_SOCKET_BUSES 2 /* |
a0c36a1f0 i7core_edac: Add ... |
59 60 |
* Alter this version for the module when modifications are made */ |
152ba3942 edac: Drop __DATE... |
61 |
#define I7CORE_REVISION " Ver: 1.0.0" |
a0c36a1f0 i7core_edac: Add ... |
62 |
#define EDAC_MOD_STR "i7core_edac" |
a0c36a1f0 i7core_edac: Add ... |
63 64 65 66 67 68 69 70 71 72 73 74 |
/* * Debug macros */ #define i7core_printk(level, fmt, arg...) \ edac_printk(level, "i7core", fmt, ##arg) #define i7core_mc_printk(mci, level, fmt, arg...) \ edac_mc_chipset_printk(mci, level, "i7core", fmt, ##arg) /* * i7core Memory Controller Registers */ |
e9bd2e737 i7core_edac: Adds... |
75 76 77 |
/* OFFSETS for Device 0 Function 0 */ #define MC_CFG_CONTROL 0x90 |
e8b6a1271 i7core_edac: Add ... |
78 79 |
#define MC_CFG_UNLOCK 0x02 #define MC_CFG_LOCK 0x00 |
e9bd2e737 i7core_edac: Adds... |
80 |
|
a0c36a1f0 i7core_edac: Add ... |
81 82 83 84 85 |
/* OFFSETS for Device 3 Function 0 */ #define MC_CONTROL 0x48 #define MC_STATUS 0x4c #define MC_MAX_DOD 0x64 |
442305b15 i7core_edac: Add ... |
86 87 88 89 90 91 92 93 94 95 96 |
/* * OFFSETS for Device 3 Function 4, as inicated on Xeon 5500 datasheet: * http://www.arrownac.com/manufacturers/intel/s/nehalem/5500-datasheet-v2.pdf */ #define MC_TEST_ERR_RCV1 0x60 #define DIMM2_COR_ERR(r) ((r) & 0x7fff) #define MC_TEST_ERR_RCV0 0x64 #define DIMM1_COR_ERR(r) (((r) >> 16) & 0x7fff) #define DIMM0_COR_ERR(r) ((r) & 0x7fff) |
b4e8f0b6e i7core_edac: Use ... |
97 |
/* OFFSETS for Device 3 Function 2, as inicated on Xeon 5500 datasheet */ |
e8b6a1271 i7core_edac: Add ... |
98 99 100 101 102 103 104 |
#define MC_SSRCONTROL 0x48 #define SSR_MODE_DISABLE 0x00 #define SSR_MODE_ENABLE 0x01 #define SSR_MODE_MASK 0x03 #define MC_SCRUB_CONTROL 0x4c #define STARTSCRUB (1 << 24) |
535e9c78e i7core_edac: scru... |
105 |
#define SCRUBINTERVAL_MASK 0xffffff |
e8b6a1271 i7core_edac: Add ... |
106 |
|
b4e8f0b6e i7core_edac: Use ... |
107 108 109 110 111 112 113 114 115 |
#define MC_COR_ECC_CNT_0 0x80 #define MC_COR_ECC_CNT_1 0x84 #define MC_COR_ECC_CNT_2 0x88 #define MC_COR_ECC_CNT_3 0x8c #define MC_COR_ECC_CNT_4 0x90 #define MC_COR_ECC_CNT_5 0x94 #define DIMM_TOP_COR_ERR(r) (((r) >> 16) & 0x7fff) #define DIMM_BOT_COR_ERR(r) ((r) & 0x7fff) |
a0c36a1f0 i7core_edac: Add ... |
116 |
/* OFFSETS for Devices 4,5 and 6 Function 0 */ |
0b2b7b7ec i7core_edac: Add ... |
117 118 119 120 121 |
#define MC_CHANNEL_DIMM_INIT_PARAMS 0x58 #define THREE_DIMMS_PRESENT (1 << 24) #define SINGLE_QUAD_RANK_PRESENT (1 << 23) #define QUAD_RANK_PRESENT (1 << 22) #define REGISTERED_DIMM (1 << 15) |
f122a8922 i7core_edac: Show... |
122 123 124 |
#define MC_CHANNEL_MAPPER 0x60 #define RDLCH(r, ch) ((((r) >> (3 + (ch * 6))) & 0x07) - 1) #define WRLCH(r, ch) ((((r) >> (ch * 6)) & 0x07) - 1) |
0b2b7b7ec i7core_edac: Add ... |
125 126 |
#define MC_CHANNEL_RANK_PRESENT 0x7c #define RANK_PRESENT_MASK 0xffff |
a0c36a1f0 i7core_edac: Add ... |
127 |
#define MC_CHANNEL_ADDR_MATCH 0xf0 |
194a40fea i7core_edac: Add ... |
128 129 130 131 132 133 134 135 136 137 |
#define MC_CHANNEL_ERROR_MASK 0xf8 #define MC_CHANNEL_ERROR_INJECT 0xfc #define INJECT_ADDR_PARITY 0x10 #define INJECT_ECC 0x08 #define MASK_CACHELINE 0x06 #define MASK_FULL_CACHELINE 0x06 #define MASK_MSB32_CACHELINE 0x04 #define MASK_LSB32_CACHELINE 0x02 #define NO_MASK_CACHELINE 0x00 #define REPEAT_EN 0x01 |
a0c36a1f0 i7core_edac: Add ... |
138 |
|
0b2b7b7ec i7core_edac: Add ... |
139 |
/* OFFSETS for Devices 4,5 and 6 Function 1 */ |
b990538a7 i7core_edac: Codi... |
140 |
|
0b2b7b7ec i7core_edac: Add ... |
141 142 143 144 145 146 147 |
#define MC_DOD_CH_DIMM0 0x48 #define MC_DOD_CH_DIMM1 0x4c #define MC_DOD_CH_DIMM2 0x50 #define RANKOFFSET_MASK ((1 << 12) | (1 << 11) | (1 << 10)) #define RANKOFFSET(x) ((x & RANKOFFSET_MASK) >> 10) #define DIMM_PRESENT_MASK (1 << 9) #define DIMM_PRESENT(x) (((x) & DIMM_PRESENT_MASK) >> 9) |
854d33499 i7core_edac: Get ... |
148 149 150 151 |
#define MC_DOD_NUMBANK_MASK ((1 << 8) | (1 << 7)) #define MC_DOD_NUMBANK(x) (((x) & MC_DOD_NUMBANK_MASK) >> 7) #define MC_DOD_NUMRANK_MASK ((1 << 6) | (1 << 5)) #define MC_DOD_NUMRANK(x) (((x) & MC_DOD_NUMRANK_MASK) >> 5) |
41fcb7fee i7core_edac: Codi... |
152 |
#define MC_DOD_NUMROW_MASK ((1 << 4) | (1 << 3) | (1 << 2)) |
5566cb7c9 i7core_edac: Memo... |
153 |
#define MC_DOD_NUMROW(x) (((x) & MC_DOD_NUMROW_MASK) >> 2) |
854d33499 i7core_edac: Get ... |
154 155 |
#define MC_DOD_NUMCOL_MASK 3 #define MC_DOD_NUMCOL(x) ((x) & MC_DOD_NUMCOL_MASK) |
0b2b7b7ec i7core_edac: Add ... |
156 |
|
f122a8922 i7core_edac: Show... |
157 |
#define MC_RANK_PRESENT 0x7c |
0b2b7b7ec i7core_edac: Add ... |
158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 |
#define MC_SAG_CH_0 0x80 #define MC_SAG_CH_1 0x84 #define MC_SAG_CH_2 0x88 #define MC_SAG_CH_3 0x8c #define MC_SAG_CH_4 0x90 #define MC_SAG_CH_5 0x94 #define MC_SAG_CH_6 0x98 #define MC_SAG_CH_7 0x9c #define MC_RIR_LIMIT_CH_0 0x40 #define MC_RIR_LIMIT_CH_1 0x44 #define MC_RIR_LIMIT_CH_2 0x48 #define MC_RIR_LIMIT_CH_3 0x4C #define MC_RIR_LIMIT_CH_4 0x50 #define MC_RIR_LIMIT_CH_5 0x54 #define MC_RIR_LIMIT_CH_6 0x58 #define MC_RIR_LIMIT_CH_7 0x5C #define MC_RIR_LIMIT_MASK ((1 << 10) - 1) #define MC_RIR_WAY_CH 0x80 #define MC_RIR_WAY_OFFSET_MASK (((1 << 14) - 1) & ~0x7) #define MC_RIR_WAY_RANK_MASK 0x7 |
a0c36a1f0 i7core_edac: Add ... |
180 181 182 183 184 |
/* * i7core structs */ #define NUM_CHANS 3 |
442305b15 i7core_edac: Add ... |
185 186 187 |
#define MAX_DIMMS 3 /* Max DIMMS per channel */ #define MAX_MCR_FUNC 4 #define MAX_CHAN_FUNC 3 |
a0c36a1f0 i7core_edac: Add ... |
188 189 190 191 192 |
struct i7core_info { u32 mc_control; u32 mc_status; u32 max_dod; |
f122a8922 i7core_edac: Show... |
193 |
u32 ch_map; |
a0c36a1f0 i7core_edac: Add ... |
194 |
}; |
194a40fea i7core_edac: Add ... |
195 196 197 198 199 200 201 202 203 204 205 |
struct i7core_inject { int enable; u32 section; u32 type; u32 eccmask; /* Error address mask */ int channel, dimm, rank, bank, page, col; }; |
0b2b7b7ec i7core_edac: Add ... |
206 |
struct i7core_channel { |
442305b15 i7core_edac: Add ... |
207 208 |
u32 ranks; u32 dimms; |
0b2b7b7ec i7core_edac: Add ... |
209 |
}; |
8f3319075 i7core_edac: Regi... |
210 |
struct pci_id_descr { |
66607706c Dynamically alloc... |
211 212 213 |
int dev; int func; int dev_id; |
de06eeef5 i7core_edac: Use ... |
214 |
int optional; |
8f3319075 i7core_edac: Regi... |
215 |
}; |
bd9e19ca4 Add support for W... |
216 |
struct pci_id_table { |
1288c18f4 i7core_edac: Prop... |
217 218 |
const struct pci_id_descr *descr; int n_devs; |
bd9e19ca4 Add support for W... |
219 |
}; |
f47429494 i7core_edac: crea... |
220 221 222 223 |
struct i7core_dev { struct list_head list; u8 socket; struct pci_dev **pdev; |
de06eeef5 i7core_edac: Use ... |
224 |
int n_devs; |
f47429494 i7core_edac: crea... |
225 226 |
struct mem_ctl_info *mci; }; |
a0c36a1f0 i7core_edac: Add ... |
227 |
struct i7core_pvt { |
f47429494 i7core_edac: crea... |
228 229 230 231 232 |
struct pci_dev *pci_noncore; struct pci_dev *pci_mcr[MAX_MCR_FUNC + 1]; struct pci_dev *pci_ch[NUM_CHANS][MAX_CHAN_FUNC + 1]; struct i7core_dev *i7core_dev; |
67166af4a i7core_edac: add ... |
233 |
|
a0c36a1f0 i7core_edac: Add ... |
234 |
struct i7core_info info; |
194a40fea i7core_edac: Add ... |
235 |
struct i7core_inject inject; |
f47429494 i7core_edac: crea... |
236 |
struct i7core_channel channel[NUM_CHANS]; |
67166af4a i7core_edac: add ... |
237 |
|
f47429494 i7core_edac: crea... |
238 239 |
int ce_count_available; int csrow_map[NUM_CHANS][MAX_DIMMS]; |
b4e8f0b6e i7core_edac: Use ... |
240 241 |
/* ECC corrected errors counts per udimm */ |
f47429494 i7core_edac: crea... |
242 243 |
unsigned long udimm_ce_count[MAX_DIMMS]; int udimm_last_ce_count[MAX_DIMMS]; |
b4e8f0b6e i7core_edac: Use ... |
244 |
/* ECC corrected errors counts per rdimm */ |
f47429494 i7core_edac: crea... |
245 246 |
unsigned long rdimm_ce_count[NUM_CHANS][MAX_DIMMS]; int rdimm_last_ce_count[NUM_CHANS][MAX_DIMMS]; |
442305b15 i7core_edac: Add ... |
247 |
|
27100db0e i7core_edac: Don'... |
248 |
bool is_registered, enable_scrub; |
14d2c0834 i7core: Use regis... |
249 |
|
ca9c90ba0 i7core_edac: Use ... |
250 |
/* Fifo double buffers */ |
d5381642a i7core_edac: Add ... |
251 |
struct mce mce_entry[MCE_LOG_LEN]; |
ca9c90ba0 i7core_edac: Use ... |
252 253 254 255 256 257 258 |
struct mce mce_outentry[MCE_LOG_LEN]; /* Fifo in/out counters */ unsigned mce_in, mce_out; /* Count indicator to show errors not got */ unsigned mce_overrun; |
939747bd6 i7core_edac: Be s... |
259 |
|
535e9c78e i7core_edac: scru... |
260 261 |
/* DCLK Frequency used for computing scrub rate */ int dclk_freq; |
939747bd6 i7core_edac: Be s... |
262 263 |
/* Struct to control EDAC polling */ struct edac_pci_ctl_info *i7core_pci; |
a0c36a1f0 i7core_edac: Add ... |
264 |
}; |
8f3319075 i7core_edac: Regi... |
265 266 267 268 |
#define PCI_DESCR(device, function, device_id) \ .dev = (device), \ .func = (function), \ .dev_id = (device_id) |
1288c18f4 i7core_edac: Prop... |
269 |
static const struct pci_id_descr pci_dev_descr_i7core_nehalem[] = { |
8f3319075 i7core_edac: Regi... |
270 271 272 |
/* Memory controller */ { PCI_DESCR(3, 0, PCI_DEVICE_ID_INTEL_I7_MCR) }, { PCI_DESCR(3, 1, PCI_DEVICE_ID_INTEL_I7_MC_TAD) }, |
224e871f3 i7core_edac: Fix ... |
273 |
/* Exists only for RDIMM */ |
de06eeef5 i7core_edac: Use ... |
274 |
{ PCI_DESCR(3, 2, PCI_DEVICE_ID_INTEL_I7_MC_RAS), .optional = 1 }, |
8f3319075 i7core_edac: Regi... |
275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 |
{ PCI_DESCR(3, 4, PCI_DEVICE_ID_INTEL_I7_MC_TEST) }, /* Channel 0 */ { PCI_DESCR(4, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH0_CTRL) }, { PCI_DESCR(4, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH0_ADDR) }, { PCI_DESCR(4, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH0_RANK) }, { PCI_DESCR(4, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH0_TC) }, /* Channel 1 */ { PCI_DESCR(5, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH1_CTRL) }, { PCI_DESCR(5, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH1_ADDR) }, { PCI_DESCR(5, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH1_RANK) }, { PCI_DESCR(5, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH1_TC) }, /* Channel 2 */ { PCI_DESCR(6, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH2_CTRL) }, { PCI_DESCR(6, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH2_ADDR) }, { PCI_DESCR(6, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH2_RANK) }, { PCI_DESCR(6, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH2_TC) }, |
224e871f3 i7core_edac: Fix ... |
294 295 296 297 298 299 300 301 302 |
/* Generic Non-core registers */ /* * This is the PCI device on i7core and on Xeon 35xx (8086:2c41) * On Xeon 55xx, however, it has a different id (8086:2c40). So, * the probing code needs to test for the other address in case of * failure of this one */ { PCI_DESCR(0, 0, PCI_DEVICE_ID_INTEL_I7_NONCORE) }, |
a0c36a1f0 i7core_edac: Add ... |
303 |
}; |
8f3319075 i7core_edac: Regi... |
304 |
|
1288c18f4 i7core_edac: Prop... |
305 |
static const struct pci_id_descr pci_dev_descr_lynnfield[] = { |
52a2e4fc3 i7core_edac: Add ... |
306 307 308 309 310 311 312 313 |
{ PCI_DESCR( 3, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_MCR) }, { PCI_DESCR( 3, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_TAD) }, { PCI_DESCR( 3, 4, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_TEST) }, { PCI_DESCR( 4, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_CTRL) }, { PCI_DESCR( 4, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_ADDR) }, { PCI_DESCR( 4, 2, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_RANK) }, { PCI_DESCR( 4, 3, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_TC) }, |
508fa179f i7core_edac: Fix ... |
314 315 316 317 |
{ PCI_DESCR( 5, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_CTRL) }, { PCI_DESCR( 5, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_ADDR) }, { PCI_DESCR( 5, 2, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_RANK) }, { PCI_DESCR( 5, 3, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_TC) }, |
224e871f3 i7core_edac: Fix ... |
318 319 320 321 322 323 |
/* * This is the PCI device has an alternate address on some * processors like Core i7 860 */ { PCI_DESCR( 0, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE) }, |
52a2e4fc3 i7core_edac: Add ... |
324 |
}; |
1288c18f4 i7core_edac: Prop... |
325 |
static const struct pci_id_descr pci_dev_descr_i7core_westmere[] = { |
bd9e19ca4 Add support for W... |
326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 |
/* Memory controller */ { PCI_DESCR(3, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_MCR_REV2) }, { PCI_DESCR(3, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_TAD_REV2) }, /* Exists only for RDIMM */ { PCI_DESCR(3, 2, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_RAS_REV2), .optional = 1 }, { PCI_DESCR(3, 4, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_TEST_REV2) }, /* Channel 0 */ { PCI_DESCR(4, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_CTRL_REV2) }, { PCI_DESCR(4, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_ADDR_REV2) }, { PCI_DESCR(4, 2, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_RANK_REV2) }, { PCI_DESCR(4, 3, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_TC_REV2) }, /* Channel 1 */ { PCI_DESCR(5, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_CTRL_REV2) }, { PCI_DESCR(5, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_ADDR_REV2) }, { PCI_DESCR(5, 2, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_RANK_REV2) }, { PCI_DESCR(5, 3, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_TC_REV2) }, /* Channel 2 */ { PCI_DESCR(6, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH2_CTRL_REV2) }, { PCI_DESCR(6, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH2_ADDR_REV2) }, { PCI_DESCR(6, 2, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH2_RANK_REV2) }, { PCI_DESCR(6, 3, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH2_TC_REV2) }, |
224e871f3 i7core_edac: Fix ... |
350 351 352 |
/* Generic Non-core registers */ { PCI_DESCR(0, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE_REV2) }, |
bd9e19ca4 Add support for W... |
353 |
}; |
1288c18f4 i7core_edac: Prop... |
354 355 |
#define PCI_ID_TABLE_ENTRY(A) { .descr=A, .n_devs = ARRAY_SIZE(A) } static const struct pci_id_table pci_dev_table[] = { |
bd9e19ca4 Add support for W... |
356 357 358 |
PCI_ID_TABLE_ENTRY(pci_dev_descr_i7core_nehalem), PCI_ID_TABLE_ENTRY(pci_dev_descr_lynnfield), PCI_ID_TABLE_ENTRY(pci_dev_descr_i7core_westmere), |
3c52cc57c i7core_edac: prop... |
359 |
{0,} /* 0 terminated list. */ |
bd9e19ca4 Add support for W... |
360 |
}; |
8f3319075 i7core_edac: Regi... |
361 362 |
/* * pci_device_id table for which devices we are looking for |
8f3319075 i7core_edac: Regi... |
363 364 |
*/ static const struct pci_device_id i7core_pci_tbl[] __devinitdata = { |
d1fd4fb69 i7core_edac: Add ... |
365 |
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_X58_HUB_MGMT)}, |
f05da2f78 i7core: add suppo... |
366 |
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_LYNNFIELD_QPI_LINK0)}, |
8f3319075 i7core_edac: Regi... |
367 368 |
{0,} /* 0 terminated list. */ }; |
a0c36a1f0 i7core_edac: Add ... |
369 370 371 372 373 |
/**************************************************************************** Anciliary status routines ****************************************************************************/ /* MC_CONTROL bits */ |
ef708b53b i7core_edac: Add ... |
374 375 |
#define CH_ACTIVE(pvt, ch) ((pvt)->info.mc_control & (1 << (8 + ch))) #define ECCx8(pvt) ((pvt)->info.mc_control & (1 << 1)) |
a0c36a1f0 i7core_edac: Add ... |
376 377 |
/* MC_STATUS bits */ |
61053fded i7core_edac: Fix ... |
378 |
#define ECC_ENABLED(pvt) ((pvt)->info.mc_status & (1 << 4)) |
ef708b53b i7core_edac: Add ... |
379 |
#define CH_DISABLED(pvt, ch) ((pvt)->info.mc_status & (1 << ch)) |
a0c36a1f0 i7core_edac: Add ... |
380 381 |
/* MC_MAX_DOD read functions */ |
854d33499 i7core_edac: Get ... |
382 |
static inline int numdimms(u32 dimms) |
a0c36a1f0 i7core_edac: Add ... |
383 |
{ |
854d33499 i7core_edac: Get ... |
384 |
return (dimms & 0x3) + 1; |
a0c36a1f0 i7core_edac: Add ... |
385 |
} |
854d33499 i7core_edac: Get ... |
386 |
static inline int numrank(u32 rank) |
a0c36a1f0 i7core_edac: Add ... |
387 388 |
{ static int ranks[4] = { 1, 2, 4, -EINVAL }; |
854d33499 i7core_edac: Get ... |
389 |
return ranks[rank & 0x3]; |
a0c36a1f0 i7core_edac: Add ... |
390 |
} |
854d33499 i7core_edac: Get ... |
391 |
static inline int numbank(u32 bank) |
a0c36a1f0 i7core_edac: Add ... |
392 393 |
{ static int banks[4] = { 4, 8, 16, -EINVAL }; |
854d33499 i7core_edac: Get ... |
394 |
return banks[bank & 0x3]; |
a0c36a1f0 i7core_edac: Add ... |
395 |
} |
854d33499 i7core_edac: Get ... |
396 |
static inline int numrow(u32 row) |
a0c36a1f0 i7core_edac: Add ... |
397 398 399 400 401 |
{ static int rows[8] = { 1 << 12, 1 << 13, 1 << 14, 1 << 15, 1 << 16, -EINVAL, -EINVAL, -EINVAL, }; |
854d33499 i7core_edac: Get ... |
402 |
return rows[row & 0x7]; |
a0c36a1f0 i7core_edac: Add ... |
403 |
} |
854d33499 i7core_edac: Get ... |
404 |
static inline int numcol(u32 col) |
a0c36a1f0 i7core_edac: Add ... |
405 406 407 408 |
{ static int cols[8] = { 1 << 10, 1 << 11, 1 << 12, -EINVAL, }; |
854d33499 i7core_edac: Get ... |
409 |
return cols[col & 0x3]; |
a0c36a1f0 i7core_edac: Add ... |
410 |
} |
f47429494 i7core_edac: crea... |
411 |
static struct i7core_dev *get_i7core_dev(u8 socket) |
66607706c Dynamically alloc... |
412 413 414 415 416 417 418 419 420 421 |
{ struct i7core_dev *i7core_dev; list_for_each_entry(i7core_dev, &i7core_edac_list, list) { if (i7core_dev->socket == socket) return i7core_dev; } return NULL; } |
848b2f7ed i7core_edac: Intr... |
422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 |
static struct i7core_dev *alloc_i7core_dev(u8 socket, const struct pci_id_table *table) { struct i7core_dev *i7core_dev; i7core_dev = kzalloc(sizeof(*i7core_dev), GFP_KERNEL); if (!i7core_dev) return NULL; i7core_dev->pdev = kzalloc(sizeof(*i7core_dev->pdev) * table->n_devs, GFP_KERNEL); if (!i7core_dev->pdev) { kfree(i7core_dev); return NULL; } i7core_dev->socket = socket; i7core_dev->n_devs = table->n_devs; list_add_tail(&i7core_dev->list, &i7core_edac_list); return i7core_dev; } |
2aa9be448 i7core_edac: Intr... |
444 445 446 447 448 449 |
static void free_i7core_dev(struct i7core_dev *i7core_dev) { list_del(&i7core_dev->list); kfree(i7core_dev->pdev); kfree(i7core_dev); } |
a0c36a1f0 i7core_edac: Add ... |
450 451 452 |
/**************************************************************************** Memory check routines ****************************************************************************/ |
67166af4a i7core_edac: add ... |
453 454 |
static struct pci_dev *get_pdev_slot_func(u8 socket, unsigned slot, unsigned func) |
ef708b53b i7core_edac: Add ... |
455 |
{ |
66607706c Dynamically alloc... |
456 |
struct i7core_dev *i7core_dev = get_i7core_dev(socket); |
ef708b53b i7core_edac: Add ... |
457 |
int i; |
ef708b53b i7core_edac: Add ... |
458 |
|
66607706c Dynamically alloc... |
459 460 |
if (!i7core_dev) return NULL; |
de06eeef5 i7core_edac: Use ... |
461 |
for (i = 0; i < i7core_dev->n_devs; i++) { |
66607706c Dynamically alloc... |
462 |
if (!i7core_dev->pdev[i]) |
ef708b53b i7core_edac: Add ... |
463 |
continue; |
66607706c Dynamically alloc... |
464 465 466 |
if (PCI_SLOT(i7core_dev->pdev[i]->devfn) == slot && PCI_FUNC(i7core_dev->pdev[i]->devfn) == func) { return i7core_dev->pdev[i]; |
ef708b53b i7core_edac: Add ... |
467 468 |
} } |
eb94fc402 i7core_edac: fill... |
469 470 |
return NULL; } |
ec6df24c1 i7core: better do... |
471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 |
/** * i7core_get_active_channels() - gets the number of channels and csrows * @socket: Quick Path Interconnect socket * @channels: Number of channels that will be returned * @csrows: Number of csrows found * * Since EDAC core needs to know in advance the number of available channels * and csrows, in order to allocate memory for csrows/channels, it is needed * to run two similar steps. At the first step, implemented on this function, * it checks the number of csrows/channels present at one socket. * this is used in order to properly allocate the size of mci components. * * It should be noticed that none of the current available datasheets explain * or even mention how csrows are seen by the memory controller. So, we need * to add a fake description for csrows. * So, this driver is attributing one DIMM memory for one csrow. */ |
1288c18f4 i7core_edac: Prop... |
488 |
static int i7core_get_active_channels(const u8 socket, unsigned *channels, |
67166af4a i7core_edac: add ... |
489 |
unsigned *csrows) |
eb94fc402 i7core_edac: fill... |
490 491 492 493 494 495 496 |
{ struct pci_dev *pdev = NULL; int i, j; u32 status, control; *channels = 0; *csrows = 0; |
67166af4a i7core_edac: add ... |
497 |
pdev = get_pdev_slot_func(socket, 3, 0); |
b7c761512 i7core_edac: Impr... |
498 |
if (!pdev) { |
67166af4a i7core_edac: add ... |
499 500 501 |
i7core_printk(KERN_ERR, "Couldn't find socket %d fn 3.0!!! ", socket); |
ef708b53b i7core_edac: Add ... |
502 |
return -ENODEV; |
b7c761512 i7core_edac: Impr... |
503 |
} |
ef708b53b i7core_edac: Add ... |
504 505 506 507 508 509 |
/* Device 3 function 0 reads */ pci_read_config_dword(pdev, MC_STATUS, &status); pci_read_config_dword(pdev, MC_CONTROL, &control); for (i = 0; i < NUM_CHANS; i++) { |
eb94fc402 i7core_edac: fill... |
510 |
u32 dimm_dod[3]; |
ef708b53b i7core_edac: Add ... |
511 512 513 514 515 |
/* Check if the channel is active */ if (!(control & (1 << (8 + i)))) continue; /* Check if the channel is disabled */ |
41fcb7fee i7core_edac: Codi... |
516 |
if (status & (1 << i)) |
ef708b53b i7core_edac: Add ... |
517 |
continue; |
ef708b53b i7core_edac: Add ... |
518 |
|
67166af4a i7core_edac: add ... |
519 |
pdev = get_pdev_slot_func(socket, i + 4, 1); |
eb94fc402 i7core_edac: fill... |
520 |
if (!pdev) { |
67166af4a i7core_edac: add ... |
521 522 523 524 |
i7core_printk(KERN_ERR, "Couldn't find socket %d " "fn %d.%d!!! ", socket, i + 4, 1); |
eb94fc402 i7core_edac: fill... |
525 526 527 528 529 530 531 532 533 |
return -ENODEV; } /* Devices 4-6 function 1 */ pci_read_config_dword(pdev, MC_DOD_CH_DIMM0, &dimm_dod[0]); pci_read_config_dword(pdev, MC_DOD_CH_DIMM1, &dimm_dod[1]); pci_read_config_dword(pdev, MC_DOD_CH_DIMM2, &dimm_dod[2]); |
ef708b53b i7core_edac: Add ... |
534 |
(*channels)++; |
eb94fc402 i7core_edac: fill... |
535 536 537 538 539 540 |
for (j = 0; j < 3; j++) { if (!DIMM_PRESENT(dimm_dod[j])) continue; (*csrows)++; } |
ef708b53b i7core_edac: Add ... |
541 |
} |
c77720b95 i7core: fix get_d... |
542 543 |
debugf0("Number of active channels on socket %d: %d ", |
67166af4a i7core_edac: add ... |
544 |
socket, *channels); |
1c6fed808 i7core_edac: Prop... |
545 |
|
ef708b53b i7core_edac: Add ... |
546 547 |
return 0; } |
2e5185f7f i7core_edac: Remo... |
548 |
static int get_dimm_config(const struct mem_ctl_info *mci) |
a0c36a1f0 i7core_edac: Add ... |
549 550 |
{ struct i7core_pvt *pvt = mci->pvt_info; |
1c6fed808 i7core_edac: Prop... |
551 |
struct csrow_info *csr; |
854d33499 i7core_edac: Get ... |
552 |
struct pci_dev *pdev; |
ba6c5c62e i7core_edac: maps... |
553 |
int i, j; |
2e5185f7f i7core_edac: Remo... |
554 |
int csrow = 0; |
5566cb7c9 i7core_edac: Memo... |
555 |
unsigned long last_page = 0; |
1c6fed808 i7core_edac: Prop... |
556 |
enum edac_type mode; |
854d33499 i7core_edac: Get ... |
557 |
enum mem_type mtype; |
a0c36a1f0 i7core_edac: Add ... |
558 |
|
854d33499 i7core_edac: Get ... |
559 |
/* Get data from the MC register, function 0 */ |
f47429494 i7core_edac: crea... |
560 |
pdev = pvt->pci_mcr[0]; |
7dd6953c5 i7core_edac: Add ... |
561 |
if (!pdev) |
8f3319075 i7core_edac: Regi... |
562 |
return -ENODEV; |
f122a8922 i7core_edac: Show... |
563 |
/* Device 3 function 0 reads */ |
7dd6953c5 i7core_edac: Add ... |
564 565 566 567 |
pci_read_config_dword(pdev, MC_CONTROL, &pvt->info.mc_control); pci_read_config_dword(pdev, MC_STATUS, &pvt->info.mc_status); pci_read_config_dword(pdev, MC_MAX_DOD, &pvt->info.max_dod); pci_read_config_dword(pdev, MC_CHANNEL_MAPPER, &pvt->info.ch_map); |
f122a8922 i7core_edac: Show... |
568 |
|
17cb7b0cf i7core_edac: Some... |
569 570 |
debugf0("QPI %d control=0x%08x status=0x%08x dod=0x%08x map=0x%08x ", |
4af91889e i7core_edac: Avoi... |
571 |
pvt->i7core_dev->socket, pvt->info.mc_control, pvt->info.mc_status, |
f122a8922 i7core_edac: Show... |
572 |
pvt->info.max_dod, pvt->info.ch_map); |
a0c36a1f0 i7core_edac: Add ... |
573 |
|
1c6fed808 i7core_edac: Prop... |
574 |
if (ECC_ENABLED(pvt)) { |
41fcb7fee i7core_edac: Codi... |
575 576 |
debugf0("ECC enabled with x%d SDCC ", ECCx8(pvt) ? 8 : 4); |
1c6fed808 i7core_edac: Prop... |
577 578 579 580 581 |
if (ECCx8(pvt)) mode = EDAC_S8ECD8ED; else mode = EDAC_S4ECD4ED; } else { |
a0c36a1f0 i7core_edac: Add ... |
582 583 |
debugf0("ECC disabled "); |
1c6fed808 i7core_edac: Prop... |
584 585 |
mode = EDAC_NONE; } |
a0c36a1f0 i7core_edac: Add ... |
586 587 |
/* FIXME: need to handle the error codes */ |
17cb7b0cf i7core_edac: Some... |
588 589 590 |
debugf0("DOD Max limits: DIMMS: %d, %d-ranked, %d-banked " "x%x x 0x%x ", |
854d33499 i7core_edac: Get ... |
591 592 |
numdimms(pvt->info.max_dod), numrank(pvt->info.max_dod >> 2), |
276b824c3 i7core_edac: some... |
593 |
numbank(pvt->info.max_dod >> 4), |
854d33499 i7core_edac: Get ... |
594 595 |
numrow(pvt->info.max_dod >> 6), numcol(pvt->info.max_dod >> 9)); |
a0c36a1f0 i7core_edac: Add ... |
596 |
|
0b2b7b7ec i7core_edac: Add ... |
597 |
for (i = 0; i < NUM_CHANS; i++) { |
854d33499 i7core_edac: Get ... |
598 |
u32 data, dimm_dod[3], value[8]; |
0b2b7b7ec i7core_edac: Add ... |
599 |
|
52a2e4fc3 i7core_edac: Add ... |
600 601 |
if (!pvt->pci_ch[i][0]) continue; |
0b2b7b7ec i7core_edac: Add ... |
602 603 604 605 606 607 608 609 610 611 |
if (!CH_ACTIVE(pvt, i)) { debugf0("Channel %i is not active ", i); continue; } if (CH_DISABLED(pvt, i)) { debugf0("Channel %i is disabled ", i); continue; } |
f122a8922 i7core_edac: Show... |
612 |
/* Devices 4-6 function 0 */ |
f47429494 i7core_edac: crea... |
613 |
pci_read_config_dword(pvt->pci_ch[i][0], |
0b2b7b7ec i7core_edac: Add ... |
614 |
MC_CHANNEL_DIMM_INIT_PARAMS, &data); |
f47429494 i7core_edac: crea... |
615 |
pvt->channel[i].ranks = (data & QUAD_RANK_PRESENT) ? |
67166af4a i7core_edac: add ... |
616 |
4 : 2; |
0b2b7b7ec i7core_edac: Add ... |
617 |
|
854d33499 i7core_edac: Get ... |
618 619 |
if (data & REGISTERED_DIMM) mtype = MEM_RDDR3; |
14d2c0834 i7core: Use regis... |
620 |
else |
854d33499 i7core_edac: Get ... |
621 622 |
mtype = MEM_DDR3; #if 0 |
0b2b7b7ec i7core_edac: Add ... |
623 624 625 626 627 628 |
if (data & THREE_DIMMS_PRESENT) pvt->channel[i].dimms = 3; else if (data & SINGLE_QUAD_RANK_PRESENT) pvt->channel[i].dimms = 1; else pvt->channel[i].dimms = 2; |
854d33499 i7core_edac: Get ... |
629 630 631 |
#endif /* Devices 4-6 function 1 */ |
f47429494 i7core_edac: crea... |
632 |
pci_read_config_dword(pvt->pci_ch[i][1], |
854d33499 i7core_edac: Get ... |
633 |
MC_DOD_CH_DIMM0, &dimm_dod[0]); |
f47429494 i7core_edac: crea... |
634 |
pci_read_config_dword(pvt->pci_ch[i][1], |
854d33499 i7core_edac: Get ... |
635 |
MC_DOD_CH_DIMM1, &dimm_dod[1]); |
f47429494 i7core_edac: crea... |
636 |
pci_read_config_dword(pvt->pci_ch[i][1], |
854d33499 i7core_edac: Get ... |
637 |
MC_DOD_CH_DIMM2, &dimm_dod[2]); |
0b2b7b7ec i7core_edac: Add ... |
638 |
|
1c6fed808 i7core_edac: Prop... |
639 |
debugf0("Ch%d phy rd%d, wr%d (0x%08x): " |
854d33499 i7core_edac: Get ... |
640 641 |
"%d ranks, %cDIMMs ", |
1c6fed808 i7core_edac: Prop... |
642 643 644 |
i, RDLCH(pvt->info.ch_map, i), WRLCH(pvt->info.ch_map, i), data, |
f47429494 i7core_edac: crea... |
645 |
pvt->channel[i].ranks, |
41fcb7fee i7core_edac: Codi... |
646 |
(data & REGISTERED_DIMM) ? 'R' : 'U'); |
854d33499 i7core_edac: Get ... |
647 648 649 |
for (j = 0; j < 3; j++) { u32 banks, ranks, rows, cols; |
5566cb7c9 i7core_edac: Memo... |
650 |
u32 size, npages; |
854d33499 i7core_edac: Get ... |
651 652 653 654 655 656 657 658 |
if (!DIMM_PRESENT(dimm_dod[j])) continue; banks = numbank(MC_DOD_NUMBANK(dimm_dod[j])); ranks = numrank(MC_DOD_NUMRANK(dimm_dod[j])); rows = numrow(MC_DOD_NUMROW(dimm_dod[j])); cols = numcol(MC_DOD_NUMCOL(dimm_dod[j])); |
5566cb7c9 i7core_edac: Memo... |
659 660 |
/* DDR3 has 8 I/O banks */ size = (rows * cols * banks * ranks) >> (20 - 3); |
f47429494 i7core_edac: crea... |
661 |
pvt->channel[i].dimms++; |
854d33499 i7core_edac: Get ... |
662 |
|
17cb7b0cf i7core_edac: Some... |
663 664 665 666 |
debugf0("\tdimm %d %d Mb offset: %x, " "bank: %d, rank: %d, row: %#x, col: %#x ", j, size, |
854d33499 i7core_edac: Get ... |
667 668 |
RANKOFFSET(dimm_dod[j]), banks, ranks, rows, cols); |
e9144601d i7core_edac: move... |
669 |
npages = MiB_TO_PAGES(size); |
5566cb7c9 i7core_edac: Memo... |
670 |
|
2e5185f7f i7core_edac: Remo... |
671 |
csr = &mci->csrows[csrow]; |
5566cb7c9 i7core_edac: Memo... |
672 673 674 675 |
csr->first_page = last_page + 1; last_page += npages; csr->last_page = last_page; csr->nr_pages = npages; |
854d33499 i7core_edac: Get ... |
676 |
csr->page_mask = 0; |
eb94fc402 i7core_edac: fill... |
677 |
csr->grain = 8; |
2e5185f7f i7core_edac: Remo... |
678 |
csr->csrow_idx = csrow; |
eb94fc402 i7core_edac: fill... |
679 680 681 682 |
csr->nr_channels = 1; csr->channels[0].chan_idx = i; csr->channels[0].ce_count = 0; |
854d33499 i7core_edac: Get ... |
683 |
|
2e5185f7f i7core_edac: Remo... |
684 |
pvt->csrow_map[i][j] = csrow; |
b4e8f0b6e i7core_edac: Use ... |
685 |
|
854d33499 i7core_edac: Get ... |
686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 |
switch (banks) { case 4: csr->dtype = DEV_X4; break; case 8: csr->dtype = DEV_X8; break; case 16: csr->dtype = DEV_X16; break; default: csr->dtype = DEV_UNKNOWN; } csr->edac_mode = mode; csr->mtype = mtype; |
767ba4a52 i7core_edac: Init... |
702 703 704 705 |
snprintf(csr->channels[0].label, sizeof(csr->channels[0].label), "CPU#%uChannel#%u_DIMM#%u", pvt->i7core_dev->socket, i, j); |
854d33499 i7core_edac: Get ... |
706 |
|
2e5185f7f i7core_edac: Remo... |
707 |
csrow++; |
854d33499 i7core_edac: Get ... |
708 |
} |
1c6fed808 i7core_edac: Prop... |
709 |
|
854d33499 i7core_edac: Get ... |
710 711 712 713 714 715 716 717 |
pci_read_config_dword(pdev, MC_SAG_CH_0, &value[0]); pci_read_config_dword(pdev, MC_SAG_CH_1, &value[1]); pci_read_config_dword(pdev, MC_SAG_CH_2, &value[2]); pci_read_config_dword(pdev, MC_SAG_CH_3, &value[3]); pci_read_config_dword(pdev, MC_SAG_CH_4, &value[4]); pci_read_config_dword(pdev, MC_SAG_CH_5, &value[5]); pci_read_config_dword(pdev, MC_SAG_CH_6, &value[6]); pci_read_config_dword(pdev, MC_SAG_CH_7, &value[7]); |
17cb7b0cf i7core_edac: Some... |
718 719 |
debugf1("\t[%i] DIVBY3\tREMOVED\tOFFSET ", i); |
854d33499 i7core_edac: Get ... |
720 |
for (j = 0; j < 8; j++) |
17cb7b0cf i7core_edac: Some... |
721 722 |
debugf1("\t\t%#x\t%#x\t%#x ", |
854d33499 i7core_edac: Get ... |
723 724 |
(value[j] >> 27) & 0x1, (value[j] >> 24) & 0x7, |
80b8ce89e i7core_edac: fix ... |
725 |
(value[j] & ((1 << 24) - 1))); |
0b2b7b7ec i7core_edac: Add ... |
726 |
} |
a0c36a1f0 i7core_edac: Add ... |
727 728 729 730 |
return 0; } /**************************************************************************** |
194a40fea i7core_edac: Add ... |
731 732 733 734 735 736 737 738 739 740 |
Error insertion routines ****************************************************************************/ /* The i7core has independent error injection features per channel. However, to have a simpler code, we don't allow enabling error injection on more than one channel. Also, since a change at an inject parameter will be applied only at enable, we're disabling error injection on all write calls to the sysfs nodes that controls the error code injection. */ |
1288c18f4 i7core_edac: Prop... |
741 |
static int disable_inject(const struct mem_ctl_info *mci) |
194a40fea i7core_edac: Add ... |
742 743 744 745 |
{ struct i7core_pvt *pvt = mci->pvt_info; pvt->inject.enable = 0; |
f47429494 i7core_edac: crea... |
746 |
if (!pvt->pci_ch[pvt->inject.channel][0]) |
8f3319075 i7core_edac: Regi... |
747 |
return -ENODEV; |
f47429494 i7core_edac: crea... |
748 |
pci_write_config_dword(pvt->pci_ch[pvt->inject.channel][0], |
4157d9f55 i7core_edac: fix ... |
749 |
MC_CHANNEL_ERROR_INJECT, 0); |
8f3319075 i7core_edac: Regi... |
750 751 |
return 0; |
194a40fea i7core_edac: Add ... |
752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 |
} /* * i7core inject inject.section * * accept and store error injection inject.section value * bit 0 - refers to the lower 32-byte half cacheline * bit 1 - refers to the upper 32-byte half cacheline */ static ssize_t i7core_inject_section_store(struct mem_ctl_info *mci, const char *data, size_t count) { struct i7core_pvt *pvt = mci->pvt_info; unsigned long value; int rc; if (pvt->inject.enable) |
41fcb7fee i7core_edac: Codi... |
769 |
disable_inject(mci); |
194a40fea i7core_edac: Add ... |
770 771 772 |
rc = strict_strtoul(data, 10, &value); if ((rc < 0) || (value > 3)) |
2068def56 i7core_edac: fix ... |
773 |
return -EIO; |
194a40fea i7core_edac: Add ... |
774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 |
pvt->inject.section = (u32) value; return count; } static ssize_t i7core_inject_section_show(struct mem_ctl_info *mci, char *data) { struct i7core_pvt *pvt = mci->pvt_info; return sprintf(data, "0x%08x ", pvt->inject.section); } /* * i7core inject.type * * accept and store error injection inject.section value * bit 0 - repeat enable - Enable error repetition * bit 1 - inject ECC error * bit 2 - inject parity error */ static ssize_t i7core_inject_type_store(struct mem_ctl_info *mci, const char *data, size_t count) { struct i7core_pvt *pvt = mci->pvt_info; unsigned long value; int rc; if (pvt->inject.enable) |
41fcb7fee i7core_edac: Codi... |
803 |
disable_inject(mci); |
194a40fea i7core_edac: Add ... |
804 805 806 |
rc = strict_strtoul(data, 10, &value); if ((rc < 0) || (value > 7)) |
2068def56 i7core_edac: fix ... |
807 |
return -EIO; |
194a40fea i7core_edac: Add ... |
808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 |
pvt->inject.type = (u32) value; return count; } static ssize_t i7core_inject_type_show(struct mem_ctl_info *mci, char *data) { struct i7core_pvt *pvt = mci->pvt_info; return sprintf(data, "0x%08x ", pvt->inject.type); } /* * i7core_inject_inject.eccmask_store * * The type of error (UE/CE) will depend on the inject.eccmask value: * Any bits set to a 1 will flip the corresponding ECC bit * Correctable errors can be injected by flipping 1 bit or the bits within * a symbol pair (2 consecutive aligned 8-bit pairs - i.e. 7:0 and 15:8 or * 23:16 and 31:24). Flipping bits in two symbol pairs will cause an * uncorrectable error to be injected. */ static ssize_t i7core_inject_eccmask_store(struct mem_ctl_info *mci, const char *data, size_t count) { struct i7core_pvt *pvt = mci->pvt_info; unsigned long value; int rc; if (pvt->inject.enable) |
41fcb7fee i7core_edac: Codi... |
839 |
disable_inject(mci); |
194a40fea i7core_edac: Add ... |
840 841 842 |
rc = strict_strtoul(data, 10, &value); if (rc < 0) |
2068def56 i7core_edac: fix ... |
843 |
return -EIO; |
194a40fea i7core_edac: Add ... |
844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 |
pvt->inject.eccmask = (u32) value; return count; } static ssize_t i7core_inject_eccmask_show(struct mem_ctl_info *mci, char *data) { struct i7core_pvt *pvt = mci->pvt_info; return sprintf(data, "0x%08x ", pvt->inject.eccmask); } /* * i7core_addrmatch * * The type of error (UE/CE) will depend on the inject.eccmask value: * Any bits set to a 1 will flip the corresponding ECC bit * Correctable errors can be injected by flipping 1 bit or the bits within * a symbol pair (2 consecutive aligned 8-bit pairs - i.e. 7:0 and 15:8 or * 23:16 and 31:24). Flipping bits in two symbol pairs will cause an * uncorrectable error to be injected. */ |
194a40fea i7core_edac: Add ... |
867 |
|
a5538e531 i7core_edac: Add ... |
868 869 870 871 872 |
#define DECLARE_ADDR_MATCH(param, limit) \ static ssize_t i7core_inject_store_##param( \ struct mem_ctl_info *mci, \ const char *data, size_t count) \ { \ |
cc301b3ae edac: store/show ... |
873 |
struct i7core_pvt *pvt; \ |
a5538e531 i7core_edac: Add ... |
874 875 876 |
long value; \ int rc; \ \ |
cc301b3ae edac: store/show ... |
877 878 879 880 |
debugf1("%s() ", __func__); \ pvt = mci->pvt_info; \ \ |
a5538e531 i7core_edac: Add ... |
881 882 883 |
if (pvt->inject.enable) \ disable_inject(mci); \ \ |
4f87fad1d i7core_edac: Bett... |
884 885 |
if (!strcasecmp(data, "any") || !strcasecmp(data, "any "))\ |
a5538e531 i7core_edac: Add ... |
886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 |
value = -1; \ else { \ rc = strict_strtoul(data, 10, &value); \ if ((rc < 0) || (value >= limit)) \ return -EIO; \ } \ \ pvt->inject.param = value; \ \ return count; \ } \ \ static ssize_t i7core_inject_show_##param( \ struct mem_ctl_info *mci, \ char *data) \ { \ |
cc301b3ae edac: store/show ... |
902 903 904 905 906 |
struct i7core_pvt *pvt; \ \ pvt = mci->pvt_info; \ debugf1("%s() pvt=%p ", __func__, pvt); \ |
a5538e531 i7core_edac: Add ... |
907 908 909 910 911 912 |
if (pvt->inject.param < 0) \ return sprintf(data, "any "); \ else \ return sprintf(data, "%d ", pvt->inject.param);\ |
194a40fea i7core_edac: Add ... |
913 |
} |
a5538e531 i7core_edac: Add ... |
914 915 916 917 918 919 920 921 922 |
#define ATTR_ADDR_MATCH(param) \ { \ .attr = { \ .name = #param, \ .mode = (S_IRUGO | S_IWUSR) \ }, \ .show = i7core_inject_show_##param, \ .store = i7core_inject_store_##param, \ } |
194a40fea i7core_edac: Add ... |
923 |
|
a5538e531 i7core_edac: Add ... |
924 925 926 927 928 929 |
DECLARE_ADDR_MATCH(channel, 3); DECLARE_ADDR_MATCH(dimm, 3); DECLARE_ADDR_MATCH(rank, 4); DECLARE_ADDR_MATCH(bank, 32); DECLARE_ADDR_MATCH(page, 0x10000); DECLARE_ADDR_MATCH(col, 0x4000); |
194a40fea i7core_edac: Add ... |
930 |
|
1288c18f4 i7core_edac: Prop... |
931 |
static int write_and_test(struct pci_dev *dev, const int where, const u32 val) |
276b824c3 i7core_edac: some... |
932 933 934 |
{ u32 read; int count; |
4157d9f55 i7core_edac: fix ... |
935 936 937 938 |
debugf0("setting pci %02x:%02x.%x reg=%02x value=%08x ", dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn), where, val); |
276b824c3 i7core_edac: some... |
939 940 |
for (count = 0; count < 10; count++) { if (count) |
b990538a7 i7core_edac: Codi... |
941 |
msleep(100); |
276b824c3 i7core_edac: some... |
942 943 944 945 946 947 |
pci_write_config_dword(dev, where, val); pci_read_config_dword(dev, where, &read); if (read == val) return 0; } |
4157d9f55 i7core_edac: fix ... |
948 949 950 951 952 |
i7core_printk(KERN_ERR, "Error during set pci %02x:%02x.%x reg=%02x " "write=%08x. Read=%08x ", dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn), where, val, read); |
276b824c3 i7core_edac: some... |
953 954 955 |
return -EINVAL; } |
194a40fea i7core_edac: Add ... |
956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 |
/* * This routine prepares the Memory Controller for error injection. * The error will be injected when some process tries to write to the * memory that matches the given criteria. * The criteria can be set in terms of a mask where dimm, rank, bank, page * and col can be specified. * A -1 value for any of the mask items will make the MCU to ignore * that matching criteria for error injection. * * It should be noticed that the error will only happen after a write operation * on a memory that matches the condition. if REPEAT_EN is not enabled at * inject mask, then it will produce just one error. Otherwise, it will repeat * until the injectmask would be cleaned. * * FIXME: This routine assumes that MAXNUMDIMMS value of MC_MAX_DOD * is reliable enough to check if the MC is using the * three channels. However, this is not clear at the datasheet. */ static ssize_t i7core_inject_enable_store(struct mem_ctl_info *mci, const char *data, size_t count) { struct i7core_pvt *pvt = mci->pvt_info; u32 injectmask; u64 mask = 0; int rc; long enable; |
f47429494 i7core_edac: crea... |
982 |
if (!pvt->pci_ch[pvt->inject.channel][0]) |
8f3319075 i7core_edac: Regi... |
983 |
return 0; |
194a40fea i7core_edac: Add ... |
984 985 986 987 988 989 990 991 992 993 994 995 996 |
rc = strict_strtoul(data, 10, &enable); if ((rc < 0)) return 0; if (enable) { pvt->inject.enable = 1; } else { disable_inject(mci); return count; } /* Sets pvt->inject.dimm mask */ if (pvt->inject.dimm < 0) |
486dd09f1 edac: i7core_edac... |
997 |
mask |= 1LL << 41; |
194a40fea i7core_edac: Add ... |
998 |
else { |
f47429494 i7core_edac: crea... |
999 |
if (pvt->channel[pvt->inject.channel].dimms > 2) |
486dd09f1 edac: i7core_edac... |
1000 |
mask |= (pvt->inject.dimm & 0x3LL) << 35; |
194a40fea i7core_edac: Add ... |
1001 |
else |
486dd09f1 edac: i7core_edac... |
1002 |
mask |= (pvt->inject.dimm & 0x1LL) << 36; |
194a40fea i7core_edac: Add ... |
1003 1004 1005 1006 |
} /* Sets pvt->inject.rank mask */ if (pvt->inject.rank < 0) |
486dd09f1 edac: i7core_edac... |
1007 |
mask |= 1LL << 40; |
194a40fea i7core_edac: Add ... |
1008 |
else { |
f47429494 i7core_edac: crea... |
1009 |
if (pvt->channel[pvt->inject.channel].dimms > 2) |
486dd09f1 edac: i7core_edac... |
1010 |
mask |= (pvt->inject.rank & 0x1LL) << 34; |
194a40fea i7core_edac: Add ... |
1011 |
else |
486dd09f1 edac: i7core_edac... |
1012 |
mask |= (pvt->inject.rank & 0x3LL) << 34; |
194a40fea i7core_edac: Add ... |
1013 1014 1015 1016 |
} /* Sets pvt->inject.bank mask */ if (pvt->inject.bank < 0) |
486dd09f1 edac: i7core_edac... |
1017 |
mask |= 1LL << 39; |
194a40fea i7core_edac: Add ... |
1018 |
else |
486dd09f1 edac: i7core_edac... |
1019 |
mask |= (pvt->inject.bank & 0x15LL) << 30; |
194a40fea i7core_edac: Add ... |
1020 1021 1022 |
/* Sets pvt->inject.page mask */ if (pvt->inject.page < 0) |
486dd09f1 edac: i7core_edac... |
1023 |
mask |= 1LL << 38; |
194a40fea i7core_edac: Add ... |
1024 |
else |
486dd09f1 edac: i7core_edac... |
1025 |
mask |= (pvt->inject.page & 0xffff) << 14; |
194a40fea i7core_edac: Add ... |
1026 1027 1028 |
/* Sets pvt->inject.column mask */ if (pvt->inject.col < 0) |
486dd09f1 edac: i7core_edac... |
1029 |
mask |= 1LL << 37; |
194a40fea i7core_edac: Add ... |
1030 |
else |
486dd09f1 edac: i7core_edac... |
1031 |
mask |= (pvt->inject.col & 0x3fff); |
194a40fea i7core_edac: Add ... |
1032 |
|
276b824c3 i7core_edac: some... |
1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 |
/* * bit 0: REPEAT_EN * bits 1-2: MASK_HALF_CACHELINE * bit 3: INJECT_ECC * bit 4: INJECT_ADDR_PARITY */ injectmask = (pvt->inject.type & 1) | (pvt->inject.section & 0x3) << 1 | (pvt->inject.type & 0x6) << (3 - 1); /* Unlock writes to registers - this register is write only */ |
f47429494 i7core_edac: crea... |
1045 |
pci_write_config_dword(pvt->pci_noncore, |
67166af4a i7core_edac: add ... |
1046 |
MC_CFG_CONTROL, 0x2); |
e9bd2e737 i7core_edac: Adds... |
1047 |
|
f47429494 i7core_edac: crea... |
1048 |
write_and_test(pvt->pci_ch[pvt->inject.channel][0], |
194a40fea i7core_edac: Add ... |
1049 |
MC_CHANNEL_ADDR_MATCH, mask); |
f47429494 i7core_edac: crea... |
1050 |
write_and_test(pvt->pci_ch[pvt->inject.channel][0], |
7b029d03c i7core_edac: A fe... |
1051 |
MC_CHANNEL_ADDR_MATCH + 4, mask >> 32L); |
7b029d03c i7core_edac: A fe... |
1052 |
|
f47429494 i7core_edac: crea... |
1053 |
write_and_test(pvt->pci_ch[pvt->inject.channel][0], |
194a40fea i7core_edac: Add ... |
1054 |
MC_CHANNEL_ERROR_MASK, pvt->inject.eccmask); |
f47429494 i7core_edac: crea... |
1055 |
write_and_test(pvt->pci_ch[pvt->inject.channel][0], |
4157d9f55 i7core_edac: fix ... |
1056 |
MC_CHANNEL_ERROR_INJECT, injectmask); |
276b824c3 i7core_edac: some... |
1057 |
|
194a40fea i7core_edac: Add ... |
1058 |
/* |
276b824c3 i7core_edac: some... |
1059 1060 1061 |
* This is something undocumented, based on my tests * Without writing 8 to this register, errors aren't injected. Not sure * why. |
194a40fea i7core_edac: Add ... |
1062 |
*/ |
f47429494 i7core_edac: crea... |
1063 |
pci_write_config_dword(pvt->pci_noncore, |
276b824c3 i7core_edac: some... |
1064 |
MC_CFG_CONTROL, 8); |
194a40fea i7core_edac: Add ... |
1065 |
|
41fcb7fee i7core_edac: Codi... |
1066 1067 1068 |
debugf0("Error inject addr match 0x%016llx, ecc 0x%08x," " inject 0x%08x ", |
194a40fea i7core_edac: Add ... |
1069 |
mask, pvt->inject.eccmask, injectmask); |
7b029d03c i7core_edac: A fe... |
1070 |
|
194a40fea i7core_edac: Add ... |
1071 1072 1073 1074 1075 1076 1077 |
return count; } static ssize_t i7core_inject_enable_show(struct mem_ctl_info *mci, char *data) { struct i7core_pvt *pvt = mci->pvt_info; |
7b029d03c i7core_edac: A fe... |
1078 |
u32 injectmask; |
52a2e4fc3 i7core_edac: Add ... |
1079 1080 |
if (!pvt->pci_ch[pvt->inject.channel][0]) return 0; |
f47429494 i7core_edac: crea... |
1081 |
pci_read_config_dword(pvt->pci_ch[pvt->inject.channel][0], |
4157d9f55 i7core_edac: fix ... |
1082 |
MC_CHANNEL_ERROR_INJECT, &injectmask); |
7b029d03c i7core_edac: A fe... |
1083 1084 1085 1086 1087 1088 |
debugf0("Inject error read: 0x%018x ", injectmask); if (injectmask & 0x0c) pvt->inject.enable = 1; |
194a40fea i7core_edac: Add ... |
1089 1090 1091 |
return sprintf(data, "%d ", pvt->inject.enable); } |
f338d7369 i7core_edac: Conv... |
1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 |
#define DECLARE_COUNTER(param) \ static ssize_t i7core_show_counter_##param( \ struct mem_ctl_info *mci, \ char *data) \ { \ struct i7core_pvt *pvt = mci->pvt_info; \ \ debugf1("%s() ", __func__); \ if (!pvt->ce_count_available || (pvt->is_registered)) \ return sprintf(data, "data unavailable "); \ return sprintf(data, "%lu ", \ pvt->udimm_ce_count[param]); \ } |
442305b15 i7core_edac: Add ... |
1108 |
|
f338d7369 i7core_edac: Conv... |
1109 1110 1111 1112 1113 1114 1115 |
#define ATTR_COUNTER(param) \ { \ .attr = { \ .name = __stringify(udimm##param), \ .mode = (S_IRUGO | S_IWUSR) \ }, \ .show = i7core_show_counter_##param \ |
d88b85072 i7core_edac: Fix ... |
1116 |
} |
442305b15 i7core_edac: Add ... |
1117 |
|
f338d7369 i7core_edac: Conv... |
1118 1119 1120 |
DECLARE_COUNTER(0); DECLARE_COUNTER(1); DECLARE_COUNTER(2); |
442305b15 i7core_edac: Add ... |
1121 |
|
194a40fea i7core_edac: Add ... |
1122 1123 1124 |
/* * Sysfs struct */ |
a5538e531 i7core_edac: Add ... |
1125 |
|
1288c18f4 i7core_edac: Prop... |
1126 |
static const struct mcidev_sysfs_attribute i7core_addrmatch_attrs[] = { |
a5538e531 i7core_edac: Add ... |
1127 1128 1129 1130 1131 1132 |
ATTR_ADDR_MATCH(channel), ATTR_ADDR_MATCH(dimm), ATTR_ADDR_MATCH(rank), ATTR_ADDR_MATCH(bank), ATTR_ADDR_MATCH(page), ATTR_ADDR_MATCH(col), |
1288c18f4 i7core_edac: Prop... |
1133 |
{ } /* End of list */ |
a5538e531 i7core_edac: Add ... |
1134 |
}; |
1288c18f4 i7core_edac: Prop... |
1135 |
static const struct mcidev_sysfs_group i7core_inject_addrmatch = { |
a5538e531 i7core_edac: Add ... |
1136 1137 1138 |
.name = "inject_addrmatch", .mcidev_attr = i7core_addrmatch_attrs, }; |
1288c18f4 i7core_edac: Prop... |
1139 |
static const struct mcidev_sysfs_attribute i7core_udimm_counters_attrs[] = { |
f338d7369 i7core_edac: Conv... |
1140 1141 1142 |
ATTR_COUNTER(0), ATTR_COUNTER(1), ATTR_COUNTER(2), |
64aab720b i7core_edac: fix ... |
1143 |
{ .attr = { .name = NULL } } |
f338d7369 i7core_edac: Conv... |
1144 |
}; |
1288c18f4 i7core_edac: Prop... |
1145 |
static const struct mcidev_sysfs_group i7core_udimm_counters = { |
f338d7369 i7core_edac: Conv... |
1146 1147 1148 |
.name = "all_channel_counts", .mcidev_attr = i7core_udimm_counters_attrs, }; |
1288c18f4 i7core_edac: Prop... |
1149 |
static const struct mcidev_sysfs_attribute i7core_sysfs_rdimm_attrs[] = { |
194a40fea i7core_edac: Add ... |
1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 |
{ .attr = { .name = "inject_section", .mode = (S_IRUGO | S_IWUSR) }, .show = i7core_inject_section_show, .store = i7core_inject_section_store, }, { .attr = { .name = "inject_type", .mode = (S_IRUGO | S_IWUSR) }, .show = i7core_inject_type_show, .store = i7core_inject_type_store, }, { .attr = { .name = "inject_eccmask", .mode = (S_IRUGO | S_IWUSR) }, .show = i7core_inject_eccmask_show, .store = i7core_inject_eccmask_store, }, { |
a5538e531 i7core_edac: Add ... |
1172 |
.grp = &i7core_inject_addrmatch, |
194a40fea i7core_edac: Add ... |
1173 1174 1175 1176 1177 1178 1179 1180 |
}, { .attr = { .name = "inject_enable", .mode = (S_IRUGO | S_IWUSR) }, .show = i7core_inject_enable_show, .store = i7core_inject_enable_store, }, |
1288c18f4 i7core_edac: Prop... |
1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 |
{ } /* End of list */ }; static const struct mcidev_sysfs_attribute i7core_sysfs_udimm_attrs[] = { { .attr = { .name = "inject_section", .mode = (S_IRUGO | S_IWUSR) }, .show = i7core_inject_section_show, .store = i7core_inject_section_store, }, { .attr = { .name = "inject_type", .mode = (S_IRUGO | S_IWUSR) }, .show = i7core_inject_type_show, .store = i7core_inject_type_store, }, { .attr = { .name = "inject_eccmask", .mode = (S_IRUGO | S_IWUSR) }, .show = i7core_inject_eccmask_show, .store = i7core_inject_eccmask_store, }, { .grp = &i7core_inject_addrmatch, }, { .attr = { .name = "inject_enable", .mode = (S_IRUGO | S_IWUSR) }, .show = i7core_inject_enable_show, .store = i7core_inject_enable_store, }, { .grp = &i7core_udimm_counters, }, { } /* End of list */ |
194a40fea i7core_edac: Add ... |
1219 1220 1221 |
}; /**************************************************************************** |
a0c36a1f0 i7core_edac: Add ... |
1222 1223 1224 1225 |
Device initialization routines: put/get, init/exit ****************************************************************************/ /* |
64c10f6e0 i7core_edac: Alwa... |
1226 |
* i7core_put_all_devices 'put' all the devices that we have |
a0c36a1f0 i7core_edac: Add ... |
1227 1228 |
* reserved via 'get' */ |
13d6e9b65 i7core_edac: at r... |
1229 |
static void i7core_put_devices(struct i7core_dev *i7core_dev) |
a0c36a1f0 i7core_edac: Add ... |
1230 |
{ |
13d6e9b65 i7core_edac: at r... |
1231 |
int i; |
a0c36a1f0 i7core_edac: Add ... |
1232 |
|
22e6bcbdc i7core_edac: chan... |
1233 1234 |
debugf0(__FILE__ ": %s() ", __func__); |
de06eeef5 i7core_edac: Use ... |
1235 |
for (i = 0; i < i7core_dev->n_devs; i++) { |
22e6bcbdc i7core_edac: chan... |
1236 1237 1238 1239 1240 1241 1242 1243 1244 |
struct pci_dev *pdev = i7core_dev->pdev[i]; if (!pdev) continue; debugf0("Removing dev %02x:%02x.%d ", pdev->bus->number, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn)); pci_dev_put(pdev); } |
13d6e9b65 i7core_edac: at r... |
1245 |
} |
66607706c Dynamically alloc... |
1246 |
|
13d6e9b65 i7core_edac: at r... |
1247 1248 |
static void i7core_put_all_devices(void) { |
425386803 i7core_edac: We n... |
1249 |
struct i7core_dev *i7core_dev, *tmp; |
13d6e9b65 i7core_edac: at r... |
1250 |
|
39300e714 i7core_edac: expl... |
1251 |
list_for_each_entry_safe(i7core_dev, tmp, &i7core_edac_list, list) { |
13d6e9b65 i7core_edac: at r... |
1252 |
i7core_put_devices(i7core_dev); |
2aa9be448 i7core_edac: Intr... |
1253 |
free_i7core_dev(i7core_dev); |
39300e714 i7core_edac: expl... |
1254 |
} |
a0c36a1f0 i7core_edac: Add ... |
1255 |
} |
1288c18f4 i7core_edac: Prop... |
1256 |
static void __init i7core_xeon_pci_fixup(const struct pci_id_table *table) |
bc2d7245f i7core_edac: Prob... |
1257 1258 1259 |
{ struct pci_dev *pdev = NULL; int i; |
54a08ab15 i7core_edac: Don'... |
1260 |
|
bc2d7245f i7core_edac: Prob... |
1261 |
/* |
e7bf068aa i7core_edac: fix ... |
1262 |
* On Xeon 55xx, the Intel Quick Path Arch Generic Non-core pci buses |
bc2d7245f i7core_edac: Prob... |
1263 1264 1265 |
* aren't announced by acpi. So, we need to use a legacy scan probing * to detect them */ |
bd9e19ca4 Add support for W... |
1266 1267 1268 1269 1270 1271 |
while (table && table->descr) { pdev = pci_get_device(PCI_VENDOR_ID_INTEL, table->descr[0].dev_id, NULL); if (unlikely(!pdev)) { for (i = 0; i < MAX_SOCKET_BUSES; i++) pcibios_scan_specific_bus(255-i); } |
bda142890 i7core_edac: Prop... |
1272 |
pci_dev_put(pdev); |
bd9e19ca4 Add support for W... |
1273 |
table++; |
bc2d7245f i7core_edac: Prob... |
1274 1275 |
} } |
bda142890 i7core_edac: Prop... |
1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 |
static unsigned i7core_pci_lastbus(void) { int last_bus = 0, bus; struct pci_bus *b = NULL; while ((b = pci_find_next_bus(b)) != NULL) { bus = b->number; debugf0("Found bus %d ", bus); if (bus > last_bus) last_bus = bus; } debugf0("Last bus %d ", last_bus); return last_bus; } |
a0c36a1f0 i7core_edac: Add ... |
1294 |
/* |
64c10f6e0 i7core_edac: Alwa... |
1295 |
* i7core_get_all_devices Find and perform 'get' operation on the MCH's |
a0c36a1f0 i7core_edac: Add ... |
1296 1297 1298 1299 |
* device/functions we want to reference for this driver * * Need to 'get' device 16 func 1 and func 2 */ |
b197cba07 i7core_edac: Redu... |
1300 1301 1302 1303 |
static int i7core_get_onedevice(struct pci_dev **prev, const struct pci_id_table *table, const unsigned devno, const unsigned last_bus) |
a0c36a1f0 i7core_edac: Add ... |
1304 |
{ |
66607706c Dynamically alloc... |
1305 |
struct i7core_dev *i7core_dev; |
b197cba07 i7core_edac: Redu... |
1306 |
const struct pci_id_descr *dev_descr = &table->descr[devno]; |
66607706c Dynamically alloc... |
1307 |
|
8f3319075 i7core_edac: Regi... |
1308 |
struct pci_dev *pdev = NULL; |
67166af4a i7core_edac: add ... |
1309 1310 |
u8 bus = 0; u8 socket = 0; |
a0c36a1f0 i7core_edac: Add ... |
1311 |
|
c77720b95 i7core: fix get_d... |
1312 |
pdev = pci_get_device(PCI_VENDOR_ID_INTEL, |
de06eeef5 i7core_edac: Use ... |
1313 |
dev_descr->dev_id, *prev); |
c77720b95 i7core: fix get_d... |
1314 |
|
224e871f3 i7core_edac: Fix ... |
1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 |
/* * On Xeon 55xx, the Intel Quckpath Arch Generic Non-core regs * is at addr 8086:2c40, instead of 8086:2c41. So, we need * to probe for the alternate address in case of failure */ if (dev_descr->dev_id == PCI_DEVICE_ID_INTEL_I7_NONCORE && !pdev) pdev = pci_get_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_I7_NONCORE_ALT, *prev); if (dev_descr->dev_id == PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE && !pdev) pdev = pci_get_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE_ALT, *prev); |
c77720b95 i7core: fix get_d... |
1328 1329 1330 1331 |
if (!pdev) { if (*prev) { *prev = pdev; return 0; |
d1fd4fb69 i7core_edac: Add ... |
1332 |
} |
de06eeef5 i7core_edac: Use ... |
1333 |
if (dev_descr->optional) |
c77720b95 i7core: fix get_d... |
1334 |
return 0; |
310cbb728 i7core: fix probi... |
1335 |
|
bd9e19ca4 Add support for W... |
1336 1337 |
if (devno == 0) return -ENODEV; |
ab0893740 quiesce EDAC init... |
1338 |
i7core_printk(KERN_INFO, |
c77720b95 i7core: fix get_d... |
1339 1340 |
"Device not found: dev %02x.%d PCI ID %04x:%04x ", |
de06eeef5 i7core_edac: Use ... |
1341 1342 |
dev_descr->dev, dev_descr->func, PCI_VENDOR_ID_INTEL, dev_descr->dev_id); |
67166af4a i7core_edac: add ... |
1343 |
|
c77720b95 i7core: fix get_d... |
1344 1345 1346 1347 |
/* End of list, leave */ return -ENODEV; } bus = pdev->bus->number; |
67166af4a i7core_edac: add ... |
1348 |
|
bda142890 i7core_edac: Prop... |
1349 |
socket = last_bus - bus; |
c77720b95 i7core: fix get_d... |
1350 |
|
66607706c Dynamically alloc... |
1351 1352 |
i7core_dev = get_i7core_dev(socket); if (!i7core_dev) { |
848b2f7ed i7core_edac: Intr... |
1353 |
i7core_dev = alloc_i7core_dev(socket, table); |
2896637b8 i7core_edac: Call... |
1354 1355 |
if (!i7core_dev) { pci_dev_put(pdev); |
66607706c Dynamically alloc... |
1356 |
return -ENOMEM; |
2896637b8 i7core_edac: Call... |
1357 |
} |
c77720b95 i7core: fix get_d... |
1358 |
} |
67166af4a i7core_edac: add ... |
1359 |
|
66607706c Dynamically alloc... |
1360 |
if (i7core_dev->pdev[devno]) { |
c77720b95 i7core: fix get_d... |
1361 1362 1363 1364 |
i7core_printk(KERN_ERR, "Duplicated device for " "dev %02x:%02x.%d PCI ID %04x:%04x ", |
de06eeef5 i7core_edac: Use ... |
1365 1366 |
bus, dev_descr->dev, dev_descr->func, PCI_VENDOR_ID_INTEL, dev_descr->dev_id); |
c77720b95 i7core: fix get_d... |
1367 1368 1369 |
pci_dev_put(pdev); return -ENODEV; } |
67166af4a i7core_edac: add ... |
1370 |
|
66607706c Dynamically alloc... |
1371 |
i7core_dev->pdev[devno] = pdev; |
c77720b95 i7core: fix get_d... |
1372 1373 |
/* Sanity check */ |
de06eeef5 i7core_edac: Use ... |
1374 1375 |
if (unlikely(PCI_SLOT(pdev->devfn) != dev_descr->dev || PCI_FUNC(pdev->devfn) != dev_descr->func)) { |
c77720b95 i7core: fix get_d... |
1376 1377 1378 1379 |
i7core_printk(KERN_ERR, "Device PCI ID %04x:%04x " "has dev %02x:%02x.%d instead of dev %02x:%02x.%d ", |
de06eeef5 i7core_edac: Use ... |
1380 |
PCI_VENDOR_ID_INTEL, dev_descr->dev_id, |
c77720b95 i7core: fix get_d... |
1381 |
bus, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn), |
de06eeef5 i7core_edac: Use ... |
1382 |
bus, dev_descr->dev, dev_descr->func); |
c77720b95 i7core: fix get_d... |
1383 1384 |
return -ENODEV; } |
ef708b53b i7core_edac: Add ... |
1385 |
|
c77720b95 i7core: fix get_d... |
1386 1387 1388 1389 1390 1391 |
/* Be sure that the device is enabled */ if (unlikely(pci_enable_device(pdev) < 0)) { i7core_printk(KERN_ERR, "Couldn't enable " "dev %02x:%02x.%d PCI ID %04x:%04x ", |
de06eeef5 i7core_edac: Use ... |
1392 1393 |
bus, dev_descr->dev, dev_descr->func, PCI_VENDOR_ID_INTEL, dev_descr->dev_id); |
c77720b95 i7core: fix get_d... |
1394 1395 |
return -ENODEV; } |
ef708b53b i7core_edac: Add ... |
1396 |
|
d4c277957 i7core_edac: a fe... |
1397 1398 |
debugf0("Detected socket %d dev %02x:%02x.%d PCI ID %04x:%04x ", |
de06eeef5 i7core_edac: Use ... |
1399 1400 1401 |
socket, bus, dev_descr->dev, dev_descr->func, PCI_VENDOR_ID_INTEL, dev_descr->dev_id); |
8f3319075 i7core_edac: Regi... |
1402 |
|
a3e154163 i7core_edac: Avoi... |
1403 1404 1405 1406 1407 1408 |
/* * As stated on drivers/pci/search.c, the reference count for * @from is always decremented if it is not %NULL. So, as we need * to get all devices up to null, we need to do a get for the device */ pci_dev_get(pdev); |
c77720b95 i7core: fix get_d... |
1409 |
*prev = pdev; |
ef708b53b i7core_edac: Add ... |
1410 |
|
c77720b95 i7core: fix get_d... |
1411 1412 |
return 0; } |
a0c36a1f0 i7core_edac: Add ... |
1413 |
|
64c10f6e0 i7core_edac: Alwa... |
1414 |
static int i7core_get_all_devices(void) |
c77720b95 i7core: fix get_d... |
1415 |
{ |
3c52cc57c i7core_edac: prop... |
1416 |
int i, rc, last_bus; |
c77720b95 i7core: fix get_d... |
1417 |
struct pci_dev *pdev = NULL; |
3c52cc57c i7core_edac: prop... |
1418 |
const struct pci_id_table *table = pci_dev_table; |
bd9e19ca4 Add support for W... |
1419 |
|
bda142890 i7core_edac: Prop... |
1420 |
last_bus = i7core_pci_lastbus(); |
3c52cc57c i7core_edac: prop... |
1421 |
while (table && table->descr) { |
bd9e19ca4 Add support for W... |
1422 1423 1424 |
for (i = 0; i < table->n_devs; i++) { pdev = NULL; do { |
b197cba07 i7core_edac: Redu... |
1425 |
rc = i7core_get_onedevice(&pdev, table, i, |
bda142890 i7core_edac: Prop... |
1426 |
last_bus); |
bd9e19ca4 Add support for W... |
1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 |
if (rc < 0) { if (i == 0) { i = table->n_devs; break; } i7core_put_all_devices(); return -ENODEV; } } while (pdev); } |
3c52cc57c i7core_edac: prop... |
1437 |
table++; |
c77720b95 i7core: fix get_d... |
1438 |
} |
66607706c Dynamically alloc... |
1439 |
|
ef708b53b i7core_edac: Add ... |
1440 |
return 0; |
ef708b53b i7core_edac: Add ... |
1441 |
} |
f47429494 i7core_edac: crea... |
1442 1443 |
static int mci_bind_devs(struct mem_ctl_info *mci, struct i7core_dev *i7core_dev) |
ef708b53b i7core_edac: Add ... |
1444 1445 1446 |
{ struct i7core_pvt *pvt = mci->pvt_info; struct pci_dev *pdev; |
f47429494 i7core_edac: crea... |
1447 |
int i, func, slot; |
27100db0e i7core_edac: Don'... |
1448 |
char *family; |
ef708b53b i7core_edac: Add ... |
1449 |
|
27100db0e i7core_edac: Don'... |
1450 1451 |
pvt->is_registered = false; pvt->enable_scrub = false; |
de06eeef5 i7core_edac: Use ... |
1452 |
for (i = 0; i < i7core_dev->n_devs; i++) { |
f47429494 i7core_edac: crea... |
1453 1454 |
pdev = i7core_dev->pdev[i]; if (!pdev) |
66607706c Dynamically alloc... |
1455 |
continue; |
f47429494 i7core_edac: crea... |
1456 1457 1458 1459 1460 1461 1462 1463 |
func = PCI_FUNC(pdev->devfn); slot = PCI_SLOT(pdev->devfn); if (slot == 3) { if (unlikely(func > MAX_MCR_FUNC)) goto error; pvt->pci_mcr[func] = pdev; } else if (likely(slot >= 4 && slot < 4 + NUM_CHANS)) { if (unlikely(func > MAX_CHAN_FUNC)) |
ef708b53b i7core_edac: Add ... |
1464 |
goto error; |
f47429494 i7core_edac: crea... |
1465 |
pvt->pci_ch[slot - 4][func] = pdev; |
27100db0e i7core_edac: Don'... |
1466 |
} else if (!slot && !func) { |
f47429494 i7core_edac: crea... |
1467 |
pvt->pci_noncore = pdev; |
27100db0e i7core_edac: Don'... |
1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 |
/* Detect the processor family */ switch (pdev->device) { case PCI_DEVICE_ID_INTEL_I7_NONCORE: family = "Xeon 35xx/ i7core"; pvt->enable_scrub = false; break; case PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE_ALT: family = "i7-800/i5-700"; pvt->enable_scrub = false; break; case PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE: family = "Xeon 34xx"; pvt->enable_scrub = false; break; case PCI_DEVICE_ID_INTEL_I7_NONCORE_ALT: family = "Xeon 55xx"; pvt->enable_scrub = true; break; case PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE_REV2: family = "Xeon 56xx / i7-900"; pvt->enable_scrub = true; break; default: family = "unknown"; pvt->enable_scrub = false; } debugf0("Detected a processor type %s ", family); } else |
f47429494 i7core_edac: crea... |
1498 |
goto error; |
ef708b53b i7core_edac: Add ... |
1499 |
|
f47429494 i7core_edac: crea... |
1500 1501 1502 1503 |
debugf0("Associated fn %d.%d, dev = %p, socket %d ", PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn), pdev, i7core_dev->socket); |
14d2c0834 i7core: Use regis... |
1504 |
|
f47429494 i7core_edac: crea... |
1505 1506 |
if (PCI_SLOT(pdev->devfn) == 3 && PCI_FUNC(pdev->devfn) == 2) |
27100db0e i7core_edac: Don'... |
1507 |
pvt->is_registered = true; |
a0c36a1f0 i7core_edac: Add ... |
1508 |
} |
e9bd2e737 i7core_edac: Adds... |
1509 |
|
a0c36a1f0 i7core_edac: Add ... |
1510 |
return 0; |
ef708b53b i7core_edac: Add ... |
1511 1512 1513 1514 1515 1516 1517 |
error: i7core_printk(KERN_ERR, "Device %d, function %d " "is out of the expected range ", slot, func); return -EINVAL; |
a0c36a1f0 i7core_edac: Add ... |
1518 |
} |
442305b15 i7core_edac: Add ... |
1519 1520 1521 |
/**************************************************************************** Error check routines ****************************************************************************/ |
f47429494 i7core_edac: crea... |
1522 |
static void i7core_rdimm_update_csrow(struct mem_ctl_info *mci, |
1288c18f4 i7core_edac: Prop... |
1523 1524 1525 |
const int chan, const int dimm, const int add) |
b4e8f0b6e i7core_edac: Use ... |
1526 1527 1528 |
{ char *msg; struct i7core_pvt *pvt = mci->pvt_info; |
f47429494 i7core_edac: crea... |
1529 |
int row = pvt->csrow_map[chan][dimm], i; |
b4e8f0b6e i7core_edac: Use ... |
1530 1531 1532 |
for (i = 0; i < add; i++) { msg = kasprintf(GFP_KERNEL, "Corrected error " |
f47429494 i7core_edac: crea... |
1533 1534 |
"(Socket=%d channel=%d dimm=%d)", pvt->i7core_dev->socket, chan, dimm); |
b4e8f0b6e i7core_edac: Use ... |
1535 1536 1537 1538 1539 1540 1541 |
edac_mc_handle_fbd_ce(mci, row, 0, msg); kfree (msg); } } static void i7core_rdimm_update_ce_count(struct mem_ctl_info *mci, |
1288c18f4 i7core_edac: Prop... |
1542 1543 1544 1545 |
const int chan, const int new0, const int new1, const int new2) |
b4e8f0b6e i7core_edac: Use ... |
1546 1547 1548 1549 |
{ struct i7core_pvt *pvt = mci->pvt_info; int add0 = 0, add1 = 0, add2 = 0; /* Updates CE counters if it is not the first time here */ |
f47429494 i7core_edac: crea... |
1550 |
if (pvt->ce_count_available) { |
b4e8f0b6e i7core_edac: Use ... |
1551 |
/* Updates CE counters */ |
f47429494 i7core_edac: crea... |
1552 1553 1554 |
add2 = new2 - pvt->rdimm_last_ce_count[chan][2]; add1 = new1 - pvt->rdimm_last_ce_count[chan][1]; add0 = new0 - pvt->rdimm_last_ce_count[chan][0]; |
b4e8f0b6e i7core_edac: Use ... |
1555 1556 1557 |
if (add2 < 0) add2 += 0x7fff; |
f47429494 i7core_edac: crea... |
1558 |
pvt->rdimm_ce_count[chan][2] += add2; |
b4e8f0b6e i7core_edac: Use ... |
1559 1560 1561 |
if (add1 < 0) add1 += 0x7fff; |
f47429494 i7core_edac: crea... |
1562 |
pvt->rdimm_ce_count[chan][1] += add1; |
b4e8f0b6e i7core_edac: Use ... |
1563 1564 1565 |
if (add0 < 0) add0 += 0x7fff; |
f47429494 i7core_edac: crea... |
1566 |
pvt->rdimm_ce_count[chan][0] += add0; |
b4e8f0b6e i7core_edac: Use ... |
1567 |
} else |
f47429494 i7core_edac: crea... |
1568 |
pvt->ce_count_available = 1; |
b4e8f0b6e i7core_edac: Use ... |
1569 1570 |
/* Store the new values */ |
f47429494 i7core_edac: crea... |
1571 1572 1573 |
pvt->rdimm_last_ce_count[chan][2] = new2; pvt->rdimm_last_ce_count[chan][1] = new1; pvt->rdimm_last_ce_count[chan][0] = new0; |
b4e8f0b6e i7core_edac: Use ... |
1574 1575 1576 |
/*updated the edac core */ if (add0 != 0) |
f47429494 i7core_edac: crea... |
1577 |
i7core_rdimm_update_csrow(mci, chan, 0, add0); |
b4e8f0b6e i7core_edac: Use ... |
1578 |
if (add1 != 0) |
f47429494 i7core_edac: crea... |
1579 |
i7core_rdimm_update_csrow(mci, chan, 1, add1); |
b4e8f0b6e i7core_edac: Use ... |
1580 |
if (add2 != 0) |
f47429494 i7core_edac: crea... |
1581 |
i7core_rdimm_update_csrow(mci, chan, 2, add2); |
b4e8f0b6e i7core_edac: Use ... |
1582 1583 |
} |
f47429494 i7core_edac: crea... |
1584 |
static void i7core_rdimm_check_mc_ecc_err(struct mem_ctl_info *mci) |
b4e8f0b6e i7core_edac: Use ... |
1585 1586 1587 1588 1589 1590 |
{ struct i7core_pvt *pvt = mci->pvt_info; u32 rcv[3][2]; int i, new0, new1, new2; /*Read DEV 3: FUN 2: MC_COR_ECC_CNT regs directly*/ |
f47429494 i7core_edac: crea... |
1591 |
pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_0, |
b4e8f0b6e i7core_edac: Use ... |
1592 |
&rcv[0][0]); |
f47429494 i7core_edac: crea... |
1593 |
pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_1, |
b4e8f0b6e i7core_edac: Use ... |
1594 |
&rcv[0][1]); |
f47429494 i7core_edac: crea... |
1595 |
pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_2, |
b4e8f0b6e i7core_edac: Use ... |
1596 |
&rcv[1][0]); |
f47429494 i7core_edac: crea... |
1597 |
pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_3, |
b4e8f0b6e i7core_edac: Use ... |
1598 |
&rcv[1][1]); |
f47429494 i7core_edac: crea... |
1599 |
pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_4, |
b4e8f0b6e i7core_edac: Use ... |
1600 |
&rcv[2][0]); |
f47429494 i7core_edac: crea... |
1601 |
pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_5, |
b4e8f0b6e i7core_edac: Use ... |
1602 1603 1604 1605 1606 1607 |
&rcv[2][1]); for (i = 0 ; i < 3; i++) { debugf3("MC_COR_ECC_CNT%d = 0x%x; MC_COR_ECC_CNT%d = 0x%x ", (i * 2), rcv[i][0], (i * 2) + 1, rcv[i][1]); /*if the channel has 3 dimms*/ |
f47429494 i7core_edac: crea... |
1608 |
if (pvt->channel[i].dimms > 2) { |
b4e8f0b6e i7core_edac: Use ... |
1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 |
new0 = DIMM_BOT_COR_ERR(rcv[i][0]); new1 = DIMM_TOP_COR_ERR(rcv[i][0]); new2 = DIMM_BOT_COR_ERR(rcv[i][1]); } else { new0 = DIMM_TOP_COR_ERR(rcv[i][0]) + DIMM_BOT_COR_ERR(rcv[i][0]); new1 = DIMM_TOP_COR_ERR(rcv[i][1]) + DIMM_BOT_COR_ERR(rcv[i][1]); new2 = 0; } |
f47429494 i7core_edac: crea... |
1619 |
i7core_rdimm_update_ce_count(mci, i, new0, new1, new2); |
b4e8f0b6e i7core_edac: Use ... |
1620 1621 |
} } |
442305b15 i7core_edac: Add ... |
1622 1623 1624 1625 1626 1627 1628 |
/* This function is based on the device 3 function 4 registers as described on: * Intel Xeon Processor 5500 Series Datasheet Volume 2 * http://www.intel.com/Assets/PDF/datasheet/321322.pdf * also available at: * http://www.arrownac.com/manufacturers/intel/s/nehalem/5500-datasheet-v2.pdf */ |
f47429494 i7core_edac: crea... |
1629 |
static void i7core_udimm_check_mc_ecc_err(struct mem_ctl_info *mci) |
442305b15 i7core_edac: Add ... |
1630 1631 1632 1633 |
{ struct i7core_pvt *pvt = mci->pvt_info; u32 rcv1, rcv0; int new0, new1, new2; |
f47429494 i7core_edac: crea... |
1634 |
if (!pvt->pci_mcr[4]) { |
b990538a7 i7core_edac: Codi... |
1635 1636 |
debugf0("%s MCR registers not found ", __func__); |
442305b15 i7core_edac: Add ... |
1637 1638 |
return; } |
b4e8f0b6e i7core_edac: Use ... |
1639 |
/* Corrected test errors */ |
f47429494 i7core_edac: crea... |
1640 1641 |
pci_read_config_dword(pvt->pci_mcr[4], MC_TEST_ERR_RCV1, &rcv1); pci_read_config_dword(pvt->pci_mcr[4], MC_TEST_ERR_RCV0, &rcv0); |
442305b15 i7core_edac: Add ... |
1642 1643 1644 1645 1646 |
/* Store the new values */ new2 = DIMM2_COR_ERR(rcv1); new1 = DIMM1_COR_ERR(rcv0); new0 = DIMM0_COR_ERR(rcv0); |
442305b15 i7core_edac: Add ... |
1647 |
/* Updates CE counters if it is not the first time here */ |
f47429494 i7core_edac: crea... |
1648 |
if (pvt->ce_count_available) { |
442305b15 i7core_edac: Add ... |
1649 1650 |
/* Updates CE counters */ int add0, add1, add2; |
f47429494 i7core_edac: crea... |
1651 1652 1653 |
add2 = new2 - pvt->udimm_last_ce_count[2]; add1 = new1 - pvt->udimm_last_ce_count[1]; add0 = new0 - pvt->udimm_last_ce_count[0]; |
442305b15 i7core_edac: Add ... |
1654 1655 1656 |
if (add2 < 0) add2 += 0x7fff; |
f47429494 i7core_edac: crea... |
1657 |
pvt->udimm_ce_count[2] += add2; |
442305b15 i7core_edac: Add ... |
1658 1659 1660 |
if (add1 < 0) add1 += 0x7fff; |
f47429494 i7core_edac: crea... |
1661 |
pvt->udimm_ce_count[1] += add1; |
442305b15 i7core_edac: Add ... |
1662 1663 1664 |
if (add0 < 0) add0 += 0x7fff; |
f47429494 i7core_edac: crea... |
1665 |
pvt->udimm_ce_count[0] += add0; |
b4e8f0b6e i7core_edac: Use ... |
1666 1667 1668 1669 1670 1671 |
if (add0 | add1 | add2) i7core_printk(KERN_ERR, "New Corrected error(s): " "dimm0: +%d, dimm1: +%d, dimm2 +%d ", add0, add1, add2); |
442305b15 i7core_edac: Add ... |
1672 |
} else |
f47429494 i7core_edac: crea... |
1673 |
pvt->ce_count_available = 1; |
442305b15 i7core_edac: Add ... |
1674 1675 |
/* Store the new values */ |
f47429494 i7core_edac: crea... |
1676 1677 1678 |
pvt->udimm_last_ce_count[2] = new2; pvt->udimm_last_ce_count[1] = new1; pvt->udimm_last_ce_count[0] = new0; |
442305b15 i7core_edac: Add ... |
1679 |
} |
8a2f118e3 i7core_edac: deco... |
1680 1681 1682 |
/* * According with tables E-11 and E-12 of chapter E.3.3 of Intel 64 and IA-32 * Architectures Software Developer’s Manual Volume 3B. |
f237fcf2b i7core_edac: some... |
1683 1684 1685 |
* Nehalem are defined as family 0x06, model 0x1a * * The MCA registers used here are the following ones: |
8a2f118e3 i7core_edac: deco... |
1686 |
* struct mce field MCA Register |
f237fcf2b i7core_edac: some... |
1687 1688 1689 |
* m->status MSR_IA32_MC8_STATUS * m->addr MSR_IA32_MC8_ADDR * m->misc MSR_IA32_MC8_MISC |
8a2f118e3 i7core_edac: deco... |
1690 1691 1692 |
* In the case of Nehalem, the error information is masked at .status and .misc * fields */ |
d5381642a i7core_edac: Add ... |
1693 |
static void i7core_mce_output_error(struct mem_ctl_info *mci, |
1288c18f4 i7core_edac: Prop... |
1694 |
const struct mce *m) |
d5381642a i7core_edac: Add ... |
1695 |
{ |
b4e8f0b6e i7core_edac: Use ... |
1696 |
struct i7core_pvt *pvt = mci->pvt_info; |
a639539fa i7core: enrich er... |
1697 |
char *type, *optype, *err, *msg; |
8a2f118e3 i7core_edac: deco... |
1698 |
unsigned long error = m->status & 0x1ff0000l; |
a639539fa i7core: enrich er... |
1699 |
u32 optypenum = (m->status >> 4) & 0x07; |
8cf2d2399 i7core_edac: fixe... |
1700 |
u32 core_err_cnt = (m->status >> 38) & 0x7fff; |
8a2f118e3 i7core_edac: deco... |
1701 1702 1703 1704 |
u32 dimm = (m->misc >> 16) & 0x3; u32 channel = (m->misc >> 18) & 0x3; u32 syndrome = m->misc >> 32; u32 errnum = find_first_bit(&error, 32); |
b4e8f0b6e i7core_edac: Use ... |
1705 |
int csrow; |
8a2f118e3 i7core_edac: deco... |
1706 |
|
c5d345286 i7core: check if ... |
1707 1708 1709 1710 |
if (m->mcgstatus & 1) type = "FATAL"; else type = "NON_FATAL"; |
a639539fa i7core: enrich er... |
1711 |
switch (optypenum) { |
b990538a7 i7core_edac: Codi... |
1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 |
case 0: optype = "generic undef request"; break; case 1: optype = "read error"; break; case 2: optype = "write error"; break; case 3: optype = "addr/cmd error"; break; case 4: optype = "scrubbing error"; break; default: optype = "reserved"; break; |
a639539fa i7core: enrich er... |
1730 |
} |
8a2f118e3 i7core_edac: deco... |
1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 |
switch (errnum) { case 16: err = "read ECC error"; break; case 17: err = "RAS ECC error"; break; case 18: err = "write parity error"; break; case 19: err = "redundacy loss"; break; case 20: err = "reserved"; break; case 21: err = "memory range error"; break; case 22: err = "RTID out of range"; break; case 23: err = "address parity error"; break; case 24: err = "byte enable parity error"; break; default: err = "unknown"; |
d5381642a i7core_edac: Add ... |
1761 |
} |
d5381642a i7core_edac: Add ... |
1762 |
|
f237fcf2b i7core_edac: some... |
1763 |
/* FIXME: should convert addr into bank and rank information */ |
8a2f118e3 i7core_edac: deco... |
1764 |
msg = kasprintf(GFP_ATOMIC, |
f47429494 i7core_edac: crea... |
1765 |
"%s (addr = 0x%08llx, cpu=%d, Dimm=%d, Channel=%d, " |
a639539fa i7core: enrich er... |
1766 1767 |
"syndrome=0x%08x, count=%d, Err=%08llx:%08llx (%s: %s)) ", |
f47429494 i7core_edac: crea... |
1768 |
type, (long long) m->addr, m->cpu, dimm, channel, |
a639539fa i7core: enrich er... |
1769 1770 |
syndrome, core_err_cnt, (long long)m->status, (long long)m->misc, optype, err); |
8a2f118e3 i7core_edac: deco... |
1771 1772 |
debugf0("%s", msg); |
d5381642a i7core_edac: Add ... |
1773 |
|
f47429494 i7core_edac: crea... |
1774 |
csrow = pvt->csrow_map[channel][dimm]; |
b4e8f0b6e i7core_edac: Use ... |
1775 |
|
d5381642a i7core_edac: Add ... |
1776 |
/* Call the helper to output message */ |
b4e8f0b6e i7core_edac: Use ... |
1777 1778 1779 |
if (m->mcgstatus & 1) edac_mc_handle_fbd_ue(mci, csrow, 0, 0 /* FIXME: should be channel here */, msg); |
f47429494 i7core_edac: crea... |
1780 |
else if (!pvt->is_registered) |
b4e8f0b6e i7core_edac: Use ... |
1781 1782 |
edac_mc_handle_fbd_ce(mci, csrow, 0 /* FIXME: should be channel here */, msg); |
8a2f118e3 i7core_edac: deco... |
1783 1784 |
kfree(msg); |
d5381642a i7core_edac: Add ... |
1785 |
} |
a0c36a1f0 i7core_edac: Add ... |
1786 |
/* |
87d1d272b i7core_edac: need... |
1787 1788 1789 1790 1791 |
* i7core_check_error Retrieve and process errors reported by the * hardware. Called by the Core module. */ static void i7core_check_error(struct mem_ctl_info *mci) { |
d5381642a i7core_edac: Add ... |
1792 1793 1794 |
struct i7core_pvt *pvt = mci->pvt_info; int i; unsigned count = 0; |
ca9c90ba0 i7core_edac: Use ... |
1795 |
struct mce *m; |
d5381642a i7core_edac: Add ... |
1796 |
|
ca9c90ba0 i7core_edac: Use ... |
1797 1798 1799 |
/* * MCE first step: Copy all mce errors into a temporary buffer * We use a double buffering here, to reduce the risk of |
25985edce Fix common misspe... |
1800 |
* losing an error. |
ca9c90ba0 i7core_edac: Use ... |
1801 1802 |
*/ smp_rmb(); |
321ece4dd i7core_edac: Fix ... |
1803 1804 |
count = (pvt->mce_out + MCE_LOG_LEN - pvt->mce_in) % MCE_LOG_LEN; |
ca9c90ba0 i7core_edac: Use ... |
1805 |
if (!count) |
8a311e179 Always call i7cor... |
1806 |
goto check_ce_error; |
f47429494 i7core_edac: crea... |
1807 |
|
ca9c90ba0 i7core_edac: Use ... |
1808 |
m = pvt->mce_outentry; |
321ece4dd i7core_edac: Fix ... |
1809 1810 |
if (pvt->mce_in + count > MCE_LOG_LEN) { unsigned l = MCE_LOG_LEN - pvt->mce_in; |
f47429494 i7core_edac: crea... |
1811 |
|
ca9c90ba0 i7core_edac: Use ... |
1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 |
memcpy(m, &pvt->mce_entry[pvt->mce_in], sizeof(*m) * l); smp_wmb(); pvt->mce_in = 0; count -= l; m += l; } memcpy(m, &pvt->mce_entry[pvt->mce_in], sizeof(*m) * count); smp_wmb(); pvt->mce_in += count; smp_rmb(); if (pvt->mce_overrun) { i7core_printk(KERN_ERR, "Lost %d memory errors ", pvt->mce_overrun); smp_wmb(); pvt->mce_overrun = 0; } |
d5381642a i7core_edac: Add ... |
1830 |
|
ca9c90ba0 i7core_edac: Use ... |
1831 1832 1833 |
/* * MCE second step: parse errors and display */ |
d5381642a i7core_edac: Add ... |
1834 |
for (i = 0; i < count; i++) |
ca9c90ba0 i7core_edac: Use ... |
1835 |
i7core_mce_output_error(mci, &pvt->mce_outentry[i]); |
d5381642a i7core_edac: Add ... |
1836 |
|
ca9c90ba0 i7core_edac: Use ... |
1837 1838 1839 |
/* * Now, let's increment CE error counts */ |
8a311e179 Always call i7cor... |
1840 |
check_ce_error: |
f47429494 i7core_edac: crea... |
1841 1842 1843 1844 |
if (!pvt->is_registered) i7core_udimm_check_mc_ecc_err(mci); else i7core_rdimm_check_mc_ecc_err(mci); |
87d1d272b i7core_edac: need... |
1845 1846 1847 |
} /* |
d5381642a i7core_edac: Add ... |
1848 1849 1850 1851 |
* i7core_mce_check_error Replicates mcelog routine to get errors * This routine simply queues mcelog errors, and * return. The error itself should be handled later * by i7core_check_error. |
6e103be1c i7core_edac: Firs... |
1852 1853 |
* WARNING: As this routine should be called at NMI time, extra care should * be taken to avoid deadlocks, and to be as fast as possible. |
d5381642a i7core_edac: Add ... |
1854 |
*/ |
4140c5426 i7core_edac: Drop... |
1855 1856 |
static int i7core_mce_check_error(struct notifier_block *nb, unsigned long val, void *data) |
d5381642a i7core_edac: Add ... |
1857 |
{ |
4140c5426 i7core_edac: Drop... |
1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 |
struct mce *mce = (struct mce *)data; struct i7core_dev *i7_dev; struct mem_ctl_info *mci; struct i7core_pvt *pvt; i7_dev = get_i7core_dev(mce->socketid); if (!i7_dev) return NOTIFY_BAD; mci = i7_dev->mci; pvt = mci->pvt_info; |
d5381642a i7core_edac: Add ... |
1869 |
|
8a2f118e3 i7core_edac: deco... |
1870 1871 1872 1873 1874 |
/* * Just let mcelog handle it if the error is * outside the memory controller */ if (((mce->status & 0xffff) >> 7) != 1) |
4140c5426 i7core_edac: Drop... |
1875 |
return NOTIFY_DONE; |
8a2f118e3 i7core_edac: deco... |
1876 |
|
f237fcf2b i7core_edac: some... |
1877 1878 |
/* Bank 8 registers are the only ones that we know how to handle */ if (mce->bank != 8) |
4140c5426 i7core_edac: Drop... |
1879 |
return NOTIFY_DONE; |
f237fcf2b i7core_edac: some... |
1880 |
|
3b918c12d edac: fix i7core ... |
1881 |
#ifdef CONFIG_SMP |
f47429494 i7core_edac: crea... |
1882 |
/* Only handle if it is the right mc controller */ |
5034086b7 EDAC i7core: Use ... |
1883 |
if (mce->socketid != pvt->i7core_dev->socket) |
4140c5426 i7core_edac: Drop... |
1884 |
return NOTIFY_DONE; |
3b918c12d edac: fix i7core ... |
1885 |
#endif |
f47429494 i7core_edac: crea... |
1886 |
|
ca9c90ba0 i7core_edac: Use ... |
1887 |
smp_rmb(); |
321ece4dd i7core_edac: Fix ... |
1888 |
if ((pvt->mce_out + 1) % MCE_LOG_LEN == pvt->mce_in) { |
ca9c90ba0 i7core_edac: Use ... |
1889 1890 |
smp_wmb(); pvt->mce_overrun++; |
4140c5426 i7core_edac: Drop... |
1891 |
return NOTIFY_DONE; |
d5381642a i7core_edac: Add ... |
1892 |
} |
6e103be1c i7core_edac: Firs... |
1893 1894 1895 |
/* Copy memory error at the ringbuffer */ memcpy(&pvt->mce_entry[pvt->mce_out], mce, sizeof(*mce)); |
ca9c90ba0 i7core_edac: Use ... |
1896 |
smp_wmb(); |
321ece4dd i7core_edac: Fix ... |
1897 |
pvt->mce_out = (pvt->mce_out + 1) % MCE_LOG_LEN; |
d5381642a i7core_edac: Add ... |
1898 |
|
c5d345286 i7core: check if ... |
1899 1900 1901 |
/* Handle fatal errors immediately */ if (mce->mcgstatus & 1) i7core_check_error(mci); |
e7bf068aa i7core_edac: fix ... |
1902 |
/* Advise mcelog that the errors were handled */ |
4140c5426 i7core_edac: Drop... |
1903 |
return NOTIFY_STOP; |
d5381642a i7core_edac: Add ... |
1904 |
} |
4140c5426 i7core_edac: Drop... |
1905 1906 1907 |
static struct notifier_block i7_mce_dec = { .notifier_call = i7core_mce_check_error, }; |
535e9c78e i7core_edac: scru... |
1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 |
struct memdev_dmi_entry { u8 type; u8 length; u16 handle; u16 phys_mem_array_handle; u16 mem_err_info_handle; u16 total_width; u16 data_width; u16 size; u8 form; u8 device_set; u8 device_locator; u8 bank_locator; u8 memory_type; u16 type_detail; u16 speed; u8 manufacturer; u8 serial_number; u8 asset_tag; u8 part_number; u8 attributes; u32 extended_size; u16 conf_mem_clk_speed; } __attribute__((__packed__)); /* * Decode the DRAM Clock Frequency, be paranoid, make sure that all * memory devices show the same speed, and if they don't then consider * all speeds to be invalid. */ static void decode_dclk(const struct dmi_header *dh, void *_dclk_freq) { int *dclk_freq = _dclk_freq; u16 dmi_mem_clk_speed; if (*dclk_freq == -1) return; if (dh->type == DMI_ENTRY_MEM_DEVICE) { struct memdev_dmi_entry *memdev_dmi_entry = (struct memdev_dmi_entry *)dh; unsigned long conf_mem_clk_speed_offset = (unsigned long)&memdev_dmi_entry->conf_mem_clk_speed - (unsigned long)&memdev_dmi_entry->type; unsigned long speed_offset = (unsigned long)&memdev_dmi_entry->speed - (unsigned long)&memdev_dmi_entry->type; /* Check that a DIMM is present */ if (memdev_dmi_entry->size == 0) return; /* * Pick the configured speed if it's available, otherwise * pick the DIMM speed, or we don't have a speed. */ if (memdev_dmi_entry->length > conf_mem_clk_speed_offset) { dmi_mem_clk_speed = memdev_dmi_entry->conf_mem_clk_speed; } else if (memdev_dmi_entry->length > speed_offset) { dmi_mem_clk_speed = memdev_dmi_entry->speed; } else { *dclk_freq = -1; return; } if (*dclk_freq == 0) { /* First pass, speed was 0 */ if (dmi_mem_clk_speed > 0) { /* Set speed if a valid speed is read */ *dclk_freq = dmi_mem_clk_speed; } else { /* Otherwise we don't have a valid speed */ *dclk_freq = -1; } } else if (*dclk_freq > 0 && *dclk_freq != dmi_mem_clk_speed) { /* * If we have a speed, check that all DIMMS are the same * speed, otherwise set the speed as invalid. */ *dclk_freq = -1; } } } /* * The default DCLK frequency is used as a fallback if we * fail to find anything reliable in the DMI. The value * is taken straight from the datasheet. */ #define DEFAULT_DCLK_FREQ 800 static int get_dclk_freq(void) { int dclk_freq = 0; dmi_walk(decode_dclk, (void *)&dclk_freq); if (dclk_freq < 1) return DEFAULT_DCLK_FREQ; return dclk_freq; } |
e8b6a1271 i7core_edac: Add ... |
2013 2014 2015 2016 2017 2018 2019 2020 2021 |
/* * set_sdram_scrub_rate This routine sets byte/sec bandwidth scrub rate * to hardware according to SCRUBINTERVAL formula * found in datasheet. */ static int set_sdram_scrub_rate(struct mem_ctl_info *mci, u32 new_bw) { struct i7core_pvt *pvt = mci->pvt_info; struct pci_dev *pdev; |
e8b6a1271 i7core_edac: Add ... |
2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 |
u32 dw_scrub; u32 dw_ssr; /* Get data from the MC register, function 2 */ pdev = pvt->pci_mcr[2]; if (!pdev) return -ENODEV; pci_read_config_dword(pdev, MC_SCRUB_CONTROL, &dw_scrub); if (new_bw == 0) { /* Prepare to disable petrol scrub */ dw_scrub &= ~STARTSCRUB; /* Stop the patrol scrub engine */ |
535e9c78e i7core_edac: scru... |
2036 2037 |
write_and_test(pdev, MC_SCRUB_CONTROL, dw_scrub & ~SCRUBINTERVAL_MASK); |
e8b6a1271 i7core_edac: Add ... |
2038 2039 2040 2041 2042 2043 |
/* Get current status of scrub rate and set bit to disable */ pci_read_config_dword(pdev, MC_SSRCONTROL, &dw_ssr); dw_ssr &= ~SSR_MODE_MASK; dw_ssr |= SSR_MODE_DISABLE; } else { |
535e9c78e i7core_edac: scru... |
2044 2045 2046 |
const int cache_line_size = 64; const u32 freq_dclk_mhz = pvt->dclk_freq; unsigned long long scrub_interval; |
e8b6a1271 i7core_edac: Add ... |
2047 2048 |
/* * Translate the desired scrub rate to a register value and |
535e9c78e i7core_edac: scru... |
2049 |
* program the corresponding register value. |
e8b6a1271 i7core_edac: Add ... |
2050 |
*/ |
535e9c78e i7core_edac: scru... |
2051 |
scrub_interval = (unsigned long long)freq_dclk_mhz * |
4fad8098b i7core_edac: Fix ... |
2052 2053 |
cache_line_size * 1000000; do_div(scrub_interval, new_bw); |
535e9c78e i7core_edac: scru... |
2054 2055 2056 2057 2058 |
if (!scrub_interval || scrub_interval > SCRUBINTERVAL_MASK) return -EINVAL; dw_scrub = SCRUBINTERVAL_MASK & scrub_interval; |
e8b6a1271 i7core_edac: Add ... |
2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 |
/* Start the patrol scrub engine */ pci_write_config_dword(pdev, MC_SCRUB_CONTROL, STARTSCRUB | dw_scrub); /* Get current status of scrub rate and set bit to enable */ pci_read_config_dword(pdev, MC_SSRCONTROL, &dw_ssr); dw_ssr &= ~SSR_MODE_MASK; dw_ssr |= SSR_MODE_ENABLE; } /* Disable or enable scrubbing */ pci_write_config_dword(pdev, MC_SSRCONTROL, dw_ssr); return new_bw; } /* * get_sdram_scrub_rate This routine convert current scrub rate value * into byte/sec bandwidth accourding to * SCRUBINTERVAL formula found in datasheet. */ static int get_sdram_scrub_rate(struct mem_ctl_info *mci) { struct i7core_pvt *pvt = mci->pvt_info; struct pci_dev *pdev; const u32 cache_line_size = 64; |
535e9c78e i7core_edac: scru... |
2085 2086 |
const u32 freq_dclk_mhz = pvt->dclk_freq; unsigned long long scrub_rate; |
e8b6a1271 i7core_edac: Add ... |
2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 |
u32 scrubval; /* Get data from the MC register, function 2 */ pdev = pvt->pci_mcr[2]; if (!pdev) return -ENODEV; /* Get current scrub control data */ pci_read_config_dword(pdev, MC_SCRUB_CONTROL, &scrubval); /* Mask highest 8-bits to 0 */ |
535e9c78e i7core_edac: scru... |
2098 |
scrubval &= SCRUBINTERVAL_MASK; |
e8b6a1271 i7core_edac: Add ... |
2099 2100 2101 2102 |
if (!scrubval) return 0; /* Calculate scrub rate value into byte/sec bandwidth */ |
535e9c78e i7core_edac: scru... |
2103 |
scrub_rate = (unsigned long long)freq_dclk_mhz * |
4fad8098b i7core_edac: Fix ... |
2104 2105 |
1000000 * cache_line_size; do_div(scrub_rate, scrubval); |
535e9c78e i7core_edac: scru... |
2106 |
return (int)scrub_rate; |
e8b6a1271 i7core_edac: Add ... |
2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 |
} static void enable_sdram_scrub_setting(struct mem_ctl_info *mci) { struct i7core_pvt *pvt = mci->pvt_info; u32 pci_lock; /* Unlock writes to pci registers */ pci_read_config_dword(pvt->pci_noncore, MC_CFG_CONTROL, &pci_lock); pci_lock &= ~0x3; pci_write_config_dword(pvt->pci_noncore, MC_CFG_CONTROL, pci_lock | MC_CFG_UNLOCK); mci->set_sdram_scrub_rate = set_sdram_scrub_rate; mci->get_sdram_scrub_rate = get_sdram_scrub_rate; } static void disable_sdram_scrub_setting(struct mem_ctl_info *mci) { struct i7core_pvt *pvt = mci->pvt_info; u32 pci_lock; /* Lock writes to pci registers */ pci_read_config_dword(pvt->pci_noncore, MC_CFG_CONTROL, &pci_lock); pci_lock &= ~0x3; pci_write_config_dword(pvt->pci_noncore, MC_CFG_CONTROL, pci_lock | MC_CFG_LOCK); } |
a3aa0a4ab i7core_edac: Intr... |
2135 2136 2137 2138 2139 2140 |
static void i7core_pci_ctl_create(struct i7core_pvt *pvt) { pvt->i7core_pci = edac_pci_create_generic_ctl( &pvt->i7core_dev->pdev[0]->dev, EDAC_MOD_STR); if (unlikely(!pvt->i7core_pci)) |
f9902f24f i7core_edac: use ... |
2141 2142 2143 |
i7core_printk(KERN_WARNING, "Unable to setup PCI error report via EDAC "); |
a3aa0a4ab i7core_edac: Intr... |
2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 |
} static void i7core_pci_ctl_release(struct i7core_pvt *pvt) { if (likely(pvt->i7core_pci)) edac_pci_release_generic_ctl(pvt->i7core_pci); else i7core_printk(KERN_ERR, "Couldn't find mem_ctl_info for socket %d ", pvt->i7core_dev->socket); pvt->i7core_pci = NULL; } |
1c6edbbe2 i7core_edac: Intr... |
2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 |
static void i7core_unregister_mci(struct i7core_dev *i7core_dev) { struct mem_ctl_info *mci = i7core_dev->mci; struct i7core_pvt *pvt; if (unlikely(!mci || !mci->pvt_info)) { debugf0("MC: " __FILE__ ": %s(): dev = %p ", __func__, &i7core_dev->pdev[0]->dev); i7core_printk(KERN_ERR, "Couldn't find mci handler "); return; } pvt = mci->pvt_info; debugf0("MC: " __FILE__ ": %s(): mci = %p, dev = %p ", __func__, mci, &i7core_dev->pdev[0]->dev); |
e8b6a1271 i7core_edac: Add ... |
2177 |
/* Disable scrubrate setting */ |
27100db0e i7core_edac: Don'... |
2178 2179 |
if (pvt->enable_scrub) disable_sdram_scrub_setting(mci); |
e8b6a1271 i7core_edac: Add ... |
2180 |
|
3653ada5d x86, mce: Add wra... |
2181 |
mce_unregister_decode_chain(&i7_mce_dec); |
1c6edbbe2 i7core_edac: Intr... |
2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 |
/* Disable EDAC polling */ i7core_pci_ctl_release(pvt); /* Remove MC sysfs nodes */ edac_mc_del_mc(mci->dev); debugf1("%s: free mci struct ", mci->ctl_name); kfree(mci->ctl_name); edac_mc_free(mci); i7core_dev->mci = NULL; } |
aace42831 i7core_edac: Redu... |
2195 |
static int i7core_register_mci(struct i7core_dev *i7core_dev) |
a0c36a1f0 i7core_edac: Add ... |
2196 2197 2198 |
{ struct mem_ctl_info *mci; struct i7core_pvt *pvt; |
aace42831 i7core_edac: Redu... |
2199 2200 2201 2202 2203 2204 |
int rc, channels, csrows; /* Check the number of active and not disabled channels */ rc = i7core_get_active_channels(i7core_dev->socket, &channels, &csrows); if (unlikely(rc < 0)) return rc; |
a0c36a1f0 i7core_edac: Add ... |
2205 |
|
a0c36a1f0 i7core_edac: Add ... |
2206 |
/* allocate a new MC control structure */ |
aace42831 i7core_edac: Redu... |
2207 |
mci = edac_mc_alloc(sizeof(*pvt), csrows, channels, i7core_dev->socket); |
f47429494 i7core_edac: crea... |
2208 2209 |
if (unlikely(!mci)) return -ENOMEM; |
a0c36a1f0 i7core_edac: Add ... |
2210 |
|
3cfd01468 i7core_edac: Impr... |
2211 2212 2213 |
debugf0("MC: " __FILE__ ": %s(): mci = %p, dev = %p ", __func__, mci, &i7core_dev->pdev[0]->dev); |
a0c36a1f0 i7core_edac: Add ... |
2214 |
|
a0c36a1f0 i7core_edac: Add ... |
2215 |
pvt = mci->pvt_info; |
ef708b53b i7core_edac: Add ... |
2216 |
memset(pvt, 0, sizeof(*pvt)); |
67166af4a i7core_edac: add ... |
2217 |
|
6d37d240f i7core_edac: Fix ... |
2218 2219 2220 |
/* Associates i7core_dev and mci for future usage */ pvt->i7core_dev = i7core_dev; i7core_dev->mci = mci; |
41fcb7fee i7core_edac: Codi... |
2221 2222 2223 2224 2225 2226 |
/* * FIXME: how to handle RDDR3 at MCI level? It is possible to have * Mixed RDDR3/UDDR3 with Nehalem, provided that they are on different * memory channels */ mci->mtype_cap = MEM_FLAG_DDR3; |
a0c36a1f0 i7core_edac: Add ... |
2227 2228 2229 2230 |
mci->edac_ctl_cap = EDAC_FLAG_NONE; mci->edac_cap = EDAC_FLAG_NONE; mci->mod_name = "i7core_edac.c"; mci->mod_ver = I7CORE_REVISION; |
f47429494 i7core_edac: crea... |
2231 2232 2233 |
mci->ctl_name = kasprintf(GFP_KERNEL, "i7 core #%d", i7core_dev->socket); mci->dev_name = pci_name(i7core_dev->pdev[0]); |
a0c36a1f0 i7core_edac: Add ... |
2234 |
mci->ctl_page_to_phys = NULL; |
1288c18f4 i7core_edac: Prop... |
2235 |
|
ef708b53b i7core_edac: Add ... |
2236 |
/* Store pci devices at mci for faster access */ |
f47429494 i7core_edac: crea... |
2237 |
rc = mci_bind_devs(mci, i7core_dev); |
41fcb7fee i7core_edac: Codi... |
2238 |
if (unlikely(rc < 0)) |
628c5ddfb i7core_edac: Fix ... |
2239 |
goto fail0; |
ef708b53b i7core_edac: Add ... |
2240 |
|
5939813b9 i7core_edac: Fix ... |
2241 2242 2243 2244 |
if (pvt->is_registered) mci->mc_driver_sysfs_attributes = i7core_sysfs_rdimm_attrs; else mci->mc_driver_sysfs_attributes = i7core_sysfs_udimm_attrs; |
ef708b53b i7core_edac: Add ... |
2245 |
/* Get dimm basic config */ |
2e5185f7f i7core_edac: Remo... |
2246 |
get_dimm_config(mci); |
5939813b9 i7core_edac: Fix ... |
2247 2248 2249 2250 |
/* record ptr to the generic device */ mci->dev = &i7core_dev->pdev[0]->dev; /* Set the function pointer to an actual operation function */ mci->edac_check = i7core_check_error; |
ef708b53b i7core_edac: Add ... |
2251 |
|
e8b6a1271 i7core_edac: Add ... |
2252 |
/* Enable scrubrate setting */ |
27100db0e i7core_edac: Don'... |
2253 2254 |
if (pvt->enable_scrub) enable_sdram_scrub_setting(mci); |
e8b6a1271 i7core_edac: Add ... |
2255 |
|
a0c36a1f0 i7core_edac: Add ... |
2256 |
/* add this new MC control structure to EDAC's list of MCs */ |
b7c761512 i7core_edac: Impr... |
2257 |
if (unlikely(edac_mc_add_mc(mci))) { |
a0c36a1f0 i7core_edac: Add ... |
2258 2259 2260 2261 2262 2263 |
debugf0("MC: " __FILE__ ": %s(): failed edac_mc_add_mc() ", __func__); /* FIXME: perhaps some code should go here that disables error * reporting if we just enabled it */ |
b7c761512 i7core_edac: Impr... |
2264 2265 |
rc = -EINVAL; |
628c5ddfb i7core_edac: Fix ... |
2266 |
goto fail0; |
a0c36a1f0 i7core_edac: Add ... |
2267 |
} |
194a40fea i7core_edac: Add ... |
2268 |
/* Default error mask is any memory */ |
ef708b53b i7core_edac: Add ... |
2269 |
pvt->inject.channel = 0; |
194a40fea i7core_edac: Add ... |
2270 2271 2272 2273 2274 |
pvt->inject.dimm = -1; pvt->inject.rank = -1; pvt->inject.bank = -1; pvt->inject.page = -1; pvt->inject.col = -1; |
a3aa0a4ab i7core_edac: Intr... |
2275 2276 |
/* allocating generic PCI control info */ i7core_pci_ctl_create(pvt); |
535e9c78e i7core_edac: scru... |
2277 2278 |
/* DCLK for scrub rate setting */ pvt->dclk_freq = get_dclk_freq(); |
3653ada5d x86, mce: Add wra... |
2279 |
mce_register_decode_chain(&i7_mce_dec); |
f47429494 i7core_edac: crea... |
2280 |
|
628c5ddfb i7core_edac: Fix ... |
2281 |
return 0; |
628c5ddfb i7core_edac: Fix ... |
2282 2283 2284 |
fail0: kfree(mci->ctl_name); edac_mc_free(mci); |
1c6edbbe2 i7core_edac: Intr... |
2285 |
i7core_dev->mci = NULL; |
f47429494 i7core_edac: crea... |
2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 |
return rc; } /* * i7core_probe Probe for ONE instance of device to see if it is * present. * return: * 0 for FOUND a device * < 0 for error code */ |
2d95d8158 i7core_edac: Avoi... |
2296 |
|
f47429494 i7core_edac: crea... |
2297 2298 2299 |
static int __devinit i7core_probe(struct pci_dev *pdev, const struct pci_device_id *id) { |
405575914 i7core_edac: retu... |
2300 |
int rc, count = 0; |
f47429494 i7core_edac: crea... |
2301 |
struct i7core_dev *i7core_dev; |
2d95d8158 i7core_edac: Avoi... |
2302 2303 |
/* get the pci devices we want to reserve for our use */ mutex_lock(&i7core_edac_lock); |
f47429494 i7core_edac: crea... |
2304 |
/* |
d4c277957 i7core_edac: a fe... |
2305 |
* All memory controllers are allocated at the first pass. |
f47429494 i7core_edac: crea... |
2306 |
*/ |
2d95d8158 i7core_edac: Avoi... |
2307 2308 |
if (unlikely(probed >= 1)) { mutex_unlock(&i7core_edac_lock); |
76a7bd811 i7core_edac: retu... |
2309 |
return -ENODEV; |
2d95d8158 i7core_edac: Avoi... |
2310 2311 |
} probed++; |
de06eeef5 i7core_edac: Use ... |
2312 |
|
64c10f6e0 i7core_edac: Alwa... |
2313 |
rc = i7core_get_all_devices(); |
f47429494 i7core_edac: crea... |
2314 2315 2316 2317 |
if (unlikely(rc < 0)) goto fail0; list_for_each_entry(i7core_dev, &i7core_edac_list, list) { |
405575914 i7core_edac: retu... |
2318 |
count++; |
aace42831 i7core_edac: Redu... |
2319 |
rc = i7core_register_mci(i7core_dev); |
d4c277957 i7core_edac: a fe... |
2320 2321 |
if (unlikely(rc < 0)) goto fail1; |
d5381642a i7core_edac: Add ... |
2322 |
} |
405575914 i7core_edac: retu... |
2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 |
/* * Nehalem-EX uses a different memory controller. However, as the * memory controller is not visible on some Nehalem/Nehalem-EP, we * need to indirectly probe via a X58 PCI device. The same devices * are found on (some) Nehalem-EX. So, on those machines, the * probe routine needs to return -ENODEV, as the actual Memory * Controller registers won't be detected. */ if (!count) { rc = -ENODEV; goto fail1; } i7core_printk(KERN_INFO, "Driver loaded, %d memory controller(s) found. ", count); |
8f3319075 i7core_edac: Regi... |
2340 |
|
66607706c Dynamically alloc... |
2341 |
mutex_unlock(&i7core_edac_lock); |
a0c36a1f0 i7core_edac: Add ... |
2342 |
return 0; |
66607706c Dynamically alloc... |
2343 |
fail1: |
88ef5ea97 i7core_edac: it i... |
2344 2345 |
list_for_each_entry(i7core_dev, &i7core_edac_list, list) i7core_unregister_mci(i7core_dev); |
13d6e9b65 i7core_edac: at r... |
2346 |
i7core_put_all_devices(); |
66607706c Dynamically alloc... |
2347 2348 |
fail0: mutex_unlock(&i7core_edac_lock); |
b7c761512 i7core_edac: Impr... |
2349 |
return rc; |
a0c36a1f0 i7core_edac: Add ... |
2350 2351 2352 2353 2354 2355 2356 2357 |
} /* * i7core_remove destructor for one instance of device * */ static void __devexit i7core_remove(struct pci_dev *pdev) { |
64c10f6e0 i7core_edac: Alwa... |
2358 |
struct i7core_dev *i7core_dev; |
a0c36a1f0 i7core_edac: Add ... |
2359 2360 2361 |
debugf0(__FILE__ ": %s() ", __func__); |
22e6bcbdc i7core_edac: chan... |
2362 2363 2364 2365 2366 2367 2368 |
/* * we have a trouble here: pdev value for removal will be wrong, since * it will point to the X58 register used to detect that the machine * is a Nehalem or upper design. However, due to the way several PCI * devices are grouped together to provide MC functionality, we need * to use a different method for releasing the devices */ |
87d1d272b i7core_edac: need... |
2369 |
|
66607706c Dynamically alloc... |
2370 |
mutex_lock(&i7core_edac_lock); |
71fe01706 i7core_edac: Chec... |
2371 2372 2373 2374 2375 |
if (unlikely(!probed)) { mutex_unlock(&i7core_edac_lock); return; } |
88ef5ea97 i7core_edac: it i... |
2376 2377 |
list_for_each_entry(i7core_dev, &i7core_edac_list, list) i7core_unregister_mci(i7core_dev); |
64c10f6e0 i7core_edac: Alwa... |
2378 2379 2380 |
/* Release PCI resources */ i7core_put_all_devices(); |
2d95d8158 i7core_edac: Avoi... |
2381 |
probed--; |
66607706c Dynamically alloc... |
2382 |
mutex_unlock(&i7core_edac_lock); |
a0c36a1f0 i7core_edac: Add ... |
2383 |
} |
a0c36a1f0 i7core_edac: Add ... |
2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 |
MODULE_DEVICE_TABLE(pci, i7core_pci_tbl); /* * i7core_driver pci_driver structure for this module * */ static struct pci_driver i7core_driver = { .name = "i7core_edac", .probe = i7core_probe, .remove = __devexit_p(i7core_remove), .id_table = i7core_pci_tbl, }; /* * i7core_init Module entry function * Try to initialize this module for its devices */ static int __init i7core_init(void) { int pci_rc; debugf2("MC: " __FILE__ ": %s() ", __func__); /* Ensure that the OPSTATE is set correctly for POLL or NMI */ opstate_init(); |
54a08ab15 i7core_edac: Don'... |
2410 2411 |
if (use_pci_fixup) i7core_xeon_pci_fixup(pci_dev_table); |
bc2d7245f i7core_edac: Prob... |
2412 |
|
a0c36a1f0 i7core_edac: Add ... |
2413 |
pci_rc = pci_register_driver(&i7core_driver); |
3ef288a98 i7core_edac: Prin... |
2414 2415 2416 2417 2418 2419 2420 2421 |
if (pci_rc >= 0) return 0; i7core_printk(KERN_ERR, "Failed to register device with error %d. ", pci_rc); return pci_rc; |
a0c36a1f0 i7core_edac: Add ... |
2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 |
} /* * i7core_exit() Module exit function * Unregister the driver */ static void __exit i7core_exit(void) { debugf2("MC: " __FILE__ ": %s() ", __func__); pci_unregister_driver(&i7core_driver); } module_init(i7core_init); module_exit(i7core_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>"); MODULE_AUTHOR("Red Hat Inc. (http://www.redhat.com)"); MODULE_DESCRIPTION("MC Driver for Intel i7 Core memory controllers - " I7CORE_REVISION); module_param(edac_op_state, int, 0444); MODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI"); |