blob: 26205e2efecfd9fb3daabc90126def7c923d54f4 [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
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030041/*
42 * Debug macros
43 */
44#define i7core_printk(level, fmt, arg...) \
45 edac_printk(level, "i7core", fmt, ##arg)
46
47#define i7core_mc_printk(mci, level, fmt, arg...) \
48 edac_mc_chipset_printk(mci, level, "i7core", fmt, ##arg)
49
50/*
51 * i7core Memory Controller Registers
52 */
53
Mauro Carvalho Chehabe9bd2e72009-07-09 22:14:35 -030054 /* OFFSETS for Device 0 Function 0 */
55
56#define MC_CFG_CONTROL 0x90
57
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030058 /* OFFSETS for Device 3 Function 0 */
59
60#define MC_CONTROL 0x48
61#define MC_STATUS 0x4c
62#define MC_MAX_DOD 0x64
63
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -030064/*
65 * OFFSETS for Device 3 Function 4, as inicated on Xeon 5500 datasheet:
66 * http://www.arrownac.com/manufacturers/intel/s/nehalem/5500-datasheet-v2.pdf
67 */
68
69#define MC_TEST_ERR_RCV1 0x60
70 #define DIMM2_COR_ERR(r) ((r) & 0x7fff)
71
72#define MC_TEST_ERR_RCV0 0x64
73 #define DIMM1_COR_ERR(r) (((r) >> 16) & 0x7fff)
74 #define DIMM0_COR_ERR(r) ((r) & 0x7fff)
75
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030076 /* OFFSETS for Devices 4,5 and 6 Function 0 */
77
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -030078#define MC_CHANNEL_DIMM_INIT_PARAMS 0x58
79 #define THREE_DIMMS_PRESENT (1 << 24)
80 #define SINGLE_QUAD_RANK_PRESENT (1 << 23)
81 #define QUAD_RANK_PRESENT (1 << 22)
82 #define REGISTERED_DIMM (1 << 15)
83
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -030084#define MC_CHANNEL_MAPPER 0x60
85 #define RDLCH(r, ch) ((((r) >> (3 + (ch * 6))) & 0x07) - 1)
86 #define WRLCH(r, ch) ((((r) >> (ch * 6)) & 0x07) - 1)
87
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -030088#define MC_CHANNEL_RANK_PRESENT 0x7c
89 #define RANK_PRESENT_MASK 0xffff
90
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030091#define MC_CHANNEL_ADDR_MATCH 0xf0
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -030092#define MC_CHANNEL_ERROR_MASK 0xf8
93#define MC_CHANNEL_ERROR_INJECT 0xfc
94 #define INJECT_ADDR_PARITY 0x10
95 #define INJECT_ECC 0x08
96 #define MASK_CACHELINE 0x06
97 #define MASK_FULL_CACHELINE 0x06
98 #define MASK_MSB32_CACHELINE 0x04
99 #define MASK_LSB32_CACHELINE 0x02
100 #define NO_MASK_CACHELINE 0x00
101 #define REPEAT_EN 0x01
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300102
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300103 /* OFFSETS for Devices 4,5 and 6 Function 1 */
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -0300104
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300105#define MC_DOD_CH_DIMM0 0x48
106#define MC_DOD_CH_DIMM1 0x4c
107#define MC_DOD_CH_DIMM2 0x50
108 #define RANKOFFSET_MASK ((1 << 12) | (1 << 11) | (1 << 10))
109 #define RANKOFFSET(x) ((x & RANKOFFSET_MASK) >> 10)
110 #define DIMM_PRESENT_MASK (1 << 9)
111 #define DIMM_PRESENT(x) (((x) & DIMM_PRESENT_MASK) >> 9)
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300112 #define MC_DOD_NUMBANK_MASK ((1 << 8) | (1 << 7))
113 #define MC_DOD_NUMBANK(x) (((x) & MC_DOD_NUMBANK_MASK) >> 7)
114 #define MC_DOD_NUMRANK_MASK ((1 << 6) | (1 << 5))
115 #define MC_DOD_NUMRANK(x) (((x) & MC_DOD_NUMRANK_MASK) >> 5)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300116 #define MC_DOD_NUMROW_MASK ((1 << 4) | (1 << 3) | (1 << 2))
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300117 #define MC_DOD_NUMROW(x) (((x) & MC_DOD_NUMROW_MASK) >> 2)
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300118 #define MC_DOD_NUMCOL_MASK 3
119 #define MC_DOD_NUMCOL(x) ((x) & MC_DOD_NUMCOL_MASK)
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300120
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300121#define MC_RANK_PRESENT 0x7c
122
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300123#define MC_SAG_CH_0 0x80
124#define MC_SAG_CH_1 0x84
125#define MC_SAG_CH_2 0x88
126#define MC_SAG_CH_3 0x8c
127#define MC_SAG_CH_4 0x90
128#define MC_SAG_CH_5 0x94
129#define MC_SAG_CH_6 0x98
130#define MC_SAG_CH_7 0x9c
131
132#define MC_RIR_LIMIT_CH_0 0x40
133#define MC_RIR_LIMIT_CH_1 0x44
134#define MC_RIR_LIMIT_CH_2 0x48
135#define MC_RIR_LIMIT_CH_3 0x4C
136#define MC_RIR_LIMIT_CH_4 0x50
137#define MC_RIR_LIMIT_CH_5 0x54
138#define MC_RIR_LIMIT_CH_6 0x58
139#define MC_RIR_LIMIT_CH_7 0x5C
140#define MC_RIR_LIMIT_MASK ((1 << 10) - 1)
141
142#define MC_RIR_WAY_CH 0x80
143 #define MC_RIR_WAY_OFFSET_MASK (((1 << 14) - 1) & ~0x7)
144 #define MC_RIR_WAY_RANK_MASK 0x7
145
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300146/*
147 * i7core structs
148 */
149
150#define NUM_CHANS 3
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -0300151#define MAX_DIMMS 3 /* Max DIMMS per channel */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300152#define NUM_SOCKETS 2 /* Max number of MC sockets */
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -0300153#define MAX_MCR_FUNC 4
154#define MAX_CHAN_FUNC 3
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300155
156struct i7core_info {
157 u32 mc_control;
158 u32 mc_status;
159 u32 max_dod;
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300160 u32 ch_map;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300161};
162
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300163
164struct i7core_inject {
165 int enable;
166
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300167 u8 socket;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300168 u32 section;
169 u32 type;
170 u32 eccmask;
171
172 /* Error address mask */
173 int channel, dimm, rank, bank, page, col;
174};
175
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300176struct i7core_channel {
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -0300177 u32 ranks;
178 u32 dimms;
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300179};
180
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300181struct pci_id_descr {
182 int dev;
183 int func;
184 int dev_id;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300185 struct pci_dev *pdev[NUM_SOCKETS];
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300186};
187
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300188struct i7core_pvt {
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300189 struct pci_dev *pci_noncore[NUM_SOCKETS];
190 struct pci_dev *pci_mcr[NUM_SOCKETS][MAX_MCR_FUNC + 1];
191 struct pci_dev *pci_ch[NUM_SOCKETS][NUM_CHANS][MAX_CHAN_FUNC + 1];
192
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300193 struct i7core_info info;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300194 struct i7core_inject inject;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300195 struct i7core_channel channel[NUM_SOCKETS][NUM_CHANS];
196
197 int sockets; /* Number of sockets */
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300198 int channels; /* Number of active channels */
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -0300199
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300200 int ce_count_available[NUM_SOCKETS];
201 /* ECC corrected errors counts per dimm */
202 unsigned long ce_count[NUM_SOCKETS][MAX_DIMMS];
203 int last_ce_count[NUM_SOCKETS][MAX_DIMMS];
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -0300204
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -0300205 /* mcelog glue */
206 struct edac_mce edac_mce;
207 struct mce mce_entry[MCE_LOG_LEN];
208 unsigned mce_count;
209 spinlock_t mce_lock;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300210};
211
212/* Device name and register DID (Device ID) */
213struct i7core_dev_info {
214 const char *ctl_name; /* name for this device */
215 u16 fsb_mapping_errors; /* DID for the branchmap,control */
216};
217
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300218#define PCI_DESCR(device, function, device_id) \
219 .dev = (device), \
220 .func = (function), \
221 .dev_id = (device_id)
222
223struct pci_id_descr pci_devs[] = {
224 /* Memory controller */
225 { PCI_DESCR(3, 0, PCI_DEVICE_ID_INTEL_I7_MCR) },
226 { PCI_DESCR(3, 1, PCI_DEVICE_ID_INTEL_I7_MC_TAD) },
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -0300227 { PCI_DESCR(3, 2, PCI_DEVICE_ID_INTEL_I7_MC_RAS) }, /* if RDIMM */
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300228 { PCI_DESCR(3, 4, PCI_DEVICE_ID_INTEL_I7_MC_TEST) },
229
230 /* Channel 0 */
231 { PCI_DESCR(4, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH0_CTRL) },
232 { PCI_DESCR(4, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH0_ADDR) },
233 { PCI_DESCR(4, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH0_RANK) },
234 { PCI_DESCR(4, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH0_TC) },
235
236 /* Channel 1 */
237 { PCI_DESCR(5, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH1_CTRL) },
238 { PCI_DESCR(5, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH1_ADDR) },
239 { PCI_DESCR(5, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH1_RANK) },
240 { PCI_DESCR(5, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH1_TC) },
241
242 /* Channel 2 */
243 { PCI_DESCR(6, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH2_CTRL) },
244 { PCI_DESCR(6, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH2_ADDR) },
245 { PCI_DESCR(6, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH2_RANK) },
246 { PCI_DESCR(6, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH2_TC) },
Mauro Carvalho Chehab310cbb72009-07-17 00:09:10 -0300247
248 /* Generic Non-core registers */
249 /*
250 * This is the PCI device on i7core and on Xeon 35xx (8086:2c41)
251 * On Xeon 55xx, however, it has a different id (8086:2c40). So,
252 * the probing code needs to test for the other address in case of
253 * failure of this one
254 */
255 { PCI_DESCR(0, 0, PCI_DEVICE_ID_INTEL_I7_NOCORE) },
256
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300257};
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300258#define N_DEVS ARRAY_SIZE(pci_devs)
259
260/*
261 * pci_device_id table for which devices we are looking for
262 * This should match the first device at pci_devs table
263 */
264static const struct pci_device_id i7core_pci_tbl[] __devinitdata = {
Mauro Carvalho Chehabd1fd4fb2009-07-10 18:39:53 -0300265 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_X58_HUB_MGMT)},
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300266 {0,} /* 0 terminated list. */
267};
268
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300269
270/* Table of devices attributes supported by this driver */
271static const struct i7core_dev_info i7core_devs[] = {
272 {
273 .ctl_name = "i7 Core",
274 .fsb_mapping_errors = PCI_DEVICE_ID_INTEL_I7_MCR,
275 },
276};
277
278static struct edac_pci_ctl_info *i7core_pci;
279
280/****************************************************************************
281 Anciliary status routines
282 ****************************************************************************/
283
284 /* MC_CONTROL bits */
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300285#define CH_ACTIVE(pvt, ch) ((pvt)->info.mc_control & (1 << (8 + ch)))
286#define ECCx8(pvt) ((pvt)->info.mc_control & (1 << 1))
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300287
288 /* MC_STATUS bits */
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300289#define ECC_ENABLED(pvt) ((pvt)->info.mc_status & (1 << 3))
290#define CH_DISABLED(pvt, ch) ((pvt)->info.mc_status & (1 << ch))
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300291
292 /* MC_MAX_DOD read functions */
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300293static inline int numdimms(u32 dimms)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300294{
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300295 return (dimms & 0x3) + 1;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300296}
297
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300298static inline int numrank(u32 rank)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300299{
300 static int ranks[4] = { 1, 2, 4, -EINVAL };
301
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300302 return ranks[rank & 0x3];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300303}
304
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300305static inline int numbank(u32 bank)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300306{
307 static int banks[4] = { 4, 8, 16, -EINVAL };
308
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300309 return banks[bank & 0x3];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300310}
311
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300312static inline int numrow(u32 row)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300313{
314 static int rows[8] = {
315 1 << 12, 1 << 13, 1 << 14, 1 << 15,
316 1 << 16, -EINVAL, -EINVAL, -EINVAL,
317 };
318
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300319 return rows[row & 0x7];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300320}
321
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300322static inline int numcol(u32 col)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300323{
324 static int cols[8] = {
325 1 << 10, 1 << 11, 1 << 12, -EINVAL,
326 };
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300327 return cols[col & 0x3];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300328}
329
330/****************************************************************************
331 Memory check routines
332 ****************************************************************************/
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300333static struct pci_dev *get_pdev_slot_func(u8 socket, unsigned slot,
334 unsigned func)
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300335{
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300336 int i;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300337
338 for (i = 0; i < N_DEVS; i++) {
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300339 if (!pci_devs[i].pdev[socket])
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300340 continue;
341
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300342 if (PCI_SLOT(pci_devs[i].pdev[socket]->devfn) == slot &&
343 PCI_FUNC(pci_devs[i].pdev[socket]->devfn) == func) {
344 return pci_devs[i].pdev[socket];
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300345 }
346 }
347
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300348 return NULL;
349}
350
Mauro Carvalho Chehabec6df242009-07-18 10:44:30 -0300351/**
352 * i7core_get_active_channels() - gets the number of channels and csrows
353 * @socket: Quick Path Interconnect socket
354 * @channels: Number of channels that will be returned
355 * @csrows: Number of csrows found
356 *
357 * Since EDAC core needs to know in advance the number of available channels
358 * and csrows, in order to allocate memory for csrows/channels, it is needed
359 * to run two similar steps. At the first step, implemented on this function,
360 * it checks the number of csrows/channels present at one socket.
361 * this is used in order to properly allocate the size of mci components.
362 *
363 * It should be noticed that none of the current available datasheets explain
364 * or even mention how csrows are seen by the memory controller. So, we need
365 * to add a fake description for csrows.
366 * So, this driver is attributing one DIMM memory for one csrow.
367 */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300368static int i7core_get_active_channels(u8 socket, unsigned *channels,
369 unsigned *csrows)
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300370{
371 struct pci_dev *pdev = NULL;
372 int i, j;
373 u32 status, control;
374
375 *channels = 0;
376 *csrows = 0;
377
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300378 pdev = get_pdev_slot_func(socket, 3, 0);
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -0300379 if (!pdev) {
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300380 i7core_printk(KERN_ERR, "Couldn't find socket %d fn 3.0!!!\n",
381 socket);
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300382 return -ENODEV;
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -0300383 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300384
385 /* Device 3 function 0 reads */
386 pci_read_config_dword(pdev, MC_STATUS, &status);
387 pci_read_config_dword(pdev, MC_CONTROL, &control);
388
389 for (i = 0; i < NUM_CHANS; i++) {
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300390 u32 dimm_dod[3];
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300391 /* Check if the channel is active */
392 if (!(control & (1 << (8 + i))))
393 continue;
394
395 /* Check if the channel is disabled */
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300396 if (status & (1 << i))
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300397 continue;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300398
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300399 pdev = get_pdev_slot_func(socket, i + 4, 1);
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300400 if (!pdev) {
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300401 i7core_printk(KERN_ERR, "Couldn't find socket %d "
402 "fn %d.%d!!!\n",
403 socket, i + 4, 1);
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300404 return -ENODEV;
405 }
406 /* Devices 4-6 function 1 */
407 pci_read_config_dword(pdev,
408 MC_DOD_CH_DIMM0, &dimm_dod[0]);
409 pci_read_config_dword(pdev,
410 MC_DOD_CH_DIMM1, &dimm_dod[1]);
411 pci_read_config_dword(pdev,
412 MC_DOD_CH_DIMM2, &dimm_dod[2]);
413
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300414 (*channels)++;
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300415
416 for (j = 0; j < 3; j++) {
417 if (!DIMM_PRESENT(dimm_dod[j]))
418 continue;
419 (*csrows)++;
420 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300421 }
422
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -0300423 debugf0("Number of active channels on socket %d: %d\n",
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300424 socket, *channels);
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300425
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300426 return 0;
427}
428
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -0300429static int get_dimm_config(struct mem_ctl_info *mci, int *csrow, u8 socket)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300430{
431 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300432 struct csrow_info *csr;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300433 struct pci_dev *pdev;
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -0300434 int i, j;
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300435 unsigned long last_page = 0;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300436 enum edac_type mode;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300437 enum mem_type mtype;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300438
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300439 /* Get data from the MC register, function 0 */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300440 pdev = pvt->pci_mcr[socket][0];
Mauro Carvalho Chehab7dd69532009-06-22 22:48:30 -0300441 if (!pdev)
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300442 return -ENODEV;
443
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300444 /* Device 3 function 0 reads */
Mauro Carvalho Chehab7dd69532009-06-22 22:48:30 -0300445 pci_read_config_dword(pdev, MC_CONTROL, &pvt->info.mc_control);
446 pci_read_config_dword(pdev, MC_STATUS, &pvt->info.mc_status);
447 pci_read_config_dword(pdev, MC_MAX_DOD, &pvt->info.max_dod);
448 pci_read_config_dword(pdev, MC_CHANNEL_MAPPER, &pvt->info.ch_map);
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300449
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300450 debugf0("QPI %d control=0x%08x status=0x%08x dod=0x%08x map=0x%08x\n",
451 socket, pvt->info.mc_control, pvt->info.mc_status,
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300452 pvt->info.max_dod, pvt->info.ch_map);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300453
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300454 if (ECC_ENABLED(pvt)) {
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300455 debugf0("ECC enabled with x%d SDCC\n", ECCx8(pvt) ? 8 : 4);
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300456 if (ECCx8(pvt))
457 mode = EDAC_S8ECD8ED;
458 else
459 mode = EDAC_S4ECD4ED;
460 } else {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300461 debugf0("ECC disabled\n");
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300462 mode = EDAC_NONE;
463 }
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300464
465 /* FIXME: need to handle the error codes */
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300466 debugf0("DOD Max limits: DIMMS: %d, %d-ranked, %d-banked "
467 "x%x x 0x%x\n",
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300468 numdimms(pvt->info.max_dod),
469 numrank(pvt->info.max_dod >> 2),
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300470 numbank(pvt->info.max_dod >> 4),
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300471 numrow(pvt->info.max_dod >> 6),
472 numcol(pvt->info.max_dod >> 9));
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300473
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300474 for (i = 0; i < NUM_CHANS; i++) {
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300475 u32 data, dimm_dod[3], value[8];
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300476
477 if (!CH_ACTIVE(pvt, i)) {
478 debugf0("Channel %i is not active\n", i);
479 continue;
480 }
481 if (CH_DISABLED(pvt, i)) {
482 debugf0("Channel %i is disabled\n", i);
483 continue;
484 }
485
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300486 /* Devices 4-6 function 0 */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300487 pci_read_config_dword(pvt->pci_ch[socket][i][0],
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300488 MC_CHANNEL_DIMM_INIT_PARAMS, &data);
489
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300490 pvt->channel[socket][i].ranks = (data & QUAD_RANK_PRESENT) ?
491 4 : 2;
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300492
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300493 if (data & REGISTERED_DIMM)
494 mtype = MEM_RDDR3;
495 else
496 mtype = MEM_DDR3;
497#if 0
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300498 if (data & THREE_DIMMS_PRESENT)
499 pvt->channel[i].dimms = 3;
500 else if (data & SINGLE_QUAD_RANK_PRESENT)
501 pvt->channel[i].dimms = 1;
502 else
503 pvt->channel[i].dimms = 2;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300504#endif
505
506 /* Devices 4-6 function 1 */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300507 pci_read_config_dword(pvt->pci_ch[socket][i][1],
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300508 MC_DOD_CH_DIMM0, &dimm_dod[0]);
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300509 pci_read_config_dword(pvt->pci_ch[socket][i][1],
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300510 MC_DOD_CH_DIMM1, &dimm_dod[1]);
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300511 pci_read_config_dword(pvt->pci_ch[socket][i][1],
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300512 MC_DOD_CH_DIMM2, &dimm_dod[2]);
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300513
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300514 debugf0("Ch%d phy rd%d, wr%d (0x%08x): "
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300515 "%d ranks, %cDIMMs\n",
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300516 i,
517 RDLCH(pvt->info.ch_map, i), WRLCH(pvt->info.ch_map, i),
518 data,
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300519 pvt->channel[socket][i].ranks,
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300520 (data & REGISTERED_DIMM) ? 'R' : 'U');
Mauro Carvalho Chehab7dd69532009-06-22 22:48:30 -0300521
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300522 for (j = 0; j < 3; j++) {
523 u32 banks, ranks, rows, cols;
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300524 u32 size, npages;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300525
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300526 if (!DIMM_PRESENT(dimm_dod[j]))
527 continue;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300528
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300529 banks = numbank(MC_DOD_NUMBANK(dimm_dod[j]));
530 ranks = numrank(MC_DOD_NUMRANK(dimm_dod[j]));
531 rows = numrow(MC_DOD_NUMROW(dimm_dod[j]));
532 cols = numcol(MC_DOD_NUMCOL(dimm_dod[j]));
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300533
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300534 /* DDR3 has 8 I/O banks */
535 size = (rows * cols * banks * ranks) >> (20 - 3);
536
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300537 pvt->channel[socket][i].dimms++;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300538
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300539 debugf0("\tdimm %d %d Mb offset: %x, "
540 "bank: %d, rank: %d, row: %#x, col: %#x\n",
541 j, size,
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300542 RANKOFFSET(dimm_dod[j]),
543 banks, ranks, rows, cols);
544
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300545#if PAGE_SHIFT > 20
546 npages = size >> (PAGE_SHIFT - 20);
547#else
548 npages = size << (20 - PAGE_SHIFT);
549#endif
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300550
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -0300551 csr = &mci->csrows[*csrow];
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300552 csr->first_page = last_page + 1;
553 last_page += npages;
554 csr->last_page = last_page;
555 csr->nr_pages = npages;
556
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300557 csr->page_mask = 0;
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300558 csr->grain = 8;
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -0300559 csr->csrow_idx = *csrow;
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300560 csr->nr_channels = 1;
561
562 csr->channels[0].chan_idx = i;
563 csr->channels[0].ce_count = 0;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300564
565 switch (banks) {
566 case 4:
567 csr->dtype = DEV_X4;
568 break;
569 case 8:
570 csr->dtype = DEV_X8;
571 break;
572 case 16:
573 csr->dtype = DEV_X16;
574 break;
575 default:
576 csr->dtype = DEV_UNKNOWN;
577 }
578
579 csr->edac_mode = mode;
580 csr->mtype = mtype;
581
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -0300582 (*csrow)++;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300583 }
584
585 pci_read_config_dword(pdev, MC_SAG_CH_0, &value[0]);
586 pci_read_config_dword(pdev, MC_SAG_CH_1, &value[1]);
587 pci_read_config_dword(pdev, MC_SAG_CH_2, &value[2]);
588 pci_read_config_dword(pdev, MC_SAG_CH_3, &value[3]);
589 pci_read_config_dword(pdev, MC_SAG_CH_4, &value[4]);
590 pci_read_config_dword(pdev, MC_SAG_CH_5, &value[5]);
591 pci_read_config_dword(pdev, MC_SAG_CH_6, &value[6]);
592 pci_read_config_dword(pdev, MC_SAG_CH_7, &value[7]);
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300593 debugf1("\t[%i] DIVBY3\tREMOVED\tOFFSET\n", i);
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300594 for (j = 0; j < 8; j++)
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300595 debugf1("\t\t%#x\t%#x\t%#x\n",
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300596 (value[j] >> 27) & 0x1,
597 (value[j] >> 24) & 0x7,
598 (value[j] && ((1 << 24) - 1)));
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300599 }
600
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300601 return 0;
602}
603
604/****************************************************************************
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300605 Error insertion routines
606 ****************************************************************************/
607
608/* The i7core has independent error injection features per channel.
609 However, to have a simpler code, we don't allow enabling error injection
610 on more than one channel.
611 Also, since a change at an inject parameter will be applied only at enable,
612 we're disabling error injection on all write calls to the sysfs nodes that
613 controls the error code injection.
614 */
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300615static int disable_inject(struct mem_ctl_info *mci)
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300616{
617 struct i7core_pvt *pvt = mci->pvt_info;
618
619 pvt->inject.enable = 0;
620
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300621 if (!pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0])
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300622 return -ENODEV;
623
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300624 pci_write_config_dword(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0],
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -0300625 MC_CHANNEL_ERROR_INJECT, 0);
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300626
627 return 0;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300628}
629
630/*
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300631 * i7core inject inject.socket
632 *
633 * accept and store error injection inject.socket value
634 */
635static ssize_t i7core_inject_socket_store(struct mem_ctl_info *mci,
636 const char *data, size_t count)
637{
638 struct i7core_pvt *pvt = mci->pvt_info;
639 unsigned long value;
640 int rc;
641
642 rc = strict_strtoul(data, 10, &value);
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300643 if ((rc < 0) || (value >= pvt->sockets))
Mauro Carvalho Chehab2068def2009-08-05 19:28:27 -0300644 return -EIO;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300645
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -0300646 pvt->inject.socket = (u32) value;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300647 return count;
648}
649
650static ssize_t i7core_inject_socket_show(struct mem_ctl_info *mci,
651 char *data)
652{
653 struct i7core_pvt *pvt = mci->pvt_info;
654 return sprintf(data, "%d\n", pvt->inject.socket);
655}
656
657/*
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300658 * i7core inject inject.section
659 *
660 * accept and store error injection inject.section value
661 * bit 0 - refers to the lower 32-byte half cacheline
662 * bit 1 - refers to the upper 32-byte half cacheline
663 */
664static ssize_t i7core_inject_section_store(struct mem_ctl_info *mci,
665 const char *data, size_t count)
666{
667 struct i7core_pvt *pvt = mci->pvt_info;
668 unsigned long value;
669 int rc;
670
671 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300672 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300673
674 rc = strict_strtoul(data, 10, &value);
675 if ((rc < 0) || (value > 3))
Mauro Carvalho Chehab2068def2009-08-05 19:28:27 -0300676 return -EIO;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300677
678 pvt->inject.section = (u32) value;
679 return count;
680}
681
682static ssize_t i7core_inject_section_show(struct mem_ctl_info *mci,
683 char *data)
684{
685 struct i7core_pvt *pvt = mci->pvt_info;
686 return sprintf(data, "0x%08x\n", pvt->inject.section);
687}
688
689/*
690 * i7core inject.type
691 *
692 * accept and store error injection inject.section value
693 * bit 0 - repeat enable - Enable error repetition
694 * bit 1 - inject ECC error
695 * bit 2 - inject parity error
696 */
697static ssize_t i7core_inject_type_store(struct mem_ctl_info *mci,
698 const char *data, size_t count)
699{
700 struct i7core_pvt *pvt = mci->pvt_info;
701 unsigned long value;
702 int rc;
703
704 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300705 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300706
707 rc = strict_strtoul(data, 10, &value);
708 if ((rc < 0) || (value > 7))
Mauro Carvalho Chehab2068def2009-08-05 19:28:27 -0300709 return -EIO;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300710
711 pvt->inject.type = (u32) value;
712 return count;
713}
714
715static ssize_t i7core_inject_type_show(struct mem_ctl_info *mci,
716 char *data)
717{
718 struct i7core_pvt *pvt = mci->pvt_info;
719 return sprintf(data, "0x%08x\n", pvt->inject.type);
720}
721
722/*
723 * i7core_inject_inject.eccmask_store
724 *
725 * The type of error (UE/CE) will depend on the inject.eccmask value:
726 * Any bits set to a 1 will flip the corresponding ECC bit
727 * Correctable errors can be injected by flipping 1 bit or the bits within
728 * a symbol pair (2 consecutive aligned 8-bit pairs - i.e. 7:0 and 15:8 or
729 * 23:16 and 31:24). Flipping bits in two symbol pairs will cause an
730 * uncorrectable error to be injected.
731 */
732static ssize_t i7core_inject_eccmask_store(struct mem_ctl_info *mci,
733 const char *data, size_t count)
734{
735 struct i7core_pvt *pvt = mci->pvt_info;
736 unsigned long value;
737 int rc;
738
739 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300740 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300741
742 rc = strict_strtoul(data, 10, &value);
743 if (rc < 0)
Mauro Carvalho Chehab2068def2009-08-05 19:28:27 -0300744 return -EIO;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300745
746 pvt->inject.eccmask = (u32) value;
747 return count;
748}
749
750static ssize_t i7core_inject_eccmask_show(struct mem_ctl_info *mci,
751 char *data)
752{
753 struct i7core_pvt *pvt = mci->pvt_info;
754 return sprintf(data, "0x%08x\n", pvt->inject.eccmask);
755}
756
757/*
758 * i7core_addrmatch
759 *
760 * The type of error (UE/CE) will depend on the inject.eccmask value:
761 * Any bits set to a 1 will flip the corresponding ECC bit
762 * Correctable errors can be injected by flipping 1 bit or the bits within
763 * a symbol pair (2 consecutive aligned 8-bit pairs - i.e. 7:0 and 15:8 or
764 * 23:16 and 31:24). Flipping bits in two symbol pairs will cause an
765 * uncorrectable error to be injected.
766 */
767static ssize_t i7core_inject_addrmatch_store(struct mem_ctl_info *mci,
768 const char *data, size_t count)
769{
770 struct i7core_pvt *pvt = mci->pvt_info;
771 char *cmd, *val;
772 long value;
773 int rc;
774
775 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300776 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300777
778 do {
779 cmd = strsep((char **) &data, ":");
780 if (!cmd)
781 break;
782 val = strsep((char **) &data, " \n\t");
783 if (!val)
784 return cmd - data;
785
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300786 if (!strcasecmp(val, "any"))
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300787 value = -1;
788 else {
789 rc = strict_strtol(val, 10, &value);
790 if ((rc < 0) || (value < 0))
791 return cmd - data;
792 }
793
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300794 if (!strcasecmp(cmd, "channel")) {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300795 if (value < 3)
796 pvt->inject.channel = value;
797 else
798 return cmd - data;
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300799 } else if (!strcasecmp(cmd, "dimm")) {
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300800 if (value < 3)
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300801 pvt->inject.dimm = value;
802 else
803 return cmd - data;
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300804 } else if (!strcasecmp(cmd, "rank")) {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300805 if (value < 4)
806 pvt->inject.rank = value;
807 else
808 return cmd - data;
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300809 } else if (!strcasecmp(cmd, "bank")) {
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300810 if (value < 32)
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300811 pvt->inject.bank = value;
812 else
813 return cmd - data;
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300814 } else if (!strcasecmp(cmd, "page")) {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300815 if (value <= 0xffff)
816 pvt->inject.page = value;
817 else
818 return cmd - data;
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300819 } else if (!strcasecmp(cmd, "col") ||
820 !strcasecmp(cmd, "column")) {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300821 if (value <= 0x3fff)
822 pvt->inject.col = value;
823 else
824 return cmd - data;
825 }
826 } while (1);
827
828 return count;
829}
830
831static ssize_t i7core_inject_addrmatch_show(struct mem_ctl_info *mci,
832 char *data)
833{
834 struct i7core_pvt *pvt = mci->pvt_info;
835 char channel[4], dimm[4], bank[4], rank[4], page[7], col[7];
836
837 if (pvt->inject.channel < 0)
838 sprintf(channel, "any");
839 else
840 sprintf(channel, "%d", pvt->inject.channel);
841 if (pvt->inject.dimm < 0)
842 sprintf(dimm, "any");
843 else
844 sprintf(dimm, "%d", pvt->inject.dimm);
845 if (pvt->inject.bank < 0)
846 sprintf(bank, "any");
847 else
848 sprintf(bank, "%d", pvt->inject.bank);
849 if (pvt->inject.rank < 0)
850 sprintf(rank, "any");
851 else
852 sprintf(rank, "%d", pvt->inject.rank);
853 if (pvt->inject.page < 0)
854 sprintf(page, "any");
855 else
856 sprintf(page, "0x%04x", pvt->inject.page);
857 if (pvt->inject.col < 0)
858 sprintf(col, "any");
859 else
860 sprintf(col, "0x%04x", pvt->inject.col);
861
862 return sprintf(data, "channel: %s\ndimm: %s\nbank: %s\n"
863 "rank: %s\npage: %s\ncolumn: %s\n",
864 channel, dimm, bank, rank, page, col);
865}
866
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300867static int write_and_test(struct pci_dev *dev, int where, u32 val)
868{
869 u32 read;
870 int count;
871
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -0300872 debugf0("setting pci %02x:%02x.%x reg=%02x value=%08x\n",
873 dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn),
874 where, val);
875
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300876 for (count = 0; count < 10; count++) {
877 if (count)
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -0300878 msleep(100);
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300879 pci_write_config_dword(dev, where, val);
880 pci_read_config_dword(dev, where, &read);
881
882 if (read == val)
883 return 0;
884 }
885
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -0300886 i7core_printk(KERN_ERR, "Error during set pci %02x:%02x.%x reg=%02x "
887 "write=%08x. Read=%08x\n",
888 dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn),
889 where, val, read);
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300890
891 return -EINVAL;
892}
893
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300894/*
895 * This routine prepares the Memory Controller for error injection.
896 * The error will be injected when some process tries to write to the
897 * memory that matches the given criteria.
898 * The criteria can be set in terms of a mask where dimm, rank, bank, page
899 * and col can be specified.
900 * A -1 value for any of the mask items will make the MCU to ignore
901 * that matching criteria for error injection.
902 *
903 * It should be noticed that the error will only happen after a write operation
904 * on a memory that matches the condition. if REPEAT_EN is not enabled at
905 * inject mask, then it will produce just one error. Otherwise, it will repeat
906 * until the injectmask would be cleaned.
907 *
908 * FIXME: This routine assumes that MAXNUMDIMMS value of MC_MAX_DOD
909 * is reliable enough to check if the MC is using the
910 * three channels. However, this is not clear at the datasheet.
911 */
912static ssize_t i7core_inject_enable_store(struct mem_ctl_info *mci,
913 const char *data, size_t count)
914{
915 struct i7core_pvt *pvt = mci->pvt_info;
916 u32 injectmask;
917 u64 mask = 0;
918 int rc;
919 long enable;
920
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300921 if (!pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0])
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300922 return 0;
923
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300924 rc = strict_strtoul(data, 10, &enable);
925 if ((rc < 0))
926 return 0;
927
928 if (enable) {
929 pvt->inject.enable = 1;
930 } else {
931 disable_inject(mci);
932 return count;
933 }
934
935 /* Sets pvt->inject.dimm mask */
936 if (pvt->inject.dimm < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300937 mask |= 1L << 41;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300938 else {
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300939 if (pvt->channel[pvt->inject.socket][pvt->inject.channel].dimms > 2)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300940 mask |= (pvt->inject.dimm & 0x3L) << 35;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300941 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300942 mask |= (pvt->inject.dimm & 0x1L) << 36;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300943 }
944
945 /* Sets pvt->inject.rank mask */
946 if (pvt->inject.rank < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300947 mask |= 1L << 40;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300948 else {
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300949 if (pvt->channel[pvt->inject.socket][pvt->inject.channel].dimms > 2)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300950 mask |= (pvt->inject.rank & 0x1L) << 34;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300951 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300952 mask |= (pvt->inject.rank & 0x3L) << 34;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300953 }
954
955 /* Sets pvt->inject.bank mask */
956 if (pvt->inject.bank < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300957 mask |= 1L << 39;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300958 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300959 mask |= (pvt->inject.bank & 0x15L) << 30;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300960
961 /* Sets pvt->inject.page mask */
962 if (pvt->inject.page < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300963 mask |= 1L << 38;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300964 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300965 mask |= (pvt->inject.page & 0xffffL) << 14;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300966
967 /* Sets pvt->inject.column mask */
968 if (pvt->inject.col < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300969 mask |= 1L << 37;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300970 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300971 mask |= (pvt->inject.col & 0x3fffL);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300972
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300973 /*
974 * bit 0: REPEAT_EN
975 * bits 1-2: MASK_HALF_CACHELINE
976 * bit 3: INJECT_ECC
977 * bit 4: INJECT_ADDR_PARITY
978 */
979
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300980 injectmask = (pvt->inject.type & 1) |
981 (pvt->inject.section & 0x3) << 1 |
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300982 (pvt->inject.type & 0x6) << (3 - 1);
983
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300984 /* Unlock writes to registers - this register is write only */
985 pci_write_config_dword(pvt->pci_noncore[pvt->inject.socket],
986 MC_CFG_CONTROL, 0x2);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300987
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300988 write_and_test(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0],
989 MC_CHANNEL_ADDR_MATCH, mask);
990 write_and_test(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0],
991 MC_CHANNEL_ADDR_MATCH + 4, mask >> 32L);
992
993 write_and_test(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0],
994 MC_CHANNEL_ERROR_MASK, pvt->inject.eccmask);
995
996 write_and_test(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0],
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -0300997 MC_CHANNEL_ERROR_INJECT, injectmask);
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300998
999 /*
1000 * This is something undocumented, based on my tests
1001 * Without writing 8 to this register, errors aren't injected. Not sure
1002 * why.
1003 */
1004 pci_write_config_dword(pvt->pci_noncore[pvt->inject.socket],
1005 MC_CFG_CONTROL, 8);
1006
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001007 debugf0("Error inject addr match 0x%016llx, ecc 0x%08x,"
1008 " inject 0x%08x\n",
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001009 mask, pvt->inject.eccmask, injectmask);
1010
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -03001011
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001012 return count;
1013}
1014
1015static ssize_t i7core_inject_enable_show(struct mem_ctl_info *mci,
1016 char *data)
1017{
1018 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -03001019 u32 injectmask;
1020
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001021 pci_read_config_dword(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0],
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -03001022 MC_CHANNEL_ERROR_INJECT, &injectmask);
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -03001023
1024 debugf0("Inject error read: 0x%018x\n", injectmask);
1025
1026 if (injectmask & 0x0c)
1027 pvt->inject.enable = 1;
1028
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001029 return sprintf(data, "%d\n", pvt->inject.enable);
1030}
1031
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001032static ssize_t i7core_ce_regs_show(struct mem_ctl_info *mci, char *data)
1033{
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001034 unsigned i, count, total = 0;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001035 struct i7core_pvt *pvt = mci->pvt_info;
1036
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001037 for (i = 0; i < pvt->sockets; i++) {
1038 if (!pvt->ce_count_available[i])
1039 count = sprintf(data, "socket 0 data unavailable\n");
1040 else
1041 count = sprintf(data, "socket %d, dimm0: %lu\n"
1042 "dimm1: %lu\ndimm2: %lu\n",
1043 i,
1044 pvt->ce_count[i][0],
1045 pvt->ce_count[i][1],
1046 pvt->ce_count[i][2]);
1047 data += count;
1048 total += count;
1049 }
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001050
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001051 return total;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001052}
1053
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001054/*
1055 * Sysfs struct
1056 */
1057static struct mcidev_sysfs_attribute i7core_inj_attrs[] = {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001058 {
1059 .attr = {
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001060 .name = "inject_socket",
1061 .mode = (S_IRUGO | S_IWUSR)
1062 },
1063 .show = i7core_inject_socket_show,
1064 .store = i7core_inject_socket_store,
1065 }, {
1066 .attr = {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001067 .name = "inject_section",
1068 .mode = (S_IRUGO | S_IWUSR)
1069 },
1070 .show = i7core_inject_section_show,
1071 .store = i7core_inject_section_store,
1072 }, {
1073 .attr = {
1074 .name = "inject_type",
1075 .mode = (S_IRUGO | S_IWUSR)
1076 },
1077 .show = i7core_inject_type_show,
1078 .store = i7core_inject_type_store,
1079 }, {
1080 .attr = {
1081 .name = "inject_eccmask",
1082 .mode = (S_IRUGO | S_IWUSR)
1083 },
1084 .show = i7core_inject_eccmask_show,
1085 .store = i7core_inject_eccmask_store,
1086 }, {
1087 .attr = {
1088 .name = "inject_addrmatch",
1089 .mode = (S_IRUGO | S_IWUSR)
1090 },
1091 .show = i7core_inject_addrmatch_show,
1092 .store = i7core_inject_addrmatch_store,
1093 }, {
1094 .attr = {
1095 .name = "inject_enable",
1096 .mode = (S_IRUGO | S_IWUSR)
1097 },
1098 .show = i7core_inject_enable_show,
1099 .store = i7core_inject_enable_store,
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001100 }, {
1101 .attr = {
1102 .name = "corrected_error_counts",
1103 .mode = (S_IRUGO | S_IWUSR)
1104 },
1105 .show = i7core_ce_regs_show,
1106 .store = NULL,
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001107 },
1108};
1109
1110/****************************************************************************
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001111 Device initialization routines: put/get, init/exit
1112 ****************************************************************************/
1113
1114/*
1115 * i7core_put_devices 'put' all the devices that we have
1116 * reserved via 'get'
1117 */
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001118static void i7core_put_devices(void)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001119{
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001120 int i, j;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001121
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001122 for (i = 0; i < NUM_SOCKETS; i++)
1123 for (j = 0; j < N_DEVS; j++)
1124 pci_dev_put(pci_devs[j].pdev[i]);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001125}
1126
1127/*
1128 * i7core_get_devices Find and perform 'get' operation on the MCH's
1129 * device/functions we want to reference for this driver
1130 *
1131 * Need to 'get' device 16 func 1 and func 2
1132 */
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001133int i7core_get_onedevice(struct pci_dev **prev, int devno)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001134{
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001135 struct pci_dev *pdev = NULL;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001136 u8 bus = 0;
1137 u8 socket = 0;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001138
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001139 pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
1140 pci_devs[devno].dev_id, *prev);
1141
1142 /*
1143 * On Xeon 55xx, the Intel Quckpath Arch Generic Non-core pci buses
1144 * aren't announced by acpi. So, we need to use a legacy scan probing
1145 * to detect them
1146 */
1147 if (unlikely(!pdev && !devno && !prev)) {
1148 pcibios_scan_specific_bus(254);
1149 pcibios_scan_specific_bus(255);
1150
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001151 pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001152 pci_devs[devno].dev_id, *prev);
1153 }
Mauro Carvalho Chehabd1fd4fb2009-07-10 18:39:53 -03001154
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001155 /*
1156 * On Xeon 55xx, the Intel Quckpath Arch Generic Non-core regs
1157 * is at addr 8086:2c40, instead of 8086:2c41. So, we need
1158 * to probe for the alternate address in case of failure
1159 */
1160 if (pci_devs[devno].dev_id == PCI_DEVICE_ID_INTEL_I7_NOCORE && !pdev)
1161 pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
1162 PCI_DEVICE_ID_INTEL_I7_NOCORE_ALT, *prev);
Mauro Carvalho Chehabd1fd4fb2009-07-10 18:39:53 -03001163
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001164 if (!pdev) {
1165 if (*prev) {
1166 *prev = pdev;
1167 return 0;
Mauro Carvalho Chehabd1fd4fb2009-07-10 18:39:53 -03001168 }
1169
Mauro Carvalho Chehab310cbb72009-07-17 00:09:10 -03001170 /*
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001171 * Dev 3 function 2 only exists on chips with RDIMMs
1172 * so, it is ok to not found it
Mauro Carvalho Chehab310cbb72009-07-17 00:09:10 -03001173 */
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001174 if ((pci_devs[devno].dev == 3) && (pci_devs[devno].func == 2)) {
1175 *prev = pdev;
1176 return 0;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001177 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001178
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001179 i7core_printk(KERN_ERR,
1180 "Device not found: dev %02x.%d PCI ID %04x:%04x\n",
1181 pci_devs[devno].dev, pci_devs[devno].func,
1182 PCI_VENDOR_ID_INTEL, pci_devs[devno].dev_id);
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001183
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001184 /* End of list, leave */
1185 return -ENODEV;
1186 }
1187 bus = pdev->bus->number;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001188
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001189 if (bus == 0x3f)
1190 socket = 0;
1191 else
1192 socket = 255 - bus;
1193
1194 if (socket >= NUM_SOCKETS) {
1195 i7core_printk(KERN_ERR,
1196 "Unexpected socket for "
1197 "dev %02x:%02x.%d PCI ID %04x:%04x\n",
1198 bus, pci_devs[devno].dev, pci_devs[devno].func,
1199 PCI_VENDOR_ID_INTEL, pci_devs[devno].dev_id);
1200 pci_dev_put(pdev);
1201 return -ENODEV;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001202 }
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001203
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001204 if (pci_devs[devno].pdev[socket]) {
1205 i7core_printk(KERN_ERR,
1206 "Duplicated device for "
1207 "dev %02x:%02x.%d PCI ID %04x:%04x\n",
1208 bus, pci_devs[devno].dev, pci_devs[devno].func,
1209 PCI_VENDOR_ID_INTEL, pci_devs[devno].dev_id);
1210 pci_dev_put(pdev);
1211 return -ENODEV;
1212 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001213
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001214 pci_devs[devno].pdev[socket] = pdev;
1215
1216 /* Sanity check */
1217 if (unlikely(PCI_SLOT(pdev->devfn) != pci_devs[devno].dev ||
1218 PCI_FUNC(pdev->devfn) != pci_devs[devno].func)) {
1219 i7core_printk(KERN_ERR,
1220 "Device PCI ID %04x:%04x "
1221 "has dev %02x:%02x.%d instead of dev %02x:%02x.%d\n",
1222 PCI_VENDOR_ID_INTEL, pci_devs[devno].dev_id,
1223 bus, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
1224 bus, pci_devs[devno].dev, pci_devs[devno].func);
1225 return -ENODEV;
1226 }
1227
1228 /* Be sure that the device is enabled */
1229 if (unlikely(pci_enable_device(pdev) < 0)) {
1230 i7core_printk(KERN_ERR,
1231 "Couldn't enable "
1232 "dev %02x:%02x.%d PCI ID %04x:%04x\n",
1233 bus, pci_devs[devno].dev, pci_devs[devno].func,
1234 PCI_VENDOR_ID_INTEL, pci_devs[devno].dev_id);
1235 return -ENODEV;
1236 }
1237
1238 i7core_printk(KERN_INFO,
1239 "Registered socket %d "
1240 "dev %02x:%02x.%d PCI ID %04x:%04x\n",
1241 socket, bus, pci_devs[devno].dev, pci_devs[devno].func,
1242 PCI_VENDOR_ID_INTEL, pci_devs[devno].dev_id);
1243
1244 *prev = pdev;
1245
1246 return 0;
1247}
1248
1249static int i7core_get_devices(void)
1250{
1251 int i;
1252 struct pci_dev *pdev = NULL;
1253
1254 for (i = 0; i < N_DEVS; i++) {
1255 pdev = NULL;
1256 do {
1257 if (i7core_get_onedevice(&pdev, i) < 0) {
1258 i7core_put_devices();
1259 return -ENODEV;
1260 }
1261 } while (pdev);
1262 }
1263 return 0;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001264}
1265
1266static int mci_bind_devs(struct mem_ctl_info *mci)
1267{
1268 struct i7core_pvt *pvt = mci->pvt_info;
1269 struct pci_dev *pdev;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001270 int i, j, func, slot;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001271
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001272 for (i = 0; i < pvt->sockets; i++) {
1273 for (j = 0; j < N_DEVS; j++) {
1274 pdev = pci_devs[j].pdev[i];
1275 if (!pdev)
1276 continue;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001277
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001278 func = PCI_FUNC(pdev->devfn);
1279 slot = PCI_SLOT(pdev->devfn);
1280 if (slot == 3) {
1281 if (unlikely(func > MAX_MCR_FUNC))
1282 goto error;
1283 pvt->pci_mcr[i][func] = pdev;
1284 } else if (likely(slot >= 4 && slot < 4 + NUM_CHANS)) {
1285 if (unlikely(func > MAX_CHAN_FUNC))
1286 goto error;
1287 pvt->pci_ch[i][slot - 4][func] = pdev;
1288 } else if (!slot && !func)
1289 pvt->pci_noncore[i] = pdev;
1290 else
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001291 goto error;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001292
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001293 debugf0("Associated fn %d.%d, dev = %p, socket %d\n",
1294 PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
1295 pdev, i);
1296 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001297 }
Mauro Carvalho Chehabe9bd2e72009-07-09 22:14:35 -03001298
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001299 return 0;
1300
1301error:
1302 i7core_printk(KERN_ERR, "Device %d, function %d "
1303 "is out of the expected range\n",
1304 slot, func);
1305 return -EINVAL;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001306}
1307
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001308/****************************************************************************
1309 Error check routines
1310 ****************************************************************************/
1311
1312/* This function is based on the device 3 function 4 registers as described on:
1313 * Intel Xeon Processor 5500 Series Datasheet Volume 2
1314 * http://www.intel.com/Assets/PDF/datasheet/321322.pdf
1315 * also available at:
1316 * http://www.arrownac.com/manufacturers/intel/s/nehalem/5500-datasheet-v2.pdf
1317 */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001318static void check_mc_test_err(struct mem_ctl_info *mci, u8 socket)
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001319{
1320 struct i7core_pvt *pvt = mci->pvt_info;
1321 u32 rcv1, rcv0;
1322 int new0, new1, new2;
1323
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001324 if (!pvt->pci_mcr[socket][4]) {
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -03001325 debugf0("%s MCR registers not found\n", __func__);
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001326 return;
1327 }
1328
1329 /* Corrected error reads */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001330 pci_read_config_dword(pvt->pci_mcr[socket][4], MC_TEST_ERR_RCV1, &rcv1);
1331 pci_read_config_dword(pvt->pci_mcr[socket][4], MC_TEST_ERR_RCV0, &rcv0);
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001332
1333 /* Store the new values */
1334 new2 = DIMM2_COR_ERR(rcv1);
1335 new1 = DIMM1_COR_ERR(rcv0);
1336 new0 = DIMM0_COR_ERR(rcv0);
1337
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001338#if 0
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001339 debugf2("%s CE rcv1=0x%08x rcv0=0x%08x, %d %d %d\n",
1340 (pvt->ce_count_available ? "UPDATE" : "READ"),
1341 rcv1, rcv0, new0, new1, new2);
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001342#endif
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001343
1344 /* Updates CE counters if it is not the first time here */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001345 if (pvt->ce_count_available[socket]) {
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001346 /* Updates CE counters */
1347 int add0, add1, add2;
1348
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001349 add2 = new2 - pvt->last_ce_count[socket][2];
1350 add1 = new1 - pvt->last_ce_count[socket][1];
1351 add0 = new0 - pvt->last_ce_count[socket][0];
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001352
1353 if (add2 < 0)
1354 add2 += 0x7fff;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001355 pvt->ce_count[socket][2] += add2;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001356
1357 if (add1 < 0)
1358 add1 += 0x7fff;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001359 pvt->ce_count[socket][1] += add1;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001360
1361 if (add0 < 0)
1362 add0 += 0x7fff;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001363 pvt->ce_count[socket][0] += add0;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001364 } else
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001365 pvt->ce_count_available[socket] = 1;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001366
1367 /* Store the new values */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001368 pvt->last_ce_count[socket][2] = new2;
1369 pvt->last_ce_count[socket][1] = new1;
1370 pvt->last_ce_count[socket][0] = new0;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001371}
1372
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001373/*
1374 * According with tables E-11 and E-12 of chapter E.3.3 of Intel 64 and IA-32
1375 * Architectures Software Developer’s Manual Volume 3B.
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001376 * Nehalem are defined as family 0x06, model 0x1a
1377 *
1378 * The MCA registers used here are the following ones:
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001379 * struct mce field MCA Register
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001380 * m->status MSR_IA32_MC8_STATUS
1381 * m->addr MSR_IA32_MC8_ADDR
1382 * m->misc MSR_IA32_MC8_MISC
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001383 * In the case of Nehalem, the error information is masked at .status and .misc
1384 * fields
1385 */
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001386static void i7core_mce_output_error(struct mem_ctl_info *mci,
1387 struct mce *m)
1388{
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001389 char *type, *optype, *err, *msg;
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001390 unsigned long error = m->status & 0x1ff0000l;
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001391 u32 optypenum = (m->status >> 4) & 0x07;
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001392 u32 core_err_cnt = (m->status >> 38) && 0x7fff;
1393 u32 dimm = (m->misc >> 16) & 0x3;
1394 u32 channel = (m->misc >> 18) & 0x3;
1395 u32 syndrome = m->misc >> 32;
1396 u32 errnum = find_first_bit(&error, 32);
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001397
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001398 if (m->mcgstatus & 1)
1399 type = "FATAL";
1400 else
1401 type = "NON_FATAL";
1402
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001403 switch (optypenum) {
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -03001404 case 0:
1405 optype = "generic undef request";
1406 break;
1407 case 1:
1408 optype = "read error";
1409 break;
1410 case 2:
1411 optype = "write error";
1412 break;
1413 case 3:
1414 optype = "addr/cmd error";
1415 break;
1416 case 4:
1417 optype = "scrubbing error";
1418 break;
1419 default:
1420 optype = "reserved";
1421 break;
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001422 }
1423
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001424 switch (errnum) {
1425 case 16:
1426 err = "read ECC error";
1427 break;
1428 case 17:
1429 err = "RAS ECC error";
1430 break;
1431 case 18:
1432 err = "write parity error";
1433 break;
1434 case 19:
1435 err = "redundacy loss";
1436 break;
1437 case 20:
1438 err = "reserved";
1439 break;
1440 case 21:
1441 err = "memory range error";
1442 break;
1443 case 22:
1444 err = "RTID out of range";
1445 break;
1446 case 23:
1447 err = "address parity error";
1448 break;
1449 case 24:
1450 err = "byte enable parity error";
1451 break;
1452 default:
1453 err = "unknown";
1454 }
1455
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001456 /* FIXME: should convert addr into bank and rank information */
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001457 msg = kasprintf(GFP_ATOMIC,
Mauro Carvalho Chehab3a7dde72009-07-18 12:20:04 -03001458 "%s (addr = 0x%08llx, socket=%d, Dimm=%d, Channel=%d, "
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001459 "syndrome=0x%08x, count=%d, Err=%08llx:%08llx (%s: %s))\n",
Mauro Carvalho Chehab3a7dde72009-07-18 12:20:04 -03001460 type, (long long) m->addr, m->cpu, dimm, channel,
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001461 syndrome, core_err_cnt, (long long)m->status,
1462 (long long)m->misc, optype, err);
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001463
1464 debugf0("%s", msg);
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001465
1466 /* Call the helper to output message */
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001467 edac_mc_handle_fbd_ue(mci, 0 /* FIXME: should be rank here */,
1468 0, 0 /* FIXME: should be channel here */, msg);
1469
1470 kfree(msg);
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001471}
1472
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001473/*
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001474 * i7core_check_error Retrieve and process errors reported by the
1475 * hardware. Called by the Core module.
1476 */
1477static void i7core_check_error(struct mem_ctl_info *mci)
1478{
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001479 struct i7core_pvt *pvt = mci->pvt_info;
1480 int i;
1481 unsigned count = 0;
1482 struct mce *m = NULL;
1483 unsigned long flags;
1484
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001485 /* Copy all mce errors into a temporary buffer */
1486 spin_lock_irqsave(&pvt->mce_lock, flags);
1487 if (pvt->mce_count) {
1488 m = kmalloc(sizeof(*m) * pvt->mce_count, GFP_ATOMIC);
1489 if (m) {
1490 count = pvt->mce_count;
1491 memcpy(m, &pvt->mce_entry, sizeof(*m) * count);
1492 }
1493 pvt->mce_count = 0;
1494 }
1495 spin_unlock_irqrestore(&pvt->mce_lock, flags);
1496
1497 /* proccess mcelog errors */
1498 for (i = 0; i < count; i++)
1499 i7core_mce_output_error(mci, &m[i]);
1500
1501 kfree(m);
1502
1503 /* check memory count errors */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001504 for (i = 0; i < pvt->sockets; i++)
1505 check_mc_test_err(mci, i);
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001506}
1507
1508/*
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001509 * i7core_mce_check_error Replicates mcelog routine to get errors
1510 * This routine simply queues mcelog errors, and
1511 * return. The error itself should be handled later
1512 * by i7core_check_error.
1513 */
1514static int i7core_mce_check_error(void *priv, struct mce *mce)
1515{
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001516 struct mem_ctl_info *mci = priv;
1517 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001518 unsigned long flags;
1519
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001520 /*
1521 * Just let mcelog handle it if the error is
1522 * outside the memory controller
1523 */
1524 if (((mce->status & 0xffff) >> 7) != 1)
1525 return 0;
1526
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001527 /* Bank 8 registers are the only ones that we know how to handle */
1528 if (mce->bank != 8)
1529 return 0;
1530
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001531 spin_lock_irqsave(&pvt->mce_lock, flags);
1532 if (pvt->mce_count < MCE_LOG_LEN) {
1533 memcpy(&pvt->mce_entry[pvt->mce_count], mce, sizeof(*mce));
1534 pvt->mce_count++;
1535 }
1536 spin_unlock_irqrestore(&pvt->mce_lock, flags);
1537
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001538 /* Handle fatal errors immediately */
1539 if (mce->mcgstatus & 1)
1540 i7core_check_error(mci);
1541
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001542 /* Advice mcelog that the error were handled */
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001543 return 1;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001544}
1545
1546/*
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001547 * i7core_probe Probe for ONE instance of device to see if it is
1548 * present.
1549 * return:
1550 * 0 for FOUND a device
1551 * < 0 for error code
1552 */
1553static int __devinit i7core_probe(struct pci_dev *pdev,
1554 const struct pci_device_id *id)
1555{
1556 struct mem_ctl_info *mci;
1557 struct i7core_pvt *pvt;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001558 int num_channels = 0;
1559 int num_csrows = 0;
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -03001560 int csrow = 0;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001561 int dev_idx = id->driver_data;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001562 int rc, i;
1563 u8 sockets;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001564
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001565 if (unlikely(dev_idx >= ARRAY_SIZE(i7core_devs)))
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001566 return -EINVAL;
1567
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001568 /* get the pci devices we want to reserve for our use */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001569 rc = i7core_get_devices();
1570 if (unlikely(rc < 0))
1571 return rc;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001572
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001573 sockets = 1;
1574 for (i = NUM_SOCKETS - 1; i > 0; i--)
1575 if (pci_devs[0].pdev[i]) {
1576 sockets = i + 1;
1577 break;
1578 }
1579
1580 for (i = 0; i < sockets; i++) {
1581 int channels;
1582 int csrows;
1583
1584 /* Check the number of active and not disabled channels */
1585 rc = i7core_get_active_channels(i, &channels, &csrows);
1586 if (unlikely(rc < 0))
1587 goto fail0;
1588
1589 num_channels += channels;
1590 num_csrows += csrows;
1591 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001592
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001593 /* allocate a new MC control structure */
1594 mci = edac_mc_alloc(sizeof(*pvt), num_csrows, num_channels, 0);
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001595 if (unlikely(!mci)) {
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001596 rc = -ENOMEM;
1597 goto fail0;
1598 }
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001599
1600 debugf0("MC: " __FILE__ ": %s(): mci = %p\n", __func__, mci);
1601
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001602 mci->dev = &pdev->dev; /* record ptr to the generic device */
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001603 pvt = mci->pvt_info;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001604 memset(pvt, 0, sizeof(*pvt));
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001605 pvt->sockets = sockets;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001606 mci->mc_idx = 0;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001607
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001608 /*
1609 * FIXME: how to handle RDDR3 at MCI level? It is possible to have
1610 * Mixed RDDR3/UDDR3 with Nehalem, provided that they are on different
1611 * memory channels
1612 */
1613 mci->mtype_cap = MEM_FLAG_DDR3;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001614 mci->edac_ctl_cap = EDAC_FLAG_NONE;
1615 mci->edac_cap = EDAC_FLAG_NONE;
1616 mci->mod_name = "i7core_edac.c";
1617 mci->mod_ver = I7CORE_REVISION;
1618 mci->ctl_name = i7core_devs[dev_idx].ctl_name;
1619 mci->dev_name = pci_name(pdev);
1620 mci->ctl_page_to_phys = NULL;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001621 mci->mc_driver_sysfs_attributes = i7core_inj_attrs;
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001622 /* Set the function pointer to an actual operation function */
1623 mci->edac_check = i7core_check_error;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001624
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001625 /* Store pci devices at mci for faster access */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001626 rc = mci_bind_devs(mci);
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001627 if (unlikely(rc < 0))
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001628 goto fail1;
1629
1630 /* Get dimm basic config */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001631 for (i = 0; i < sockets; i++)
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -03001632 get_dimm_config(mci, &csrow, i);
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001633
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001634 /* add this new MC control structure to EDAC's list of MCs */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001635 if (unlikely(edac_mc_add_mc(mci))) {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001636 debugf0("MC: " __FILE__
1637 ": %s(): failed edac_mc_add_mc()\n", __func__);
1638 /* FIXME: perhaps some code should go here that disables error
1639 * reporting if we just enabled it
1640 */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001641
1642 rc = -EINVAL;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001643 goto fail1;
1644 }
1645
1646 /* allocating generic PCI control info */
1647 i7core_pci = edac_pci_create_generic_ctl(&pdev->dev, EDAC_MOD_STR);
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001648 if (unlikely(!i7core_pci)) {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001649 printk(KERN_WARNING
1650 "%s(): Unable to create PCI control\n",
1651 __func__);
1652 printk(KERN_WARNING
1653 "%s(): PCI error report via EDAC not setup\n",
1654 __func__);
1655 }
1656
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001657 /* Default error mask is any memory */
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001658 pvt->inject.channel = 0;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001659 pvt->inject.dimm = -1;
1660 pvt->inject.rank = -1;
1661 pvt->inject.bank = -1;
1662 pvt->inject.page = -1;
1663 pvt->inject.col = -1;
1664
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001665 /* Registers on edac_mce in order to receive memory errors */
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001666 pvt->edac_mce.priv = mci;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001667 pvt->edac_mce.check_error = i7core_mce_check_error;
1668 spin_lock_init(&pvt->mce_lock);
1669
1670 rc = edac_mce_register(&pvt->edac_mce);
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -03001671 if (unlikely(rc < 0)) {
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001672 debugf0("MC: " __FILE__
1673 ": %s(): failed edac_mce_register()\n", __func__);
1674 goto fail1;
1675 }
1676
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001677 i7core_printk(KERN_INFO, "Driver loaded.\n");
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001678
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001679 return 0;
1680
1681fail1:
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001682 edac_mc_free(mci);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001683
1684fail0:
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001685 i7core_put_devices();
1686 return rc;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001687}
1688
1689/*
1690 * i7core_remove destructor for one instance of device
1691 *
1692 */
1693static void __devexit i7core_remove(struct pci_dev *pdev)
1694{
1695 struct mem_ctl_info *mci;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001696 struct i7core_pvt *pvt;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001697
1698 debugf0(__FILE__ ": %s()\n", __func__);
1699
1700 if (i7core_pci)
1701 edac_pci_release_generic_ctl(i7core_pci);
1702
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001703
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001704 mci = edac_mc_del_mc(&pdev->dev);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001705 if (!mci)
1706 return;
1707
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001708 /* Unregisters on edac_mce in order to receive memory errors */
1709 pvt = mci->pvt_info;
1710 edac_mce_unregister(&pvt->edac_mce);
1711
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001712 /* retrieve references to resources, and free those resources */
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001713 i7core_put_devices();
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001714
1715 edac_mc_free(mci);
1716}
1717
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001718MODULE_DEVICE_TABLE(pci, i7core_pci_tbl);
1719
1720/*
1721 * i7core_driver pci_driver structure for this module
1722 *
1723 */
1724static struct pci_driver i7core_driver = {
1725 .name = "i7core_edac",
1726 .probe = i7core_probe,
1727 .remove = __devexit_p(i7core_remove),
1728 .id_table = i7core_pci_tbl,
1729};
1730
1731/*
1732 * i7core_init Module entry function
1733 * Try to initialize this module for its devices
1734 */
1735static int __init i7core_init(void)
1736{
1737 int pci_rc;
1738
1739 debugf2("MC: " __FILE__ ": %s()\n", __func__);
1740
1741 /* Ensure that the OPSTATE is set correctly for POLL or NMI */
1742 opstate_init();
1743
1744 pci_rc = pci_register_driver(&i7core_driver);
1745
Mauro Carvalho Chehab3ef288a2009-09-02 23:43:33 -03001746 if (pci_rc >= 0)
1747 return 0;
1748
1749 i7core_printk(KERN_ERR, "Failed to register device with error %d.\n",
1750 pci_rc);
1751
1752 return pci_rc;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001753}
1754
1755/*
1756 * i7core_exit() Module exit function
1757 * Unregister the driver
1758 */
1759static void __exit i7core_exit(void)
1760{
1761 debugf2("MC: " __FILE__ ": %s()\n", __func__);
1762 pci_unregister_driver(&i7core_driver);
1763}
1764
1765module_init(i7core_init);
1766module_exit(i7core_exit);
1767
1768MODULE_LICENSE("GPL");
1769MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
1770MODULE_AUTHOR("Red Hat Inc. (http://www.redhat.com)");
1771MODULE_DESCRIPTION("MC Driver for Intel i7 Core memory controllers - "
1772 I7CORE_REVISION);
1773
1774module_param(edac_op_state, int, 0444);
1775MODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI");