blob: 86af14840b88ed3f74334b8015b3307b9a07e2eb [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
336/****************************************************************************
337 Memory check routines
338 ****************************************************************************/
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300339static struct pci_dev *get_pdev_slot_func(u8 socket, unsigned slot,
340 unsigned func)
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300341{
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300342 int i;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300343
344 for (i = 0; i < N_DEVS; i++) {
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300345 if (!pci_devs[i].pdev[socket])
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300346 continue;
347
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300348 if (PCI_SLOT(pci_devs[i].pdev[socket]->devfn) == slot &&
349 PCI_FUNC(pci_devs[i].pdev[socket]->devfn) == func) {
350 return pci_devs[i].pdev[socket];
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300351 }
352 }
353
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300354 return NULL;
355}
356
Mauro Carvalho Chehabec6df242009-07-18 10:44:30 -0300357/**
358 * i7core_get_active_channels() - gets the number of channels and csrows
359 * @socket: Quick Path Interconnect socket
360 * @channels: Number of channels that will be returned
361 * @csrows: Number of csrows found
362 *
363 * Since EDAC core needs to know in advance the number of available channels
364 * and csrows, in order to allocate memory for csrows/channels, it is needed
365 * to run two similar steps. At the first step, implemented on this function,
366 * it checks the number of csrows/channels present at one socket.
367 * this is used in order to properly allocate the size of mci components.
368 *
369 * It should be noticed that none of the current available datasheets explain
370 * or even mention how csrows are seen by the memory controller. So, we need
371 * to add a fake description for csrows.
372 * So, this driver is attributing one DIMM memory for one csrow.
373 */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300374static int i7core_get_active_channels(u8 socket, unsigned *channels,
375 unsigned *csrows)
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300376{
377 struct pci_dev *pdev = NULL;
378 int i, j;
379 u32 status, control;
380
381 *channels = 0;
382 *csrows = 0;
383
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300384 pdev = get_pdev_slot_func(socket, 3, 0);
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -0300385 if (!pdev) {
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300386 i7core_printk(KERN_ERR, "Couldn't find socket %d fn 3.0!!!\n",
387 socket);
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300388 return -ENODEV;
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -0300389 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300390
391 /* Device 3 function 0 reads */
392 pci_read_config_dword(pdev, MC_STATUS, &status);
393 pci_read_config_dword(pdev, MC_CONTROL, &control);
394
395 for (i = 0; i < NUM_CHANS; i++) {
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300396 u32 dimm_dod[3];
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300397 /* Check if the channel is active */
398 if (!(control & (1 << (8 + i))))
399 continue;
400
401 /* Check if the channel is disabled */
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300402 if (status & (1 << i))
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300403 continue;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300404
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300405 pdev = get_pdev_slot_func(socket, i + 4, 1);
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300406 if (!pdev) {
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300407 i7core_printk(KERN_ERR, "Couldn't find socket %d "
408 "fn %d.%d!!!\n",
409 socket, i + 4, 1);
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300410 return -ENODEV;
411 }
412 /* Devices 4-6 function 1 */
413 pci_read_config_dword(pdev,
414 MC_DOD_CH_DIMM0, &dimm_dod[0]);
415 pci_read_config_dword(pdev,
416 MC_DOD_CH_DIMM1, &dimm_dod[1]);
417 pci_read_config_dword(pdev,
418 MC_DOD_CH_DIMM2, &dimm_dod[2]);
419
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300420 (*channels)++;
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300421
422 for (j = 0; j < 3; j++) {
423 if (!DIMM_PRESENT(dimm_dod[j]))
424 continue;
425 (*csrows)++;
426 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300427 }
428
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -0300429 debugf0("Number of active channels on socket %d: %d\n",
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300430 socket, *channels);
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300431
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300432 return 0;
433}
434
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -0300435static int get_dimm_config(struct mem_ctl_info *mci, int *csrow, u8 socket)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300436{
437 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300438 struct csrow_info *csr;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300439 struct pci_dev *pdev;
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -0300440 int i, j;
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300441 unsigned long last_page = 0;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300442 enum edac_type mode;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300443 enum mem_type mtype;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300444
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300445 /* Get data from the MC register, function 0 */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300446 pdev = pvt->pci_mcr[socket][0];
Mauro Carvalho Chehab7dd69532009-06-22 22:48:30 -0300447 if (!pdev)
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300448 return -ENODEV;
449
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300450 /* Device 3 function 0 reads */
Mauro Carvalho Chehab7dd69532009-06-22 22:48:30 -0300451 pci_read_config_dword(pdev, MC_CONTROL, &pvt->info.mc_control);
452 pci_read_config_dword(pdev, MC_STATUS, &pvt->info.mc_status);
453 pci_read_config_dword(pdev, MC_MAX_DOD, &pvt->info.max_dod);
454 pci_read_config_dword(pdev, MC_CHANNEL_MAPPER, &pvt->info.ch_map);
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300455
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300456 debugf0("QPI %d control=0x%08x status=0x%08x dod=0x%08x map=0x%08x\n",
457 socket, pvt->info.mc_control, pvt->info.mc_status,
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300458 pvt->info.max_dod, pvt->info.ch_map);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300459
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300460 if (ECC_ENABLED(pvt)) {
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300461 debugf0("ECC enabled with x%d SDCC\n", ECCx8(pvt) ? 8 : 4);
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300462 if (ECCx8(pvt))
463 mode = EDAC_S8ECD8ED;
464 else
465 mode = EDAC_S4ECD4ED;
466 } else {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300467 debugf0("ECC disabled\n");
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300468 mode = EDAC_NONE;
469 }
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300470
471 /* FIXME: need to handle the error codes */
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300472 debugf0("DOD Max limits: DIMMS: %d, %d-ranked, %d-banked "
473 "x%x x 0x%x\n",
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300474 numdimms(pvt->info.max_dod),
475 numrank(pvt->info.max_dod >> 2),
476 numbank(pvt->info.max_dod >> 4));
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300477 numrow(pvt->info.max_dod >> 6),
478 numcol(pvt->info.max_dod >> 9));
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300479
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300480 for (i = 0; i < NUM_CHANS; i++) {
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300481 u32 data, dimm_dod[3], value[8];
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300482
483 if (!CH_ACTIVE(pvt, i)) {
484 debugf0("Channel %i is not active\n", i);
485 continue;
486 }
487 if (CH_DISABLED(pvt, i)) {
488 debugf0("Channel %i is disabled\n", i);
489 continue;
490 }
491
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300492 /* Devices 4-6 function 0 */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300493 pci_read_config_dword(pvt->pci_ch[socket][i][0],
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300494 MC_CHANNEL_DIMM_INIT_PARAMS, &data);
495
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300496 pvt->channel[socket][i].ranks = (data & QUAD_RANK_PRESENT) ?
497 4 : 2;
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300498
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300499 if (data & REGISTERED_DIMM)
500 mtype = MEM_RDDR3;
501 else
502 mtype = MEM_DDR3;
503#if 0
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300504 if (data & THREE_DIMMS_PRESENT)
505 pvt->channel[i].dimms = 3;
506 else if (data & SINGLE_QUAD_RANK_PRESENT)
507 pvt->channel[i].dimms = 1;
508 else
509 pvt->channel[i].dimms = 2;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300510#endif
511
512 /* Devices 4-6 function 1 */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300513 pci_read_config_dword(pvt->pci_ch[socket][i][1],
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300514 MC_DOD_CH_DIMM0, &dimm_dod[0]);
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300515 pci_read_config_dword(pvt->pci_ch[socket][i][1],
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300516 MC_DOD_CH_DIMM1, &dimm_dod[1]);
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300517 pci_read_config_dword(pvt->pci_ch[socket][i][1],
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300518 MC_DOD_CH_DIMM2, &dimm_dod[2]);
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300519
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300520 debugf0("Ch%d phy rd%d, wr%d (0x%08x): "
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300521 "%d ranks, %cDIMMs\n",
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300522 i,
523 RDLCH(pvt->info.ch_map, i), WRLCH(pvt->info.ch_map, i),
524 data,
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300525 pvt->channel[socket][i].ranks,
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300526 (data & REGISTERED_DIMM) ? 'R' : 'U');
Mauro Carvalho Chehab7dd69532009-06-22 22:48:30 -0300527
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300528 for (j = 0; j < 3; j++) {
529 u32 banks, ranks, rows, cols;
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300530 u32 size, npages;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300531
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300532 if (!DIMM_PRESENT(dimm_dod[j]))
533 continue;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300534
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300535 banks = numbank(MC_DOD_NUMBANK(dimm_dod[j]));
536 ranks = numrank(MC_DOD_NUMRANK(dimm_dod[j]));
537 rows = numrow(MC_DOD_NUMROW(dimm_dod[j]));
538 cols = numcol(MC_DOD_NUMCOL(dimm_dod[j]));
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300539
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300540 /* DDR3 has 8 I/O banks */
541 size = (rows * cols * banks * ranks) >> (20 - 3);
542
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300543 pvt->channel[socket][i].dimms++;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300544
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300545 debugf0("\tdimm %d %d Mb offset: %x, "
546 "bank: %d, rank: %d, row: %#x, col: %#x\n",
547 j, size,
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300548 RANKOFFSET(dimm_dod[j]),
549 banks, ranks, rows, cols);
550
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300551#if PAGE_SHIFT > 20
552 npages = size >> (PAGE_SHIFT - 20);
553#else
554 npages = size << (20 - PAGE_SHIFT);
555#endif
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300556
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -0300557 csr = &mci->csrows[*csrow];
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300558 csr->first_page = last_page + 1;
559 last_page += npages;
560 csr->last_page = last_page;
561 csr->nr_pages = npages;
562
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300563 csr->page_mask = 0;
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300564 csr->grain = 8;
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -0300565 csr->csrow_idx = *csrow;
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300566 csr->nr_channels = 1;
567
568 csr->channels[0].chan_idx = i;
569 csr->channels[0].ce_count = 0;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300570
571 switch (banks) {
572 case 4:
573 csr->dtype = DEV_X4;
574 break;
575 case 8:
576 csr->dtype = DEV_X8;
577 break;
578 case 16:
579 csr->dtype = DEV_X16;
580 break;
581 default:
582 csr->dtype = DEV_UNKNOWN;
583 }
584
585 csr->edac_mode = mode;
586 csr->mtype = mtype;
587
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -0300588 (*csrow)++;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300589 }
590
591 pci_read_config_dword(pdev, MC_SAG_CH_0, &value[0]);
592 pci_read_config_dword(pdev, MC_SAG_CH_1, &value[1]);
593 pci_read_config_dword(pdev, MC_SAG_CH_2, &value[2]);
594 pci_read_config_dword(pdev, MC_SAG_CH_3, &value[3]);
595 pci_read_config_dword(pdev, MC_SAG_CH_4, &value[4]);
596 pci_read_config_dword(pdev, MC_SAG_CH_5, &value[5]);
597 pci_read_config_dword(pdev, MC_SAG_CH_6, &value[6]);
598 pci_read_config_dword(pdev, MC_SAG_CH_7, &value[7]);
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300599 debugf1("\t[%i] DIVBY3\tREMOVED\tOFFSET\n", i);
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300600 for (j = 0; j < 8; j++)
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300601 debugf1("\t\t%#x\t%#x\t%#x\n",
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300602 (value[j] >> 27) & 0x1,
603 (value[j] >> 24) & 0x7,
604 (value[j] && ((1 << 24) - 1)));
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300605 }
606
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300607 return 0;
608}
609
610/****************************************************************************
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300611 Error insertion routines
612 ****************************************************************************/
613
614/* The i7core has independent error injection features per channel.
615 However, to have a simpler code, we don't allow enabling error injection
616 on more than one channel.
617 Also, since a change at an inject parameter will be applied only at enable,
618 we're disabling error injection on all write calls to the sysfs nodes that
619 controls the error code injection.
620 */
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300621static int disable_inject(struct mem_ctl_info *mci)
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300622{
623 struct i7core_pvt *pvt = mci->pvt_info;
624
625 pvt->inject.enable = 0;
626
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300627 if (!pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0])
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300628 return -ENODEV;
629
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300630 pci_write_config_dword(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0],
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300631 MC_CHANNEL_ERROR_MASK, 0);
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300632
633 return 0;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300634}
635
636/*
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300637 * i7core inject inject.socket
638 *
639 * accept and store error injection inject.socket value
640 */
641static ssize_t i7core_inject_socket_store(struct mem_ctl_info *mci,
642 const char *data, size_t count)
643{
644 struct i7core_pvt *pvt = mci->pvt_info;
645 unsigned long value;
646 int rc;
647
648 rc = strict_strtoul(data, 10, &value);
649 if ((rc < 0) || (value > pvt->sockets))
650 return 0;
651
652 pvt->inject.section = (u32) value;
653 return count;
654}
655
656static ssize_t i7core_inject_socket_show(struct mem_ctl_info *mci,
657 char *data)
658{
659 struct i7core_pvt *pvt = mci->pvt_info;
660 return sprintf(data, "%d\n", pvt->inject.socket);
661}
662
663/*
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300664 * i7core inject inject.section
665 *
666 * accept and store error injection inject.section value
667 * bit 0 - refers to the lower 32-byte half cacheline
668 * bit 1 - refers to the upper 32-byte half cacheline
669 */
670static ssize_t i7core_inject_section_store(struct mem_ctl_info *mci,
671 const char *data, size_t count)
672{
673 struct i7core_pvt *pvt = mci->pvt_info;
674 unsigned long value;
675 int rc;
676
677 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300678 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300679
680 rc = strict_strtoul(data, 10, &value);
681 if ((rc < 0) || (value > 3))
682 return 0;
683
684 pvt->inject.section = (u32) value;
685 return count;
686}
687
688static ssize_t i7core_inject_section_show(struct mem_ctl_info *mci,
689 char *data)
690{
691 struct i7core_pvt *pvt = mci->pvt_info;
692 return sprintf(data, "0x%08x\n", pvt->inject.section);
693}
694
695/*
696 * i7core inject.type
697 *
698 * accept and store error injection inject.section value
699 * bit 0 - repeat enable - Enable error repetition
700 * bit 1 - inject ECC error
701 * bit 2 - inject parity error
702 */
703static ssize_t i7core_inject_type_store(struct mem_ctl_info *mci,
704 const char *data, size_t count)
705{
706 struct i7core_pvt *pvt = mci->pvt_info;
707 unsigned long value;
708 int rc;
709
710 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300711 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300712
713 rc = strict_strtoul(data, 10, &value);
714 if ((rc < 0) || (value > 7))
715 return 0;
716
717 pvt->inject.type = (u32) value;
718 return count;
719}
720
721static ssize_t i7core_inject_type_show(struct mem_ctl_info *mci,
722 char *data)
723{
724 struct i7core_pvt *pvt = mci->pvt_info;
725 return sprintf(data, "0x%08x\n", pvt->inject.type);
726}
727
728/*
729 * i7core_inject_inject.eccmask_store
730 *
731 * The type of error (UE/CE) will depend on the inject.eccmask value:
732 * Any bits set to a 1 will flip the corresponding ECC bit
733 * Correctable errors can be injected by flipping 1 bit or the bits within
734 * a symbol pair (2 consecutive aligned 8-bit pairs - i.e. 7:0 and 15:8 or
735 * 23:16 and 31:24). Flipping bits in two symbol pairs will cause an
736 * uncorrectable error to be injected.
737 */
738static ssize_t i7core_inject_eccmask_store(struct mem_ctl_info *mci,
739 const char *data, size_t count)
740{
741 struct i7core_pvt *pvt = mci->pvt_info;
742 unsigned long value;
743 int rc;
744
745 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300746 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300747
748 rc = strict_strtoul(data, 10, &value);
749 if (rc < 0)
750 return 0;
751
752 pvt->inject.eccmask = (u32) value;
753 return count;
754}
755
756static ssize_t i7core_inject_eccmask_show(struct mem_ctl_info *mci,
757 char *data)
758{
759 struct i7core_pvt *pvt = mci->pvt_info;
760 return sprintf(data, "0x%08x\n", pvt->inject.eccmask);
761}
762
763/*
764 * i7core_addrmatch
765 *
766 * The type of error (UE/CE) will depend on the inject.eccmask value:
767 * Any bits set to a 1 will flip the corresponding ECC bit
768 * Correctable errors can be injected by flipping 1 bit or the bits within
769 * a symbol pair (2 consecutive aligned 8-bit pairs - i.e. 7:0 and 15:8 or
770 * 23:16 and 31:24). Flipping bits in two symbol pairs will cause an
771 * uncorrectable error to be injected.
772 */
773static ssize_t i7core_inject_addrmatch_store(struct mem_ctl_info *mci,
774 const char *data, size_t count)
775{
776 struct i7core_pvt *pvt = mci->pvt_info;
777 char *cmd, *val;
778 long value;
779 int rc;
780
781 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300782 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300783
784 do {
785 cmd = strsep((char **) &data, ":");
786 if (!cmd)
787 break;
788 val = strsep((char **) &data, " \n\t");
789 if (!val)
790 return cmd - data;
791
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300792 if (!strcasecmp(val, "any"))
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300793 value = -1;
794 else {
795 rc = strict_strtol(val, 10, &value);
796 if ((rc < 0) || (value < 0))
797 return cmd - data;
798 }
799
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300800 if (!strcasecmp(cmd, "channel")) {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300801 if (value < 3)
802 pvt->inject.channel = value;
803 else
804 return cmd - data;
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300805 } else if (!strcasecmp(cmd, "dimm")) {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300806 if (value < 4)
807 pvt->inject.dimm = value;
808 else
809 return cmd - data;
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300810 } else if (!strcasecmp(cmd, "rank")) {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300811 if (value < 4)
812 pvt->inject.rank = value;
813 else
814 return cmd - data;
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300815 } else if (!strcasecmp(cmd, "bank")) {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300816 if (value < 4)
817 pvt->inject.bank = value;
818 else
819 return cmd - data;
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300820 } else if (!strcasecmp(cmd, "page")) {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300821 if (value <= 0xffff)
822 pvt->inject.page = value;
823 else
824 return cmd - data;
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300825 } else if (!strcasecmp(cmd, "col") ||
826 !strcasecmp(cmd, "column")) {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300827 if (value <= 0x3fff)
828 pvt->inject.col = value;
829 else
830 return cmd - data;
831 }
832 } while (1);
833
834 return count;
835}
836
837static ssize_t i7core_inject_addrmatch_show(struct mem_ctl_info *mci,
838 char *data)
839{
840 struct i7core_pvt *pvt = mci->pvt_info;
841 char channel[4], dimm[4], bank[4], rank[4], page[7], col[7];
842
843 if (pvt->inject.channel < 0)
844 sprintf(channel, "any");
845 else
846 sprintf(channel, "%d", pvt->inject.channel);
847 if (pvt->inject.dimm < 0)
848 sprintf(dimm, "any");
849 else
850 sprintf(dimm, "%d", pvt->inject.dimm);
851 if (pvt->inject.bank < 0)
852 sprintf(bank, "any");
853 else
854 sprintf(bank, "%d", pvt->inject.bank);
855 if (pvt->inject.rank < 0)
856 sprintf(rank, "any");
857 else
858 sprintf(rank, "%d", pvt->inject.rank);
859 if (pvt->inject.page < 0)
860 sprintf(page, "any");
861 else
862 sprintf(page, "0x%04x", pvt->inject.page);
863 if (pvt->inject.col < 0)
864 sprintf(col, "any");
865 else
866 sprintf(col, "0x%04x", pvt->inject.col);
867
868 return sprintf(data, "channel: %s\ndimm: %s\nbank: %s\n"
869 "rank: %s\npage: %s\ncolumn: %s\n",
870 channel, dimm, bank, rank, page, col);
871}
872
873/*
874 * This routine prepares the Memory Controller for error injection.
875 * The error will be injected when some process tries to write to the
876 * memory that matches the given criteria.
877 * The criteria can be set in terms of a mask where dimm, rank, bank, page
878 * and col can be specified.
879 * A -1 value for any of the mask items will make the MCU to ignore
880 * that matching criteria for error injection.
881 *
882 * It should be noticed that the error will only happen after a write operation
883 * on a memory that matches the condition. if REPEAT_EN is not enabled at
884 * inject mask, then it will produce just one error. Otherwise, it will repeat
885 * until the injectmask would be cleaned.
886 *
887 * FIXME: This routine assumes that MAXNUMDIMMS value of MC_MAX_DOD
888 * is reliable enough to check if the MC is using the
889 * three channels. However, this is not clear at the datasheet.
890 */
891static ssize_t i7core_inject_enable_store(struct mem_ctl_info *mci,
892 const char *data, size_t count)
893{
894 struct i7core_pvt *pvt = mci->pvt_info;
895 u32 injectmask;
896 u64 mask = 0;
897 int rc;
898 long enable;
899
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300900 if (!pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0])
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300901 return 0;
902
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300903 rc = strict_strtoul(data, 10, &enable);
904 if ((rc < 0))
905 return 0;
906
907 if (enable) {
908 pvt->inject.enable = 1;
909 } else {
910 disable_inject(mci);
911 return count;
912 }
913
914 /* Sets pvt->inject.dimm mask */
915 if (pvt->inject.dimm < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300916 mask |= 1L << 41;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300917 else {
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300918 if (pvt->channel[pvt->inject.socket][pvt->inject.channel].dimms > 2)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300919 mask |= (pvt->inject.dimm & 0x3L) << 35;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300920 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300921 mask |= (pvt->inject.dimm & 0x1L) << 36;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300922 }
923
924 /* Sets pvt->inject.rank mask */
925 if (pvt->inject.rank < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300926 mask |= 1L << 40;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300927 else {
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300928 if (pvt->channel[pvt->inject.socket][pvt->inject.channel].dimms > 2)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300929 mask |= (pvt->inject.rank & 0x1L) << 34;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300930 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300931 mask |= (pvt->inject.rank & 0x3L) << 34;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300932 }
933
934 /* Sets pvt->inject.bank mask */
935 if (pvt->inject.bank < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300936 mask |= 1L << 39;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300937 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300938 mask |= (pvt->inject.bank & 0x15L) << 30;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300939
940 /* Sets pvt->inject.page mask */
941 if (pvt->inject.page < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300942 mask |= 1L << 38;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300943 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300944 mask |= (pvt->inject.page & 0xffffL) << 14;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300945
946 /* Sets pvt->inject.column mask */
947 if (pvt->inject.col < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300948 mask |= 1L << 37;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300949 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300950 mask |= (pvt->inject.col & 0x3fffL);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300951
Mauro Carvalho Chehabe9bd2e72009-07-09 22:14:35 -0300952 /* Unlock writes to registers */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300953 pci_write_config_dword(pvt->pci_noncore[pvt->inject.socket],
954 MC_CFG_CONTROL, 0x2);
Mauro Carvalho Chehabe9bd2e72009-07-09 22:14:35 -0300955 msleep(100);
956
957 /* Zeroes error count registers */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300958 pci_write_config_dword(pvt->pci_mcr[pvt->inject.socket][4],
959 MC_TEST_ERR_RCV1, 0);
960 pci_write_config_dword(pvt->pci_mcr[pvt->inject.socket][4],
961 MC_TEST_ERR_RCV0, 0);
962 pvt->ce_count_available[pvt->inject.socket] = 0;
Mauro Carvalho Chehabe9bd2e72009-07-09 22:14:35 -0300963
964
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300965#if USE_QWORD
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300966 pci_write_config_qword(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0],
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300967 MC_CHANNEL_ADDR_MATCH, mask);
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300968#else
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300969 pci_write_config_dword(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0],
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300970 MC_CHANNEL_ADDR_MATCH, mask);
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300971 pci_write_config_dword(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0],
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300972 MC_CHANNEL_ADDR_MATCH + 4, mask >> 32L);
973#endif
974
975#if 1
976#if USE_QWORD
977 u64 rdmask;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300978 pci_read_config_qword(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0],
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300979 MC_CHANNEL_ADDR_MATCH, &rdmask);
980 debugf0("Inject addr match write 0x%016llx, read: 0x%016llx\n",
981 mask, rdmask);
982#else
983 u32 rdmask1, rdmask2;
984
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300985 pci_read_config_dword(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0],
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300986 MC_CHANNEL_ADDR_MATCH, &rdmask1);
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300987 pci_read_config_dword(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0],
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300988 MC_CHANNEL_ADDR_MATCH + 4, &rdmask2);
989
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -0300990 debugf0("Inject addr match write 0x%016llx, read: 0x%08x 0x%08x\n",
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300991 mask, rdmask1, rdmask2);
992#endif
993#endif
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300994
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300995 pci_write_config_dword(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0],
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300996 MC_CHANNEL_ERROR_MASK, pvt->inject.eccmask);
997
998 /*
999 * bit 0: REPEAT_EN
1000 * bits 1-2: MASK_HALF_CACHELINE
1001 * bit 3: INJECT_ECC
1002 * bit 4: INJECT_ADDR_PARITY
1003 */
1004
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -03001005 injectmask = (pvt->inject.type & 1) |
1006 (pvt->inject.section & 0x3) << 1 |
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001007 (pvt->inject.type & 0x6) << (3 - 1);
1008
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001009 pci_write_config_dword(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0],
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001010 MC_CHANNEL_ERROR_MASK, injectmask);
1011
Mauro Carvalho Chehabe9bd2e72009-07-09 22:14:35 -03001012#if 0
1013 /* lock writes to registers */
1014 pci_write_config_dword(pvt->pci_noncore, MC_CFG_CONTROL, 0);
1015#endif
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001016 debugf0("Error inject addr match 0x%016llx, ecc 0x%08x,"
1017 " inject 0x%08x\n",
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001018 mask, pvt->inject.eccmask, injectmask);
1019
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -03001020
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001021 return count;
1022}
1023
1024static ssize_t i7core_inject_enable_show(struct mem_ctl_info *mci,
1025 char *data)
1026{
1027 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -03001028 u32 injectmask;
1029
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001030 pci_read_config_dword(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0],
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -03001031 MC_CHANNEL_ERROR_MASK, &injectmask);
1032
1033 debugf0("Inject error read: 0x%018x\n", injectmask);
1034
1035 if (injectmask & 0x0c)
1036 pvt->inject.enable = 1;
1037
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001038 return sprintf(data, "%d\n", pvt->inject.enable);
1039}
1040
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001041static ssize_t i7core_ce_regs_show(struct mem_ctl_info *mci, char *data)
1042{
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001043 unsigned i, count, total = 0;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001044 struct i7core_pvt *pvt = mci->pvt_info;
1045
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001046 for (i = 0; i < pvt->sockets; i++) {
1047 if (!pvt->ce_count_available[i])
1048 count = sprintf(data, "socket 0 data unavailable\n");
1049 else
1050 count = sprintf(data, "socket %d, dimm0: %lu\n"
1051 "dimm1: %lu\ndimm2: %lu\n",
1052 i,
1053 pvt->ce_count[i][0],
1054 pvt->ce_count[i][1],
1055 pvt->ce_count[i][2]);
1056 data += count;
1057 total += count;
1058 }
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001059
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001060 return total;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001061}
1062
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001063/*
1064 * Sysfs struct
1065 */
1066static struct mcidev_sysfs_attribute i7core_inj_attrs[] = {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001067 {
1068 .attr = {
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001069 .name = "inject_socket",
1070 .mode = (S_IRUGO | S_IWUSR)
1071 },
1072 .show = i7core_inject_socket_show,
1073 .store = i7core_inject_socket_store,
1074 }, {
1075 .attr = {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001076 .name = "inject_section",
1077 .mode = (S_IRUGO | S_IWUSR)
1078 },
1079 .show = i7core_inject_section_show,
1080 .store = i7core_inject_section_store,
1081 }, {
1082 .attr = {
1083 .name = "inject_type",
1084 .mode = (S_IRUGO | S_IWUSR)
1085 },
1086 .show = i7core_inject_type_show,
1087 .store = i7core_inject_type_store,
1088 }, {
1089 .attr = {
1090 .name = "inject_eccmask",
1091 .mode = (S_IRUGO | S_IWUSR)
1092 },
1093 .show = i7core_inject_eccmask_show,
1094 .store = i7core_inject_eccmask_store,
1095 }, {
1096 .attr = {
1097 .name = "inject_addrmatch",
1098 .mode = (S_IRUGO | S_IWUSR)
1099 },
1100 .show = i7core_inject_addrmatch_show,
1101 .store = i7core_inject_addrmatch_store,
1102 }, {
1103 .attr = {
1104 .name = "inject_enable",
1105 .mode = (S_IRUGO | S_IWUSR)
1106 },
1107 .show = i7core_inject_enable_show,
1108 .store = i7core_inject_enable_store,
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001109 }, {
1110 .attr = {
1111 .name = "corrected_error_counts",
1112 .mode = (S_IRUGO | S_IWUSR)
1113 },
1114 .show = i7core_ce_regs_show,
1115 .store = NULL,
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001116 },
1117};
1118
1119/****************************************************************************
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001120 Device initialization routines: put/get, init/exit
1121 ****************************************************************************/
1122
1123/*
1124 * i7core_put_devices 'put' all the devices that we have
1125 * reserved via 'get'
1126 */
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001127static void i7core_put_devices(void)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001128{
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001129 int i, j;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001130
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001131 for (i = 0; i < NUM_SOCKETS; i++)
1132 for (j = 0; j < N_DEVS; j++)
1133 pci_dev_put(pci_devs[j].pdev[i]);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001134}
1135
1136/*
1137 * i7core_get_devices Find and perform 'get' operation on the MCH's
1138 * device/functions we want to reference for this driver
1139 *
1140 * Need to 'get' device 16 func 1 and func 2
1141 */
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001142int i7core_get_onedevice(struct pci_dev **prev, int devno)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001143{
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001144 struct pci_dev *pdev = NULL;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001145 u8 bus = 0;
1146 u8 socket = 0;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001147
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001148 pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
1149 pci_devs[devno].dev_id, *prev);
1150
1151 /*
1152 * On Xeon 55xx, the Intel Quckpath Arch Generic Non-core pci buses
1153 * aren't announced by acpi. So, we need to use a legacy scan probing
1154 * to detect them
1155 */
1156 if (unlikely(!pdev && !devno && !prev)) {
1157 pcibios_scan_specific_bus(254);
1158 pcibios_scan_specific_bus(255);
1159
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001160 pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001161 pci_devs[devno].dev_id, *prev);
1162 }
Mauro Carvalho Chehabd1fd4fb2009-07-10 18:39:53 -03001163
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001164 /*
1165 * On Xeon 55xx, the Intel Quckpath Arch Generic Non-core regs
1166 * is at addr 8086:2c40, instead of 8086:2c41. So, we need
1167 * to probe for the alternate address in case of failure
1168 */
1169 if (pci_devs[devno].dev_id == PCI_DEVICE_ID_INTEL_I7_NOCORE && !pdev)
1170 pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
1171 PCI_DEVICE_ID_INTEL_I7_NOCORE_ALT, *prev);
Mauro Carvalho Chehabd1fd4fb2009-07-10 18:39:53 -03001172
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001173 if (!pdev) {
1174 if (*prev) {
1175 *prev = pdev;
1176 return 0;
Mauro Carvalho Chehabd1fd4fb2009-07-10 18:39:53 -03001177 }
1178
Mauro Carvalho Chehab310cbb72009-07-17 00:09:10 -03001179 /*
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001180 * Dev 3 function 2 only exists on chips with RDIMMs
1181 * so, it is ok to not found it
Mauro Carvalho Chehab310cbb72009-07-17 00:09:10 -03001182 */
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001183 if ((pci_devs[devno].dev == 3) && (pci_devs[devno].func == 2)) {
1184 *prev = pdev;
1185 return 0;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001186 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001187
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001188 i7core_printk(KERN_ERR,
1189 "Device not found: dev %02x.%d PCI ID %04x:%04x\n",
1190 pci_devs[devno].dev, pci_devs[devno].func,
1191 PCI_VENDOR_ID_INTEL, pci_devs[devno].dev_id);
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001192
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001193 /* End of list, leave */
1194 return -ENODEV;
1195 }
1196 bus = pdev->bus->number;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001197
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001198 if (bus == 0x3f)
1199 socket = 0;
1200 else
1201 socket = 255 - bus;
1202
1203 if (socket >= NUM_SOCKETS) {
1204 i7core_printk(KERN_ERR,
1205 "Unexpected socket for "
1206 "dev %02x:%02x.%d PCI ID %04x:%04x\n",
1207 bus, pci_devs[devno].dev, pci_devs[devno].func,
1208 PCI_VENDOR_ID_INTEL, pci_devs[devno].dev_id);
1209 pci_dev_put(pdev);
1210 return -ENODEV;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001211 }
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001212
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001213 if (pci_devs[devno].pdev[socket]) {
1214 i7core_printk(KERN_ERR,
1215 "Duplicated device for "
1216 "dev %02x:%02x.%d PCI ID %04x:%04x\n",
1217 bus, pci_devs[devno].dev, pci_devs[devno].func,
1218 PCI_VENDOR_ID_INTEL, pci_devs[devno].dev_id);
1219 pci_dev_put(pdev);
1220 return -ENODEV;
1221 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001222
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001223 pci_devs[devno].pdev[socket] = pdev;
1224
1225 /* Sanity check */
1226 if (unlikely(PCI_SLOT(pdev->devfn) != pci_devs[devno].dev ||
1227 PCI_FUNC(pdev->devfn) != pci_devs[devno].func)) {
1228 i7core_printk(KERN_ERR,
1229 "Device PCI ID %04x:%04x "
1230 "has dev %02x:%02x.%d instead of dev %02x:%02x.%d\n",
1231 PCI_VENDOR_ID_INTEL, pci_devs[devno].dev_id,
1232 bus, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
1233 bus, pci_devs[devno].dev, pci_devs[devno].func);
1234 return -ENODEV;
1235 }
1236
1237 /* Be sure that the device is enabled */
1238 if (unlikely(pci_enable_device(pdev) < 0)) {
1239 i7core_printk(KERN_ERR,
1240 "Couldn't enable "
1241 "dev %02x:%02x.%d PCI ID %04x:%04x\n",
1242 bus, pci_devs[devno].dev, pci_devs[devno].func,
1243 PCI_VENDOR_ID_INTEL, pci_devs[devno].dev_id);
1244 return -ENODEV;
1245 }
1246
1247 i7core_printk(KERN_INFO,
1248 "Registered socket %d "
1249 "dev %02x:%02x.%d PCI ID %04x:%04x\n",
1250 socket, bus, pci_devs[devno].dev, pci_devs[devno].func,
1251 PCI_VENDOR_ID_INTEL, pci_devs[devno].dev_id);
1252
1253 *prev = pdev;
1254
1255 return 0;
1256}
1257
1258static int i7core_get_devices(void)
1259{
1260 int i;
1261 struct pci_dev *pdev = NULL;
1262
1263 for (i = 0; i < N_DEVS; i++) {
1264 pdev = NULL;
1265 do {
1266 if (i7core_get_onedevice(&pdev, i) < 0) {
1267 i7core_put_devices();
1268 return -ENODEV;
1269 }
1270 } while (pdev);
1271 }
1272 return 0;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001273}
1274
1275static int mci_bind_devs(struct mem_ctl_info *mci)
1276{
1277 struct i7core_pvt *pvt = mci->pvt_info;
1278 struct pci_dev *pdev;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001279 int i, j, func, slot;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001280
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001281 for (i = 0; i < pvt->sockets; i++) {
1282 for (j = 0; j < N_DEVS; j++) {
1283 pdev = pci_devs[j].pdev[i];
1284 if (!pdev)
1285 continue;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001286
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001287 func = PCI_FUNC(pdev->devfn);
1288 slot = PCI_SLOT(pdev->devfn);
1289 if (slot == 3) {
1290 if (unlikely(func > MAX_MCR_FUNC))
1291 goto error;
1292 pvt->pci_mcr[i][func] = pdev;
1293 } else if (likely(slot >= 4 && slot < 4 + NUM_CHANS)) {
1294 if (unlikely(func > MAX_CHAN_FUNC))
1295 goto error;
1296 pvt->pci_ch[i][slot - 4][func] = pdev;
1297 } else if (!slot && !func)
1298 pvt->pci_noncore[i] = pdev;
1299 else
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001300 goto error;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001301
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001302 debugf0("Associated fn %d.%d, dev = %p, socket %d\n",
1303 PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
1304 pdev, i);
1305 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001306 }
Mauro Carvalho Chehabe9bd2e72009-07-09 22:14:35 -03001307
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001308 return 0;
1309
1310error:
1311 i7core_printk(KERN_ERR, "Device %d, function %d "
1312 "is out of the expected range\n",
1313 slot, func);
1314 return -EINVAL;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001315}
1316
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001317/****************************************************************************
1318 Error check routines
1319 ****************************************************************************/
1320
1321/* This function is based on the device 3 function 4 registers as described on:
1322 * Intel Xeon Processor 5500 Series Datasheet Volume 2
1323 * http://www.intel.com/Assets/PDF/datasheet/321322.pdf
1324 * also available at:
1325 * http://www.arrownac.com/manufacturers/intel/s/nehalem/5500-datasheet-v2.pdf
1326 */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001327static void check_mc_test_err(struct mem_ctl_info *mci, u8 socket)
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001328{
1329 struct i7core_pvt *pvt = mci->pvt_info;
1330 u32 rcv1, rcv0;
1331 int new0, new1, new2;
1332
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001333 if (!pvt->pci_mcr[socket][4]) {
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001334 debugf0("%s MCR registers not found\n",__func__);
1335 return;
1336 }
1337
1338 /* Corrected error reads */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001339 pci_read_config_dword(pvt->pci_mcr[socket][4], MC_TEST_ERR_RCV1, &rcv1);
1340 pci_read_config_dword(pvt->pci_mcr[socket][4], MC_TEST_ERR_RCV0, &rcv0);
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001341
1342 /* Store the new values */
1343 new2 = DIMM2_COR_ERR(rcv1);
1344 new1 = DIMM1_COR_ERR(rcv0);
1345 new0 = DIMM0_COR_ERR(rcv0);
1346
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001347#if 0
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001348 debugf2("%s CE rcv1=0x%08x rcv0=0x%08x, %d %d %d\n",
1349 (pvt->ce_count_available ? "UPDATE" : "READ"),
1350 rcv1, rcv0, new0, new1, new2);
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001351#endif
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001352
1353 /* Updates CE counters if it is not the first time here */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001354 if (pvt->ce_count_available[socket]) {
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001355 /* Updates CE counters */
1356 int add0, add1, add2;
1357
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001358 add2 = new2 - pvt->last_ce_count[socket][2];
1359 add1 = new1 - pvt->last_ce_count[socket][1];
1360 add0 = new0 - pvt->last_ce_count[socket][0];
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001361
1362 if (add2 < 0)
1363 add2 += 0x7fff;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001364 pvt->ce_count[socket][2] += add2;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001365
1366 if (add1 < 0)
1367 add1 += 0x7fff;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001368 pvt->ce_count[socket][1] += add1;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001369
1370 if (add0 < 0)
1371 add0 += 0x7fff;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001372 pvt->ce_count[socket][0] += add0;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001373 } else
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001374 pvt->ce_count_available[socket] = 1;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001375
1376 /* Store the new values */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001377 pvt->last_ce_count[socket][2] = new2;
1378 pvt->last_ce_count[socket][1] = new1;
1379 pvt->last_ce_count[socket][0] = new0;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001380}
1381
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001382/*
1383 * According with tables E-11 and E-12 of chapter E.3.3 of Intel 64 and IA-32
1384 * Architectures Software Developer’s Manual Volume 3B.
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001385 * Nehalem are defined as family 0x06, model 0x1a
1386 *
1387 * The MCA registers used here are the following ones:
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001388 * struct mce field MCA Register
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001389 * m->status MSR_IA32_MC8_STATUS
1390 * m->addr MSR_IA32_MC8_ADDR
1391 * m->misc MSR_IA32_MC8_MISC
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001392 * In the case of Nehalem, the error information is masked at .status and .misc
1393 * fields
1394 */
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001395static void i7core_mce_output_error(struct mem_ctl_info *mci,
1396 struct mce *m)
1397{
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001398 char *type, *optype, *err, *msg;
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001399 unsigned long error = m->status & 0x1ff0000l;
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001400 u32 optypenum = (m->status >> 4) & 0x07;
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001401 u32 core_err_cnt = (m->status >> 38) && 0x7fff;
1402 u32 dimm = (m->misc >> 16) & 0x3;
1403 u32 channel = (m->misc >> 18) & 0x3;
1404 u32 syndrome = m->misc >> 32;
1405 u32 errnum = find_first_bit(&error, 32);
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001406
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001407 if (m->mcgstatus & 1)
1408 type = "FATAL";
1409 else
1410 type = "NON_FATAL";
1411
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001412 switch (optypenum) {
1413 case 0:
1414 optype = "generic undef request";
1415 break;
1416 case 1:
1417 optype = "read error";
1418 break;
1419 case 2:
1420 optype = "write error";
1421 break;
1422 case 3:
1423 optype = "addr/cmd error";
1424 break;
1425 case 4:
1426 optype = "scrubbing error";
1427 break;
1428 default:
1429 optype = "reserved";
1430 break;
1431 }
1432
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001433 switch (errnum) {
1434 case 16:
1435 err = "read ECC error";
1436 break;
1437 case 17:
1438 err = "RAS ECC error";
1439 break;
1440 case 18:
1441 err = "write parity error";
1442 break;
1443 case 19:
1444 err = "redundacy loss";
1445 break;
1446 case 20:
1447 err = "reserved";
1448 break;
1449 case 21:
1450 err = "memory range error";
1451 break;
1452 case 22:
1453 err = "RTID out of range";
1454 break;
1455 case 23:
1456 err = "address parity error";
1457 break;
1458 case 24:
1459 err = "byte enable parity error";
1460 break;
1461 default:
1462 err = "unknown";
1463 }
1464
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001465 /* FIXME: should convert addr into bank and rank information */
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001466 msg = kasprintf(GFP_ATOMIC,
Mauro Carvalho Chehab3a7dde72009-07-18 12:20:04 -03001467 "%s (addr = 0x%08llx, socket=%d, Dimm=%d, Channel=%d, "
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001468 "syndrome=0x%08x, count=%d, Err=%08llx:%08llx (%s: %s))\n",
Mauro Carvalho Chehab3a7dde72009-07-18 12:20:04 -03001469 type, (long long) m->addr, m->cpu, dimm, channel,
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001470 syndrome, core_err_cnt, (long long)m->status,
1471 (long long)m->misc, optype, err);
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001472
1473 debugf0("%s", msg);
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001474
1475 /* Call the helper to output message */
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001476 edac_mc_handle_fbd_ue(mci, 0 /* FIXME: should be rank here */,
1477 0, 0 /* FIXME: should be channel here */, msg);
1478
1479 kfree(msg);
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001480}
1481
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001482/*
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001483 * i7core_check_error Retrieve and process errors reported by the
1484 * hardware. Called by the Core module.
1485 */
1486static void i7core_check_error(struct mem_ctl_info *mci)
1487{
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001488 struct i7core_pvt *pvt = mci->pvt_info;
1489 int i;
1490 unsigned count = 0;
1491 struct mce *m = NULL;
1492 unsigned long flags;
1493
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001494 /* Copy all mce errors into a temporary buffer */
1495 spin_lock_irqsave(&pvt->mce_lock, flags);
1496 if (pvt->mce_count) {
1497 m = kmalloc(sizeof(*m) * pvt->mce_count, GFP_ATOMIC);
1498 if (m) {
1499 count = pvt->mce_count;
1500 memcpy(m, &pvt->mce_entry, sizeof(*m) * count);
1501 }
1502 pvt->mce_count = 0;
1503 }
1504 spin_unlock_irqrestore(&pvt->mce_lock, flags);
1505
1506 /* proccess mcelog errors */
1507 for (i = 0; i < count; i++)
1508 i7core_mce_output_error(mci, &m[i]);
1509
1510 kfree(m);
1511
1512 /* check memory count errors */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001513 for (i = 0; i < pvt->sockets; i++)
1514 check_mc_test_err(mci, i);
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001515}
1516
1517/*
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001518 * i7core_mce_check_error Replicates mcelog routine to get errors
1519 * This routine simply queues mcelog errors, and
1520 * return. The error itself should be handled later
1521 * by i7core_check_error.
1522 */
1523static int i7core_mce_check_error(void *priv, struct mce *mce)
1524{
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001525 struct mem_ctl_info *mci = priv;
1526 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001527 unsigned long flags;
1528
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001529 /*
1530 * Just let mcelog handle it if the error is
1531 * outside the memory controller
1532 */
1533 if (((mce->status & 0xffff) >> 7) != 1)
1534 return 0;
1535
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001536 /* Bank 8 registers are the only ones that we know how to handle */
1537 if (mce->bank != 8)
1538 return 0;
1539
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001540 spin_lock_irqsave(&pvt->mce_lock, flags);
1541 if (pvt->mce_count < MCE_LOG_LEN) {
1542 memcpy(&pvt->mce_entry[pvt->mce_count], mce, sizeof(*mce));
1543 pvt->mce_count++;
1544 }
1545 spin_unlock_irqrestore(&pvt->mce_lock, flags);
1546
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001547 /* Handle fatal errors immediately */
1548 if (mce->mcgstatus & 1)
1549 i7core_check_error(mci);
1550
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001551 /* Advice mcelog that the error were handled */
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001552 return 1;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001553}
1554
1555/*
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001556 * i7core_probe Probe for ONE instance of device to see if it is
1557 * present.
1558 * return:
1559 * 0 for FOUND a device
1560 * < 0 for error code
1561 */
1562static int __devinit i7core_probe(struct pci_dev *pdev,
1563 const struct pci_device_id *id)
1564{
1565 struct mem_ctl_info *mci;
1566 struct i7core_pvt *pvt;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001567 int num_channels = 0;
1568 int num_csrows = 0;
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -03001569 int csrow = 0;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001570 int dev_idx = id->driver_data;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001571 int rc, i;
1572 u8 sockets;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001573
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001574 if (unlikely(dev_idx >= ARRAY_SIZE(i7core_devs)))
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001575 return -EINVAL;
1576
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001577 /* get the pci devices we want to reserve for our use */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001578 rc = i7core_get_devices();
1579 if (unlikely(rc < 0))
1580 return rc;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001581
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001582 sockets = 1;
1583 for (i = NUM_SOCKETS - 1; i > 0; i--)
1584 if (pci_devs[0].pdev[i]) {
1585 sockets = i + 1;
1586 break;
1587 }
1588
1589 for (i = 0; i < sockets; i++) {
1590 int channels;
1591 int csrows;
1592
1593 /* Check the number of active and not disabled channels */
1594 rc = i7core_get_active_channels(i, &channels, &csrows);
1595 if (unlikely(rc < 0))
1596 goto fail0;
1597
1598 num_channels += channels;
1599 num_csrows += csrows;
1600 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001601
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001602 /* allocate a new MC control structure */
1603 mci = edac_mc_alloc(sizeof(*pvt), num_csrows, num_channels, 0);
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001604 if (unlikely(!mci)) {
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001605 rc = -ENOMEM;
1606 goto fail0;
1607 }
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001608
1609 debugf0("MC: " __FILE__ ": %s(): mci = %p\n", __func__, mci);
1610
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001611 mci->dev = &pdev->dev; /* record ptr to the generic device */
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001612 pvt = mci->pvt_info;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001613 memset(pvt, 0, sizeof(*pvt));
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001614 pvt->sockets = sockets;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001615 mci->mc_idx = 0;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001616
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001617 /*
1618 * FIXME: how to handle RDDR3 at MCI level? It is possible to have
1619 * Mixed RDDR3/UDDR3 with Nehalem, provided that they are on different
1620 * memory channels
1621 */
1622 mci->mtype_cap = MEM_FLAG_DDR3;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001623 mci->edac_ctl_cap = EDAC_FLAG_NONE;
1624 mci->edac_cap = EDAC_FLAG_NONE;
1625 mci->mod_name = "i7core_edac.c";
1626 mci->mod_ver = I7CORE_REVISION;
1627 mci->ctl_name = i7core_devs[dev_idx].ctl_name;
1628 mci->dev_name = pci_name(pdev);
1629 mci->ctl_page_to_phys = NULL;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001630 mci->mc_driver_sysfs_attributes = i7core_inj_attrs;
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001631 /* Set the function pointer to an actual operation function */
1632 mci->edac_check = i7core_check_error;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001633
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001634 /* Store pci devices at mci for faster access */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001635 rc = mci_bind_devs(mci);
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001636 if (unlikely(rc < 0))
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001637 goto fail1;
1638
1639 /* Get dimm basic config */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001640 for (i = 0; i < sockets; i++)
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -03001641 get_dimm_config(mci, &csrow, i);
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001642
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001643 /* add this new MC control structure to EDAC's list of MCs */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001644 if (unlikely(edac_mc_add_mc(mci))) {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001645 debugf0("MC: " __FILE__
1646 ": %s(): failed edac_mc_add_mc()\n", __func__);
1647 /* FIXME: perhaps some code should go here that disables error
1648 * reporting if we just enabled it
1649 */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001650
1651 rc = -EINVAL;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001652 goto fail1;
1653 }
1654
1655 /* allocating generic PCI control info */
1656 i7core_pci = edac_pci_create_generic_ctl(&pdev->dev, EDAC_MOD_STR);
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001657 if (unlikely(!i7core_pci)) {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001658 printk(KERN_WARNING
1659 "%s(): Unable to create PCI control\n",
1660 __func__);
1661 printk(KERN_WARNING
1662 "%s(): PCI error report via EDAC not setup\n",
1663 __func__);
1664 }
1665
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001666 /* Default error mask is any memory */
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001667 pvt->inject.channel = 0;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001668 pvt->inject.dimm = -1;
1669 pvt->inject.rank = -1;
1670 pvt->inject.bank = -1;
1671 pvt->inject.page = -1;
1672 pvt->inject.col = -1;
1673
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001674 /* Registers on edac_mce in order to receive memory errors */
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001675 pvt->edac_mce.priv = mci;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001676 pvt->edac_mce.check_error = i7core_mce_check_error;
1677 spin_lock_init(&pvt->mce_lock);
1678
1679 rc = edac_mce_register(&pvt->edac_mce);
1680 if (unlikely (rc < 0)) {
1681 debugf0("MC: " __FILE__
1682 ": %s(): failed edac_mce_register()\n", __func__);
1683 goto fail1;
1684 }
1685
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001686 i7core_printk(KERN_INFO, "Driver loaded.\n");
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001687
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001688 return 0;
1689
1690fail1:
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001691 edac_mc_free(mci);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001692
1693fail0:
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001694 i7core_put_devices();
1695 return rc;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001696}
1697
1698/*
1699 * i7core_remove destructor for one instance of device
1700 *
1701 */
1702static void __devexit i7core_remove(struct pci_dev *pdev)
1703{
1704 struct mem_ctl_info *mci;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001705 struct i7core_pvt *pvt;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001706
1707 debugf0(__FILE__ ": %s()\n", __func__);
1708
1709 if (i7core_pci)
1710 edac_pci_release_generic_ctl(i7core_pci);
1711
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001712
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001713 mci = edac_mc_del_mc(&pdev->dev);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001714 if (!mci)
1715 return;
1716
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001717 /* Unregisters on edac_mce in order to receive memory errors */
1718 pvt = mci->pvt_info;
1719 edac_mce_unregister(&pvt->edac_mce);
1720
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001721 /* retrieve references to resources, and free those resources */
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001722 i7core_put_devices();
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001723
1724 edac_mc_free(mci);
1725}
1726
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001727MODULE_DEVICE_TABLE(pci, i7core_pci_tbl);
1728
1729/*
1730 * i7core_driver pci_driver structure for this module
1731 *
1732 */
1733static struct pci_driver i7core_driver = {
1734 .name = "i7core_edac",
1735 .probe = i7core_probe,
1736 .remove = __devexit_p(i7core_remove),
1737 .id_table = i7core_pci_tbl,
1738};
1739
1740/*
1741 * i7core_init Module entry function
1742 * Try to initialize this module for its devices
1743 */
1744static int __init i7core_init(void)
1745{
1746 int pci_rc;
1747
1748 debugf2("MC: " __FILE__ ": %s()\n", __func__);
1749
1750 /* Ensure that the OPSTATE is set correctly for POLL or NMI */
1751 opstate_init();
1752
1753 pci_rc = pci_register_driver(&i7core_driver);
1754
1755 return (pci_rc < 0) ? pci_rc : 0;
1756}
1757
1758/*
1759 * i7core_exit() Module exit function
1760 * Unregister the driver
1761 */
1762static void __exit i7core_exit(void)
1763{
1764 debugf2("MC: " __FILE__ ": %s()\n", __func__);
1765 pci_unregister_driver(&i7core_driver);
1766}
1767
1768module_init(i7core_init);
1769module_exit(i7core_exit);
1770
1771MODULE_LICENSE("GPL");
1772MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
1773MODULE_AUTHOR("Red Hat Inc. (http://www.redhat.com)");
1774MODULE_DESCRIPTION("MC Driver for Intel i7 Core memory controllers - "
1775 I7CORE_REVISION);
1776
1777module_param(edac_op_state, int, 0444);
1778MODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI");