blob: 79636b58ec526afbfd02b2ac444bac93421025d0 [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[] = {
230 /* Memory controller */
231 { PCI_DESCR(3, 0, PCI_DEVICE_ID_INTEL_I7_MCR) },
232 { PCI_DESCR(3, 1, PCI_DEVICE_ID_INTEL_I7_MC_TAD) },
233 { PCI_DESCR(3, 2, PCI_DEVICE_ID_INTEL_I7_MC_RAS) }, /* if RDIMM is supported */
234 { PCI_DESCR(3, 4, PCI_DEVICE_ID_INTEL_I7_MC_TEST) },
235
236 /* Channel 0 */
237 { PCI_DESCR(4, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH0_CTRL) },
238 { PCI_DESCR(4, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH0_ADDR) },
239 { PCI_DESCR(4, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH0_RANK) },
240 { PCI_DESCR(4, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH0_TC) },
241
242 /* Channel 1 */
243 { PCI_DESCR(5, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH1_CTRL) },
244 { PCI_DESCR(5, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH1_ADDR) },
245 { PCI_DESCR(5, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH1_RANK) },
246 { PCI_DESCR(5, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH1_TC) },
247
248 /* Channel 2 */
249 { PCI_DESCR(6, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH2_CTRL) },
250 { PCI_DESCR(6, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH2_ADDR) },
251 { PCI_DESCR(6, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH2_RANK) },
252 { PCI_DESCR(6, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH2_TC) },
Mauro Carvalho Chehab310cbb72009-07-17 00:09:10 -0300253
254 /* Generic Non-core registers */
255 /*
256 * This is the PCI device on i7core and on Xeon 35xx (8086:2c41)
257 * On Xeon 55xx, however, it has a different id (8086:2c40). So,
258 * the probing code needs to test for the other address in case of
259 * failure of this one
260 */
261 { PCI_DESCR(0, 0, PCI_DEVICE_ID_INTEL_I7_NOCORE) },
262
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300263};
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300264#define N_DEVS ARRAY_SIZE(pci_devs)
265
266/*
267 * pci_device_id table for which devices we are looking for
268 * This should match the first device at pci_devs table
269 */
270static const struct pci_device_id i7core_pci_tbl[] __devinitdata = {
Mauro Carvalho Chehabd1fd4fb2009-07-10 18:39:53 -0300271 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_X58_HUB_MGMT)},
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300272 {0,} /* 0 terminated list. */
273};
274
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300275
276/* Table of devices attributes supported by this driver */
277static const struct i7core_dev_info i7core_devs[] = {
278 {
279 .ctl_name = "i7 Core",
280 .fsb_mapping_errors = PCI_DEVICE_ID_INTEL_I7_MCR,
281 },
282};
283
284static struct edac_pci_ctl_info *i7core_pci;
285
286/****************************************************************************
287 Anciliary status routines
288 ****************************************************************************/
289
290 /* MC_CONTROL bits */
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300291#define CH_ACTIVE(pvt, ch) ((pvt)->info.mc_control & (1 << (8 + ch)))
292#define ECCx8(pvt) ((pvt)->info.mc_control & (1 << 1))
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300293
294 /* MC_STATUS bits */
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300295#define ECC_ENABLED(pvt) ((pvt)->info.mc_status & (1 << 3))
296#define CH_DISABLED(pvt, ch) ((pvt)->info.mc_status & (1 << ch))
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300297
298 /* MC_MAX_DOD read functions */
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300299static inline int numdimms(u32 dimms)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300300{
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300301 return (dimms & 0x3) + 1;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300302}
303
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300304static inline int numrank(u32 rank)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300305{
306 static int ranks[4] = { 1, 2, 4, -EINVAL };
307
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300308 return ranks[rank & 0x3];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300309}
310
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300311static inline int numbank(u32 bank)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300312{
313 static int banks[4] = { 4, 8, 16, -EINVAL };
314
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300315 return banks[bank & 0x3];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300316}
317
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300318static inline int numrow(u32 row)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300319{
320 static int rows[8] = {
321 1 << 12, 1 << 13, 1 << 14, 1 << 15,
322 1 << 16, -EINVAL, -EINVAL, -EINVAL,
323 };
324
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300325 return rows[row & 0x7];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300326}
327
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300328static inline int numcol(u32 col)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300329{
330 static int cols[8] = {
331 1 << 10, 1 << 11, 1 << 12, -EINVAL,
332 };
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300333 return cols[col & 0x3];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300334}
335
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300336
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300337/****************************************************************************
338 Memory check routines
339 ****************************************************************************/
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300340static struct pci_dev *get_pdev_slot_func(u8 socket, unsigned slot,
341 unsigned func)
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300342{
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300343 int i;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300344
345 for (i = 0; i < N_DEVS; i++) {
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300346 if (!pci_devs[i].pdev[socket])
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300347 continue;
348
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300349 if (PCI_SLOT(pci_devs[i].pdev[socket]->devfn) == slot &&
350 PCI_FUNC(pci_devs[i].pdev[socket]->devfn) == func) {
351 return pci_devs[i].pdev[socket];
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300352 }
353 }
354
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300355 return NULL;
356}
357
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300358static int i7core_get_active_channels(u8 socket, unsigned *channels,
359 unsigned *csrows)
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300360{
361 struct pci_dev *pdev = NULL;
362 int i, j;
363 u32 status, control;
364
365 *channels = 0;
366 *csrows = 0;
367
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300368 pdev = get_pdev_slot_func(socket, 3, 0);
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -0300369 if (!pdev) {
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300370 i7core_printk(KERN_ERR, "Couldn't find socket %d fn 3.0!!!\n",
371 socket);
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300372 return -ENODEV;
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -0300373 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300374
375 /* Device 3 function 0 reads */
376 pci_read_config_dword(pdev, MC_STATUS, &status);
377 pci_read_config_dword(pdev, MC_CONTROL, &control);
378
379 for (i = 0; i < NUM_CHANS; i++) {
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300380 u32 dimm_dod[3];
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300381 /* Check if the channel is active */
382 if (!(control & (1 << (8 + i))))
383 continue;
384
385 /* Check if the channel is disabled */
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300386 if (status & (1 << i))
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300387 continue;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300388
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300389 pdev = get_pdev_slot_func(socket, i + 4, 1);
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300390 if (!pdev) {
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300391 i7core_printk(KERN_ERR, "Couldn't find socket %d "
392 "fn %d.%d!!!\n",
393 socket, i + 4, 1);
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300394 return -ENODEV;
395 }
396 /* Devices 4-6 function 1 */
397 pci_read_config_dword(pdev,
398 MC_DOD_CH_DIMM0, &dimm_dod[0]);
399 pci_read_config_dword(pdev,
400 MC_DOD_CH_DIMM1, &dimm_dod[1]);
401 pci_read_config_dword(pdev,
402 MC_DOD_CH_DIMM2, &dimm_dod[2]);
403
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300404 (*channels)++;
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300405
406 for (j = 0; j < 3; j++) {
407 if (!DIMM_PRESENT(dimm_dod[j]))
408 continue;
409 (*csrows)++;
410 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300411 }
412
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -0300413 debugf0("Number of active channels on socket %d: %d\n",
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300414 socket, *channels);
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300415
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300416 return 0;
417}
418
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -0300419static int get_dimm_config(struct mem_ctl_info *mci, int *csrow, u8 socket)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300420{
421 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300422 struct csrow_info *csr;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300423 struct pci_dev *pdev;
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -0300424 int i, j;
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300425 unsigned long last_page = 0;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300426 enum edac_type mode;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300427 enum mem_type mtype;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300428
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300429 /* Get data from the MC register, function 0 */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300430 pdev = pvt->pci_mcr[socket][0];
Mauro Carvalho Chehab7dd69532009-06-22 22:48:30 -0300431 if (!pdev)
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300432 return -ENODEV;
433
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300434 /* Device 3 function 0 reads */
Mauro Carvalho Chehab7dd69532009-06-22 22:48:30 -0300435 pci_read_config_dword(pdev, MC_CONTROL, &pvt->info.mc_control);
436 pci_read_config_dword(pdev, MC_STATUS, &pvt->info.mc_status);
437 pci_read_config_dword(pdev, MC_MAX_DOD, &pvt->info.max_dod);
438 pci_read_config_dword(pdev, MC_CHANNEL_MAPPER, &pvt->info.ch_map);
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300439
440 debugf0("MC control=0x%08x status=0x%08x dod=0x%08x map=0x%08x\n",
441 pvt->info.mc_control, pvt->info.mc_status,
442 pvt->info.max_dod, pvt->info.ch_map);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300443
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300444 if (ECC_ENABLED(pvt)) {
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300445 debugf0("ECC enabled with x%d SDCC\n", ECCx8(pvt) ? 8 : 4);
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300446 if (ECCx8(pvt))
447 mode = EDAC_S8ECD8ED;
448 else
449 mode = EDAC_S4ECD4ED;
450 } else {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300451 debugf0("ECC disabled\n");
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300452 mode = EDAC_NONE;
453 }
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300454
455 /* FIXME: need to handle the error codes */
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300456 debugf0("DOD Max limits: DIMMS: %d, %d-ranked, %d-banked\n",
457 numdimms(pvt->info.max_dod),
458 numrank(pvt->info.max_dod >> 2),
459 numbank(pvt->info.max_dod >> 4));
460 debugf0("DOD Max rows x colums = 0x%x x 0x%x\n",
461 numrow(pvt->info.max_dod >> 6),
462 numcol(pvt->info.max_dod >> 9));
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300463
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300464 debugf0("Memory channel configuration:\n");
465
466 for (i = 0; i < NUM_CHANS; i++) {
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300467 u32 data, dimm_dod[3], value[8];
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300468
469 if (!CH_ACTIVE(pvt, i)) {
470 debugf0("Channel %i is not active\n", i);
471 continue;
472 }
473 if (CH_DISABLED(pvt, i)) {
474 debugf0("Channel %i is disabled\n", i);
475 continue;
476 }
477
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300478 /* Devices 4-6 function 0 */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300479 pci_read_config_dword(pvt->pci_ch[socket][i][0],
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300480 MC_CHANNEL_DIMM_INIT_PARAMS, &data);
481
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300482 pvt->channel[socket][i].ranks = (data & QUAD_RANK_PRESENT) ?
483 4 : 2;
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300484
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300485 if (data & REGISTERED_DIMM)
486 mtype = MEM_RDDR3;
487 else
488 mtype = MEM_DDR3;
489#if 0
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300490 if (data & THREE_DIMMS_PRESENT)
491 pvt->channel[i].dimms = 3;
492 else if (data & SINGLE_QUAD_RANK_PRESENT)
493 pvt->channel[i].dimms = 1;
494 else
495 pvt->channel[i].dimms = 2;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300496#endif
497
498 /* Devices 4-6 function 1 */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300499 pci_read_config_dword(pvt->pci_ch[socket][i][1],
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300500 MC_DOD_CH_DIMM0, &dimm_dod[0]);
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300501 pci_read_config_dword(pvt->pci_ch[socket][i][1],
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300502 MC_DOD_CH_DIMM1, &dimm_dod[1]);
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300503 pci_read_config_dword(pvt->pci_ch[socket][i][1],
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300504 MC_DOD_CH_DIMM2, &dimm_dod[2]);
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300505
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300506 debugf0("Ch%d phy rd%d, wr%d (0x%08x): "
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300507 "%d ranks, %cDIMMs\n",
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300508 i,
509 RDLCH(pvt->info.ch_map, i), WRLCH(pvt->info.ch_map, i),
510 data,
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300511 pvt->channel[socket][i].ranks,
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300512 (data & REGISTERED_DIMM) ? 'R' : 'U');
Mauro Carvalho Chehab7dd69532009-06-22 22:48:30 -0300513
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300514 for (j = 0; j < 3; j++) {
515 u32 banks, ranks, rows, cols;
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300516 u32 size, npages;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300517
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300518 if (!DIMM_PRESENT(dimm_dod[j]))
519 continue;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300520
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300521 banks = numbank(MC_DOD_NUMBANK(dimm_dod[j]));
522 ranks = numrank(MC_DOD_NUMRANK(dimm_dod[j]));
523 rows = numrow(MC_DOD_NUMROW(dimm_dod[j]));
524 cols = numcol(MC_DOD_NUMCOL(dimm_dod[j]));
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300525
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300526 /* DDR3 has 8 I/O banks */
527 size = (rows * cols * banks * ranks) >> (20 - 3);
528
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300529 pvt->channel[socket][i].dimms++;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300530
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300531 debugf0("\tdimm %d (0x%08x) %d Mb offset: %x, "
532 "numbank: %d,\n\t\t"
533 "numrank: %d, numrow: %#x, numcol: %#x\n",
534 j, dimm_dod[j], size,
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300535 RANKOFFSET(dimm_dod[j]),
536 banks, ranks, rows, cols);
537
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300538#if PAGE_SHIFT > 20
539 npages = size >> (PAGE_SHIFT - 20);
540#else
541 npages = size << (20 - PAGE_SHIFT);
542#endif
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300543
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -0300544 csr = &mci->csrows[*csrow];
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300545 csr->first_page = last_page + 1;
546 last_page += npages;
547 csr->last_page = last_page;
548 csr->nr_pages = npages;
549
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300550 csr->page_mask = 0;
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300551 csr->grain = 8;
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -0300552 csr->csrow_idx = *csrow;
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300553 csr->nr_channels = 1;
554
555 csr->channels[0].chan_idx = i;
556 csr->channels[0].ce_count = 0;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300557
558 switch (banks) {
559 case 4:
560 csr->dtype = DEV_X4;
561 break;
562 case 8:
563 csr->dtype = DEV_X8;
564 break;
565 case 16:
566 csr->dtype = DEV_X16;
567 break;
568 default:
569 csr->dtype = DEV_UNKNOWN;
570 }
571
572 csr->edac_mode = mode;
573 csr->mtype = mtype;
574
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -0300575 (*csrow)++;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300576 }
577
578 pci_read_config_dword(pdev, MC_SAG_CH_0, &value[0]);
579 pci_read_config_dword(pdev, MC_SAG_CH_1, &value[1]);
580 pci_read_config_dword(pdev, MC_SAG_CH_2, &value[2]);
581 pci_read_config_dword(pdev, MC_SAG_CH_3, &value[3]);
582 pci_read_config_dword(pdev, MC_SAG_CH_4, &value[4]);
583 pci_read_config_dword(pdev, MC_SAG_CH_5, &value[5]);
584 pci_read_config_dword(pdev, MC_SAG_CH_6, &value[6]);
585 pci_read_config_dword(pdev, MC_SAG_CH_7, &value[7]);
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300586 debugf0("\t[%i] DIVBY3\tREMOVED\tOFFSET\n", i);
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300587 for (j = 0; j < 8; j++)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300588 debugf0("\t\t%#x\t%#x\t%#x\n",
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300589 (value[j] >> 27) & 0x1,
590 (value[j] >> 24) & 0x7,
591 (value[j] && ((1 << 24) - 1)));
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300592 }
593
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300594 return 0;
595}
596
597/****************************************************************************
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300598 Error insertion routines
599 ****************************************************************************/
600
601/* The i7core has independent error injection features per channel.
602 However, to have a simpler code, we don't allow enabling error injection
603 on more than one channel.
604 Also, since a change at an inject parameter will be applied only at enable,
605 we're disabling error injection on all write calls to the sysfs nodes that
606 controls the error code injection.
607 */
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300608static int disable_inject(struct mem_ctl_info *mci)
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300609{
610 struct i7core_pvt *pvt = mci->pvt_info;
611
612 pvt->inject.enable = 0;
613
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300614 if (!pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0])
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300615 return -ENODEV;
616
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300617 pci_write_config_dword(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0],
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300618 MC_CHANNEL_ERROR_MASK, 0);
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300619
620 return 0;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300621}
622
623/*
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300624 * i7core inject inject.socket
625 *
626 * accept and store error injection inject.socket value
627 */
628static ssize_t i7core_inject_socket_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 rc = strict_strtoul(data, 10, &value);
636 if ((rc < 0) || (value > pvt->sockets))
637 return 0;
638
639 pvt->inject.section = (u32) value;
640 return count;
641}
642
643static ssize_t i7core_inject_socket_show(struct mem_ctl_info *mci,
644 char *data)
645{
646 struct i7core_pvt *pvt = mci->pvt_info;
647 return sprintf(data, "%d\n", pvt->inject.socket);
648}
649
650/*
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300651 * i7core inject inject.section
652 *
653 * accept and store error injection inject.section value
654 * bit 0 - refers to the lower 32-byte half cacheline
655 * bit 1 - refers to the upper 32-byte half cacheline
656 */
657static ssize_t i7core_inject_section_store(struct mem_ctl_info *mci,
658 const char *data, size_t count)
659{
660 struct i7core_pvt *pvt = mci->pvt_info;
661 unsigned long value;
662 int rc;
663
664 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300665 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300666
667 rc = strict_strtoul(data, 10, &value);
668 if ((rc < 0) || (value > 3))
669 return 0;
670
671 pvt->inject.section = (u32) value;
672 return count;
673}
674
675static ssize_t i7core_inject_section_show(struct mem_ctl_info *mci,
676 char *data)
677{
678 struct i7core_pvt *pvt = mci->pvt_info;
679 return sprintf(data, "0x%08x\n", pvt->inject.section);
680}
681
682/*
683 * i7core inject.type
684 *
685 * accept and store error injection inject.section value
686 * bit 0 - repeat enable - Enable error repetition
687 * bit 1 - inject ECC error
688 * bit 2 - inject parity error
689 */
690static ssize_t i7core_inject_type_store(struct mem_ctl_info *mci,
691 const char *data, size_t count)
692{
693 struct i7core_pvt *pvt = mci->pvt_info;
694 unsigned long value;
695 int rc;
696
697 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300698 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300699
700 rc = strict_strtoul(data, 10, &value);
701 if ((rc < 0) || (value > 7))
702 return 0;
703
704 pvt->inject.type = (u32) value;
705 return count;
706}
707
708static ssize_t i7core_inject_type_show(struct mem_ctl_info *mci,
709 char *data)
710{
711 struct i7core_pvt *pvt = mci->pvt_info;
712 return sprintf(data, "0x%08x\n", pvt->inject.type);
713}
714
715/*
716 * i7core_inject_inject.eccmask_store
717 *
718 * The type of error (UE/CE) will depend on the inject.eccmask value:
719 * Any bits set to a 1 will flip the corresponding ECC bit
720 * Correctable errors can be injected by flipping 1 bit or the bits within
721 * a symbol pair (2 consecutive aligned 8-bit pairs - i.e. 7:0 and 15:8 or
722 * 23:16 and 31:24). Flipping bits in two symbol pairs will cause an
723 * uncorrectable error to be injected.
724 */
725static ssize_t i7core_inject_eccmask_store(struct mem_ctl_info *mci,
726 const char *data, size_t count)
727{
728 struct i7core_pvt *pvt = mci->pvt_info;
729 unsigned long value;
730 int rc;
731
732 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300733 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300734
735 rc = strict_strtoul(data, 10, &value);
736 if (rc < 0)
737 return 0;
738
739 pvt->inject.eccmask = (u32) value;
740 return count;
741}
742
743static ssize_t i7core_inject_eccmask_show(struct mem_ctl_info *mci,
744 char *data)
745{
746 struct i7core_pvt *pvt = mci->pvt_info;
747 return sprintf(data, "0x%08x\n", pvt->inject.eccmask);
748}
749
750/*
751 * i7core_addrmatch
752 *
753 * The type of error (UE/CE) will depend on the inject.eccmask value:
754 * Any bits set to a 1 will flip the corresponding ECC bit
755 * Correctable errors can be injected by flipping 1 bit or the bits within
756 * a symbol pair (2 consecutive aligned 8-bit pairs - i.e. 7:0 and 15:8 or
757 * 23:16 and 31:24). Flipping bits in two symbol pairs will cause an
758 * uncorrectable error to be injected.
759 */
760static ssize_t i7core_inject_addrmatch_store(struct mem_ctl_info *mci,
761 const char *data, size_t count)
762{
763 struct i7core_pvt *pvt = mci->pvt_info;
764 char *cmd, *val;
765 long value;
766 int rc;
767
768 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300769 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300770
771 do {
772 cmd = strsep((char **) &data, ":");
773 if (!cmd)
774 break;
775 val = strsep((char **) &data, " \n\t");
776 if (!val)
777 return cmd - data;
778
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300779 if (!strcasecmp(val, "any"))
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300780 value = -1;
781 else {
782 rc = strict_strtol(val, 10, &value);
783 if ((rc < 0) || (value < 0))
784 return cmd - data;
785 }
786
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300787 if (!strcasecmp(cmd, "channel")) {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300788 if (value < 3)
789 pvt->inject.channel = value;
790 else
791 return cmd - data;
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300792 } else if (!strcasecmp(cmd, "dimm")) {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300793 if (value < 4)
794 pvt->inject.dimm = value;
795 else
796 return cmd - data;
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300797 } else if (!strcasecmp(cmd, "rank")) {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300798 if (value < 4)
799 pvt->inject.rank = value;
800 else
801 return cmd - data;
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300802 } else if (!strcasecmp(cmd, "bank")) {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300803 if (value < 4)
804 pvt->inject.bank = value;
805 else
806 return cmd - data;
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300807 } else if (!strcasecmp(cmd, "page")) {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300808 if (value <= 0xffff)
809 pvt->inject.page = value;
810 else
811 return cmd - data;
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300812 } else if (!strcasecmp(cmd, "col") ||
813 !strcasecmp(cmd, "column")) {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300814 if (value <= 0x3fff)
815 pvt->inject.col = value;
816 else
817 return cmd - data;
818 }
819 } while (1);
820
821 return count;
822}
823
824static ssize_t i7core_inject_addrmatch_show(struct mem_ctl_info *mci,
825 char *data)
826{
827 struct i7core_pvt *pvt = mci->pvt_info;
828 char channel[4], dimm[4], bank[4], rank[4], page[7], col[7];
829
830 if (pvt->inject.channel < 0)
831 sprintf(channel, "any");
832 else
833 sprintf(channel, "%d", pvt->inject.channel);
834 if (pvt->inject.dimm < 0)
835 sprintf(dimm, "any");
836 else
837 sprintf(dimm, "%d", pvt->inject.dimm);
838 if (pvt->inject.bank < 0)
839 sprintf(bank, "any");
840 else
841 sprintf(bank, "%d", pvt->inject.bank);
842 if (pvt->inject.rank < 0)
843 sprintf(rank, "any");
844 else
845 sprintf(rank, "%d", pvt->inject.rank);
846 if (pvt->inject.page < 0)
847 sprintf(page, "any");
848 else
849 sprintf(page, "0x%04x", pvt->inject.page);
850 if (pvt->inject.col < 0)
851 sprintf(col, "any");
852 else
853 sprintf(col, "0x%04x", pvt->inject.col);
854
855 return sprintf(data, "channel: %s\ndimm: %s\nbank: %s\n"
856 "rank: %s\npage: %s\ncolumn: %s\n",
857 channel, dimm, bank, rank, page, col);
858}
859
860/*
861 * This routine prepares the Memory Controller for error injection.
862 * The error will be injected when some process tries to write to the
863 * memory that matches the given criteria.
864 * The criteria can be set in terms of a mask where dimm, rank, bank, page
865 * and col can be specified.
866 * A -1 value for any of the mask items will make the MCU to ignore
867 * that matching criteria for error injection.
868 *
869 * It should be noticed that the error will only happen after a write operation
870 * on a memory that matches the condition. if REPEAT_EN is not enabled at
871 * inject mask, then it will produce just one error. Otherwise, it will repeat
872 * until the injectmask would be cleaned.
873 *
874 * FIXME: This routine assumes that MAXNUMDIMMS value of MC_MAX_DOD
875 * is reliable enough to check if the MC is using the
876 * three channels. However, this is not clear at the datasheet.
877 */
878static ssize_t i7core_inject_enable_store(struct mem_ctl_info *mci,
879 const char *data, size_t count)
880{
881 struct i7core_pvt *pvt = mci->pvt_info;
882 u32 injectmask;
883 u64 mask = 0;
884 int rc;
885 long enable;
886
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300887 if (!pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0])
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300888 return 0;
889
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300890 rc = strict_strtoul(data, 10, &enable);
891 if ((rc < 0))
892 return 0;
893
894 if (enable) {
895 pvt->inject.enable = 1;
896 } else {
897 disable_inject(mci);
898 return count;
899 }
900
901 /* Sets pvt->inject.dimm mask */
902 if (pvt->inject.dimm < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300903 mask |= 1L << 41;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300904 else {
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300905 if (pvt->channel[pvt->inject.socket][pvt->inject.channel].dimms > 2)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300906 mask |= (pvt->inject.dimm & 0x3L) << 35;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300907 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300908 mask |= (pvt->inject.dimm & 0x1L) << 36;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300909 }
910
911 /* Sets pvt->inject.rank mask */
912 if (pvt->inject.rank < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300913 mask |= 1L << 40;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300914 else {
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300915 if (pvt->channel[pvt->inject.socket][pvt->inject.channel].dimms > 2)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300916 mask |= (pvt->inject.rank & 0x1L) << 34;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300917 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300918 mask |= (pvt->inject.rank & 0x3L) << 34;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300919 }
920
921 /* Sets pvt->inject.bank mask */
922 if (pvt->inject.bank < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300923 mask |= 1L << 39;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300924 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300925 mask |= (pvt->inject.bank & 0x15L) << 30;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300926
927 /* Sets pvt->inject.page mask */
928 if (pvt->inject.page < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300929 mask |= 1L << 38;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300930 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300931 mask |= (pvt->inject.page & 0xffffL) << 14;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300932
933 /* Sets pvt->inject.column mask */
934 if (pvt->inject.col < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300935 mask |= 1L << 37;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300936 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300937 mask |= (pvt->inject.col & 0x3fffL);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300938
Mauro Carvalho Chehabe9bd2e72009-07-09 22:14:35 -0300939 /* Unlock writes to registers */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300940 pci_write_config_dword(pvt->pci_noncore[pvt->inject.socket],
941 MC_CFG_CONTROL, 0x2);
Mauro Carvalho Chehabe9bd2e72009-07-09 22:14:35 -0300942 msleep(100);
943
944 /* Zeroes error count registers */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300945 pci_write_config_dword(pvt->pci_mcr[pvt->inject.socket][4],
946 MC_TEST_ERR_RCV1, 0);
947 pci_write_config_dword(pvt->pci_mcr[pvt->inject.socket][4],
948 MC_TEST_ERR_RCV0, 0);
949 pvt->ce_count_available[pvt->inject.socket] = 0;
Mauro Carvalho Chehabe9bd2e72009-07-09 22:14:35 -0300950
951
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300952#if USE_QWORD
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300953 pci_write_config_qword(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0],
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300954 MC_CHANNEL_ADDR_MATCH, mask);
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300955#else
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300956 pci_write_config_dword(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0],
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300957 MC_CHANNEL_ADDR_MATCH, mask);
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300958 pci_write_config_dword(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0],
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300959 MC_CHANNEL_ADDR_MATCH + 4, mask >> 32L);
960#endif
961
962#if 1
963#if USE_QWORD
964 u64 rdmask;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300965 pci_read_config_qword(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0],
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300966 MC_CHANNEL_ADDR_MATCH, &rdmask);
967 debugf0("Inject addr match write 0x%016llx, read: 0x%016llx\n",
968 mask, rdmask);
969#else
970 u32 rdmask1, rdmask2;
971
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300972 pci_read_config_dword(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0],
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300973 MC_CHANNEL_ADDR_MATCH, &rdmask1);
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300974 pci_read_config_dword(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0],
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300975 MC_CHANNEL_ADDR_MATCH + 4, &rdmask2);
976
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -0300977 debugf0("Inject addr match write 0x%016llx, read: 0x%08x 0x%08x\n",
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300978 mask, rdmask1, rdmask2);
979#endif
980#endif
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300981
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300982 pci_write_config_dword(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0],
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300983 MC_CHANNEL_ERROR_MASK, pvt->inject.eccmask);
984
985 /*
986 * bit 0: REPEAT_EN
987 * bits 1-2: MASK_HALF_CACHELINE
988 * bit 3: INJECT_ECC
989 * bit 4: INJECT_ADDR_PARITY
990 */
991
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300992 injectmask = (pvt->inject.type & 1) |
993 (pvt->inject.section & 0x3) << 1 |
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300994 (pvt->inject.type & 0x6) << (3 - 1);
995
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300996 pci_write_config_dword(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0],
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300997 MC_CHANNEL_ERROR_MASK, injectmask);
998
Mauro Carvalho Chehabe9bd2e72009-07-09 22:14:35 -0300999#if 0
1000 /* lock writes to registers */
1001 pci_write_config_dword(pvt->pci_noncore, MC_CFG_CONTROL, 0);
1002#endif
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001003 debugf0("Error inject addr match 0x%016llx, ecc 0x%08x,"
1004 " inject 0x%08x\n",
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001005 mask, pvt->inject.eccmask, injectmask);
1006
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -03001007
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001008 return count;
1009}
1010
1011static ssize_t i7core_inject_enable_show(struct mem_ctl_info *mci,
1012 char *data)
1013{
1014 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -03001015 u32 injectmask;
1016
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001017 pci_read_config_dword(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0],
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -03001018 MC_CHANNEL_ERROR_MASK, &injectmask);
1019
1020 debugf0("Inject error read: 0x%018x\n", injectmask);
1021
1022 if (injectmask & 0x0c)
1023 pvt->inject.enable = 1;
1024
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001025 return sprintf(data, "%d\n", pvt->inject.enable);
1026}
1027
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001028static ssize_t i7core_ce_regs_show(struct mem_ctl_info *mci, char *data)
1029{
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001030 unsigned i, count, total = 0;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001031 struct i7core_pvt *pvt = mci->pvt_info;
1032
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001033 for (i = 0; i < pvt->sockets; i++) {
1034 if (!pvt->ce_count_available[i])
1035 count = sprintf(data, "socket 0 data unavailable\n");
1036 else
1037 count = sprintf(data, "socket %d, dimm0: %lu\n"
1038 "dimm1: %lu\ndimm2: %lu\n",
1039 i,
1040 pvt->ce_count[i][0],
1041 pvt->ce_count[i][1],
1042 pvt->ce_count[i][2]);
1043 data += count;
1044 total += count;
1045 }
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001046
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001047 return total;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001048}
1049
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001050/*
1051 * Sysfs struct
1052 */
1053static struct mcidev_sysfs_attribute i7core_inj_attrs[] = {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001054 {
1055 .attr = {
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001056 .name = "inject_socket",
1057 .mode = (S_IRUGO | S_IWUSR)
1058 },
1059 .show = i7core_inject_socket_show,
1060 .store = i7core_inject_socket_store,
1061 }, {
1062 .attr = {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001063 .name = "inject_section",
1064 .mode = (S_IRUGO | S_IWUSR)
1065 },
1066 .show = i7core_inject_section_show,
1067 .store = i7core_inject_section_store,
1068 }, {
1069 .attr = {
1070 .name = "inject_type",
1071 .mode = (S_IRUGO | S_IWUSR)
1072 },
1073 .show = i7core_inject_type_show,
1074 .store = i7core_inject_type_store,
1075 }, {
1076 .attr = {
1077 .name = "inject_eccmask",
1078 .mode = (S_IRUGO | S_IWUSR)
1079 },
1080 .show = i7core_inject_eccmask_show,
1081 .store = i7core_inject_eccmask_store,
1082 }, {
1083 .attr = {
1084 .name = "inject_addrmatch",
1085 .mode = (S_IRUGO | S_IWUSR)
1086 },
1087 .show = i7core_inject_addrmatch_show,
1088 .store = i7core_inject_addrmatch_store,
1089 }, {
1090 .attr = {
1091 .name = "inject_enable",
1092 .mode = (S_IRUGO | S_IWUSR)
1093 },
1094 .show = i7core_inject_enable_show,
1095 .store = i7core_inject_enable_store,
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001096 }, {
1097 .attr = {
1098 .name = "corrected_error_counts",
1099 .mode = (S_IRUGO | S_IWUSR)
1100 },
1101 .show = i7core_ce_regs_show,
1102 .store = NULL,
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001103 },
1104};
1105
1106/****************************************************************************
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001107 Device initialization routines: put/get, init/exit
1108 ****************************************************************************/
1109
1110/*
1111 * i7core_put_devices 'put' all the devices that we have
1112 * reserved via 'get'
1113 */
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001114static void i7core_put_devices(void)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001115{
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001116 int i, j;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001117
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001118 for (i = 0; i < NUM_SOCKETS; i++)
1119 for (j = 0; j < N_DEVS; j++)
1120 pci_dev_put(pci_devs[j].pdev[i]);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001121}
1122
1123/*
1124 * i7core_get_devices Find and perform 'get' operation on the MCH's
1125 * device/functions we want to reference for this driver
1126 *
1127 * Need to 'get' device 16 func 1 and func 2
1128 */
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001129int i7core_get_onedevice(struct pci_dev **prev, int devno)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001130{
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001131 struct pci_dev *pdev = NULL;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001132 u8 bus = 0;
1133 u8 socket = 0;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001134
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001135 pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
1136 pci_devs[devno].dev_id, *prev);
1137
1138 /*
1139 * On Xeon 55xx, the Intel Quckpath Arch Generic Non-core pci buses
1140 * aren't announced by acpi. So, we need to use a legacy scan probing
1141 * to detect them
1142 */
1143 if (unlikely(!pdev && !devno && !prev)) {
1144 pcibios_scan_specific_bus(254);
1145 pcibios_scan_specific_bus(255);
1146
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001147 pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001148 pci_devs[devno].dev_id, *prev);
1149 }
Mauro Carvalho Chehabd1fd4fb2009-07-10 18:39:53 -03001150
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001151 /*
1152 * On Xeon 55xx, the Intel Quckpath Arch Generic Non-core regs
1153 * is at addr 8086:2c40, instead of 8086:2c41. So, we need
1154 * to probe for the alternate address in case of failure
1155 */
1156 if (pci_devs[devno].dev_id == PCI_DEVICE_ID_INTEL_I7_NOCORE && !pdev)
1157 pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
1158 PCI_DEVICE_ID_INTEL_I7_NOCORE_ALT, *prev);
Mauro Carvalho Chehabd1fd4fb2009-07-10 18:39:53 -03001159
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001160 if (!pdev) {
1161 if (*prev) {
1162 *prev = pdev;
1163 return 0;
Mauro Carvalho Chehabd1fd4fb2009-07-10 18:39:53 -03001164 }
1165
Mauro Carvalho Chehab310cbb72009-07-17 00:09:10 -03001166 /*
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001167 * Dev 3 function 2 only exists on chips with RDIMMs
1168 * so, it is ok to not found it
Mauro Carvalho Chehab310cbb72009-07-17 00:09:10 -03001169 */
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001170 if ((pci_devs[devno].dev == 3) && (pci_devs[devno].func == 2)) {
1171 *prev = pdev;
1172 return 0;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001173 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001174
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001175 i7core_printk(KERN_ERR,
1176 "Device not found: dev %02x.%d PCI ID %04x:%04x\n",
1177 pci_devs[devno].dev, pci_devs[devno].func,
1178 PCI_VENDOR_ID_INTEL, pci_devs[devno].dev_id);
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001179
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001180 /* End of list, leave */
1181 return -ENODEV;
1182 }
1183 bus = pdev->bus->number;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001184
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001185 if (bus == 0x3f)
1186 socket = 0;
1187 else
1188 socket = 255 - bus;
1189
1190 if (socket >= NUM_SOCKETS) {
1191 i7core_printk(KERN_ERR,
1192 "Unexpected socket for "
1193 "dev %02x:%02x.%d PCI ID %04x:%04x\n",
1194 bus, pci_devs[devno].dev, pci_devs[devno].func,
1195 PCI_VENDOR_ID_INTEL, pci_devs[devno].dev_id);
1196 pci_dev_put(pdev);
1197 return -ENODEV;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001198 }
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001199
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001200 if (pci_devs[devno].pdev[socket]) {
1201 i7core_printk(KERN_ERR,
1202 "Duplicated device for "
1203 "dev %02x:%02x.%d PCI ID %04x:%04x\n",
1204 bus, pci_devs[devno].dev, pci_devs[devno].func,
1205 PCI_VENDOR_ID_INTEL, pci_devs[devno].dev_id);
1206 pci_dev_put(pdev);
1207 return -ENODEV;
1208 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001209
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001210 pci_devs[devno].pdev[socket] = pdev;
1211
1212 /* Sanity check */
1213 if (unlikely(PCI_SLOT(pdev->devfn) != pci_devs[devno].dev ||
1214 PCI_FUNC(pdev->devfn) != pci_devs[devno].func)) {
1215 i7core_printk(KERN_ERR,
1216 "Device PCI ID %04x:%04x "
1217 "has dev %02x:%02x.%d instead of dev %02x:%02x.%d\n",
1218 PCI_VENDOR_ID_INTEL, pci_devs[devno].dev_id,
1219 bus, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
1220 bus, pci_devs[devno].dev, pci_devs[devno].func);
1221 return -ENODEV;
1222 }
1223
1224 /* Be sure that the device is enabled */
1225 if (unlikely(pci_enable_device(pdev) < 0)) {
1226 i7core_printk(KERN_ERR,
1227 "Couldn't enable "
1228 "dev %02x:%02x.%d PCI ID %04x:%04x\n",
1229 bus, pci_devs[devno].dev, pci_devs[devno].func,
1230 PCI_VENDOR_ID_INTEL, pci_devs[devno].dev_id);
1231 return -ENODEV;
1232 }
1233
1234 i7core_printk(KERN_INFO,
1235 "Registered socket %d "
1236 "dev %02x:%02x.%d PCI ID %04x:%04x\n",
1237 socket, bus, pci_devs[devno].dev, pci_devs[devno].func,
1238 PCI_VENDOR_ID_INTEL, pci_devs[devno].dev_id);
1239
1240 *prev = pdev;
1241
1242 return 0;
1243}
1244
1245static int i7core_get_devices(void)
1246{
1247 int i;
1248 struct pci_dev *pdev = NULL;
1249
1250 for (i = 0; i < N_DEVS; i++) {
1251 pdev = NULL;
1252 do {
1253 if (i7core_get_onedevice(&pdev, i) < 0) {
1254 i7core_put_devices();
1255 return -ENODEV;
1256 }
1257 } while (pdev);
1258 }
1259 return 0;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001260}
1261
1262static int mci_bind_devs(struct mem_ctl_info *mci)
1263{
1264 struct i7core_pvt *pvt = mci->pvt_info;
1265 struct pci_dev *pdev;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001266 int i, j, func, slot;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001267
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001268 for (i = 0; i < pvt->sockets; i++) {
1269 for (j = 0; j < N_DEVS; j++) {
1270 pdev = pci_devs[j].pdev[i];
1271 if (!pdev)
1272 continue;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001273
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001274 func = PCI_FUNC(pdev->devfn);
1275 slot = PCI_SLOT(pdev->devfn);
1276 if (slot == 3) {
1277 if (unlikely(func > MAX_MCR_FUNC))
1278 goto error;
1279 pvt->pci_mcr[i][func] = pdev;
1280 } else if (likely(slot >= 4 && slot < 4 + NUM_CHANS)) {
1281 if (unlikely(func > MAX_CHAN_FUNC))
1282 goto error;
1283 pvt->pci_ch[i][slot - 4][func] = pdev;
1284 } else if (!slot && !func)
1285 pvt->pci_noncore[i] = pdev;
1286 else
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001287 goto error;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001288
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001289 debugf0("Associated fn %d.%d, dev = %p, socket %d\n",
1290 PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
1291 pdev, i);
1292 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001293 }
Mauro Carvalho Chehabe9bd2e72009-07-09 22:14:35 -03001294
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001295 return 0;
1296
1297error:
1298 i7core_printk(KERN_ERR, "Device %d, function %d "
1299 "is out of the expected range\n",
1300 slot, func);
1301 return -EINVAL;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001302}
1303
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001304/****************************************************************************
1305 Error check routines
1306 ****************************************************************************/
1307
1308/* This function is based on the device 3 function 4 registers as described on:
1309 * Intel Xeon Processor 5500 Series Datasheet Volume 2
1310 * http://www.intel.com/Assets/PDF/datasheet/321322.pdf
1311 * also available at:
1312 * http://www.arrownac.com/manufacturers/intel/s/nehalem/5500-datasheet-v2.pdf
1313 */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001314static void check_mc_test_err(struct mem_ctl_info *mci, u8 socket)
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001315{
1316 struct i7core_pvt *pvt = mci->pvt_info;
1317 u32 rcv1, rcv0;
1318 int new0, new1, new2;
1319
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001320 if (!pvt->pci_mcr[socket][4]) {
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001321 debugf0("%s MCR registers not found\n",__func__);
1322 return;
1323 }
1324
1325 /* Corrected error reads */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001326 pci_read_config_dword(pvt->pci_mcr[socket][4], MC_TEST_ERR_RCV1, &rcv1);
1327 pci_read_config_dword(pvt->pci_mcr[socket][4], MC_TEST_ERR_RCV0, &rcv0);
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001328
1329 /* Store the new values */
1330 new2 = DIMM2_COR_ERR(rcv1);
1331 new1 = DIMM1_COR_ERR(rcv0);
1332 new0 = DIMM0_COR_ERR(rcv0);
1333
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001334#if 0
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001335 debugf2("%s CE rcv1=0x%08x rcv0=0x%08x, %d %d %d\n",
1336 (pvt->ce_count_available ? "UPDATE" : "READ"),
1337 rcv1, rcv0, new0, new1, new2);
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001338#endif
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001339
1340 /* Updates CE counters if it is not the first time here */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001341 if (pvt->ce_count_available[socket]) {
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001342 /* Updates CE counters */
1343 int add0, add1, add2;
1344
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001345 add2 = new2 - pvt->last_ce_count[socket][2];
1346 add1 = new1 - pvt->last_ce_count[socket][1];
1347 add0 = new0 - pvt->last_ce_count[socket][0];
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001348
1349 if (add2 < 0)
1350 add2 += 0x7fff;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001351 pvt->ce_count[socket][2] += add2;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001352
1353 if (add1 < 0)
1354 add1 += 0x7fff;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001355 pvt->ce_count[socket][1] += add1;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001356
1357 if (add0 < 0)
1358 add0 += 0x7fff;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001359 pvt->ce_count[socket][0] += add0;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001360 } else
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001361 pvt->ce_count_available[socket] = 1;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001362
1363 /* Store the new values */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001364 pvt->last_ce_count[socket][2] = new2;
1365 pvt->last_ce_count[socket][1] = new1;
1366 pvt->last_ce_count[socket][0] = new0;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001367}
1368
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001369/*
1370 * According with tables E-11 and E-12 of chapter E.3.3 of Intel 64 and IA-32
1371 * Architectures Software Developer’s Manual Volume 3B.
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001372 * Nehalem are defined as family 0x06, model 0x1a
1373 *
1374 * The MCA registers used here are the following ones:
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001375 * struct mce field MCA Register
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001376 * m->status MSR_IA32_MC8_STATUS
1377 * m->addr MSR_IA32_MC8_ADDR
1378 * m->misc MSR_IA32_MC8_MISC
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001379 * In the case of Nehalem, the error information is masked at .status and .misc
1380 * fields
1381 */
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001382static void i7core_mce_output_error(struct mem_ctl_info *mci,
1383 struct mce *m)
1384{
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001385 char *type, *optype, *err, *msg;
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001386 unsigned long error = m->status & 0x1ff0000l;
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001387 u32 optypenum = (m->status >> 4) & 0x07;
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001388 u32 core_err_cnt = (m->status >> 38) && 0x7fff;
1389 u32 dimm = (m->misc >> 16) & 0x3;
1390 u32 channel = (m->misc >> 18) & 0x3;
1391 u32 syndrome = m->misc >> 32;
1392 u32 errnum = find_first_bit(&error, 32);
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001393
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001394 if (m->mcgstatus & 1)
1395 type = "FATAL";
1396 else
1397 type = "NON_FATAL";
1398
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001399 switch (optypenum) {
1400 case 0:
1401 optype = "generic undef request";
1402 break;
1403 case 1:
1404 optype = "read error";
1405 break;
1406 case 2:
1407 optype = "write error";
1408 break;
1409 case 3:
1410 optype = "addr/cmd error";
1411 break;
1412 case 4:
1413 optype = "scrubbing error";
1414 break;
1415 default:
1416 optype = "reserved";
1417 break;
1418 }
1419
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001420 switch (errnum) {
1421 case 16:
1422 err = "read ECC error";
1423 break;
1424 case 17:
1425 err = "RAS ECC error";
1426 break;
1427 case 18:
1428 err = "write parity error";
1429 break;
1430 case 19:
1431 err = "redundacy loss";
1432 break;
1433 case 20:
1434 err = "reserved";
1435 break;
1436 case 21:
1437 err = "memory range error";
1438 break;
1439 case 22:
1440 err = "RTID out of range";
1441 break;
1442 case 23:
1443 err = "address parity error";
1444 break;
1445 case 24:
1446 err = "byte enable parity error";
1447 break;
1448 default:
1449 err = "unknown";
1450 }
1451
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001452 /* FIXME: should convert addr into bank and rank information */
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001453 msg = kasprintf(GFP_ATOMIC,
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001454 "%s (addr = 0x%08llx, Dimm=%d, Channel=%d, "
1455 "syndrome=0x%08x, count=%d, Err=%08llx:%08llx (%s: %s))\n",
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001456 type, (long long) m->addr, dimm, channel,
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001457 syndrome, core_err_cnt, (long long)m->status,
1458 (long long)m->misc, optype, err);
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001459
1460 debugf0("%s", msg);
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001461
1462 /* Call the helper to output message */
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001463 edac_mc_handle_fbd_ue(mci, 0 /* FIXME: should be rank here */,
1464 0, 0 /* FIXME: should be channel here */, msg);
1465
1466 kfree(msg);
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001467}
1468
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001469/*
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001470 * i7core_check_error Retrieve and process errors reported by the
1471 * hardware. Called by the Core module.
1472 */
1473static void i7core_check_error(struct mem_ctl_info *mci)
1474{
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001475 struct i7core_pvt *pvt = mci->pvt_info;
1476 int i;
1477 unsigned count = 0;
1478 struct mce *m = NULL;
1479 unsigned long flags;
1480
1481 debugf0(__FILE__ ": %s()\n", __func__);
1482
1483 /* Copy all mce errors into a temporary buffer */
1484 spin_lock_irqsave(&pvt->mce_lock, flags);
1485 if (pvt->mce_count) {
1486 m = kmalloc(sizeof(*m) * pvt->mce_count, GFP_ATOMIC);
1487 if (m) {
1488 count = pvt->mce_count;
1489 memcpy(m, &pvt->mce_entry, sizeof(*m) * count);
1490 }
1491 pvt->mce_count = 0;
1492 }
1493 spin_unlock_irqrestore(&pvt->mce_lock, flags);
1494
1495 /* proccess mcelog errors */
1496 for (i = 0; i < count; i++)
1497 i7core_mce_output_error(mci, &m[i]);
1498
1499 kfree(m);
1500
1501 /* check memory count errors */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001502 for (i = 0; i < pvt->sockets; i++)
1503 check_mc_test_err(mci, i);
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001504}
1505
1506/*
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001507 * i7core_mce_check_error Replicates mcelog routine to get errors
1508 * This routine simply queues mcelog errors, and
1509 * return. The error itself should be handled later
1510 * by i7core_check_error.
1511 */
1512static int i7core_mce_check_error(void *priv, struct mce *mce)
1513{
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001514 struct mem_ctl_info *mci = priv;
1515 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001516 unsigned long flags;
1517
1518 debugf0(__FILE__ ": %s()\n", __func__);
1519
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001520 /*
1521 * Just let mcelog handle it if the error is
1522 * outside the memory controller
1523 */
1524 if (((mce->status & 0xffff) >> 7) != 1)
1525 return 0;
1526
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001527 /* Bank 8 registers are the only ones that we know how to handle */
1528 if (mce->bank != 8)
1529 return 0;
1530
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001531 spin_lock_irqsave(&pvt->mce_lock, flags);
1532 if (pvt->mce_count < MCE_LOG_LEN) {
1533 memcpy(&pvt->mce_entry[pvt->mce_count], mce, sizeof(*mce));
1534 pvt->mce_count++;
1535 }
1536 spin_unlock_irqrestore(&pvt->mce_lock, flags);
1537
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001538 /* Handle fatal errors immediately */
1539 if (mce->mcgstatus & 1)
1540 i7core_check_error(mci);
1541
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001542 /* Advice mcelog that the error were handled */
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001543 return 1;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001544}
1545
1546/*
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001547 * i7core_probe Probe for ONE instance of device to see if it is
1548 * present.
1549 * return:
1550 * 0 for FOUND a device
1551 * < 0 for error code
1552 */
1553static int __devinit i7core_probe(struct pci_dev *pdev,
1554 const struct pci_device_id *id)
1555{
1556 struct mem_ctl_info *mci;
1557 struct i7core_pvt *pvt;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001558 int num_channels = 0;
1559 int num_csrows = 0;
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -03001560 int csrow = 0;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001561 int dev_idx = id->driver_data;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001562 int rc, i;
1563 u8 sockets;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001564
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001565 if (unlikely(dev_idx >= ARRAY_SIZE(i7core_devs)))
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001566 return -EINVAL;
1567
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001568 /* get the pci devices we want to reserve for our use */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001569 rc = i7core_get_devices();
1570 if (unlikely(rc < 0))
1571 return rc;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001572
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001573 sockets = 1;
1574 for (i = NUM_SOCKETS - 1; i > 0; i--)
1575 if (pci_devs[0].pdev[i]) {
1576 sockets = i + 1;
1577 break;
1578 }
1579
1580 for (i = 0; i < sockets; i++) {
1581 int channels;
1582 int csrows;
1583
1584 /* Check the number of active and not disabled channels */
1585 rc = i7core_get_active_channels(i, &channels, &csrows);
1586 if (unlikely(rc < 0))
1587 goto fail0;
1588
1589 num_channels += channels;
1590 num_csrows += csrows;
1591 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001592
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001593 /* allocate a new MC control structure */
1594 mci = edac_mc_alloc(sizeof(*pvt), num_csrows, num_channels, 0);
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001595 if (unlikely(!mci)) {
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001596 rc = -ENOMEM;
1597 goto fail0;
1598 }
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001599
1600 debugf0("MC: " __FILE__ ": %s(): mci = %p\n", __func__, mci);
1601
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001602 mci->dev = &pdev->dev; /* record ptr to the generic device */
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001603 pvt = mci->pvt_info;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001604 memset(pvt, 0, sizeof(*pvt));
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001605 pvt->sockets = sockets;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001606 mci->mc_idx = 0;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001607
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001608 /*
1609 * FIXME: how to handle RDDR3 at MCI level? It is possible to have
1610 * Mixed RDDR3/UDDR3 with Nehalem, provided that they are on different
1611 * memory channels
1612 */
1613 mci->mtype_cap = MEM_FLAG_DDR3;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001614 mci->edac_ctl_cap = EDAC_FLAG_NONE;
1615 mci->edac_cap = EDAC_FLAG_NONE;
1616 mci->mod_name = "i7core_edac.c";
1617 mci->mod_ver = I7CORE_REVISION;
1618 mci->ctl_name = i7core_devs[dev_idx].ctl_name;
1619 mci->dev_name = pci_name(pdev);
1620 mci->ctl_page_to_phys = NULL;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001621 mci->mc_driver_sysfs_attributes = i7core_inj_attrs;
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001622 /* Set the function pointer to an actual operation function */
1623 mci->edac_check = i7core_check_error;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001624
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001625 /* Store pci devices at mci for faster access */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001626 rc = mci_bind_devs(mci);
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001627 if (unlikely(rc < 0))
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001628 goto fail1;
1629
1630 /* Get dimm basic config */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001631 for (i = 0; i < sockets; i++)
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -03001632 get_dimm_config(mci, &csrow, i);
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001633
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001634 /* add this new MC control structure to EDAC's list of MCs */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001635 if (unlikely(edac_mc_add_mc(mci))) {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001636 debugf0("MC: " __FILE__
1637 ": %s(): failed edac_mc_add_mc()\n", __func__);
1638 /* FIXME: perhaps some code should go here that disables error
1639 * reporting if we just enabled it
1640 */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001641
1642 rc = -EINVAL;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001643 goto fail1;
1644 }
1645
1646 /* allocating generic PCI control info */
1647 i7core_pci = edac_pci_create_generic_ctl(&pdev->dev, EDAC_MOD_STR);
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001648 if (unlikely(!i7core_pci)) {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001649 printk(KERN_WARNING
1650 "%s(): Unable to create PCI control\n",
1651 __func__);
1652 printk(KERN_WARNING
1653 "%s(): PCI error report via EDAC not setup\n",
1654 __func__);
1655 }
1656
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001657 /* Default error mask is any memory */
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001658 pvt->inject.channel = 0;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001659 pvt->inject.dimm = -1;
1660 pvt->inject.rank = -1;
1661 pvt->inject.bank = -1;
1662 pvt->inject.page = -1;
1663 pvt->inject.col = -1;
1664
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001665 /* Registers on edac_mce in order to receive memory errors */
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001666 pvt->edac_mce.priv = mci;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001667 pvt->edac_mce.check_error = i7core_mce_check_error;
1668 spin_lock_init(&pvt->mce_lock);
1669
1670 rc = edac_mce_register(&pvt->edac_mce);
1671 if (unlikely (rc < 0)) {
1672 debugf0("MC: " __FILE__
1673 ": %s(): failed edac_mce_register()\n", __func__);
1674 goto fail1;
1675 }
1676
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001677 i7core_printk(KERN_INFO, "Driver loaded.\n");
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001678
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001679 return 0;
1680
1681fail1:
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001682 edac_mc_free(mci);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001683
1684fail0:
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001685 i7core_put_devices();
1686 return rc;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001687}
1688
1689/*
1690 * i7core_remove destructor for one instance of device
1691 *
1692 */
1693static void __devexit i7core_remove(struct pci_dev *pdev)
1694{
1695 struct mem_ctl_info *mci;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001696 struct i7core_pvt *pvt;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001697
1698 debugf0(__FILE__ ": %s()\n", __func__);
1699
1700 if (i7core_pci)
1701 edac_pci_release_generic_ctl(i7core_pci);
1702
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001703
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001704 mci = edac_mc_del_mc(&pdev->dev);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001705 if (!mci)
1706 return;
1707
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001708 /* Unregisters on edac_mce in order to receive memory errors */
1709 pvt = mci->pvt_info;
1710 edac_mce_unregister(&pvt->edac_mce);
1711
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001712 /* retrieve references to resources, and free those resources */
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001713 i7core_put_devices();
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001714
1715 edac_mc_free(mci);
1716}
1717
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001718MODULE_DEVICE_TABLE(pci, i7core_pci_tbl);
1719
1720/*
1721 * i7core_driver pci_driver structure for this module
1722 *
1723 */
1724static struct pci_driver i7core_driver = {
1725 .name = "i7core_edac",
1726 .probe = i7core_probe,
1727 .remove = __devexit_p(i7core_remove),
1728 .id_table = i7core_pci_tbl,
1729};
1730
1731/*
1732 * i7core_init Module entry function
1733 * Try to initialize this module for its devices
1734 */
1735static int __init i7core_init(void)
1736{
1737 int pci_rc;
1738
1739 debugf2("MC: " __FILE__ ": %s()\n", __func__);
1740
1741 /* Ensure that the OPSTATE is set correctly for POLL or NMI */
1742 opstate_init();
1743
1744 pci_rc = pci_register_driver(&i7core_driver);
1745
1746 return (pci_rc < 0) ? pci_rc : 0;
1747}
1748
1749/*
1750 * i7core_exit() Module exit function
1751 * Unregister the driver
1752 */
1753static void __exit i7core_exit(void)
1754{
1755 debugf2("MC: " __FILE__ ": %s()\n", __func__);
1756 pci_unregister_driver(&i7core_driver);
1757}
1758
1759module_init(i7core_init);
1760module_exit(i7core_exit);
1761
1762MODULE_LICENSE("GPL");
1763MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
1764MODULE_AUTHOR("Red Hat Inc. (http://www.redhat.com)");
1765MODULE_DESCRIPTION("MC Driver for Intel i7 Core memory controllers - "
1766 I7CORE_REVISION);
1767
1768module_param(edac_op_state, int, 0444);
1769MODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI");