blob: 772219fa10beac51d8d4d68c28d53572667f2768 [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)
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300116 #define MC_DOD_NUMROW_MASK ((1 << 4) | (1 << 3)| (1 << 2))
117 #define MC_DOD_NUMROW(x) (((x) & MC_DOD_NUMROW_MASK) >> 2)
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300118 #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 Chehab5566cb72009-06-22 22:48:31 -0300364 unsigned long last_page = 0;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300365 enum edac_type mode;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300366 enum mem_type mtype;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300367
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300368 /* Get data from the MC register, function 0 */
369 pdev = pvt->pci_mcr[0];
Mauro Carvalho Chehab7dd69532009-06-22 22:48:30 -0300370 if (!pdev)
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300371 return -ENODEV;
372
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300373 /* Device 3 function 0 reads */
Mauro Carvalho Chehab7dd69532009-06-22 22:48:30 -0300374 pci_read_config_dword(pdev, MC_CONTROL, &pvt->info.mc_control);
375 pci_read_config_dword(pdev, MC_STATUS, &pvt->info.mc_status);
376 pci_read_config_dword(pdev, MC_MAX_DOD, &pvt->info.max_dod);
377 pci_read_config_dword(pdev, MC_CHANNEL_MAPPER, &pvt->info.ch_map);
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300378
379 debugf0("MC control=0x%08x status=0x%08x dod=0x%08x map=0x%08x\n",
380 pvt->info.mc_control, pvt->info.mc_status,
381 pvt->info.max_dod, pvt->info.ch_map);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300382
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300383 if (ECC_ENABLED(pvt)) {
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300384 debugf0("ECC enabled with x%d SDCC\n", ECCx8(pvt) ?8:4);
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300385 if (ECCx8(pvt))
386 mode = EDAC_S8ECD8ED;
387 else
388 mode = EDAC_S4ECD4ED;
389 } else {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300390 debugf0("ECC disabled\n");
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300391 mode = EDAC_NONE;
392 }
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300393
394 /* FIXME: need to handle the error codes */
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300395 debugf0("DOD Max limits: DIMMS: %d, %d-ranked, %d-banked\n",
396 numdimms(pvt->info.max_dod),
397 numrank(pvt->info.max_dod >> 2),
398 numbank(pvt->info.max_dod >> 4));
399 debugf0("DOD Max rows x colums = 0x%x x 0x%x\n",
400 numrow(pvt->info.max_dod >> 6),
401 numcol(pvt->info.max_dod >> 9));
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300402
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300403 debugf0("Memory channel configuration:\n");
404
405 for (i = 0; i < NUM_CHANS; i++) {
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300406 u32 data, dimm_dod[3], value[8];
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300407
408 if (!CH_ACTIVE(pvt, i)) {
409 debugf0("Channel %i is not active\n", i);
410 continue;
411 }
412 if (CH_DISABLED(pvt, i)) {
413 debugf0("Channel %i is disabled\n", i);
414 continue;
415 }
416
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300417 /* Devices 4-6 function 0 */
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300418 pci_read_config_dword(pvt->pci_ch[i][0],
419 MC_CHANNEL_DIMM_INIT_PARAMS, &data);
420
421 pvt->channel[i].ranks = (data & QUAD_RANK_PRESENT)? 4 : 2;
422
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300423 if (data & REGISTERED_DIMM)
424 mtype = MEM_RDDR3;
425 else
426 mtype = MEM_DDR3;
427#if 0
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300428 if (data & THREE_DIMMS_PRESENT)
429 pvt->channel[i].dimms = 3;
430 else if (data & SINGLE_QUAD_RANK_PRESENT)
431 pvt->channel[i].dimms = 1;
432 else
433 pvt->channel[i].dimms = 2;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300434#endif
435
436 /* Devices 4-6 function 1 */
437 pci_read_config_dword(pvt->pci_ch[i][1],
438 MC_DOD_CH_DIMM0, &dimm_dod[0]);
439 pci_read_config_dword(pvt->pci_ch[i][1],
440 MC_DOD_CH_DIMM1, &dimm_dod[1]);
441 pci_read_config_dword(pvt->pci_ch[i][1],
442 MC_DOD_CH_DIMM2, &dimm_dod[2]);
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300443
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300444 debugf0("Ch%d phy rd%d, wr%d (0x%08x): "
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300445 "%d ranks, %cDIMMs\n",
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300446 i,
447 RDLCH(pvt->info.ch_map, i), WRLCH(pvt->info.ch_map, i),
448 data,
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300449 pvt->channel[i].ranks,
450 (data & REGISTERED_DIMM)? 'R' : 'U');
Mauro Carvalho Chehab7dd69532009-06-22 22:48:30 -0300451
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300452 for (j = 0; j < 3; j++) {
453 u32 banks, ranks, rows, cols;
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300454 u32 size, npages;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300455
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300456 if (!DIMM_PRESENT(dimm_dod[j]))
457 continue;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300458
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300459 banks = numbank(MC_DOD_NUMBANK(dimm_dod[j]));
460 ranks = numrank(MC_DOD_NUMRANK(dimm_dod[j]));
461 rows = numrow(MC_DOD_NUMROW(dimm_dod[j]));
462 cols = numcol(MC_DOD_NUMCOL(dimm_dod[j]));
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300463
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300464 /* DDR3 has 8 I/O banks */
465 size = (rows * cols * banks * ranks) >> (20 - 3);
466
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300467 pvt->channel[i].dimms++;
468
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300469 debugf0("\tdimm %d (0x%08x) %d Mb offset: %x, "
470 "numbank: %d,\n\t\t"
471 "numrank: %d, numrow: %#x, numcol: %#x\n",
472 j, dimm_dod[j], size,
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300473 RANKOFFSET(dimm_dod[j]),
474 banks, ranks, rows, cols);
475
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300476 npages = cols * rows; /* FIXME */
477
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300478 csr = &mci->csrows[csrow];
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300479 csr->first_page = last_page + 1;
480 last_page += npages;
481 csr->last_page = last_page;
482 csr->nr_pages = npages;
483
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300484 csr->page_mask = 0;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300485 csr->grain = 0;
486 csr->csrow_idx = csrow;
487
488 switch (banks) {
489 case 4:
490 csr->dtype = DEV_X4;
491 break;
492 case 8:
493 csr->dtype = DEV_X8;
494 break;
495 case 16:
496 csr->dtype = DEV_X16;
497 break;
498 default:
499 csr->dtype = DEV_UNKNOWN;
500 }
501
502 csr->edac_mode = mode;
503 csr->mtype = mtype;
504
505 csrow++;
506 }
507
508 pci_read_config_dword(pdev, MC_SAG_CH_0, &value[0]);
509 pci_read_config_dword(pdev, MC_SAG_CH_1, &value[1]);
510 pci_read_config_dword(pdev, MC_SAG_CH_2, &value[2]);
511 pci_read_config_dword(pdev, MC_SAG_CH_3, &value[3]);
512 pci_read_config_dword(pdev, MC_SAG_CH_4, &value[4]);
513 pci_read_config_dword(pdev, MC_SAG_CH_5, &value[5]);
514 pci_read_config_dword(pdev, MC_SAG_CH_6, &value[6]);
515 pci_read_config_dword(pdev, MC_SAG_CH_7, &value[7]);
516 printk("\t[%i] DIVBY3\tREMOVED\tOFFSET\n", i);
517 for (j = 0; j < 8; j++)
518 printk("\t\t%#x\t%#x\t%#x\n",
519 (value[j] >> 27) & 0x1,
520 (value[j] >> 24) & 0x7,
521 (value[j] && ((1 << 24) - 1)));
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300522 }
523
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300524 return 0;
525}
526
527/****************************************************************************
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300528 Error insertion routines
529 ****************************************************************************/
530
531/* The i7core has independent error injection features per channel.
532 However, to have a simpler code, we don't allow enabling error injection
533 on more than one channel.
534 Also, since a change at an inject parameter will be applied only at enable,
535 we're disabling error injection on all write calls to the sysfs nodes that
536 controls the error code injection.
537 */
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300538static int disable_inject(struct mem_ctl_info *mci)
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300539{
540 struct i7core_pvt *pvt = mci->pvt_info;
541
542 pvt->inject.enable = 0;
543
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300544 if (!pvt->pci_ch[pvt->inject.channel][0])
545 return -ENODEV;
546
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300547 pci_write_config_dword(pvt->pci_ch[pvt->inject.channel][0],
548 MC_CHANNEL_ERROR_MASK, 0);
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300549
550 return 0;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300551}
552
553/*
554 * i7core inject inject.section
555 *
556 * accept and store error injection inject.section value
557 * bit 0 - refers to the lower 32-byte half cacheline
558 * bit 1 - refers to the upper 32-byte half cacheline
559 */
560static ssize_t i7core_inject_section_store(struct mem_ctl_info *mci,
561 const char *data, size_t count)
562{
563 struct i7core_pvt *pvt = mci->pvt_info;
564 unsigned long value;
565 int rc;
566
567 if (pvt->inject.enable)
568 disable_inject(mci);
569
570 rc = strict_strtoul(data, 10, &value);
571 if ((rc < 0) || (value > 3))
572 return 0;
573
574 pvt->inject.section = (u32) value;
575 return count;
576}
577
578static ssize_t i7core_inject_section_show(struct mem_ctl_info *mci,
579 char *data)
580{
581 struct i7core_pvt *pvt = mci->pvt_info;
582 return sprintf(data, "0x%08x\n", pvt->inject.section);
583}
584
585/*
586 * i7core inject.type
587 *
588 * accept and store error injection inject.section value
589 * bit 0 - repeat enable - Enable error repetition
590 * bit 1 - inject ECC error
591 * bit 2 - inject parity error
592 */
593static ssize_t i7core_inject_type_store(struct mem_ctl_info *mci,
594 const char *data, size_t count)
595{
596 struct i7core_pvt *pvt = mci->pvt_info;
597 unsigned long value;
598 int rc;
599
600 if (pvt->inject.enable)
601 disable_inject(mci);
602
603 rc = strict_strtoul(data, 10, &value);
604 if ((rc < 0) || (value > 7))
605 return 0;
606
607 pvt->inject.type = (u32) value;
608 return count;
609}
610
611static ssize_t i7core_inject_type_show(struct mem_ctl_info *mci,
612 char *data)
613{
614 struct i7core_pvt *pvt = mci->pvt_info;
615 return sprintf(data, "0x%08x\n", pvt->inject.type);
616}
617
618/*
619 * i7core_inject_inject.eccmask_store
620 *
621 * The type of error (UE/CE) will depend on the inject.eccmask value:
622 * Any bits set to a 1 will flip the corresponding ECC bit
623 * Correctable errors can be injected by flipping 1 bit or the bits within
624 * a symbol pair (2 consecutive aligned 8-bit pairs - i.e. 7:0 and 15:8 or
625 * 23:16 and 31:24). Flipping bits in two symbol pairs will cause an
626 * uncorrectable error to be injected.
627 */
628static ssize_t i7core_inject_eccmask_store(struct mem_ctl_info *mci,
629 const char *data, size_t count)
630{
631 struct i7core_pvt *pvt = mci->pvt_info;
632 unsigned long value;
633 int rc;
634
635 if (pvt->inject.enable)
636 disable_inject(mci);
637
638 rc = strict_strtoul(data, 10, &value);
639 if (rc < 0)
640 return 0;
641
642 pvt->inject.eccmask = (u32) value;
643 return count;
644}
645
646static ssize_t i7core_inject_eccmask_show(struct mem_ctl_info *mci,
647 char *data)
648{
649 struct i7core_pvt *pvt = mci->pvt_info;
650 return sprintf(data, "0x%08x\n", pvt->inject.eccmask);
651}
652
653/*
654 * i7core_addrmatch
655 *
656 * The type of error (UE/CE) will depend on the inject.eccmask value:
657 * Any bits set to a 1 will flip the corresponding ECC bit
658 * Correctable errors can be injected by flipping 1 bit or the bits within
659 * a symbol pair (2 consecutive aligned 8-bit pairs - i.e. 7:0 and 15:8 or
660 * 23:16 and 31:24). Flipping bits in two symbol pairs will cause an
661 * uncorrectable error to be injected.
662 */
663static ssize_t i7core_inject_addrmatch_store(struct mem_ctl_info *mci,
664 const char *data, size_t count)
665{
666 struct i7core_pvt *pvt = mci->pvt_info;
667 char *cmd, *val;
668 long value;
669 int rc;
670
671 if (pvt->inject.enable)
672 disable_inject(mci);
673
674 do {
675 cmd = strsep((char **) &data, ":");
676 if (!cmd)
677 break;
678 val = strsep((char **) &data, " \n\t");
679 if (!val)
680 return cmd - data;
681
682 if (!strcasecmp(val,"any"))
683 value = -1;
684 else {
685 rc = strict_strtol(val, 10, &value);
686 if ((rc < 0) || (value < 0))
687 return cmd - data;
688 }
689
690 if (!strcasecmp(cmd,"channel")) {
691 if (value < 3)
692 pvt->inject.channel = value;
693 else
694 return cmd - data;
695 } else if (!strcasecmp(cmd,"dimm")) {
696 if (value < 4)
697 pvt->inject.dimm = value;
698 else
699 return cmd - data;
700 } else if (!strcasecmp(cmd,"rank")) {
701 if (value < 4)
702 pvt->inject.rank = value;
703 else
704 return cmd - data;
705 } else if (!strcasecmp(cmd,"bank")) {
706 if (value < 4)
707 pvt->inject.bank = value;
708 else
709 return cmd - data;
710 } else if (!strcasecmp(cmd,"page")) {
711 if (value <= 0xffff)
712 pvt->inject.page = value;
713 else
714 return cmd - data;
715 } else if (!strcasecmp(cmd,"col") ||
716 !strcasecmp(cmd,"column")) {
717 if (value <= 0x3fff)
718 pvt->inject.col = value;
719 else
720 return cmd - data;
721 }
722 } while (1);
723
724 return count;
725}
726
727static ssize_t i7core_inject_addrmatch_show(struct mem_ctl_info *mci,
728 char *data)
729{
730 struct i7core_pvt *pvt = mci->pvt_info;
731 char channel[4], dimm[4], bank[4], rank[4], page[7], col[7];
732
733 if (pvt->inject.channel < 0)
734 sprintf(channel, "any");
735 else
736 sprintf(channel, "%d", pvt->inject.channel);
737 if (pvt->inject.dimm < 0)
738 sprintf(dimm, "any");
739 else
740 sprintf(dimm, "%d", pvt->inject.dimm);
741 if (pvt->inject.bank < 0)
742 sprintf(bank, "any");
743 else
744 sprintf(bank, "%d", pvt->inject.bank);
745 if (pvt->inject.rank < 0)
746 sprintf(rank, "any");
747 else
748 sprintf(rank, "%d", pvt->inject.rank);
749 if (pvt->inject.page < 0)
750 sprintf(page, "any");
751 else
752 sprintf(page, "0x%04x", pvt->inject.page);
753 if (pvt->inject.col < 0)
754 sprintf(col, "any");
755 else
756 sprintf(col, "0x%04x", pvt->inject.col);
757
758 return sprintf(data, "channel: %s\ndimm: %s\nbank: %s\n"
759 "rank: %s\npage: %s\ncolumn: %s\n",
760 channel, dimm, bank, rank, page, col);
761}
762
763/*
764 * This routine prepares the Memory Controller for error injection.
765 * The error will be injected when some process tries to write to the
766 * memory that matches the given criteria.
767 * The criteria can be set in terms of a mask where dimm, rank, bank, page
768 * and col can be specified.
769 * A -1 value for any of the mask items will make the MCU to ignore
770 * that matching criteria for error injection.
771 *
772 * It should be noticed that the error will only happen after a write operation
773 * on a memory that matches the condition. if REPEAT_EN is not enabled at
774 * inject mask, then it will produce just one error. Otherwise, it will repeat
775 * until the injectmask would be cleaned.
776 *
777 * FIXME: This routine assumes that MAXNUMDIMMS value of MC_MAX_DOD
778 * is reliable enough to check if the MC is using the
779 * three channels. However, this is not clear at the datasheet.
780 */
781static ssize_t i7core_inject_enable_store(struct mem_ctl_info *mci,
782 const char *data, size_t count)
783{
784 struct i7core_pvt *pvt = mci->pvt_info;
785 u32 injectmask;
786 u64 mask = 0;
787 int rc;
788 long enable;
789
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300790 if (!pvt->pci_ch[pvt->inject.channel][0])
791 return 0;
792
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300793 rc = strict_strtoul(data, 10, &enable);
794 if ((rc < 0))
795 return 0;
796
797 if (enable) {
798 pvt->inject.enable = 1;
799 } else {
800 disable_inject(mci);
801 return count;
802 }
803
804 /* Sets pvt->inject.dimm mask */
805 if (pvt->inject.dimm < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300806 mask |= 1L << 41;
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.dimm & 0x3L) << 35;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300810 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300811 mask |= (pvt->inject.dimm & 0x1L) << 36;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300812 }
813
814 /* Sets pvt->inject.rank mask */
815 if (pvt->inject.rank < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300816 mask |= 1L << 40;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300817 else {
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300818 if (pvt->channel[pvt->inject.channel].dimms > 2)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300819 mask |= (pvt->inject.rank & 0x1L) << 34;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300820 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300821 mask |= (pvt->inject.rank & 0x3L) << 34;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300822 }
823
824 /* Sets pvt->inject.bank mask */
825 if (pvt->inject.bank < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300826 mask |= 1L << 39;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300827 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300828 mask |= (pvt->inject.bank & 0x15L) << 30;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300829
830 /* Sets pvt->inject.page mask */
831 if (pvt->inject.page < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300832 mask |= 1L << 38;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300833 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300834 mask |= (pvt->inject.page & 0xffffL) << 14;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300835
836 /* Sets pvt->inject.column mask */
837 if (pvt->inject.col < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300838 mask |= 1L << 37;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300839 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300840 mask |= (pvt->inject.col & 0x3fffL);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300841
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300842#if USE_QWORD
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300843 pci_write_config_qword(pvt->pci_ch[pvt->inject.channel][0],
844 MC_CHANNEL_ADDR_MATCH, mask);
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300845#else
846 pci_write_config_dword(pvt->pci_ch[pvt->inject.channel][0],
847 MC_CHANNEL_ADDR_MATCH, mask);
848 pci_write_config_dword(pvt->pci_ch[pvt->inject.channel][0],
849 MC_CHANNEL_ADDR_MATCH + 4, mask >> 32L);
850#endif
851
852#if 1
853#if USE_QWORD
854 u64 rdmask;
855 pci_read_config_qword(pvt->pci_ch[pvt->inject.channel][0],
856 MC_CHANNEL_ADDR_MATCH, &rdmask);
857 debugf0("Inject addr match write 0x%016llx, read: 0x%016llx\n",
858 mask, rdmask);
859#else
860 u32 rdmask1, rdmask2;
861
862 pci_read_config_dword(pvt->pci_ch[pvt->inject.channel][0],
863 MC_CHANNEL_ADDR_MATCH, &rdmask1);
864 pci_read_config_dword(pvt->pci_ch[pvt->inject.channel][0],
865 MC_CHANNEL_ADDR_MATCH + 4, &rdmask2);
866
867 debugf0("Inject addr match write 0x%016llx, read: 0x%08x%08x\n",
868 mask, rdmask1, rdmask2);
869#endif
870#endif
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300871
872 pci_write_config_dword(pvt->pci_ch[pvt->inject.channel][0],
873 MC_CHANNEL_ERROR_MASK, pvt->inject.eccmask);
874
875 /*
876 * bit 0: REPEAT_EN
877 * bits 1-2: MASK_HALF_CACHELINE
878 * bit 3: INJECT_ECC
879 * bit 4: INJECT_ADDR_PARITY
880 */
881
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300882 injectmask = (pvt->inject.type & 1) |
883 (pvt->inject.section & 0x3) << 1 |
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300884 (pvt->inject.type & 0x6) << (3 - 1);
885
886 pci_write_config_dword(pvt->pci_ch[pvt->inject.channel][0],
887 MC_CHANNEL_ERROR_MASK, injectmask);
888
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300889 debugf0("Error inject addr match 0x%016llx, ecc 0x%08x, inject 0x%08x\n",
890 mask, pvt->inject.eccmask, injectmask);
891
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300892
893
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300894 return count;
895}
896
897static ssize_t i7core_inject_enable_show(struct mem_ctl_info *mci,
898 char *data)
899{
900 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300901 u32 injectmask;
902
903 pci_read_config_dword(pvt->pci_ch[pvt->inject.channel][0],
904 MC_CHANNEL_ERROR_MASK, &injectmask);
905
906 debugf0("Inject error read: 0x%018x\n", injectmask);
907
908 if (injectmask & 0x0c)
909 pvt->inject.enable = 1;
910
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300911 return sprintf(data, "%d\n", pvt->inject.enable);
912}
913
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -0300914static ssize_t i7core_ce_regs_show(struct mem_ctl_info *mci, char *data)
915{
916 struct i7core_pvt *pvt = mci->pvt_info;
917
918 if (!pvt->ce_count_available)
919 return sprintf(data, "unavailable\n");
920
921 return sprintf(data, "dimm0: %lu\ndimm1: %lu\ndimm2: %lu\n",
922 pvt->ce_count[0],
923 pvt->ce_count[1],
924 pvt->ce_count[2]);
925}
926
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300927/*
928 * Sysfs struct
929 */
930static struct mcidev_sysfs_attribute i7core_inj_attrs[] = {
931
932 {
933 .attr = {
934 .name = "inject_section",
935 .mode = (S_IRUGO | S_IWUSR)
936 },
937 .show = i7core_inject_section_show,
938 .store = i7core_inject_section_store,
939 }, {
940 .attr = {
941 .name = "inject_type",
942 .mode = (S_IRUGO | S_IWUSR)
943 },
944 .show = i7core_inject_type_show,
945 .store = i7core_inject_type_store,
946 }, {
947 .attr = {
948 .name = "inject_eccmask",
949 .mode = (S_IRUGO | S_IWUSR)
950 },
951 .show = i7core_inject_eccmask_show,
952 .store = i7core_inject_eccmask_store,
953 }, {
954 .attr = {
955 .name = "inject_addrmatch",
956 .mode = (S_IRUGO | S_IWUSR)
957 },
958 .show = i7core_inject_addrmatch_show,
959 .store = i7core_inject_addrmatch_store,
960 }, {
961 .attr = {
962 .name = "inject_enable",
963 .mode = (S_IRUGO | S_IWUSR)
964 },
965 .show = i7core_inject_enable_show,
966 .store = i7core_inject_enable_store,
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -0300967 }, {
968 .attr = {
969 .name = "corrected_error_counts",
970 .mode = (S_IRUGO | S_IWUSR)
971 },
972 .show = i7core_ce_regs_show,
973 .store = NULL,
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300974 },
975};
976
977/****************************************************************************
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300978 Device initialization routines: put/get, init/exit
979 ****************************************************************************/
980
981/*
982 * i7core_put_devices 'put' all the devices that we have
983 * reserved via 'get'
984 */
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300985static void i7core_put_devices(void)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300986{
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300987 int i;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300988
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300989 for (i = 0; i < N_DEVS; i++)
990 pci_dev_put(pci_devs[i].pdev);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300991}
992
993/*
994 * i7core_get_devices Find and perform 'get' operation on the MCH's
995 * device/functions we want to reference for this driver
996 *
997 * Need to 'get' device 16 func 1 and func 2
998 */
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300999static int i7core_get_devices(void)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001000{
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001001 int rc, i;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001002 struct pci_dev *pdev = NULL;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001003
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001004 for (i = 0; i < N_DEVS; i++) {
1005 pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
1006 pci_devs[i].dev_id, NULL);
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001007 if (likely(pdev))
1008 pci_devs[i].pdev = pdev;
1009 else {
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001010 i7core_printk(KERN_ERR,
1011 "Device not found: PCI ID %04x:%04x "
1012 "(dev %d, func %d)\n",
1013 PCI_VENDOR_ID_INTEL, pci_devs[i].dev_id,
1014 pci_devs[i].dev,pci_devs[i].func);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001015
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001016 /* Dev 3 function 2 only exists on chips with RDIMMs */
1017 if ((pci_devs[i].dev == 3) && (pci_devs[i].func == 2))
1018 continue;
1019
1020 /* End of list, leave */
1021 rc = -ENODEV;
1022 goto error;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001023 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001024
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001025 /* Sanity check */
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001026 if (unlikely(PCI_SLOT(pdev->devfn) != pci_devs[i].dev ||
1027 PCI_FUNC(pdev->devfn) != pci_devs[i].func)) {
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001028 i7core_printk(KERN_ERR,
1029 "Device PCI ID %04x:%04x "
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001030 "has fn %d.%d instead of fn %d.%d\n",
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001031 PCI_VENDOR_ID_INTEL, pci_devs[i].dev_id,
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001032 PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
1033 pci_devs[i].dev, pci_devs[i].func);
1034 rc = -EINVAL;
1035 goto error;
1036 }
1037
1038 /* Be sure that the device is enabled */
1039 rc = pci_enable_device(pdev);
1040 if (unlikely(rc < 0)) {
1041 i7core_printk(KERN_ERR,
1042 "Couldn't enable PCI ID %04x:%04x "
1043 "fn %d.%d\n",
1044 PCI_VENDOR_ID_INTEL, pci_devs[i].dev_id,
1045 PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
1046 goto error;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001047 }
1048
1049 i7core_printk(KERN_INFO,
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001050 "Registered device %0x:%0x fn %d.%d\n",
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001051 PCI_VENDOR_ID_INTEL, pci_devs[i].dev_id,
1052 PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001053 }
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001054
1055 return 0;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001056
1057error:
1058 i7core_put_devices();
1059 return -EINVAL;
1060}
1061
1062static int mci_bind_devs(struct mem_ctl_info *mci)
1063{
1064 struct i7core_pvt *pvt = mci->pvt_info;
1065 struct pci_dev *pdev;
1066 int i, func, slot;
1067
1068 for (i = 0; i < N_DEVS; i++) {
1069 pdev = pci_devs[i].pdev;
1070 if (!pdev)
1071 continue;
1072
1073 func = PCI_FUNC(pdev->devfn);
1074 slot = PCI_SLOT(pdev->devfn);
1075 if (slot == 3) {
1076 if (unlikely(func > MAX_MCR_FUNC))
1077 goto error;
1078 pvt->pci_mcr[func] = pdev;
1079 } else if (likely(slot >= 4 && slot < 4 + NUM_CHANS)) {
1080 if (unlikely(func > MAX_CHAN_FUNC))
1081 goto error;
1082 pvt->pci_ch[slot - 4][func] = pdev;
1083 } else
1084 goto error;
1085
1086 debugf0("Associated fn %d.%d, dev = %p\n",
1087 PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn), pdev);
1088 }
1089 return 0;
1090
1091error:
1092 i7core_printk(KERN_ERR, "Device %d, function %d "
1093 "is out of the expected range\n",
1094 slot, func);
1095 return -EINVAL;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001096}
1097
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001098/****************************************************************************
1099 Error check routines
1100 ****************************************************************************/
1101
1102/* This function is based on the device 3 function 4 registers as described on:
1103 * Intel Xeon Processor 5500 Series Datasheet Volume 2
1104 * http://www.intel.com/Assets/PDF/datasheet/321322.pdf
1105 * also available at:
1106 * http://www.arrownac.com/manufacturers/intel/s/nehalem/5500-datasheet-v2.pdf
1107 */
1108static void check_mc_test_err(struct mem_ctl_info *mci)
1109{
1110 struct i7core_pvt *pvt = mci->pvt_info;
1111 u32 rcv1, rcv0;
1112 int new0, new1, new2;
1113
1114 if (!pvt->pci_mcr[4]) {
1115 debugf0("%s MCR registers not found\n",__func__);
1116 return;
1117 }
1118
1119 /* Corrected error reads */
1120 pci_read_config_dword(pvt->pci_mcr[4], MC_TEST_ERR_RCV1, &rcv1);
1121 pci_read_config_dword(pvt->pci_mcr[4], MC_TEST_ERR_RCV0, &rcv0);
1122
1123 /* Store the new values */
1124 new2 = DIMM2_COR_ERR(rcv1);
1125 new1 = DIMM1_COR_ERR(rcv0);
1126 new0 = DIMM0_COR_ERR(rcv0);
1127
1128 debugf2("%s CE rcv1=0x%08x rcv0=0x%08x, %d %d %d\n",
1129 (pvt->ce_count_available ? "UPDATE" : "READ"),
1130 rcv1, rcv0, new0, new1, new2);
1131
1132 /* Updates CE counters if it is not the first time here */
1133 if (pvt->ce_count_available) {
1134 /* Updates CE counters */
1135 int add0, add1, add2;
1136
1137 add2 = new2 - pvt->last_ce_count[2];
1138 add1 = new1 - pvt->last_ce_count[1];
1139 add0 = new0 - pvt->last_ce_count[0];
1140
1141 if (add2 < 0)
1142 add2 += 0x7fff;
1143 pvt->ce_count[2] += add2;
1144
1145 if (add1 < 0)
1146 add1 += 0x7fff;
1147 pvt->ce_count[1] += add1;
1148
1149 if (add0 < 0)
1150 add0 += 0x7fff;
1151 pvt->ce_count[0] += add0;
1152 } else
1153 pvt->ce_count_available = 1;
1154
1155 /* Store the new values */
1156 pvt->last_ce_count[2] = new2;
1157 pvt->last_ce_count[1] = new1;
1158 pvt->last_ce_count[0] = new0;
1159}
1160
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001161/*
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001162 * i7core_check_error Retrieve and process errors reported by the
1163 * hardware. Called by the Core module.
1164 */
1165static void i7core_check_error(struct mem_ctl_info *mci)
1166{
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001167 check_mc_test_err(mci);
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001168}
1169
1170/*
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001171 * i7core_probe Probe for ONE instance of device to see if it is
1172 * present.
1173 * return:
1174 * 0 for FOUND a device
1175 * < 0 for error code
1176 */
1177static int __devinit i7core_probe(struct pci_dev *pdev,
1178 const struct pci_device_id *id)
1179{
1180 struct mem_ctl_info *mci;
1181 struct i7core_pvt *pvt;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001182 int num_channels = 0;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001183 int num_csrows;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001184 int dev_idx = id->driver_data;
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001185 int rc;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001186
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001187 if (unlikely(dev_idx >= ARRAY_SIZE(i7core_devs)))
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001188 return -EINVAL;
1189
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001190 /* get the pci devices we want to reserve for our use */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001191 rc = i7core_get_devices();
1192 if (unlikely(rc < 0))
1193 return rc;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001194
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001195 /* Check the number of active and not disabled channels */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001196 rc = i7core_get_active_channels(&num_channels);
1197 if (unlikely (rc < 0))
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001198 goto fail0;
1199
1200 /* FIXME: we currently don't know the number of csrows */
1201 num_csrows = num_channels;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001202
1203 /* allocate a new MC control structure */
1204 mci = edac_mc_alloc(sizeof(*pvt), num_csrows, num_channels, 0);
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001205 if (unlikely (!mci)) {
1206 rc = -ENOMEM;
1207 goto fail0;
1208 }
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001209
1210 debugf0("MC: " __FILE__ ": %s(): mci = %p\n", __func__, mci);
1211
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001212 mci->dev = &pdev->dev; /* record ptr to the generic device */
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001213
1214 pvt = mci->pvt_info;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001215 memset(pvt, 0, sizeof(*pvt));
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001216
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001217 mci->mc_idx = 0;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001218 mci->mtype_cap = MEM_FLAG_DDR3; /* FIXME: how to handle RDDR3? */
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001219 mci->edac_ctl_cap = EDAC_FLAG_NONE;
1220 mci->edac_cap = EDAC_FLAG_NONE;
1221 mci->mod_name = "i7core_edac.c";
1222 mci->mod_ver = I7CORE_REVISION;
1223 mci->ctl_name = i7core_devs[dev_idx].ctl_name;
1224 mci->dev_name = pci_name(pdev);
1225 mci->ctl_page_to_phys = NULL;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001226 mci->mc_driver_sysfs_attributes = i7core_inj_attrs;
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001227 /* Set the function pointer to an actual operation function */
1228 mci->edac_check = i7core_check_error;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001229
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001230 /* Store pci devices at mci for faster access */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001231 rc = mci_bind_devs(mci);
1232 if (unlikely (rc < 0))
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001233 goto fail1;
1234
1235 /* Get dimm basic config */
1236 get_dimm_config(mci);
1237
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001238 /* add this new MC control structure to EDAC's list of MCs */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001239 if (unlikely(edac_mc_add_mc(mci))) {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001240 debugf0("MC: " __FILE__
1241 ": %s(): failed edac_mc_add_mc()\n", __func__);
1242 /* FIXME: perhaps some code should go here that disables error
1243 * reporting if we just enabled it
1244 */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001245
1246 rc = -EINVAL;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001247 goto fail1;
1248 }
1249
1250 /* allocating generic PCI control info */
1251 i7core_pci = edac_pci_create_generic_ctl(&pdev->dev, EDAC_MOD_STR);
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001252 if (unlikely (!i7core_pci)) {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001253 printk(KERN_WARNING
1254 "%s(): Unable to create PCI control\n",
1255 __func__);
1256 printk(KERN_WARNING
1257 "%s(): PCI error report via EDAC not setup\n",
1258 __func__);
1259 }
1260
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001261 /* Default error mask is any memory */
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001262 pvt->inject.channel = 0;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001263 pvt->inject.dimm = -1;
1264 pvt->inject.rank = -1;
1265 pvt->inject.bank = -1;
1266 pvt->inject.page = -1;
1267 pvt->inject.col = -1;
1268
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001269 i7core_printk(KERN_INFO, "Driver loaded.\n");
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001270
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001271 return 0;
1272
1273fail1:
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001274 edac_mc_free(mci);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001275
1276fail0:
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001277 i7core_put_devices();
1278 return rc;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001279}
1280
1281/*
1282 * i7core_remove destructor for one instance of device
1283 *
1284 */
1285static void __devexit i7core_remove(struct pci_dev *pdev)
1286{
1287 struct mem_ctl_info *mci;
1288
1289 debugf0(__FILE__ ": %s()\n", __func__);
1290
1291 if (i7core_pci)
1292 edac_pci_release_generic_ctl(i7core_pci);
1293
1294 mci = edac_mc_del_mc(&pdev->dev);
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001295
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001296 if (!mci)
1297 return;
1298
1299 /* retrieve references to resources, and free those resources */
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001300 i7core_put_devices();
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001301
1302 edac_mc_free(mci);
1303}
1304
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001305MODULE_DEVICE_TABLE(pci, i7core_pci_tbl);
1306
1307/*
1308 * i7core_driver pci_driver structure for this module
1309 *
1310 */
1311static struct pci_driver i7core_driver = {
1312 .name = "i7core_edac",
1313 .probe = i7core_probe,
1314 .remove = __devexit_p(i7core_remove),
1315 .id_table = i7core_pci_tbl,
1316};
1317
1318/*
1319 * i7core_init Module entry function
1320 * Try to initialize this module for its devices
1321 */
1322static int __init i7core_init(void)
1323{
1324 int pci_rc;
1325
1326 debugf2("MC: " __FILE__ ": %s()\n", __func__);
1327
1328 /* Ensure that the OPSTATE is set correctly for POLL or NMI */
1329 opstate_init();
1330
1331 pci_rc = pci_register_driver(&i7core_driver);
1332
1333 return (pci_rc < 0) ? pci_rc : 0;
1334}
1335
1336/*
1337 * i7core_exit() Module exit function
1338 * Unregister the driver
1339 */
1340static void __exit i7core_exit(void)
1341{
1342 debugf2("MC: " __FILE__ ": %s()\n", __func__);
1343 pci_unregister_driver(&i7core_driver);
1344}
1345
1346module_init(i7core_init);
1347module_exit(i7core_exit);
1348
1349MODULE_LICENSE("GPL");
1350MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
1351MODULE_AUTHOR("Red Hat Inc. (http://www.redhat.com)");
1352MODULE_DESCRIPTION("MC Driver for Intel i7 Core memory controllers - "
1353 I7CORE_REVISION);
1354
1355module_param(edac_op_state, int, 0444);
1356MODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI");