blob: a93ebdf9c121026c04427a432102e7ac21fb3287 [file] [log] [blame]
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001/* Intel 7 core Memory Controller kernel module (Nehalem)
2 *
3 * This file may be distributed under the terms of the
4 * GNU General Public License version 2 only.
5 *
6 * Copyright (c) 2009 by:
7 * Mauro Carvalho Chehab <mchehab@redhat.com>
8 *
9 * Red Hat Inc. http://www.redhat.com
10 *
11 * Forked and adapted from the i5400_edac driver
12 *
13 * Based on the following public Intel datasheets:
14 * Intel Core i7 Processor Extreme Edition and Intel Core i7 Processor
15 * Datasheet, Volume 2:
16 * http://download.intel.com/design/processor/datashts/320835.pdf
17 * Intel Xeon Processor 5500 Series Datasheet Volume 2
18 * http://www.intel.com/Assets/PDF/datasheet/321322.pdf
19 * also available at:
20 * http://www.arrownac.com/manufacturers/intel/s/nehalem/5500-datasheet-v2.pdf
21 */
22
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030023#include <linux/module.h>
24#include <linux/init.h>
25#include <linux/pci.h>
26#include <linux/pci_ids.h>
27#include <linux/slab.h>
28#include <linux/edac.h>
29#include <linux/mmzone.h>
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -030030#include <linux/edac_mce.h>
31#include <linux/spinlock.h>
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030032
33#include "edac_core.h"
34
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -030035/* To use the new pci_[read/write]_config_qword instead of two dword */
Mauro Carvalho Chehabe9bd2e72009-07-09 22:14:35 -030036#define USE_QWORD 0
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030037
38/*
39 * Alter this version for the module when modifications are made
40 */
41#define I7CORE_REVISION " Ver: 1.0.0 " __DATE__
42#define EDAC_MOD_STR "i7core_edac"
43
44/* HACK: temporary, just to enable all logs, for now */
45#undef debugf0
46#define debugf0(fmt, arg...) edac_printk(KERN_INFO, "i7core", fmt, ##arg)
47
48/*
49 * Debug macros
50 */
51#define i7core_printk(level, fmt, arg...) \
52 edac_printk(level, "i7core", fmt, ##arg)
53
54#define i7core_mc_printk(mci, level, fmt, arg...) \
55 edac_mc_chipset_printk(mci, level, "i7core", fmt, ##arg)
56
57/*
58 * i7core Memory Controller Registers
59 */
60
Mauro Carvalho Chehabe9bd2e72009-07-09 22:14:35 -030061 /* OFFSETS for Device 0 Function 0 */
62
63#define MC_CFG_CONTROL 0x90
64
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030065 /* OFFSETS for Device 3 Function 0 */
66
67#define MC_CONTROL 0x48
68#define MC_STATUS 0x4c
69#define MC_MAX_DOD 0x64
70
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -030071/*
72 * OFFSETS for Device 3 Function 4, as inicated on Xeon 5500 datasheet:
73 * http://www.arrownac.com/manufacturers/intel/s/nehalem/5500-datasheet-v2.pdf
74 */
75
76#define MC_TEST_ERR_RCV1 0x60
77 #define DIMM2_COR_ERR(r) ((r) & 0x7fff)
78
79#define MC_TEST_ERR_RCV0 0x64
80 #define DIMM1_COR_ERR(r) (((r) >> 16) & 0x7fff)
81 #define DIMM0_COR_ERR(r) ((r) & 0x7fff)
82
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030083 /* OFFSETS for Devices 4,5 and 6 Function 0 */
84
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -030085#define MC_CHANNEL_DIMM_INIT_PARAMS 0x58
86 #define THREE_DIMMS_PRESENT (1 << 24)
87 #define SINGLE_QUAD_RANK_PRESENT (1 << 23)
88 #define QUAD_RANK_PRESENT (1 << 22)
89 #define REGISTERED_DIMM (1 << 15)
90
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -030091#define MC_CHANNEL_MAPPER 0x60
92 #define RDLCH(r, ch) ((((r) >> (3 + (ch * 6))) & 0x07) - 1)
93 #define WRLCH(r, ch) ((((r) >> (ch * 6)) & 0x07) - 1)
94
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -030095#define MC_CHANNEL_RANK_PRESENT 0x7c
96 #define RANK_PRESENT_MASK 0xffff
97
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030098#define MC_CHANNEL_ADDR_MATCH 0xf0
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -030099#define MC_CHANNEL_ERROR_MASK 0xf8
100#define MC_CHANNEL_ERROR_INJECT 0xfc
101 #define INJECT_ADDR_PARITY 0x10
102 #define INJECT_ECC 0x08
103 #define MASK_CACHELINE 0x06
104 #define MASK_FULL_CACHELINE 0x06
105 #define MASK_MSB32_CACHELINE 0x04
106 #define MASK_LSB32_CACHELINE 0x02
107 #define NO_MASK_CACHELINE 0x00
108 #define REPEAT_EN 0x01
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300109
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300110 /* OFFSETS for Devices 4,5 and 6 Function 1 */
111#define MC_DOD_CH_DIMM0 0x48
112#define MC_DOD_CH_DIMM1 0x4c
113#define MC_DOD_CH_DIMM2 0x50
114 #define RANKOFFSET_MASK ((1 << 12) | (1 << 11) | (1 << 10))
115 #define RANKOFFSET(x) ((x & RANKOFFSET_MASK) >> 10)
116 #define DIMM_PRESENT_MASK (1 << 9)
117 #define DIMM_PRESENT(x) (((x) & DIMM_PRESENT_MASK) >> 9)
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300118 #define MC_DOD_NUMBANK_MASK ((1 << 8) | (1 << 7))
119 #define MC_DOD_NUMBANK(x) (((x) & MC_DOD_NUMBANK_MASK) >> 7)
120 #define MC_DOD_NUMRANK_MASK ((1 << 6) | (1 << 5))
121 #define MC_DOD_NUMRANK(x) (((x) & MC_DOD_NUMRANK_MASK) >> 5)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300122 #define MC_DOD_NUMROW_MASK ((1 << 4) | (1 << 3) | (1 << 2))
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300123 #define MC_DOD_NUMROW(x) (((x) & MC_DOD_NUMROW_MASK) >> 2)
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300124 #define MC_DOD_NUMCOL_MASK 3
125 #define MC_DOD_NUMCOL(x) ((x) & MC_DOD_NUMCOL_MASK)
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300126
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300127#define MC_RANK_PRESENT 0x7c
128
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300129#define MC_SAG_CH_0 0x80
130#define MC_SAG_CH_1 0x84
131#define MC_SAG_CH_2 0x88
132#define MC_SAG_CH_3 0x8c
133#define MC_SAG_CH_4 0x90
134#define MC_SAG_CH_5 0x94
135#define MC_SAG_CH_6 0x98
136#define MC_SAG_CH_7 0x9c
137
138#define MC_RIR_LIMIT_CH_0 0x40
139#define MC_RIR_LIMIT_CH_1 0x44
140#define MC_RIR_LIMIT_CH_2 0x48
141#define MC_RIR_LIMIT_CH_3 0x4C
142#define MC_RIR_LIMIT_CH_4 0x50
143#define MC_RIR_LIMIT_CH_5 0x54
144#define MC_RIR_LIMIT_CH_6 0x58
145#define MC_RIR_LIMIT_CH_7 0x5C
146#define MC_RIR_LIMIT_MASK ((1 << 10) - 1)
147
148#define MC_RIR_WAY_CH 0x80
149 #define MC_RIR_WAY_OFFSET_MASK (((1 << 14) - 1) & ~0x7)
150 #define MC_RIR_WAY_RANK_MASK 0x7
151
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300152/*
153 * i7core structs
154 */
155
156#define NUM_CHANS 3
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -0300157#define MAX_DIMMS 3 /* Max DIMMS per channel */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300158#define NUM_SOCKETS 2 /* Max number of MC sockets */
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -0300159#define MAX_MCR_FUNC 4
160#define MAX_CHAN_FUNC 3
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300161
162struct i7core_info {
163 u32 mc_control;
164 u32 mc_status;
165 u32 max_dod;
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300166 u32 ch_map;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300167};
168
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300169
170struct i7core_inject {
171 int enable;
172
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300173 u8 socket;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300174 u32 section;
175 u32 type;
176 u32 eccmask;
177
178 /* Error address mask */
179 int channel, dimm, rank, bank, page, col;
180};
181
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300182struct i7core_channel {
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -0300183 u32 ranks;
184 u32 dimms;
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300185};
186
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300187struct pci_id_descr {
188 int dev;
189 int func;
190 int dev_id;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300191 struct pci_dev *pdev[NUM_SOCKETS];
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300192};
193
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300194struct i7core_pvt {
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300195 struct pci_dev *pci_noncore[NUM_SOCKETS];
196 struct pci_dev *pci_mcr[NUM_SOCKETS][MAX_MCR_FUNC + 1];
197 struct pci_dev *pci_ch[NUM_SOCKETS][NUM_CHANS][MAX_CHAN_FUNC + 1];
198
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300199 struct i7core_info info;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300200 struct i7core_inject inject;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300201 struct i7core_channel channel[NUM_SOCKETS][NUM_CHANS];
202
203 int sockets; /* Number of sockets */
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300204 int channels; /* Number of active channels */
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -0300205
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300206 int ce_count_available[NUM_SOCKETS];
207 /* ECC corrected errors counts per dimm */
208 unsigned long ce_count[NUM_SOCKETS][MAX_DIMMS];
209 int last_ce_count[NUM_SOCKETS][MAX_DIMMS];
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -0300210
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -0300211 /* mcelog glue */
212 struct edac_mce edac_mce;
213 struct mce mce_entry[MCE_LOG_LEN];
214 unsigned mce_count;
215 spinlock_t mce_lock;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300216};
217
218/* Device name and register DID (Device ID) */
219struct i7core_dev_info {
220 const char *ctl_name; /* name for this device */
221 u16 fsb_mapping_errors; /* DID for the branchmap,control */
222};
223
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300224#define PCI_DESCR(device, function, device_id) \
225 .dev = (device), \
226 .func = (function), \
227 .dev_id = (device_id)
228
229struct pci_id_descr pci_devs[] = {
Mauro Carvalho Chehabd1fd4fb2009-07-10 18:39:53 -0300230 /* Generic Non-core registers */
231 { PCI_DESCR(0, 0, PCI_DEVICE_ID_INTEL_I7_NOCORE) },
232
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300233 /* Memory controller */
234 { PCI_DESCR(3, 0, PCI_DEVICE_ID_INTEL_I7_MCR) },
235 { PCI_DESCR(3, 1, PCI_DEVICE_ID_INTEL_I7_MC_TAD) },
236 { PCI_DESCR(3, 2, PCI_DEVICE_ID_INTEL_I7_MC_RAS) }, /* if RDIMM is supported */
237 { PCI_DESCR(3, 4, PCI_DEVICE_ID_INTEL_I7_MC_TEST) },
238
239 /* Channel 0 */
240 { PCI_DESCR(4, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH0_CTRL) },
241 { PCI_DESCR(4, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH0_ADDR) },
242 { PCI_DESCR(4, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH0_RANK) },
243 { PCI_DESCR(4, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH0_TC) },
244
245 /* Channel 1 */
246 { PCI_DESCR(5, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH1_CTRL) },
247 { PCI_DESCR(5, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH1_ADDR) },
248 { PCI_DESCR(5, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH1_RANK) },
249 { PCI_DESCR(5, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH1_TC) },
250
251 /* Channel 2 */
252 { PCI_DESCR(6, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH2_CTRL) },
253 { PCI_DESCR(6, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH2_ADDR) },
254 { PCI_DESCR(6, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH2_RANK) },
255 { PCI_DESCR(6, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH2_TC) },
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300256};
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300257#define N_DEVS ARRAY_SIZE(pci_devs)
258
259/*
260 * pci_device_id table for which devices we are looking for
261 * This should match the first device at pci_devs table
262 */
263static const struct pci_device_id i7core_pci_tbl[] __devinitdata = {
Mauro Carvalho Chehabd1fd4fb2009-07-10 18:39:53 -0300264 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_X58_HUB_MGMT)},
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300265 {0,} /* 0 terminated list. */
266};
267
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300268
269/* Table of devices attributes supported by this driver */
270static const struct i7core_dev_info i7core_devs[] = {
271 {
272 .ctl_name = "i7 Core",
273 .fsb_mapping_errors = PCI_DEVICE_ID_INTEL_I7_MCR,
274 },
275};
276
277static struct edac_pci_ctl_info *i7core_pci;
278
279/****************************************************************************
280 Anciliary status routines
281 ****************************************************************************/
282
283 /* MC_CONTROL bits */
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300284#define CH_ACTIVE(pvt, ch) ((pvt)->info.mc_control & (1 << (8 + ch)))
285#define ECCx8(pvt) ((pvt)->info.mc_control & (1 << 1))
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300286
287 /* MC_STATUS bits */
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300288#define ECC_ENABLED(pvt) ((pvt)->info.mc_status & (1 << 3))
289#define CH_DISABLED(pvt, ch) ((pvt)->info.mc_status & (1 << ch))
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300290
291 /* MC_MAX_DOD read functions */
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300292static inline int numdimms(u32 dimms)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300293{
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300294 return (dimms & 0x3) + 1;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300295}
296
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300297static inline int numrank(u32 rank)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300298{
299 static int ranks[4] = { 1, 2, 4, -EINVAL };
300
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300301 return ranks[rank & 0x3];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300302}
303
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300304static inline int numbank(u32 bank)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300305{
306 static int banks[4] = { 4, 8, 16, -EINVAL };
307
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300308 return banks[bank & 0x3];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300309}
310
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300311static inline int numrow(u32 row)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300312{
313 static int rows[8] = {
314 1 << 12, 1 << 13, 1 << 14, 1 << 15,
315 1 << 16, -EINVAL, -EINVAL, -EINVAL,
316 };
317
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300318 return rows[row & 0x7];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300319}
320
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300321static inline int numcol(u32 col)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300322{
323 static int cols[8] = {
324 1 << 10, 1 << 11, 1 << 12, -EINVAL,
325 };
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300326 return cols[col & 0x3];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300327}
328
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300329
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300330/****************************************************************************
331 Memory check routines
332 ****************************************************************************/
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300333static struct pci_dev *get_pdev_slot_func(u8 socket, unsigned slot,
334 unsigned func)
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300335{
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300336 int i;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300337
338 for (i = 0; i < N_DEVS; i++) {
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300339 if (!pci_devs[i].pdev[socket])
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300340 continue;
341
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300342 if (PCI_SLOT(pci_devs[i].pdev[socket]->devfn) == slot &&
343 PCI_FUNC(pci_devs[i].pdev[socket]->devfn) == func) {
344 return pci_devs[i].pdev[socket];
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300345 }
346 }
347
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300348 return NULL;
349}
350
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300351static int i7core_get_active_channels(u8 socket, unsigned *channels,
352 unsigned *csrows)
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300353{
354 struct pci_dev *pdev = NULL;
355 int i, j;
356 u32 status, control;
357
358 *channels = 0;
359 *csrows = 0;
360
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300361 pdev = get_pdev_slot_func(socket, 3, 0);
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -0300362 if (!pdev) {
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300363 i7core_printk(KERN_ERR, "Couldn't find socket %d fn 3.0!!!\n",
364 socket);
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300365 return -ENODEV;
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -0300366 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300367
368 /* Device 3 function 0 reads */
369 pci_read_config_dword(pdev, MC_STATUS, &status);
370 pci_read_config_dword(pdev, MC_CONTROL, &control);
371
372 for (i = 0; i < NUM_CHANS; i++) {
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300373 u32 dimm_dod[3];
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300374 /* Check if the channel is active */
375 if (!(control & (1 << (8 + i))))
376 continue;
377
378 /* Check if the channel is disabled */
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300379 if (status & (1 << i))
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300380 continue;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300381
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300382 pdev = get_pdev_slot_func(socket, i + 4, 1);
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300383 if (!pdev) {
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300384 i7core_printk(KERN_ERR, "Couldn't find socket %d "
385 "fn %d.%d!!!\n",
386 socket, i + 4, 1);
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300387 return -ENODEV;
388 }
389 /* Devices 4-6 function 1 */
390 pci_read_config_dword(pdev,
391 MC_DOD_CH_DIMM0, &dimm_dod[0]);
392 pci_read_config_dword(pdev,
393 MC_DOD_CH_DIMM1, &dimm_dod[1]);
394 pci_read_config_dword(pdev,
395 MC_DOD_CH_DIMM2, &dimm_dod[2]);
396
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300397 (*channels)++;
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300398
399 for (j = 0; j < 3; j++) {
400 if (!DIMM_PRESENT(dimm_dod[j]))
401 continue;
402 (*csrows)++;
403 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300404 }
405
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300406 debugf0("Number of active channels on socked %d: %d\n",
407 socket, *channels);
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300408
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300409 return 0;
410}
411
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -0300412static int get_dimm_config(struct mem_ctl_info *mci, int *csrow, u8 socket)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300413{
414 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300415 struct csrow_info *csr;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300416 struct pci_dev *pdev;
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -0300417 int i, j;
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300418 unsigned long last_page = 0;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300419 enum edac_type mode;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300420 enum mem_type mtype;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300421
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300422 /* Get data from the MC register, function 0 */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300423 pdev = pvt->pci_mcr[socket][0];
Mauro Carvalho Chehab7dd69532009-06-22 22:48:30 -0300424 if (!pdev)
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300425 return -ENODEV;
426
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300427 /* Device 3 function 0 reads */
Mauro Carvalho Chehab7dd69532009-06-22 22:48:30 -0300428 pci_read_config_dword(pdev, MC_CONTROL, &pvt->info.mc_control);
429 pci_read_config_dword(pdev, MC_STATUS, &pvt->info.mc_status);
430 pci_read_config_dword(pdev, MC_MAX_DOD, &pvt->info.max_dod);
431 pci_read_config_dword(pdev, MC_CHANNEL_MAPPER, &pvt->info.ch_map);
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300432
433 debugf0("MC control=0x%08x status=0x%08x dod=0x%08x map=0x%08x\n",
434 pvt->info.mc_control, pvt->info.mc_status,
435 pvt->info.max_dod, pvt->info.ch_map);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300436
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300437 if (ECC_ENABLED(pvt)) {
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300438 debugf0("ECC enabled with x%d SDCC\n", ECCx8(pvt) ? 8 : 4);
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300439 if (ECCx8(pvt))
440 mode = EDAC_S8ECD8ED;
441 else
442 mode = EDAC_S4ECD4ED;
443 } else {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300444 debugf0("ECC disabled\n");
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300445 mode = EDAC_NONE;
446 }
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300447
448 /* FIXME: need to handle the error codes */
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300449 debugf0("DOD Max limits: DIMMS: %d, %d-ranked, %d-banked\n",
450 numdimms(pvt->info.max_dod),
451 numrank(pvt->info.max_dod >> 2),
452 numbank(pvt->info.max_dod >> 4));
453 debugf0("DOD Max rows x colums = 0x%x x 0x%x\n",
454 numrow(pvt->info.max_dod >> 6),
455 numcol(pvt->info.max_dod >> 9));
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300456
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300457 debugf0("Memory channel configuration:\n");
458
459 for (i = 0; i < NUM_CHANS; i++) {
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300460 u32 data, dimm_dod[3], value[8];
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300461
462 if (!CH_ACTIVE(pvt, i)) {
463 debugf0("Channel %i is not active\n", i);
464 continue;
465 }
466 if (CH_DISABLED(pvt, i)) {
467 debugf0("Channel %i is disabled\n", i);
468 continue;
469 }
470
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300471 /* Devices 4-6 function 0 */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300472 pci_read_config_dword(pvt->pci_ch[socket][i][0],
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300473 MC_CHANNEL_DIMM_INIT_PARAMS, &data);
474
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300475 pvt->channel[socket][i].ranks = (data & QUAD_RANK_PRESENT) ?
476 4 : 2;
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300477
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300478 if (data & REGISTERED_DIMM)
479 mtype = MEM_RDDR3;
480 else
481 mtype = MEM_DDR3;
482#if 0
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300483 if (data & THREE_DIMMS_PRESENT)
484 pvt->channel[i].dimms = 3;
485 else if (data & SINGLE_QUAD_RANK_PRESENT)
486 pvt->channel[i].dimms = 1;
487 else
488 pvt->channel[i].dimms = 2;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300489#endif
490
491 /* Devices 4-6 function 1 */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300492 pci_read_config_dword(pvt->pci_ch[socket][i][1],
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300493 MC_DOD_CH_DIMM0, &dimm_dod[0]);
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300494 pci_read_config_dword(pvt->pci_ch[socket][i][1],
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300495 MC_DOD_CH_DIMM1, &dimm_dod[1]);
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300496 pci_read_config_dword(pvt->pci_ch[socket][i][1],
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300497 MC_DOD_CH_DIMM2, &dimm_dod[2]);
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300498
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300499 debugf0("Ch%d phy rd%d, wr%d (0x%08x): "
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300500 "%d ranks, %cDIMMs\n",
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300501 i,
502 RDLCH(pvt->info.ch_map, i), WRLCH(pvt->info.ch_map, i),
503 data,
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300504 pvt->channel[socket][i].ranks,
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300505 (data & REGISTERED_DIMM) ? 'R' : 'U');
Mauro Carvalho Chehab7dd69532009-06-22 22:48:30 -0300506
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300507 for (j = 0; j < 3; j++) {
508 u32 banks, ranks, rows, cols;
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300509 u32 size, npages;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300510
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300511 if (!DIMM_PRESENT(dimm_dod[j]))
512 continue;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300513
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300514 banks = numbank(MC_DOD_NUMBANK(dimm_dod[j]));
515 ranks = numrank(MC_DOD_NUMRANK(dimm_dod[j]));
516 rows = numrow(MC_DOD_NUMROW(dimm_dod[j]));
517 cols = numcol(MC_DOD_NUMCOL(dimm_dod[j]));
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300518
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300519 /* DDR3 has 8 I/O banks */
520 size = (rows * cols * banks * ranks) >> (20 - 3);
521
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300522 pvt->channel[socket][i].dimms++;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300523
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300524 debugf0("\tdimm %d (0x%08x) %d Mb offset: %x, "
525 "numbank: %d,\n\t\t"
526 "numrank: %d, numrow: %#x, numcol: %#x\n",
527 j, dimm_dod[j], size,
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300528 RANKOFFSET(dimm_dod[j]),
529 banks, ranks, rows, cols);
530
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300531#if PAGE_SHIFT > 20
532 npages = size >> (PAGE_SHIFT - 20);
533#else
534 npages = size << (20 - PAGE_SHIFT);
535#endif
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300536
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -0300537 csr = &mci->csrows[*csrow];
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300538 csr->first_page = last_page + 1;
539 last_page += npages;
540 csr->last_page = last_page;
541 csr->nr_pages = npages;
542
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300543 csr->page_mask = 0;
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300544 csr->grain = 8;
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -0300545 csr->csrow_idx = *csrow;
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300546 csr->nr_channels = 1;
547
548 csr->channels[0].chan_idx = i;
549 csr->channels[0].ce_count = 0;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300550
551 switch (banks) {
552 case 4:
553 csr->dtype = DEV_X4;
554 break;
555 case 8:
556 csr->dtype = DEV_X8;
557 break;
558 case 16:
559 csr->dtype = DEV_X16;
560 break;
561 default:
562 csr->dtype = DEV_UNKNOWN;
563 }
564
565 csr->edac_mode = mode;
566 csr->mtype = mtype;
567
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -0300568 (*csrow)++;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300569 }
570
571 pci_read_config_dword(pdev, MC_SAG_CH_0, &value[0]);
572 pci_read_config_dword(pdev, MC_SAG_CH_1, &value[1]);
573 pci_read_config_dword(pdev, MC_SAG_CH_2, &value[2]);
574 pci_read_config_dword(pdev, MC_SAG_CH_3, &value[3]);
575 pci_read_config_dword(pdev, MC_SAG_CH_4, &value[4]);
576 pci_read_config_dword(pdev, MC_SAG_CH_5, &value[5]);
577 pci_read_config_dword(pdev, MC_SAG_CH_6, &value[6]);
578 pci_read_config_dword(pdev, MC_SAG_CH_7, &value[7]);
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300579 debugf0("\t[%i] DIVBY3\tREMOVED\tOFFSET\n", i);
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300580 for (j = 0; j < 8; j++)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300581 debugf0("\t\t%#x\t%#x\t%#x\n",
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300582 (value[j] >> 27) & 0x1,
583 (value[j] >> 24) & 0x7,
584 (value[j] && ((1 << 24) - 1)));
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300585 }
586
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300587 return 0;
588}
589
590/****************************************************************************
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300591 Error insertion routines
592 ****************************************************************************/
593
594/* The i7core has independent error injection features per channel.
595 However, to have a simpler code, we don't allow enabling error injection
596 on more than one channel.
597 Also, since a change at an inject parameter will be applied only at enable,
598 we're disabling error injection on all write calls to the sysfs nodes that
599 controls the error code injection.
600 */
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300601static int disable_inject(struct mem_ctl_info *mci)
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300602{
603 struct i7core_pvt *pvt = mci->pvt_info;
604
605 pvt->inject.enable = 0;
606
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300607 if (!pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0])
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300608 return -ENODEV;
609
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300610 pci_write_config_dword(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0],
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300611 MC_CHANNEL_ERROR_MASK, 0);
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300612
613 return 0;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300614}
615
616/*
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300617 * i7core inject inject.socket
618 *
619 * accept and store error injection inject.socket value
620 */
621static ssize_t i7core_inject_socket_store(struct mem_ctl_info *mci,
622 const char *data, size_t count)
623{
624 struct i7core_pvt *pvt = mci->pvt_info;
625 unsigned long value;
626 int rc;
627
628 rc = strict_strtoul(data, 10, &value);
629 if ((rc < 0) || (value > pvt->sockets))
630 return 0;
631
632 pvt->inject.section = (u32) value;
633 return count;
634}
635
636static ssize_t i7core_inject_socket_show(struct mem_ctl_info *mci,
637 char *data)
638{
639 struct i7core_pvt *pvt = mci->pvt_info;
640 return sprintf(data, "%d\n", pvt->inject.socket);
641}
642
643/*
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300644 * i7core inject inject.section
645 *
646 * accept and store error injection inject.section value
647 * bit 0 - refers to the lower 32-byte half cacheline
648 * bit 1 - refers to the upper 32-byte half cacheline
649 */
650static ssize_t i7core_inject_section_store(struct mem_ctl_info *mci,
651 const char *data, size_t count)
652{
653 struct i7core_pvt *pvt = mci->pvt_info;
654 unsigned long value;
655 int rc;
656
657 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300658 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300659
660 rc = strict_strtoul(data, 10, &value);
661 if ((rc < 0) || (value > 3))
662 return 0;
663
664 pvt->inject.section = (u32) value;
665 return count;
666}
667
668static ssize_t i7core_inject_section_show(struct mem_ctl_info *mci,
669 char *data)
670{
671 struct i7core_pvt *pvt = mci->pvt_info;
672 return sprintf(data, "0x%08x\n", pvt->inject.section);
673}
674
675/*
676 * i7core inject.type
677 *
678 * accept and store error injection inject.section value
679 * bit 0 - repeat enable - Enable error repetition
680 * bit 1 - inject ECC error
681 * bit 2 - inject parity error
682 */
683static ssize_t i7core_inject_type_store(struct mem_ctl_info *mci,
684 const char *data, size_t count)
685{
686 struct i7core_pvt *pvt = mci->pvt_info;
687 unsigned long value;
688 int rc;
689
690 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300691 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300692
693 rc = strict_strtoul(data, 10, &value);
694 if ((rc < 0) || (value > 7))
695 return 0;
696
697 pvt->inject.type = (u32) value;
698 return count;
699}
700
701static ssize_t i7core_inject_type_show(struct mem_ctl_info *mci,
702 char *data)
703{
704 struct i7core_pvt *pvt = mci->pvt_info;
705 return sprintf(data, "0x%08x\n", pvt->inject.type);
706}
707
708/*
709 * i7core_inject_inject.eccmask_store
710 *
711 * The type of error (UE/CE) will depend on the inject.eccmask value:
712 * Any bits set to a 1 will flip the corresponding ECC bit
713 * Correctable errors can be injected by flipping 1 bit or the bits within
714 * a symbol pair (2 consecutive aligned 8-bit pairs - i.e. 7:0 and 15:8 or
715 * 23:16 and 31:24). Flipping bits in two symbol pairs will cause an
716 * uncorrectable error to be injected.
717 */
718static ssize_t i7core_inject_eccmask_store(struct mem_ctl_info *mci,
719 const char *data, size_t count)
720{
721 struct i7core_pvt *pvt = mci->pvt_info;
722 unsigned long value;
723 int rc;
724
725 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300726 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300727
728 rc = strict_strtoul(data, 10, &value);
729 if (rc < 0)
730 return 0;
731
732 pvt->inject.eccmask = (u32) value;
733 return count;
734}
735
736static ssize_t i7core_inject_eccmask_show(struct mem_ctl_info *mci,
737 char *data)
738{
739 struct i7core_pvt *pvt = mci->pvt_info;
740 return sprintf(data, "0x%08x\n", pvt->inject.eccmask);
741}
742
743/*
744 * i7core_addrmatch
745 *
746 * The type of error (UE/CE) will depend on the inject.eccmask value:
747 * Any bits set to a 1 will flip the corresponding ECC bit
748 * Correctable errors can be injected by flipping 1 bit or the bits within
749 * a symbol pair (2 consecutive aligned 8-bit pairs - i.e. 7:0 and 15:8 or
750 * 23:16 and 31:24). Flipping bits in two symbol pairs will cause an
751 * uncorrectable error to be injected.
752 */
753static ssize_t i7core_inject_addrmatch_store(struct mem_ctl_info *mci,
754 const char *data, size_t count)
755{
756 struct i7core_pvt *pvt = mci->pvt_info;
757 char *cmd, *val;
758 long value;
759 int rc;
760
761 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300762 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300763
764 do {
765 cmd = strsep((char **) &data, ":");
766 if (!cmd)
767 break;
768 val = strsep((char **) &data, " \n\t");
769 if (!val)
770 return cmd - data;
771
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300772 if (!strcasecmp(val, "any"))
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300773 value = -1;
774 else {
775 rc = strict_strtol(val, 10, &value);
776 if ((rc < 0) || (value < 0))
777 return cmd - data;
778 }
779
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300780 if (!strcasecmp(cmd, "channel")) {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300781 if (value < 3)
782 pvt->inject.channel = value;
783 else
784 return cmd - data;
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300785 } else if (!strcasecmp(cmd, "dimm")) {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300786 if (value < 4)
787 pvt->inject.dimm = value;
788 else
789 return cmd - data;
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300790 } else if (!strcasecmp(cmd, "rank")) {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300791 if (value < 4)
792 pvt->inject.rank = value;
793 else
794 return cmd - data;
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300795 } else if (!strcasecmp(cmd, "bank")) {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300796 if (value < 4)
797 pvt->inject.bank = value;
798 else
799 return cmd - data;
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300800 } else if (!strcasecmp(cmd, "page")) {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300801 if (value <= 0xffff)
802 pvt->inject.page = value;
803 else
804 return cmd - data;
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300805 } else if (!strcasecmp(cmd, "col") ||
806 !strcasecmp(cmd, "column")) {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300807 if (value <= 0x3fff)
808 pvt->inject.col = value;
809 else
810 return cmd - data;
811 }
812 } while (1);
813
814 return count;
815}
816
817static ssize_t i7core_inject_addrmatch_show(struct mem_ctl_info *mci,
818 char *data)
819{
820 struct i7core_pvt *pvt = mci->pvt_info;
821 char channel[4], dimm[4], bank[4], rank[4], page[7], col[7];
822
823 if (pvt->inject.channel < 0)
824 sprintf(channel, "any");
825 else
826 sprintf(channel, "%d", pvt->inject.channel);
827 if (pvt->inject.dimm < 0)
828 sprintf(dimm, "any");
829 else
830 sprintf(dimm, "%d", pvt->inject.dimm);
831 if (pvt->inject.bank < 0)
832 sprintf(bank, "any");
833 else
834 sprintf(bank, "%d", pvt->inject.bank);
835 if (pvt->inject.rank < 0)
836 sprintf(rank, "any");
837 else
838 sprintf(rank, "%d", pvt->inject.rank);
839 if (pvt->inject.page < 0)
840 sprintf(page, "any");
841 else
842 sprintf(page, "0x%04x", pvt->inject.page);
843 if (pvt->inject.col < 0)
844 sprintf(col, "any");
845 else
846 sprintf(col, "0x%04x", pvt->inject.col);
847
848 return sprintf(data, "channel: %s\ndimm: %s\nbank: %s\n"
849 "rank: %s\npage: %s\ncolumn: %s\n",
850 channel, dimm, bank, rank, page, col);
851}
852
853/*
854 * This routine prepares the Memory Controller for error injection.
855 * The error will be injected when some process tries to write to the
856 * memory that matches the given criteria.
857 * The criteria can be set in terms of a mask where dimm, rank, bank, page
858 * and col can be specified.
859 * A -1 value for any of the mask items will make the MCU to ignore
860 * that matching criteria for error injection.
861 *
862 * It should be noticed that the error will only happen after a write operation
863 * on a memory that matches the condition. if REPEAT_EN is not enabled at
864 * inject mask, then it will produce just one error. Otherwise, it will repeat
865 * until the injectmask would be cleaned.
866 *
867 * FIXME: This routine assumes that MAXNUMDIMMS value of MC_MAX_DOD
868 * is reliable enough to check if the MC is using the
869 * three channels. However, this is not clear at the datasheet.
870 */
871static ssize_t i7core_inject_enable_store(struct mem_ctl_info *mci,
872 const char *data, size_t count)
873{
874 struct i7core_pvt *pvt = mci->pvt_info;
875 u32 injectmask;
876 u64 mask = 0;
877 int rc;
878 long enable;
879
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300880 if (!pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0])
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300881 return 0;
882
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300883 rc = strict_strtoul(data, 10, &enable);
884 if ((rc < 0))
885 return 0;
886
887 if (enable) {
888 pvt->inject.enable = 1;
889 } else {
890 disable_inject(mci);
891 return count;
892 }
893
894 /* Sets pvt->inject.dimm mask */
895 if (pvt->inject.dimm < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300896 mask |= 1L << 41;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300897 else {
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300898 if (pvt->channel[pvt->inject.socket][pvt->inject.channel].dimms > 2)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300899 mask |= (pvt->inject.dimm & 0x3L) << 35;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300900 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300901 mask |= (pvt->inject.dimm & 0x1L) << 36;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300902 }
903
904 /* Sets pvt->inject.rank mask */
905 if (pvt->inject.rank < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300906 mask |= 1L << 40;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300907 else {
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300908 if (pvt->channel[pvt->inject.socket][pvt->inject.channel].dimms > 2)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300909 mask |= (pvt->inject.rank & 0x1L) << 34;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300910 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300911 mask |= (pvt->inject.rank & 0x3L) << 34;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300912 }
913
914 /* Sets pvt->inject.bank mask */
915 if (pvt->inject.bank < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300916 mask |= 1L << 39;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300917 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300918 mask |= (pvt->inject.bank & 0x15L) << 30;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300919
920 /* Sets pvt->inject.page mask */
921 if (pvt->inject.page < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300922 mask |= 1L << 38;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300923 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300924 mask |= (pvt->inject.page & 0xffffL) << 14;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300925
926 /* Sets pvt->inject.column mask */
927 if (pvt->inject.col < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300928 mask |= 1L << 37;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300929 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300930 mask |= (pvt->inject.col & 0x3fffL);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300931
Mauro Carvalho Chehabe9bd2e72009-07-09 22:14:35 -0300932 /* Unlock writes to registers */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300933 pci_write_config_dword(pvt->pci_noncore[pvt->inject.socket],
934 MC_CFG_CONTROL, 0x2);
Mauro Carvalho Chehabe9bd2e72009-07-09 22:14:35 -0300935 msleep(100);
936
937 /* Zeroes error count registers */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300938 pci_write_config_dword(pvt->pci_mcr[pvt->inject.socket][4],
939 MC_TEST_ERR_RCV1, 0);
940 pci_write_config_dword(pvt->pci_mcr[pvt->inject.socket][4],
941 MC_TEST_ERR_RCV0, 0);
942 pvt->ce_count_available[pvt->inject.socket] = 0;
Mauro Carvalho Chehabe9bd2e72009-07-09 22:14:35 -0300943
944
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300945#if USE_QWORD
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300946 pci_write_config_qword(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0],
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300947 MC_CHANNEL_ADDR_MATCH, mask);
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300948#else
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300949 pci_write_config_dword(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0],
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300950 MC_CHANNEL_ADDR_MATCH, mask);
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300951 pci_write_config_dword(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0],
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300952 MC_CHANNEL_ADDR_MATCH + 4, mask >> 32L);
953#endif
954
955#if 1
956#if USE_QWORD
957 u64 rdmask;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300958 pci_read_config_qword(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0],
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300959 MC_CHANNEL_ADDR_MATCH, &rdmask);
960 debugf0("Inject addr match write 0x%016llx, read: 0x%016llx\n",
961 mask, rdmask);
962#else
963 u32 rdmask1, rdmask2;
964
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300965 pci_read_config_dword(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0],
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300966 MC_CHANNEL_ADDR_MATCH, &rdmask1);
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300967 pci_read_config_dword(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0],
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300968 MC_CHANNEL_ADDR_MATCH + 4, &rdmask2);
969
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -0300970 debugf0("Inject addr match write 0x%016llx, read: 0x%08x 0x%08x\n",
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300971 mask, rdmask1, rdmask2);
972#endif
973#endif
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300974
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300975 pci_write_config_dword(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0],
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300976 MC_CHANNEL_ERROR_MASK, pvt->inject.eccmask);
977
978 /*
979 * bit 0: REPEAT_EN
980 * bits 1-2: MASK_HALF_CACHELINE
981 * bit 3: INJECT_ECC
982 * bit 4: INJECT_ADDR_PARITY
983 */
984
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300985 injectmask = (pvt->inject.type & 1) |
986 (pvt->inject.section & 0x3) << 1 |
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300987 (pvt->inject.type & 0x6) << (3 - 1);
988
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300989 pci_write_config_dword(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0],
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300990 MC_CHANNEL_ERROR_MASK, injectmask);
991
Mauro Carvalho Chehabe9bd2e72009-07-09 22:14:35 -0300992#if 0
993 /* lock writes to registers */
994 pci_write_config_dword(pvt->pci_noncore, MC_CFG_CONTROL, 0);
995#endif
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300996 debugf0("Error inject addr match 0x%016llx, ecc 0x%08x,"
997 " inject 0x%08x\n",
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300998 mask, pvt->inject.eccmask, injectmask);
999
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -03001000
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001001 return count;
1002}
1003
1004static ssize_t i7core_inject_enable_show(struct mem_ctl_info *mci,
1005 char *data)
1006{
1007 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -03001008 u32 injectmask;
1009
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001010 pci_read_config_dword(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0],
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -03001011 MC_CHANNEL_ERROR_MASK, &injectmask);
1012
1013 debugf0("Inject error read: 0x%018x\n", injectmask);
1014
1015 if (injectmask & 0x0c)
1016 pvt->inject.enable = 1;
1017
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001018 return sprintf(data, "%d\n", pvt->inject.enable);
1019}
1020
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001021static ssize_t i7core_ce_regs_show(struct mem_ctl_info *mci, char *data)
1022{
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001023 unsigned i, count, total = 0;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001024 struct i7core_pvt *pvt = mci->pvt_info;
1025
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001026 for (i = 0; i < pvt->sockets; i++) {
1027 if (!pvt->ce_count_available[i])
1028 count = sprintf(data, "socket 0 data unavailable\n");
1029 else
1030 count = sprintf(data, "socket %d, dimm0: %lu\n"
1031 "dimm1: %lu\ndimm2: %lu\n",
1032 i,
1033 pvt->ce_count[i][0],
1034 pvt->ce_count[i][1],
1035 pvt->ce_count[i][2]);
1036 data += count;
1037 total += count;
1038 }
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001039
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001040 return total;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001041}
1042
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001043/*
1044 * Sysfs struct
1045 */
1046static struct mcidev_sysfs_attribute i7core_inj_attrs[] = {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001047 {
1048 .attr = {
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001049 .name = "inject_socket",
1050 .mode = (S_IRUGO | S_IWUSR)
1051 },
1052 .show = i7core_inject_socket_show,
1053 .store = i7core_inject_socket_store,
1054 }, {
1055 .attr = {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001056 .name = "inject_section",
1057 .mode = (S_IRUGO | S_IWUSR)
1058 },
1059 .show = i7core_inject_section_show,
1060 .store = i7core_inject_section_store,
1061 }, {
1062 .attr = {
1063 .name = "inject_type",
1064 .mode = (S_IRUGO | S_IWUSR)
1065 },
1066 .show = i7core_inject_type_show,
1067 .store = i7core_inject_type_store,
1068 }, {
1069 .attr = {
1070 .name = "inject_eccmask",
1071 .mode = (S_IRUGO | S_IWUSR)
1072 },
1073 .show = i7core_inject_eccmask_show,
1074 .store = i7core_inject_eccmask_store,
1075 }, {
1076 .attr = {
1077 .name = "inject_addrmatch",
1078 .mode = (S_IRUGO | S_IWUSR)
1079 },
1080 .show = i7core_inject_addrmatch_show,
1081 .store = i7core_inject_addrmatch_store,
1082 }, {
1083 .attr = {
1084 .name = "inject_enable",
1085 .mode = (S_IRUGO | S_IWUSR)
1086 },
1087 .show = i7core_inject_enable_show,
1088 .store = i7core_inject_enable_store,
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001089 }, {
1090 .attr = {
1091 .name = "corrected_error_counts",
1092 .mode = (S_IRUGO | S_IWUSR)
1093 },
1094 .show = i7core_ce_regs_show,
1095 .store = NULL,
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001096 },
1097};
1098
1099/****************************************************************************
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001100 Device initialization routines: put/get, init/exit
1101 ****************************************************************************/
1102
1103/*
1104 * i7core_put_devices 'put' all the devices that we have
1105 * reserved via 'get'
1106 */
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001107static void i7core_put_devices(void)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001108{
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001109 int i, j;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001110
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001111 for (i = 0; i < NUM_SOCKETS; i++)
1112 for (j = 0; j < N_DEVS; j++)
1113 pci_dev_put(pci_devs[j].pdev[i]);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001114}
1115
1116/*
1117 * i7core_get_devices Find and perform 'get' operation on the MCH's
1118 * device/functions we want to reference for this driver
1119 *
1120 * Need to 'get' device 16 func 1 and func 2
1121 */
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001122static int i7core_get_devices(void)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001123{
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001124 int rc, i;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001125 struct pci_dev *pdev = NULL;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001126 u8 bus = 0;
1127 u8 socket = 0;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001128
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001129 for (i = 0; i < N_DEVS; i++) {
1130 pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
1131 pci_devs[i].dev_id, NULL);
Mauro Carvalho Chehabd1fd4fb2009-07-10 18:39:53 -03001132
1133 if (!pdev && !i) {
1134 pcibios_scan_specific_bus(254);
1135 pcibios_scan_specific_bus(255);
1136
1137 pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
1138 pci_devs[i].dev_id, NULL);
1139 }
1140
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001141 if (likely(pdev)) {
1142 bus = pdev->bus->number;
1143
1144 if (bus == 0x3f)
1145 socket = 0;
1146 else
1147 socket = 255 - bus;
1148
1149 if (socket >= NUM_SOCKETS) {
1150 i7core_printk(KERN_ERR,
1151 "Found unexpected socket for "
1152 "dev %02x:%02x.%d PCI ID %04x:%04x\n",
1153 bus, pci_devs[i].dev, pci_devs[i].func,
1154 PCI_VENDOR_ID_INTEL, pci_devs[i].dev_id);
1155
1156 rc = -ENODEV;
1157 goto error;
1158 }
1159
1160 pci_devs[i].pdev[socket] = pdev;
1161 } else {
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001162 i7core_printk(KERN_ERR,
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001163 "Device not found: "
1164 "dev %02x:%02x.%d PCI ID %04x:%04x\n",
1165 bus, pci_devs[i].dev, pci_devs[i].func,
1166 PCI_VENDOR_ID_INTEL, pci_devs[i].dev_id);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001167
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001168 /* Dev 3 function 2 only exists on chips with RDIMMs */
1169 if ((pci_devs[i].dev == 3) && (pci_devs[i].func == 2))
1170 continue;
1171
1172 /* End of list, leave */
1173 rc = -ENODEV;
1174 goto error;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001175 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001176
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001177 /* Sanity check */
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001178 if (unlikely(PCI_SLOT(pdev->devfn) != pci_devs[i].dev ||
1179 PCI_FUNC(pdev->devfn) != pci_devs[i].func)) {
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001180 i7core_printk(KERN_ERR,
1181 "Device PCI ID %04x:%04x "
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001182 "has fn %d.%d instead of fn %d.%d\n",
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001183 PCI_VENDOR_ID_INTEL, pci_devs[i].dev_id,
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001184 PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
1185 pci_devs[i].dev, pci_devs[i].func);
1186 rc = -EINVAL;
1187 goto error;
1188 }
1189
1190 /* Be sure that the device is enabled */
1191 rc = pci_enable_device(pdev);
1192 if (unlikely(rc < 0)) {
1193 i7core_printk(KERN_ERR,
1194 "Couldn't enable PCI ID %04x:%04x "
1195 "fn %d.%d\n",
1196 PCI_VENDOR_ID_INTEL, pci_devs[i].dev_id,
1197 PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
1198 goto error;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001199 }
1200
1201 i7core_printk(KERN_INFO,
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001202 "Registered socket %d "
1203 "dev %02x:%02x.%d PCI ID %04x:%04x\n",
1204 socket, bus, pci_devs[i].dev, pci_devs[i].func,
1205 PCI_VENDOR_ID_INTEL, pci_devs[i].dev_id);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001206 }
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001207
1208 return 0;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001209
1210error:
1211 i7core_put_devices();
1212 return -EINVAL;
1213}
1214
1215static int mci_bind_devs(struct mem_ctl_info *mci)
1216{
1217 struct i7core_pvt *pvt = mci->pvt_info;
1218 struct pci_dev *pdev;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001219 int i, j, func, slot;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001220
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001221 for (i = 0; i < pvt->sockets; i++) {
1222 for (j = 0; j < N_DEVS; j++) {
1223 pdev = pci_devs[j].pdev[i];
1224 if (!pdev)
1225 continue;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001226
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001227 func = PCI_FUNC(pdev->devfn);
1228 slot = PCI_SLOT(pdev->devfn);
1229 if (slot == 3) {
1230 if (unlikely(func > MAX_MCR_FUNC))
1231 goto error;
1232 pvt->pci_mcr[i][func] = pdev;
1233 } else if (likely(slot >= 4 && slot < 4 + NUM_CHANS)) {
1234 if (unlikely(func > MAX_CHAN_FUNC))
1235 goto error;
1236 pvt->pci_ch[i][slot - 4][func] = pdev;
1237 } else if (!slot && !func)
1238 pvt->pci_noncore[i] = pdev;
1239 else
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001240 goto error;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001241
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001242 debugf0("Associated fn %d.%d, dev = %p, socket %d\n",
1243 PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
1244 pdev, i);
1245 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001246 }
Mauro Carvalho Chehabe9bd2e72009-07-09 22:14:35 -03001247
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001248 return 0;
1249
1250error:
1251 i7core_printk(KERN_ERR, "Device %d, function %d "
1252 "is out of the expected range\n",
1253 slot, func);
1254 return -EINVAL;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001255}
1256
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001257/****************************************************************************
1258 Error check routines
1259 ****************************************************************************/
1260
1261/* This function is based on the device 3 function 4 registers as described on:
1262 * Intel Xeon Processor 5500 Series Datasheet Volume 2
1263 * http://www.intel.com/Assets/PDF/datasheet/321322.pdf
1264 * also available at:
1265 * http://www.arrownac.com/manufacturers/intel/s/nehalem/5500-datasheet-v2.pdf
1266 */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001267static void check_mc_test_err(struct mem_ctl_info *mci, u8 socket)
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001268{
1269 struct i7core_pvt *pvt = mci->pvt_info;
1270 u32 rcv1, rcv0;
1271 int new0, new1, new2;
1272
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001273 if (!pvt->pci_mcr[socket][4]) {
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001274 debugf0("%s MCR registers not found\n",__func__);
1275 return;
1276 }
1277
1278 /* Corrected error reads */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001279 pci_read_config_dword(pvt->pci_mcr[socket][4], MC_TEST_ERR_RCV1, &rcv1);
1280 pci_read_config_dword(pvt->pci_mcr[socket][4], MC_TEST_ERR_RCV0, &rcv0);
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001281
1282 /* Store the new values */
1283 new2 = DIMM2_COR_ERR(rcv1);
1284 new1 = DIMM1_COR_ERR(rcv0);
1285 new0 = DIMM0_COR_ERR(rcv0);
1286
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001287#if 0
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001288 debugf2("%s CE rcv1=0x%08x rcv0=0x%08x, %d %d %d\n",
1289 (pvt->ce_count_available ? "UPDATE" : "READ"),
1290 rcv1, rcv0, new0, new1, new2);
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001291#endif
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001292
1293 /* Updates CE counters if it is not the first time here */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001294 if (pvt->ce_count_available[socket]) {
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001295 /* Updates CE counters */
1296 int add0, add1, add2;
1297
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001298 add2 = new2 - pvt->last_ce_count[socket][2];
1299 add1 = new1 - pvt->last_ce_count[socket][1];
1300 add0 = new0 - pvt->last_ce_count[socket][0];
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001301
1302 if (add2 < 0)
1303 add2 += 0x7fff;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001304 pvt->ce_count[socket][2] += add2;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001305
1306 if (add1 < 0)
1307 add1 += 0x7fff;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001308 pvt->ce_count[socket][1] += add1;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001309
1310 if (add0 < 0)
1311 add0 += 0x7fff;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001312 pvt->ce_count[socket][0] += add0;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001313 } else
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001314 pvt->ce_count_available[socket] = 1;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001315
1316 /* Store the new values */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001317 pvt->last_ce_count[socket][2] = new2;
1318 pvt->last_ce_count[socket][1] = new1;
1319 pvt->last_ce_count[socket][0] = new0;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001320}
1321
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001322static void i7core_mce_output_error(struct mem_ctl_info *mci,
1323 struct mce *m)
1324{
1325 debugf0("CPU %d: Machine Check Exception: %16Lx"
1326 "Bank %d: %016Lx\n",
1327 m->cpu, m->mcgstatus, m->bank, m->status);
1328 if (m->ip) {
1329 debugf0("RIP%s %02x:<%016Lx>\n",
1330 !(m->mcgstatus & MCG_STATUS_EIPV) ? " !INEXACT!" : "",
1331 m->cs, m->ip);
1332 }
1333 printk(KERN_EMERG "TSC %llx ", m->tsc);
1334 if (m->addr)
1335 printk("ADDR %llx ", m->addr);
1336 if (m->misc)
1337 printk("MISC %llx ", m->misc);
1338
1339#if 0
1340 snprintf(msg, sizeof(msg),
1341 "%s (Branch=%d DRAM-Bank=%d Buffer ID = %d RDWR=%s "
1342 "RAS=%d CAS=%d %s Err=0x%lx (%s))",
1343 type, branch >> 1, bank, buf_id, rdwr_str(rdwr), ras, cas,
1344 type, allErrors, error_name[errnum]);
1345
1346 /* Call the helper to output message */
1347 edac_mc_handle_fbd_ue(mci, rank, channel, channel + 1, msg);
1348#endif
1349}
1350
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001351/*
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001352 * i7core_check_error Retrieve and process errors reported by the
1353 * hardware. Called by the Core module.
1354 */
1355static void i7core_check_error(struct mem_ctl_info *mci)
1356{
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001357 struct i7core_pvt *pvt = mci->pvt_info;
1358 int i;
1359 unsigned count = 0;
1360 struct mce *m = NULL;
1361 unsigned long flags;
1362
1363 debugf0(__FILE__ ": %s()\n", __func__);
1364
1365 /* Copy all mce errors into a temporary buffer */
1366 spin_lock_irqsave(&pvt->mce_lock, flags);
1367 if (pvt->mce_count) {
1368 m = kmalloc(sizeof(*m) * pvt->mce_count, GFP_ATOMIC);
1369 if (m) {
1370 count = pvt->mce_count;
1371 memcpy(m, &pvt->mce_entry, sizeof(*m) * count);
1372 }
1373 pvt->mce_count = 0;
1374 }
1375 spin_unlock_irqrestore(&pvt->mce_lock, flags);
1376
1377 /* proccess mcelog errors */
1378 for (i = 0; i < count; i++)
1379 i7core_mce_output_error(mci, &m[i]);
1380
1381 kfree(m);
1382
1383 /* check memory count errors */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001384 for (i = 0; i < pvt->sockets; i++)
1385 check_mc_test_err(mci, i);
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001386}
1387
1388/*
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001389 * i7core_mce_check_error Replicates mcelog routine to get errors
1390 * This routine simply queues mcelog errors, and
1391 * return. The error itself should be handled later
1392 * by i7core_check_error.
1393 */
1394static int i7core_mce_check_error(void *priv, struct mce *mce)
1395{
1396 struct i7core_pvt *pvt = priv;
1397 unsigned long flags;
1398
1399 debugf0(__FILE__ ": %s()\n", __func__);
1400
1401 spin_lock_irqsave(&pvt->mce_lock, flags);
1402 if (pvt->mce_count < MCE_LOG_LEN) {
1403 memcpy(&pvt->mce_entry[pvt->mce_count], mce, sizeof(*mce));
1404 pvt->mce_count++;
1405 }
1406 spin_unlock_irqrestore(&pvt->mce_lock, flags);
1407
1408 /* Advice mcelog that the error were handled */
1409// return 1;
1410 return 0; // Let's duplicate the log
1411}
1412
1413/*
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001414 * i7core_probe Probe for ONE instance of device to see if it is
1415 * present.
1416 * return:
1417 * 0 for FOUND a device
1418 * < 0 for error code
1419 */
1420static int __devinit i7core_probe(struct pci_dev *pdev,
1421 const struct pci_device_id *id)
1422{
1423 struct mem_ctl_info *mci;
1424 struct i7core_pvt *pvt;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001425 int num_channels = 0;
1426 int num_csrows = 0;
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -03001427 int csrow = 0;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001428 int dev_idx = id->driver_data;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001429 int rc, i;
1430 u8 sockets;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001431
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001432 if (unlikely(dev_idx >= ARRAY_SIZE(i7core_devs)))
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001433 return -EINVAL;
1434
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001435 /* get the pci devices we want to reserve for our use */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001436 rc = i7core_get_devices();
1437 if (unlikely(rc < 0))
1438 return rc;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001439
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001440 sockets = 1;
1441 for (i = NUM_SOCKETS - 1; i > 0; i--)
1442 if (pci_devs[0].pdev[i]) {
1443 sockets = i + 1;
1444 break;
1445 }
1446
1447 for (i = 0; i < sockets; i++) {
1448 int channels;
1449 int csrows;
1450
1451 /* Check the number of active and not disabled channels */
1452 rc = i7core_get_active_channels(i, &channels, &csrows);
1453 if (unlikely(rc < 0))
1454 goto fail0;
1455
1456 num_channels += channels;
1457 num_csrows += csrows;
1458 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001459
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001460 /* allocate a new MC control structure */
1461 mci = edac_mc_alloc(sizeof(*pvt), num_csrows, num_channels, 0);
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001462 if (unlikely(!mci)) {
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001463 rc = -ENOMEM;
1464 goto fail0;
1465 }
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001466
1467 debugf0("MC: " __FILE__ ": %s(): mci = %p\n", __func__, mci);
1468
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001469 mci->dev = &pdev->dev; /* record ptr to the generic device */
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001470 pvt = mci->pvt_info;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001471 memset(pvt, 0, sizeof(*pvt));
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001472 pvt->sockets = sockets;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001473 mci->mc_idx = 0;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001474
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001475 /*
1476 * FIXME: how to handle RDDR3 at MCI level? It is possible to have
1477 * Mixed RDDR3/UDDR3 with Nehalem, provided that they are on different
1478 * memory channels
1479 */
1480 mci->mtype_cap = MEM_FLAG_DDR3;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001481 mci->edac_ctl_cap = EDAC_FLAG_NONE;
1482 mci->edac_cap = EDAC_FLAG_NONE;
1483 mci->mod_name = "i7core_edac.c";
1484 mci->mod_ver = I7CORE_REVISION;
1485 mci->ctl_name = i7core_devs[dev_idx].ctl_name;
1486 mci->dev_name = pci_name(pdev);
1487 mci->ctl_page_to_phys = NULL;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001488 mci->mc_driver_sysfs_attributes = i7core_inj_attrs;
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001489 /* Set the function pointer to an actual operation function */
1490 mci->edac_check = i7core_check_error;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001491
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001492 /* Store pci devices at mci for faster access */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001493 rc = mci_bind_devs(mci);
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001494 if (unlikely(rc < 0))
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001495 goto fail1;
1496
1497 /* Get dimm basic config */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001498 for (i = 0; i < sockets; i++)
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -03001499 get_dimm_config(mci, &csrow, i);
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001500
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001501 /* add this new MC control structure to EDAC's list of MCs */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001502 if (unlikely(edac_mc_add_mc(mci))) {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001503 debugf0("MC: " __FILE__
1504 ": %s(): failed edac_mc_add_mc()\n", __func__);
1505 /* FIXME: perhaps some code should go here that disables error
1506 * reporting if we just enabled it
1507 */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001508
1509 rc = -EINVAL;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001510 goto fail1;
1511 }
1512
1513 /* allocating generic PCI control info */
1514 i7core_pci = edac_pci_create_generic_ctl(&pdev->dev, EDAC_MOD_STR);
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001515 if (unlikely(!i7core_pci)) {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001516 printk(KERN_WARNING
1517 "%s(): Unable to create PCI control\n",
1518 __func__);
1519 printk(KERN_WARNING
1520 "%s(): PCI error report via EDAC not setup\n",
1521 __func__);
1522 }
1523
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001524 /* Default error mask is any memory */
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001525 pvt->inject.channel = 0;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001526 pvt->inject.dimm = -1;
1527 pvt->inject.rank = -1;
1528 pvt->inject.bank = -1;
1529 pvt->inject.page = -1;
1530 pvt->inject.col = -1;
1531
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001532 /* Registers on edac_mce in order to receive memory errors */
1533 pvt->edac_mce.priv = pvt;
1534 pvt->edac_mce.check_error = i7core_mce_check_error;
1535 spin_lock_init(&pvt->mce_lock);
1536
1537 rc = edac_mce_register(&pvt->edac_mce);
1538 if (unlikely (rc < 0)) {
1539 debugf0("MC: " __FILE__
1540 ": %s(): failed edac_mce_register()\n", __func__);
1541 goto fail1;
1542 }
1543
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001544 i7core_printk(KERN_INFO, "Driver loaded.\n");
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001545
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001546 return 0;
1547
1548fail1:
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001549 edac_mc_free(mci);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001550
1551fail0:
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001552 i7core_put_devices();
1553 return rc;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001554}
1555
1556/*
1557 * i7core_remove destructor for one instance of device
1558 *
1559 */
1560static void __devexit i7core_remove(struct pci_dev *pdev)
1561{
1562 struct mem_ctl_info *mci;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001563 struct i7core_pvt *pvt;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001564
1565 debugf0(__FILE__ ": %s()\n", __func__);
1566
1567 if (i7core_pci)
1568 edac_pci_release_generic_ctl(i7core_pci);
1569
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001570
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001571 mci = edac_mc_del_mc(&pdev->dev);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001572 if (!mci)
1573 return;
1574
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001575 /* Unregisters on edac_mce in order to receive memory errors */
1576 pvt = mci->pvt_info;
1577 edac_mce_unregister(&pvt->edac_mce);
1578
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001579 /* retrieve references to resources, and free those resources */
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001580 i7core_put_devices();
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001581
1582 edac_mc_free(mci);
1583}
1584
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001585MODULE_DEVICE_TABLE(pci, i7core_pci_tbl);
1586
1587/*
1588 * i7core_driver pci_driver structure for this module
1589 *
1590 */
1591static struct pci_driver i7core_driver = {
1592 .name = "i7core_edac",
1593 .probe = i7core_probe,
1594 .remove = __devexit_p(i7core_remove),
1595 .id_table = i7core_pci_tbl,
1596};
1597
1598/*
1599 * i7core_init Module entry function
1600 * Try to initialize this module for its devices
1601 */
1602static int __init i7core_init(void)
1603{
1604 int pci_rc;
1605
1606 debugf2("MC: " __FILE__ ": %s()\n", __func__);
1607
1608 /* Ensure that the OPSTATE is set correctly for POLL or NMI */
1609 opstate_init();
1610
1611 pci_rc = pci_register_driver(&i7core_driver);
1612
1613 return (pci_rc < 0) ? pci_rc : 0;
1614}
1615
1616/*
1617 * i7core_exit() Module exit function
1618 * Unregister the driver
1619 */
1620static void __exit i7core_exit(void)
1621{
1622 debugf2("MC: " __FILE__ ": %s()\n", __func__);
1623 pci_unregister_driver(&i7core_driver);
1624}
1625
1626module_init(i7core_init);
1627module_exit(i7core_exit);
1628
1629MODULE_LICENSE("GPL");
1630MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
1631MODULE_AUTHOR("Red Hat Inc. (http://www.redhat.com)");
1632MODULE_DESCRIPTION("MC Driver for Intel i7 Core memory controllers - "
1633 I7CORE_REVISION);
1634
1635module_param(edac_op_state, int, 0444);
1636MODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI");