blob: 26cd5c924d56927446da79fcc147438f46d0c005 [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[] = {
224 /* Memory controller */
225 { PCI_DESCR(3, 0, PCI_DEVICE_ID_INTEL_I7_MCR) },
226 { PCI_DESCR(3, 1, PCI_DEVICE_ID_INTEL_I7_MC_TAD) },
227 { PCI_DESCR(3, 2, PCI_DEVICE_ID_INTEL_I7_MC_RAS) }, /* if RDIMM is supported */
228 { PCI_DESCR(3, 4, PCI_DEVICE_ID_INTEL_I7_MC_TEST) },
229
Mauro Carvalho Chehabe9bd2e72009-07-09 22:14:35 -0300230 /* Generic Non-core registers */
231 { PCI_DESCR(0, 0, PCI_DEVICE_ID_INTEL_I7_NOCORE) },
232
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300233 /* 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 = {
258 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_I7_MCR)},
259 {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 Chehabef708b52009-06-22 22:48:30 -03001072 if (likely(pdev))
1073 pci_devs[i].pdev = pdev;
1074 else {
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001075 i7core_printk(KERN_ERR,
1076 "Device not found: PCI ID %04x:%04x "
1077 "(dev %d, func %d)\n",
1078 PCI_VENDOR_ID_INTEL, pci_devs[i].dev_id,
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001079 pci_devs[i].dev, pci_devs[i].func);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001080
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001081 /* Dev 3 function 2 only exists on chips with RDIMMs */
1082 if ((pci_devs[i].dev == 3) && (pci_devs[i].func == 2))
1083 continue;
1084
1085 /* End of list, leave */
1086 rc = -ENODEV;
1087 goto error;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001088 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001089
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001090 /* Sanity check */
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001091 if (unlikely(PCI_SLOT(pdev->devfn) != pci_devs[i].dev ||
1092 PCI_FUNC(pdev->devfn) != pci_devs[i].func)) {
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001093 i7core_printk(KERN_ERR,
1094 "Device PCI ID %04x:%04x "
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001095 "has fn %d.%d instead of fn %d.%d\n",
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001096 PCI_VENDOR_ID_INTEL, pci_devs[i].dev_id,
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001097 PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
1098 pci_devs[i].dev, pci_devs[i].func);
1099 rc = -EINVAL;
1100 goto error;
1101 }
1102
1103 /* Be sure that the device is enabled */
1104 rc = pci_enable_device(pdev);
1105 if (unlikely(rc < 0)) {
1106 i7core_printk(KERN_ERR,
1107 "Couldn't enable PCI ID %04x:%04x "
1108 "fn %d.%d\n",
1109 PCI_VENDOR_ID_INTEL, pci_devs[i].dev_id,
1110 PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
1111 goto error;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001112 }
1113
1114 i7core_printk(KERN_INFO,
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001115 "Registered device %0x:%0x fn %d.%d\n",
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001116 PCI_VENDOR_ID_INTEL, pci_devs[i].dev_id,
1117 PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001118 }
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001119
1120 return 0;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001121
1122error:
1123 i7core_put_devices();
1124 return -EINVAL;
1125}
1126
1127static int mci_bind_devs(struct mem_ctl_info *mci)
1128{
1129 struct i7core_pvt *pvt = mci->pvt_info;
1130 struct pci_dev *pdev;
1131 int i, func, slot;
1132
1133 for (i = 0; i < N_DEVS; i++) {
1134 pdev = pci_devs[i].pdev;
1135 if (!pdev)
1136 continue;
1137
1138 func = PCI_FUNC(pdev->devfn);
1139 slot = PCI_SLOT(pdev->devfn);
1140 if (slot == 3) {
1141 if (unlikely(func > MAX_MCR_FUNC))
1142 goto error;
1143 pvt->pci_mcr[func] = pdev;
1144 } else if (likely(slot >= 4 && slot < 4 + NUM_CHANS)) {
1145 if (unlikely(func > MAX_CHAN_FUNC))
1146 goto error;
1147 pvt->pci_ch[slot - 4][func] = pdev;
Mauro Carvalho Chehabe9bd2e72009-07-09 22:14:35 -03001148 } else if (!slot && !func)
1149 pvt->pci_noncore = pdev;
1150 else
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001151 goto error;
1152
1153 debugf0("Associated fn %d.%d, dev = %p\n",
1154 PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn), pdev);
1155 }
Mauro Carvalho Chehabe9bd2e72009-07-09 22:14:35 -03001156
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001157 return 0;
1158
1159error:
1160 i7core_printk(KERN_ERR, "Device %d, function %d "
1161 "is out of the expected range\n",
1162 slot, func);
1163 return -EINVAL;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001164}
1165
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001166/****************************************************************************
1167 Error check routines
1168 ****************************************************************************/
1169
1170/* This function is based on the device 3 function 4 registers as described on:
1171 * Intel Xeon Processor 5500 Series Datasheet Volume 2
1172 * http://www.intel.com/Assets/PDF/datasheet/321322.pdf
1173 * also available at:
1174 * http://www.arrownac.com/manufacturers/intel/s/nehalem/5500-datasheet-v2.pdf
1175 */
1176static void check_mc_test_err(struct mem_ctl_info *mci)
1177{
1178 struct i7core_pvt *pvt = mci->pvt_info;
1179 u32 rcv1, rcv0;
1180 int new0, new1, new2;
1181
1182 if (!pvt->pci_mcr[4]) {
1183 debugf0("%s MCR registers not found\n",__func__);
1184 return;
1185 }
1186
1187 /* Corrected error reads */
1188 pci_read_config_dword(pvt->pci_mcr[4], MC_TEST_ERR_RCV1, &rcv1);
1189 pci_read_config_dword(pvt->pci_mcr[4], MC_TEST_ERR_RCV0, &rcv0);
1190
1191 /* Store the new values */
1192 new2 = DIMM2_COR_ERR(rcv1);
1193 new1 = DIMM1_COR_ERR(rcv0);
1194 new0 = DIMM0_COR_ERR(rcv0);
1195
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001196#if 0
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001197 debugf2("%s CE rcv1=0x%08x rcv0=0x%08x, %d %d %d\n",
1198 (pvt->ce_count_available ? "UPDATE" : "READ"),
1199 rcv1, rcv0, new0, new1, new2);
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001200#endif
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001201
1202 /* Updates CE counters if it is not the first time here */
1203 if (pvt->ce_count_available) {
1204 /* Updates CE counters */
1205 int add0, add1, add2;
1206
1207 add2 = new2 - pvt->last_ce_count[2];
1208 add1 = new1 - pvt->last_ce_count[1];
1209 add0 = new0 - pvt->last_ce_count[0];
1210
1211 if (add2 < 0)
1212 add2 += 0x7fff;
1213 pvt->ce_count[2] += add2;
1214
1215 if (add1 < 0)
1216 add1 += 0x7fff;
1217 pvt->ce_count[1] += add1;
1218
1219 if (add0 < 0)
1220 add0 += 0x7fff;
1221 pvt->ce_count[0] += add0;
1222 } else
1223 pvt->ce_count_available = 1;
1224
1225 /* Store the new values */
1226 pvt->last_ce_count[2] = new2;
1227 pvt->last_ce_count[1] = new1;
1228 pvt->last_ce_count[0] = new0;
1229}
1230
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001231static void i7core_mce_output_error(struct mem_ctl_info *mci,
1232 struct mce *m)
1233{
1234 debugf0("CPU %d: Machine Check Exception: %16Lx"
1235 "Bank %d: %016Lx\n",
1236 m->cpu, m->mcgstatus, m->bank, m->status);
1237 if (m->ip) {
1238 debugf0("RIP%s %02x:<%016Lx>\n",
1239 !(m->mcgstatus & MCG_STATUS_EIPV) ? " !INEXACT!" : "",
1240 m->cs, m->ip);
1241 }
1242 printk(KERN_EMERG "TSC %llx ", m->tsc);
1243 if (m->addr)
1244 printk("ADDR %llx ", m->addr);
1245 if (m->misc)
1246 printk("MISC %llx ", m->misc);
1247
1248#if 0
1249 snprintf(msg, sizeof(msg),
1250 "%s (Branch=%d DRAM-Bank=%d Buffer ID = %d RDWR=%s "
1251 "RAS=%d CAS=%d %s Err=0x%lx (%s))",
1252 type, branch >> 1, bank, buf_id, rdwr_str(rdwr), ras, cas,
1253 type, allErrors, error_name[errnum]);
1254
1255 /* Call the helper to output message */
1256 edac_mc_handle_fbd_ue(mci, rank, channel, channel + 1, msg);
1257#endif
1258}
1259
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001260/*
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001261 * i7core_check_error Retrieve and process errors reported by the
1262 * hardware. Called by the Core module.
1263 */
1264static void i7core_check_error(struct mem_ctl_info *mci)
1265{
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001266 struct i7core_pvt *pvt = mci->pvt_info;
1267 int i;
1268 unsigned count = 0;
1269 struct mce *m = NULL;
1270 unsigned long flags;
1271
1272 debugf0(__FILE__ ": %s()\n", __func__);
1273
1274 /* Copy all mce errors into a temporary buffer */
1275 spin_lock_irqsave(&pvt->mce_lock, flags);
1276 if (pvt->mce_count) {
1277 m = kmalloc(sizeof(*m) * pvt->mce_count, GFP_ATOMIC);
1278 if (m) {
1279 count = pvt->mce_count;
1280 memcpy(m, &pvt->mce_entry, sizeof(*m) * count);
1281 }
1282 pvt->mce_count = 0;
1283 }
1284 spin_unlock_irqrestore(&pvt->mce_lock, flags);
1285
1286 /* proccess mcelog errors */
1287 for (i = 0; i < count; i++)
1288 i7core_mce_output_error(mci, &m[i]);
1289
1290 kfree(m);
1291
1292 /* check memory count errors */
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001293 check_mc_test_err(mci);
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001294}
1295
1296/*
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001297 * i7core_mce_check_error Replicates mcelog routine to get errors
1298 * This routine simply queues mcelog errors, and
1299 * return. The error itself should be handled later
1300 * by i7core_check_error.
1301 */
1302static int i7core_mce_check_error(void *priv, struct mce *mce)
1303{
1304 struct i7core_pvt *pvt = priv;
1305 unsigned long flags;
1306
1307 debugf0(__FILE__ ": %s()\n", __func__);
1308
1309 spin_lock_irqsave(&pvt->mce_lock, flags);
1310 if (pvt->mce_count < MCE_LOG_LEN) {
1311 memcpy(&pvt->mce_entry[pvt->mce_count], mce, sizeof(*mce));
1312 pvt->mce_count++;
1313 }
1314 spin_unlock_irqrestore(&pvt->mce_lock, flags);
1315
1316 /* Advice mcelog that the error were handled */
1317// return 1;
1318 return 0; // Let's duplicate the log
1319}
1320
1321/*
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001322 * i7core_probe Probe for ONE instance of device to see if it is
1323 * present.
1324 * return:
1325 * 0 for FOUND a device
1326 * < 0 for error code
1327 */
1328static int __devinit i7core_probe(struct pci_dev *pdev,
1329 const struct pci_device_id *id)
1330{
1331 struct mem_ctl_info *mci;
1332 struct i7core_pvt *pvt;
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -03001333 int num_channels;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001334 int num_csrows;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001335 int dev_idx = id->driver_data;
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001336 int rc;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001337
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001338 if (unlikely(dev_idx >= ARRAY_SIZE(i7core_devs)))
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001339 return -EINVAL;
1340
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001341 /* get the pci devices we want to reserve for our use */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001342 rc = i7core_get_devices();
1343 if (unlikely(rc < 0))
1344 return rc;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001345
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001346 /* Check the number of active and not disabled channels */
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -03001347 rc = i7core_get_active_channels(&num_channels, &num_csrows);
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001348 if (unlikely(rc < 0))
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001349 goto fail0;
1350
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001351 /* allocate a new MC control structure */
1352 mci = edac_mc_alloc(sizeof(*pvt), num_csrows, num_channels, 0);
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001353 if (unlikely(!mci)) {
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001354 rc = -ENOMEM;
1355 goto fail0;
1356 }
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001357
1358 debugf0("MC: " __FILE__ ": %s(): mci = %p\n", __func__, mci);
1359
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001360 mci->dev = &pdev->dev; /* record ptr to the generic device */
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001361
1362 pvt = mci->pvt_info;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001363 memset(pvt, 0, sizeof(*pvt));
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001364
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001365 mci->mc_idx = 0;
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001366 /*
1367 * FIXME: how to handle RDDR3 at MCI level? It is possible to have
1368 * Mixed RDDR3/UDDR3 with Nehalem, provided that they are on different
1369 * memory channels
1370 */
1371 mci->mtype_cap = MEM_FLAG_DDR3;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001372 mci->edac_ctl_cap = EDAC_FLAG_NONE;
1373 mci->edac_cap = EDAC_FLAG_NONE;
1374 mci->mod_name = "i7core_edac.c";
1375 mci->mod_ver = I7CORE_REVISION;
1376 mci->ctl_name = i7core_devs[dev_idx].ctl_name;
1377 mci->dev_name = pci_name(pdev);
1378 mci->ctl_page_to_phys = NULL;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001379 mci->mc_driver_sysfs_attributes = i7core_inj_attrs;
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001380 /* Set the function pointer to an actual operation function */
1381 mci->edac_check = i7core_check_error;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001382
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001383 /* Store pci devices at mci for faster access */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001384 rc = mci_bind_devs(mci);
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001385 if (unlikely(rc < 0))
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001386 goto fail1;
1387
1388 /* Get dimm basic config */
1389 get_dimm_config(mci);
1390
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001391 /* add this new MC control structure to EDAC's list of MCs */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001392 if (unlikely(edac_mc_add_mc(mci))) {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001393 debugf0("MC: " __FILE__
1394 ": %s(): failed edac_mc_add_mc()\n", __func__);
1395 /* FIXME: perhaps some code should go here that disables error
1396 * reporting if we just enabled it
1397 */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001398
1399 rc = -EINVAL;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001400 goto fail1;
1401 }
1402
1403 /* allocating generic PCI control info */
1404 i7core_pci = edac_pci_create_generic_ctl(&pdev->dev, EDAC_MOD_STR);
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001405 if (unlikely(!i7core_pci)) {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001406 printk(KERN_WARNING
1407 "%s(): Unable to create PCI control\n",
1408 __func__);
1409 printk(KERN_WARNING
1410 "%s(): PCI error report via EDAC not setup\n",
1411 __func__);
1412 }
1413
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001414 /* Default error mask is any memory */
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001415 pvt->inject.channel = 0;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001416 pvt->inject.dimm = -1;
1417 pvt->inject.rank = -1;
1418 pvt->inject.bank = -1;
1419 pvt->inject.page = -1;
1420 pvt->inject.col = -1;
1421
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001422 /* Registers on edac_mce in order to receive memory errors */
1423 pvt->edac_mce.priv = pvt;
1424 pvt->edac_mce.check_error = i7core_mce_check_error;
1425 spin_lock_init(&pvt->mce_lock);
1426
1427 rc = edac_mce_register(&pvt->edac_mce);
1428 if (unlikely (rc < 0)) {
1429 debugf0("MC: " __FILE__
1430 ": %s(): failed edac_mce_register()\n", __func__);
1431 goto fail1;
1432 }
1433
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001434 i7core_printk(KERN_INFO, "Driver loaded.\n");
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001435
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001436 return 0;
1437
1438fail1:
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001439 edac_mc_free(mci);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001440
1441fail0:
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001442 i7core_put_devices();
1443 return rc;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001444}
1445
1446/*
1447 * i7core_remove destructor for one instance of device
1448 *
1449 */
1450static void __devexit i7core_remove(struct pci_dev *pdev)
1451{
1452 struct mem_ctl_info *mci;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001453 struct i7core_pvt *pvt;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001454
1455 debugf0(__FILE__ ": %s()\n", __func__);
1456
1457 if (i7core_pci)
1458 edac_pci_release_generic_ctl(i7core_pci);
1459
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001460
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001461 mci = edac_mc_del_mc(&pdev->dev);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001462 if (!mci)
1463 return;
1464
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001465 /* Unregisters on edac_mce in order to receive memory errors */
1466 pvt = mci->pvt_info;
1467 edac_mce_unregister(&pvt->edac_mce);
1468
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001469 /* retrieve references to resources, and free those resources */
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001470 i7core_put_devices();
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001471
1472 edac_mc_free(mci);
1473}
1474
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001475MODULE_DEVICE_TABLE(pci, i7core_pci_tbl);
1476
1477/*
1478 * i7core_driver pci_driver structure for this module
1479 *
1480 */
1481static struct pci_driver i7core_driver = {
1482 .name = "i7core_edac",
1483 .probe = i7core_probe,
1484 .remove = __devexit_p(i7core_remove),
1485 .id_table = i7core_pci_tbl,
1486};
1487
1488/*
1489 * i7core_init Module entry function
1490 * Try to initialize this module for its devices
1491 */
1492static int __init i7core_init(void)
1493{
1494 int pci_rc;
1495
1496 debugf2("MC: " __FILE__ ": %s()\n", __func__);
1497
1498 /* Ensure that the OPSTATE is set correctly for POLL or NMI */
1499 opstate_init();
1500
1501 pci_rc = pci_register_driver(&i7core_driver);
1502
1503 return (pci_rc < 0) ? pci_rc : 0;
1504}
1505
1506/*
1507 * i7core_exit() Module exit function
1508 * Unregister the driver
1509 */
1510static void __exit i7core_exit(void)
1511{
1512 debugf2("MC: " __FILE__ ": %s()\n", __func__);
1513 pci_unregister_driver(&i7core_driver);
1514}
1515
1516module_init(i7core_init);
1517module_exit(i7core_exit);
1518
1519MODULE_LICENSE("GPL");
1520MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
1521MODULE_AUTHOR("Red Hat Inc. (http://www.redhat.com)");
1522MODULE_DESCRIPTION("MC Driver for Intel i7 Core memory controllers - "
1523 I7CORE_REVISION);
1524
1525module_param(edac_op_state, int, 0444);
1526MODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI");