blob: 483cca2e543befa8ef207c0e0f31f63ccdc5e486 [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>
30
31#include "edac_core.h"
32
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -030033/* To use the new pci_[read/write]_config_qword instead of two dword */
34#define USE_QWORD 1
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030035
36/*
37 * Alter this version for the module when modifications are made
38 */
39#define I7CORE_REVISION " Ver: 1.0.0 " __DATE__
40#define EDAC_MOD_STR "i7core_edac"
41
42/* HACK: temporary, just to enable all logs, for now */
43#undef debugf0
44#define debugf0(fmt, arg...) edac_printk(KERN_INFO, "i7core", fmt, ##arg)
45
46/*
47 * Debug macros
48 */
49#define i7core_printk(level, fmt, arg...) \
50 edac_printk(level, "i7core", fmt, ##arg)
51
52#define i7core_mc_printk(mci, level, fmt, arg...) \
53 edac_mc_chipset_printk(mci, level, "i7core", fmt, ##arg)
54
55/*
56 * i7core Memory Controller Registers
57 */
58
59 /* OFFSETS for Device 3 Function 0 */
60
61#define MC_CONTROL 0x48
62#define MC_STATUS 0x4c
63#define MC_MAX_DOD 0x64
64
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -030065/*
66 * OFFSETS for Device 3 Function 4, as inicated on Xeon 5500 datasheet:
67 * http://www.arrownac.com/manufacturers/intel/s/nehalem/5500-datasheet-v2.pdf
68 */
69
70#define MC_TEST_ERR_RCV1 0x60
71 #define DIMM2_COR_ERR(r) ((r) & 0x7fff)
72
73#define MC_TEST_ERR_RCV0 0x64
74 #define DIMM1_COR_ERR(r) (((r) >> 16) & 0x7fff)
75 #define DIMM0_COR_ERR(r) ((r) & 0x7fff)
76
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030077 /* OFFSETS for Devices 4,5 and 6 Function 0 */
78
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -030079#define MC_CHANNEL_DIMM_INIT_PARAMS 0x58
80 #define THREE_DIMMS_PRESENT (1 << 24)
81 #define SINGLE_QUAD_RANK_PRESENT (1 << 23)
82 #define QUAD_RANK_PRESENT (1 << 22)
83 #define REGISTERED_DIMM (1 << 15)
84
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -030085#define MC_CHANNEL_MAPPER 0x60
86 #define RDLCH(r, ch) ((((r) >> (3 + (ch * 6))) & 0x07) - 1)
87 #define WRLCH(r, ch) ((((r) >> (ch * 6)) & 0x07) - 1)
88
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -030089#define MC_CHANNEL_RANK_PRESENT 0x7c
90 #define RANK_PRESENT_MASK 0xffff
91
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030092#define MC_CHANNEL_ADDR_MATCH 0xf0
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -030093#define MC_CHANNEL_ERROR_MASK 0xf8
94#define MC_CHANNEL_ERROR_INJECT 0xfc
95 #define INJECT_ADDR_PARITY 0x10
96 #define INJECT_ECC 0x08
97 #define MASK_CACHELINE 0x06
98 #define MASK_FULL_CACHELINE 0x06
99 #define MASK_MSB32_CACHELINE 0x04
100 #define MASK_LSB32_CACHELINE 0x02
101 #define NO_MASK_CACHELINE 0x00
102 #define REPEAT_EN 0x01
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300103
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300104 /* OFFSETS for Devices 4,5 and 6 Function 1 */
105#define MC_DOD_CH_DIMM0 0x48
106#define MC_DOD_CH_DIMM1 0x4c
107#define MC_DOD_CH_DIMM2 0x50
108 #define RANKOFFSET_MASK ((1 << 12) | (1 << 11) | (1 << 10))
109 #define RANKOFFSET(x) ((x & RANKOFFSET_MASK) >> 10)
110 #define DIMM_PRESENT_MASK (1 << 9)
111 #define DIMM_PRESENT(x) (((x) & DIMM_PRESENT_MASK) >> 9)
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300112 #define MC_DOD_NUMBANK_MASK ((1 << 8) | (1 << 7))
113 #define MC_DOD_NUMBANK(x) (((x) & MC_DOD_NUMBANK_MASK) >> 7)
114 #define MC_DOD_NUMRANK_MASK ((1 << 6) | (1 << 5))
115 #define MC_DOD_NUMRANK(x) (((x) & MC_DOD_NUMRANK_MASK) >> 5)
116 #define MC_DOD_NUMROW_MASK ((1 << 4) | (1 << 3))
117 #define MC_DOD_NUMROW(x) (((x) & MC_DOD_NUMROW_MASK) >> 3)
118 #define MC_DOD_NUMCOL_MASK 3
119 #define MC_DOD_NUMCOL(x) ((x) & MC_DOD_NUMCOL_MASK)
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300120
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300121#define MC_RANK_PRESENT 0x7c
122
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300123#define MC_SAG_CH_0 0x80
124#define MC_SAG_CH_1 0x84
125#define MC_SAG_CH_2 0x88
126#define MC_SAG_CH_3 0x8c
127#define MC_SAG_CH_4 0x90
128#define MC_SAG_CH_5 0x94
129#define MC_SAG_CH_6 0x98
130#define MC_SAG_CH_7 0x9c
131
132#define MC_RIR_LIMIT_CH_0 0x40
133#define MC_RIR_LIMIT_CH_1 0x44
134#define MC_RIR_LIMIT_CH_2 0x48
135#define MC_RIR_LIMIT_CH_3 0x4C
136#define MC_RIR_LIMIT_CH_4 0x50
137#define MC_RIR_LIMIT_CH_5 0x54
138#define MC_RIR_LIMIT_CH_6 0x58
139#define MC_RIR_LIMIT_CH_7 0x5C
140#define MC_RIR_LIMIT_MASK ((1 << 10) - 1)
141
142#define MC_RIR_WAY_CH 0x80
143 #define MC_RIR_WAY_OFFSET_MASK (((1 << 14) - 1) & ~0x7)
144 #define MC_RIR_WAY_RANK_MASK 0x7
145
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300146/*
147 * i7core structs
148 */
149
150#define NUM_CHANS 3
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -0300151#define MAX_DIMMS 3 /* Max DIMMS per channel */
152#define MAX_MCR_FUNC 4
153#define MAX_CHAN_FUNC 3
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300154
155struct i7core_info {
156 u32 mc_control;
157 u32 mc_status;
158 u32 max_dod;
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300159 u32 ch_map;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300160};
161
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300162
163struct i7core_inject {
164 int enable;
165
166 u32 section;
167 u32 type;
168 u32 eccmask;
169
170 /* Error address mask */
171 int channel, dimm, rank, bank, page, col;
172};
173
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300174struct i7core_channel {
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -0300175 u32 ranks;
176 u32 dimms;
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300177};
178
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300179struct pci_id_descr {
180 int dev;
181 int func;
182 int dev_id;
183 struct pci_dev *pdev;
184};
185
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300186struct i7core_pvt {
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -0300187 struct pci_dev *pci_mcr[MAX_MCR_FUNC + 1];
188 struct pci_dev *pci_ch[NUM_CHANS][MAX_CHAN_FUNC + 1];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300189 struct i7core_info info;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300190 struct i7core_inject inject;
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300191 struct i7core_channel channel[NUM_CHANS];
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300192 int channels; /* Number of active channels */
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -0300193
194 int ce_count_available;
195 unsigned long ce_count[MAX_DIMMS]; /* ECC corrected errors counts per dimm */
196 int last_ce_count[MAX_DIMMS];
197
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300198};
199
200/* Device name and register DID (Device ID) */
201struct i7core_dev_info {
202 const char *ctl_name; /* name for this device */
203 u16 fsb_mapping_errors; /* DID for the branchmap,control */
204};
205
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300206#define PCI_DESCR(device, function, device_id) \
207 .dev = (device), \
208 .func = (function), \
209 .dev_id = (device_id)
210
211struct pci_id_descr pci_devs[] = {
212 /* Memory controller */
213 { PCI_DESCR(3, 0, PCI_DEVICE_ID_INTEL_I7_MCR) },
214 { PCI_DESCR(3, 1, PCI_DEVICE_ID_INTEL_I7_MC_TAD) },
215 { PCI_DESCR(3, 2, PCI_DEVICE_ID_INTEL_I7_MC_RAS) }, /* if RDIMM is supported */
216 { PCI_DESCR(3, 4, PCI_DEVICE_ID_INTEL_I7_MC_TEST) },
217
218 /* Channel 0 */
219 { PCI_DESCR(4, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH0_CTRL) },
220 { PCI_DESCR(4, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH0_ADDR) },
221 { PCI_DESCR(4, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH0_RANK) },
222 { PCI_DESCR(4, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH0_TC) },
223
224 /* Channel 1 */
225 { PCI_DESCR(5, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH1_CTRL) },
226 { PCI_DESCR(5, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH1_ADDR) },
227 { PCI_DESCR(5, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH1_RANK) },
228 { PCI_DESCR(5, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH1_TC) },
229
230 /* Channel 2 */
231 { PCI_DESCR(6, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH2_CTRL) },
232 { PCI_DESCR(6, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH2_ADDR) },
233 { PCI_DESCR(6, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH2_RANK) },
234 { PCI_DESCR(6, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH2_TC) },
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300235};
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300236#define N_DEVS ARRAY_SIZE(pci_devs)
237
238/*
239 * pci_device_id table for which devices we are looking for
240 * This should match the first device at pci_devs table
241 */
242static const struct pci_device_id i7core_pci_tbl[] __devinitdata = {
243 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_I7_MCR)},
244 {0,} /* 0 terminated list. */
245};
246
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300247
248/* Table of devices attributes supported by this driver */
249static const struct i7core_dev_info i7core_devs[] = {
250 {
251 .ctl_name = "i7 Core",
252 .fsb_mapping_errors = PCI_DEVICE_ID_INTEL_I7_MCR,
253 },
254};
255
256static struct edac_pci_ctl_info *i7core_pci;
257
258/****************************************************************************
259 Anciliary status routines
260 ****************************************************************************/
261
262 /* MC_CONTROL bits */
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300263#define CH_ACTIVE(pvt, ch) ((pvt)->info.mc_control & (1 << (8 + ch)))
264#define ECCx8(pvt) ((pvt)->info.mc_control & (1 << 1))
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300265
266 /* MC_STATUS bits */
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300267#define ECC_ENABLED(pvt) ((pvt)->info.mc_status & (1 << 3))
268#define CH_DISABLED(pvt, ch) ((pvt)->info.mc_status & (1 << ch))
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300269
270 /* MC_MAX_DOD read functions */
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300271static inline int numdimms(u32 dimms)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300272{
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300273 return (dimms & 0x3) + 1;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300274}
275
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300276static inline int numrank(u32 rank)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300277{
278 static int ranks[4] = { 1, 2, 4, -EINVAL };
279
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300280 return ranks[rank & 0x3];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300281}
282
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300283static inline int numbank(u32 bank)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300284{
285 static int banks[4] = { 4, 8, 16, -EINVAL };
286
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300287 return banks[bank & 0x3];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300288}
289
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300290static inline int numrow(u32 row)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300291{
292 static int rows[8] = {
293 1 << 12, 1 << 13, 1 << 14, 1 << 15,
294 1 << 16, -EINVAL, -EINVAL, -EINVAL,
295 };
296
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300297 return rows[row & 0x7];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300298}
299
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300300static inline int numcol(u32 col)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300301{
302 static int cols[8] = {
303 1 << 10, 1 << 11, 1 << 12, -EINVAL,
304 };
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300305 return cols[col & 0x3];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300306}
307
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300308
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300309/****************************************************************************
310 Memory check routines
311 ****************************************************************************/
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300312static int i7core_get_active_channels(int *channels)
313{
314 struct pci_dev *pdev = NULL;
315 int i;
316 u32 status, control;
317
318 *channels = 0;
319
320 for (i = 0; i < N_DEVS; i++) {
321 if (!pci_devs[i].pdev)
322 continue;
323
324 if (PCI_SLOT(pci_devs[i].pdev->devfn) == 3 &&
325 PCI_FUNC(pci_devs[i].pdev->devfn) == 0) {
326 pdev = pci_devs[i].pdev;
327 break;
328 }
329 }
330
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -0300331 if (!pdev) {
332 i7core_printk(KERN_ERR, "Couldn't find fn 3.0!!!\n");
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300333 return -ENODEV;
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -0300334 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300335
336 /* Device 3 function 0 reads */
337 pci_read_config_dword(pdev, MC_STATUS, &status);
338 pci_read_config_dword(pdev, MC_CONTROL, &control);
339
340 for (i = 0; i < NUM_CHANS; i++) {
341 /* Check if the channel is active */
342 if (!(control & (1 << (8 + i))))
343 continue;
344
345 /* Check if the channel is disabled */
346 if (status & (1 << i)) {
347 continue;
348 }
349
350 (*channels)++;
351 }
352
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300353 debugf0("Number of active channels: %d\n", *channels);
354
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300355 return 0;
356}
357
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300358static int get_dimm_config(struct mem_ctl_info *mci)
359{
360 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300361 struct csrow_info *csr;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300362 struct pci_dev *pdev;
Mauro Carvalho Chehab7dd69532009-06-22 22:48:30 -0300363 int i, j, csrow = 0;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300364 enum edac_type mode;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300365 enum mem_type mtype;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300366
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300367 /* Get data from the MC register, function 0 */
368 pdev = pvt->pci_mcr[0];
Mauro Carvalho Chehab7dd69532009-06-22 22:48:30 -0300369 if (!pdev)
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300370 return -ENODEV;
371
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300372 /* Device 3 function 0 reads */
Mauro Carvalho Chehab7dd69532009-06-22 22:48:30 -0300373 pci_read_config_dword(pdev, MC_CONTROL, &pvt->info.mc_control);
374 pci_read_config_dword(pdev, MC_STATUS, &pvt->info.mc_status);
375 pci_read_config_dword(pdev, MC_MAX_DOD, &pvt->info.max_dod);
376 pci_read_config_dword(pdev, MC_CHANNEL_MAPPER, &pvt->info.ch_map);
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300377
378 debugf0("MC control=0x%08x status=0x%08x dod=0x%08x map=0x%08x\n",
379 pvt->info.mc_control, pvt->info.mc_status,
380 pvt->info.max_dod, pvt->info.ch_map);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300381
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300382 if (ECC_ENABLED(pvt)) {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300383 debugf0("ECC enabled with x%d SDCC\n", ECCx8(pvt)?8:4);
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300384 if (ECCx8(pvt))
385 mode = EDAC_S8ECD8ED;
386 else
387 mode = EDAC_S4ECD4ED;
388 } else {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300389 debugf0("ECC disabled\n");
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300390 mode = EDAC_NONE;
391 }
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300392
393 /* FIXME: need to handle the error codes */
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300394 debugf0("DOD Max limits: DIMMS: %d, %d-ranked, %d-banked\n",
395 numdimms(pvt->info.max_dod),
396 numrank(pvt->info.max_dod >> 2),
397 numbank(pvt->info.max_dod >> 4));
398 debugf0("DOD Max rows x colums = 0x%x x 0x%x\n",
399 numrow(pvt->info.max_dod >> 6),
400 numcol(pvt->info.max_dod >> 9));
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300401
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300402 debugf0("Memory channel configuration:\n");
403
404 for (i = 0; i < NUM_CHANS; i++) {
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300405 u32 data, dimm_dod[3], value[8];
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300406
407 if (!CH_ACTIVE(pvt, i)) {
408 debugf0("Channel %i is not active\n", i);
409 continue;
410 }
411 if (CH_DISABLED(pvt, i)) {
412 debugf0("Channel %i is disabled\n", i);
413 continue;
414 }
415
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300416 /* Devices 4-6 function 0 */
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300417 pci_read_config_dword(pvt->pci_ch[i][0],
418 MC_CHANNEL_DIMM_INIT_PARAMS, &data);
419
420 pvt->channel[i].ranks = (data & QUAD_RANK_PRESENT)? 4 : 2;
421
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300422 if (data & REGISTERED_DIMM)
423 mtype = MEM_RDDR3;
424 else
425 mtype = MEM_DDR3;
426#if 0
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300427 if (data & THREE_DIMMS_PRESENT)
428 pvt->channel[i].dimms = 3;
429 else if (data & SINGLE_QUAD_RANK_PRESENT)
430 pvt->channel[i].dimms = 1;
431 else
432 pvt->channel[i].dimms = 2;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300433#endif
434
435 /* Devices 4-6 function 1 */
436 pci_read_config_dword(pvt->pci_ch[i][1],
437 MC_DOD_CH_DIMM0, &dimm_dod[0]);
438 pci_read_config_dword(pvt->pci_ch[i][1],
439 MC_DOD_CH_DIMM1, &dimm_dod[1]);
440 pci_read_config_dword(pvt->pci_ch[i][1],
441 MC_DOD_CH_DIMM2, &dimm_dod[2]);
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300442
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300443 debugf0("Ch%d phy rd%d, wr%d (0x%08x): "
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300444 "%d ranks, %cDIMMs\n",
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300445 i,
446 RDLCH(pvt->info.ch_map, i), WRLCH(pvt->info.ch_map, i),
447 data,
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300448 pvt->channel[i].ranks,
449 (data & REGISTERED_DIMM)? 'R' : 'U');
Mauro Carvalho Chehab7dd69532009-06-22 22:48:30 -0300450
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300451 for (j = 0; j < 3; j++) {
452 u32 banks, ranks, rows, cols;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300453
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300454 if (!DIMM_PRESENT(dimm_dod[j]))
455 continue;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300456
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300457 banks = numbank(MC_DOD_NUMBANK(dimm_dod[j]));
458 ranks = numrank(MC_DOD_NUMRANK(dimm_dod[j]));
459 rows = numrow(MC_DOD_NUMROW(dimm_dod[j]));
460 cols = numcol(MC_DOD_NUMCOL(dimm_dod[j]));
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300461
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300462 pvt->channel[i].dimms++;
463
464 debugf0("\tdimm %d offset: %x, numbank: %#x, "
465 "numrank: %#x, numrow: %#x, numcol: %#x\n",
466 j,
467 RANKOFFSET(dimm_dod[j]),
468 banks, ranks, rows, cols);
469
470 csr = &mci->csrows[csrow];
471 csr->first_page = 0;
472 csr->last_page = 0;
473 csr->page_mask = 0;
474 csr->nr_pages = 0;
475 csr->grain = 0;
476 csr->csrow_idx = csrow;
477
478 switch (banks) {
479 case 4:
480 csr->dtype = DEV_X4;
481 break;
482 case 8:
483 csr->dtype = DEV_X8;
484 break;
485 case 16:
486 csr->dtype = DEV_X16;
487 break;
488 default:
489 csr->dtype = DEV_UNKNOWN;
490 }
491
492 csr->edac_mode = mode;
493 csr->mtype = mtype;
494
495 csrow++;
496 }
497
498 pci_read_config_dword(pdev, MC_SAG_CH_0, &value[0]);
499 pci_read_config_dword(pdev, MC_SAG_CH_1, &value[1]);
500 pci_read_config_dword(pdev, MC_SAG_CH_2, &value[2]);
501 pci_read_config_dword(pdev, MC_SAG_CH_3, &value[3]);
502 pci_read_config_dword(pdev, MC_SAG_CH_4, &value[4]);
503 pci_read_config_dword(pdev, MC_SAG_CH_5, &value[5]);
504 pci_read_config_dword(pdev, MC_SAG_CH_6, &value[6]);
505 pci_read_config_dword(pdev, MC_SAG_CH_7, &value[7]);
506 printk("\t[%i] DIVBY3\tREMOVED\tOFFSET\n", i);
507 for (j = 0; j < 8; j++)
508 printk("\t\t%#x\t%#x\t%#x\n",
509 (value[j] >> 27) & 0x1,
510 (value[j] >> 24) & 0x7,
511 (value[j] && ((1 << 24) - 1)));
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300512 }
513
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300514 return 0;
515}
516
517/****************************************************************************
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300518 Error insertion routines
519 ****************************************************************************/
520
521/* The i7core has independent error injection features per channel.
522 However, to have a simpler code, we don't allow enabling error injection
523 on more than one channel.
524 Also, since a change at an inject parameter will be applied only at enable,
525 we're disabling error injection on all write calls to the sysfs nodes that
526 controls the error code injection.
527 */
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300528static int disable_inject(struct mem_ctl_info *mci)
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300529{
530 struct i7core_pvt *pvt = mci->pvt_info;
531
532 pvt->inject.enable = 0;
533
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300534 if (!pvt->pci_ch[pvt->inject.channel][0])
535 return -ENODEV;
536
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300537 pci_write_config_dword(pvt->pci_ch[pvt->inject.channel][0],
538 MC_CHANNEL_ERROR_MASK, 0);
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300539
540 return 0;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300541}
542
543/*
544 * i7core inject inject.section
545 *
546 * accept and store error injection inject.section value
547 * bit 0 - refers to the lower 32-byte half cacheline
548 * bit 1 - refers to the upper 32-byte half cacheline
549 */
550static ssize_t i7core_inject_section_store(struct mem_ctl_info *mci,
551 const char *data, size_t count)
552{
553 struct i7core_pvt *pvt = mci->pvt_info;
554 unsigned long value;
555 int rc;
556
557 if (pvt->inject.enable)
558 disable_inject(mci);
559
560 rc = strict_strtoul(data, 10, &value);
561 if ((rc < 0) || (value > 3))
562 return 0;
563
564 pvt->inject.section = (u32) value;
565 return count;
566}
567
568static ssize_t i7core_inject_section_show(struct mem_ctl_info *mci,
569 char *data)
570{
571 struct i7core_pvt *pvt = mci->pvt_info;
572 return sprintf(data, "0x%08x\n", pvt->inject.section);
573}
574
575/*
576 * i7core inject.type
577 *
578 * accept and store error injection inject.section value
579 * bit 0 - repeat enable - Enable error repetition
580 * bit 1 - inject ECC error
581 * bit 2 - inject parity error
582 */
583static ssize_t i7core_inject_type_store(struct mem_ctl_info *mci,
584 const char *data, size_t count)
585{
586 struct i7core_pvt *pvt = mci->pvt_info;
587 unsigned long value;
588 int rc;
589
590 if (pvt->inject.enable)
591 disable_inject(mci);
592
593 rc = strict_strtoul(data, 10, &value);
594 if ((rc < 0) || (value > 7))
595 return 0;
596
597 pvt->inject.type = (u32) value;
598 return count;
599}
600
601static ssize_t i7core_inject_type_show(struct mem_ctl_info *mci,
602 char *data)
603{
604 struct i7core_pvt *pvt = mci->pvt_info;
605 return sprintf(data, "0x%08x\n", pvt->inject.type);
606}
607
608/*
609 * i7core_inject_inject.eccmask_store
610 *
611 * The type of error (UE/CE) will depend on the inject.eccmask value:
612 * Any bits set to a 1 will flip the corresponding ECC bit
613 * Correctable errors can be injected by flipping 1 bit or the bits within
614 * a symbol pair (2 consecutive aligned 8-bit pairs - i.e. 7:0 and 15:8 or
615 * 23:16 and 31:24). Flipping bits in two symbol pairs will cause an
616 * uncorrectable error to be injected.
617 */
618static ssize_t i7core_inject_eccmask_store(struct mem_ctl_info *mci,
619 const char *data, size_t count)
620{
621 struct i7core_pvt *pvt = mci->pvt_info;
622 unsigned long value;
623 int rc;
624
625 if (pvt->inject.enable)
626 disable_inject(mci);
627
628 rc = strict_strtoul(data, 10, &value);
629 if (rc < 0)
630 return 0;
631
632 pvt->inject.eccmask = (u32) value;
633 return count;
634}
635
636static ssize_t i7core_inject_eccmask_show(struct mem_ctl_info *mci,
637 char *data)
638{
639 struct i7core_pvt *pvt = mci->pvt_info;
640 return sprintf(data, "0x%08x\n", pvt->inject.eccmask);
641}
642
643/*
644 * i7core_addrmatch
645 *
646 * The type of error (UE/CE) will depend on the inject.eccmask value:
647 * Any bits set to a 1 will flip the corresponding ECC bit
648 * Correctable errors can be injected by flipping 1 bit or the bits within
649 * a symbol pair (2 consecutive aligned 8-bit pairs - i.e. 7:0 and 15:8 or
650 * 23:16 and 31:24). Flipping bits in two symbol pairs will cause an
651 * uncorrectable error to be injected.
652 */
653static ssize_t i7core_inject_addrmatch_store(struct mem_ctl_info *mci,
654 const char *data, size_t count)
655{
656 struct i7core_pvt *pvt = mci->pvt_info;
657 char *cmd, *val;
658 long value;
659 int rc;
660
661 if (pvt->inject.enable)
662 disable_inject(mci);
663
664 do {
665 cmd = strsep((char **) &data, ":");
666 if (!cmd)
667 break;
668 val = strsep((char **) &data, " \n\t");
669 if (!val)
670 return cmd - data;
671
672 if (!strcasecmp(val,"any"))
673 value = -1;
674 else {
675 rc = strict_strtol(val, 10, &value);
676 if ((rc < 0) || (value < 0))
677 return cmd - data;
678 }
679
680 if (!strcasecmp(cmd,"channel")) {
681 if (value < 3)
682 pvt->inject.channel = value;
683 else
684 return cmd - data;
685 } else if (!strcasecmp(cmd,"dimm")) {
686 if (value < 4)
687 pvt->inject.dimm = value;
688 else
689 return cmd - data;
690 } else if (!strcasecmp(cmd,"rank")) {
691 if (value < 4)
692 pvt->inject.rank = value;
693 else
694 return cmd - data;
695 } else if (!strcasecmp(cmd,"bank")) {
696 if (value < 4)
697 pvt->inject.bank = value;
698 else
699 return cmd - data;
700 } else if (!strcasecmp(cmd,"page")) {
701 if (value <= 0xffff)
702 pvt->inject.page = value;
703 else
704 return cmd - data;
705 } else if (!strcasecmp(cmd,"col") ||
706 !strcasecmp(cmd,"column")) {
707 if (value <= 0x3fff)
708 pvt->inject.col = value;
709 else
710 return cmd - data;
711 }
712 } while (1);
713
714 return count;
715}
716
717static ssize_t i7core_inject_addrmatch_show(struct mem_ctl_info *mci,
718 char *data)
719{
720 struct i7core_pvt *pvt = mci->pvt_info;
721 char channel[4], dimm[4], bank[4], rank[4], page[7], col[7];
722
723 if (pvt->inject.channel < 0)
724 sprintf(channel, "any");
725 else
726 sprintf(channel, "%d", pvt->inject.channel);
727 if (pvt->inject.dimm < 0)
728 sprintf(dimm, "any");
729 else
730 sprintf(dimm, "%d", pvt->inject.dimm);
731 if (pvt->inject.bank < 0)
732 sprintf(bank, "any");
733 else
734 sprintf(bank, "%d", pvt->inject.bank);
735 if (pvt->inject.rank < 0)
736 sprintf(rank, "any");
737 else
738 sprintf(rank, "%d", pvt->inject.rank);
739 if (pvt->inject.page < 0)
740 sprintf(page, "any");
741 else
742 sprintf(page, "0x%04x", pvt->inject.page);
743 if (pvt->inject.col < 0)
744 sprintf(col, "any");
745 else
746 sprintf(col, "0x%04x", pvt->inject.col);
747
748 return sprintf(data, "channel: %s\ndimm: %s\nbank: %s\n"
749 "rank: %s\npage: %s\ncolumn: %s\n",
750 channel, dimm, bank, rank, page, col);
751}
752
753/*
754 * This routine prepares the Memory Controller for error injection.
755 * The error will be injected when some process tries to write to the
756 * memory that matches the given criteria.
757 * The criteria can be set in terms of a mask where dimm, rank, bank, page
758 * and col can be specified.
759 * A -1 value for any of the mask items will make the MCU to ignore
760 * that matching criteria for error injection.
761 *
762 * It should be noticed that the error will only happen after a write operation
763 * on a memory that matches the condition. if REPEAT_EN is not enabled at
764 * inject mask, then it will produce just one error. Otherwise, it will repeat
765 * until the injectmask would be cleaned.
766 *
767 * FIXME: This routine assumes that MAXNUMDIMMS value of MC_MAX_DOD
768 * is reliable enough to check if the MC is using the
769 * three channels. However, this is not clear at the datasheet.
770 */
771static ssize_t i7core_inject_enable_store(struct mem_ctl_info *mci,
772 const char *data, size_t count)
773{
774 struct i7core_pvt *pvt = mci->pvt_info;
775 u32 injectmask;
776 u64 mask = 0;
777 int rc;
778 long enable;
779
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300780 if (!pvt->pci_ch[pvt->inject.channel][0])
781 return 0;
782
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300783 rc = strict_strtoul(data, 10, &enable);
784 if ((rc < 0))
785 return 0;
786
787 if (enable) {
788 pvt->inject.enable = 1;
789 } else {
790 disable_inject(mci);
791 return count;
792 }
793
794 /* Sets pvt->inject.dimm mask */
795 if (pvt->inject.dimm < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300796 mask |= 1L << 41;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300797 else {
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300798 if (pvt->channel[pvt->inject.channel].dimms > 2)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300799 mask |= (pvt->inject.dimm & 0x3L) << 35;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300800 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300801 mask |= (pvt->inject.dimm & 0x1L) << 36;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300802 }
803
804 /* Sets pvt->inject.rank mask */
805 if (pvt->inject.rank < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300806 mask |= 1L << 40;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300807 else {
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300808 if (pvt->channel[pvt->inject.channel].dimms > 2)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300809 mask |= (pvt->inject.rank & 0x1L) << 34;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300810 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300811 mask |= (pvt->inject.rank & 0x3L) << 34;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300812 }
813
814 /* Sets pvt->inject.bank mask */
815 if (pvt->inject.bank < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300816 mask |= 1L << 39;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300817 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300818 mask |= (pvt->inject.bank & 0x15L) << 30;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300819
820 /* Sets pvt->inject.page mask */
821 if (pvt->inject.page < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300822 mask |= 1L << 38;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300823 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300824 mask |= (pvt->inject.page & 0xffffL) << 14;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300825
826 /* Sets pvt->inject.column mask */
827 if (pvt->inject.col < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300828 mask |= 1L << 37;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300829 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300830 mask |= (pvt->inject.col & 0x3fffL);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300831
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300832#if USE_QWORD
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300833 pci_write_config_qword(pvt->pci_ch[pvt->inject.channel][0],
834 MC_CHANNEL_ADDR_MATCH, mask);
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300835#else
836 pci_write_config_dword(pvt->pci_ch[pvt->inject.channel][0],
837 MC_CHANNEL_ADDR_MATCH, mask);
838 pci_write_config_dword(pvt->pci_ch[pvt->inject.channel][0],
839 MC_CHANNEL_ADDR_MATCH + 4, mask >> 32L);
840#endif
841
842#if 1
843#if USE_QWORD
844 u64 rdmask;
845 pci_read_config_qword(pvt->pci_ch[pvt->inject.channel][0],
846 MC_CHANNEL_ADDR_MATCH, &rdmask);
847 debugf0("Inject addr match write 0x%016llx, read: 0x%016llx\n",
848 mask, rdmask);
849#else
850 u32 rdmask1, rdmask2;
851
852 pci_read_config_dword(pvt->pci_ch[pvt->inject.channel][0],
853 MC_CHANNEL_ADDR_MATCH, &rdmask1);
854 pci_read_config_dword(pvt->pci_ch[pvt->inject.channel][0],
855 MC_CHANNEL_ADDR_MATCH + 4, &rdmask2);
856
857 debugf0("Inject addr match write 0x%016llx, read: 0x%08x%08x\n",
858 mask, rdmask1, rdmask2);
859#endif
860#endif
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300861
862 pci_write_config_dword(pvt->pci_ch[pvt->inject.channel][0],
863 MC_CHANNEL_ERROR_MASK, pvt->inject.eccmask);
864
865 /*
866 * bit 0: REPEAT_EN
867 * bits 1-2: MASK_HALF_CACHELINE
868 * bit 3: INJECT_ECC
869 * bit 4: INJECT_ADDR_PARITY
870 */
871
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300872 injectmask = (pvt->inject.type & 1) |
873 (pvt->inject.section & 0x3) << 1 |
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300874 (pvt->inject.type & 0x6) << (3 - 1);
875
876 pci_write_config_dword(pvt->pci_ch[pvt->inject.channel][0],
877 MC_CHANNEL_ERROR_MASK, injectmask);
878
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300879 debugf0("Error inject addr match 0x%016llx, ecc 0x%08x, inject 0x%08x\n",
880 mask, pvt->inject.eccmask, injectmask);
881
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300882
883
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300884 return count;
885}
886
887static ssize_t i7core_inject_enable_show(struct mem_ctl_info *mci,
888 char *data)
889{
890 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300891 u32 injectmask;
892
893 pci_read_config_dword(pvt->pci_ch[pvt->inject.channel][0],
894 MC_CHANNEL_ERROR_MASK, &injectmask);
895
896 debugf0("Inject error read: 0x%018x\n", injectmask);
897
898 if (injectmask & 0x0c)
899 pvt->inject.enable = 1;
900
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300901 return sprintf(data, "%d\n", pvt->inject.enable);
902}
903
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -0300904static ssize_t i7core_ce_regs_show(struct mem_ctl_info *mci, char *data)
905{
906 struct i7core_pvt *pvt = mci->pvt_info;
907
908 if (!pvt->ce_count_available)
909 return sprintf(data, "unavailable\n");
910
911 return sprintf(data, "dimm0: %lu\ndimm1: %lu\ndimm2: %lu\n",
912 pvt->ce_count[0],
913 pvt->ce_count[1],
914 pvt->ce_count[2]);
915}
916
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300917/*
918 * Sysfs struct
919 */
920static struct mcidev_sysfs_attribute i7core_inj_attrs[] = {
921
922 {
923 .attr = {
924 .name = "inject_section",
925 .mode = (S_IRUGO | S_IWUSR)
926 },
927 .show = i7core_inject_section_show,
928 .store = i7core_inject_section_store,
929 }, {
930 .attr = {
931 .name = "inject_type",
932 .mode = (S_IRUGO | S_IWUSR)
933 },
934 .show = i7core_inject_type_show,
935 .store = i7core_inject_type_store,
936 }, {
937 .attr = {
938 .name = "inject_eccmask",
939 .mode = (S_IRUGO | S_IWUSR)
940 },
941 .show = i7core_inject_eccmask_show,
942 .store = i7core_inject_eccmask_store,
943 }, {
944 .attr = {
945 .name = "inject_addrmatch",
946 .mode = (S_IRUGO | S_IWUSR)
947 },
948 .show = i7core_inject_addrmatch_show,
949 .store = i7core_inject_addrmatch_store,
950 }, {
951 .attr = {
952 .name = "inject_enable",
953 .mode = (S_IRUGO | S_IWUSR)
954 },
955 .show = i7core_inject_enable_show,
956 .store = i7core_inject_enable_store,
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -0300957 }, {
958 .attr = {
959 .name = "corrected_error_counts",
960 .mode = (S_IRUGO | S_IWUSR)
961 },
962 .show = i7core_ce_regs_show,
963 .store = NULL,
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300964 },
965};
966
967/****************************************************************************
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300968 Device initialization routines: put/get, init/exit
969 ****************************************************************************/
970
971/*
972 * i7core_put_devices 'put' all the devices that we have
973 * reserved via 'get'
974 */
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300975static void i7core_put_devices(void)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300976{
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300977 int i;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300978
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300979 for (i = 0; i < N_DEVS; i++)
980 pci_dev_put(pci_devs[i].pdev);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300981}
982
983/*
984 * i7core_get_devices Find and perform 'get' operation on the MCH's
985 * device/functions we want to reference for this driver
986 *
987 * Need to 'get' device 16 func 1 and func 2
988 */
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300989static int i7core_get_devices(void)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300990{
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300991 int rc, i;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300992 struct pci_dev *pdev = NULL;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300993
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300994 for (i = 0; i < N_DEVS; i++) {
995 pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
996 pci_devs[i].dev_id, NULL);
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300997 if (likely(pdev))
998 pci_devs[i].pdev = pdev;
999 else {
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001000 i7core_printk(KERN_ERR,
1001 "Device not found: PCI ID %04x:%04x "
1002 "(dev %d, func %d)\n",
1003 PCI_VENDOR_ID_INTEL, pci_devs[i].dev_id,
1004 pci_devs[i].dev,pci_devs[i].func);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001005
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001006 /* Dev 3 function 2 only exists on chips with RDIMMs */
1007 if ((pci_devs[i].dev == 3) && (pci_devs[i].func == 2))
1008 continue;
1009
1010 /* End of list, leave */
1011 rc = -ENODEV;
1012 goto error;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001013 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001014
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001015 /* Sanity check */
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001016 if (unlikely(PCI_SLOT(pdev->devfn) != pci_devs[i].dev ||
1017 PCI_FUNC(pdev->devfn) != pci_devs[i].func)) {
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001018 i7core_printk(KERN_ERR,
1019 "Device PCI ID %04x:%04x "
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001020 "has fn %d.%d instead of fn %d.%d\n",
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001021 PCI_VENDOR_ID_INTEL, pci_devs[i].dev_id,
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001022 PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
1023 pci_devs[i].dev, pci_devs[i].func);
1024 rc = -EINVAL;
1025 goto error;
1026 }
1027
1028 /* Be sure that the device is enabled */
1029 rc = pci_enable_device(pdev);
1030 if (unlikely(rc < 0)) {
1031 i7core_printk(KERN_ERR,
1032 "Couldn't enable PCI ID %04x:%04x "
1033 "fn %d.%d\n",
1034 PCI_VENDOR_ID_INTEL, pci_devs[i].dev_id,
1035 PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
1036 goto error;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001037 }
1038
1039 i7core_printk(KERN_INFO,
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001040 "Registered device %0x:%0x fn %d.%d\n",
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001041 PCI_VENDOR_ID_INTEL, pci_devs[i].dev_id,
1042 PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001043 }
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001044
1045 return 0;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001046
1047error:
1048 i7core_put_devices();
1049 return -EINVAL;
1050}
1051
1052static int mci_bind_devs(struct mem_ctl_info *mci)
1053{
1054 struct i7core_pvt *pvt = mci->pvt_info;
1055 struct pci_dev *pdev;
1056 int i, func, slot;
1057
1058 for (i = 0; i < N_DEVS; i++) {
1059 pdev = pci_devs[i].pdev;
1060 if (!pdev)
1061 continue;
1062
1063 func = PCI_FUNC(pdev->devfn);
1064 slot = PCI_SLOT(pdev->devfn);
1065 if (slot == 3) {
1066 if (unlikely(func > MAX_MCR_FUNC))
1067 goto error;
1068 pvt->pci_mcr[func] = pdev;
1069 } else if (likely(slot >= 4 && slot < 4 + NUM_CHANS)) {
1070 if (unlikely(func > MAX_CHAN_FUNC))
1071 goto error;
1072 pvt->pci_ch[slot - 4][func] = pdev;
1073 } else
1074 goto error;
1075
1076 debugf0("Associated fn %d.%d, dev = %p\n",
1077 PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn), pdev);
1078 }
1079 return 0;
1080
1081error:
1082 i7core_printk(KERN_ERR, "Device %d, function %d "
1083 "is out of the expected range\n",
1084 slot, func);
1085 return -EINVAL;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001086}
1087
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001088/****************************************************************************
1089 Error check routines
1090 ****************************************************************************/
1091
1092/* This function is based on the device 3 function 4 registers as described on:
1093 * Intel Xeon Processor 5500 Series Datasheet Volume 2
1094 * http://www.intel.com/Assets/PDF/datasheet/321322.pdf
1095 * also available at:
1096 * http://www.arrownac.com/manufacturers/intel/s/nehalem/5500-datasheet-v2.pdf
1097 */
1098static void check_mc_test_err(struct mem_ctl_info *mci)
1099{
1100 struct i7core_pvt *pvt = mci->pvt_info;
1101 u32 rcv1, rcv0;
1102 int new0, new1, new2;
1103
1104 if (!pvt->pci_mcr[4]) {
1105 debugf0("%s MCR registers not found\n",__func__);
1106 return;
1107 }
1108
1109 /* Corrected error reads */
1110 pci_read_config_dword(pvt->pci_mcr[4], MC_TEST_ERR_RCV1, &rcv1);
1111 pci_read_config_dword(pvt->pci_mcr[4], MC_TEST_ERR_RCV0, &rcv0);
1112
1113 /* Store the new values */
1114 new2 = DIMM2_COR_ERR(rcv1);
1115 new1 = DIMM1_COR_ERR(rcv0);
1116 new0 = DIMM0_COR_ERR(rcv0);
1117
1118 debugf2("%s CE rcv1=0x%08x rcv0=0x%08x, %d %d %d\n",
1119 (pvt->ce_count_available ? "UPDATE" : "READ"),
1120 rcv1, rcv0, new0, new1, new2);
1121
1122 /* Updates CE counters if it is not the first time here */
1123 if (pvt->ce_count_available) {
1124 /* Updates CE counters */
1125 int add0, add1, add2;
1126
1127 add2 = new2 - pvt->last_ce_count[2];
1128 add1 = new1 - pvt->last_ce_count[1];
1129 add0 = new0 - pvt->last_ce_count[0];
1130
1131 if (add2 < 0)
1132 add2 += 0x7fff;
1133 pvt->ce_count[2] += add2;
1134
1135 if (add1 < 0)
1136 add1 += 0x7fff;
1137 pvt->ce_count[1] += add1;
1138
1139 if (add0 < 0)
1140 add0 += 0x7fff;
1141 pvt->ce_count[0] += add0;
1142 } else
1143 pvt->ce_count_available = 1;
1144
1145 /* Store the new values */
1146 pvt->last_ce_count[2] = new2;
1147 pvt->last_ce_count[1] = new1;
1148 pvt->last_ce_count[0] = new0;
1149}
1150
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001151/*
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001152 * i7core_check_error Retrieve and process errors reported by the
1153 * hardware. Called by the Core module.
1154 */
1155static void i7core_check_error(struct mem_ctl_info *mci)
1156{
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001157 check_mc_test_err(mci);
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001158}
1159
1160/*
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001161 * i7core_probe Probe for ONE instance of device to see if it is
1162 * present.
1163 * return:
1164 * 0 for FOUND a device
1165 * < 0 for error code
1166 */
1167static int __devinit i7core_probe(struct pci_dev *pdev,
1168 const struct pci_device_id *id)
1169{
1170 struct mem_ctl_info *mci;
1171 struct i7core_pvt *pvt;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001172 int num_channels = 0;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001173 int num_csrows;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001174 int dev_idx = id->driver_data;
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001175 int rc;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001176
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001177 if (unlikely(dev_idx >= ARRAY_SIZE(i7core_devs)))
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001178 return -EINVAL;
1179
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001180 /* get the pci devices we want to reserve for our use */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001181 rc = i7core_get_devices();
1182 if (unlikely(rc < 0))
1183 return rc;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001184
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001185 /* Check the number of active and not disabled channels */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001186 rc = i7core_get_active_channels(&num_channels);
1187 if (unlikely (rc < 0))
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001188 goto fail0;
1189
1190 /* FIXME: we currently don't know the number of csrows */
1191 num_csrows = num_channels;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001192
1193 /* allocate a new MC control structure */
1194 mci = edac_mc_alloc(sizeof(*pvt), num_csrows, num_channels, 0);
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001195 if (unlikely (!mci)) {
1196 rc = -ENOMEM;
1197 goto fail0;
1198 }
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001199
1200 debugf0("MC: " __FILE__ ": %s(): mci = %p\n", __func__, mci);
1201
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001202 mci->dev = &pdev->dev; /* record ptr to the generic device */
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001203
1204 pvt = mci->pvt_info;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001205 memset(pvt, 0, sizeof(*pvt));
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001206
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001207 mci->mc_idx = 0;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001208 mci->mtype_cap = MEM_FLAG_DDR3; /* FIXME: how to handle RDDR3? */
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001209 mci->edac_ctl_cap = EDAC_FLAG_NONE;
1210 mci->edac_cap = EDAC_FLAG_NONE;
1211 mci->mod_name = "i7core_edac.c";
1212 mci->mod_ver = I7CORE_REVISION;
1213 mci->ctl_name = i7core_devs[dev_idx].ctl_name;
1214 mci->dev_name = pci_name(pdev);
1215 mci->ctl_page_to_phys = NULL;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001216 mci->mc_driver_sysfs_attributes = i7core_inj_attrs;
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001217 /* Set the function pointer to an actual operation function */
1218 mci->edac_check = i7core_check_error;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001219
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001220 /* Store pci devices at mci for faster access */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001221 rc = mci_bind_devs(mci);
1222 if (unlikely (rc < 0))
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001223 goto fail1;
1224
1225 /* Get dimm basic config */
1226 get_dimm_config(mci);
1227
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001228 /* add this new MC control structure to EDAC's list of MCs */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001229 if (unlikely(edac_mc_add_mc(mci))) {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001230 debugf0("MC: " __FILE__
1231 ": %s(): failed edac_mc_add_mc()\n", __func__);
1232 /* FIXME: perhaps some code should go here that disables error
1233 * reporting if we just enabled it
1234 */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001235
1236 rc = -EINVAL;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001237 goto fail1;
1238 }
1239
1240 /* allocating generic PCI control info */
1241 i7core_pci = edac_pci_create_generic_ctl(&pdev->dev, EDAC_MOD_STR);
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001242 if (unlikely (!i7core_pci)) {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001243 printk(KERN_WARNING
1244 "%s(): Unable to create PCI control\n",
1245 __func__);
1246 printk(KERN_WARNING
1247 "%s(): PCI error report via EDAC not setup\n",
1248 __func__);
1249 }
1250
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001251 /* Default error mask is any memory */
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001252 pvt->inject.channel = 0;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001253 pvt->inject.dimm = -1;
1254 pvt->inject.rank = -1;
1255 pvt->inject.bank = -1;
1256 pvt->inject.page = -1;
1257 pvt->inject.col = -1;
1258
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001259 i7core_printk(KERN_INFO, "Driver loaded.\n");
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001260
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001261 return 0;
1262
1263fail1:
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001264 edac_mc_free(mci);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001265
1266fail0:
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001267 i7core_put_devices();
1268 return rc;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001269}
1270
1271/*
1272 * i7core_remove destructor for one instance of device
1273 *
1274 */
1275static void __devexit i7core_remove(struct pci_dev *pdev)
1276{
1277 struct mem_ctl_info *mci;
1278
1279 debugf0(__FILE__ ": %s()\n", __func__);
1280
1281 if (i7core_pci)
1282 edac_pci_release_generic_ctl(i7core_pci);
1283
1284 mci = edac_mc_del_mc(&pdev->dev);
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001285
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001286 if (!mci)
1287 return;
1288
1289 /* retrieve references to resources, and free those resources */
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001290 i7core_put_devices();
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001291
1292 edac_mc_free(mci);
1293}
1294
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001295MODULE_DEVICE_TABLE(pci, i7core_pci_tbl);
1296
1297/*
1298 * i7core_driver pci_driver structure for this module
1299 *
1300 */
1301static struct pci_driver i7core_driver = {
1302 .name = "i7core_edac",
1303 .probe = i7core_probe,
1304 .remove = __devexit_p(i7core_remove),
1305 .id_table = i7core_pci_tbl,
1306};
1307
1308/*
1309 * i7core_init Module entry function
1310 * Try to initialize this module for its devices
1311 */
1312static int __init i7core_init(void)
1313{
1314 int pci_rc;
1315
1316 debugf2("MC: " __FILE__ ": %s()\n", __func__);
1317
1318 /* Ensure that the OPSTATE is set correctly for POLL or NMI */
1319 opstate_init();
1320
1321 pci_rc = pci_register_driver(&i7core_driver);
1322
1323 return (pci_rc < 0) ? pci_rc : 0;
1324}
1325
1326/*
1327 * i7core_exit() Module exit function
1328 * Unregister the driver
1329 */
1330static void __exit i7core_exit(void)
1331{
1332 debugf2("MC: " __FILE__ ": %s()\n", __func__);
1333 pci_unregister_driver(&i7core_driver);
1334}
1335
1336module_init(i7core_init);
1337module_exit(i7core_exit);
1338
1339MODULE_LICENSE("GPL");
1340MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
1341MODULE_AUTHOR("Red Hat Inc. (http://www.redhat.com)");
1342MODULE_DESCRIPTION("MC Driver for Intel i7 Core memory controllers - "
1343 I7CORE_REVISION);
1344
1345module_param(edac_op_state, int, 0444);
1346MODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI");