blob: e2f6dfdca8415aea82a42c9f790ce775d956c594 [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 Chehab67166af2009-07-15 06:56:23 -0300413 debugf0("Number of active channels on socked %d: %d\n",
414 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 Chehabef708b52009-06-22 22:48:30 -03001129static int i7core_get_devices(void)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001130{
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001131 int rc, i;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001132 struct pci_dev *pdev = NULL;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001133 u8 bus = 0;
1134 u8 socket = 0;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001135
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001136 for (i = 0; i < N_DEVS; i++) {
1137 pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
1138 pci_devs[i].dev_id, NULL);
Mauro Carvalho Chehabd1fd4fb2009-07-10 18:39:53 -03001139
1140 if (!pdev && !i) {
1141 pcibios_scan_specific_bus(254);
1142 pcibios_scan_specific_bus(255);
1143
1144 pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
1145 pci_devs[i].dev_id, NULL);
1146 }
1147
Mauro Carvalho Chehab310cbb72009-07-17 00:09:10 -03001148 /*
1149 * On Xeon 55xx, the Intel Quckpath Arch Generic Non-core regs
1150 * is at addr 8086:2c40, instead of 8086:2c41. So, we need
1151 * to probe for the alternate address in case of failure
1152 */
1153 if (pci_devs[i].dev_id == PCI_DEVICE_ID_INTEL_I7_NOCORE
1154 && !pdev)
1155 pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
1156 PCI_DEVICE_ID_INTEL_I7_NOCORE_ALT, NULL);
1157
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001158 if (likely(pdev)) {
1159 bus = pdev->bus->number;
1160
1161 if (bus == 0x3f)
1162 socket = 0;
1163 else
1164 socket = 255 - bus;
1165
1166 if (socket >= NUM_SOCKETS) {
1167 i7core_printk(KERN_ERR,
1168 "Found unexpected socket for "
1169 "dev %02x:%02x.%d PCI ID %04x:%04x\n",
1170 bus, pci_devs[i].dev, pci_devs[i].func,
1171 PCI_VENDOR_ID_INTEL, pci_devs[i].dev_id);
1172
1173 rc = -ENODEV;
1174 goto error;
1175 }
1176
1177 pci_devs[i].pdev[socket] = pdev;
1178 } else {
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001179 i7core_printk(KERN_ERR,
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001180 "Device not found: "
1181 "dev %02x:%02x.%d PCI ID %04x:%04x\n",
1182 bus, pci_devs[i].dev, pci_devs[i].func,
1183 PCI_VENDOR_ID_INTEL, pci_devs[i].dev_id);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001184
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001185 /* Dev 3 function 2 only exists on chips with RDIMMs */
1186 if ((pci_devs[i].dev == 3) && (pci_devs[i].func == 2))
1187 continue;
1188
1189 /* End of list, leave */
1190 rc = -ENODEV;
1191 goto error;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001192 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001193
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001194 /* Sanity check */
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001195 if (unlikely(PCI_SLOT(pdev->devfn) != pci_devs[i].dev ||
1196 PCI_FUNC(pdev->devfn) != pci_devs[i].func)) {
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001197 i7core_printk(KERN_ERR,
1198 "Device PCI ID %04x:%04x "
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001199 "has fn %d.%d instead of fn %d.%d\n",
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001200 PCI_VENDOR_ID_INTEL, pci_devs[i].dev_id,
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001201 PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
1202 pci_devs[i].dev, pci_devs[i].func);
1203 rc = -EINVAL;
1204 goto error;
1205 }
1206
1207 /* Be sure that the device is enabled */
1208 rc = pci_enable_device(pdev);
1209 if (unlikely(rc < 0)) {
1210 i7core_printk(KERN_ERR,
1211 "Couldn't enable PCI ID %04x:%04x "
1212 "fn %d.%d\n",
1213 PCI_VENDOR_ID_INTEL, pci_devs[i].dev_id,
1214 PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
1215 goto error;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001216 }
1217
1218 i7core_printk(KERN_INFO,
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001219 "Registered socket %d "
1220 "dev %02x:%02x.%d PCI ID %04x:%04x\n",
1221 socket, bus, pci_devs[i].dev, pci_devs[i].func,
1222 PCI_VENDOR_ID_INTEL, pci_devs[i].dev_id);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001223 }
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001224
1225 return 0;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001226
1227error:
1228 i7core_put_devices();
1229 return -EINVAL;
1230}
1231
1232static int mci_bind_devs(struct mem_ctl_info *mci)
1233{
1234 struct i7core_pvt *pvt = mci->pvt_info;
1235 struct pci_dev *pdev;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001236 int i, j, func, slot;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001237
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001238 for (i = 0; i < pvt->sockets; i++) {
1239 for (j = 0; j < N_DEVS; j++) {
1240 pdev = pci_devs[j].pdev[i];
1241 if (!pdev)
1242 continue;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001243
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001244 func = PCI_FUNC(pdev->devfn);
1245 slot = PCI_SLOT(pdev->devfn);
1246 if (slot == 3) {
1247 if (unlikely(func > MAX_MCR_FUNC))
1248 goto error;
1249 pvt->pci_mcr[i][func] = pdev;
1250 } else if (likely(slot >= 4 && slot < 4 + NUM_CHANS)) {
1251 if (unlikely(func > MAX_CHAN_FUNC))
1252 goto error;
1253 pvt->pci_ch[i][slot - 4][func] = pdev;
1254 } else if (!slot && !func)
1255 pvt->pci_noncore[i] = pdev;
1256 else
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001257 goto error;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001258
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001259 debugf0("Associated fn %d.%d, dev = %p, socket %d\n",
1260 PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
1261 pdev, i);
1262 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001263 }
Mauro Carvalho Chehabe9bd2e72009-07-09 22:14:35 -03001264
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001265 return 0;
1266
1267error:
1268 i7core_printk(KERN_ERR, "Device %d, function %d "
1269 "is out of the expected range\n",
1270 slot, func);
1271 return -EINVAL;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001272}
1273
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001274/****************************************************************************
1275 Error check routines
1276 ****************************************************************************/
1277
1278/* This function is based on the device 3 function 4 registers as described on:
1279 * Intel Xeon Processor 5500 Series Datasheet Volume 2
1280 * http://www.intel.com/Assets/PDF/datasheet/321322.pdf
1281 * also available at:
1282 * http://www.arrownac.com/manufacturers/intel/s/nehalem/5500-datasheet-v2.pdf
1283 */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001284static void check_mc_test_err(struct mem_ctl_info *mci, u8 socket)
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001285{
1286 struct i7core_pvt *pvt = mci->pvt_info;
1287 u32 rcv1, rcv0;
1288 int new0, new1, new2;
1289
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001290 if (!pvt->pci_mcr[socket][4]) {
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001291 debugf0("%s MCR registers not found\n",__func__);
1292 return;
1293 }
1294
1295 /* Corrected error reads */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001296 pci_read_config_dword(pvt->pci_mcr[socket][4], MC_TEST_ERR_RCV1, &rcv1);
1297 pci_read_config_dword(pvt->pci_mcr[socket][4], MC_TEST_ERR_RCV0, &rcv0);
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001298
1299 /* Store the new values */
1300 new2 = DIMM2_COR_ERR(rcv1);
1301 new1 = DIMM1_COR_ERR(rcv0);
1302 new0 = DIMM0_COR_ERR(rcv0);
1303
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001304#if 0
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001305 debugf2("%s CE rcv1=0x%08x rcv0=0x%08x, %d %d %d\n",
1306 (pvt->ce_count_available ? "UPDATE" : "READ"),
1307 rcv1, rcv0, new0, new1, new2);
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001308#endif
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001309
1310 /* Updates CE counters if it is not the first time here */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001311 if (pvt->ce_count_available[socket]) {
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001312 /* Updates CE counters */
1313 int add0, add1, add2;
1314
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001315 add2 = new2 - pvt->last_ce_count[socket][2];
1316 add1 = new1 - pvt->last_ce_count[socket][1];
1317 add0 = new0 - pvt->last_ce_count[socket][0];
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001318
1319 if (add2 < 0)
1320 add2 += 0x7fff;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001321 pvt->ce_count[socket][2] += add2;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001322
1323 if (add1 < 0)
1324 add1 += 0x7fff;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001325 pvt->ce_count[socket][1] += add1;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001326
1327 if (add0 < 0)
1328 add0 += 0x7fff;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001329 pvt->ce_count[socket][0] += add0;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001330 } else
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001331 pvt->ce_count_available[socket] = 1;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001332
1333 /* Store the new values */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001334 pvt->last_ce_count[socket][2] = new2;
1335 pvt->last_ce_count[socket][1] = new1;
1336 pvt->last_ce_count[socket][0] = new0;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001337}
1338
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001339/*
1340 * According with tables E-11 and E-12 of chapter E.3.3 of Intel 64 and IA-32
1341 * Architectures Software Developer’s Manual Volume 3B.
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001342 * Nehalem are defined as family 0x06, model 0x1a
1343 *
1344 * The MCA registers used here are the following ones:
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001345 * struct mce field MCA Register
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001346 * m->status MSR_IA32_MC8_STATUS
1347 * m->addr MSR_IA32_MC8_ADDR
1348 * m->misc MSR_IA32_MC8_MISC
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001349 * In the case of Nehalem, the error information is masked at .status and .misc
1350 * fields
1351 */
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001352static void i7core_mce_output_error(struct mem_ctl_info *mci,
1353 struct mce *m)
1354{
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001355 char *type="NON-FATAL";
1356 char *err, *msg;
1357 unsigned long error = m->status & 0x1ff0000l;
1358 u32 core_err_cnt = (m->status >> 38) && 0x7fff;
1359 u32 dimm = (m->misc >> 16) & 0x3;
1360 u32 channel = (m->misc >> 18) & 0x3;
1361 u32 syndrome = m->misc >> 32;
1362 u32 errnum = find_first_bit(&error, 32);
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001363
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001364 switch (errnum) {
1365 case 16:
1366 err = "read ECC error";
1367 break;
1368 case 17:
1369 err = "RAS ECC error";
1370 break;
1371 case 18:
1372 err = "write parity error";
1373 break;
1374 case 19:
1375 err = "redundacy loss";
1376 break;
1377 case 20:
1378 err = "reserved";
1379 break;
1380 case 21:
1381 err = "memory range error";
1382 break;
1383 case 22:
1384 err = "RTID out of range";
1385 break;
1386 case 23:
1387 err = "address parity error";
1388 break;
1389 case 24:
1390 err = "byte enable parity error";
1391 break;
1392 default:
1393 err = "unknown";
1394 }
1395
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001396 /* FIXME: should convert addr into bank and rank information */
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001397 msg = kasprintf(GFP_ATOMIC,
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001398 "%s (addr = 0x%08llx Dimm=%d, Channel=%d, "
1399 "syndrome=0x%08x, count=%d Err=%d (%s))\n",
1400 type, (long long) m->addr, dimm, channel,
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001401 syndrome, core_err_cnt,errnum, err);
1402
1403 debugf0("%s", msg);
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001404
1405 /* Call the helper to output message */
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001406 edac_mc_handle_fbd_ue(mci, 0 /* FIXME: should be rank here */,
1407 0, 0 /* FIXME: should be channel here */, msg);
1408
1409 kfree(msg);
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001410}
1411
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001412/*
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001413 * i7core_check_error Retrieve and process errors reported by the
1414 * hardware. Called by the Core module.
1415 */
1416static void i7core_check_error(struct mem_ctl_info *mci)
1417{
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001418 struct i7core_pvt *pvt = mci->pvt_info;
1419 int i;
1420 unsigned count = 0;
1421 struct mce *m = NULL;
1422 unsigned long flags;
1423
1424 debugf0(__FILE__ ": %s()\n", __func__);
1425
1426 /* Copy all mce errors into a temporary buffer */
1427 spin_lock_irqsave(&pvt->mce_lock, flags);
1428 if (pvt->mce_count) {
1429 m = kmalloc(sizeof(*m) * pvt->mce_count, GFP_ATOMIC);
1430 if (m) {
1431 count = pvt->mce_count;
1432 memcpy(m, &pvt->mce_entry, sizeof(*m) * count);
1433 }
1434 pvt->mce_count = 0;
1435 }
1436 spin_unlock_irqrestore(&pvt->mce_lock, flags);
1437
1438 /* proccess mcelog errors */
1439 for (i = 0; i < count; i++)
1440 i7core_mce_output_error(mci, &m[i]);
1441
1442 kfree(m);
1443
1444 /* check memory count errors */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001445 for (i = 0; i < pvt->sockets; i++)
1446 check_mc_test_err(mci, i);
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001447}
1448
1449/*
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001450 * i7core_mce_check_error Replicates mcelog routine to get errors
1451 * This routine simply queues mcelog errors, and
1452 * return. The error itself should be handled later
1453 * by i7core_check_error.
1454 */
1455static int i7core_mce_check_error(void *priv, struct mce *mce)
1456{
1457 struct i7core_pvt *pvt = priv;
1458 unsigned long flags;
1459
1460 debugf0(__FILE__ ": %s()\n", __func__);
1461
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001462 /*
1463 * Just let mcelog handle it if the error is
1464 * outside the memory controller
1465 */
1466 if (((mce->status & 0xffff) >> 7) != 1)
1467 return 0;
1468
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001469 /* Bank 8 registers are the only ones that we know how to handle */
1470 if (mce->bank != 8)
1471 return 0;
1472
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001473 spin_lock_irqsave(&pvt->mce_lock, flags);
1474 if (pvt->mce_count < MCE_LOG_LEN) {
1475 memcpy(&pvt->mce_entry[pvt->mce_count], mce, sizeof(*mce));
1476 pvt->mce_count++;
1477 }
1478 spin_unlock_irqrestore(&pvt->mce_lock, flags);
1479
1480 /* Advice mcelog that the error were handled */
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001481 return 1;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001482}
1483
1484/*
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001485 * i7core_probe Probe for ONE instance of device to see if it is
1486 * present.
1487 * return:
1488 * 0 for FOUND a device
1489 * < 0 for error code
1490 */
1491static int __devinit i7core_probe(struct pci_dev *pdev,
1492 const struct pci_device_id *id)
1493{
1494 struct mem_ctl_info *mci;
1495 struct i7core_pvt *pvt;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001496 int num_channels = 0;
1497 int num_csrows = 0;
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -03001498 int csrow = 0;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001499 int dev_idx = id->driver_data;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001500 int rc, i;
1501 u8 sockets;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001502
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001503 if (unlikely(dev_idx >= ARRAY_SIZE(i7core_devs)))
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001504 return -EINVAL;
1505
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001506 /* get the pci devices we want to reserve for our use */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001507 rc = i7core_get_devices();
1508 if (unlikely(rc < 0))
1509 return rc;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001510
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001511 sockets = 1;
1512 for (i = NUM_SOCKETS - 1; i > 0; i--)
1513 if (pci_devs[0].pdev[i]) {
1514 sockets = i + 1;
1515 break;
1516 }
1517
1518 for (i = 0; i < sockets; i++) {
1519 int channels;
1520 int csrows;
1521
1522 /* Check the number of active and not disabled channels */
1523 rc = i7core_get_active_channels(i, &channels, &csrows);
1524 if (unlikely(rc < 0))
1525 goto fail0;
1526
1527 num_channels += channels;
1528 num_csrows += csrows;
1529 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001530
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001531 /* allocate a new MC control structure */
1532 mci = edac_mc_alloc(sizeof(*pvt), num_csrows, num_channels, 0);
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001533 if (unlikely(!mci)) {
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001534 rc = -ENOMEM;
1535 goto fail0;
1536 }
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001537
1538 debugf0("MC: " __FILE__ ": %s(): mci = %p\n", __func__, mci);
1539
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001540 mci->dev = &pdev->dev; /* record ptr to the generic device */
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001541 pvt = mci->pvt_info;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001542 memset(pvt, 0, sizeof(*pvt));
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001543 pvt->sockets = sockets;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001544 mci->mc_idx = 0;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001545
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001546 /*
1547 * FIXME: how to handle RDDR3 at MCI level? It is possible to have
1548 * Mixed RDDR3/UDDR3 with Nehalem, provided that they are on different
1549 * memory channels
1550 */
1551 mci->mtype_cap = MEM_FLAG_DDR3;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001552 mci->edac_ctl_cap = EDAC_FLAG_NONE;
1553 mci->edac_cap = EDAC_FLAG_NONE;
1554 mci->mod_name = "i7core_edac.c";
1555 mci->mod_ver = I7CORE_REVISION;
1556 mci->ctl_name = i7core_devs[dev_idx].ctl_name;
1557 mci->dev_name = pci_name(pdev);
1558 mci->ctl_page_to_phys = NULL;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001559 mci->mc_driver_sysfs_attributes = i7core_inj_attrs;
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001560 /* Set the function pointer to an actual operation function */
1561 mci->edac_check = i7core_check_error;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001562
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001563 /* Store pci devices at mci for faster access */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001564 rc = mci_bind_devs(mci);
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001565 if (unlikely(rc < 0))
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001566 goto fail1;
1567
1568 /* Get dimm basic config */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001569 for (i = 0; i < sockets; i++)
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -03001570 get_dimm_config(mci, &csrow, i);
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001571
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001572 /* add this new MC control structure to EDAC's list of MCs */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001573 if (unlikely(edac_mc_add_mc(mci))) {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001574 debugf0("MC: " __FILE__
1575 ": %s(): failed edac_mc_add_mc()\n", __func__);
1576 /* FIXME: perhaps some code should go here that disables error
1577 * reporting if we just enabled it
1578 */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001579
1580 rc = -EINVAL;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001581 goto fail1;
1582 }
1583
1584 /* allocating generic PCI control info */
1585 i7core_pci = edac_pci_create_generic_ctl(&pdev->dev, EDAC_MOD_STR);
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001586 if (unlikely(!i7core_pci)) {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001587 printk(KERN_WARNING
1588 "%s(): Unable to create PCI control\n",
1589 __func__);
1590 printk(KERN_WARNING
1591 "%s(): PCI error report via EDAC not setup\n",
1592 __func__);
1593 }
1594
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001595 /* Default error mask is any memory */
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001596 pvt->inject.channel = 0;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001597 pvt->inject.dimm = -1;
1598 pvt->inject.rank = -1;
1599 pvt->inject.bank = -1;
1600 pvt->inject.page = -1;
1601 pvt->inject.col = -1;
1602
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001603 /* Registers on edac_mce in order to receive memory errors */
1604 pvt->edac_mce.priv = pvt;
1605 pvt->edac_mce.check_error = i7core_mce_check_error;
1606 spin_lock_init(&pvt->mce_lock);
1607
1608 rc = edac_mce_register(&pvt->edac_mce);
1609 if (unlikely (rc < 0)) {
1610 debugf0("MC: " __FILE__
1611 ": %s(): failed edac_mce_register()\n", __func__);
1612 goto fail1;
1613 }
1614
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001615 i7core_printk(KERN_INFO, "Driver loaded.\n");
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001616
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001617 return 0;
1618
1619fail1:
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001620 edac_mc_free(mci);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001621
1622fail0:
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001623 i7core_put_devices();
1624 return rc;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001625}
1626
1627/*
1628 * i7core_remove destructor for one instance of device
1629 *
1630 */
1631static void __devexit i7core_remove(struct pci_dev *pdev)
1632{
1633 struct mem_ctl_info *mci;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001634 struct i7core_pvt *pvt;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001635
1636 debugf0(__FILE__ ": %s()\n", __func__);
1637
1638 if (i7core_pci)
1639 edac_pci_release_generic_ctl(i7core_pci);
1640
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001641
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001642 mci = edac_mc_del_mc(&pdev->dev);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001643 if (!mci)
1644 return;
1645
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001646 /* Unregisters on edac_mce in order to receive memory errors */
1647 pvt = mci->pvt_info;
1648 edac_mce_unregister(&pvt->edac_mce);
1649
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001650 /* retrieve references to resources, and free those resources */
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001651 i7core_put_devices();
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001652
1653 edac_mc_free(mci);
1654}
1655
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001656MODULE_DEVICE_TABLE(pci, i7core_pci_tbl);
1657
1658/*
1659 * i7core_driver pci_driver structure for this module
1660 *
1661 */
1662static struct pci_driver i7core_driver = {
1663 .name = "i7core_edac",
1664 .probe = i7core_probe,
1665 .remove = __devexit_p(i7core_remove),
1666 .id_table = i7core_pci_tbl,
1667};
1668
1669/*
1670 * i7core_init Module entry function
1671 * Try to initialize this module for its devices
1672 */
1673static int __init i7core_init(void)
1674{
1675 int pci_rc;
1676
1677 debugf2("MC: " __FILE__ ": %s()\n", __func__);
1678
1679 /* Ensure that the OPSTATE is set correctly for POLL or NMI */
1680 opstate_init();
1681
1682 pci_rc = pci_register_driver(&i7core_driver);
1683
1684 return (pci_rc < 0) ? pci_rc : 0;
1685}
1686
1687/*
1688 * i7core_exit() Module exit function
1689 * Unregister the driver
1690 */
1691static void __exit i7core_exit(void)
1692{
1693 debugf2("MC: " __FILE__ ": %s()\n", __func__);
1694 pci_unregister_driver(&i7core_driver);
1695}
1696
1697module_init(i7core_init);
1698module_exit(i7core_exit);
1699
1700MODULE_LICENSE("GPL");
1701MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
1702MODULE_AUTHOR("Red Hat Inc. (http://www.redhat.com)");
1703MODULE_DESCRIPTION("MC Driver for Intel i7 Core memory controllers - "
1704 I7CORE_REVISION);
1705
1706module_param(edac_op_state, int, 0444);
1707MODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI");