blob: f16aac253654240963eba9382e845456a1591ba3 [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 Chehaba0c36a12009-06-22 22:41:15 -030035/*
36 * Alter this version for the module when modifications are made
37 */
38#define I7CORE_REVISION " Ver: 1.0.0 " __DATE__
39#define EDAC_MOD_STR "i7core_edac"
40
41/* HACK: temporary, just to enable all logs, for now */
42#undef debugf0
43#define debugf0(fmt, arg...) edac_printk(KERN_INFO, "i7core", fmt, ##arg)
44
45/*
46 * Debug macros
47 */
48#define i7core_printk(level, fmt, arg...) \
49 edac_printk(level, "i7core", fmt, ##arg)
50
51#define i7core_mc_printk(mci, level, fmt, arg...) \
52 edac_mc_chipset_printk(mci, level, "i7core", fmt, ##arg)
53
54/*
55 * i7core Memory Controller Registers
56 */
57
Mauro Carvalho Chehabe9bd2e72009-07-09 22:14:35 -030058 /* OFFSETS for Device 0 Function 0 */
59
60#define MC_CFG_CONTROL 0x90
61
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030062 /* OFFSETS for Device 3 Function 0 */
63
64#define MC_CONTROL 0x48
65#define MC_STATUS 0x4c
66#define MC_MAX_DOD 0x64
67
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -030068/*
69 * OFFSETS for Device 3 Function 4, as inicated on Xeon 5500 datasheet:
70 * http://www.arrownac.com/manufacturers/intel/s/nehalem/5500-datasheet-v2.pdf
71 */
72
73#define MC_TEST_ERR_RCV1 0x60
74 #define DIMM2_COR_ERR(r) ((r) & 0x7fff)
75
76#define MC_TEST_ERR_RCV0 0x64
77 #define DIMM1_COR_ERR(r) (((r) >> 16) & 0x7fff)
78 #define DIMM0_COR_ERR(r) ((r) & 0x7fff)
79
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030080 /* OFFSETS for Devices 4,5 and 6 Function 0 */
81
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -030082#define MC_CHANNEL_DIMM_INIT_PARAMS 0x58
83 #define THREE_DIMMS_PRESENT (1 << 24)
84 #define SINGLE_QUAD_RANK_PRESENT (1 << 23)
85 #define QUAD_RANK_PRESENT (1 << 22)
86 #define REGISTERED_DIMM (1 << 15)
87
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -030088#define MC_CHANNEL_MAPPER 0x60
89 #define RDLCH(r, ch) ((((r) >> (3 + (ch * 6))) & 0x07) - 1)
90 #define WRLCH(r, ch) ((((r) >> (ch * 6)) & 0x07) - 1)
91
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -030092#define MC_CHANNEL_RANK_PRESENT 0x7c
93 #define RANK_PRESENT_MASK 0xffff
94
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030095#define MC_CHANNEL_ADDR_MATCH 0xf0
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -030096#define MC_CHANNEL_ERROR_MASK 0xf8
97#define MC_CHANNEL_ERROR_INJECT 0xfc
98 #define INJECT_ADDR_PARITY 0x10
99 #define INJECT_ECC 0x08
100 #define MASK_CACHELINE 0x06
101 #define MASK_FULL_CACHELINE 0x06
102 #define MASK_MSB32_CACHELINE 0x04
103 #define MASK_LSB32_CACHELINE 0x02
104 #define NO_MASK_CACHELINE 0x00
105 #define REPEAT_EN 0x01
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300106
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300107 /* OFFSETS for Devices 4,5 and 6 Function 1 */
108#define MC_DOD_CH_DIMM0 0x48
109#define MC_DOD_CH_DIMM1 0x4c
110#define MC_DOD_CH_DIMM2 0x50
111 #define RANKOFFSET_MASK ((1 << 12) | (1 << 11) | (1 << 10))
112 #define RANKOFFSET(x) ((x & RANKOFFSET_MASK) >> 10)
113 #define DIMM_PRESENT_MASK (1 << 9)
114 #define DIMM_PRESENT(x) (((x) & DIMM_PRESENT_MASK) >> 9)
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300115 #define MC_DOD_NUMBANK_MASK ((1 << 8) | (1 << 7))
116 #define MC_DOD_NUMBANK(x) (((x) & MC_DOD_NUMBANK_MASK) >> 7)
117 #define MC_DOD_NUMRANK_MASK ((1 << 6) | (1 << 5))
118 #define MC_DOD_NUMRANK(x) (((x) & MC_DOD_NUMRANK_MASK) >> 5)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300119 #define MC_DOD_NUMROW_MASK ((1 << 4) | (1 << 3) | (1 << 2))
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300120 #define MC_DOD_NUMROW(x) (((x) & MC_DOD_NUMROW_MASK) >> 2)
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300121 #define MC_DOD_NUMCOL_MASK 3
122 #define MC_DOD_NUMCOL(x) ((x) & MC_DOD_NUMCOL_MASK)
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300123
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300124#define MC_RANK_PRESENT 0x7c
125
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300126#define MC_SAG_CH_0 0x80
127#define MC_SAG_CH_1 0x84
128#define MC_SAG_CH_2 0x88
129#define MC_SAG_CH_3 0x8c
130#define MC_SAG_CH_4 0x90
131#define MC_SAG_CH_5 0x94
132#define MC_SAG_CH_6 0x98
133#define MC_SAG_CH_7 0x9c
134
135#define MC_RIR_LIMIT_CH_0 0x40
136#define MC_RIR_LIMIT_CH_1 0x44
137#define MC_RIR_LIMIT_CH_2 0x48
138#define MC_RIR_LIMIT_CH_3 0x4C
139#define MC_RIR_LIMIT_CH_4 0x50
140#define MC_RIR_LIMIT_CH_5 0x54
141#define MC_RIR_LIMIT_CH_6 0x58
142#define MC_RIR_LIMIT_CH_7 0x5C
143#define MC_RIR_LIMIT_MASK ((1 << 10) - 1)
144
145#define MC_RIR_WAY_CH 0x80
146 #define MC_RIR_WAY_OFFSET_MASK (((1 << 14) - 1) & ~0x7)
147 #define MC_RIR_WAY_RANK_MASK 0x7
148
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300149/*
150 * i7core structs
151 */
152
153#define NUM_CHANS 3
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -0300154#define MAX_DIMMS 3 /* Max DIMMS per channel */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300155#define NUM_SOCKETS 2 /* Max number of MC sockets */
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -0300156#define MAX_MCR_FUNC 4
157#define MAX_CHAN_FUNC 3
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300158
159struct i7core_info {
160 u32 mc_control;
161 u32 mc_status;
162 u32 max_dod;
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300163 u32 ch_map;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300164};
165
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300166
167struct i7core_inject {
168 int enable;
169
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300170 u8 socket;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300171 u32 section;
172 u32 type;
173 u32 eccmask;
174
175 /* Error address mask */
176 int channel, dimm, rank, bank, page, col;
177};
178
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300179struct i7core_channel {
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -0300180 u32 ranks;
181 u32 dimms;
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300182};
183
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300184struct pci_id_descr {
185 int dev;
186 int func;
187 int dev_id;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300188 struct pci_dev *pdev[NUM_SOCKETS];
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300189};
190
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300191struct i7core_pvt {
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300192 struct pci_dev *pci_noncore[NUM_SOCKETS];
193 struct pci_dev *pci_mcr[NUM_SOCKETS][MAX_MCR_FUNC + 1];
194 struct pci_dev *pci_ch[NUM_SOCKETS][NUM_CHANS][MAX_CHAN_FUNC + 1];
195
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300196 struct i7core_info info;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300197 struct i7core_inject inject;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300198 struct i7core_channel channel[NUM_SOCKETS][NUM_CHANS];
199
200 int sockets; /* Number of sockets */
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300201 int channels; /* Number of active channels */
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -0300202
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300203 int ce_count_available[NUM_SOCKETS];
204 /* ECC corrected errors counts per dimm */
205 unsigned long ce_count[NUM_SOCKETS][MAX_DIMMS];
206 int last_ce_count[NUM_SOCKETS][MAX_DIMMS];
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -0300207
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -0300208 /* mcelog glue */
209 struct edac_mce edac_mce;
210 struct mce mce_entry[MCE_LOG_LEN];
211 unsigned mce_count;
212 spinlock_t mce_lock;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300213};
214
215/* Device name and register DID (Device ID) */
216struct i7core_dev_info {
217 const char *ctl_name; /* name for this device */
218 u16 fsb_mapping_errors; /* DID for the branchmap,control */
219};
220
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300221#define PCI_DESCR(device, function, device_id) \
222 .dev = (device), \
223 .func = (function), \
224 .dev_id = (device_id)
225
226struct pci_id_descr pci_devs[] = {
227 /* Memory controller */
228 { PCI_DESCR(3, 0, PCI_DEVICE_ID_INTEL_I7_MCR) },
229 { PCI_DESCR(3, 1, PCI_DEVICE_ID_INTEL_I7_MC_TAD) },
230 { PCI_DESCR(3, 2, PCI_DEVICE_ID_INTEL_I7_MC_RAS) }, /* if RDIMM is supported */
231 { PCI_DESCR(3, 4, PCI_DEVICE_ID_INTEL_I7_MC_TEST) },
232
233 /* Channel 0 */
234 { PCI_DESCR(4, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH0_CTRL) },
235 { PCI_DESCR(4, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH0_ADDR) },
236 { PCI_DESCR(4, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH0_RANK) },
237 { PCI_DESCR(4, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH0_TC) },
238
239 /* Channel 1 */
240 { PCI_DESCR(5, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH1_CTRL) },
241 { PCI_DESCR(5, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH1_ADDR) },
242 { PCI_DESCR(5, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH1_RANK) },
243 { PCI_DESCR(5, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH1_TC) },
244
245 /* Channel 2 */
246 { PCI_DESCR(6, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH2_CTRL) },
247 { PCI_DESCR(6, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH2_ADDR) },
248 { PCI_DESCR(6, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH2_RANK) },
249 { PCI_DESCR(6, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH2_TC) },
Mauro Carvalho Chehab310cbb72009-07-17 00:09:10 -0300250
251 /* Generic Non-core registers */
252 /*
253 * This is the PCI device on i7core and on Xeon 35xx (8086:2c41)
254 * On Xeon 55xx, however, it has a different id (8086:2c40). So,
255 * the probing code needs to test for the other address in case of
256 * failure of this one
257 */
258 { PCI_DESCR(0, 0, PCI_DEVICE_ID_INTEL_I7_NOCORE) },
259
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300260};
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300261#define N_DEVS ARRAY_SIZE(pci_devs)
262
263/*
264 * pci_device_id table for which devices we are looking for
265 * This should match the first device at pci_devs table
266 */
267static const struct pci_device_id i7core_pci_tbl[] __devinitdata = {
Mauro Carvalho Chehabd1fd4fb2009-07-10 18:39:53 -0300268 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_X58_HUB_MGMT)},
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300269 {0,} /* 0 terminated list. */
270};
271
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300272
273/* Table of devices attributes supported by this driver */
274static const struct i7core_dev_info i7core_devs[] = {
275 {
276 .ctl_name = "i7 Core",
277 .fsb_mapping_errors = PCI_DEVICE_ID_INTEL_I7_MCR,
278 },
279};
280
281static struct edac_pci_ctl_info *i7core_pci;
282
283/****************************************************************************
284 Anciliary status routines
285 ****************************************************************************/
286
287 /* MC_CONTROL bits */
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300288#define CH_ACTIVE(pvt, ch) ((pvt)->info.mc_control & (1 << (8 + ch)))
289#define ECCx8(pvt) ((pvt)->info.mc_control & (1 << 1))
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300290
291 /* MC_STATUS bits */
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300292#define ECC_ENABLED(pvt) ((pvt)->info.mc_status & (1 << 3))
293#define CH_DISABLED(pvt, ch) ((pvt)->info.mc_status & (1 << ch))
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300294
295 /* MC_MAX_DOD read functions */
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300296static inline int numdimms(u32 dimms)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300297{
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300298 return (dimms & 0x3) + 1;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300299}
300
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300301static inline int numrank(u32 rank)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300302{
303 static int ranks[4] = { 1, 2, 4, -EINVAL };
304
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300305 return ranks[rank & 0x3];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300306}
307
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300308static inline int numbank(u32 bank)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300309{
310 static int banks[4] = { 4, 8, 16, -EINVAL };
311
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300312 return banks[bank & 0x3];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300313}
314
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300315static inline int numrow(u32 row)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300316{
317 static int rows[8] = {
318 1 << 12, 1 << 13, 1 << 14, 1 << 15,
319 1 << 16, -EINVAL, -EINVAL, -EINVAL,
320 };
321
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300322 return rows[row & 0x7];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300323}
324
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300325static inline int numcol(u32 col)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300326{
327 static int cols[8] = {
328 1 << 10, 1 << 11, 1 << 12, -EINVAL,
329 };
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300330 return cols[col & 0x3];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300331}
332
333/****************************************************************************
334 Memory check routines
335 ****************************************************************************/
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300336static struct pci_dev *get_pdev_slot_func(u8 socket, unsigned slot,
337 unsigned func)
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300338{
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300339 int i;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300340
341 for (i = 0; i < N_DEVS; i++) {
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300342 if (!pci_devs[i].pdev[socket])
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300343 continue;
344
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300345 if (PCI_SLOT(pci_devs[i].pdev[socket]->devfn) == slot &&
346 PCI_FUNC(pci_devs[i].pdev[socket]->devfn) == func) {
347 return pci_devs[i].pdev[socket];
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300348 }
349 }
350
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300351 return NULL;
352}
353
Mauro Carvalho Chehabec6df242009-07-18 10:44:30 -0300354/**
355 * i7core_get_active_channels() - gets the number of channels and csrows
356 * @socket: Quick Path Interconnect socket
357 * @channels: Number of channels that will be returned
358 * @csrows: Number of csrows found
359 *
360 * Since EDAC core needs to know in advance the number of available channels
361 * and csrows, in order to allocate memory for csrows/channels, it is needed
362 * to run two similar steps. At the first step, implemented on this function,
363 * it checks the number of csrows/channels present at one socket.
364 * this is used in order to properly allocate the size of mci components.
365 *
366 * It should be noticed that none of the current available datasheets explain
367 * or even mention how csrows are seen by the memory controller. So, we need
368 * to add a fake description for csrows.
369 * So, this driver is attributing one DIMM memory for one csrow.
370 */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300371static int i7core_get_active_channels(u8 socket, unsigned *channels,
372 unsigned *csrows)
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300373{
374 struct pci_dev *pdev = NULL;
375 int i, j;
376 u32 status, control;
377
378 *channels = 0;
379 *csrows = 0;
380
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300381 pdev = get_pdev_slot_func(socket, 3, 0);
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -0300382 if (!pdev) {
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300383 i7core_printk(KERN_ERR, "Couldn't find socket %d fn 3.0!!!\n",
384 socket);
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300385 return -ENODEV;
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -0300386 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300387
388 /* Device 3 function 0 reads */
389 pci_read_config_dword(pdev, MC_STATUS, &status);
390 pci_read_config_dword(pdev, MC_CONTROL, &control);
391
392 for (i = 0; i < NUM_CHANS; i++) {
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300393 u32 dimm_dod[3];
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300394 /* Check if the channel is active */
395 if (!(control & (1 << (8 + i))))
396 continue;
397
398 /* Check if the channel is disabled */
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300399 if (status & (1 << i))
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300400 continue;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300401
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300402 pdev = get_pdev_slot_func(socket, i + 4, 1);
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300403 if (!pdev) {
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300404 i7core_printk(KERN_ERR, "Couldn't find socket %d "
405 "fn %d.%d!!!\n",
406 socket, i + 4, 1);
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300407 return -ENODEV;
408 }
409 /* Devices 4-6 function 1 */
410 pci_read_config_dword(pdev,
411 MC_DOD_CH_DIMM0, &dimm_dod[0]);
412 pci_read_config_dword(pdev,
413 MC_DOD_CH_DIMM1, &dimm_dod[1]);
414 pci_read_config_dword(pdev,
415 MC_DOD_CH_DIMM2, &dimm_dod[2]);
416
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300417 (*channels)++;
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300418
419 for (j = 0; j < 3; j++) {
420 if (!DIMM_PRESENT(dimm_dod[j]))
421 continue;
422 (*csrows)++;
423 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300424 }
425
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -0300426 debugf0("Number of active channels on socket %d: %d\n",
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300427 socket, *channels);
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300428
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300429 return 0;
430}
431
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -0300432static int get_dimm_config(struct mem_ctl_info *mci, int *csrow, u8 socket)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300433{
434 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300435 struct csrow_info *csr;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300436 struct pci_dev *pdev;
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -0300437 int i, j;
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300438 unsigned long last_page = 0;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300439 enum edac_type mode;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300440 enum mem_type mtype;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300441
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300442 /* Get data from the MC register, function 0 */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300443 pdev = pvt->pci_mcr[socket][0];
Mauro Carvalho Chehab7dd69532009-06-22 22:48:30 -0300444 if (!pdev)
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300445 return -ENODEV;
446
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300447 /* Device 3 function 0 reads */
Mauro Carvalho Chehab7dd69532009-06-22 22:48:30 -0300448 pci_read_config_dword(pdev, MC_CONTROL, &pvt->info.mc_control);
449 pci_read_config_dword(pdev, MC_STATUS, &pvt->info.mc_status);
450 pci_read_config_dword(pdev, MC_MAX_DOD, &pvt->info.max_dod);
451 pci_read_config_dword(pdev, MC_CHANNEL_MAPPER, &pvt->info.ch_map);
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300452
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300453 debugf0("QPI %d control=0x%08x status=0x%08x dod=0x%08x map=0x%08x\n",
454 socket, pvt->info.mc_control, pvt->info.mc_status,
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300455 pvt->info.max_dod, pvt->info.ch_map);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300456
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300457 if (ECC_ENABLED(pvt)) {
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300458 debugf0("ECC enabled with x%d SDCC\n", ECCx8(pvt) ? 8 : 4);
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300459 if (ECCx8(pvt))
460 mode = EDAC_S8ECD8ED;
461 else
462 mode = EDAC_S4ECD4ED;
463 } else {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300464 debugf0("ECC disabled\n");
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300465 mode = EDAC_NONE;
466 }
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300467
468 /* FIXME: need to handle the error codes */
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300469 debugf0("DOD Max limits: DIMMS: %d, %d-ranked, %d-banked "
470 "x%x x 0x%x\n",
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300471 numdimms(pvt->info.max_dod),
472 numrank(pvt->info.max_dod >> 2),
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300473 numbank(pvt->info.max_dod >> 4),
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300474 numrow(pvt->info.max_dod >> 6),
475 numcol(pvt->info.max_dod >> 9));
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300476
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300477 for (i = 0; i < NUM_CHANS; i++) {
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300478 u32 data, dimm_dod[3], value[8];
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300479
480 if (!CH_ACTIVE(pvt, i)) {
481 debugf0("Channel %i is not active\n", i);
482 continue;
483 }
484 if (CH_DISABLED(pvt, i)) {
485 debugf0("Channel %i is disabled\n", i);
486 continue;
487 }
488
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300489 /* Devices 4-6 function 0 */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300490 pci_read_config_dword(pvt->pci_ch[socket][i][0],
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300491 MC_CHANNEL_DIMM_INIT_PARAMS, &data);
492
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300493 pvt->channel[socket][i].ranks = (data & QUAD_RANK_PRESENT) ?
494 4 : 2;
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300495
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300496 if (data & REGISTERED_DIMM)
497 mtype = MEM_RDDR3;
498 else
499 mtype = MEM_DDR3;
500#if 0
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300501 if (data & THREE_DIMMS_PRESENT)
502 pvt->channel[i].dimms = 3;
503 else if (data & SINGLE_QUAD_RANK_PRESENT)
504 pvt->channel[i].dimms = 1;
505 else
506 pvt->channel[i].dimms = 2;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300507#endif
508
509 /* Devices 4-6 function 1 */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300510 pci_read_config_dword(pvt->pci_ch[socket][i][1],
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300511 MC_DOD_CH_DIMM0, &dimm_dod[0]);
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300512 pci_read_config_dword(pvt->pci_ch[socket][i][1],
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300513 MC_DOD_CH_DIMM1, &dimm_dod[1]);
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300514 pci_read_config_dword(pvt->pci_ch[socket][i][1],
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300515 MC_DOD_CH_DIMM2, &dimm_dod[2]);
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300516
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300517 debugf0("Ch%d phy rd%d, wr%d (0x%08x): "
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300518 "%d ranks, %cDIMMs\n",
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300519 i,
520 RDLCH(pvt->info.ch_map, i), WRLCH(pvt->info.ch_map, i),
521 data,
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300522 pvt->channel[socket][i].ranks,
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300523 (data & REGISTERED_DIMM) ? 'R' : 'U');
Mauro Carvalho Chehab7dd69532009-06-22 22:48:30 -0300524
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300525 for (j = 0; j < 3; j++) {
526 u32 banks, ranks, rows, cols;
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300527 u32 size, npages;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300528
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300529 if (!DIMM_PRESENT(dimm_dod[j]))
530 continue;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300531
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300532 banks = numbank(MC_DOD_NUMBANK(dimm_dod[j]));
533 ranks = numrank(MC_DOD_NUMRANK(dimm_dod[j]));
534 rows = numrow(MC_DOD_NUMROW(dimm_dod[j]));
535 cols = numcol(MC_DOD_NUMCOL(dimm_dod[j]));
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300536
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300537 /* DDR3 has 8 I/O banks */
538 size = (rows * cols * banks * ranks) >> (20 - 3);
539
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300540 pvt->channel[socket][i].dimms++;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300541
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300542 debugf0("\tdimm %d %d Mb offset: %x, "
543 "bank: %d, rank: %d, row: %#x, col: %#x\n",
544 j, size,
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300545 RANKOFFSET(dimm_dod[j]),
546 banks, ranks, rows, cols);
547
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300548#if PAGE_SHIFT > 20
549 npages = size >> (PAGE_SHIFT - 20);
550#else
551 npages = size << (20 - PAGE_SHIFT);
552#endif
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300553
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -0300554 csr = &mci->csrows[*csrow];
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300555 csr->first_page = last_page + 1;
556 last_page += npages;
557 csr->last_page = last_page;
558 csr->nr_pages = npages;
559
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300560 csr->page_mask = 0;
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300561 csr->grain = 8;
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -0300562 csr->csrow_idx = *csrow;
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300563 csr->nr_channels = 1;
564
565 csr->channels[0].chan_idx = i;
566 csr->channels[0].ce_count = 0;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300567
568 switch (banks) {
569 case 4:
570 csr->dtype = DEV_X4;
571 break;
572 case 8:
573 csr->dtype = DEV_X8;
574 break;
575 case 16:
576 csr->dtype = DEV_X16;
577 break;
578 default:
579 csr->dtype = DEV_UNKNOWN;
580 }
581
582 csr->edac_mode = mode;
583 csr->mtype = mtype;
584
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -0300585 (*csrow)++;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300586 }
587
588 pci_read_config_dword(pdev, MC_SAG_CH_0, &value[0]);
589 pci_read_config_dword(pdev, MC_SAG_CH_1, &value[1]);
590 pci_read_config_dword(pdev, MC_SAG_CH_2, &value[2]);
591 pci_read_config_dword(pdev, MC_SAG_CH_3, &value[3]);
592 pci_read_config_dword(pdev, MC_SAG_CH_4, &value[4]);
593 pci_read_config_dword(pdev, MC_SAG_CH_5, &value[5]);
594 pci_read_config_dword(pdev, MC_SAG_CH_6, &value[6]);
595 pci_read_config_dword(pdev, MC_SAG_CH_7, &value[7]);
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300596 debugf1("\t[%i] DIVBY3\tREMOVED\tOFFSET\n", i);
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300597 for (j = 0; j < 8; j++)
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300598 debugf1("\t\t%#x\t%#x\t%#x\n",
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300599 (value[j] >> 27) & 0x1,
600 (value[j] >> 24) & 0x7,
601 (value[j] && ((1 << 24) - 1)));
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300602 }
603
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300604 return 0;
605}
606
607/****************************************************************************
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300608 Error insertion routines
609 ****************************************************************************/
610
611/* The i7core has independent error injection features per channel.
612 However, to have a simpler code, we don't allow enabling error injection
613 on more than one channel.
614 Also, since a change at an inject parameter will be applied only at enable,
615 we're disabling error injection on all write calls to the sysfs nodes that
616 controls the error code injection.
617 */
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300618static int disable_inject(struct mem_ctl_info *mci)
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300619{
620 struct i7core_pvt *pvt = mci->pvt_info;
621
622 pvt->inject.enable = 0;
623
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300624 if (!pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0])
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300625 return -ENODEV;
626
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300627 pci_write_config_dword(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0],
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -0300628 MC_CHANNEL_ERROR_INJECT, 0);
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300629
630 return 0;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300631}
632
633/*
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300634 * i7core inject inject.socket
635 *
636 * accept and store error injection inject.socket value
637 */
638static ssize_t i7core_inject_socket_store(struct mem_ctl_info *mci,
639 const char *data, size_t count)
640{
641 struct i7core_pvt *pvt = mci->pvt_info;
642 unsigned long value;
643 int rc;
644
645 rc = strict_strtoul(data, 10, &value);
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300646 if ((rc < 0) || (value >= pvt->sockets))
Mauro Carvalho Chehab2068def2009-08-05 19:28:27 -0300647 return -EIO;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300648
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -0300649 pvt->inject.socket = (u32) value;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300650 return count;
651}
652
653static ssize_t i7core_inject_socket_show(struct mem_ctl_info *mci,
654 char *data)
655{
656 struct i7core_pvt *pvt = mci->pvt_info;
657 return sprintf(data, "%d\n", pvt->inject.socket);
658}
659
660/*
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300661 * i7core inject inject.section
662 *
663 * accept and store error injection inject.section value
664 * bit 0 - refers to the lower 32-byte half cacheline
665 * bit 1 - refers to the upper 32-byte half cacheline
666 */
667static ssize_t i7core_inject_section_store(struct mem_ctl_info *mci,
668 const char *data, size_t count)
669{
670 struct i7core_pvt *pvt = mci->pvt_info;
671 unsigned long value;
672 int rc;
673
674 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300675 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300676
677 rc = strict_strtoul(data, 10, &value);
678 if ((rc < 0) || (value > 3))
Mauro Carvalho Chehab2068def2009-08-05 19:28:27 -0300679 return -EIO;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300680
681 pvt->inject.section = (u32) value;
682 return count;
683}
684
685static ssize_t i7core_inject_section_show(struct mem_ctl_info *mci,
686 char *data)
687{
688 struct i7core_pvt *pvt = mci->pvt_info;
689 return sprintf(data, "0x%08x\n", pvt->inject.section);
690}
691
692/*
693 * i7core inject.type
694 *
695 * accept and store error injection inject.section value
696 * bit 0 - repeat enable - Enable error repetition
697 * bit 1 - inject ECC error
698 * bit 2 - inject parity error
699 */
700static ssize_t i7core_inject_type_store(struct mem_ctl_info *mci,
701 const char *data, size_t count)
702{
703 struct i7core_pvt *pvt = mci->pvt_info;
704 unsigned long value;
705 int rc;
706
707 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300708 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300709
710 rc = strict_strtoul(data, 10, &value);
711 if ((rc < 0) || (value > 7))
Mauro Carvalho Chehab2068def2009-08-05 19:28:27 -0300712 return -EIO;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300713
714 pvt->inject.type = (u32) value;
715 return count;
716}
717
718static ssize_t i7core_inject_type_show(struct mem_ctl_info *mci,
719 char *data)
720{
721 struct i7core_pvt *pvt = mci->pvt_info;
722 return sprintf(data, "0x%08x\n", pvt->inject.type);
723}
724
725/*
726 * i7core_inject_inject.eccmask_store
727 *
728 * The type of error (UE/CE) will depend on the inject.eccmask value:
729 * Any bits set to a 1 will flip the corresponding ECC bit
730 * Correctable errors can be injected by flipping 1 bit or the bits within
731 * a symbol pair (2 consecutive aligned 8-bit pairs - i.e. 7:0 and 15:8 or
732 * 23:16 and 31:24). Flipping bits in two symbol pairs will cause an
733 * uncorrectable error to be injected.
734 */
735static ssize_t i7core_inject_eccmask_store(struct mem_ctl_info *mci,
736 const char *data, size_t count)
737{
738 struct i7core_pvt *pvt = mci->pvt_info;
739 unsigned long value;
740 int rc;
741
742 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300743 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300744
745 rc = strict_strtoul(data, 10, &value);
746 if (rc < 0)
Mauro Carvalho Chehab2068def2009-08-05 19:28:27 -0300747 return -EIO;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300748
749 pvt->inject.eccmask = (u32) value;
750 return count;
751}
752
753static ssize_t i7core_inject_eccmask_show(struct mem_ctl_info *mci,
754 char *data)
755{
756 struct i7core_pvt *pvt = mci->pvt_info;
757 return sprintf(data, "0x%08x\n", pvt->inject.eccmask);
758}
759
760/*
761 * i7core_addrmatch
762 *
763 * The type of error (UE/CE) will depend on the inject.eccmask value:
764 * Any bits set to a 1 will flip the corresponding ECC bit
765 * Correctable errors can be injected by flipping 1 bit or the bits within
766 * a symbol pair (2 consecutive aligned 8-bit pairs - i.e. 7:0 and 15:8 or
767 * 23:16 and 31:24). Flipping bits in two symbol pairs will cause an
768 * uncorrectable error to be injected.
769 */
770static ssize_t i7core_inject_addrmatch_store(struct mem_ctl_info *mci,
771 const char *data, size_t count)
772{
773 struct i7core_pvt *pvt = mci->pvt_info;
774 char *cmd, *val;
775 long value;
776 int rc;
777
778 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300779 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300780
781 do {
782 cmd = strsep((char **) &data, ":");
783 if (!cmd)
784 break;
785 val = strsep((char **) &data, " \n\t");
786 if (!val)
787 return cmd - data;
788
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300789 if (!strcasecmp(val, "any"))
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300790 value = -1;
791 else {
792 rc = strict_strtol(val, 10, &value);
793 if ((rc < 0) || (value < 0))
794 return cmd - data;
795 }
796
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300797 if (!strcasecmp(cmd, "channel")) {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300798 if (value < 3)
799 pvt->inject.channel = value;
800 else
801 return cmd - data;
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300802 } else if (!strcasecmp(cmd, "dimm")) {
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300803 if (value < 3)
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300804 pvt->inject.dimm = value;
805 else
806 return cmd - data;
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300807 } else if (!strcasecmp(cmd, "rank")) {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300808 if (value < 4)
809 pvt->inject.rank = value;
810 else
811 return cmd - data;
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300812 } else if (!strcasecmp(cmd, "bank")) {
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300813 if (value < 32)
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300814 pvt->inject.bank = value;
815 else
816 return cmd - data;
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300817 } else if (!strcasecmp(cmd, "page")) {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300818 if (value <= 0xffff)
819 pvt->inject.page = value;
820 else
821 return cmd - data;
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300822 } else if (!strcasecmp(cmd, "col") ||
823 !strcasecmp(cmd, "column")) {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300824 if (value <= 0x3fff)
825 pvt->inject.col = value;
826 else
827 return cmd - data;
828 }
829 } while (1);
830
831 return count;
832}
833
834static ssize_t i7core_inject_addrmatch_show(struct mem_ctl_info *mci,
835 char *data)
836{
837 struct i7core_pvt *pvt = mci->pvt_info;
838 char channel[4], dimm[4], bank[4], rank[4], page[7], col[7];
839
840 if (pvt->inject.channel < 0)
841 sprintf(channel, "any");
842 else
843 sprintf(channel, "%d", pvt->inject.channel);
844 if (pvt->inject.dimm < 0)
845 sprintf(dimm, "any");
846 else
847 sprintf(dimm, "%d", pvt->inject.dimm);
848 if (pvt->inject.bank < 0)
849 sprintf(bank, "any");
850 else
851 sprintf(bank, "%d", pvt->inject.bank);
852 if (pvt->inject.rank < 0)
853 sprintf(rank, "any");
854 else
855 sprintf(rank, "%d", pvt->inject.rank);
856 if (pvt->inject.page < 0)
857 sprintf(page, "any");
858 else
859 sprintf(page, "0x%04x", pvt->inject.page);
860 if (pvt->inject.col < 0)
861 sprintf(col, "any");
862 else
863 sprintf(col, "0x%04x", pvt->inject.col);
864
865 return sprintf(data, "channel: %s\ndimm: %s\nbank: %s\n"
866 "rank: %s\npage: %s\ncolumn: %s\n",
867 channel, dimm, bank, rank, page, col);
868}
869
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300870static int write_and_test(struct pci_dev *dev, int where, u32 val)
871{
872 u32 read;
873 int count;
874
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -0300875 debugf0("setting pci %02x:%02x.%x reg=%02x value=%08x\n",
876 dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn),
877 where, val);
878
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300879 for (count = 0; count < 10; count++) {
880 if (count)
881 msleep (100);
882 pci_write_config_dword(dev, where, val);
883 pci_read_config_dword(dev, where, &read);
884
885 if (read == val)
886 return 0;
887 }
888
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -0300889 i7core_printk(KERN_ERR, "Error during set pci %02x:%02x.%x reg=%02x "
890 "write=%08x. Read=%08x\n",
891 dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn),
892 where, val, read);
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300893
894 return -EINVAL;
895}
896
897
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300898/*
899 * This routine prepares the Memory Controller for error injection.
900 * The error will be injected when some process tries to write to the
901 * memory that matches the given criteria.
902 * The criteria can be set in terms of a mask where dimm, rank, bank, page
903 * and col can be specified.
904 * A -1 value for any of the mask items will make the MCU to ignore
905 * that matching criteria for error injection.
906 *
907 * It should be noticed that the error will only happen after a write operation
908 * on a memory that matches the condition. if REPEAT_EN is not enabled at
909 * inject mask, then it will produce just one error. Otherwise, it will repeat
910 * until the injectmask would be cleaned.
911 *
912 * FIXME: This routine assumes that MAXNUMDIMMS value of MC_MAX_DOD
913 * is reliable enough to check if the MC is using the
914 * three channels. However, this is not clear at the datasheet.
915 */
916static ssize_t i7core_inject_enable_store(struct mem_ctl_info *mci,
917 const char *data, size_t count)
918{
919 struct i7core_pvt *pvt = mci->pvt_info;
920 u32 injectmask;
921 u64 mask = 0;
922 int rc;
923 long enable;
924
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300925 if (!pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0])
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300926 return 0;
927
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300928 rc = strict_strtoul(data, 10, &enable);
929 if ((rc < 0))
930 return 0;
931
932 if (enable) {
933 pvt->inject.enable = 1;
934 } else {
935 disable_inject(mci);
936 return count;
937 }
938
939 /* Sets pvt->inject.dimm mask */
940 if (pvt->inject.dimm < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300941 mask |= 1L << 41;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300942 else {
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300943 if (pvt->channel[pvt->inject.socket][pvt->inject.channel].dimms > 2)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300944 mask |= (pvt->inject.dimm & 0x3L) << 35;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300945 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300946 mask |= (pvt->inject.dimm & 0x1L) << 36;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300947 }
948
949 /* Sets pvt->inject.rank mask */
950 if (pvt->inject.rank < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300951 mask |= 1L << 40;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300952 else {
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300953 if (pvt->channel[pvt->inject.socket][pvt->inject.channel].dimms > 2)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300954 mask |= (pvt->inject.rank & 0x1L) << 34;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300955 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300956 mask |= (pvt->inject.rank & 0x3L) << 34;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300957 }
958
959 /* Sets pvt->inject.bank mask */
960 if (pvt->inject.bank < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300961 mask |= 1L << 39;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300962 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300963 mask |= (pvt->inject.bank & 0x15L) << 30;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300964
965 /* Sets pvt->inject.page mask */
966 if (pvt->inject.page < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300967 mask |= 1L << 38;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300968 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300969 mask |= (pvt->inject.page & 0xffffL) << 14;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300970
971 /* Sets pvt->inject.column mask */
972 if (pvt->inject.col < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300973 mask |= 1L << 37;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300974 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300975 mask |= (pvt->inject.col & 0x3fffL);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300976
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300977 /*
978 * bit 0: REPEAT_EN
979 * bits 1-2: MASK_HALF_CACHELINE
980 * bit 3: INJECT_ECC
981 * bit 4: INJECT_ADDR_PARITY
982 */
983
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300984 injectmask = (pvt->inject.type & 1) |
985 (pvt->inject.section & 0x3) << 1 |
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300986 (pvt->inject.type & 0x6) << (3 - 1);
987
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300988 /* Unlock writes to registers - this register is write only */
989 pci_write_config_dword(pvt->pci_noncore[pvt->inject.socket],
990 MC_CFG_CONTROL, 0x2);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300991
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300992 write_and_test(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0],
993 MC_CHANNEL_ADDR_MATCH, mask);
994 write_and_test(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0],
995 MC_CHANNEL_ADDR_MATCH + 4, mask >> 32L);
996
997 write_and_test(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0],
998 MC_CHANNEL_ERROR_MASK, pvt->inject.eccmask);
999
1000 write_and_test(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0],
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -03001001 MC_CHANNEL_ERROR_INJECT, injectmask);
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -03001002
1003 /*
1004 * This is something undocumented, based on my tests
1005 * Without writing 8 to this register, errors aren't injected. Not sure
1006 * why.
1007 */
1008 pci_write_config_dword(pvt->pci_noncore[pvt->inject.socket],
1009 MC_CFG_CONTROL, 8);
1010
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001011 debugf0("Error inject addr match 0x%016llx, ecc 0x%08x,"
1012 " inject 0x%08x\n",
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001013 mask, pvt->inject.eccmask, injectmask);
1014
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -03001015
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001016 return count;
1017}
1018
1019static ssize_t i7core_inject_enable_show(struct mem_ctl_info *mci,
1020 char *data)
1021{
1022 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -03001023 u32 injectmask;
1024
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001025 pci_read_config_dword(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0],
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -03001026 MC_CHANNEL_ERROR_INJECT, &injectmask);
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -03001027
1028 debugf0("Inject error read: 0x%018x\n", injectmask);
1029
1030 if (injectmask & 0x0c)
1031 pvt->inject.enable = 1;
1032
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001033 return sprintf(data, "%d\n", pvt->inject.enable);
1034}
1035
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001036static ssize_t i7core_ce_regs_show(struct mem_ctl_info *mci, char *data)
1037{
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001038 unsigned i, count, total = 0;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001039 struct i7core_pvt *pvt = mci->pvt_info;
1040
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001041 for (i = 0; i < pvt->sockets; i++) {
1042 if (!pvt->ce_count_available[i])
1043 count = sprintf(data, "socket 0 data unavailable\n");
1044 else
1045 count = sprintf(data, "socket %d, dimm0: %lu\n"
1046 "dimm1: %lu\ndimm2: %lu\n",
1047 i,
1048 pvt->ce_count[i][0],
1049 pvt->ce_count[i][1],
1050 pvt->ce_count[i][2]);
1051 data += count;
1052 total += count;
1053 }
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001054
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001055 return total;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001056}
1057
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001058/*
1059 * Sysfs struct
1060 */
1061static struct mcidev_sysfs_attribute i7core_inj_attrs[] = {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001062 {
1063 .attr = {
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001064 .name = "inject_socket",
1065 .mode = (S_IRUGO | S_IWUSR)
1066 },
1067 .show = i7core_inject_socket_show,
1068 .store = i7core_inject_socket_store,
1069 }, {
1070 .attr = {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001071 .name = "inject_section",
1072 .mode = (S_IRUGO | S_IWUSR)
1073 },
1074 .show = i7core_inject_section_show,
1075 .store = i7core_inject_section_store,
1076 }, {
1077 .attr = {
1078 .name = "inject_type",
1079 .mode = (S_IRUGO | S_IWUSR)
1080 },
1081 .show = i7core_inject_type_show,
1082 .store = i7core_inject_type_store,
1083 }, {
1084 .attr = {
1085 .name = "inject_eccmask",
1086 .mode = (S_IRUGO | S_IWUSR)
1087 },
1088 .show = i7core_inject_eccmask_show,
1089 .store = i7core_inject_eccmask_store,
1090 }, {
1091 .attr = {
1092 .name = "inject_addrmatch",
1093 .mode = (S_IRUGO | S_IWUSR)
1094 },
1095 .show = i7core_inject_addrmatch_show,
1096 .store = i7core_inject_addrmatch_store,
1097 }, {
1098 .attr = {
1099 .name = "inject_enable",
1100 .mode = (S_IRUGO | S_IWUSR)
1101 },
1102 .show = i7core_inject_enable_show,
1103 .store = i7core_inject_enable_store,
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001104 }, {
1105 .attr = {
1106 .name = "corrected_error_counts",
1107 .mode = (S_IRUGO | S_IWUSR)
1108 },
1109 .show = i7core_ce_regs_show,
1110 .store = NULL,
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001111 },
1112};
1113
1114/****************************************************************************
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001115 Device initialization routines: put/get, init/exit
1116 ****************************************************************************/
1117
1118/*
1119 * i7core_put_devices 'put' all the devices that we have
1120 * reserved via 'get'
1121 */
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001122static void i7core_put_devices(void)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001123{
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001124 int i, j;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001125
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001126 for (i = 0; i < NUM_SOCKETS; i++)
1127 for (j = 0; j < N_DEVS; j++)
1128 pci_dev_put(pci_devs[j].pdev[i]);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001129}
1130
1131/*
1132 * i7core_get_devices Find and perform 'get' operation on the MCH's
1133 * device/functions we want to reference for this driver
1134 *
1135 * Need to 'get' device 16 func 1 and func 2
1136 */
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001137int i7core_get_onedevice(struct pci_dev **prev, int devno)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001138{
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001139 struct pci_dev *pdev = NULL;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001140 u8 bus = 0;
1141 u8 socket = 0;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001142
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001143 pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
1144 pci_devs[devno].dev_id, *prev);
1145
1146 /*
1147 * On Xeon 55xx, the Intel Quckpath Arch Generic Non-core pci buses
1148 * aren't announced by acpi. So, we need to use a legacy scan probing
1149 * to detect them
1150 */
1151 if (unlikely(!pdev && !devno && !prev)) {
1152 pcibios_scan_specific_bus(254);
1153 pcibios_scan_specific_bus(255);
1154
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001155 pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001156 pci_devs[devno].dev_id, *prev);
1157 }
Mauro Carvalho Chehabd1fd4fb2009-07-10 18:39:53 -03001158
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001159 /*
1160 * On Xeon 55xx, the Intel Quckpath Arch Generic Non-core regs
1161 * is at addr 8086:2c40, instead of 8086:2c41. So, we need
1162 * to probe for the alternate address in case of failure
1163 */
1164 if (pci_devs[devno].dev_id == PCI_DEVICE_ID_INTEL_I7_NOCORE && !pdev)
1165 pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
1166 PCI_DEVICE_ID_INTEL_I7_NOCORE_ALT, *prev);
Mauro Carvalho Chehabd1fd4fb2009-07-10 18:39:53 -03001167
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001168 if (!pdev) {
1169 if (*prev) {
1170 *prev = pdev;
1171 return 0;
Mauro Carvalho Chehabd1fd4fb2009-07-10 18:39:53 -03001172 }
1173
Mauro Carvalho Chehab310cbb72009-07-17 00:09:10 -03001174 /*
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001175 * Dev 3 function 2 only exists on chips with RDIMMs
1176 * so, it is ok to not found it
Mauro Carvalho Chehab310cbb72009-07-17 00:09:10 -03001177 */
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001178 if ((pci_devs[devno].dev == 3) && (pci_devs[devno].func == 2)) {
1179 *prev = pdev;
1180 return 0;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001181 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001182
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001183 i7core_printk(KERN_ERR,
1184 "Device not found: dev %02x.%d PCI ID %04x:%04x\n",
1185 pci_devs[devno].dev, pci_devs[devno].func,
1186 PCI_VENDOR_ID_INTEL, pci_devs[devno].dev_id);
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001187
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001188 /* End of list, leave */
1189 return -ENODEV;
1190 }
1191 bus = pdev->bus->number;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001192
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001193 if (bus == 0x3f)
1194 socket = 0;
1195 else
1196 socket = 255 - bus;
1197
1198 if (socket >= NUM_SOCKETS) {
1199 i7core_printk(KERN_ERR,
1200 "Unexpected socket for "
1201 "dev %02x:%02x.%d PCI ID %04x:%04x\n",
1202 bus, pci_devs[devno].dev, pci_devs[devno].func,
1203 PCI_VENDOR_ID_INTEL, pci_devs[devno].dev_id);
1204 pci_dev_put(pdev);
1205 return -ENODEV;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001206 }
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001207
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001208 if (pci_devs[devno].pdev[socket]) {
1209 i7core_printk(KERN_ERR,
1210 "Duplicated device for "
1211 "dev %02x:%02x.%d PCI ID %04x:%04x\n",
1212 bus, pci_devs[devno].dev, pci_devs[devno].func,
1213 PCI_VENDOR_ID_INTEL, pci_devs[devno].dev_id);
1214 pci_dev_put(pdev);
1215 return -ENODEV;
1216 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001217
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001218 pci_devs[devno].pdev[socket] = pdev;
1219
1220 /* Sanity check */
1221 if (unlikely(PCI_SLOT(pdev->devfn) != pci_devs[devno].dev ||
1222 PCI_FUNC(pdev->devfn) != pci_devs[devno].func)) {
1223 i7core_printk(KERN_ERR,
1224 "Device PCI ID %04x:%04x "
1225 "has dev %02x:%02x.%d instead of dev %02x:%02x.%d\n",
1226 PCI_VENDOR_ID_INTEL, pci_devs[devno].dev_id,
1227 bus, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
1228 bus, pci_devs[devno].dev, pci_devs[devno].func);
1229 return -ENODEV;
1230 }
1231
1232 /* Be sure that the device is enabled */
1233 if (unlikely(pci_enable_device(pdev) < 0)) {
1234 i7core_printk(KERN_ERR,
1235 "Couldn't enable "
1236 "dev %02x:%02x.%d PCI ID %04x:%04x\n",
1237 bus, pci_devs[devno].dev, pci_devs[devno].func,
1238 PCI_VENDOR_ID_INTEL, pci_devs[devno].dev_id);
1239 return -ENODEV;
1240 }
1241
1242 i7core_printk(KERN_INFO,
1243 "Registered socket %d "
1244 "dev %02x:%02x.%d PCI ID %04x:%04x\n",
1245 socket, bus, pci_devs[devno].dev, pci_devs[devno].func,
1246 PCI_VENDOR_ID_INTEL, pci_devs[devno].dev_id);
1247
1248 *prev = pdev;
1249
1250 return 0;
1251}
1252
1253static int i7core_get_devices(void)
1254{
1255 int i;
1256 struct pci_dev *pdev = NULL;
1257
1258 for (i = 0; i < N_DEVS; i++) {
1259 pdev = NULL;
1260 do {
1261 if (i7core_get_onedevice(&pdev, i) < 0) {
1262 i7core_put_devices();
1263 return -ENODEV;
1264 }
1265 } while (pdev);
1266 }
1267 return 0;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001268}
1269
1270static int mci_bind_devs(struct mem_ctl_info *mci)
1271{
1272 struct i7core_pvt *pvt = mci->pvt_info;
1273 struct pci_dev *pdev;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001274 int i, j, func, slot;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001275
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001276 for (i = 0; i < pvt->sockets; i++) {
1277 for (j = 0; j < N_DEVS; j++) {
1278 pdev = pci_devs[j].pdev[i];
1279 if (!pdev)
1280 continue;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001281
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001282 func = PCI_FUNC(pdev->devfn);
1283 slot = PCI_SLOT(pdev->devfn);
1284 if (slot == 3) {
1285 if (unlikely(func > MAX_MCR_FUNC))
1286 goto error;
1287 pvt->pci_mcr[i][func] = pdev;
1288 } else if (likely(slot >= 4 && slot < 4 + NUM_CHANS)) {
1289 if (unlikely(func > MAX_CHAN_FUNC))
1290 goto error;
1291 pvt->pci_ch[i][slot - 4][func] = pdev;
1292 } else if (!slot && !func)
1293 pvt->pci_noncore[i] = pdev;
1294 else
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001295 goto error;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001296
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001297 debugf0("Associated fn %d.%d, dev = %p, socket %d\n",
1298 PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
1299 pdev, i);
1300 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001301 }
Mauro Carvalho Chehabe9bd2e72009-07-09 22:14:35 -03001302
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001303 return 0;
1304
1305error:
1306 i7core_printk(KERN_ERR, "Device %d, function %d "
1307 "is out of the expected range\n",
1308 slot, func);
1309 return -EINVAL;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001310}
1311
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001312/****************************************************************************
1313 Error check routines
1314 ****************************************************************************/
1315
1316/* This function is based on the device 3 function 4 registers as described on:
1317 * Intel Xeon Processor 5500 Series Datasheet Volume 2
1318 * http://www.intel.com/Assets/PDF/datasheet/321322.pdf
1319 * also available at:
1320 * http://www.arrownac.com/manufacturers/intel/s/nehalem/5500-datasheet-v2.pdf
1321 */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001322static void check_mc_test_err(struct mem_ctl_info *mci, u8 socket)
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001323{
1324 struct i7core_pvt *pvt = mci->pvt_info;
1325 u32 rcv1, rcv0;
1326 int new0, new1, new2;
1327
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001328 if (!pvt->pci_mcr[socket][4]) {
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001329 debugf0("%s MCR registers not found\n",__func__);
1330 return;
1331 }
1332
1333 /* Corrected error reads */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001334 pci_read_config_dword(pvt->pci_mcr[socket][4], MC_TEST_ERR_RCV1, &rcv1);
1335 pci_read_config_dword(pvt->pci_mcr[socket][4], MC_TEST_ERR_RCV0, &rcv0);
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001336
1337 /* Store the new values */
1338 new2 = DIMM2_COR_ERR(rcv1);
1339 new1 = DIMM1_COR_ERR(rcv0);
1340 new0 = DIMM0_COR_ERR(rcv0);
1341
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001342#if 0
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001343 debugf2("%s CE rcv1=0x%08x rcv0=0x%08x, %d %d %d\n",
1344 (pvt->ce_count_available ? "UPDATE" : "READ"),
1345 rcv1, rcv0, new0, new1, new2);
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001346#endif
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001347
1348 /* Updates CE counters if it is not the first time here */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001349 if (pvt->ce_count_available[socket]) {
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001350 /* Updates CE counters */
1351 int add0, add1, add2;
1352
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001353 add2 = new2 - pvt->last_ce_count[socket][2];
1354 add1 = new1 - pvt->last_ce_count[socket][1];
1355 add0 = new0 - pvt->last_ce_count[socket][0];
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001356
1357 if (add2 < 0)
1358 add2 += 0x7fff;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001359 pvt->ce_count[socket][2] += add2;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001360
1361 if (add1 < 0)
1362 add1 += 0x7fff;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001363 pvt->ce_count[socket][1] += add1;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001364
1365 if (add0 < 0)
1366 add0 += 0x7fff;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001367 pvt->ce_count[socket][0] += add0;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001368 } else
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001369 pvt->ce_count_available[socket] = 1;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001370
1371 /* Store the new values */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001372 pvt->last_ce_count[socket][2] = new2;
1373 pvt->last_ce_count[socket][1] = new1;
1374 pvt->last_ce_count[socket][0] = new0;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001375}
1376
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001377/*
1378 * According with tables E-11 and E-12 of chapter E.3.3 of Intel 64 and IA-32
1379 * Architectures Software Developer’s Manual Volume 3B.
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001380 * Nehalem are defined as family 0x06, model 0x1a
1381 *
1382 * The MCA registers used here are the following ones:
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001383 * struct mce field MCA Register
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001384 * m->status MSR_IA32_MC8_STATUS
1385 * m->addr MSR_IA32_MC8_ADDR
1386 * m->misc MSR_IA32_MC8_MISC
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001387 * In the case of Nehalem, the error information is masked at .status and .misc
1388 * fields
1389 */
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001390static void i7core_mce_output_error(struct mem_ctl_info *mci,
1391 struct mce *m)
1392{
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001393 char *type, *optype, *err, *msg;
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001394 unsigned long error = m->status & 0x1ff0000l;
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001395 u32 optypenum = (m->status >> 4) & 0x07;
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001396 u32 core_err_cnt = (m->status >> 38) && 0x7fff;
1397 u32 dimm = (m->misc >> 16) & 0x3;
1398 u32 channel = (m->misc >> 18) & 0x3;
1399 u32 syndrome = m->misc >> 32;
1400 u32 errnum = find_first_bit(&error, 32);
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001401
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001402 if (m->mcgstatus & 1)
1403 type = "FATAL";
1404 else
1405 type = "NON_FATAL";
1406
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001407 switch (optypenum) {
1408 case 0:
1409 optype = "generic undef request";
1410 break;
1411 case 1:
1412 optype = "read error";
1413 break;
1414 case 2:
1415 optype = "write error";
1416 break;
1417 case 3:
1418 optype = "addr/cmd error";
1419 break;
1420 case 4:
1421 optype = "scrubbing error";
1422 break;
1423 default:
1424 optype = "reserved";
1425 break;
1426 }
1427
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001428 switch (errnum) {
1429 case 16:
1430 err = "read ECC error";
1431 break;
1432 case 17:
1433 err = "RAS ECC error";
1434 break;
1435 case 18:
1436 err = "write parity error";
1437 break;
1438 case 19:
1439 err = "redundacy loss";
1440 break;
1441 case 20:
1442 err = "reserved";
1443 break;
1444 case 21:
1445 err = "memory range error";
1446 break;
1447 case 22:
1448 err = "RTID out of range";
1449 break;
1450 case 23:
1451 err = "address parity error";
1452 break;
1453 case 24:
1454 err = "byte enable parity error";
1455 break;
1456 default:
1457 err = "unknown";
1458 }
1459
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001460 /* FIXME: should convert addr into bank and rank information */
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001461 msg = kasprintf(GFP_ATOMIC,
Mauro Carvalho Chehab3a7dde72009-07-18 12:20:04 -03001462 "%s (addr = 0x%08llx, socket=%d, Dimm=%d, Channel=%d, "
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001463 "syndrome=0x%08x, count=%d, Err=%08llx:%08llx (%s: %s))\n",
Mauro Carvalho Chehab3a7dde72009-07-18 12:20:04 -03001464 type, (long long) m->addr, m->cpu, dimm, channel,
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001465 syndrome, core_err_cnt, (long long)m->status,
1466 (long long)m->misc, optype, err);
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001467
1468 debugf0("%s", msg);
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001469
1470 /* Call the helper to output message */
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001471 edac_mc_handle_fbd_ue(mci, 0 /* FIXME: should be rank here */,
1472 0, 0 /* FIXME: should be channel here */, msg);
1473
1474 kfree(msg);
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001475}
1476
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001477/*
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001478 * i7core_check_error Retrieve and process errors reported by the
1479 * hardware. Called by the Core module.
1480 */
1481static void i7core_check_error(struct mem_ctl_info *mci)
1482{
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001483 struct i7core_pvt *pvt = mci->pvt_info;
1484 int i;
1485 unsigned count = 0;
1486 struct mce *m = NULL;
1487 unsigned long flags;
1488
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001489 /* Copy all mce errors into a temporary buffer */
1490 spin_lock_irqsave(&pvt->mce_lock, flags);
1491 if (pvt->mce_count) {
1492 m = kmalloc(sizeof(*m) * pvt->mce_count, GFP_ATOMIC);
1493 if (m) {
1494 count = pvt->mce_count;
1495 memcpy(m, &pvt->mce_entry, sizeof(*m) * count);
1496 }
1497 pvt->mce_count = 0;
1498 }
1499 spin_unlock_irqrestore(&pvt->mce_lock, flags);
1500
1501 /* proccess mcelog errors */
1502 for (i = 0; i < count; i++)
1503 i7core_mce_output_error(mci, &m[i]);
1504
1505 kfree(m);
1506
1507 /* check memory count errors */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001508 for (i = 0; i < pvt->sockets; i++)
1509 check_mc_test_err(mci, i);
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001510}
1511
1512/*
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001513 * i7core_mce_check_error Replicates mcelog routine to get errors
1514 * This routine simply queues mcelog errors, and
1515 * return. The error itself should be handled later
1516 * by i7core_check_error.
1517 */
1518static int i7core_mce_check_error(void *priv, struct mce *mce)
1519{
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001520 struct mem_ctl_info *mci = priv;
1521 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001522 unsigned long flags;
1523
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001524 /*
1525 * Just let mcelog handle it if the error is
1526 * outside the memory controller
1527 */
1528 if (((mce->status & 0xffff) >> 7) != 1)
1529 return 0;
1530
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001531 /* Bank 8 registers are the only ones that we know how to handle */
1532 if (mce->bank != 8)
1533 return 0;
1534
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001535 spin_lock_irqsave(&pvt->mce_lock, flags);
1536 if (pvt->mce_count < MCE_LOG_LEN) {
1537 memcpy(&pvt->mce_entry[pvt->mce_count], mce, sizeof(*mce));
1538 pvt->mce_count++;
1539 }
1540 spin_unlock_irqrestore(&pvt->mce_lock, flags);
1541
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001542 /* Handle fatal errors immediately */
1543 if (mce->mcgstatus & 1)
1544 i7core_check_error(mci);
1545
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001546 /* Advice mcelog that the error were handled */
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001547 return 1;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001548}
1549
1550/*
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001551 * i7core_probe Probe for ONE instance of device to see if it is
1552 * present.
1553 * return:
1554 * 0 for FOUND a device
1555 * < 0 for error code
1556 */
1557static int __devinit i7core_probe(struct pci_dev *pdev,
1558 const struct pci_device_id *id)
1559{
1560 struct mem_ctl_info *mci;
1561 struct i7core_pvt *pvt;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001562 int num_channels = 0;
1563 int num_csrows = 0;
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -03001564 int csrow = 0;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001565 int dev_idx = id->driver_data;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001566 int rc, i;
1567 u8 sockets;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001568
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001569 if (unlikely(dev_idx >= ARRAY_SIZE(i7core_devs)))
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001570 return -EINVAL;
1571
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001572 /* get the pci devices we want to reserve for our use */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001573 rc = i7core_get_devices();
1574 if (unlikely(rc < 0))
1575 return rc;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001576
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001577 sockets = 1;
1578 for (i = NUM_SOCKETS - 1; i > 0; i--)
1579 if (pci_devs[0].pdev[i]) {
1580 sockets = i + 1;
1581 break;
1582 }
1583
1584 for (i = 0; i < sockets; i++) {
1585 int channels;
1586 int csrows;
1587
1588 /* Check the number of active and not disabled channels */
1589 rc = i7core_get_active_channels(i, &channels, &csrows);
1590 if (unlikely(rc < 0))
1591 goto fail0;
1592
1593 num_channels += channels;
1594 num_csrows += csrows;
1595 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001596
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001597 /* allocate a new MC control structure */
1598 mci = edac_mc_alloc(sizeof(*pvt), num_csrows, num_channels, 0);
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001599 if (unlikely(!mci)) {
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001600 rc = -ENOMEM;
1601 goto fail0;
1602 }
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001603
1604 debugf0("MC: " __FILE__ ": %s(): mci = %p\n", __func__, mci);
1605
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001606 mci->dev = &pdev->dev; /* record ptr to the generic device */
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001607 pvt = mci->pvt_info;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001608 memset(pvt, 0, sizeof(*pvt));
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001609 pvt->sockets = sockets;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001610 mci->mc_idx = 0;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001611
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001612 /*
1613 * FIXME: how to handle RDDR3 at MCI level? It is possible to have
1614 * Mixed RDDR3/UDDR3 with Nehalem, provided that they are on different
1615 * memory channels
1616 */
1617 mci->mtype_cap = MEM_FLAG_DDR3;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001618 mci->edac_ctl_cap = EDAC_FLAG_NONE;
1619 mci->edac_cap = EDAC_FLAG_NONE;
1620 mci->mod_name = "i7core_edac.c";
1621 mci->mod_ver = I7CORE_REVISION;
1622 mci->ctl_name = i7core_devs[dev_idx].ctl_name;
1623 mci->dev_name = pci_name(pdev);
1624 mci->ctl_page_to_phys = NULL;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001625 mci->mc_driver_sysfs_attributes = i7core_inj_attrs;
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001626 /* Set the function pointer to an actual operation function */
1627 mci->edac_check = i7core_check_error;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001628
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001629 /* Store pci devices at mci for faster access */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001630 rc = mci_bind_devs(mci);
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001631 if (unlikely(rc < 0))
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001632 goto fail1;
1633
1634 /* Get dimm basic config */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001635 for (i = 0; i < sockets; i++)
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -03001636 get_dimm_config(mci, &csrow, i);
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001637
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001638 /* add this new MC control structure to EDAC's list of MCs */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001639 if (unlikely(edac_mc_add_mc(mci))) {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001640 debugf0("MC: " __FILE__
1641 ": %s(): failed edac_mc_add_mc()\n", __func__);
1642 /* FIXME: perhaps some code should go here that disables error
1643 * reporting if we just enabled it
1644 */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001645
1646 rc = -EINVAL;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001647 goto fail1;
1648 }
1649
1650 /* allocating generic PCI control info */
1651 i7core_pci = edac_pci_create_generic_ctl(&pdev->dev, EDAC_MOD_STR);
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001652 if (unlikely(!i7core_pci)) {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001653 printk(KERN_WARNING
1654 "%s(): Unable to create PCI control\n",
1655 __func__);
1656 printk(KERN_WARNING
1657 "%s(): PCI error report via EDAC not setup\n",
1658 __func__);
1659 }
1660
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001661 /* Default error mask is any memory */
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001662 pvt->inject.channel = 0;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001663 pvt->inject.dimm = -1;
1664 pvt->inject.rank = -1;
1665 pvt->inject.bank = -1;
1666 pvt->inject.page = -1;
1667 pvt->inject.col = -1;
1668
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001669 /* Registers on edac_mce in order to receive memory errors */
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001670 pvt->edac_mce.priv = mci;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001671 pvt->edac_mce.check_error = i7core_mce_check_error;
1672 spin_lock_init(&pvt->mce_lock);
1673
1674 rc = edac_mce_register(&pvt->edac_mce);
1675 if (unlikely (rc < 0)) {
1676 debugf0("MC: " __FILE__
1677 ": %s(): failed edac_mce_register()\n", __func__);
1678 goto fail1;
1679 }
1680
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001681 i7core_printk(KERN_INFO, "Driver loaded.\n");
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001682
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001683 return 0;
1684
1685fail1:
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001686 edac_mc_free(mci);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001687
1688fail0:
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001689 i7core_put_devices();
1690 return rc;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001691}
1692
1693/*
1694 * i7core_remove destructor for one instance of device
1695 *
1696 */
1697static void __devexit i7core_remove(struct pci_dev *pdev)
1698{
1699 struct mem_ctl_info *mci;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001700 struct i7core_pvt *pvt;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001701
1702 debugf0(__FILE__ ": %s()\n", __func__);
1703
1704 if (i7core_pci)
1705 edac_pci_release_generic_ctl(i7core_pci);
1706
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001707
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001708 mci = edac_mc_del_mc(&pdev->dev);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001709 if (!mci)
1710 return;
1711
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001712 /* Unregisters on edac_mce in order to receive memory errors */
1713 pvt = mci->pvt_info;
1714 edac_mce_unregister(&pvt->edac_mce);
1715
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001716 /* retrieve references to resources, and free those resources */
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001717 i7core_put_devices();
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001718
1719 edac_mc_free(mci);
1720}
1721
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001722MODULE_DEVICE_TABLE(pci, i7core_pci_tbl);
1723
1724/*
1725 * i7core_driver pci_driver structure for this module
1726 *
1727 */
1728static struct pci_driver i7core_driver = {
1729 .name = "i7core_edac",
1730 .probe = i7core_probe,
1731 .remove = __devexit_p(i7core_remove),
1732 .id_table = i7core_pci_tbl,
1733};
1734
1735/*
1736 * i7core_init Module entry function
1737 * Try to initialize this module for its devices
1738 */
1739static int __init i7core_init(void)
1740{
1741 int pci_rc;
1742
1743 debugf2("MC: " __FILE__ ": %s()\n", __func__);
1744
1745 /* Ensure that the OPSTATE is set correctly for POLL or NMI */
1746 opstate_init();
1747
1748 pci_rc = pci_register_driver(&i7core_driver);
1749
1750 return (pci_rc < 0) ? pci_rc : 0;
1751}
1752
1753/*
1754 * i7core_exit() Module exit function
1755 * Unregister the driver
1756 */
1757static void __exit i7core_exit(void)
1758{
1759 debugf2("MC: " __FILE__ ": %s()\n", __func__);
1760 pci_unregister_driver(&i7core_driver);
1761}
1762
1763module_init(i7core_init);
1764module_exit(i7core_exit);
1765
1766MODULE_LICENSE("GPL");
1767MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
1768MODULE_AUTHOR("Red Hat Inc. (http://www.redhat.com)");
1769MODULE_DESCRIPTION("MC Driver for Intel i7 Core memory controllers - "
1770 I7CORE_REVISION);
1771
1772module_param(edac_op_state, int, 0444);
1773MODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI");