blob: bf0374062e3d5346d6aff941d0718116ae643a86 [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
456 debugf0("MC control=0x%08x status=0x%08x dod=0x%08x map=0x%08x\n",
457 pvt->info.mc_control, pvt->info.mc_status,
458 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 Chehab854d3342009-06-22 22:48:30 -0300472 debugf0("DOD Max limits: DIMMS: %d, %d-ranked, %d-banked\n",
473 numdimms(pvt->info.max_dod),
474 numrank(pvt->info.max_dod >> 2),
475 numbank(pvt->info.max_dod >> 4));
476 debugf0("DOD Max rows x colums = 0x%x x 0x%x\n",
477 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 debugf0("Memory channel configuration:\n");
481
482 for (i = 0; i < NUM_CHANS; i++) {
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300483 u32 data, dimm_dod[3], value[8];
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300484
485 if (!CH_ACTIVE(pvt, i)) {
486 debugf0("Channel %i is not active\n", i);
487 continue;
488 }
489 if (CH_DISABLED(pvt, i)) {
490 debugf0("Channel %i is disabled\n", i);
491 continue;
492 }
493
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300494 /* Devices 4-6 function 0 */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300495 pci_read_config_dword(pvt->pci_ch[socket][i][0],
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300496 MC_CHANNEL_DIMM_INIT_PARAMS, &data);
497
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300498 pvt->channel[socket][i].ranks = (data & QUAD_RANK_PRESENT) ?
499 4 : 2;
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300500
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300501 if (data & REGISTERED_DIMM)
502 mtype = MEM_RDDR3;
503 else
504 mtype = MEM_DDR3;
505#if 0
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300506 if (data & THREE_DIMMS_PRESENT)
507 pvt->channel[i].dimms = 3;
508 else if (data & SINGLE_QUAD_RANK_PRESENT)
509 pvt->channel[i].dimms = 1;
510 else
511 pvt->channel[i].dimms = 2;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300512#endif
513
514 /* Devices 4-6 function 1 */
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_DIMM0, &dimm_dod[0]);
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_DIMM1, &dimm_dod[1]);
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300519 pci_read_config_dword(pvt->pci_ch[socket][i][1],
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300520 MC_DOD_CH_DIMM2, &dimm_dod[2]);
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300521
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300522 debugf0("Ch%d phy rd%d, wr%d (0x%08x): "
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300523 "%d ranks, %cDIMMs\n",
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300524 i,
525 RDLCH(pvt->info.ch_map, i), WRLCH(pvt->info.ch_map, i),
526 data,
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300527 pvt->channel[socket][i].ranks,
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300528 (data & REGISTERED_DIMM) ? 'R' : 'U');
Mauro Carvalho Chehab7dd69532009-06-22 22:48:30 -0300529
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300530 for (j = 0; j < 3; j++) {
531 u32 banks, ranks, rows, cols;
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300532 u32 size, npages;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300533
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300534 if (!DIMM_PRESENT(dimm_dod[j]))
535 continue;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300536
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300537 banks = numbank(MC_DOD_NUMBANK(dimm_dod[j]));
538 ranks = numrank(MC_DOD_NUMRANK(dimm_dod[j]));
539 rows = numrow(MC_DOD_NUMROW(dimm_dod[j]));
540 cols = numcol(MC_DOD_NUMCOL(dimm_dod[j]));
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300541
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300542 /* DDR3 has 8 I/O banks */
543 size = (rows * cols * banks * ranks) >> (20 - 3);
544
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300545 pvt->channel[socket][i].dimms++;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300546
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300547 debugf0("\tdimm %d (0x%08x) %d Mb offset: %x, "
548 "numbank: %d,\n\t\t"
549 "numrank: %d, numrow: %#x, numcol: %#x\n",
550 j, dimm_dod[j], size,
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300551 RANKOFFSET(dimm_dod[j]),
552 banks, ranks, rows, cols);
553
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300554#if PAGE_SHIFT > 20
555 npages = size >> (PAGE_SHIFT - 20);
556#else
557 npages = size << (20 - PAGE_SHIFT);
558#endif
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300559
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -0300560 csr = &mci->csrows[*csrow];
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300561 csr->first_page = last_page + 1;
562 last_page += npages;
563 csr->last_page = last_page;
564 csr->nr_pages = npages;
565
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300566 csr->page_mask = 0;
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300567 csr->grain = 8;
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -0300568 csr->csrow_idx = *csrow;
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300569 csr->nr_channels = 1;
570
571 csr->channels[0].chan_idx = i;
572 csr->channels[0].ce_count = 0;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300573
574 switch (banks) {
575 case 4:
576 csr->dtype = DEV_X4;
577 break;
578 case 8:
579 csr->dtype = DEV_X8;
580 break;
581 case 16:
582 csr->dtype = DEV_X16;
583 break;
584 default:
585 csr->dtype = DEV_UNKNOWN;
586 }
587
588 csr->edac_mode = mode;
589 csr->mtype = mtype;
590
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -0300591 (*csrow)++;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300592 }
593
594 pci_read_config_dword(pdev, MC_SAG_CH_0, &value[0]);
595 pci_read_config_dword(pdev, MC_SAG_CH_1, &value[1]);
596 pci_read_config_dword(pdev, MC_SAG_CH_2, &value[2]);
597 pci_read_config_dword(pdev, MC_SAG_CH_3, &value[3]);
598 pci_read_config_dword(pdev, MC_SAG_CH_4, &value[4]);
599 pci_read_config_dword(pdev, MC_SAG_CH_5, &value[5]);
600 pci_read_config_dword(pdev, MC_SAG_CH_6, &value[6]);
601 pci_read_config_dword(pdev, MC_SAG_CH_7, &value[7]);
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300602 debugf0("\t[%i] DIVBY3\tREMOVED\tOFFSET\n", i);
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300603 for (j = 0; j < 8; j++)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300604 debugf0("\t\t%#x\t%#x\t%#x\n",
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300605 (value[j] >> 27) & 0x1,
606 (value[j] >> 24) & 0x7,
607 (value[j] && ((1 << 24) - 1)));
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300608 }
609
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300610 return 0;
611}
612
613/****************************************************************************
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300614 Error insertion routines
615 ****************************************************************************/
616
617/* The i7core has independent error injection features per channel.
618 However, to have a simpler code, we don't allow enabling error injection
619 on more than one channel.
620 Also, since a change at an inject parameter will be applied only at enable,
621 we're disabling error injection on all write calls to the sysfs nodes that
622 controls the error code injection.
623 */
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300624static int disable_inject(struct mem_ctl_info *mci)
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300625{
626 struct i7core_pvt *pvt = mci->pvt_info;
627
628 pvt->inject.enable = 0;
629
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300630 if (!pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0])
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300631 return -ENODEV;
632
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300633 pci_write_config_dword(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0],
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300634 MC_CHANNEL_ERROR_MASK, 0);
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300635
636 return 0;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300637}
638
639/*
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300640 * i7core inject inject.socket
641 *
642 * accept and store error injection inject.socket value
643 */
644static ssize_t i7core_inject_socket_store(struct mem_ctl_info *mci,
645 const char *data, size_t count)
646{
647 struct i7core_pvt *pvt = mci->pvt_info;
648 unsigned long value;
649 int rc;
650
651 rc = strict_strtoul(data, 10, &value);
652 if ((rc < 0) || (value > pvt->sockets))
653 return 0;
654
655 pvt->inject.section = (u32) value;
656 return count;
657}
658
659static ssize_t i7core_inject_socket_show(struct mem_ctl_info *mci,
660 char *data)
661{
662 struct i7core_pvt *pvt = mci->pvt_info;
663 return sprintf(data, "%d\n", pvt->inject.socket);
664}
665
666/*
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300667 * i7core inject inject.section
668 *
669 * accept and store error injection inject.section value
670 * bit 0 - refers to the lower 32-byte half cacheline
671 * bit 1 - refers to the upper 32-byte half cacheline
672 */
673static ssize_t i7core_inject_section_store(struct mem_ctl_info *mci,
674 const char *data, size_t count)
675{
676 struct i7core_pvt *pvt = mci->pvt_info;
677 unsigned long value;
678 int rc;
679
680 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300681 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300682
683 rc = strict_strtoul(data, 10, &value);
684 if ((rc < 0) || (value > 3))
685 return 0;
686
687 pvt->inject.section = (u32) value;
688 return count;
689}
690
691static ssize_t i7core_inject_section_show(struct mem_ctl_info *mci,
692 char *data)
693{
694 struct i7core_pvt *pvt = mci->pvt_info;
695 return sprintf(data, "0x%08x\n", pvt->inject.section);
696}
697
698/*
699 * i7core inject.type
700 *
701 * accept and store error injection inject.section value
702 * bit 0 - repeat enable - Enable error repetition
703 * bit 1 - inject ECC error
704 * bit 2 - inject parity error
705 */
706static ssize_t i7core_inject_type_store(struct mem_ctl_info *mci,
707 const char *data, size_t count)
708{
709 struct i7core_pvt *pvt = mci->pvt_info;
710 unsigned long value;
711 int rc;
712
713 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300714 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300715
716 rc = strict_strtoul(data, 10, &value);
717 if ((rc < 0) || (value > 7))
718 return 0;
719
720 pvt->inject.type = (u32) value;
721 return count;
722}
723
724static ssize_t i7core_inject_type_show(struct mem_ctl_info *mci,
725 char *data)
726{
727 struct i7core_pvt *pvt = mci->pvt_info;
728 return sprintf(data, "0x%08x\n", pvt->inject.type);
729}
730
731/*
732 * i7core_inject_inject.eccmask_store
733 *
734 * The type of error (UE/CE) will depend on the inject.eccmask value:
735 * Any bits set to a 1 will flip the corresponding ECC bit
736 * Correctable errors can be injected by flipping 1 bit or the bits within
737 * a symbol pair (2 consecutive aligned 8-bit pairs - i.e. 7:0 and 15:8 or
738 * 23:16 and 31:24). Flipping bits in two symbol pairs will cause an
739 * uncorrectable error to be injected.
740 */
741static ssize_t i7core_inject_eccmask_store(struct mem_ctl_info *mci,
742 const char *data, size_t count)
743{
744 struct i7core_pvt *pvt = mci->pvt_info;
745 unsigned long value;
746 int rc;
747
748 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300749 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300750
751 rc = strict_strtoul(data, 10, &value);
752 if (rc < 0)
753 return 0;
754
755 pvt->inject.eccmask = (u32) value;
756 return count;
757}
758
759static ssize_t i7core_inject_eccmask_show(struct mem_ctl_info *mci,
760 char *data)
761{
762 struct i7core_pvt *pvt = mci->pvt_info;
763 return sprintf(data, "0x%08x\n", pvt->inject.eccmask);
764}
765
766/*
767 * i7core_addrmatch
768 *
769 * The type of error (UE/CE) will depend on the inject.eccmask value:
770 * Any bits set to a 1 will flip the corresponding ECC bit
771 * Correctable errors can be injected by flipping 1 bit or the bits within
772 * a symbol pair (2 consecutive aligned 8-bit pairs - i.e. 7:0 and 15:8 or
773 * 23:16 and 31:24). Flipping bits in two symbol pairs will cause an
774 * uncorrectable error to be injected.
775 */
776static ssize_t i7core_inject_addrmatch_store(struct mem_ctl_info *mci,
777 const char *data, size_t count)
778{
779 struct i7core_pvt *pvt = mci->pvt_info;
780 char *cmd, *val;
781 long value;
782 int rc;
783
784 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300785 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300786
787 do {
788 cmd = strsep((char **) &data, ":");
789 if (!cmd)
790 break;
791 val = strsep((char **) &data, " \n\t");
792 if (!val)
793 return cmd - data;
794
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300795 if (!strcasecmp(val, "any"))
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300796 value = -1;
797 else {
798 rc = strict_strtol(val, 10, &value);
799 if ((rc < 0) || (value < 0))
800 return cmd - data;
801 }
802
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300803 if (!strcasecmp(cmd, "channel")) {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300804 if (value < 3)
805 pvt->inject.channel = value;
806 else
807 return cmd - data;
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300808 } else if (!strcasecmp(cmd, "dimm")) {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300809 if (value < 4)
810 pvt->inject.dimm = value;
811 else
812 return cmd - data;
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300813 } else if (!strcasecmp(cmd, "rank")) {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300814 if (value < 4)
815 pvt->inject.rank = value;
816 else
817 return cmd - data;
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300818 } else if (!strcasecmp(cmd, "bank")) {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300819 if (value < 4)
820 pvt->inject.bank = value;
821 else
822 return cmd - data;
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300823 } else if (!strcasecmp(cmd, "page")) {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300824 if (value <= 0xffff)
825 pvt->inject.page = value;
826 else
827 return cmd - data;
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300828 } else if (!strcasecmp(cmd, "col") ||
829 !strcasecmp(cmd, "column")) {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300830 if (value <= 0x3fff)
831 pvt->inject.col = value;
832 else
833 return cmd - data;
834 }
835 } while (1);
836
837 return count;
838}
839
840static ssize_t i7core_inject_addrmatch_show(struct mem_ctl_info *mci,
841 char *data)
842{
843 struct i7core_pvt *pvt = mci->pvt_info;
844 char channel[4], dimm[4], bank[4], rank[4], page[7], col[7];
845
846 if (pvt->inject.channel < 0)
847 sprintf(channel, "any");
848 else
849 sprintf(channel, "%d", pvt->inject.channel);
850 if (pvt->inject.dimm < 0)
851 sprintf(dimm, "any");
852 else
853 sprintf(dimm, "%d", pvt->inject.dimm);
854 if (pvt->inject.bank < 0)
855 sprintf(bank, "any");
856 else
857 sprintf(bank, "%d", pvt->inject.bank);
858 if (pvt->inject.rank < 0)
859 sprintf(rank, "any");
860 else
861 sprintf(rank, "%d", pvt->inject.rank);
862 if (pvt->inject.page < 0)
863 sprintf(page, "any");
864 else
865 sprintf(page, "0x%04x", pvt->inject.page);
866 if (pvt->inject.col < 0)
867 sprintf(col, "any");
868 else
869 sprintf(col, "0x%04x", pvt->inject.col);
870
871 return sprintf(data, "channel: %s\ndimm: %s\nbank: %s\n"
872 "rank: %s\npage: %s\ncolumn: %s\n",
873 channel, dimm, bank, rank, page, col);
874}
875
876/*
877 * This routine prepares the Memory Controller for error injection.
878 * The error will be injected when some process tries to write to the
879 * memory that matches the given criteria.
880 * The criteria can be set in terms of a mask where dimm, rank, bank, page
881 * and col can be specified.
882 * A -1 value for any of the mask items will make the MCU to ignore
883 * that matching criteria for error injection.
884 *
885 * It should be noticed that the error will only happen after a write operation
886 * on a memory that matches the condition. if REPEAT_EN is not enabled at
887 * inject mask, then it will produce just one error. Otherwise, it will repeat
888 * until the injectmask would be cleaned.
889 *
890 * FIXME: This routine assumes that MAXNUMDIMMS value of MC_MAX_DOD
891 * is reliable enough to check if the MC is using the
892 * three channels. However, this is not clear at the datasheet.
893 */
894static ssize_t i7core_inject_enable_store(struct mem_ctl_info *mci,
895 const char *data, size_t count)
896{
897 struct i7core_pvt *pvt = mci->pvt_info;
898 u32 injectmask;
899 u64 mask = 0;
900 int rc;
901 long enable;
902
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300903 if (!pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0])
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300904 return 0;
905
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300906 rc = strict_strtoul(data, 10, &enable);
907 if ((rc < 0))
908 return 0;
909
910 if (enable) {
911 pvt->inject.enable = 1;
912 } else {
913 disable_inject(mci);
914 return count;
915 }
916
917 /* Sets pvt->inject.dimm mask */
918 if (pvt->inject.dimm < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300919 mask |= 1L << 41;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300920 else {
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300921 if (pvt->channel[pvt->inject.socket][pvt->inject.channel].dimms > 2)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300922 mask |= (pvt->inject.dimm & 0x3L) << 35;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300923 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300924 mask |= (pvt->inject.dimm & 0x1L) << 36;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300925 }
926
927 /* Sets pvt->inject.rank mask */
928 if (pvt->inject.rank < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300929 mask |= 1L << 40;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300930 else {
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300931 if (pvt->channel[pvt->inject.socket][pvt->inject.channel].dimms > 2)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300932 mask |= (pvt->inject.rank & 0x1L) << 34;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300933 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300934 mask |= (pvt->inject.rank & 0x3L) << 34;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300935 }
936
937 /* Sets pvt->inject.bank mask */
938 if (pvt->inject.bank < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300939 mask |= 1L << 39;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300940 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300941 mask |= (pvt->inject.bank & 0x15L) << 30;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300942
943 /* Sets pvt->inject.page mask */
944 if (pvt->inject.page < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300945 mask |= 1L << 38;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300946 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300947 mask |= (pvt->inject.page & 0xffffL) << 14;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300948
949 /* Sets pvt->inject.column mask */
950 if (pvt->inject.col < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300951 mask |= 1L << 37;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300952 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300953 mask |= (pvt->inject.col & 0x3fffL);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300954
Mauro Carvalho Chehabe9bd2e72009-07-09 22:14:35 -0300955 /* Unlock writes to registers */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300956 pci_write_config_dword(pvt->pci_noncore[pvt->inject.socket],
957 MC_CFG_CONTROL, 0x2);
Mauro Carvalho Chehabe9bd2e72009-07-09 22:14:35 -0300958 msleep(100);
959
960 /* Zeroes error count registers */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300961 pci_write_config_dword(pvt->pci_mcr[pvt->inject.socket][4],
962 MC_TEST_ERR_RCV1, 0);
963 pci_write_config_dword(pvt->pci_mcr[pvt->inject.socket][4],
964 MC_TEST_ERR_RCV0, 0);
965 pvt->ce_count_available[pvt->inject.socket] = 0;
Mauro Carvalho Chehabe9bd2e72009-07-09 22:14:35 -0300966
967
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300968#if USE_QWORD
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300969 pci_write_config_qword(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0],
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300970 MC_CHANNEL_ADDR_MATCH, mask);
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300971#else
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300972 pci_write_config_dword(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0],
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300973 MC_CHANNEL_ADDR_MATCH, mask);
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300974 pci_write_config_dword(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0],
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300975 MC_CHANNEL_ADDR_MATCH + 4, mask >> 32L);
976#endif
977
978#if 1
979#if USE_QWORD
980 u64 rdmask;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300981 pci_read_config_qword(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0],
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300982 MC_CHANNEL_ADDR_MATCH, &rdmask);
983 debugf0("Inject addr match write 0x%016llx, read: 0x%016llx\n",
984 mask, rdmask);
985#else
986 u32 rdmask1, rdmask2;
987
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300988 pci_read_config_dword(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0],
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300989 MC_CHANNEL_ADDR_MATCH, &rdmask1);
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300990 pci_read_config_dword(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0],
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300991 MC_CHANNEL_ADDR_MATCH + 4, &rdmask2);
992
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -0300993 debugf0("Inject addr match write 0x%016llx, read: 0x%08x 0x%08x\n",
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300994 mask, rdmask1, rdmask2);
995#endif
996#endif
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300997
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300998 pci_write_config_dword(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0],
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300999 MC_CHANNEL_ERROR_MASK, pvt->inject.eccmask);
1000
1001 /*
1002 * bit 0: REPEAT_EN
1003 * bits 1-2: MASK_HALF_CACHELINE
1004 * bit 3: INJECT_ECC
1005 * bit 4: INJECT_ADDR_PARITY
1006 */
1007
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -03001008 injectmask = (pvt->inject.type & 1) |
1009 (pvt->inject.section & 0x3) << 1 |
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001010 (pvt->inject.type & 0x6) << (3 - 1);
1011
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001012 pci_write_config_dword(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0],
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001013 MC_CHANNEL_ERROR_MASK, injectmask);
1014
Mauro Carvalho Chehabe9bd2e72009-07-09 22:14:35 -03001015#if 0
1016 /* lock writes to registers */
1017 pci_write_config_dword(pvt->pci_noncore, MC_CFG_CONTROL, 0);
1018#endif
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001019 debugf0("Error inject addr match 0x%016llx, ecc 0x%08x,"
1020 " inject 0x%08x\n",
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001021 mask, pvt->inject.eccmask, injectmask);
1022
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -03001023
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001024 return count;
1025}
1026
1027static ssize_t i7core_inject_enable_show(struct mem_ctl_info *mci,
1028 char *data)
1029{
1030 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -03001031 u32 injectmask;
1032
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001033 pci_read_config_dword(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0],
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -03001034 MC_CHANNEL_ERROR_MASK, &injectmask);
1035
1036 debugf0("Inject error read: 0x%018x\n", injectmask);
1037
1038 if (injectmask & 0x0c)
1039 pvt->inject.enable = 1;
1040
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001041 return sprintf(data, "%d\n", pvt->inject.enable);
1042}
1043
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001044static ssize_t i7core_ce_regs_show(struct mem_ctl_info *mci, char *data)
1045{
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001046 unsigned i, count, total = 0;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001047 struct i7core_pvt *pvt = mci->pvt_info;
1048
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001049 for (i = 0; i < pvt->sockets; i++) {
1050 if (!pvt->ce_count_available[i])
1051 count = sprintf(data, "socket 0 data unavailable\n");
1052 else
1053 count = sprintf(data, "socket %d, dimm0: %lu\n"
1054 "dimm1: %lu\ndimm2: %lu\n",
1055 i,
1056 pvt->ce_count[i][0],
1057 pvt->ce_count[i][1],
1058 pvt->ce_count[i][2]);
1059 data += count;
1060 total += count;
1061 }
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001062
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001063 return total;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001064}
1065
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001066/*
1067 * Sysfs struct
1068 */
1069static struct mcidev_sysfs_attribute i7core_inj_attrs[] = {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001070 {
1071 .attr = {
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001072 .name = "inject_socket",
1073 .mode = (S_IRUGO | S_IWUSR)
1074 },
1075 .show = i7core_inject_socket_show,
1076 .store = i7core_inject_socket_store,
1077 }, {
1078 .attr = {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001079 .name = "inject_section",
1080 .mode = (S_IRUGO | S_IWUSR)
1081 },
1082 .show = i7core_inject_section_show,
1083 .store = i7core_inject_section_store,
1084 }, {
1085 .attr = {
1086 .name = "inject_type",
1087 .mode = (S_IRUGO | S_IWUSR)
1088 },
1089 .show = i7core_inject_type_show,
1090 .store = i7core_inject_type_store,
1091 }, {
1092 .attr = {
1093 .name = "inject_eccmask",
1094 .mode = (S_IRUGO | S_IWUSR)
1095 },
1096 .show = i7core_inject_eccmask_show,
1097 .store = i7core_inject_eccmask_store,
1098 }, {
1099 .attr = {
1100 .name = "inject_addrmatch",
1101 .mode = (S_IRUGO | S_IWUSR)
1102 },
1103 .show = i7core_inject_addrmatch_show,
1104 .store = i7core_inject_addrmatch_store,
1105 }, {
1106 .attr = {
1107 .name = "inject_enable",
1108 .mode = (S_IRUGO | S_IWUSR)
1109 },
1110 .show = i7core_inject_enable_show,
1111 .store = i7core_inject_enable_store,
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001112 }, {
1113 .attr = {
1114 .name = "corrected_error_counts",
1115 .mode = (S_IRUGO | S_IWUSR)
1116 },
1117 .show = i7core_ce_regs_show,
1118 .store = NULL,
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001119 },
1120};
1121
1122/****************************************************************************
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001123 Device initialization routines: put/get, init/exit
1124 ****************************************************************************/
1125
1126/*
1127 * i7core_put_devices 'put' all the devices that we have
1128 * reserved via 'get'
1129 */
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001130static void i7core_put_devices(void)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001131{
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001132 int i, j;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001133
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001134 for (i = 0; i < NUM_SOCKETS; i++)
1135 for (j = 0; j < N_DEVS; j++)
1136 pci_dev_put(pci_devs[j].pdev[i]);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001137}
1138
1139/*
1140 * i7core_get_devices Find and perform 'get' operation on the MCH's
1141 * device/functions we want to reference for this driver
1142 *
1143 * Need to 'get' device 16 func 1 and func 2
1144 */
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001145int i7core_get_onedevice(struct pci_dev **prev, int devno)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001146{
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001147 struct pci_dev *pdev = NULL;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001148 u8 bus = 0;
1149 u8 socket = 0;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001150
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001151 pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
1152 pci_devs[devno].dev_id, *prev);
1153
1154 /*
1155 * On Xeon 55xx, the Intel Quckpath Arch Generic Non-core pci buses
1156 * aren't announced by acpi. So, we need to use a legacy scan probing
1157 * to detect them
1158 */
1159 if (unlikely(!pdev && !devno && !prev)) {
1160 pcibios_scan_specific_bus(254);
1161 pcibios_scan_specific_bus(255);
1162
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001163 pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001164 pci_devs[devno].dev_id, *prev);
1165 }
Mauro Carvalho Chehabd1fd4fb2009-07-10 18:39:53 -03001166
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001167 /*
1168 * On Xeon 55xx, the Intel Quckpath Arch Generic Non-core regs
1169 * is at addr 8086:2c40, instead of 8086:2c41. So, we need
1170 * to probe for the alternate address in case of failure
1171 */
1172 if (pci_devs[devno].dev_id == PCI_DEVICE_ID_INTEL_I7_NOCORE && !pdev)
1173 pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
1174 PCI_DEVICE_ID_INTEL_I7_NOCORE_ALT, *prev);
Mauro Carvalho Chehabd1fd4fb2009-07-10 18:39:53 -03001175
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001176 if (!pdev) {
1177 if (*prev) {
1178 *prev = pdev;
1179 return 0;
Mauro Carvalho Chehabd1fd4fb2009-07-10 18:39:53 -03001180 }
1181
Mauro Carvalho Chehab310cbb72009-07-17 00:09:10 -03001182 /*
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001183 * Dev 3 function 2 only exists on chips with RDIMMs
1184 * so, it is ok to not found it
Mauro Carvalho Chehab310cbb72009-07-17 00:09:10 -03001185 */
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001186 if ((pci_devs[devno].dev == 3) && (pci_devs[devno].func == 2)) {
1187 *prev = pdev;
1188 return 0;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001189 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001190
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001191 i7core_printk(KERN_ERR,
1192 "Device not found: dev %02x.%d PCI ID %04x:%04x\n",
1193 pci_devs[devno].dev, pci_devs[devno].func,
1194 PCI_VENDOR_ID_INTEL, pci_devs[devno].dev_id);
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001195
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001196 /* End of list, leave */
1197 return -ENODEV;
1198 }
1199 bus = pdev->bus->number;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001200
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001201 if (bus == 0x3f)
1202 socket = 0;
1203 else
1204 socket = 255 - bus;
1205
1206 if (socket >= NUM_SOCKETS) {
1207 i7core_printk(KERN_ERR,
1208 "Unexpected socket for "
1209 "dev %02x:%02x.%d PCI ID %04x:%04x\n",
1210 bus, pci_devs[devno].dev, pci_devs[devno].func,
1211 PCI_VENDOR_ID_INTEL, pci_devs[devno].dev_id);
1212 pci_dev_put(pdev);
1213 return -ENODEV;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001214 }
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001215
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001216 if (pci_devs[devno].pdev[socket]) {
1217 i7core_printk(KERN_ERR,
1218 "Duplicated device for "
1219 "dev %02x:%02x.%d PCI ID %04x:%04x\n",
1220 bus, pci_devs[devno].dev, pci_devs[devno].func,
1221 PCI_VENDOR_ID_INTEL, pci_devs[devno].dev_id);
1222 pci_dev_put(pdev);
1223 return -ENODEV;
1224 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001225
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001226 pci_devs[devno].pdev[socket] = pdev;
1227
1228 /* Sanity check */
1229 if (unlikely(PCI_SLOT(pdev->devfn) != pci_devs[devno].dev ||
1230 PCI_FUNC(pdev->devfn) != pci_devs[devno].func)) {
1231 i7core_printk(KERN_ERR,
1232 "Device PCI ID %04x:%04x "
1233 "has dev %02x:%02x.%d instead of dev %02x:%02x.%d\n",
1234 PCI_VENDOR_ID_INTEL, pci_devs[devno].dev_id,
1235 bus, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
1236 bus, pci_devs[devno].dev, pci_devs[devno].func);
1237 return -ENODEV;
1238 }
1239
1240 /* Be sure that the device is enabled */
1241 if (unlikely(pci_enable_device(pdev) < 0)) {
1242 i7core_printk(KERN_ERR,
1243 "Couldn't enable "
1244 "dev %02x:%02x.%d PCI ID %04x:%04x\n",
1245 bus, pci_devs[devno].dev, pci_devs[devno].func,
1246 PCI_VENDOR_ID_INTEL, pci_devs[devno].dev_id);
1247 return -ENODEV;
1248 }
1249
1250 i7core_printk(KERN_INFO,
1251 "Registered socket %d "
1252 "dev %02x:%02x.%d PCI ID %04x:%04x\n",
1253 socket, bus, pci_devs[devno].dev, pci_devs[devno].func,
1254 PCI_VENDOR_ID_INTEL, pci_devs[devno].dev_id);
1255
1256 *prev = pdev;
1257
1258 return 0;
1259}
1260
1261static int i7core_get_devices(void)
1262{
1263 int i;
1264 struct pci_dev *pdev = NULL;
1265
1266 for (i = 0; i < N_DEVS; i++) {
1267 pdev = NULL;
1268 do {
1269 if (i7core_get_onedevice(&pdev, i) < 0) {
1270 i7core_put_devices();
1271 return -ENODEV;
1272 }
1273 } while (pdev);
1274 }
1275 return 0;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001276}
1277
1278static int mci_bind_devs(struct mem_ctl_info *mci)
1279{
1280 struct i7core_pvt *pvt = mci->pvt_info;
1281 struct pci_dev *pdev;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001282 int i, j, func, slot;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001283
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001284 for (i = 0; i < pvt->sockets; i++) {
1285 for (j = 0; j < N_DEVS; j++) {
1286 pdev = pci_devs[j].pdev[i];
1287 if (!pdev)
1288 continue;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001289
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001290 func = PCI_FUNC(pdev->devfn);
1291 slot = PCI_SLOT(pdev->devfn);
1292 if (slot == 3) {
1293 if (unlikely(func > MAX_MCR_FUNC))
1294 goto error;
1295 pvt->pci_mcr[i][func] = pdev;
1296 } else if (likely(slot >= 4 && slot < 4 + NUM_CHANS)) {
1297 if (unlikely(func > MAX_CHAN_FUNC))
1298 goto error;
1299 pvt->pci_ch[i][slot - 4][func] = pdev;
1300 } else if (!slot && !func)
1301 pvt->pci_noncore[i] = pdev;
1302 else
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001303 goto error;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001304
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001305 debugf0("Associated fn %d.%d, dev = %p, socket %d\n",
1306 PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
1307 pdev, i);
1308 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001309 }
Mauro Carvalho Chehabe9bd2e72009-07-09 22:14:35 -03001310
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001311 return 0;
1312
1313error:
1314 i7core_printk(KERN_ERR, "Device %d, function %d "
1315 "is out of the expected range\n",
1316 slot, func);
1317 return -EINVAL;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001318}
1319
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001320/****************************************************************************
1321 Error check routines
1322 ****************************************************************************/
1323
1324/* This function is based on the device 3 function 4 registers as described on:
1325 * Intel Xeon Processor 5500 Series Datasheet Volume 2
1326 * http://www.intel.com/Assets/PDF/datasheet/321322.pdf
1327 * also available at:
1328 * http://www.arrownac.com/manufacturers/intel/s/nehalem/5500-datasheet-v2.pdf
1329 */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001330static void check_mc_test_err(struct mem_ctl_info *mci, u8 socket)
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001331{
1332 struct i7core_pvt *pvt = mci->pvt_info;
1333 u32 rcv1, rcv0;
1334 int new0, new1, new2;
1335
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001336 if (!pvt->pci_mcr[socket][4]) {
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001337 debugf0("%s MCR registers not found\n",__func__);
1338 return;
1339 }
1340
1341 /* Corrected error reads */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001342 pci_read_config_dword(pvt->pci_mcr[socket][4], MC_TEST_ERR_RCV1, &rcv1);
1343 pci_read_config_dword(pvt->pci_mcr[socket][4], MC_TEST_ERR_RCV0, &rcv0);
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001344
1345 /* Store the new values */
1346 new2 = DIMM2_COR_ERR(rcv1);
1347 new1 = DIMM1_COR_ERR(rcv0);
1348 new0 = DIMM0_COR_ERR(rcv0);
1349
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001350#if 0
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001351 debugf2("%s CE rcv1=0x%08x rcv0=0x%08x, %d %d %d\n",
1352 (pvt->ce_count_available ? "UPDATE" : "READ"),
1353 rcv1, rcv0, new0, new1, new2);
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001354#endif
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001355
1356 /* Updates CE counters if it is not the first time here */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001357 if (pvt->ce_count_available[socket]) {
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001358 /* Updates CE counters */
1359 int add0, add1, add2;
1360
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001361 add2 = new2 - pvt->last_ce_count[socket][2];
1362 add1 = new1 - pvt->last_ce_count[socket][1];
1363 add0 = new0 - pvt->last_ce_count[socket][0];
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001364
1365 if (add2 < 0)
1366 add2 += 0x7fff;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001367 pvt->ce_count[socket][2] += add2;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001368
1369 if (add1 < 0)
1370 add1 += 0x7fff;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001371 pvt->ce_count[socket][1] += add1;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001372
1373 if (add0 < 0)
1374 add0 += 0x7fff;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001375 pvt->ce_count[socket][0] += add0;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001376 } else
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001377 pvt->ce_count_available[socket] = 1;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001378
1379 /* Store the new values */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001380 pvt->last_ce_count[socket][2] = new2;
1381 pvt->last_ce_count[socket][1] = new1;
1382 pvt->last_ce_count[socket][0] = new0;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001383}
1384
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001385/*
1386 * According with tables E-11 and E-12 of chapter E.3.3 of Intel 64 and IA-32
1387 * Architectures Software Developer’s Manual Volume 3B.
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001388 * Nehalem are defined as family 0x06, model 0x1a
1389 *
1390 * The MCA registers used here are the following ones:
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001391 * struct mce field MCA Register
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001392 * m->status MSR_IA32_MC8_STATUS
1393 * m->addr MSR_IA32_MC8_ADDR
1394 * m->misc MSR_IA32_MC8_MISC
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001395 * In the case of Nehalem, the error information is masked at .status and .misc
1396 * fields
1397 */
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001398static void i7core_mce_output_error(struct mem_ctl_info *mci,
1399 struct mce *m)
1400{
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001401 char *type, *optype, *err, *msg;
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001402 unsigned long error = m->status & 0x1ff0000l;
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001403 u32 optypenum = (m->status >> 4) & 0x07;
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001404 u32 core_err_cnt = (m->status >> 38) && 0x7fff;
1405 u32 dimm = (m->misc >> 16) & 0x3;
1406 u32 channel = (m->misc >> 18) & 0x3;
1407 u32 syndrome = m->misc >> 32;
1408 u32 errnum = find_first_bit(&error, 32);
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001409
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001410 if (m->mcgstatus & 1)
1411 type = "FATAL";
1412 else
1413 type = "NON_FATAL";
1414
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001415 switch (optypenum) {
1416 case 0:
1417 optype = "generic undef request";
1418 break;
1419 case 1:
1420 optype = "read error";
1421 break;
1422 case 2:
1423 optype = "write error";
1424 break;
1425 case 3:
1426 optype = "addr/cmd error";
1427 break;
1428 case 4:
1429 optype = "scrubbing error";
1430 break;
1431 default:
1432 optype = "reserved";
1433 break;
1434 }
1435
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001436 switch (errnum) {
1437 case 16:
1438 err = "read ECC error";
1439 break;
1440 case 17:
1441 err = "RAS ECC error";
1442 break;
1443 case 18:
1444 err = "write parity error";
1445 break;
1446 case 19:
1447 err = "redundacy loss";
1448 break;
1449 case 20:
1450 err = "reserved";
1451 break;
1452 case 21:
1453 err = "memory range error";
1454 break;
1455 case 22:
1456 err = "RTID out of range";
1457 break;
1458 case 23:
1459 err = "address parity error";
1460 break;
1461 case 24:
1462 err = "byte enable parity error";
1463 break;
1464 default:
1465 err = "unknown";
1466 }
1467
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001468 /* FIXME: should convert addr into bank and rank information */
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001469 msg = kasprintf(GFP_ATOMIC,
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001470 "%s (addr = 0x%08llx, Dimm=%d, Channel=%d, "
1471 "syndrome=0x%08x, count=%d, Err=%08llx:%08llx (%s: %s))\n",
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001472 type, (long long) m->addr, dimm, channel,
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001473 syndrome, core_err_cnt, (long long)m->status,
1474 (long long)m->misc, optype, err);
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001475
1476 debugf0("%s", msg);
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001477
1478 /* Call the helper to output message */
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001479 edac_mc_handle_fbd_ue(mci, 0 /* FIXME: should be rank here */,
1480 0, 0 /* FIXME: should be channel here */, msg);
1481
1482 kfree(msg);
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001483}
1484
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001485/*
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001486 * i7core_check_error Retrieve and process errors reported by the
1487 * hardware. Called by the Core module.
1488 */
1489static void i7core_check_error(struct mem_ctl_info *mci)
1490{
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001491 struct i7core_pvt *pvt = mci->pvt_info;
1492 int i;
1493 unsigned count = 0;
1494 struct mce *m = NULL;
1495 unsigned long flags;
1496
1497 debugf0(__FILE__ ": %s()\n", __func__);
1498
1499 /* Copy all mce errors into a temporary buffer */
1500 spin_lock_irqsave(&pvt->mce_lock, flags);
1501 if (pvt->mce_count) {
1502 m = kmalloc(sizeof(*m) * pvt->mce_count, GFP_ATOMIC);
1503 if (m) {
1504 count = pvt->mce_count;
1505 memcpy(m, &pvt->mce_entry, sizeof(*m) * count);
1506 }
1507 pvt->mce_count = 0;
1508 }
1509 spin_unlock_irqrestore(&pvt->mce_lock, flags);
1510
1511 /* proccess mcelog errors */
1512 for (i = 0; i < count; i++)
1513 i7core_mce_output_error(mci, &m[i]);
1514
1515 kfree(m);
1516
1517 /* check memory count errors */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001518 for (i = 0; i < pvt->sockets; i++)
1519 check_mc_test_err(mci, i);
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001520}
1521
1522/*
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001523 * i7core_mce_check_error Replicates mcelog routine to get errors
1524 * This routine simply queues mcelog errors, and
1525 * return. The error itself should be handled later
1526 * by i7core_check_error.
1527 */
1528static int i7core_mce_check_error(void *priv, struct mce *mce)
1529{
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001530 struct mem_ctl_info *mci = priv;
1531 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001532 unsigned long flags;
1533
1534 debugf0(__FILE__ ": %s()\n", __func__);
1535
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001536 /*
1537 * Just let mcelog handle it if the error is
1538 * outside the memory controller
1539 */
1540 if (((mce->status & 0xffff) >> 7) != 1)
1541 return 0;
1542
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001543 /* Bank 8 registers are the only ones that we know how to handle */
1544 if (mce->bank != 8)
1545 return 0;
1546
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001547 spin_lock_irqsave(&pvt->mce_lock, flags);
1548 if (pvt->mce_count < MCE_LOG_LEN) {
1549 memcpy(&pvt->mce_entry[pvt->mce_count], mce, sizeof(*mce));
1550 pvt->mce_count++;
1551 }
1552 spin_unlock_irqrestore(&pvt->mce_lock, flags);
1553
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001554 /* Handle fatal errors immediately */
1555 if (mce->mcgstatus & 1)
1556 i7core_check_error(mci);
1557
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001558 /* Advice mcelog that the error were handled */
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001559 return 1;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001560}
1561
1562/*
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001563 * i7core_probe Probe for ONE instance of device to see if it is
1564 * present.
1565 * return:
1566 * 0 for FOUND a device
1567 * < 0 for error code
1568 */
1569static int __devinit i7core_probe(struct pci_dev *pdev,
1570 const struct pci_device_id *id)
1571{
1572 struct mem_ctl_info *mci;
1573 struct i7core_pvt *pvt;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001574 int num_channels = 0;
1575 int num_csrows = 0;
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -03001576 int csrow = 0;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001577 int dev_idx = id->driver_data;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001578 int rc, i;
1579 u8 sockets;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001580
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001581 if (unlikely(dev_idx >= ARRAY_SIZE(i7core_devs)))
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001582 return -EINVAL;
1583
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001584 /* get the pci devices we want to reserve for our use */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001585 rc = i7core_get_devices();
1586 if (unlikely(rc < 0))
1587 return rc;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001588
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001589 sockets = 1;
1590 for (i = NUM_SOCKETS - 1; i > 0; i--)
1591 if (pci_devs[0].pdev[i]) {
1592 sockets = i + 1;
1593 break;
1594 }
1595
1596 for (i = 0; i < sockets; i++) {
1597 int channels;
1598 int csrows;
1599
1600 /* Check the number of active and not disabled channels */
1601 rc = i7core_get_active_channels(i, &channels, &csrows);
1602 if (unlikely(rc < 0))
1603 goto fail0;
1604
1605 num_channels += channels;
1606 num_csrows += csrows;
1607 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001608
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001609 /* allocate a new MC control structure */
1610 mci = edac_mc_alloc(sizeof(*pvt), num_csrows, num_channels, 0);
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001611 if (unlikely(!mci)) {
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001612 rc = -ENOMEM;
1613 goto fail0;
1614 }
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001615
1616 debugf0("MC: " __FILE__ ": %s(): mci = %p\n", __func__, mci);
1617
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001618 mci->dev = &pdev->dev; /* record ptr to the generic device */
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001619 pvt = mci->pvt_info;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001620 memset(pvt, 0, sizeof(*pvt));
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001621 pvt->sockets = sockets;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001622 mci->mc_idx = 0;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001623
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001624 /*
1625 * FIXME: how to handle RDDR3 at MCI level? It is possible to have
1626 * Mixed RDDR3/UDDR3 with Nehalem, provided that they are on different
1627 * memory channels
1628 */
1629 mci->mtype_cap = MEM_FLAG_DDR3;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001630 mci->edac_ctl_cap = EDAC_FLAG_NONE;
1631 mci->edac_cap = EDAC_FLAG_NONE;
1632 mci->mod_name = "i7core_edac.c";
1633 mci->mod_ver = I7CORE_REVISION;
1634 mci->ctl_name = i7core_devs[dev_idx].ctl_name;
1635 mci->dev_name = pci_name(pdev);
1636 mci->ctl_page_to_phys = NULL;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001637 mci->mc_driver_sysfs_attributes = i7core_inj_attrs;
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001638 /* Set the function pointer to an actual operation function */
1639 mci->edac_check = i7core_check_error;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001640
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001641 /* Store pci devices at mci for faster access */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001642 rc = mci_bind_devs(mci);
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001643 if (unlikely(rc < 0))
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001644 goto fail1;
1645
1646 /* Get dimm basic config */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001647 for (i = 0; i < sockets; i++)
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -03001648 get_dimm_config(mci, &csrow, i);
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001649
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001650 /* add this new MC control structure to EDAC's list of MCs */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001651 if (unlikely(edac_mc_add_mc(mci))) {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001652 debugf0("MC: " __FILE__
1653 ": %s(): failed edac_mc_add_mc()\n", __func__);
1654 /* FIXME: perhaps some code should go here that disables error
1655 * reporting if we just enabled it
1656 */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001657
1658 rc = -EINVAL;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001659 goto fail1;
1660 }
1661
1662 /* allocating generic PCI control info */
1663 i7core_pci = edac_pci_create_generic_ctl(&pdev->dev, EDAC_MOD_STR);
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001664 if (unlikely(!i7core_pci)) {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001665 printk(KERN_WARNING
1666 "%s(): Unable to create PCI control\n",
1667 __func__);
1668 printk(KERN_WARNING
1669 "%s(): PCI error report via EDAC not setup\n",
1670 __func__);
1671 }
1672
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001673 /* Default error mask is any memory */
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001674 pvt->inject.channel = 0;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001675 pvt->inject.dimm = -1;
1676 pvt->inject.rank = -1;
1677 pvt->inject.bank = -1;
1678 pvt->inject.page = -1;
1679 pvt->inject.col = -1;
1680
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001681 /* Registers on edac_mce in order to receive memory errors */
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001682 pvt->edac_mce.priv = mci;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001683 pvt->edac_mce.check_error = i7core_mce_check_error;
1684 spin_lock_init(&pvt->mce_lock);
1685
1686 rc = edac_mce_register(&pvt->edac_mce);
1687 if (unlikely (rc < 0)) {
1688 debugf0("MC: " __FILE__
1689 ": %s(): failed edac_mce_register()\n", __func__);
1690 goto fail1;
1691 }
1692
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001693 i7core_printk(KERN_INFO, "Driver loaded.\n");
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001694
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001695 return 0;
1696
1697fail1:
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001698 edac_mc_free(mci);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001699
1700fail0:
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001701 i7core_put_devices();
1702 return rc;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001703}
1704
1705/*
1706 * i7core_remove destructor for one instance of device
1707 *
1708 */
1709static void __devexit i7core_remove(struct pci_dev *pdev)
1710{
1711 struct mem_ctl_info *mci;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001712 struct i7core_pvt *pvt;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001713
1714 debugf0(__FILE__ ": %s()\n", __func__);
1715
1716 if (i7core_pci)
1717 edac_pci_release_generic_ctl(i7core_pci);
1718
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001719
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001720 mci = edac_mc_del_mc(&pdev->dev);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001721 if (!mci)
1722 return;
1723
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001724 /* Unregisters on edac_mce in order to receive memory errors */
1725 pvt = mci->pvt_info;
1726 edac_mce_unregister(&pvt->edac_mce);
1727
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001728 /* retrieve references to resources, and free those resources */
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001729 i7core_put_devices();
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001730
1731 edac_mc_free(mci);
1732}
1733
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001734MODULE_DEVICE_TABLE(pci, i7core_pci_tbl);
1735
1736/*
1737 * i7core_driver pci_driver structure for this module
1738 *
1739 */
1740static struct pci_driver i7core_driver = {
1741 .name = "i7core_edac",
1742 .probe = i7core_probe,
1743 .remove = __devexit_p(i7core_remove),
1744 .id_table = i7core_pci_tbl,
1745};
1746
1747/*
1748 * i7core_init Module entry function
1749 * Try to initialize this module for its devices
1750 */
1751static int __init i7core_init(void)
1752{
1753 int pci_rc;
1754
1755 debugf2("MC: " __FILE__ ": %s()\n", __func__);
1756
1757 /* Ensure that the OPSTATE is set correctly for POLL or NMI */
1758 opstate_init();
1759
1760 pci_rc = pci_register_driver(&i7core_driver);
1761
1762 return (pci_rc < 0) ? pci_rc : 0;
1763}
1764
1765/*
1766 * i7core_exit() Module exit function
1767 * Unregister the driver
1768 */
1769static void __exit i7core_exit(void)
1770{
1771 debugf2("MC: " __FILE__ ": %s()\n", __func__);
1772 pci_unregister_driver(&i7core_driver);
1773}
1774
1775module_init(i7core_init);
1776module_exit(i7core_exit);
1777
1778MODULE_LICENSE("GPL");
1779MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
1780MODULE_AUTHOR("Red Hat Inc. (http://www.redhat.com)");
1781MODULE_DESCRIPTION("MC Driver for Intel i7 Core memory controllers - "
1782 I7CORE_REVISION);
1783
1784module_param(edac_op_state, int, 0444);
1785MODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI");