blob: eec0c13c0205fe46dd1ba6850a107f22449ff503 [file] [log] [blame]
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001/* Intel 7 core Memory Controller kernel module (Nehalem)
2 *
3 * This file may be distributed under the terms of the
4 * GNU General Public License version 2 only.
5 *
6 * Copyright (c) 2009 by:
7 * Mauro Carvalho Chehab <mchehab@redhat.com>
8 *
9 * Red Hat Inc. http://www.redhat.com
10 *
11 * Forked and adapted from the i5400_edac driver
12 *
13 * Based on the following public Intel datasheets:
14 * Intel Core i7 Processor Extreme Edition and Intel Core i7 Processor
15 * Datasheet, Volume 2:
16 * http://download.intel.com/design/processor/datashts/320835.pdf
17 * Intel Xeon Processor 5500 Series Datasheet Volume 2
18 * http://www.intel.com/Assets/PDF/datasheet/321322.pdf
19 * also available at:
20 * http://www.arrownac.com/manufacturers/intel/s/nehalem/5500-datasheet-v2.pdf
21 */
22
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030023#include <linux/module.h>
24#include <linux/init.h>
25#include <linux/pci.h>
26#include <linux/pci_ids.h>
27#include <linux/slab.h>
28#include <linux/edac.h>
29#include <linux/mmzone.h>
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -030030#include <linux/edac_mce.h>
31#include <linux/spinlock.h>
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030032
33#include "edac_core.h"
34
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -030035/* To use the new pci_[read/write]_config_qword instead of two dword */
Mauro Carvalho Chehabe9bd2e72009-07-09 22:14:35 -030036#define USE_QWORD 0
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030037
38/*
39 * Alter this version for the module when modifications are made
40 */
41#define I7CORE_REVISION " Ver: 1.0.0 " __DATE__
42#define EDAC_MOD_STR "i7core_edac"
43
44/* HACK: temporary, just to enable all logs, for now */
45#undef debugf0
46#define debugf0(fmt, arg...) edac_printk(KERN_INFO, "i7core", fmt, ##arg)
47
48/*
49 * Debug macros
50 */
51#define i7core_printk(level, fmt, arg...) \
52 edac_printk(level, "i7core", fmt, ##arg)
53
54#define i7core_mc_printk(mci, level, fmt, arg...) \
55 edac_mc_chipset_printk(mci, level, "i7core", fmt, ##arg)
56
57/*
58 * i7core Memory Controller Registers
59 */
60
Mauro Carvalho Chehabe9bd2e72009-07-09 22:14:35 -030061 /* OFFSETS for Device 0 Function 0 */
62
63#define MC_CFG_CONTROL 0x90
64
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030065 /* OFFSETS for Device 3 Function 0 */
66
67#define MC_CONTROL 0x48
68#define MC_STATUS 0x4c
69#define MC_MAX_DOD 0x64
70
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -030071/*
72 * OFFSETS for Device 3 Function 4, as inicated on Xeon 5500 datasheet:
73 * http://www.arrownac.com/manufacturers/intel/s/nehalem/5500-datasheet-v2.pdf
74 */
75
76#define MC_TEST_ERR_RCV1 0x60
77 #define DIMM2_COR_ERR(r) ((r) & 0x7fff)
78
79#define MC_TEST_ERR_RCV0 0x64
80 #define DIMM1_COR_ERR(r) (((r) >> 16) & 0x7fff)
81 #define DIMM0_COR_ERR(r) ((r) & 0x7fff)
82
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030083 /* OFFSETS for Devices 4,5 and 6 Function 0 */
84
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -030085#define MC_CHANNEL_DIMM_INIT_PARAMS 0x58
86 #define THREE_DIMMS_PRESENT (1 << 24)
87 #define SINGLE_QUAD_RANK_PRESENT (1 << 23)
88 #define QUAD_RANK_PRESENT (1 << 22)
89 #define REGISTERED_DIMM (1 << 15)
90
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -030091#define MC_CHANNEL_MAPPER 0x60
92 #define RDLCH(r, ch) ((((r) >> (3 + (ch * 6))) & 0x07) - 1)
93 #define WRLCH(r, ch) ((((r) >> (ch * 6)) & 0x07) - 1)
94
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -030095#define MC_CHANNEL_RANK_PRESENT 0x7c
96 #define RANK_PRESENT_MASK 0xffff
97
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030098#define MC_CHANNEL_ADDR_MATCH 0xf0
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -030099#define MC_CHANNEL_ERROR_MASK 0xf8
100#define MC_CHANNEL_ERROR_INJECT 0xfc
101 #define INJECT_ADDR_PARITY 0x10
102 #define INJECT_ECC 0x08
103 #define MASK_CACHELINE 0x06
104 #define MASK_FULL_CACHELINE 0x06
105 #define MASK_MSB32_CACHELINE 0x04
106 #define MASK_LSB32_CACHELINE 0x02
107 #define NO_MASK_CACHELINE 0x00
108 #define REPEAT_EN 0x01
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300109
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300110 /* OFFSETS for Devices 4,5 and 6 Function 1 */
111#define MC_DOD_CH_DIMM0 0x48
112#define MC_DOD_CH_DIMM1 0x4c
113#define MC_DOD_CH_DIMM2 0x50
114 #define RANKOFFSET_MASK ((1 << 12) | (1 << 11) | (1 << 10))
115 #define RANKOFFSET(x) ((x & RANKOFFSET_MASK) >> 10)
116 #define DIMM_PRESENT_MASK (1 << 9)
117 #define DIMM_PRESENT(x) (((x) & DIMM_PRESENT_MASK) >> 9)
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300118 #define MC_DOD_NUMBANK_MASK ((1 << 8) | (1 << 7))
119 #define MC_DOD_NUMBANK(x) (((x) & MC_DOD_NUMBANK_MASK) >> 7)
120 #define MC_DOD_NUMRANK_MASK ((1 << 6) | (1 << 5))
121 #define MC_DOD_NUMRANK(x) (((x) & MC_DOD_NUMRANK_MASK) >> 5)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300122 #define MC_DOD_NUMROW_MASK ((1 << 4) | (1 << 3) | (1 << 2))
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300123 #define MC_DOD_NUMROW(x) (((x) & MC_DOD_NUMROW_MASK) >> 2)
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300124 #define MC_DOD_NUMCOL_MASK 3
125 #define MC_DOD_NUMCOL(x) ((x) & MC_DOD_NUMCOL_MASK)
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300126
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300127#define MC_RANK_PRESENT 0x7c
128
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300129#define MC_SAG_CH_0 0x80
130#define MC_SAG_CH_1 0x84
131#define MC_SAG_CH_2 0x88
132#define MC_SAG_CH_3 0x8c
133#define MC_SAG_CH_4 0x90
134#define MC_SAG_CH_5 0x94
135#define MC_SAG_CH_6 0x98
136#define MC_SAG_CH_7 0x9c
137
138#define MC_RIR_LIMIT_CH_0 0x40
139#define MC_RIR_LIMIT_CH_1 0x44
140#define MC_RIR_LIMIT_CH_2 0x48
141#define MC_RIR_LIMIT_CH_3 0x4C
142#define MC_RIR_LIMIT_CH_4 0x50
143#define MC_RIR_LIMIT_CH_5 0x54
144#define MC_RIR_LIMIT_CH_6 0x58
145#define MC_RIR_LIMIT_CH_7 0x5C
146#define MC_RIR_LIMIT_MASK ((1 << 10) - 1)
147
148#define MC_RIR_WAY_CH 0x80
149 #define MC_RIR_WAY_OFFSET_MASK (((1 << 14) - 1) & ~0x7)
150 #define MC_RIR_WAY_RANK_MASK 0x7
151
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300152/*
153 * i7core structs
154 */
155
156#define NUM_CHANS 3
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -0300157#define MAX_DIMMS 3 /* Max DIMMS per channel */
158#define MAX_MCR_FUNC 4
159#define MAX_CHAN_FUNC 3
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300160
161struct i7core_info {
162 u32 mc_control;
163 u32 mc_status;
164 u32 max_dod;
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300165 u32 ch_map;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300166};
167
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300168
169struct i7core_inject {
170 int enable;
171
172 u32 section;
173 u32 type;
174 u32 eccmask;
175
176 /* Error address mask */
177 int channel, dimm, rank, bank, page, col;
178};
179
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300180struct i7core_channel {
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -0300181 u32 ranks;
182 u32 dimms;
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300183};
184
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300185struct pci_id_descr {
186 int dev;
187 int func;
188 int dev_id;
189 struct pci_dev *pdev;
190};
191
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300192struct i7core_pvt {
Mauro Carvalho Chehabe9bd2e72009-07-09 22:14:35 -0300193 struct pci_dev *pci_noncore;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -0300194 struct pci_dev *pci_mcr[MAX_MCR_FUNC + 1];
195 struct pci_dev *pci_ch[NUM_CHANS][MAX_CHAN_FUNC + 1];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300196 struct i7core_info info;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300197 struct i7core_inject inject;
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300198 struct i7core_channel channel[NUM_CHANS];
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300199 int channels; /* Number of active channels */
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -0300200
201 int ce_count_available;
202 unsigned long ce_count[MAX_DIMMS]; /* ECC corrected errors counts per dimm */
203 int last_ce_count[MAX_DIMMS];
204
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -0300205 /* mcelog glue */
206 struct edac_mce edac_mce;
207 struct mce mce_entry[MCE_LOG_LEN];
208 unsigned mce_count;
209 spinlock_t mce_lock;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300210};
211
212/* Device name and register DID (Device ID) */
213struct i7core_dev_info {
214 const char *ctl_name; /* name for this device */
215 u16 fsb_mapping_errors; /* DID for the branchmap,control */
216};
217
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300218#define PCI_DESCR(device, function, device_id) \
219 .dev = (device), \
220 .func = (function), \
221 .dev_id = (device_id)
222
223struct pci_id_descr pci_devs[] = {
Mauro Carvalho Chehabd1fd4fb2009-07-10 18:39:53 -0300224 /* Generic Non-core registers */
225 { PCI_DESCR(0, 0, PCI_DEVICE_ID_INTEL_I7_NOCORE) },
226
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300227 /* Memory controller */
228 { PCI_DESCR(3, 0, PCI_DEVICE_ID_INTEL_I7_MCR) },
229 { PCI_DESCR(3, 1, PCI_DEVICE_ID_INTEL_I7_MC_TAD) },
230 { PCI_DESCR(3, 2, PCI_DEVICE_ID_INTEL_I7_MC_RAS) }, /* if RDIMM is supported */
231 { PCI_DESCR(3, 4, PCI_DEVICE_ID_INTEL_I7_MC_TEST) },
232
233 /* Channel 0 */
234 { PCI_DESCR(4, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH0_CTRL) },
235 { PCI_DESCR(4, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH0_ADDR) },
236 { PCI_DESCR(4, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH0_RANK) },
237 { PCI_DESCR(4, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH0_TC) },
238
239 /* Channel 1 */
240 { PCI_DESCR(5, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH1_CTRL) },
241 { PCI_DESCR(5, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH1_ADDR) },
242 { PCI_DESCR(5, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH1_RANK) },
243 { PCI_DESCR(5, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH1_TC) },
244
245 /* Channel 2 */
246 { PCI_DESCR(6, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH2_CTRL) },
247 { PCI_DESCR(6, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH2_ADDR) },
248 { PCI_DESCR(6, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH2_RANK) },
249 { PCI_DESCR(6, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH2_TC) },
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300250};
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300251#define N_DEVS ARRAY_SIZE(pci_devs)
252
253/*
254 * pci_device_id table for which devices we are looking for
255 * This should match the first device at pci_devs table
256 */
257static const struct pci_device_id i7core_pci_tbl[] __devinitdata = {
Mauro Carvalho Chehabd1fd4fb2009-07-10 18:39:53 -0300258 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_X58_HUB_MGMT)},
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300259 {0,} /* 0 terminated list. */
260};
261
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300262
263/* Table of devices attributes supported by this driver */
264static const struct i7core_dev_info i7core_devs[] = {
265 {
266 .ctl_name = "i7 Core",
267 .fsb_mapping_errors = PCI_DEVICE_ID_INTEL_I7_MCR,
268 },
269};
270
271static struct edac_pci_ctl_info *i7core_pci;
272
273/****************************************************************************
274 Anciliary status routines
275 ****************************************************************************/
276
277 /* MC_CONTROL bits */
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300278#define CH_ACTIVE(pvt, ch) ((pvt)->info.mc_control & (1 << (8 + ch)))
279#define ECCx8(pvt) ((pvt)->info.mc_control & (1 << 1))
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300280
281 /* MC_STATUS bits */
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300282#define ECC_ENABLED(pvt) ((pvt)->info.mc_status & (1 << 3))
283#define CH_DISABLED(pvt, ch) ((pvt)->info.mc_status & (1 << ch))
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300284
285 /* MC_MAX_DOD read functions */
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300286static inline int numdimms(u32 dimms)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300287{
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300288 return (dimms & 0x3) + 1;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300289}
290
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300291static inline int numrank(u32 rank)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300292{
293 static int ranks[4] = { 1, 2, 4, -EINVAL };
294
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300295 return ranks[rank & 0x3];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300296}
297
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300298static inline int numbank(u32 bank)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300299{
300 static int banks[4] = { 4, 8, 16, -EINVAL };
301
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300302 return banks[bank & 0x3];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300303}
304
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300305static inline int numrow(u32 row)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300306{
307 static int rows[8] = {
308 1 << 12, 1 << 13, 1 << 14, 1 << 15,
309 1 << 16, -EINVAL, -EINVAL, -EINVAL,
310 };
311
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300312 return rows[row & 0x7];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300313}
314
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300315static inline int numcol(u32 col)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300316{
317 static int cols[8] = {
318 1 << 10, 1 << 11, 1 << 12, -EINVAL,
319 };
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300320 return cols[col & 0x3];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300321}
322
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300323
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300324/****************************************************************************
325 Memory check routines
326 ****************************************************************************/
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300327static struct pci_dev *get_pdev_slot_func(int slot, int func)
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300328{
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300329 int i;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300330
331 for (i = 0; i < N_DEVS; i++) {
332 if (!pci_devs[i].pdev)
333 continue;
334
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300335 if (PCI_SLOT(pci_devs[i].pdev->devfn) == slot &&
336 PCI_FUNC(pci_devs[i].pdev->devfn) == func) {
337 return pci_devs[i].pdev;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300338 }
339 }
340
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300341 return NULL;
342}
343
344static int i7core_get_active_channels(int *channels, int *csrows)
345{
346 struct pci_dev *pdev = NULL;
347 int i, j;
348 u32 status, control;
349
350 *channels = 0;
351 *csrows = 0;
352
353 pdev = get_pdev_slot_func(3, 0);
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -0300354 if (!pdev) {
355 i7core_printk(KERN_ERR, "Couldn't find fn 3.0!!!\n");
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300356 return -ENODEV;
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -0300357 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300358
359 /* Device 3 function 0 reads */
360 pci_read_config_dword(pdev, MC_STATUS, &status);
361 pci_read_config_dword(pdev, MC_CONTROL, &control);
362
363 for (i = 0; i < NUM_CHANS; i++) {
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300364 u32 dimm_dod[3];
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300365 /* Check if the channel is active */
366 if (!(control & (1 << (8 + i))))
367 continue;
368
369 /* Check if the channel is disabled */
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300370 if (status & (1 << i))
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300371 continue;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300372
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300373 pdev = get_pdev_slot_func(i + 4, 1);
374 if (!pdev) {
375 i7core_printk(KERN_ERR, "Couldn't find fn %d.%d!!!\n",
376 i + 4, 1);
377 return -ENODEV;
378 }
379 /* Devices 4-6 function 1 */
380 pci_read_config_dword(pdev,
381 MC_DOD_CH_DIMM0, &dimm_dod[0]);
382 pci_read_config_dword(pdev,
383 MC_DOD_CH_DIMM1, &dimm_dod[1]);
384 pci_read_config_dword(pdev,
385 MC_DOD_CH_DIMM2, &dimm_dod[2]);
386
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300387 (*channels)++;
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300388
389 for (j = 0; j < 3; j++) {
390 if (!DIMM_PRESENT(dimm_dod[j]))
391 continue;
392 (*csrows)++;
393 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300394 }
395
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300396 debugf0("Number of active channels: %d\n", *channels);
397
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300398 return 0;
399}
400
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300401static int get_dimm_config(struct mem_ctl_info *mci)
402{
403 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300404 struct csrow_info *csr;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300405 struct pci_dev *pdev;
Mauro Carvalho Chehab7dd69532009-06-22 22:48:30 -0300406 int i, j, csrow = 0;
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300407 unsigned long last_page = 0;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300408 enum edac_type mode;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300409 enum mem_type mtype;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300410
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300411 /* Get data from the MC register, function 0 */
412 pdev = pvt->pci_mcr[0];
Mauro Carvalho Chehab7dd69532009-06-22 22:48:30 -0300413 if (!pdev)
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300414 return -ENODEV;
415
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300416 /* Device 3 function 0 reads */
Mauro Carvalho Chehab7dd69532009-06-22 22:48:30 -0300417 pci_read_config_dword(pdev, MC_CONTROL, &pvt->info.mc_control);
418 pci_read_config_dword(pdev, MC_STATUS, &pvt->info.mc_status);
419 pci_read_config_dword(pdev, MC_MAX_DOD, &pvt->info.max_dod);
420 pci_read_config_dword(pdev, MC_CHANNEL_MAPPER, &pvt->info.ch_map);
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300421
422 debugf0("MC control=0x%08x status=0x%08x dod=0x%08x map=0x%08x\n",
423 pvt->info.mc_control, pvt->info.mc_status,
424 pvt->info.max_dod, pvt->info.ch_map);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300425
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300426 if (ECC_ENABLED(pvt)) {
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300427 debugf0("ECC enabled with x%d SDCC\n", ECCx8(pvt) ? 8 : 4);
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300428 if (ECCx8(pvt))
429 mode = EDAC_S8ECD8ED;
430 else
431 mode = EDAC_S4ECD4ED;
432 } else {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300433 debugf0("ECC disabled\n");
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300434 mode = EDAC_NONE;
435 }
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300436
437 /* FIXME: need to handle the error codes */
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300438 debugf0("DOD Max limits: DIMMS: %d, %d-ranked, %d-banked\n",
439 numdimms(pvt->info.max_dod),
440 numrank(pvt->info.max_dod >> 2),
441 numbank(pvt->info.max_dod >> 4));
442 debugf0("DOD Max rows x colums = 0x%x x 0x%x\n",
443 numrow(pvt->info.max_dod >> 6),
444 numcol(pvt->info.max_dod >> 9));
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300445
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300446 debugf0("Memory channel configuration:\n");
447
448 for (i = 0; i < NUM_CHANS; i++) {
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300449 u32 data, dimm_dod[3], value[8];
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300450
451 if (!CH_ACTIVE(pvt, i)) {
452 debugf0("Channel %i is not active\n", i);
453 continue;
454 }
455 if (CH_DISABLED(pvt, i)) {
456 debugf0("Channel %i is disabled\n", i);
457 continue;
458 }
459
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300460 /* Devices 4-6 function 0 */
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300461 pci_read_config_dword(pvt->pci_ch[i][0],
462 MC_CHANNEL_DIMM_INIT_PARAMS, &data);
463
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300464 pvt->channel[i].ranks = (data & QUAD_RANK_PRESENT) ? 4 : 2;
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300465
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300466 if (data & REGISTERED_DIMM)
467 mtype = MEM_RDDR3;
468 else
469 mtype = MEM_DDR3;
470#if 0
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300471 if (data & THREE_DIMMS_PRESENT)
472 pvt->channel[i].dimms = 3;
473 else if (data & SINGLE_QUAD_RANK_PRESENT)
474 pvt->channel[i].dimms = 1;
475 else
476 pvt->channel[i].dimms = 2;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300477#endif
478
479 /* Devices 4-6 function 1 */
480 pci_read_config_dword(pvt->pci_ch[i][1],
481 MC_DOD_CH_DIMM0, &dimm_dod[0]);
482 pci_read_config_dword(pvt->pci_ch[i][1],
483 MC_DOD_CH_DIMM1, &dimm_dod[1]);
484 pci_read_config_dword(pvt->pci_ch[i][1],
485 MC_DOD_CH_DIMM2, &dimm_dod[2]);
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300486
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300487 debugf0("Ch%d phy rd%d, wr%d (0x%08x): "
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300488 "%d ranks, %cDIMMs\n",
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300489 i,
490 RDLCH(pvt->info.ch_map, i), WRLCH(pvt->info.ch_map, i),
491 data,
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300492 pvt->channel[i].ranks,
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300493 (data & REGISTERED_DIMM) ? 'R' : 'U');
Mauro Carvalho Chehab7dd69532009-06-22 22:48:30 -0300494
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300495 for (j = 0; j < 3; j++) {
496 u32 banks, ranks, rows, cols;
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300497 u32 size, npages;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300498
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300499 if (!DIMM_PRESENT(dimm_dod[j]))
500 continue;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300501
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300502 banks = numbank(MC_DOD_NUMBANK(dimm_dod[j]));
503 ranks = numrank(MC_DOD_NUMRANK(dimm_dod[j]));
504 rows = numrow(MC_DOD_NUMROW(dimm_dod[j]));
505 cols = numcol(MC_DOD_NUMCOL(dimm_dod[j]));
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300506
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300507 /* DDR3 has 8 I/O banks */
508 size = (rows * cols * banks * ranks) >> (20 - 3);
509
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300510 pvt->channel[i].dimms++;
511
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300512 debugf0("\tdimm %d (0x%08x) %d Mb offset: %x, "
513 "numbank: %d,\n\t\t"
514 "numrank: %d, numrow: %#x, numcol: %#x\n",
515 j, dimm_dod[j], size,
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300516 RANKOFFSET(dimm_dod[j]),
517 banks, ranks, rows, cols);
518
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300519#if PAGE_SHIFT > 20
520 npages = size >> (PAGE_SHIFT - 20);
521#else
522 npages = size << (20 - PAGE_SHIFT);
523#endif
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300524
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300525 csr = &mci->csrows[csrow];
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300526 csr->first_page = last_page + 1;
527 last_page += npages;
528 csr->last_page = last_page;
529 csr->nr_pages = npages;
530
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300531 csr->page_mask = 0;
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300532 csr->grain = 8;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300533 csr->csrow_idx = csrow;
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300534 csr->nr_channels = 1;
535
536 csr->channels[0].chan_idx = i;
537 csr->channels[0].ce_count = 0;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300538
539 switch (banks) {
540 case 4:
541 csr->dtype = DEV_X4;
542 break;
543 case 8:
544 csr->dtype = DEV_X8;
545 break;
546 case 16:
547 csr->dtype = DEV_X16;
548 break;
549 default:
550 csr->dtype = DEV_UNKNOWN;
551 }
552
553 csr->edac_mode = mode;
554 csr->mtype = mtype;
555
556 csrow++;
557 }
558
559 pci_read_config_dword(pdev, MC_SAG_CH_0, &value[0]);
560 pci_read_config_dword(pdev, MC_SAG_CH_1, &value[1]);
561 pci_read_config_dword(pdev, MC_SAG_CH_2, &value[2]);
562 pci_read_config_dword(pdev, MC_SAG_CH_3, &value[3]);
563 pci_read_config_dword(pdev, MC_SAG_CH_4, &value[4]);
564 pci_read_config_dword(pdev, MC_SAG_CH_5, &value[5]);
565 pci_read_config_dword(pdev, MC_SAG_CH_6, &value[6]);
566 pci_read_config_dword(pdev, MC_SAG_CH_7, &value[7]);
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300567 debugf0("\t[%i] DIVBY3\tREMOVED\tOFFSET\n", i);
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300568 for (j = 0; j < 8; j++)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300569 debugf0("\t\t%#x\t%#x\t%#x\n",
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300570 (value[j] >> 27) & 0x1,
571 (value[j] >> 24) & 0x7,
572 (value[j] && ((1 << 24) - 1)));
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300573 }
574
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300575 return 0;
576}
577
578/****************************************************************************
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300579 Error insertion routines
580 ****************************************************************************/
581
582/* The i7core has independent error injection features per channel.
583 However, to have a simpler code, we don't allow enabling error injection
584 on more than one channel.
585 Also, since a change at an inject parameter will be applied only at enable,
586 we're disabling error injection on all write calls to the sysfs nodes that
587 controls the error code injection.
588 */
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300589static int disable_inject(struct mem_ctl_info *mci)
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300590{
591 struct i7core_pvt *pvt = mci->pvt_info;
592
593 pvt->inject.enable = 0;
594
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300595 if (!pvt->pci_ch[pvt->inject.channel][0])
596 return -ENODEV;
597
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300598 pci_write_config_dword(pvt->pci_ch[pvt->inject.channel][0],
599 MC_CHANNEL_ERROR_MASK, 0);
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300600
601 return 0;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300602}
603
604/*
605 * i7core inject inject.section
606 *
607 * accept and store error injection inject.section value
608 * bit 0 - refers to the lower 32-byte half cacheline
609 * bit 1 - refers to the upper 32-byte half cacheline
610 */
611static ssize_t i7core_inject_section_store(struct mem_ctl_info *mci,
612 const char *data, size_t count)
613{
614 struct i7core_pvt *pvt = mci->pvt_info;
615 unsigned long value;
616 int rc;
617
618 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300619 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300620
621 rc = strict_strtoul(data, 10, &value);
622 if ((rc < 0) || (value > 3))
623 return 0;
624
625 pvt->inject.section = (u32) value;
626 return count;
627}
628
629static ssize_t i7core_inject_section_show(struct mem_ctl_info *mci,
630 char *data)
631{
632 struct i7core_pvt *pvt = mci->pvt_info;
633 return sprintf(data, "0x%08x\n", pvt->inject.section);
634}
635
636/*
637 * i7core inject.type
638 *
639 * accept and store error injection inject.section value
640 * bit 0 - repeat enable - Enable error repetition
641 * bit 1 - inject ECC error
642 * bit 2 - inject parity error
643 */
644static ssize_t i7core_inject_type_store(struct mem_ctl_info *mci,
645 const char *data, size_t count)
646{
647 struct i7core_pvt *pvt = mci->pvt_info;
648 unsigned long value;
649 int rc;
650
651 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300652 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300653
654 rc = strict_strtoul(data, 10, &value);
655 if ((rc < 0) || (value > 7))
656 return 0;
657
658 pvt->inject.type = (u32) value;
659 return count;
660}
661
662static ssize_t i7core_inject_type_show(struct mem_ctl_info *mci,
663 char *data)
664{
665 struct i7core_pvt *pvt = mci->pvt_info;
666 return sprintf(data, "0x%08x\n", pvt->inject.type);
667}
668
669/*
670 * i7core_inject_inject.eccmask_store
671 *
672 * The type of error (UE/CE) will depend on the inject.eccmask value:
673 * Any bits set to a 1 will flip the corresponding ECC bit
674 * Correctable errors can be injected by flipping 1 bit or the bits within
675 * a symbol pair (2 consecutive aligned 8-bit pairs - i.e. 7:0 and 15:8 or
676 * 23:16 and 31:24). Flipping bits in two symbol pairs will cause an
677 * uncorrectable error to be injected.
678 */
679static ssize_t i7core_inject_eccmask_store(struct mem_ctl_info *mci,
680 const char *data, size_t count)
681{
682 struct i7core_pvt *pvt = mci->pvt_info;
683 unsigned long value;
684 int rc;
685
686 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300687 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300688
689 rc = strict_strtoul(data, 10, &value);
690 if (rc < 0)
691 return 0;
692
693 pvt->inject.eccmask = (u32) value;
694 return count;
695}
696
697static ssize_t i7core_inject_eccmask_show(struct mem_ctl_info *mci,
698 char *data)
699{
700 struct i7core_pvt *pvt = mci->pvt_info;
701 return sprintf(data, "0x%08x\n", pvt->inject.eccmask);
702}
703
704/*
705 * i7core_addrmatch
706 *
707 * The type of error (UE/CE) will depend on the inject.eccmask value:
708 * Any bits set to a 1 will flip the corresponding ECC bit
709 * Correctable errors can be injected by flipping 1 bit or the bits within
710 * a symbol pair (2 consecutive aligned 8-bit pairs - i.e. 7:0 and 15:8 or
711 * 23:16 and 31:24). Flipping bits in two symbol pairs will cause an
712 * uncorrectable error to be injected.
713 */
714static ssize_t i7core_inject_addrmatch_store(struct mem_ctl_info *mci,
715 const char *data, size_t count)
716{
717 struct i7core_pvt *pvt = mci->pvt_info;
718 char *cmd, *val;
719 long value;
720 int rc;
721
722 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300723 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300724
725 do {
726 cmd = strsep((char **) &data, ":");
727 if (!cmd)
728 break;
729 val = strsep((char **) &data, " \n\t");
730 if (!val)
731 return cmd - data;
732
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300733 if (!strcasecmp(val, "any"))
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300734 value = -1;
735 else {
736 rc = strict_strtol(val, 10, &value);
737 if ((rc < 0) || (value < 0))
738 return cmd - data;
739 }
740
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300741 if (!strcasecmp(cmd, "channel")) {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300742 if (value < 3)
743 pvt->inject.channel = value;
744 else
745 return cmd - data;
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300746 } else if (!strcasecmp(cmd, "dimm")) {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300747 if (value < 4)
748 pvt->inject.dimm = value;
749 else
750 return cmd - data;
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300751 } else if (!strcasecmp(cmd, "rank")) {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300752 if (value < 4)
753 pvt->inject.rank = value;
754 else
755 return cmd - data;
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300756 } else if (!strcasecmp(cmd, "bank")) {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300757 if (value < 4)
758 pvt->inject.bank = value;
759 else
760 return cmd - data;
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300761 } else if (!strcasecmp(cmd, "page")) {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300762 if (value <= 0xffff)
763 pvt->inject.page = value;
764 else
765 return cmd - data;
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300766 } else if (!strcasecmp(cmd, "col") ||
767 !strcasecmp(cmd, "column")) {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300768 if (value <= 0x3fff)
769 pvt->inject.col = value;
770 else
771 return cmd - data;
772 }
773 } while (1);
774
775 return count;
776}
777
778static ssize_t i7core_inject_addrmatch_show(struct mem_ctl_info *mci,
779 char *data)
780{
781 struct i7core_pvt *pvt = mci->pvt_info;
782 char channel[4], dimm[4], bank[4], rank[4], page[7], col[7];
783
784 if (pvt->inject.channel < 0)
785 sprintf(channel, "any");
786 else
787 sprintf(channel, "%d", pvt->inject.channel);
788 if (pvt->inject.dimm < 0)
789 sprintf(dimm, "any");
790 else
791 sprintf(dimm, "%d", pvt->inject.dimm);
792 if (pvt->inject.bank < 0)
793 sprintf(bank, "any");
794 else
795 sprintf(bank, "%d", pvt->inject.bank);
796 if (pvt->inject.rank < 0)
797 sprintf(rank, "any");
798 else
799 sprintf(rank, "%d", pvt->inject.rank);
800 if (pvt->inject.page < 0)
801 sprintf(page, "any");
802 else
803 sprintf(page, "0x%04x", pvt->inject.page);
804 if (pvt->inject.col < 0)
805 sprintf(col, "any");
806 else
807 sprintf(col, "0x%04x", pvt->inject.col);
808
809 return sprintf(data, "channel: %s\ndimm: %s\nbank: %s\n"
810 "rank: %s\npage: %s\ncolumn: %s\n",
811 channel, dimm, bank, rank, page, col);
812}
813
814/*
815 * This routine prepares the Memory Controller for error injection.
816 * The error will be injected when some process tries to write to the
817 * memory that matches the given criteria.
818 * The criteria can be set in terms of a mask where dimm, rank, bank, page
819 * and col can be specified.
820 * A -1 value for any of the mask items will make the MCU to ignore
821 * that matching criteria for error injection.
822 *
823 * It should be noticed that the error will only happen after a write operation
824 * on a memory that matches the condition. if REPEAT_EN is not enabled at
825 * inject mask, then it will produce just one error. Otherwise, it will repeat
826 * until the injectmask would be cleaned.
827 *
828 * FIXME: This routine assumes that MAXNUMDIMMS value of MC_MAX_DOD
829 * is reliable enough to check if the MC is using the
830 * three channels. However, this is not clear at the datasheet.
831 */
832static ssize_t i7core_inject_enable_store(struct mem_ctl_info *mci,
833 const char *data, size_t count)
834{
835 struct i7core_pvt *pvt = mci->pvt_info;
836 u32 injectmask;
837 u64 mask = 0;
838 int rc;
839 long enable;
840
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300841 if (!pvt->pci_ch[pvt->inject.channel][0])
842 return 0;
843
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300844 rc = strict_strtoul(data, 10, &enable);
845 if ((rc < 0))
846 return 0;
847
848 if (enable) {
849 pvt->inject.enable = 1;
850 } else {
851 disable_inject(mci);
852 return count;
853 }
854
855 /* Sets pvt->inject.dimm mask */
856 if (pvt->inject.dimm < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300857 mask |= 1L << 41;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300858 else {
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300859 if (pvt->channel[pvt->inject.channel].dimms > 2)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300860 mask |= (pvt->inject.dimm & 0x3L) << 35;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300861 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300862 mask |= (pvt->inject.dimm & 0x1L) << 36;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300863 }
864
865 /* Sets pvt->inject.rank mask */
866 if (pvt->inject.rank < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300867 mask |= 1L << 40;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300868 else {
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300869 if (pvt->channel[pvt->inject.channel].dimms > 2)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300870 mask |= (pvt->inject.rank & 0x1L) << 34;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300871 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300872 mask |= (pvt->inject.rank & 0x3L) << 34;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300873 }
874
875 /* Sets pvt->inject.bank mask */
876 if (pvt->inject.bank < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300877 mask |= 1L << 39;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300878 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300879 mask |= (pvt->inject.bank & 0x15L) << 30;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300880
881 /* Sets pvt->inject.page mask */
882 if (pvt->inject.page < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300883 mask |= 1L << 38;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300884 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300885 mask |= (pvt->inject.page & 0xffffL) << 14;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300886
887 /* Sets pvt->inject.column mask */
888 if (pvt->inject.col < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300889 mask |= 1L << 37;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300890 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300891 mask |= (pvt->inject.col & 0x3fffL);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300892
Mauro Carvalho Chehabe9bd2e72009-07-09 22:14:35 -0300893 /* Unlock writes to registers */
894 pci_write_config_dword(pvt->pci_noncore, MC_CFG_CONTROL, 0x2);
895 msleep(100);
896
897 /* Zeroes error count registers */
898 pci_write_config_dword(pvt->pci_mcr[4], MC_TEST_ERR_RCV1, 0);
899 pci_write_config_dword(pvt->pci_mcr[4], MC_TEST_ERR_RCV0, 0);
900 pvt->ce_count_available = 0;
901
902
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300903#if USE_QWORD
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300904 pci_write_config_qword(pvt->pci_ch[pvt->inject.channel][0],
905 MC_CHANNEL_ADDR_MATCH, mask);
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300906#else
907 pci_write_config_dword(pvt->pci_ch[pvt->inject.channel][0],
908 MC_CHANNEL_ADDR_MATCH, mask);
909 pci_write_config_dword(pvt->pci_ch[pvt->inject.channel][0],
910 MC_CHANNEL_ADDR_MATCH + 4, mask >> 32L);
911#endif
912
913#if 1
914#if USE_QWORD
915 u64 rdmask;
916 pci_read_config_qword(pvt->pci_ch[pvt->inject.channel][0],
917 MC_CHANNEL_ADDR_MATCH, &rdmask);
918 debugf0("Inject addr match write 0x%016llx, read: 0x%016llx\n",
919 mask, rdmask);
920#else
921 u32 rdmask1, rdmask2;
922
923 pci_read_config_dword(pvt->pci_ch[pvt->inject.channel][0],
924 MC_CHANNEL_ADDR_MATCH, &rdmask1);
925 pci_read_config_dword(pvt->pci_ch[pvt->inject.channel][0],
926 MC_CHANNEL_ADDR_MATCH + 4, &rdmask2);
927
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -0300928 debugf0("Inject addr match write 0x%016llx, read: 0x%08x 0x%08x\n",
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300929 mask, rdmask1, rdmask2);
930#endif
931#endif
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300932
933 pci_write_config_dword(pvt->pci_ch[pvt->inject.channel][0],
934 MC_CHANNEL_ERROR_MASK, pvt->inject.eccmask);
935
936 /*
937 * bit 0: REPEAT_EN
938 * bits 1-2: MASK_HALF_CACHELINE
939 * bit 3: INJECT_ECC
940 * bit 4: INJECT_ADDR_PARITY
941 */
942
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300943 injectmask = (pvt->inject.type & 1) |
944 (pvt->inject.section & 0x3) << 1 |
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300945 (pvt->inject.type & 0x6) << (3 - 1);
946
947 pci_write_config_dword(pvt->pci_ch[pvt->inject.channel][0],
948 MC_CHANNEL_ERROR_MASK, injectmask);
949
Mauro Carvalho Chehabe9bd2e72009-07-09 22:14:35 -0300950#if 0
951 /* lock writes to registers */
952 pci_write_config_dword(pvt->pci_noncore, MC_CFG_CONTROL, 0);
953#endif
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300954 debugf0("Error inject addr match 0x%016llx, ecc 0x%08x,"
955 " inject 0x%08x\n",
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300956 mask, pvt->inject.eccmask, injectmask);
957
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300958
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300959 return count;
960}
961
962static ssize_t i7core_inject_enable_show(struct mem_ctl_info *mci,
963 char *data)
964{
965 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300966 u32 injectmask;
967
968 pci_read_config_dword(pvt->pci_ch[pvt->inject.channel][0],
969 MC_CHANNEL_ERROR_MASK, &injectmask);
970
971 debugf0("Inject error read: 0x%018x\n", injectmask);
972
973 if (injectmask & 0x0c)
974 pvt->inject.enable = 1;
975
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300976 return sprintf(data, "%d\n", pvt->inject.enable);
977}
978
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -0300979static ssize_t i7core_ce_regs_show(struct mem_ctl_info *mci, char *data)
980{
981 struct i7core_pvt *pvt = mci->pvt_info;
982
983 if (!pvt->ce_count_available)
984 return sprintf(data, "unavailable\n");
985
986 return sprintf(data, "dimm0: %lu\ndimm1: %lu\ndimm2: %lu\n",
987 pvt->ce_count[0],
988 pvt->ce_count[1],
989 pvt->ce_count[2]);
990}
991
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300992/*
993 * Sysfs struct
994 */
995static struct mcidev_sysfs_attribute i7core_inj_attrs[] = {
996
997 {
998 .attr = {
999 .name = "inject_section",
1000 .mode = (S_IRUGO | S_IWUSR)
1001 },
1002 .show = i7core_inject_section_show,
1003 .store = i7core_inject_section_store,
1004 }, {
1005 .attr = {
1006 .name = "inject_type",
1007 .mode = (S_IRUGO | S_IWUSR)
1008 },
1009 .show = i7core_inject_type_show,
1010 .store = i7core_inject_type_store,
1011 }, {
1012 .attr = {
1013 .name = "inject_eccmask",
1014 .mode = (S_IRUGO | S_IWUSR)
1015 },
1016 .show = i7core_inject_eccmask_show,
1017 .store = i7core_inject_eccmask_store,
1018 }, {
1019 .attr = {
1020 .name = "inject_addrmatch",
1021 .mode = (S_IRUGO | S_IWUSR)
1022 },
1023 .show = i7core_inject_addrmatch_show,
1024 .store = i7core_inject_addrmatch_store,
1025 }, {
1026 .attr = {
1027 .name = "inject_enable",
1028 .mode = (S_IRUGO | S_IWUSR)
1029 },
1030 .show = i7core_inject_enable_show,
1031 .store = i7core_inject_enable_store,
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001032 }, {
1033 .attr = {
1034 .name = "corrected_error_counts",
1035 .mode = (S_IRUGO | S_IWUSR)
1036 },
1037 .show = i7core_ce_regs_show,
1038 .store = NULL,
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001039 },
1040};
1041
1042/****************************************************************************
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001043 Device initialization routines: put/get, init/exit
1044 ****************************************************************************/
1045
1046/*
1047 * i7core_put_devices 'put' all the devices that we have
1048 * reserved via 'get'
1049 */
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001050static void i7core_put_devices(void)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001051{
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001052 int i;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001053
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001054 for (i = 0; i < N_DEVS; i++)
1055 pci_dev_put(pci_devs[i].pdev);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001056}
1057
1058/*
1059 * i7core_get_devices Find and perform 'get' operation on the MCH's
1060 * device/functions we want to reference for this driver
1061 *
1062 * Need to 'get' device 16 func 1 and func 2
1063 */
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001064static int i7core_get_devices(void)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001065{
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001066 int rc, i;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001067 struct pci_dev *pdev = NULL;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001068
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001069 for (i = 0; i < N_DEVS; i++) {
1070 pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
1071 pci_devs[i].dev_id, NULL);
Mauro Carvalho Chehabd1fd4fb2009-07-10 18:39:53 -03001072
1073 if (!pdev && !i) {
1074 pcibios_scan_specific_bus(254);
1075 pcibios_scan_specific_bus(255);
1076
1077 pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
1078 pci_devs[i].dev_id, NULL);
1079 }
1080
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001081 if (likely(pdev))
1082 pci_devs[i].pdev = pdev;
1083 else {
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001084 i7core_printk(KERN_ERR,
1085 "Device not found: PCI ID %04x:%04x "
1086 "(dev %d, func %d)\n",
1087 PCI_VENDOR_ID_INTEL, pci_devs[i].dev_id,
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001088 pci_devs[i].dev, pci_devs[i].func);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001089
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001090 /* Dev 3 function 2 only exists on chips with RDIMMs */
1091 if ((pci_devs[i].dev == 3) && (pci_devs[i].func == 2))
1092 continue;
1093
1094 /* End of list, leave */
1095 rc = -ENODEV;
1096 goto error;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001097 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001098
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001099 /* Sanity check */
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001100 if (unlikely(PCI_SLOT(pdev->devfn) != pci_devs[i].dev ||
1101 PCI_FUNC(pdev->devfn) != pci_devs[i].func)) {
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001102 i7core_printk(KERN_ERR,
1103 "Device PCI ID %04x:%04x "
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001104 "has fn %d.%d instead of fn %d.%d\n",
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001105 PCI_VENDOR_ID_INTEL, pci_devs[i].dev_id,
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001106 PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
1107 pci_devs[i].dev, pci_devs[i].func);
1108 rc = -EINVAL;
1109 goto error;
1110 }
1111
1112 /* Be sure that the device is enabled */
1113 rc = pci_enable_device(pdev);
1114 if (unlikely(rc < 0)) {
1115 i7core_printk(KERN_ERR,
1116 "Couldn't enable PCI ID %04x:%04x "
1117 "fn %d.%d\n",
1118 PCI_VENDOR_ID_INTEL, pci_devs[i].dev_id,
1119 PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
1120 goto error;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001121 }
1122
1123 i7core_printk(KERN_INFO,
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001124 "Registered device %0x:%0x fn %d.%d\n",
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001125 PCI_VENDOR_ID_INTEL, pci_devs[i].dev_id,
1126 PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001127 }
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001128
1129 return 0;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001130
1131error:
1132 i7core_put_devices();
1133 return -EINVAL;
1134}
1135
1136static int mci_bind_devs(struct mem_ctl_info *mci)
1137{
1138 struct i7core_pvt *pvt = mci->pvt_info;
1139 struct pci_dev *pdev;
1140 int i, func, slot;
1141
1142 for (i = 0; i < N_DEVS; i++) {
1143 pdev = pci_devs[i].pdev;
1144 if (!pdev)
1145 continue;
1146
1147 func = PCI_FUNC(pdev->devfn);
1148 slot = PCI_SLOT(pdev->devfn);
1149 if (slot == 3) {
1150 if (unlikely(func > MAX_MCR_FUNC))
1151 goto error;
1152 pvt->pci_mcr[func] = pdev;
1153 } else if (likely(slot >= 4 && slot < 4 + NUM_CHANS)) {
1154 if (unlikely(func > MAX_CHAN_FUNC))
1155 goto error;
1156 pvt->pci_ch[slot - 4][func] = pdev;
Mauro Carvalho Chehabe9bd2e72009-07-09 22:14:35 -03001157 } else if (!slot && !func)
1158 pvt->pci_noncore = pdev;
1159 else
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001160 goto error;
1161
1162 debugf0("Associated fn %d.%d, dev = %p\n",
1163 PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn), pdev);
1164 }
Mauro Carvalho Chehabe9bd2e72009-07-09 22:14:35 -03001165
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001166 return 0;
1167
1168error:
1169 i7core_printk(KERN_ERR, "Device %d, function %d "
1170 "is out of the expected range\n",
1171 slot, func);
1172 return -EINVAL;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001173}
1174
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001175/****************************************************************************
1176 Error check routines
1177 ****************************************************************************/
1178
1179/* This function is based on the device 3 function 4 registers as described on:
1180 * Intel Xeon Processor 5500 Series Datasheet Volume 2
1181 * http://www.intel.com/Assets/PDF/datasheet/321322.pdf
1182 * also available at:
1183 * http://www.arrownac.com/manufacturers/intel/s/nehalem/5500-datasheet-v2.pdf
1184 */
1185static void check_mc_test_err(struct mem_ctl_info *mci)
1186{
1187 struct i7core_pvt *pvt = mci->pvt_info;
1188 u32 rcv1, rcv0;
1189 int new0, new1, new2;
1190
1191 if (!pvt->pci_mcr[4]) {
1192 debugf0("%s MCR registers not found\n",__func__);
1193 return;
1194 }
1195
1196 /* Corrected error reads */
1197 pci_read_config_dword(pvt->pci_mcr[4], MC_TEST_ERR_RCV1, &rcv1);
1198 pci_read_config_dword(pvt->pci_mcr[4], MC_TEST_ERR_RCV0, &rcv0);
1199
1200 /* Store the new values */
1201 new2 = DIMM2_COR_ERR(rcv1);
1202 new1 = DIMM1_COR_ERR(rcv0);
1203 new0 = DIMM0_COR_ERR(rcv0);
1204
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001205#if 0
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001206 debugf2("%s CE rcv1=0x%08x rcv0=0x%08x, %d %d %d\n",
1207 (pvt->ce_count_available ? "UPDATE" : "READ"),
1208 rcv1, rcv0, new0, new1, new2);
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001209#endif
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001210
1211 /* Updates CE counters if it is not the first time here */
1212 if (pvt->ce_count_available) {
1213 /* Updates CE counters */
1214 int add0, add1, add2;
1215
1216 add2 = new2 - pvt->last_ce_count[2];
1217 add1 = new1 - pvt->last_ce_count[1];
1218 add0 = new0 - pvt->last_ce_count[0];
1219
1220 if (add2 < 0)
1221 add2 += 0x7fff;
1222 pvt->ce_count[2] += add2;
1223
1224 if (add1 < 0)
1225 add1 += 0x7fff;
1226 pvt->ce_count[1] += add1;
1227
1228 if (add0 < 0)
1229 add0 += 0x7fff;
1230 pvt->ce_count[0] += add0;
1231 } else
1232 pvt->ce_count_available = 1;
1233
1234 /* Store the new values */
1235 pvt->last_ce_count[2] = new2;
1236 pvt->last_ce_count[1] = new1;
1237 pvt->last_ce_count[0] = new0;
1238}
1239
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001240static void i7core_mce_output_error(struct mem_ctl_info *mci,
1241 struct mce *m)
1242{
1243 debugf0("CPU %d: Machine Check Exception: %16Lx"
1244 "Bank %d: %016Lx\n",
1245 m->cpu, m->mcgstatus, m->bank, m->status);
1246 if (m->ip) {
1247 debugf0("RIP%s %02x:<%016Lx>\n",
1248 !(m->mcgstatus & MCG_STATUS_EIPV) ? " !INEXACT!" : "",
1249 m->cs, m->ip);
1250 }
1251 printk(KERN_EMERG "TSC %llx ", m->tsc);
1252 if (m->addr)
1253 printk("ADDR %llx ", m->addr);
1254 if (m->misc)
1255 printk("MISC %llx ", m->misc);
1256
1257#if 0
1258 snprintf(msg, sizeof(msg),
1259 "%s (Branch=%d DRAM-Bank=%d Buffer ID = %d RDWR=%s "
1260 "RAS=%d CAS=%d %s Err=0x%lx (%s))",
1261 type, branch >> 1, bank, buf_id, rdwr_str(rdwr), ras, cas,
1262 type, allErrors, error_name[errnum]);
1263
1264 /* Call the helper to output message */
1265 edac_mc_handle_fbd_ue(mci, rank, channel, channel + 1, msg);
1266#endif
1267}
1268
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001269/*
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001270 * i7core_check_error Retrieve and process errors reported by the
1271 * hardware. Called by the Core module.
1272 */
1273static void i7core_check_error(struct mem_ctl_info *mci)
1274{
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001275 struct i7core_pvt *pvt = mci->pvt_info;
1276 int i;
1277 unsigned count = 0;
1278 struct mce *m = NULL;
1279 unsigned long flags;
1280
1281 debugf0(__FILE__ ": %s()\n", __func__);
1282
1283 /* Copy all mce errors into a temporary buffer */
1284 spin_lock_irqsave(&pvt->mce_lock, flags);
1285 if (pvt->mce_count) {
1286 m = kmalloc(sizeof(*m) * pvt->mce_count, GFP_ATOMIC);
1287 if (m) {
1288 count = pvt->mce_count;
1289 memcpy(m, &pvt->mce_entry, sizeof(*m) * count);
1290 }
1291 pvt->mce_count = 0;
1292 }
1293 spin_unlock_irqrestore(&pvt->mce_lock, flags);
1294
1295 /* proccess mcelog errors */
1296 for (i = 0; i < count; i++)
1297 i7core_mce_output_error(mci, &m[i]);
1298
1299 kfree(m);
1300
1301 /* check memory count errors */
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001302 check_mc_test_err(mci);
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001303}
1304
1305/*
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001306 * i7core_mce_check_error Replicates mcelog routine to get errors
1307 * This routine simply queues mcelog errors, and
1308 * return. The error itself should be handled later
1309 * by i7core_check_error.
1310 */
1311static int i7core_mce_check_error(void *priv, struct mce *mce)
1312{
1313 struct i7core_pvt *pvt = priv;
1314 unsigned long flags;
1315
1316 debugf0(__FILE__ ": %s()\n", __func__);
1317
1318 spin_lock_irqsave(&pvt->mce_lock, flags);
1319 if (pvt->mce_count < MCE_LOG_LEN) {
1320 memcpy(&pvt->mce_entry[pvt->mce_count], mce, sizeof(*mce));
1321 pvt->mce_count++;
1322 }
1323 spin_unlock_irqrestore(&pvt->mce_lock, flags);
1324
1325 /* Advice mcelog that the error were handled */
1326// return 1;
1327 return 0; // Let's duplicate the log
1328}
1329
1330/*
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001331 * i7core_probe Probe for ONE instance of device to see if it is
1332 * present.
1333 * return:
1334 * 0 for FOUND a device
1335 * < 0 for error code
1336 */
1337static int __devinit i7core_probe(struct pci_dev *pdev,
1338 const struct pci_device_id *id)
1339{
1340 struct mem_ctl_info *mci;
1341 struct i7core_pvt *pvt;
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -03001342 int num_channels;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001343 int num_csrows;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001344 int dev_idx = id->driver_data;
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001345 int rc;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001346
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001347 if (unlikely(dev_idx >= ARRAY_SIZE(i7core_devs)))
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001348 return -EINVAL;
1349
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001350 /* get the pci devices we want to reserve for our use */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001351 rc = i7core_get_devices();
1352 if (unlikely(rc < 0))
1353 return rc;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001354
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001355 /* Check the number of active and not disabled channels */
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -03001356 rc = i7core_get_active_channels(&num_channels, &num_csrows);
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001357 if (unlikely(rc < 0))
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001358 goto fail0;
1359
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001360 /* allocate a new MC control structure */
1361 mci = edac_mc_alloc(sizeof(*pvt), num_csrows, num_channels, 0);
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001362 if (unlikely(!mci)) {
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001363 rc = -ENOMEM;
1364 goto fail0;
1365 }
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001366
1367 debugf0("MC: " __FILE__ ": %s(): mci = %p\n", __func__, mci);
1368
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001369 mci->dev = &pdev->dev; /* record ptr to the generic device */
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001370
1371 pvt = mci->pvt_info;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001372 memset(pvt, 0, sizeof(*pvt));
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001373
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001374 mci->mc_idx = 0;
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001375 /*
1376 * FIXME: how to handle RDDR3 at MCI level? It is possible to have
1377 * Mixed RDDR3/UDDR3 with Nehalem, provided that they are on different
1378 * memory channels
1379 */
1380 mci->mtype_cap = MEM_FLAG_DDR3;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001381 mci->edac_ctl_cap = EDAC_FLAG_NONE;
1382 mci->edac_cap = EDAC_FLAG_NONE;
1383 mci->mod_name = "i7core_edac.c";
1384 mci->mod_ver = I7CORE_REVISION;
1385 mci->ctl_name = i7core_devs[dev_idx].ctl_name;
1386 mci->dev_name = pci_name(pdev);
1387 mci->ctl_page_to_phys = NULL;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001388 mci->mc_driver_sysfs_attributes = i7core_inj_attrs;
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001389 /* Set the function pointer to an actual operation function */
1390 mci->edac_check = i7core_check_error;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001391
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001392 /* Store pci devices at mci for faster access */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001393 rc = mci_bind_devs(mci);
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001394 if (unlikely(rc < 0))
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001395 goto fail1;
1396
1397 /* Get dimm basic config */
1398 get_dimm_config(mci);
1399
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001400 /* add this new MC control structure to EDAC's list of MCs */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001401 if (unlikely(edac_mc_add_mc(mci))) {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001402 debugf0("MC: " __FILE__
1403 ": %s(): failed edac_mc_add_mc()\n", __func__);
1404 /* FIXME: perhaps some code should go here that disables error
1405 * reporting if we just enabled it
1406 */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001407
1408 rc = -EINVAL;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001409 goto fail1;
1410 }
1411
1412 /* allocating generic PCI control info */
1413 i7core_pci = edac_pci_create_generic_ctl(&pdev->dev, EDAC_MOD_STR);
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001414 if (unlikely(!i7core_pci)) {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001415 printk(KERN_WARNING
1416 "%s(): Unable to create PCI control\n",
1417 __func__);
1418 printk(KERN_WARNING
1419 "%s(): PCI error report via EDAC not setup\n",
1420 __func__);
1421 }
1422
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001423 /* Default error mask is any memory */
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001424 pvt->inject.channel = 0;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001425 pvt->inject.dimm = -1;
1426 pvt->inject.rank = -1;
1427 pvt->inject.bank = -1;
1428 pvt->inject.page = -1;
1429 pvt->inject.col = -1;
1430
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001431 /* Registers on edac_mce in order to receive memory errors */
1432 pvt->edac_mce.priv = pvt;
1433 pvt->edac_mce.check_error = i7core_mce_check_error;
1434 spin_lock_init(&pvt->mce_lock);
1435
1436 rc = edac_mce_register(&pvt->edac_mce);
1437 if (unlikely (rc < 0)) {
1438 debugf0("MC: " __FILE__
1439 ": %s(): failed edac_mce_register()\n", __func__);
1440 goto fail1;
1441 }
1442
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001443 i7core_printk(KERN_INFO, "Driver loaded.\n");
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001444
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001445 return 0;
1446
1447fail1:
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001448 edac_mc_free(mci);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001449
1450fail0:
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001451 i7core_put_devices();
1452 return rc;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001453}
1454
1455/*
1456 * i7core_remove destructor for one instance of device
1457 *
1458 */
1459static void __devexit i7core_remove(struct pci_dev *pdev)
1460{
1461 struct mem_ctl_info *mci;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001462 struct i7core_pvt *pvt;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001463
1464 debugf0(__FILE__ ": %s()\n", __func__);
1465
1466 if (i7core_pci)
1467 edac_pci_release_generic_ctl(i7core_pci);
1468
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001469
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001470 mci = edac_mc_del_mc(&pdev->dev);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001471 if (!mci)
1472 return;
1473
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001474 /* Unregisters on edac_mce in order to receive memory errors */
1475 pvt = mci->pvt_info;
1476 edac_mce_unregister(&pvt->edac_mce);
1477
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001478 /* retrieve references to resources, and free those resources */
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001479 i7core_put_devices();
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001480
1481 edac_mc_free(mci);
1482}
1483
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001484MODULE_DEVICE_TABLE(pci, i7core_pci_tbl);
1485
1486/*
1487 * i7core_driver pci_driver structure for this module
1488 *
1489 */
1490static struct pci_driver i7core_driver = {
1491 .name = "i7core_edac",
1492 .probe = i7core_probe,
1493 .remove = __devexit_p(i7core_remove),
1494 .id_table = i7core_pci_tbl,
1495};
1496
1497/*
1498 * i7core_init Module entry function
1499 * Try to initialize this module for its devices
1500 */
1501static int __init i7core_init(void)
1502{
1503 int pci_rc;
1504
1505 debugf2("MC: " __FILE__ ": %s()\n", __func__);
1506
1507 /* Ensure that the OPSTATE is set correctly for POLL or NMI */
1508 opstate_init();
1509
1510 pci_rc = pci_register_driver(&i7core_driver);
1511
1512 return (pci_rc < 0) ? pci_rc : 0;
1513}
1514
1515/*
1516 * i7core_exit() Module exit function
1517 * Unregister the driver
1518 */
1519static void __exit i7core_exit(void)
1520{
1521 debugf2("MC: " __FILE__ ": %s()\n", __func__);
1522 pci_unregister_driver(&i7core_driver);
1523}
1524
1525module_init(i7core_init);
1526module_exit(i7core_exit);
1527
1528MODULE_LICENSE("GPL");
1529MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
1530MODULE_AUTHOR("Red Hat Inc. (http://www.redhat.com)");
1531MODULE_DESCRIPTION("MC Driver for Intel i7 Core memory controllers - "
1532 I7CORE_REVISION);
1533
1534module_param(edac_op_state, int, 0444);
1535MODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI");