blob: 72859e87aeb293d2b5d487e5b4e93d694a257800 [file] [log] [blame]
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001/* Intel 7 core Memory Controller kernel module (Nehalem)
2 *
3 * This file may be distributed under the terms of the
4 * GNU General Public License version 2 only.
5 *
6 * Copyright (c) 2009 by:
7 * Mauro Carvalho Chehab <mchehab@redhat.com>
8 *
9 * Red Hat Inc. http://www.redhat.com
10 *
11 * Forked and adapted from the i5400_edac driver
12 *
13 * Based on the following public Intel datasheets:
14 * Intel Core i7 Processor Extreme Edition and Intel Core i7 Processor
15 * Datasheet, Volume 2:
16 * http://download.intel.com/design/processor/datashts/320835.pdf
17 * Intel Xeon Processor 5500 Series Datasheet Volume 2
18 * http://www.intel.com/Assets/PDF/datasheet/321322.pdf
19 * also available at:
20 * http://www.arrownac.com/manufacturers/intel/s/nehalem/5500-datasheet-v2.pdf
21 */
22
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030023#include <linux/module.h>
24#include <linux/init.h>
25#include <linux/pci.h>
26#include <linux/pci_ids.h>
27#include <linux/slab.h>
28#include <linux/edac.h>
29#include <linux/mmzone.h>
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -030030#include <linux/edac_mce.h>
31#include <linux/spinlock.h>
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030032
33#include "edac_core.h"
34
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030035/*
36 * Alter this version for the module when modifications are made
37 */
38#define I7CORE_REVISION " Ver: 1.0.0 " __DATE__
39#define EDAC_MOD_STR "i7core_edac"
40
41/* HACK: temporary, just to enable all logs, for now */
42#undef debugf0
43#define debugf0(fmt, arg...) edac_printk(KERN_INFO, "i7core", fmt, ##arg)
44
45/*
46 * Debug macros
47 */
48#define i7core_printk(level, fmt, arg...) \
49 edac_printk(level, "i7core", fmt, ##arg)
50
51#define i7core_mc_printk(mci, level, fmt, arg...) \
52 edac_mc_chipset_printk(mci, level, "i7core", fmt, ##arg)
53
54/*
55 * i7core Memory Controller Registers
56 */
57
Mauro Carvalho Chehabe9bd2e72009-07-09 22:14:35 -030058 /* OFFSETS for Device 0 Function 0 */
59
60#define MC_CFG_CONTROL 0x90
61
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030062 /* OFFSETS for Device 3 Function 0 */
63
64#define MC_CONTROL 0x48
65#define MC_STATUS 0x4c
66#define MC_MAX_DOD 0x64
67
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -030068/*
69 * OFFSETS for Device 3 Function 4, as inicated on Xeon 5500 datasheet:
70 * http://www.arrownac.com/manufacturers/intel/s/nehalem/5500-datasheet-v2.pdf
71 */
72
73#define MC_TEST_ERR_RCV1 0x60
74 #define DIMM2_COR_ERR(r) ((r) & 0x7fff)
75
76#define MC_TEST_ERR_RCV0 0x64
77 #define DIMM1_COR_ERR(r) (((r) >> 16) & 0x7fff)
78 #define DIMM0_COR_ERR(r) ((r) & 0x7fff)
79
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030080 /* OFFSETS for Devices 4,5 and 6 Function 0 */
81
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -030082#define MC_CHANNEL_DIMM_INIT_PARAMS 0x58
83 #define THREE_DIMMS_PRESENT (1 << 24)
84 #define SINGLE_QUAD_RANK_PRESENT (1 << 23)
85 #define QUAD_RANK_PRESENT (1 << 22)
86 #define REGISTERED_DIMM (1 << 15)
87
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -030088#define MC_CHANNEL_MAPPER 0x60
89 #define RDLCH(r, ch) ((((r) >> (3 + (ch * 6))) & 0x07) - 1)
90 #define WRLCH(r, ch) ((((r) >> (ch * 6)) & 0x07) - 1)
91
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -030092#define MC_CHANNEL_RANK_PRESENT 0x7c
93 #define RANK_PRESENT_MASK 0xffff
94
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030095#define MC_CHANNEL_ADDR_MATCH 0xf0
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -030096#define MC_CHANNEL_ERROR_MASK 0xf8
97#define MC_CHANNEL_ERROR_INJECT 0xfc
98 #define INJECT_ADDR_PARITY 0x10
99 #define INJECT_ECC 0x08
100 #define MASK_CACHELINE 0x06
101 #define MASK_FULL_CACHELINE 0x06
102 #define MASK_MSB32_CACHELINE 0x04
103 #define MASK_LSB32_CACHELINE 0x02
104 #define NO_MASK_CACHELINE 0x00
105 #define REPEAT_EN 0x01
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300106
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300107 /* OFFSETS for Devices 4,5 and 6 Function 1 */
108#define MC_DOD_CH_DIMM0 0x48
109#define MC_DOD_CH_DIMM1 0x4c
110#define MC_DOD_CH_DIMM2 0x50
111 #define RANKOFFSET_MASK ((1 << 12) | (1 << 11) | (1 << 10))
112 #define RANKOFFSET(x) ((x & RANKOFFSET_MASK) >> 10)
113 #define DIMM_PRESENT_MASK (1 << 9)
114 #define DIMM_PRESENT(x) (((x) & DIMM_PRESENT_MASK) >> 9)
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300115 #define MC_DOD_NUMBANK_MASK ((1 << 8) | (1 << 7))
116 #define MC_DOD_NUMBANK(x) (((x) & MC_DOD_NUMBANK_MASK) >> 7)
117 #define MC_DOD_NUMRANK_MASK ((1 << 6) | (1 << 5))
118 #define MC_DOD_NUMRANK(x) (((x) & MC_DOD_NUMRANK_MASK) >> 5)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300119 #define MC_DOD_NUMROW_MASK ((1 << 4) | (1 << 3) | (1 << 2))
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300120 #define MC_DOD_NUMROW(x) (((x) & MC_DOD_NUMROW_MASK) >> 2)
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300121 #define MC_DOD_NUMCOL_MASK 3
122 #define MC_DOD_NUMCOL(x) ((x) & MC_DOD_NUMCOL_MASK)
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300123
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300124#define MC_RANK_PRESENT 0x7c
125
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300126#define MC_SAG_CH_0 0x80
127#define MC_SAG_CH_1 0x84
128#define MC_SAG_CH_2 0x88
129#define MC_SAG_CH_3 0x8c
130#define MC_SAG_CH_4 0x90
131#define MC_SAG_CH_5 0x94
132#define MC_SAG_CH_6 0x98
133#define MC_SAG_CH_7 0x9c
134
135#define MC_RIR_LIMIT_CH_0 0x40
136#define MC_RIR_LIMIT_CH_1 0x44
137#define MC_RIR_LIMIT_CH_2 0x48
138#define MC_RIR_LIMIT_CH_3 0x4C
139#define MC_RIR_LIMIT_CH_4 0x50
140#define MC_RIR_LIMIT_CH_5 0x54
141#define MC_RIR_LIMIT_CH_6 0x58
142#define MC_RIR_LIMIT_CH_7 0x5C
143#define MC_RIR_LIMIT_MASK ((1 << 10) - 1)
144
145#define MC_RIR_WAY_CH 0x80
146 #define MC_RIR_WAY_OFFSET_MASK (((1 << 14) - 1) & ~0x7)
147 #define MC_RIR_WAY_RANK_MASK 0x7
148
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300149/*
150 * i7core structs
151 */
152
153#define NUM_CHANS 3
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -0300154#define MAX_DIMMS 3 /* Max DIMMS per channel */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300155#define NUM_SOCKETS 2 /* Max number of MC sockets */
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -0300156#define MAX_MCR_FUNC 4
157#define MAX_CHAN_FUNC 3
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300158
159struct i7core_info {
160 u32 mc_control;
161 u32 mc_status;
162 u32 max_dod;
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300163 u32 ch_map;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300164};
165
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300166
167struct i7core_inject {
168 int enable;
169
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300170 u8 socket;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300171 u32 section;
172 u32 type;
173 u32 eccmask;
174
175 /* Error address mask */
176 int channel, dimm, rank, bank, page, col;
177};
178
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300179struct i7core_channel {
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -0300180 u32 ranks;
181 u32 dimms;
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300182};
183
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300184struct pci_id_descr {
185 int dev;
186 int func;
187 int dev_id;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300188 struct pci_dev *pdev[NUM_SOCKETS];
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300189};
190
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300191struct i7core_pvt {
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300192 struct pci_dev *pci_noncore[NUM_SOCKETS];
193 struct pci_dev *pci_mcr[NUM_SOCKETS][MAX_MCR_FUNC + 1];
194 struct pci_dev *pci_ch[NUM_SOCKETS][NUM_CHANS][MAX_CHAN_FUNC + 1];
195
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300196 struct i7core_info info;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300197 struct i7core_inject inject;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300198 struct i7core_channel channel[NUM_SOCKETS][NUM_CHANS];
199
200 int sockets; /* Number of sockets */
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300201 int channels; /* Number of active channels */
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -0300202
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300203 int ce_count_available[NUM_SOCKETS];
204 /* ECC corrected errors counts per dimm */
205 unsigned long ce_count[NUM_SOCKETS][MAX_DIMMS];
206 int last_ce_count[NUM_SOCKETS][MAX_DIMMS];
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -0300207
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -0300208 /* mcelog glue */
209 struct edac_mce edac_mce;
210 struct mce mce_entry[MCE_LOG_LEN];
211 unsigned mce_count;
212 spinlock_t mce_lock;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300213};
214
215/* Device name and register DID (Device ID) */
216struct i7core_dev_info {
217 const char *ctl_name; /* name for this device */
218 u16 fsb_mapping_errors; /* DID for the branchmap,control */
219};
220
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300221#define PCI_DESCR(device, function, device_id) \
222 .dev = (device), \
223 .func = (function), \
224 .dev_id = (device_id)
225
226struct pci_id_descr pci_devs[] = {
227 /* Memory controller */
228 { PCI_DESCR(3, 0, PCI_DEVICE_ID_INTEL_I7_MCR) },
229 { PCI_DESCR(3, 1, PCI_DEVICE_ID_INTEL_I7_MC_TAD) },
230 { PCI_DESCR(3, 2, PCI_DEVICE_ID_INTEL_I7_MC_RAS) }, /* if RDIMM is supported */
231 { PCI_DESCR(3, 4, PCI_DEVICE_ID_INTEL_I7_MC_TEST) },
232
233 /* Channel 0 */
234 { PCI_DESCR(4, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH0_CTRL) },
235 { PCI_DESCR(4, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH0_ADDR) },
236 { PCI_DESCR(4, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH0_RANK) },
237 { PCI_DESCR(4, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH0_TC) },
238
239 /* Channel 1 */
240 { PCI_DESCR(5, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH1_CTRL) },
241 { PCI_DESCR(5, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH1_ADDR) },
242 { PCI_DESCR(5, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH1_RANK) },
243 { PCI_DESCR(5, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH1_TC) },
244
245 /* Channel 2 */
246 { PCI_DESCR(6, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH2_CTRL) },
247 { PCI_DESCR(6, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH2_ADDR) },
248 { PCI_DESCR(6, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH2_RANK) },
249 { PCI_DESCR(6, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH2_TC) },
Mauro Carvalho Chehab310cbb72009-07-17 00:09:10 -0300250
251 /* Generic Non-core registers */
252 /*
253 * This is the PCI device on i7core and on Xeon 35xx (8086:2c41)
254 * On Xeon 55xx, however, it has a different id (8086:2c40). So,
255 * the probing code needs to test for the other address in case of
256 * failure of this one
257 */
258 { PCI_DESCR(0, 0, PCI_DEVICE_ID_INTEL_I7_NOCORE) },
259
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300260};
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300261#define N_DEVS ARRAY_SIZE(pci_devs)
262
263/*
264 * pci_device_id table for which devices we are looking for
265 * This should match the first device at pci_devs table
266 */
267static const struct pci_device_id i7core_pci_tbl[] __devinitdata = {
Mauro Carvalho Chehabd1fd4fb2009-07-10 18:39:53 -0300268 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_X58_HUB_MGMT)},
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300269 {0,} /* 0 terminated list. */
270};
271
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300272
273/* Table of devices attributes supported by this driver */
274static const struct i7core_dev_info i7core_devs[] = {
275 {
276 .ctl_name = "i7 Core",
277 .fsb_mapping_errors = PCI_DEVICE_ID_INTEL_I7_MCR,
278 },
279};
280
281static struct edac_pci_ctl_info *i7core_pci;
282
283/****************************************************************************
284 Anciliary status routines
285 ****************************************************************************/
286
287 /* MC_CONTROL bits */
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300288#define CH_ACTIVE(pvt, ch) ((pvt)->info.mc_control & (1 << (8 + ch)))
289#define ECCx8(pvt) ((pvt)->info.mc_control & (1 << 1))
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300290
291 /* MC_STATUS bits */
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300292#define ECC_ENABLED(pvt) ((pvt)->info.mc_status & (1 << 3))
293#define CH_DISABLED(pvt, ch) ((pvt)->info.mc_status & (1 << ch))
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300294
295 /* MC_MAX_DOD read functions */
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300296static inline int numdimms(u32 dimms)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300297{
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300298 return (dimms & 0x3) + 1;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300299}
300
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300301static inline int numrank(u32 rank)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300302{
303 static int ranks[4] = { 1, 2, 4, -EINVAL };
304
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300305 return ranks[rank & 0x3];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300306}
307
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300308static inline int numbank(u32 bank)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300309{
310 static int banks[4] = { 4, 8, 16, -EINVAL };
311
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300312 return banks[bank & 0x3];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300313}
314
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300315static inline int numrow(u32 row)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300316{
317 static int rows[8] = {
318 1 << 12, 1 << 13, 1 << 14, 1 << 15,
319 1 << 16, -EINVAL, -EINVAL, -EINVAL,
320 };
321
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300322 return rows[row & 0x7];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300323}
324
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300325static inline int numcol(u32 col)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300326{
327 static int cols[8] = {
328 1 << 10, 1 << 11, 1 << 12, -EINVAL,
329 };
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300330 return cols[col & 0x3];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300331}
332
333/****************************************************************************
334 Memory check routines
335 ****************************************************************************/
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300336static struct pci_dev *get_pdev_slot_func(u8 socket, unsigned slot,
337 unsigned func)
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300338{
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300339 int i;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300340
341 for (i = 0; i < N_DEVS; i++) {
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300342 if (!pci_devs[i].pdev[socket])
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300343 continue;
344
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300345 if (PCI_SLOT(pci_devs[i].pdev[socket]->devfn) == slot &&
346 PCI_FUNC(pci_devs[i].pdev[socket]->devfn) == func) {
347 return pci_devs[i].pdev[socket];
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300348 }
349 }
350
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300351 return NULL;
352}
353
Mauro Carvalho Chehabec6df242009-07-18 10:44:30 -0300354/**
355 * i7core_get_active_channels() - gets the number of channels and csrows
356 * @socket: Quick Path Interconnect socket
357 * @channels: Number of channels that will be returned
358 * @csrows: Number of csrows found
359 *
360 * Since EDAC core needs to know in advance the number of available channels
361 * and csrows, in order to allocate memory for csrows/channels, it is needed
362 * to run two similar steps. At the first step, implemented on this function,
363 * it checks the number of csrows/channels present at one socket.
364 * this is used in order to properly allocate the size of mci components.
365 *
366 * It should be noticed that none of the current available datasheets explain
367 * or even mention how csrows are seen by the memory controller. So, we need
368 * to add a fake description for csrows.
369 * So, this driver is attributing one DIMM memory for one csrow.
370 */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300371static int i7core_get_active_channels(u8 socket, unsigned *channels,
372 unsigned *csrows)
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300373{
374 struct pci_dev *pdev = NULL;
375 int i, j;
376 u32 status, control;
377
378 *channels = 0;
379 *csrows = 0;
380
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300381 pdev = get_pdev_slot_func(socket, 3, 0);
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -0300382 if (!pdev) {
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300383 i7core_printk(KERN_ERR, "Couldn't find socket %d fn 3.0!!!\n",
384 socket);
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300385 return -ENODEV;
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -0300386 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300387
388 /* Device 3 function 0 reads */
389 pci_read_config_dword(pdev, MC_STATUS, &status);
390 pci_read_config_dword(pdev, MC_CONTROL, &control);
391
392 for (i = 0; i < NUM_CHANS; i++) {
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300393 u32 dimm_dod[3];
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300394 /* Check if the channel is active */
395 if (!(control & (1 << (8 + i))))
396 continue;
397
398 /* Check if the channel is disabled */
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300399 if (status & (1 << i))
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300400 continue;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300401
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300402 pdev = get_pdev_slot_func(socket, i + 4, 1);
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300403 if (!pdev) {
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300404 i7core_printk(KERN_ERR, "Couldn't find socket %d "
405 "fn %d.%d!!!\n",
406 socket, i + 4, 1);
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300407 return -ENODEV;
408 }
409 /* Devices 4-6 function 1 */
410 pci_read_config_dword(pdev,
411 MC_DOD_CH_DIMM0, &dimm_dod[0]);
412 pci_read_config_dword(pdev,
413 MC_DOD_CH_DIMM1, &dimm_dod[1]);
414 pci_read_config_dword(pdev,
415 MC_DOD_CH_DIMM2, &dimm_dod[2]);
416
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300417 (*channels)++;
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300418
419 for (j = 0; j < 3; j++) {
420 if (!DIMM_PRESENT(dimm_dod[j]))
421 continue;
422 (*csrows)++;
423 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300424 }
425
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -0300426 debugf0("Number of active channels on socket %d: %d\n",
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300427 socket, *channels);
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300428
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300429 return 0;
430}
431
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -0300432static int get_dimm_config(struct mem_ctl_info *mci, int *csrow, u8 socket)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300433{
434 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300435 struct csrow_info *csr;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300436 struct pci_dev *pdev;
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -0300437 int i, j;
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300438 unsigned long last_page = 0;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300439 enum edac_type mode;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300440 enum mem_type mtype;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300441
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300442 /* Get data from the MC register, function 0 */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300443 pdev = pvt->pci_mcr[socket][0];
Mauro Carvalho Chehab7dd69532009-06-22 22:48:30 -0300444 if (!pdev)
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300445 return -ENODEV;
446
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300447 /* Device 3 function 0 reads */
Mauro Carvalho Chehab7dd69532009-06-22 22:48:30 -0300448 pci_read_config_dword(pdev, MC_CONTROL, &pvt->info.mc_control);
449 pci_read_config_dword(pdev, MC_STATUS, &pvt->info.mc_status);
450 pci_read_config_dword(pdev, MC_MAX_DOD, &pvt->info.max_dod);
451 pci_read_config_dword(pdev, MC_CHANNEL_MAPPER, &pvt->info.ch_map);
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300452
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300453 debugf0("QPI %d control=0x%08x status=0x%08x dod=0x%08x map=0x%08x\n",
454 socket, pvt->info.mc_control, pvt->info.mc_status,
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300455 pvt->info.max_dod, pvt->info.ch_map);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300456
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300457 if (ECC_ENABLED(pvt)) {
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300458 debugf0("ECC enabled with x%d SDCC\n", ECCx8(pvt) ? 8 : 4);
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300459 if (ECCx8(pvt))
460 mode = EDAC_S8ECD8ED;
461 else
462 mode = EDAC_S4ECD4ED;
463 } else {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300464 debugf0("ECC disabled\n");
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300465 mode = EDAC_NONE;
466 }
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300467
468 /* FIXME: need to handle the error codes */
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300469 debugf0("DOD Max limits: DIMMS: %d, %d-ranked, %d-banked "
470 "x%x x 0x%x\n",
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300471 numdimms(pvt->info.max_dod),
472 numrank(pvt->info.max_dod >> 2),
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300473 numbank(pvt->info.max_dod >> 4),
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300474 numrow(pvt->info.max_dod >> 6),
475 numcol(pvt->info.max_dod >> 9));
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300476
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300477 for (i = 0; i < NUM_CHANS; i++) {
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300478 u32 data, dimm_dod[3], value[8];
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300479
480 if (!CH_ACTIVE(pvt, i)) {
481 debugf0("Channel %i is not active\n", i);
482 continue;
483 }
484 if (CH_DISABLED(pvt, i)) {
485 debugf0("Channel %i is disabled\n", i);
486 continue;
487 }
488
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300489 /* Devices 4-6 function 0 */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300490 pci_read_config_dword(pvt->pci_ch[socket][i][0],
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300491 MC_CHANNEL_DIMM_INIT_PARAMS, &data);
492
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300493 pvt->channel[socket][i].ranks = (data & QUAD_RANK_PRESENT) ?
494 4 : 2;
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300495
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300496 if (data & REGISTERED_DIMM)
497 mtype = MEM_RDDR3;
498 else
499 mtype = MEM_DDR3;
500#if 0
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300501 if (data & THREE_DIMMS_PRESENT)
502 pvt->channel[i].dimms = 3;
503 else if (data & SINGLE_QUAD_RANK_PRESENT)
504 pvt->channel[i].dimms = 1;
505 else
506 pvt->channel[i].dimms = 2;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300507#endif
508
509 /* Devices 4-6 function 1 */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300510 pci_read_config_dword(pvt->pci_ch[socket][i][1],
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300511 MC_DOD_CH_DIMM0, &dimm_dod[0]);
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300512 pci_read_config_dword(pvt->pci_ch[socket][i][1],
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300513 MC_DOD_CH_DIMM1, &dimm_dod[1]);
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300514 pci_read_config_dword(pvt->pci_ch[socket][i][1],
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300515 MC_DOD_CH_DIMM2, &dimm_dod[2]);
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300516
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300517 debugf0("Ch%d phy rd%d, wr%d (0x%08x): "
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300518 "%d ranks, %cDIMMs\n",
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300519 i,
520 RDLCH(pvt->info.ch_map, i), WRLCH(pvt->info.ch_map, i),
521 data,
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300522 pvt->channel[socket][i].ranks,
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300523 (data & REGISTERED_DIMM) ? 'R' : 'U');
Mauro Carvalho Chehab7dd69532009-06-22 22:48:30 -0300524
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300525 for (j = 0; j < 3; j++) {
526 u32 banks, ranks, rows, cols;
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300527 u32 size, npages;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300528
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300529 if (!DIMM_PRESENT(dimm_dod[j]))
530 continue;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300531
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300532 banks = numbank(MC_DOD_NUMBANK(dimm_dod[j]));
533 ranks = numrank(MC_DOD_NUMRANK(dimm_dod[j]));
534 rows = numrow(MC_DOD_NUMROW(dimm_dod[j]));
535 cols = numcol(MC_DOD_NUMCOL(dimm_dod[j]));
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300536
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300537 /* DDR3 has 8 I/O banks */
538 size = (rows * cols * banks * ranks) >> (20 - 3);
539
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300540 pvt->channel[socket][i].dimms++;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300541
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300542 debugf0("\tdimm %d %d Mb offset: %x, "
543 "bank: %d, rank: %d, row: %#x, col: %#x\n",
544 j, size,
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300545 RANKOFFSET(dimm_dod[j]),
546 banks, ranks, rows, cols);
547
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300548#if PAGE_SHIFT > 20
549 npages = size >> (PAGE_SHIFT - 20);
550#else
551 npages = size << (20 - PAGE_SHIFT);
552#endif
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300553
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -0300554 csr = &mci->csrows[*csrow];
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300555 csr->first_page = last_page + 1;
556 last_page += npages;
557 csr->last_page = last_page;
558 csr->nr_pages = npages;
559
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300560 csr->page_mask = 0;
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300561 csr->grain = 8;
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -0300562 csr->csrow_idx = *csrow;
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300563 csr->nr_channels = 1;
564
565 csr->channels[0].chan_idx = i;
566 csr->channels[0].ce_count = 0;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300567
568 switch (banks) {
569 case 4:
570 csr->dtype = DEV_X4;
571 break;
572 case 8:
573 csr->dtype = DEV_X8;
574 break;
575 case 16:
576 csr->dtype = DEV_X16;
577 break;
578 default:
579 csr->dtype = DEV_UNKNOWN;
580 }
581
582 csr->edac_mode = mode;
583 csr->mtype = mtype;
584
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -0300585 (*csrow)++;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300586 }
587
588 pci_read_config_dword(pdev, MC_SAG_CH_0, &value[0]);
589 pci_read_config_dword(pdev, MC_SAG_CH_1, &value[1]);
590 pci_read_config_dword(pdev, MC_SAG_CH_2, &value[2]);
591 pci_read_config_dword(pdev, MC_SAG_CH_3, &value[3]);
592 pci_read_config_dword(pdev, MC_SAG_CH_4, &value[4]);
593 pci_read_config_dword(pdev, MC_SAG_CH_5, &value[5]);
594 pci_read_config_dword(pdev, MC_SAG_CH_6, &value[6]);
595 pci_read_config_dword(pdev, MC_SAG_CH_7, &value[7]);
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300596 debugf1("\t[%i] DIVBY3\tREMOVED\tOFFSET\n", i);
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300597 for (j = 0; j < 8; j++)
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300598 debugf1("\t\t%#x\t%#x\t%#x\n",
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300599 (value[j] >> 27) & 0x1,
600 (value[j] >> 24) & 0x7,
601 (value[j] && ((1 << 24) - 1)));
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300602 }
603
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300604 return 0;
605}
606
607/****************************************************************************
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300608 Error insertion routines
609 ****************************************************************************/
610
611/* The i7core has independent error injection features per channel.
612 However, to have a simpler code, we don't allow enabling error injection
613 on more than one channel.
614 Also, since a change at an inject parameter will be applied only at enable,
615 we're disabling error injection on all write calls to the sysfs nodes that
616 controls the error code injection.
617 */
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300618static int disable_inject(struct mem_ctl_info *mci)
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300619{
620 struct i7core_pvt *pvt = mci->pvt_info;
621
622 pvt->inject.enable = 0;
623
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300624 if (!pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0])
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300625 return -ENODEV;
626
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300627 pci_write_config_dword(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0],
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300628 MC_CHANNEL_ERROR_MASK, 0);
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300629
630 return 0;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300631}
632
633/*
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300634 * i7core inject inject.socket
635 *
636 * accept and store error injection inject.socket value
637 */
638static ssize_t i7core_inject_socket_store(struct mem_ctl_info *mci,
639 const char *data, size_t count)
640{
641 struct i7core_pvt *pvt = mci->pvt_info;
642 unsigned long value;
643 int rc;
644
645 rc = strict_strtoul(data, 10, &value);
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300646 if ((rc < 0) || (value >= pvt->sockets))
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300647 return 0;
648
649 pvt->inject.section = (u32) value;
650 return count;
651}
652
653static ssize_t i7core_inject_socket_show(struct mem_ctl_info *mci,
654 char *data)
655{
656 struct i7core_pvt *pvt = mci->pvt_info;
657 return sprintf(data, "%d\n", pvt->inject.socket);
658}
659
660/*
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300661 * i7core inject inject.section
662 *
663 * accept and store error injection inject.section value
664 * bit 0 - refers to the lower 32-byte half cacheline
665 * bit 1 - refers to the upper 32-byte half cacheline
666 */
667static ssize_t i7core_inject_section_store(struct mem_ctl_info *mci,
668 const char *data, size_t count)
669{
670 struct i7core_pvt *pvt = mci->pvt_info;
671 unsigned long value;
672 int rc;
673
674 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300675 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300676
677 rc = strict_strtoul(data, 10, &value);
678 if ((rc < 0) || (value > 3))
679 return 0;
680
681 pvt->inject.section = (u32) value;
682 return count;
683}
684
685static ssize_t i7core_inject_section_show(struct mem_ctl_info *mci,
686 char *data)
687{
688 struct i7core_pvt *pvt = mci->pvt_info;
689 return sprintf(data, "0x%08x\n", pvt->inject.section);
690}
691
692/*
693 * i7core inject.type
694 *
695 * accept and store error injection inject.section value
696 * bit 0 - repeat enable - Enable error repetition
697 * bit 1 - inject ECC error
698 * bit 2 - inject parity error
699 */
700static ssize_t i7core_inject_type_store(struct mem_ctl_info *mci,
701 const char *data, size_t count)
702{
703 struct i7core_pvt *pvt = mci->pvt_info;
704 unsigned long value;
705 int rc;
706
707 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300708 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300709
710 rc = strict_strtoul(data, 10, &value);
711 if ((rc < 0) || (value > 7))
712 return 0;
713
714 pvt->inject.type = (u32) value;
715 return count;
716}
717
718static ssize_t i7core_inject_type_show(struct mem_ctl_info *mci,
719 char *data)
720{
721 struct i7core_pvt *pvt = mci->pvt_info;
722 return sprintf(data, "0x%08x\n", pvt->inject.type);
723}
724
725/*
726 * i7core_inject_inject.eccmask_store
727 *
728 * The type of error (UE/CE) will depend on the inject.eccmask value:
729 * Any bits set to a 1 will flip the corresponding ECC bit
730 * Correctable errors can be injected by flipping 1 bit or the bits within
731 * a symbol pair (2 consecutive aligned 8-bit pairs - i.e. 7:0 and 15:8 or
732 * 23:16 and 31:24). Flipping bits in two symbol pairs will cause an
733 * uncorrectable error to be injected.
734 */
735static ssize_t i7core_inject_eccmask_store(struct mem_ctl_info *mci,
736 const char *data, size_t count)
737{
738 struct i7core_pvt *pvt = mci->pvt_info;
739 unsigned long value;
740 int rc;
741
742 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300743 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300744
745 rc = strict_strtoul(data, 10, &value);
746 if (rc < 0)
747 return 0;
748
749 pvt->inject.eccmask = (u32) value;
750 return count;
751}
752
753static ssize_t i7core_inject_eccmask_show(struct mem_ctl_info *mci,
754 char *data)
755{
756 struct i7core_pvt *pvt = mci->pvt_info;
757 return sprintf(data, "0x%08x\n", pvt->inject.eccmask);
758}
759
760/*
761 * i7core_addrmatch
762 *
763 * The type of error (UE/CE) will depend on the inject.eccmask value:
764 * Any bits set to a 1 will flip the corresponding ECC bit
765 * Correctable errors can be injected by flipping 1 bit or the bits within
766 * a symbol pair (2 consecutive aligned 8-bit pairs - i.e. 7:0 and 15:8 or
767 * 23:16 and 31:24). Flipping bits in two symbol pairs will cause an
768 * uncorrectable error to be injected.
769 */
770static ssize_t i7core_inject_addrmatch_store(struct mem_ctl_info *mci,
771 const char *data, size_t count)
772{
773 struct i7core_pvt *pvt = mci->pvt_info;
774 char *cmd, *val;
775 long value;
776 int rc;
777
778 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300779 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300780
781 do {
782 cmd = strsep((char **) &data, ":");
783 if (!cmd)
784 break;
785 val = strsep((char **) &data, " \n\t");
786 if (!val)
787 return cmd - data;
788
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300789 if (!strcasecmp(val, "any"))
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300790 value = -1;
791 else {
792 rc = strict_strtol(val, 10, &value);
793 if ((rc < 0) || (value < 0))
794 return cmd - data;
795 }
796
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300797 if (!strcasecmp(cmd, "channel")) {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300798 if (value < 3)
799 pvt->inject.channel = value;
800 else
801 return cmd - data;
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300802 } else if (!strcasecmp(cmd, "dimm")) {
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300803 if (value < 3)
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300804 pvt->inject.dimm = value;
805 else
806 return cmd - data;
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300807 } else if (!strcasecmp(cmd, "rank")) {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300808 if (value < 4)
809 pvt->inject.rank = value;
810 else
811 return cmd - data;
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300812 } else if (!strcasecmp(cmd, "bank")) {
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300813 if (value < 32)
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300814 pvt->inject.bank = value;
815 else
816 return cmd - data;
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300817 } else if (!strcasecmp(cmd, "page")) {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300818 if (value <= 0xffff)
819 pvt->inject.page = value;
820 else
821 return cmd - data;
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300822 } else if (!strcasecmp(cmd, "col") ||
823 !strcasecmp(cmd, "column")) {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300824 if (value <= 0x3fff)
825 pvt->inject.col = value;
826 else
827 return cmd - data;
828 }
829 } while (1);
830
831 return count;
832}
833
834static ssize_t i7core_inject_addrmatch_show(struct mem_ctl_info *mci,
835 char *data)
836{
837 struct i7core_pvt *pvt = mci->pvt_info;
838 char channel[4], dimm[4], bank[4], rank[4], page[7], col[7];
839
840 if (pvt->inject.channel < 0)
841 sprintf(channel, "any");
842 else
843 sprintf(channel, "%d", pvt->inject.channel);
844 if (pvt->inject.dimm < 0)
845 sprintf(dimm, "any");
846 else
847 sprintf(dimm, "%d", pvt->inject.dimm);
848 if (pvt->inject.bank < 0)
849 sprintf(bank, "any");
850 else
851 sprintf(bank, "%d", pvt->inject.bank);
852 if (pvt->inject.rank < 0)
853 sprintf(rank, "any");
854 else
855 sprintf(rank, "%d", pvt->inject.rank);
856 if (pvt->inject.page < 0)
857 sprintf(page, "any");
858 else
859 sprintf(page, "0x%04x", pvt->inject.page);
860 if (pvt->inject.col < 0)
861 sprintf(col, "any");
862 else
863 sprintf(col, "0x%04x", pvt->inject.col);
864
865 return sprintf(data, "channel: %s\ndimm: %s\nbank: %s\n"
866 "rank: %s\npage: %s\ncolumn: %s\n",
867 channel, dimm, bank, rank, page, col);
868}
869
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300870static int write_and_test(struct pci_dev *dev, int where, u32 val)
871{
872 u32 read;
873 int count;
874
875 for (count = 0; count < 10; count++) {
876 if (count)
877 msleep (100);
878 pci_write_config_dword(dev, where, val);
879 pci_read_config_dword(dev, where, &read);
880
881 if (read == val)
882 return 0;
883 }
884
885 debugf0("Error Injection Register 0x%02x: Tried to write 0x%08x, "
886 "but read: 0x%08x\n", where, val, read);
887
888 return -EINVAL;
889}
890
891
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300892/*
893 * This routine prepares the Memory Controller for error injection.
894 * The error will be injected when some process tries to write to the
895 * memory that matches the given criteria.
896 * The criteria can be set in terms of a mask where dimm, rank, bank, page
897 * and col can be specified.
898 * A -1 value for any of the mask items will make the MCU to ignore
899 * that matching criteria for error injection.
900 *
901 * It should be noticed that the error will only happen after a write operation
902 * on a memory that matches the condition. if REPEAT_EN is not enabled at
903 * inject mask, then it will produce just one error. Otherwise, it will repeat
904 * until the injectmask would be cleaned.
905 *
906 * FIXME: This routine assumes that MAXNUMDIMMS value of MC_MAX_DOD
907 * is reliable enough to check if the MC is using the
908 * three channels. However, this is not clear at the datasheet.
909 */
910static ssize_t i7core_inject_enable_store(struct mem_ctl_info *mci,
911 const char *data, size_t count)
912{
913 struct i7core_pvt *pvt = mci->pvt_info;
914 u32 injectmask;
915 u64 mask = 0;
916 int rc;
917 long enable;
918
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300919 if (!pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0])
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300920 return 0;
921
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300922 rc = strict_strtoul(data, 10, &enable);
923 if ((rc < 0))
924 return 0;
925
926 if (enable) {
927 pvt->inject.enable = 1;
928 } else {
929 disable_inject(mci);
930 return count;
931 }
932
933 /* Sets pvt->inject.dimm mask */
934 if (pvt->inject.dimm < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300935 mask |= 1L << 41;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300936 else {
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300937 if (pvt->channel[pvt->inject.socket][pvt->inject.channel].dimms > 2)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300938 mask |= (pvt->inject.dimm & 0x3L) << 35;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300939 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300940 mask |= (pvt->inject.dimm & 0x1L) << 36;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300941 }
942
943 /* Sets pvt->inject.rank mask */
944 if (pvt->inject.rank < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300945 mask |= 1L << 40;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300946 else {
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300947 if (pvt->channel[pvt->inject.socket][pvt->inject.channel].dimms > 2)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300948 mask |= (pvt->inject.rank & 0x1L) << 34;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300949 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300950 mask |= (pvt->inject.rank & 0x3L) << 34;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300951 }
952
953 /* Sets pvt->inject.bank mask */
954 if (pvt->inject.bank < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300955 mask |= 1L << 39;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300956 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300957 mask |= (pvt->inject.bank & 0x15L) << 30;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300958
959 /* Sets pvt->inject.page mask */
960 if (pvt->inject.page < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300961 mask |= 1L << 38;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300962 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300963 mask |= (pvt->inject.page & 0xffffL) << 14;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300964
965 /* Sets pvt->inject.column mask */
966 if (pvt->inject.col < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300967 mask |= 1L << 37;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300968 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300969 mask |= (pvt->inject.col & 0x3fffL);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300970
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300971 /*
972 * bit 0: REPEAT_EN
973 * bits 1-2: MASK_HALF_CACHELINE
974 * bit 3: INJECT_ECC
975 * bit 4: INJECT_ADDR_PARITY
976 */
977
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300978 injectmask = (pvt->inject.type & 1) |
979 (pvt->inject.section & 0x3) << 1 |
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300980 (pvt->inject.type & 0x6) << (3 - 1);
981
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300982 /* Unlock writes to registers - this register is write only */
983 pci_write_config_dword(pvt->pci_noncore[pvt->inject.socket],
984 MC_CFG_CONTROL, 0x2);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300985
Mauro Carvalho Chehabe9bd2e72009-07-09 22:14:35 -0300986#if 0
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300987 /* Zeroes error count registers */
988 pci_write_config_dword(pvt->pci_mcr[pvt->inject.socket][4],
989 MC_TEST_ERR_RCV1, 0);
990 pci_write_config_dword(pvt->pci_mcr[pvt->inject.socket][4],
991 MC_TEST_ERR_RCV0, 0);
992 pvt->ce_count_available[pvt->inject.socket] = 0;
Mauro Carvalho Chehabe9bd2e72009-07-09 22:14:35 -0300993#endif
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300994
995 write_and_test(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0],
996 MC_CHANNEL_ADDR_MATCH, mask);
997 write_and_test(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0],
998 MC_CHANNEL_ADDR_MATCH + 4, mask >> 32L);
999
1000 write_and_test(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0],
1001 MC_CHANNEL_ERROR_MASK, pvt->inject.eccmask);
1002
1003 write_and_test(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0],
1004 MC_CHANNEL_ERROR_MASK, injectmask);
1005
1006 /*
1007 * This is something undocumented, based on my tests
1008 * Without writing 8 to this register, errors aren't injected. Not sure
1009 * why.
1010 */
1011 pci_write_config_dword(pvt->pci_noncore[pvt->inject.socket],
1012 MC_CFG_CONTROL, 8);
1013
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001014 debugf0("Error inject addr match 0x%016llx, ecc 0x%08x,"
1015 " inject 0x%08x\n",
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001016 mask, pvt->inject.eccmask, injectmask);
1017
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -03001018
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001019 return count;
1020}
1021
1022static ssize_t i7core_inject_enable_show(struct mem_ctl_info *mci,
1023 char *data)
1024{
1025 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -03001026 u32 injectmask;
1027
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001028 pci_read_config_dword(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0],
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -03001029 MC_CHANNEL_ERROR_MASK, &injectmask);
1030
1031 debugf0("Inject error read: 0x%018x\n", injectmask);
1032
1033 if (injectmask & 0x0c)
1034 pvt->inject.enable = 1;
1035
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001036 return sprintf(data, "%d\n", pvt->inject.enable);
1037}
1038
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001039static ssize_t i7core_ce_regs_show(struct mem_ctl_info *mci, char *data)
1040{
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001041 unsigned i, count, total = 0;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001042 struct i7core_pvt *pvt = mci->pvt_info;
1043
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001044 for (i = 0; i < pvt->sockets; i++) {
1045 if (!pvt->ce_count_available[i])
1046 count = sprintf(data, "socket 0 data unavailable\n");
1047 else
1048 count = sprintf(data, "socket %d, dimm0: %lu\n"
1049 "dimm1: %lu\ndimm2: %lu\n",
1050 i,
1051 pvt->ce_count[i][0],
1052 pvt->ce_count[i][1],
1053 pvt->ce_count[i][2]);
1054 data += count;
1055 total += count;
1056 }
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001057
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001058 return total;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001059}
1060
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001061/*
1062 * Sysfs struct
1063 */
1064static struct mcidev_sysfs_attribute i7core_inj_attrs[] = {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001065 {
1066 .attr = {
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001067 .name = "inject_socket",
1068 .mode = (S_IRUGO | S_IWUSR)
1069 },
1070 .show = i7core_inject_socket_show,
1071 .store = i7core_inject_socket_store,
1072 }, {
1073 .attr = {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001074 .name = "inject_section",
1075 .mode = (S_IRUGO | S_IWUSR)
1076 },
1077 .show = i7core_inject_section_show,
1078 .store = i7core_inject_section_store,
1079 }, {
1080 .attr = {
1081 .name = "inject_type",
1082 .mode = (S_IRUGO | S_IWUSR)
1083 },
1084 .show = i7core_inject_type_show,
1085 .store = i7core_inject_type_store,
1086 }, {
1087 .attr = {
1088 .name = "inject_eccmask",
1089 .mode = (S_IRUGO | S_IWUSR)
1090 },
1091 .show = i7core_inject_eccmask_show,
1092 .store = i7core_inject_eccmask_store,
1093 }, {
1094 .attr = {
1095 .name = "inject_addrmatch",
1096 .mode = (S_IRUGO | S_IWUSR)
1097 },
1098 .show = i7core_inject_addrmatch_show,
1099 .store = i7core_inject_addrmatch_store,
1100 }, {
1101 .attr = {
1102 .name = "inject_enable",
1103 .mode = (S_IRUGO | S_IWUSR)
1104 },
1105 .show = i7core_inject_enable_show,
1106 .store = i7core_inject_enable_store,
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001107 }, {
1108 .attr = {
1109 .name = "corrected_error_counts",
1110 .mode = (S_IRUGO | S_IWUSR)
1111 },
1112 .show = i7core_ce_regs_show,
1113 .store = NULL,
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001114 },
1115};
1116
1117/****************************************************************************
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001118 Device initialization routines: put/get, init/exit
1119 ****************************************************************************/
1120
1121/*
1122 * i7core_put_devices 'put' all the devices that we have
1123 * reserved via 'get'
1124 */
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001125static void i7core_put_devices(void)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001126{
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001127 int i, j;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001128
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001129 for (i = 0; i < NUM_SOCKETS; i++)
1130 for (j = 0; j < N_DEVS; j++)
1131 pci_dev_put(pci_devs[j].pdev[i]);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001132}
1133
1134/*
1135 * i7core_get_devices Find and perform 'get' operation on the MCH's
1136 * device/functions we want to reference for this driver
1137 *
1138 * Need to 'get' device 16 func 1 and func 2
1139 */
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001140int i7core_get_onedevice(struct pci_dev **prev, int devno)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001141{
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001142 struct pci_dev *pdev = NULL;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001143 u8 bus = 0;
1144 u8 socket = 0;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001145
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001146 pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
1147 pci_devs[devno].dev_id, *prev);
1148
1149 /*
1150 * On Xeon 55xx, the Intel Quckpath Arch Generic Non-core pci buses
1151 * aren't announced by acpi. So, we need to use a legacy scan probing
1152 * to detect them
1153 */
1154 if (unlikely(!pdev && !devno && !prev)) {
1155 pcibios_scan_specific_bus(254);
1156 pcibios_scan_specific_bus(255);
1157
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001158 pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001159 pci_devs[devno].dev_id, *prev);
1160 }
Mauro Carvalho Chehabd1fd4fb2009-07-10 18:39:53 -03001161
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001162 /*
1163 * On Xeon 55xx, the Intel Quckpath Arch Generic Non-core regs
1164 * is at addr 8086:2c40, instead of 8086:2c41. So, we need
1165 * to probe for the alternate address in case of failure
1166 */
1167 if (pci_devs[devno].dev_id == PCI_DEVICE_ID_INTEL_I7_NOCORE && !pdev)
1168 pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
1169 PCI_DEVICE_ID_INTEL_I7_NOCORE_ALT, *prev);
Mauro Carvalho Chehabd1fd4fb2009-07-10 18:39:53 -03001170
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001171 if (!pdev) {
1172 if (*prev) {
1173 *prev = pdev;
1174 return 0;
Mauro Carvalho Chehabd1fd4fb2009-07-10 18:39:53 -03001175 }
1176
Mauro Carvalho Chehab310cbb72009-07-17 00:09:10 -03001177 /*
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001178 * Dev 3 function 2 only exists on chips with RDIMMs
1179 * so, it is ok to not found it
Mauro Carvalho Chehab310cbb72009-07-17 00:09:10 -03001180 */
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001181 if ((pci_devs[devno].dev == 3) && (pci_devs[devno].func == 2)) {
1182 *prev = pdev;
1183 return 0;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001184 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001185
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001186 i7core_printk(KERN_ERR,
1187 "Device not found: dev %02x.%d PCI ID %04x:%04x\n",
1188 pci_devs[devno].dev, pci_devs[devno].func,
1189 PCI_VENDOR_ID_INTEL, pci_devs[devno].dev_id);
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001190
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001191 /* End of list, leave */
1192 return -ENODEV;
1193 }
1194 bus = pdev->bus->number;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001195
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001196 if (bus == 0x3f)
1197 socket = 0;
1198 else
1199 socket = 255 - bus;
1200
1201 if (socket >= NUM_SOCKETS) {
1202 i7core_printk(KERN_ERR,
1203 "Unexpected socket for "
1204 "dev %02x:%02x.%d PCI ID %04x:%04x\n",
1205 bus, pci_devs[devno].dev, pci_devs[devno].func,
1206 PCI_VENDOR_ID_INTEL, pci_devs[devno].dev_id);
1207 pci_dev_put(pdev);
1208 return -ENODEV;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001209 }
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001210
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001211 if (pci_devs[devno].pdev[socket]) {
1212 i7core_printk(KERN_ERR,
1213 "Duplicated device for "
1214 "dev %02x:%02x.%d PCI ID %04x:%04x\n",
1215 bus, pci_devs[devno].dev, pci_devs[devno].func,
1216 PCI_VENDOR_ID_INTEL, pci_devs[devno].dev_id);
1217 pci_dev_put(pdev);
1218 return -ENODEV;
1219 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001220
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001221 pci_devs[devno].pdev[socket] = pdev;
1222
1223 /* Sanity check */
1224 if (unlikely(PCI_SLOT(pdev->devfn) != pci_devs[devno].dev ||
1225 PCI_FUNC(pdev->devfn) != pci_devs[devno].func)) {
1226 i7core_printk(KERN_ERR,
1227 "Device PCI ID %04x:%04x "
1228 "has dev %02x:%02x.%d instead of dev %02x:%02x.%d\n",
1229 PCI_VENDOR_ID_INTEL, pci_devs[devno].dev_id,
1230 bus, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
1231 bus, pci_devs[devno].dev, pci_devs[devno].func);
1232 return -ENODEV;
1233 }
1234
1235 /* Be sure that the device is enabled */
1236 if (unlikely(pci_enable_device(pdev) < 0)) {
1237 i7core_printk(KERN_ERR,
1238 "Couldn't enable "
1239 "dev %02x:%02x.%d PCI ID %04x:%04x\n",
1240 bus, pci_devs[devno].dev, pci_devs[devno].func,
1241 PCI_VENDOR_ID_INTEL, pci_devs[devno].dev_id);
1242 return -ENODEV;
1243 }
1244
1245 i7core_printk(KERN_INFO,
1246 "Registered socket %d "
1247 "dev %02x:%02x.%d PCI ID %04x:%04x\n",
1248 socket, bus, pci_devs[devno].dev, pci_devs[devno].func,
1249 PCI_VENDOR_ID_INTEL, pci_devs[devno].dev_id);
1250
1251 *prev = pdev;
1252
1253 return 0;
1254}
1255
1256static int i7core_get_devices(void)
1257{
1258 int i;
1259 struct pci_dev *pdev = NULL;
1260
1261 for (i = 0; i < N_DEVS; i++) {
1262 pdev = NULL;
1263 do {
1264 if (i7core_get_onedevice(&pdev, i) < 0) {
1265 i7core_put_devices();
1266 return -ENODEV;
1267 }
1268 } while (pdev);
1269 }
1270 return 0;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001271}
1272
1273static int mci_bind_devs(struct mem_ctl_info *mci)
1274{
1275 struct i7core_pvt *pvt = mci->pvt_info;
1276 struct pci_dev *pdev;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001277 int i, j, func, slot;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001278
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001279 for (i = 0; i < pvt->sockets; i++) {
1280 for (j = 0; j < N_DEVS; j++) {
1281 pdev = pci_devs[j].pdev[i];
1282 if (!pdev)
1283 continue;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001284
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001285 func = PCI_FUNC(pdev->devfn);
1286 slot = PCI_SLOT(pdev->devfn);
1287 if (slot == 3) {
1288 if (unlikely(func > MAX_MCR_FUNC))
1289 goto error;
1290 pvt->pci_mcr[i][func] = pdev;
1291 } else if (likely(slot >= 4 && slot < 4 + NUM_CHANS)) {
1292 if (unlikely(func > MAX_CHAN_FUNC))
1293 goto error;
1294 pvt->pci_ch[i][slot - 4][func] = pdev;
1295 } else if (!slot && !func)
1296 pvt->pci_noncore[i] = pdev;
1297 else
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001298 goto error;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001299
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001300 debugf0("Associated fn %d.%d, dev = %p, socket %d\n",
1301 PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
1302 pdev, i);
1303 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001304 }
Mauro Carvalho Chehabe9bd2e72009-07-09 22:14:35 -03001305
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001306 return 0;
1307
1308error:
1309 i7core_printk(KERN_ERR, "Device %d, function %d "
1310 "is out of the expected range\n",
1311 slot, func);
1312 return -EINVAL;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001313}
1314
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001315/****************************************************************************
1316 Error check routines
1317 ****************************************************************************/
1318
1319/* This function is based on the device 3 function 4 registers as described on:
1320 * Intel Xeon Processor 5500 Series Datasheet Volume 2
1321 * http://www.intel.com/Assets/PDF/datasheet/321322.pdf
1322 * also available at:
1323 * http://www.arrownac.com/manufacturers/intel/s/nehalem/5500-datasheet-v2.pdf
1324 */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001325static void check_mc_test_err(struct mem_ctl_info *mci, u8 socket)
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001326{
1327 struct i7core_pvt *pvt = mci->pvt_info;
1328 u32 rcv1, rcv0;
1329 int new0, new1, new2;
1330
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001331 if (!pvt->pci_mcr[socket][4]) {
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001332 debugf0("%s MCR registers not found\n",__func__);
1333 return;
1334 }
1335
1336 /* Corrected error reads */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001337 pci_read_config_dword(pvt->pci_mcr[socket][4], MC_TEST_ERR_RCV1, &rcv1);
1338 pci_read_config_dword(pvt->pci_mcr[socket][4], MC_TEST_ERR_RCV0, &rcv0);
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001339
1340 /* Store the new values */
1341 new2 = DIMM2_COR_ERR(rcv1);
1342 new1 = DIMM1_COR_ERR(rcv0);
1343 new0 = DIMM0_COR_ERR(rcv0);
1344
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001345#if 0
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001346 debugf2("%s CE rcv1=0x%08x rcv0=0x%08x, %d %d %d\n",
1347 (pvt->ce_count_available ? "UPDATE" : "READ"),
1348 rcv1, rcv0, new0, new1, new2);
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001349#endif
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001350
1351 /* Updates CE counters if it is not the first time here */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001352 if (pvt->ce_count_available[socket]) {
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001353 /* Updates CE counters */
1354 int add0, add1, add2;
1355
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001356 add2 = new2 - pvt->last_ce_count[socket][2];
1357 add1 = new1 - pvt->last_ce_count[socket][1];
1358 add0 = new0 - pvt->last_ce_count[socket][0];
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001359
1360 if (add2 < 0)
1361 add2 += 0x7fff;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001362 pvt->ce_count[socket][2] += add2;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001363
1364 if (add1 < 0)
1365 add1 += 0x7fff;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001366 pvt->ce_count[socket][1] += add1;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001367
1368 if (add0 < 0)
1369 add0 += 0x7fff;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001370 pvt->ce_count[socket][0] += add0;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001371 } else
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001372 pvt->ce_count_available[socket] = 1;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001373
1374 /* Store the new values */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001375 pvt->last_ce_count[socket][2] = new2;
1376 pvt->last_ce_count[socket][1] = new1;
1377 pvt->last_ce_count[socket][0] = new0;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001378}
1379
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001380/*
1381 * According with tables E-11 and E-12 of chapter E.3.3 of Intel 64 and IA-32
1382 * Architectures Software Developer’s Manual Volume 3B.
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001383 * Nehalem are defined as family 0x06, model 0x1a
1384 *
1385 * The MCA registers used here are the following ones:
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001386 * struct mce field MCA Register
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001387 * m->status MSR_IA32_MC8_STATUS
1388 * m->addr MSR_IA32_MC8_ADDR
1389 * m->misc MSR_IA32_MC8_MISC
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001390 * In the case of Nehalem, the error information is masked at .status and .misc
1391 * fields
1392 */
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001393static void i7core_mce_output_error(struct mem_ctl_info *mci,
1394 struct mce *m)
1395{
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001396 char *type, *optype, *err, *msg;
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001397 unsigned long error = m->status & 0x1ff0000l;
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001398 u32 optypenum = (m->status >> 4) & 0x07;
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001399 u32 core_err_cnt = (m->status >> 38) && 0x7fff;
1400 u32 dimm = (m->misc >> 16) & 0x3;
1401 u32 channel = (m->misc >> 18) & 0x3;
1402 u32 syndrome = m->misc >> 32;
1403 u32 errnum = find_first_bit(&error, 32);
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001404
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001405 if (m->mcgstatus & 1)
1406 type = "FATAL";
1407 else
1408 type = "NON_FATAL";
1409
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001410 switch (optypenum) {
1411 case 0:
1412 optype = "generic undef request";
1413 break;
1414 case 1:
1415 optype = "read error";
1416 break;
1417 case 2:
1418 optype = "write error";
1419 break;
1420 case 3:
1421 optype = "addr/cmd error";
1422 break;
1423 case 4:
1424 optype = "scrubbing error";
1425 break;
1426 default:
1427 optype = "reserved";
1428 break;
1429 }
1430
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001431 switch (errnum) {
1432 case 16:
1433 err = "read ECC error";
1434 break;
1435 case 17:
1436 err = "RAS ECC error";
1437 break;
1438 case 18:
1439 err = "write parity error";
1440 break;
1441 case 19:
1442 err = "redundacy loss";
1443 break;
1444 case 20:
1445 err = "reserved";
1446 break;
1447 case 21:
1448 err = "memory range error";
1449 break;
1450 case 22:
1451 err = "RTID out of range";
1452 break;
1453 case 23:
1454 err = "address parity error";
1455 break;
1456 case 24:
1457 err = "byte enable parity error";
1458 break;
1459 default:
1460 err = "unknown";
1461 }
1462
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001463 /* FIXME: should convert addr into bank and rank information */
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001464 msg = kasprintf(GFP_ATOMIC,
Mauro Carvalho Chehab3a7dde72009-07-18 12:20:04 -03001465 "%s (addr = 0x%08llx, socket=%d, Dimm=%d, Channel=%d, "
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001466 "syndrome=0x%08x, count=%d, Err=%08llx:%08llx (%s: %s))\n",
Mauro Carvalho Chehab3a7dde72009-07-18 12:20:04 -03001467 type, (long long) m->addr, m->cpu, dimm, channel,
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001468 syndrome, core_err_cnt, (long long)m->status,
1469 (long long)m->misc, optype, err);
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001470
1471 debugf0("%s", msg);
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001472
1473 /* Call the helper to output message */
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001474 edac_mc_handle_fbd_ue(mci, 0 /* FIXME: should be rank here */,
1475 0, 0 /* FIXME: should be channel here */, msg);
1476
1477 kfree(msg);
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001478}
1479
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001480/*
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001481 * i7core_check_error Retrieve and process errors reported by the
1482 * hardware. Called by the Core module.
1483 */
1484static void i7core_check_error(struct mem_ctl_info *mci)
1485{
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001486 struct i7core_pvt *pvt = mci->pvt_info;
1487 int i;
1488 unsigned count = 0;
1489 struct mce *m = NULL;
1490 unsigned long flags;
1491
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001492 /* Copy all mce errors into a temporary buffer */
1493 spin_lock_irqsave(&pvt->mce_lock, flags);
1494 if (pvt->mce_count) {
1495 m = kmalloc(sizeof(*m) * pvt->mce_count, GFP_ATOMIC);
1496 if (m) {
1497 count = pvt->mce_count;
1498 memcpy(m, &pvt->mce_entry, sizeof(*m) * count);
1499 }
1500 pvt->mce_count = 0;
1501 }
1502 spin_unlock_irqrestore(&pvt->mce_lock, flags);
1503
1504 /* proccess mcelog errors */
1505 for (i = 0; i < count; i++)
1506 i7core_mce_output_error(mci, &m[i]);
1507
1508 kfree(m);
1509
1510 /* check memory count errors */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001511 for (i = 0; i < pvt->sockets; i++)
1512 check_mc_test_err(mci, i);
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001513}
1514
1515/*
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001516 * i7core_mce_check_error Replicates mcelog routine to get errors
1517 * This routine simply queues mcelog errors, and
1518 * return. The error itself should be handled later
1519 * by i7core_check_error.
1520 */
1521static int i7core_mce_check_error(void *priv, struct mce *mce)
1522{
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001523 struct mem_ctl_info *mci = priv;
1524 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001525 unsigned long flags;
1526
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001527 /*
1528 * Just let mcelog handle it if the error is
1529 * outside the memory controller
1530 */
1531 if (((mce->status & 0xffff) >> 7) != 1)
1532 return 0;
1533
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001534 /* Bank 8 registers are the only ones that we know how to handle */
1535 if (mce->bank != 8)
1536 return 0;
1537
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001538 spin_lock_irqsave(&pvt->mce_lock, flags);
1539 if (pvt->mce_count < MCE_LOG_LEN) {
1540 memcpy(&pvt->mce_entry[pvt->mce_count], mce, sizeof(*mce));
1541 pvt->mce_count++;
1542 }
1543 spin_unlock_irqrestore(&pvt->mce_lock, flags);
1544
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001545 /* Handle fatal errors immediately */
1546 if (mce->mcgstatus & 1)
1547 i7core_check_error(mci);
1548
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001549 /* Advice mcelog that the error were handled */
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001550 return 1;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001551}
1552
1553/*
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001554 * i7core_probe Probe for ONE instance of device to see if it is
1555 * present.
1556 * return:
1557 * 0 for FOUND a device
1558 * < 0 for error code
1559 */
1560static int __devinit i7core_probe(struct pci_dev *pdev,
1561 const struct pci_device_id *id)
1562{
1563 struct mem_ctl_info *mci;
1564 struct i7core_pvt *pvt;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001565 int num_channels = 0;
1566 int num_csrows = 0;
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -03001567 int csrow = 0;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001568 int dev_idx = id->driver_data;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001569 int rc, i;
1570 u8 sockets;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001571
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001572 if (unlikely(dev_idx >= ARRAY_SIZE(i7core_devs)))
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001573 return -EINVAL;
1574
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001575 /* get the pci devices we want to reserve for our use */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001576 rc = i7core_get_devices();
1577 if (unlikely(rc < 0))
1578 return rc;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001579
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001580 sockets = 1;
1581 for (i = NUM_SOCKETS - 1; i > 0; i--)
1582 if (pci_devs[0].pdev[i]) {
1583 sockets = i + 1;
1584 break;
1585 }
1586
1587 for (i = 0; i < sockets; i++) {
1588 int channels;
1589 int csrows;
1590
1591 /* Check the number of active and not disabled channels */
1592 rc = i7core_get_active_channels(i, &channels, &csrows);
1593 if (unlikely(rc < 0))
1594 goto fail0;
1595
1596 num_channels += channels;
1597 num_csrows += csrows;
1598 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001599
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001600 /* allocate a new MC control structure */
1601 mci = edac_mc_alloc(sizeof(*pvt), num_csrows, num_channels, 0);
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001602 if (unlikely(!mci)) {
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001603 rc = -ENOMEM;
1604 goto fail0;
1605 }
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001606
1607 debugf0("MC: " __FILE__ ": %s(): mci = %p\n", __func__, mci);
1608
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001609 mci->dev = &pdev->dev; /* record ptr to the generic device */
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001610 pvt = mci->pvt_info;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001611 memset(pvt, 0, sizeof(*pvt));
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001612 pvt->sockets = sockets;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001613 mci->mc_idx = 0;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001614
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001615 /*
1616 * FIXME: how to handle RDDR3 at MCI level? It is possible to have
1617 * Mixed RDDR3/UDDR3 with Nehalem, provided that they are on different
1618 * memory channels
1619 */
1620 mci->mtype_cap = MEM_FLAG_DDR3;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001621 mci->edac_ctl_cap = EDAC_FLAG_NONE;
1622 mci->edac_cap = EDAC_FLAG_NONE;
1623 mci->mod_name = "i7core_edac.c";
1624 mci->mod_ver = I7CORE_REVISION;
1625 mci->ctl_name = i7core_devs[dev_idx].ctl_name;
1626 mci->dev_name = pci_name(pdev);
1627 mci->ctl_page_to_phys = NULL;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001628 mci->mc_driver_sysfs_attributes = i7core_inj_attrs;
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001629 /* Set the function pointer to an actual operation function */
1630 mci->edac_check = i7core_check_error;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001631
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001632 /* Store pci devices at mci for faster access */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001633 rc = mci_bind_devs(mci);
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001634 if (unlikely(rc < 0))
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001635 goto fail1;
1636
1637 /* Get dimm basic config */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001638 for (i = 0; i < sockets; i++)
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -03001639 get_dimm_config(mci, &csrow, i);
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001640
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001641 /* add this new MC control structure to EDAC's list of MCs */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001642 if (unlikely(edac_mc_add_mc(mci))) {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001643 debugf0("MC: " __FILE__
1644 ": %s(): failed edac_mc_add_mc()\n", __func__);
1645 /* FIXME: perhaps some code should go here that disables error
1646 * reporting if we just enabled it
1647 */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001648
1649 rc = -EINVAL;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001650 goto fail1;
1651 }
1652
1653 /* allocating generic PCI control info */
1654 i7core_pci = edac_pci_create_generic_ctl(&pdev->dev, EDAC_MOD_STR);
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001655 if (unlikely(!i7core_pci)) {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001656 printk(KERN_WARNING
1657 "%s(): Unable to create PCI control\n",
1658 __func__);
1659 printk(KERN_WARNING
1660 "%s(): PCI error report via EDAC not setup\n",
1661 __func__);
1662 }
1663
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001664 /* Default error mask is any memory */
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001665 pvt->inject.channel = 0;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001666 pvt->inject.dimm = -1;
1667 pvt->inject.rank = -1;
1668 pvt->inject.bank = -1;
1669 pvt->inject.page = -1;
1670 pvt->inject.col = -1;
1671
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001672 /* Registers on edac_mce in order to receive memory errors */
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001673 pvt->edac_mce.priv = mci;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001674 pvt->edac_mce.check_error = i7core_mce_check_error;
1675 spin_lock_init(&pvt->mce_lock);
1676
1677 rc = edac_mce_register(&pvt->edac_mce);
1678 if (unlikely (rc < 0)) {
1679 debugf0("MC: " __FILE__
1680 ": %s(): failed edac_mce_register()\n", __func__);
1681 goto fail1;
1682 }
1683
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001684 i7core_printk(KERN_INFO, "Driver loaded.\n");
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001685
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001686 return 0;
1687
1688fail1:
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001689 edac_mc_free(mci);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001690
1691fail0:
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001692 i7core_put_devices();
1693 return rc;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001694}
1695
1696/*
1697 * i7core_remove destructor for one instance of device
1698 *
1699 */
1700static void __devexit i7core_remove(struct pci_dev *pdev)
1701{
1702 struct mem_ctl_info *mci;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001703 struct i7core_pvt *pvt;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001704
1705 debugf0(__FILE__ ": %s()\n", __func__);
1706
1707 if (i7core_pci)
1708 edac_pci_release_generic_ctl(i7core_pci);
1709
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001710
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001711 mci = edac_mc_del_mc(&pdev->dev);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001712 if (!mci)
1713 return;
1714
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001715 /* Unregisters on edac_mce in order to receive memory errors */
1716 pvt = mci->pvt_info;
1717 edac_mce_unregister(&pvt->edac_mce);
1718
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001719 /* retrieve references to resources, and free those resources */
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001720 i7core_put_devices();
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001721
1722 edac_mc_free(mci);
1723}
1724
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001725MODULE_DEVICE_TABLE(pci, i7core_pci_tbl);
1726
1727/*
1728 * i7core_driver pci_driver structure for this module
1729 *
1730 */
1731static struct pci_driver i7core_driver = {
1732 .name = "i7core_edac",
1733 .probe = i7core_probe,
1734 .remove = __devexit_p(i7core_remove),
1735 .id_table = i7core_pci_tbl,
1736};
1737
1738/*
1739 * i7core_init Module entry function
1740 * Try to initialize this module for its devices
1741 */
1742static int __init i7core_init(void)
1743{
1744 int pci_rc;
1745
1746 debugf2("MC: " __FILE__ ": %s()\n", __func__);
1747
1748 /* Ensure that the OPSTATE is set correctly for POLL or NMI */
1749 opstate_init();
1750
1751 pci_rc = pci_register_driver(&i7core_driver);
1752
1753 return (pci_rc < 0) ? pci_rc : 0;
1754}
1755
1756/*
1757 * i7core_exit() Module exit function
1758 * Unregister the driver
1759 */
1760static void __exit i7core_exit(void)
1761{
1762 debugf2("MC: " __FILE__ ": %s()\n", __func__);
1763 pci_unregister_driver(&i7core_driver);
1764}
1765
1766module_init(i7core_init);
1767module_exit(i7core_exit);
1768
1769MODULE_LICENSE("GPL");
1770MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
1771MODULE_AUTHOR("Red Hat Inc. (http://www.redhat.com)");
1772MODULE_DESCRIPTION("MC Driver for Intel i7 Core memory controllers - "
1773 I7CORE_REVISION);
1774
1775module_param(edac_op_state, int, 0444);
1776MODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI");