blob: 477b62a74dbfee56c2eaabf4756d58a528b90f9d [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>
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -030031#include <linux/smp.h>
Mauro Carvalho Chehab14d2c082009-09-02 23:52:36 -030032#include <asm/processor.h>
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030033
34#include "edac_core.h"
35
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030036/*
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -030037 * This is used for Nehalem-EP and Nehalem-EX devices, where the non-core
38 * registers start at bus 255, and are not reported by BIOS.
39 * We currently find devices with only 2 sockets. In order to support more QPI
40 * Quick Path Interconnect, just increment this number.
41 */
42#define MAX_SOCKET_BUSES 2
43
44
45/*
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030046 * Alter this version for the module when modifications are made
47 */
48#define I7CORE_REVISION " Ver: 1.0.0 " __DATE__
49#define EDAC_MOD_STR "i7core_edac"
50
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030051/*
52 * Debug macros
53 */
54#define i7core_printk(level, fmt, arg...) \
55 edac_printk(level, "i7core", fmt, ##arg)
56
57#define i7core_mc_printk(mci, level, fmt, arg...) \
58 edac_mc_chipset_printk(mci, level, "i7core", fmt, ##arg)
59
60/*
61 * i7core Memory Controller Registers
62 */
63
Mauro Carvalho Chehabe9bd2e72009-07-09 22:14:35 -030064 /* OFFSETS for Device 0 Function 0 */
65
66#define MC_CFG_CONTROL 0x90
67
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030068 /* OFFSETS for Device 3 Function 0 */
69
70#define MC_CONTROL 0x48
71#define MC_STATUS 0x4c
72#define MC_MAX_DOD 0x64
73
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -030074/*
75 * OFFSETS for Device 3 Function 4, as inicated on Xeon 5500 datasheet:
76 * http://www.arrownac.com/manufacturers/intel/s/nehalem/5500-datasheet-v2.pdf
77 */
78
79#define MC_TEST_ERR_RCV1 0x60
80 #define DIMM2_COR_ERR(r) ((r) & 0x7fff)
81
82#define MC_TEST_ERR_RCV0 0x64
83 #define DIMM1_COR_ERR(r) (((r) >> 16) & 0x7fff)
84 #define DIMM0_COR_ERR(r) ((r) & 0x7fff)
85
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -030086/* OFFSETS for Device 3 Function 2, as inicated on Xeon 5500 datasheet */
87#define MC_COR_ECC_CNT_0 0x80
88#define MC_COR_ECC_CNT_1 0x84
89#define MC_COR_ECC_CNT_2 0x88
90#define MC_COR_ECC_CNT_3 0x8c
91#define MC_COR_ECC_CNT_4 0x90
92#define MC_COR_ECC_CNT_5 0x94
93
94#define DIMM_TOP_COR_ERR(r) (((r) >> 16) & 0x7fff)
95#define DIMM_BOT_COR_ERR(r) ((r) & 0x7fff)
96
97
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030098 /* OFFSETS for Devices 4,5 and 6 Function 0 */
99
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300100#define MC_CHANNEL_DIMM_INIT_PARAMS 0x58
101 #define THREE_DIMMS_PRESENT (1 << 24)
102 #define SINGLE_QUAD_RANK_PRESENT (1 << 23)
103 #define QUAD_RANK_PRESENT (1 << 22)
104 #define REGISTERED_DIMM (1 << 15)
105
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300106#define MC_CHANNEL_MAPPER 0x60
107 #define RDLCH(r, ch) ((((r) >> (3 + (ch * 6))) & 0x07) - 1)
108 #define WRLCH(r, ch) ((((r) >> (ch * 6)) & 0x07) - 1)
109
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300110#define MC_CHANNEL_RANK_PRESENT 0x7c
111 #define RANK_PRESENT_MASK 0xffff
112
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300113#define MC_CHANNEL_ADDR_MATCH 0xf0
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300114#define MC_CHANNEL_ERROR_MASK 0xf8
115#define MC_CHANNEL_ERROR_INJECT 0xfc
116 #define INJECT_ADDR_PARITY 0x10
117 #define INJECT_ECC 0x08
118 #define MASK_CACHELINE 0x06
119 #define MASK_FULL_CACHELINE 0x06
120 #define MASK_MSB32_CACHELINE 0x04
121 #define MASK_LSB32_CACHELINE 0x02
122 #define NO_MASK_CACHELINE 0x00
123 #define REPEAT_EN 0x01
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300124
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300125 /* OFFSETS for Devices 4,5 and 6 Function 1 */
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -0300126
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300127#define MC_DOD_CH_DIMM0 0x48
128#define MC_DOD_CH_DIMM1 0x4c
129#define MC_DOD_CH_DIMM2 0x50
130 #define RANKOFFSET_MASK ((1 << 12) | (1 << 11) | (1 << 10))
131 #define RANKOFFSET(x) ((x & RANKOFFSET_MASK) >> 10)
132 #define DIMM_PRESENT_MASK (1 << 9)
133 #define DIMM_PRESENT(x) (((x) & DIMM_PRESENT_MASK) >> 9)
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300134 #define MC_DOD_NUMBANK_MASK ((1 << 8) | (1 << 7))
135 #define MC_DOD_NUMBANK(x) (((x) & MC_DOD_NUMBANK_MASK) >> 7)
136 #define MC_DOD_NUMRANK_MASK ((1 << 6) | (1 << 5))
137 #define MC_DOD_NUMRANK(x) (((x) & MC_DOD_NUMRANK_MASK) >> 5)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300138 #define MC_DOD_NUMROW_MASK ((1 << 4) | (1 << 3) | (1 << 2))
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300139 #define MC_DOD_NUMROW(x) (((x) & MC_DOD_NUMROW_MASK) >> 2)
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300140 #define MC_DOD_NUMCOL_MASK 3
141 #define MC_DOD_NUMCOL(x) ((x) & MC_DOD_NUMCOL_MASK)
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300142
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300143#define MC_RANK_PRESENT 0x7c
144
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300145#define MC_SAG_CH_0 0x80
146#define MC_SAG_CH_1 0x84
147#define MC_SAG_CH_2 0x88
148#define MC_SAG_CH_3 0x8c
149#define MC_SAG_CH_4 0x90
150#define MC_SAG_CH_5 0x94
151#define MC_SAG_CH_6 0x98
152#define MC_SAG_CH_7 0x9c
153
154#define MC_RIR_LIMIT_CH_0 0x40
155#define MC_RIR_LIMIT_CH_1 0x44
156#define MC_RIR_LIMIT_CH_2 0x48
157#define MC_RIR_LIMIT_CH_3 0x4C
158#define MC_RIR_LIMIT_CH_4 0x50
159#define MC_RIR_LIMIT_CH_5 0x54
160#define MC_RIR_LIMIT_CH_6 0x58
161#define MC_RIR_LIMIT_CH_7 0x5C
162#define MC_RIR_LIMIT_MASK ((1 << 10) - 1)
163
164#define MC_RIR_WAY_CH 0x80
165 #define MC_RIR_WAY_OFFSET_MASK (((1 << 14) - 1) & ~0x7)
166 #define MC_RIR_WAY_RANK_MASK 0x7
167
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300168/*
169 * i7core structs
170 */
171
172#define NUM_CHANS 3
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -0300173#define MAX_DIMMS 3 /* Max DIMMS per channel */
174#define MAX_MCR_FUNC 4
175#define MAX_CHAN_FUNC 3
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300176
177struct i7core_info {
178 u32 mc_control;
179 u32 mc_status;
180 u32 max_dod;
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300181 u32 ch_map;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300182};
183
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300184
185struct i7core_inject {
186 int enable;
187
188 u32 section;
189 u32 type;
190 u32 eccmask;
191
192 /* Error address mask */
193 int channel, dimm, rank, bank, page, col;
194};
195
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300196struct i7core_channel {
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -0300197 u32 ranks;
198 u32 dimms;
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300199};
200
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300201struct pci_id_descr {
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300202 int dev;
203 int func;
204 int dev_id;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300205};
206
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300207struct i7core_dev {
208 struct list_head list;
209 u8 socket;
210 struct pci_dev **pdev;
211 struct mem_ctl_info *mci;
212};
213
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300214struct i7core_pvt {
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300215 struct pci_dev *pci_noncore;
216 struct pci_dev *pci_mcr[MAX_MCR_FUNC + 1];
217 struct pci_dev *pci_ch[NUM_CHANS][MAX_CHAN_FUNC + 1];
218
219 struct i7core_dev *i7core_dev;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300220
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300221 struct i7core_info info;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300222 struct i7core_inject inject;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300223 struct i7core_channel channel[NUM_CHANS];
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300224
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300225 int channels; /* Number of active channels */
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -0300226
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300227 int ce_count_available;
228 int csrow_map[NUM_CHANS][MAX_DIMMS];
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -0300229
230 /* ECC corrected errors counts per udimm */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300231 unsigned long udimm_ce_count[MAX_DIMMS];
232 int udimm_last_ce_count[MAX_DIMMS];
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -0300233 /* ECC corrected errors counts per rdimm */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300234 unsigned long rdimm_ce_count[NUM_CHANS][MAX_DIMMS];
235 int rdimm_last_ce_count[NUM_CHANS][MAX_DIMMS];
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -0300236
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300237 unsigned int is_registered;
Mauro Carvalho Chehab14d2c082009-09-02 23:52:36 -0300238
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -0300239 /* mcelog glue */
240 struct edac_mce edac_mce;
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -0300241
242 /* Fifo double buffers */
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -0300243 struct mce mce_entry[MCE_LOG_LEN];
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -0300244 struct mce mce_outentry[MCE_LOG_LEN];
245
246 /* Fifo in/out counters */
247 unsigned mce_in, mce_out;
248
249 /* Count indicator to show errors not got */
250 unsigned mce_overrun;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300251};
252
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300253/* Static vars */
254static LIST_HEAD(i7core_edac_list);
255static DEFINE_MUTEX(i7core_edac_lock);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300256
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300257#define PCI_DESCR(device, function, device_id) \
258 .dev = (device), \
259 .func = (function), \
260 .dev_id = (device_id)
261
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300262struct pci_id_descr pci_dev_descr[] = {
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300263 /* Memory controller */
264 { PCI_DESCR(3, 0, PCI_DEVICE_ID_INTEL_I7_MCR) },
265 { PCI_DESCR(3, 1, PCI_DEVICE_ID_INTEL_I7_MC_TAD) },
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -0300266 { PCI_DESCR(3, 2, PCI_DEVICE_ID_INTEL_I7_MC_RAS) }, /* if RDIMM */
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300267 { PCI_DESCR(3, 4, PCI_DEVICE_ID_INTEL_I7_MC_TEST) },
268
269 /* Channel 0 */
270 { PCI_DESCR(4, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH0_CTRL) },
271 { PCI_DESCR(4, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH0_ADDR) },
272 { PCI_DESCR(4, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH0_RANK) },
273 { PCI_DESCR(4, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH0_TC) },
274
275 /* Channel 1 */
276 { PCI_DESCR(5, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH1_CTRL) },
277 { PCI_DESCR(5, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH1_ADDR) },
278 { PCI_DESCR(5, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH1_RANK) },
279 { PCI_DESCR(5, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH1_TC) },
280
281 /* Channel 2 */
282 { PCI_DESCR(6, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH2_CTRL) },
283 { PCI_DESCR(6, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH2_ADDR) },
284 { PCI_DESCR(6, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH2_RANK) },
285 { PCI_DESCR(6, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH2_TC) },
Mauro Carvalho Chehab310cbb72009-07-17 00:09:10 -0300286
287 /* Generic Non-core registers */
288 /*
289 * This is the PCI device on i7core and on Xeon 35xx (8086:2c41)
290 * On Xeon 55xx, however, it has a different id (8086:2c40). So,
291 * the probing code needs to test for the other address in case of
292 * failure of this one
293 */
294 { PCI_DESCR(0, 0, PCI_DEVICE_ID_INTEL_I7_NOCORE) },
295
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300296};
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300297#define N_DEVS ARRAY_SIZE(pci_dev_descr)
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300298
299/*
300 * pci_device_id table for which devices we are looking for
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300301 */
302static const struct pci_device_id i7core_pci_tbl[] __devinitdata = {
Mauro Carvalho Chehabd1fd4fb2009-07-10 18:39:53 -0300303 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_X58_HUB_MGMT)},
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300304 {0,} /* 0 terminated list. */
305};
306
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300307static struct edac_pci_ctl_info *i7core_pci;
308
309/****************************************************************************
310 Anciliary status routines
311 ****************************************************************************/
312
313 /* MC_CONTROL bits */
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300314#define CH_ACTIVE(pvt, ch) ((pvt)->info.mc_control & (1 << (8 + ch)))
315#define ECCx8(pvt) ((pvt)->info.mc_control & (1 << 1))
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300316
317 /* MC_STATUS bits */
Keith Mannthey61053fd2009-09-02 23:46:59 -0300318#define ECC_ENABLED(pvt) ((pvt)->info.mc_status & (1 << 4))
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300319#define CH_DISABLED(pvt, ch) ((pvt)->info.mc_status & (1 << ch))
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300320
321 /* MC_MAX_DOD read functions */
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300322static inline int numdimms(u32 dimms)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300323{
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300324 return (dimms & 0x3) + 1;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300325}
326
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300327static inline int numrank(u32 rank)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300328{
329 static int ranks[4] = { 1, 2, 4, -EINVAL };
330
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300331 return ranks[rank & 0x3];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300332}
333
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300334static inline int numbank(u32 bank)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300335{
336 static int banks[4] = { 4, 8, 16, -EINVAL };
337
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300338 return banks[bank & 0x3];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300339}
340
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300341static inline int numrow(u32 row)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300342{
343 static int rows[8] = {
344 1 << 12, 1 << 13, 1 << 14, 1 << 15,
345 1 << 16, -EINVAL, -EINVAL, -EINVAL,
346 };
347
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300348 return rows[row & 0x7];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300349}
350
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300351static inline int numcol(u32 col)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300352{
353 static int cols[8] = {
354 1 << 10, 1 << 11, 1 << 12, -EINVAL,
355 };
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300356 return cols[col & 0x3];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300357}
358
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300359static struct i7core_dev *get_i7core_dev(u8 socket)
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300360{
361 struct i7core_dev *i7core_dev;
362
363 list_for_each_entry(i7core_dev, &i7core_edac_list, list) {
364 if (i7core_dev->socket == socket)
365 return i7core_dev;
366 }
367
368 return NULL;
369}
370
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300371/****************************************************************************
372 Memory check routines
373 ****************************************************************************/
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300374static struct pci_dev *get_pdev_slot_func(u8 socket, unsigned slot,
375 unsigned func)
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300376{
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300377 struct i7core_dev *i7core_dev = get_i7core_dev(socket);
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300378 int i;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300379
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300380 if (!i7core_dev)
381 return NULL;
382
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300383 for (i = 0; i < N_DEVS; i++) {
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300384 if (!i7core_dev->pdev[i])
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300385 continue;
386
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300387 if (PCI_SLOT(i7core_dev->pdev[i]->devfn) == slot &&
388 PCI_FUNC(i7core_dev->pdev[i]->devfn) == func) {
389 return i7core_dev->pdev[i];
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300390 }
391 }
392
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300393 return NULL;
394}
395
Mauro Carvalho Chehabec6df242009-07-18 10:44:30 -0300396/**
397 * i7core_get_active_channels() - gets the number of channels and csrows
398 * @socket: Quick Path Interconnect socket
399 * @channels: Number of channels that will be returned
400 * @csrows: Number of csrows found
401 *
402 * Since EDAC core needs to know in advance the number of available channels
403 * and csrows, in order to allocate memory for csrows/channels, it is needed
404 * to run two similar steps. At the first step, implemented on this function,
405 * it checks the number of csrows/channels present at one socket.
406 * this is used in order to properly allocate the size of mci components.
407 *
408 * It should be noticed that none of the current available datasheets explain
409 * or even mention how csrows are seen by the memory controller. So, we need
410 * to add a fake description for csrows.
411 * So, this driver is attributing one DIMM memory for one csrow.
412 */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300413static int i7core_get_active_channels(u8 socket, unsigned *channels,
414 unsigned *csrows)
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300415{
416 struct pci_dev *pdev = NULL;
417 int i, j;
418 u32 status, control;
419
420 *channels = 0;
421 *csrows = 0;
422
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300423 pdev = get_pdev_slot_func(socket, 3, 0);
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -0300424 if (!pdev) {
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300425 i7core_printk(KERN_ERR, "Couldn't find socket %d fn 3.0!!!\n",
426 socket);
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300427 return -ENODEV;
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -0300428 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300429
430 /* Device 3 function 0 reads */
431 pci_read_config_dword(pdev, MC_STATUS, &status);
432 pci_read_config_dword(pdev, MC_CONTROL, &control);
433
434 for (i = 0; i < NUM_CHANS; i++) {
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300435 u32 dimm_dod[3];
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300436 /* Check if the channel is active */
437 if (!(control & (1 << (8 + i))))
438 continue;
439
440 /* Check if the channel is disabled */
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300441 if (status & (1 << i))
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300442 continue;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300443
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300444 pdev = get_pdev_slot_func(socket, i + 4, 1);
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300445 if (!pdev) {
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300446 i7core_printk(KERN_ERR, "Couldn't find socket %d "
447 "fn %d.%d!!!\n",
448 socket, i + 4, 1);
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300449 return -ENODEV;
450 }
451 /* Devices 4-6 function 1 */
452 pci_read_config_dword(pdev,
453 MC_DOD_CH_DIMM0, &dimm_dod[0]);
454 pci_read_config_dword(pdev,
455 MC_DOD_CH_DIMM1, &dimm_dod[1]);
456 pci_read_config_dword(pdev,
457 MC_DOD_CH_DIMM2, &dimm_dod[2]);
458
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300459 (*channels)++;
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300460
461 for (j = 0; j < 3; j++) {
462 if (!DIMM_PRESENT(dimm_dod[j]))
463 continue;
464 (*csrows)++;
465 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300466 }
467
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -0300468 debugf0("Number of active channels on socket %d: %d\n",
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300469 socket, *channels);
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300470
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300471 return 0;
472}
473
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300474static int get_dimm_config(struct mem_ctl_info *mci, int *csrow)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300475{
476 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300477 struct csrow_info *csr;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300478 struct pci_dev *pdev;
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -0300479 int i, j;
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300480 unsigned long last_page = 0;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300481 enum edac_type mode;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300482 enum mem_type mtype;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300483
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300484 /* Get data from the MC register, function 0 */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300485 pdev = pvt->pci_mcr[0];
Mauro Carvalho Chehab7dd69532009-06-22 22:48:30 -0300486 if (!pdev)
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300487 return -ENODEV;
488
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300489 /* Device 3 function 0 reads */
Mauro Carvalho Chehab7dd69532009-06-22 22:48:30 -0300490 pci_read_config_dword(pdev, MC_CONTROL, &pvt->info.mc_control);
491 pci_read_config_dword(pdev, MC_STATUS, &pvt->info.mc_status);
492 pci_read_config_dword(pdev, MC_MAX_DOD, &pvt->info.max_dod);
493 pci_read_config_dword(pdev, MC_CHANNEL_MAPPER, &pvt->info.ch_map);
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300494
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300495 debugf0("QPI %d control=0x%08x status=0x%08x dod=0x%08x map=0x%08x\n",
Mauro Carvalho Chehab4af91882009-09-24 09:58:26 -0300496 pvt->i7core_dev->socket, pvt->info.mc_control, pvt->info.mc_status,
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300497 pvt->info.max_dod, pvt->info.ch_map);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300498
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300499 if (ECC_ENABLED(pvt)) {
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300500 debugf0("ECC enabled with x%d SDCC\n", ECCx8(pvt) ? 8 : 4);
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300501 if (ECCx8(pvt))
502 mode = EDAC_S8ECD8ED;
503 else
504 mode = EDAC_S4ECD4ED;
505 } else {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300506 debugf0("ECC disabled\n");
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300507 mode = EDAC_NONE;
508 }
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300509
510 /* FIXME: need to handle the error codes */
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300511 debugf0("DOD Max limits: DIMMS: %d, %d-ranked, %d-banked "
512 "x%x x 0x%x\n",
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300513 numdimms(pvt->info.max_dod),
514 numrank(pvt->info.max_dod >> 2),
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300515 numbank(pvt->info.max_dod >> 4),
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300516 numrow(pvt->info.max_dod >> 6),
517 numcol(pvt->info.max_dod >> 9));
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300518
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300519 for (i = 0; i < NUM_CHANS; i++) {
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300520 u32 data, dimm_dod[3], value[8];
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300521
522 if (!CH_ACTIVE(pvt, i)) {
523 debugf0("Channel %i is not active\n", i);
524 continue;
525 }
526 if (CH_DISABLED(pvt, i)) {
527 debugf0("Channel %i is disabled\n", i);
528 continue;
529 }
530
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300531 /* Devices 4-6 function 0 */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300532 pci_read_config_dword(pvt->pci_ch[i][0],
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300533 MC_CHANNEL_DIMM_INIT_PARAMS, &data);
534
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300535 pvt->channel[i].ranks = (data & QUAD_RANK_PRESENT) ?
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300536 4 : 2;
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300537
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300538 if (data & REGISTERED_DIMM)
539 mtype = MEM_RDDR3;
Mauro Carvalho Chehab14d2c082009-09-02 23:52:36 -0300540 else
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300541 mtype = MEM_DDR3;
542#if 0
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300543 if (data & THREE_DIMMS_PRESENT)
544 pvt->channel[i].dimms = 3;
545 else if (data & SINGLE_QUAD_RANK_PRESENT)
546 pvt->channel[i].dimms = 1;
547 else
548 pvt->channel[i].dimms = 2;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300549#endif
550
551 /* Devices 4-6 function 1 */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300552 pci_read_config_dword(pvt->pci_ch[i][1],
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300553 MC_DOD_CH_DIMM0, &dimm_dod[0]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300554 pci_read_config_dword(pvt->pci_ch[i][1],
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300555 MC_DOD_CH_DIMM1, &dimm_dod[1]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300556 pci_read_config_dword(pvt->pci_ch[i][1],
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300557 MC_DOD_CH_DIMM2, &dimm_dod[2]);
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300558
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300559 debugf0("Ch%d phy rd%d, wr%d (0x%08x): "
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300560 "%d ranks, %cDIMMs\n",
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300561 i,
562 RDLCH(pvt->info.ch_map, i), WRLCH(pvt->info.ch_map, i),
563 data,
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300564 pvt->channel[i].ranks,
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300565 (data & REGISTERED_DIMM) ? 'R' : 'U');
Mauro Carvalho Chehab7dd69532009-06-22 22:48:30 -0300566
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300567 for (j = 0; j < 3; j++) {
568 u32 banks, ranks, rows, cols;
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300569 u32 size, npages;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300570
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300571 if (!DIMM_PRESENT(dimm_dod[j]))
572 continue;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300573
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300574 banks = numbank(MC_DOD_NUMBANK(dimm_dod[j]));
575 ranks = numrank(MC_DOD_NUMRANK(dimm_dod[j]));
576 rows = numrow(MC_DOD_NUMROW(dimm_dod[j]));
577 cols = numcol(MC_DOD_NUMCOL(dimm_dod[j]));
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300578
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300579 /* DDR3 has 8 I/O banks */
580 size = (rows * cols * banks * ranks) >> (20 - 3);
581
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300582 pvt->channel[i].dimms++;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300583
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300584 debugf0("\tdimm %d %d Mb offset: %x, "
585 "bank: %d, rank: %d, row: %#x, col: %#x\n",
586 j, size,
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300587 RANKOFFSET(dimm_dod[j]),
588 banks, ranks, rows, cols);
589
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300590#if PAGE_SHIFT > 20
591 npages = size >> (PAGE_SHIFT - 20);
592#else
593 npages = size << (20 - PAGE_SHIFT);
594#endif
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300595
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -0300596 csr = &mci->csrows[*csrow];
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300597 csr->first_page = last_page + 1;
598 last_page += npages;
599 csr->last_page = last_page;
600 csr->nr_pages = npages;
601
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300602 csr->page_mask = 0;
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300603 csr->grain = 8;
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -0300604 csr->csrow_idx = *csrow;
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300605 csr->nr_channels = 1;
606
607 csr->channels[0].chan_idx = i;
608 csr->channels[0].ce_count = 0;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300609
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300610 pvt->csrow_map[i][j] = *csrow;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -0300611
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300612 switch (banks) {
613 case 4:
614 csr->dtype = DEV_X4;
615 break;
616 case 8:
617 csr->dtype = DEV_X8;
618 break;
619 case 16:
620 csr->dtype = DEV_X16;
621 break;
622 default:
623 csr->dtype = DEV_UNKNOWN;
624 }
625
626 csr->edac_mode = mode;
627 csr->mtype = mtype;
628
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -0300629 (*csrow)++;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300630 }
631
632 pci_read_config_dword(pdev, MC_SAG_CH_0, &value[0]);
633 pci_read_config_dword(pdev, MC_SAG_CH_1, &value[1]);
634 pci_read_config_dword(pdev, MC_SAG_CH_2, &value[2]);
635 pci_read_config_dword(pdev, MC_SAG_CH_3, &value[3]);
636 pci_read_config_dword(pdev, MC_SAG_CH_4, &value[4]);
637 pci_read_config_dword(pdev, MC_SAG_CH_5, &value[5]);
638 pci_read_config_dword(pdev, MC_SAG_CH_6, &value[6]);
639 pci_read_config_dword(pdev, MC_SAG_CH_7, &value[7]);
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300640 debugf1("\t[%i] DIVBY3\tREMOVED\tOFFSET\n", i);
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300641 for (j = 0; j < 8; j++)
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300642 debugf1("\t\t%#x\t%#x\t%#x\n",
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300643 (value[j] >> 27) & 0x1,
644 (value[j] >> 24) & 0x7,
645 (value[j] && ((1 << 24) - 1)));
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300646 }
647
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300648 return 0;
649}
650
651/****************************************************************************
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300652 Error insertion routines
653 ****************************************************************************/
654
655/* The i7core has independent error injection features per channel.
656 However, to have a simpler code, we don't allow enabling error injection
657 on more than one channel.
658 Also, since a change at an inject parameter will be applied only at enable,
659 we're disabling error injection on all write calls to the sysfs nodes that
660 controls the error code injection.
661 */
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300662static int disable_inject(struct mem_ctl_info *mci)
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300663{
664 struct i7core_pvt *pvt = mci->pvt_info;
665
666 pvt->inject.enable = 0;
667
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300668 if (!pvt->pci_ch[pvt->inject.channel][0])
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300669 return -ENODEV;
670
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300671 pci_write_config_dword(pvt->pci_ch[pvt->inject.channel][0],
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -0300672 MC_CHANNEL_ERROR_INJECT, 0);
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300673
674 return 0;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300675}
676
677/*
678 * i7core inject inject.section
679 *
680 * accept and store error injection inject.section value
681 * bit 0 - refers to the lower 32-byte half cacheline
682 * bit 1 - refers to the upper 32-byte half cacheline
683 */
684static ssize_t i7core_inject_section_store(struct mem_ctl_info *mci,
685 const char *data, size_t count)
686{
687 struct i7core_pvt *pvt = mci->pvt_info;
688 unsigned long value;
689 int rc;
690
691 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300692 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300693
694 rc = strict_strtoul(data, 10, &value);
695 if ((rc < 0) || (value > 3))
Mauro Carvalho Chehab2068def2009-08-05 19:28:27 -0300696 return -EIO;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300697
698 pvt->inject.section = (u32) value;
699 return count;
700}
701
702static ssize_t i7core_inject_section_show(struct mem_ctl_info *mci,
703 char *data)
704{
705 struct i7core_pvt *pvt = mci->pvt_info;
706 return sprintf(data, "0x%08x\n", pvt->inject.section);
707}
708
709/*
710 * i7core inject.type
711 *
712 * accept and store error injection inject.section value
713 * bit 0 - repeat enable - Enable error repetition
714 * bit 1 - inject ECC error
715 * bit 2 - inject parity error
716 */
717static ssize_t i7core_inject_type_store(struct mem_ctl_info *mci,
718 const char *data, size_t count)
719{
720 struct i7core_pvt *pvt = mci->pvt_info;
721 unsigned long value;
722 int rc;
723
724 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300725 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300726
727 rc = strict_strtoul(data, 10, &value);
728 if ((rc < 0) || (value > 7))
Mauro Carvalho Chehab2068def2009-08-05 19:28:27 -0300729 return -EIO;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300730
731 pvt->inject.type = (u32) value;
732 return count;
733}
734
735static ssize_t i7core_inject_type_show(struct mem_ctl_info *mci,
736 char *data)
737{
738 struct i7core_pvt *pvt = mci->pvt_info;
739 return sprintf(data, "0x%08x\n", pvt->inject.type);
740}
741
742/*
743 * i7core_inject_inject.eccmask_store
744 *
745 * The type of error (UE/CE) will depend on the inject.eccmask value:
746 * Any bits set to a 1 will flip the corresponding ECC bit
747 * Correctable errors can be injected by flipping 1 bit or the bits within
748 * a symbol pair (2 consecutive aligned 8-bit pairs - i.e. 7:0 and 15:8 or
749 * 23:16 and 31:24). Flipping bits in two symbol pairs will cause an
750 * uncorrectable error to be injected.
751 */
752static ssize_t i7core_inject_eccmask_store(struct mem_ctl_info *mci,
753 const char *data, size_t count)
754{
755 struct i7core_pvt *pvt = mci->pvt_info;
756 unsigned long value;
757 int rc;
758
759 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300760 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300761
762 rc = strict_strtoul(data, 10, &value);
763 if (rc < 0)
Mauro Carvalho Chehab2068def2009-08-05 19:28:27 -0300764 return -EIO;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300765
766 pvt->inject.eccmask = (u32) value;
767 return count;
768}
769
770static ssize_t i7core_inject_eccmask_show(struct mem_ctl_info *mci,
771 char *data)
772{
773 struct i7core_pvt *pvt = mci->pvt_info;
774 return sprintf(data, "0x%08x\n", pvt->inject.eccmask);
775}
776
777/*
778 * i7core_addrmatch
779 *
780 * The type of error (UE/CE) will depend on the inject.eccmask value:
781 * Any bits set to a 1 will flip the corresponding ECC bit
782 * Correctable errors can be injected by flipping 1 bit or the bits within
783 * a symbol pair (2 consecutive aligned 8-bit pairs - i.e. 7:0 and 15:8 or
784 * 23:16 and 31:24). Flipping bits in two symbol pairs will cause an
785 * uncorrectable error to be injected.
786 */
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300787
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -0300788#define DECLARE_ADDR_MATCH(param, limit) \
789static ssize_t i7core_inject_store_##param( \
790 struct mem_ctl_info *mci, \
791 const char *data, size_t count) \
792{ \
Mauro Carvalho Chehabcc301b32009-09-24 16:23:42 -0300793 struct i7core_pvt *pvt; \
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -0300794 long value; \
795 int rc; \
796 \
Mauro Carvalho Chehabcc301b32009-09-24 16:23:42 -0300797 debugf1("%s()\n", __func__); \
798 pvt = mci->pvt_info; \
799 \
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -0300800 if (pvt->inject.enable) \
801 disable_inject(mci); \
802 \
Mauro Carvalho Chehab4f87fad2009-10-04 11:54:56 -0300803 if (!strcasecmp(data, "any") || !strcasecmp(data, "any\n"))\
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -0300804 value = -1; \
805 else { \
806 rc = strict_strtoul(data, 10, &value); \
807 if ((rc < 0) || (value >= limit)) \
808 return -EIO; \
809 } \
810 \
811 pvt->inject.param = value; \
812 \
813 return count; \
814} \
815 \
816static ssize_t i7core_inject_show_##param( \
817 struct mem_ctl_info *mci, \
818 char *data) \
819{ \
Mauro Carvalho Chehabcc301b32009-09-24 16:23:42 -0300820 struct i7core_pvt *pvt; \
821 \
822 pvt = mci->pvt_info; \
823 debugf1("%s() pvt=%p\n", __func__, pvt); \
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -0300824 if (pvt->inject.param < 0) \
825 return sprintf(data, "any\n"); \
826 else \
827 return sprintf(data, "%d\n", pvt->inject.param);\
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300828}
829
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -0300830#define ATTR_ADDR_MATCH(param) \
831 { \
832 .attr = { \
833 .name = #param, \
834 .mode = (S_IRUGO | S_IWUSR) \
835 }, \
836 .show = i7core_inject_show_##param, \
837 .store = i7core_inject_store_##param, \
838 }
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300839
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -0300840DECLARE_ADDR_MATCH(channel, 3);
841DECLARE_ADDR_MATCH(dimm, 3);
842DECLARE_ADDR_MATCH(rank, 4);
843DECLARE_ADDR_MATCH(bank, 32);
844DECLARE_ADDR_MATCH(page, 0x10000);
845DECLARE_ADDR_MATCH(col, 0x4000);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300846
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300847static int write_and_test(struct pci_dev *dev, int where, u32 val)
848{
849 u32 read;
850 int count;
851
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -0300852 debugf0("setting pci %02x:%02x.%x reg=%02x value=%08x\n",
853 dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn),
854 where, val);
855
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300856 for (count = 0; count < 10; count++) {
857 if (count)
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -0300858 msleep(100);
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300859 pci_write_config_dword(dev, where, val);
860 pci_read_config_dword(dev, where, &read);
861
862 if (read == val)
863 return 0;
864 }
865
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -0300866 i7core_printk(KERN_ERR, "Error during set pci %02x:%02x.%x reg=%02x "
867 "write=%08x. Read=%08x\n",
868 dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn),
869 where, val, read);
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300870
871 return -EINVAL;
872}
873
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300874/*
875 * This routine prepares the Memory Controller for error injection.
876 * The error will be injected when some process tries to write to the
877 * memory that matches the given criteria.
878 * The criteria can be set in terms of a mask where dimm, rank, bank, page
879 * and col can be specified.
880 * A -1 value for any of the mask items will make the MCU to ignore
881 * that matching criteria for error injection.
882 *
883 * It should be noticed that the error will only happen after a write operation
884 * on a memory that matches the condition. if REPEAT_EN is not enabled at
885 * inject mask, then it will produce just one error. Otherwise, it will repeat
886 * until the injectmask would be cleaned.
887 *
888 * FIXME: This routine assumes that MAXNUMDIMMS value of MC_MAX_DOD
889 * is reliable enough to check if the MC is using the
890 * three channels. However, this is not clear at the datasheet.
891 */
892static ssize_t i7core_inject_enable_store(struct mem_ctl_info *mci,
893 const char *data, size_t count)
894{
895 struct i7core_pvt *pvt = mci->pvt_info;
896 u32 injectmask;
897 u64 mask = 0;
898 int rc;
899 long enable;
900
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300901 if (!pvt->pci_ch[pvt->inject.channel][0])
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300902 return 0;
903
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300904 rc = strict_strtoul(data, 10, &enable);
905 if ((rc < 0))
906 return 0;
907
908 if (enable) {
909 pvt->inject.enable = 1;
910 } else {
911 disable_inject(mci);
912 return count;
913 }
914
915 /* Sets pvt->inject.dimm mask */
916 if (pvt->inject.dimm < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300917 mask |= 1L << 41;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300918 else {
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300919 if (pvt->channel[pvt->inject.channel].dimms > 2)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300920 mask |= (pvt->inject.dimm & 0x3L) << 35;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300921 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300922 mask |= (pvt->inject.dimm & 0x1L) << 36;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300923 }
924
925 /* Sets pvt->inject.rank mask */
926 if (pvt->inject.rank < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300927 mask |= 1L << 40;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300928 else {
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300929 if (pvt->channel[pvt->inject.channel].dimms > 2)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300930 mask |= (pvt->inject.rank & 0x1L) << 34;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300931 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300932 mask |= (pvt->inject.rank & 0x3L) << 34;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300933 }
934
935 /* Sets pvt->inject.bank mask */
936 if (pvt->inject.bank < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300937 mask |= 1L << 39;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300938 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300939 mask |= (pvt->inject.bank & 0x15L) << 30;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300940
941 /* Sets pvt->inject.page mask */
942 if (pvt->inject.page < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300943 mask |= 1L << 38;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300944 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300945 mask |= (pvt->inject.page & 0xffffL) << 14;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300946
947 /* Sets pvt->inject.column mask */
948 if (pvt->inject.col < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300949 mask |= 1L << 37;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300950 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300951 mask |= (pvt->inject.col & 0x3fffL);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300952
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300953 /*
954 * bit 0: REPEAT_EN
955 * bits 1-2: MASK_HALF_CACHELINE
956 * bit 3: INJECT_ECC
957 * bit 4: INJECT_ADDR_PARITY
958 */
959
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300960 injectmask = (pvt->inject.type & 1) |
961 (pvt->inject.section & 0x3) << 1 |
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300962 (pvt->inject.type & 0x6) << (3 - 1);
963
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300964 /* Unlock writes to registers - this register is write only */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300965 pci_write_config_dword(pvt->pci_noncore,
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300966 MC_CFG_CONTROL, 0x2);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300967
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300968 write_and_test(pvt->pci_ch[pvt->inject.channel][0],
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300969 MC_CHANNEL_ADDR_MATCH, mask);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300970 write_and_test(pvt->pci_ch[pvt->inject.channel][0],
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300971 MC_CHANNEL_ADDR_MATCH + 4, mask >> 32L);
972
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300973 write_and_test(pvt->pci_ch[pvt->inject.channel][0],
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300974 MC_CHANNEL_ERROR_MASK, pvt->inject.eccmask);
975
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300976 write_and_test(pvt->pci_ch[pvt->inject.channel][0],
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -0300977 MC_CHANNEL_ERROR_INJECT, injectmask);
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300978
979 /*
980 * This is something undocumented, based on my tests
981 * Without writing 8 to this register, errors aren't injected. Not sure
982 * why.
983 */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -0300984 pci_write_config_dword(pvt->pci_noncore,
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300985 MC_CFG_CONTROL, 8);
986
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300987 debugf0("Error inject addr match 0x%016llx, ecc 0x%08x,"
988 " inject 0x%08x\n",
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300989 mask, pvt->inject.eccmask, injectmask);
990
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300991
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300992 return count;
993}
994
995static ssize_t i7core_inject_enable_show(struct mem_ctl_info *mci,
996 char *data)
997{
998 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300999 u32 injectmask;
1000
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001001 pci_read_config_dword(pvt->pci_ch[pvt->inject.channel][0],
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -03001002 MC_CHANNEL_ERROR_INJECT, &injectmask);
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -03001003
1004 debugf0("Inject error read: 0x%018x\n", injectmask);
1005
1006 if (injectmask & 0x0c)
1007 pvt->inject.enable = 1;
1008
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001009 return sprintf(data, "%d\n", pvt->inject.enable);
1010}
1011
Mauro Carvalho Chehabf338d732009-09-24 17:25:43 -03001012#define DECLARE_COUNTER(param) \
1013static ssize_t i7core_show_counter_##param( \
1014 struct mem_ctl_info *mci, \
1015 char *data) \
1016{ \
1017 struct i7core_pvt *pvt = mci->pvt_info; \
1018 \
1019 debugf1("%s() \n", __func__); \
1020 if (!pvt->ce_count_available || (pvt->is_registered)) \
1021 return sprintf(data, "data unavailable\n"); \
1022 return sprintf(data, "%lu\n", \
1023 pvt->udimm_ce_count[param]); \
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001024}
1025
Mauro Carvalho Chehabf338d732009-09-24 17:25:43 -03001026#define ATTR_COUNTER(param) \
1027 { \
1028 .attr = { \
1029 .name = __stringify(udimm##param), \
1030 .mode = (S_IRUGO | S_IWUSR) \
1031 }, \
1032 .show = i7core_show_counter_##param \
1033 }
1034
1035DECLARE_COUNTER(0);
1036DECLARE_COUNTER(1);
1037DECLARE_COUNTER(2);
1038
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001039/*
1040 * Sysfs struct
1041 */
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -03001042
1043
1044static struct mcidev_sysfs_attribute i7core_addrmatch_attrs[] = {
1045 ATTR_ADDR_MATCH(channel),
1046 ATTR_ADDR_MATCH(dimm),
1047 ATTR_ADDR_MATCH(rank),
1048 ATTR_ADDR_MATCH(bank),
1049 ATTR_ADDR_MATCH(page),
1050 ATTR_ADDR_MATCH(col),
1051 { .attr = { .name = NULL } }
1052};
1053
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -03001054static struct mcidev_sysfs_group i7core_inject_addrmatch = {
1055 .name = "inject_addrmatch",
1056 .mcidev_attr = i7core_addrmatch_attrs,
1057};
1058
Mauro Carvalho Chehabf338d732009-09-24 17:25:43 -03001059static struct mcidev_sysfs_attribute i7core_udimm_counters_attrs[] = {
1060 ATTR_COUNTER(0),
1061 ATTR_COUNTER(1),
1062 ATTR_COUNTER(2),
1063};
1064
1065static struct mcidev_sysfs_group i7core_udimm_counters = {
1066 .name = "all_channel_counts",
1067 .mcidev_attr = i7core_udimm_counters_attrs,
1068};
1069
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -03001070static struct mcidev_sysfs_attribute i7core_sysfs_attrs[] = {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001071 {
1072 .attr = {
1073 .name = "inject_section",
1074 .mode = (S_IRUGO | S_IWUSR)
1075 },
1076 .show = i7core_inject_section_show,
1077 .store = i7core_inject_section_store,
1078 }, {
1079 .attr = {
1080 .name = "inject_type",
1081 .mode = (S_IRUGO | S_IWUSR)
1082 },
1083 .show = i7core_inject_type_show,
1084 .store = i7core_inject_type_store,
1085 }, {
1086 .attr = {
1087 .name = "inject_eccmask",
1088 .mode = (S_IRUGO | S_IWUSR)
1089 },
1090 .show = i7core_inject_eccmask_show,
1091 .store = i7core_inject_eccmask_store,
1092 }, {
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -03001093 .grp = &i7core_inject_addrmatch,
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001094 }, {
1095 .attr = {
1096 .name = "inject_enable",
1097 .mode = (S_IRUGO | S_IWUSR)
1098 },
1099 .show = i7core_inject_enable_show,
1100 .store = i7core_inject_enable_store,
1101 },
Mauro Carvalho Chehabf338d732009-09-24 17:25:43 -03001102 { .attr = { .name = NULL } }, /* Reserved for udimm counters */
Mauro Carvalho Chehab42538682009-09-24 09:59:13 -03001103 { .attr = { .name = NULL } }
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001104};
1105
1106/****************************************************************************
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001107 Device initialization routines: put/get, init/exit
1108 ****************************************************************************/
1109
1110/*
1111 * i7core_put_devices 'put' all the devices that we have
1112 * reserved via 'get'
1113 */
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001114static void i7core_put_devices(struct i7core_dev *i7core_dev)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001115{
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001116 int i;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001117
Mauro Carvalho Chehab22e6bcb2009-09-05 23:06:50 -03001118 debugf0(__FILE__ ": %s()\n", __func__);
1119 for (i = 0; i < N_DEVS; i++) {
1120 struct pci_dev *pdev = i7core_dev->pdev[i];
1121 if (!pdev)
1122 continue;
1123 debugf0("Removing dev %02x:%02x.%d\n",
1124 pdev->bus->number,
1125 PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
1126 pci_dev_put(pdev);
1127 }
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001128 kfree(i7core_dev->pdev);
Mauro Carvalho Chehab22e6bcb2009-09-05 23:06:50 -03001129 list_del(&i7core_dev->list);
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001130 kfree(i7core_dev);
1131}
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001132
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001133static void i7core_put_all_devices(void)
1134{
Mauro Carvalho Chehab42538682009-09-24 09:59:13 -03001135 struct i7core_dev *i7core_dev, *tmp;
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001136
Mauro Carvalho Chehab42538682009-09-24 09:59:13 -03001137 list_for_each_entry_safe(i7core_dev, tmp, &i7core_edac_list, list)
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001138 i7core_put_devices(i7core_dev);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001139}
1140
Keith Manntheybc2d7242009-09-03 00:05:05 -03001141static void i7core_xeon_pci_fixup(void)
1142{
1143 struct pci_dev *pdev = NULL;
1144 int i;
1145 /*
1146 * On Xeon 55xx, the Intel Quckpath Arch Generic Non-core pci buses
1147 * aren't announced by acpi. So, we need to use a legacy scan probing
1148 * to detect them
1149 */
1150 pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001151 pci_dev_descr[0].dev_id, NULL);
Keith Manntheybc2d7242009-09-03 00:05:05 -03001152 if (unlikely(!pdev)) {
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001153 for (i = 0; i < MAX_SOCKET_BUSES; i++)
Keith Manntheybc2d7242009-09-03 00:05:05 -03001154 pcibios_scan_specific_bus(255-i);
1155 }
1156}
1157
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001158/*
1159 * i7core_get_devices Find and perform 'get' operation on the MCH's
1160 * device/functions we want to reference for this driver
1161 *
1162 * Need to 'get' device 16 func 1 and func 2
1163 */
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001164int i7core_get_onedevice(struct pci_dev **prev, int devno)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001165{
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001166 struct i7core_dev *i7core_dev;
1167
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001168 struct pci_dev *pdev = NULL;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001169 u8 bus = 0;
1170 u8 socket = 0;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001171
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001172 pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001173 pci_dev_descr[devno].dev_id, *prev);
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001174
1175 /*
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001176 * On Xeon 55xx, the Intel Quckpath Arch Generic Non-core regs
1177 * is at addr 8086:2c40, instead of 8086:2c41. So, we need
1178 * to probe for the alternate address in case of failure
1179 */
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001180 if (pci_dev_descr[devno].dev_id == PCI_DEVICE_ID_INTEL_I7_NOCORE && !pdev)
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001181 pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
1182 PCI_DEVICE_ID_INTEL_I7_NOCORE_ALT, *prev);
Mauro Carvalho Chehabd1fd4fb2009-07-10 18:39:53 -03001183
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001184 if (!pdev) {
1185 if (*prev) {
1186 *prev = pdev;
1187 return 0;
Mauro Carvalho Chehabd1fd4fb2009-07-10 18:39:53 -03001188 }
1189
Mauro Carvalho Chehab310cbb72009-07-17 00:09:10 -03001190 /*
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001191 * Dev 3 function 2 only exists on chips with RDIMMs
1192 * so, it is ok to not found it
Mauro Carvalho Chehab310cbb72009-07-17 00:09:10 -03001193 */
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001194 if ((pci_dev_descr[devno].dev == 3) && (pci_dev_descr[devno].func == 2)) {
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001195 *prev = pdev;
1196 return 0;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001197 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001198
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001199 i7core_printk(KERN_ERR,
1200 "Device not found: dev %02x.%d PCI ID %04x:%04x\n",
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001201 pci_dev_descr[devno].dev, pci_dev_descr[devno].func,
1202 PCI_VENDOR_ID_INTEL, pci_dev_descr[devno].dev_id);
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001203
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001204 /* End of list, leave */
1205 return -ENODEV;
1206 }
1207 bus = pdev->bus->number;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001208
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001209 if (bus == 0x3f)
1210 socket = 0;
1211 else
1212 socket = 255 - bus;
1213
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001214 i7core_dev = get_i7core_dev(socket);
1215 if (!i7core_dev) {
1216 i7core_dev = kzalloc(sizeof(*i7core_dev), GFP_KERNEL);
1217 if (!i7core_dev)
1218 return -ENOMEM;
1219 i7core_dev->pdev = kzalloc(sizeof(*i7core_dev->pdev) * N_DEVS,
1220 GFP_KERNEL);
1221 if (!i7core_dev->pdev)
1222 return -ENOMEM;
1223 i7core_dev->socket = socket;
1224 list_add_tail(&i7core_dev->list, &i7core_edac_list);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001225 }
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001226
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001227 if (i7core_dev->pdev[devno]) {
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001228 i7core_printk(KERN_ERR,
1229 "Duplicated device for "
1230 "dev %02x:%02x.%d PCI ID %04x:%04x\n",
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001231 bus, pci_dev_descr[devno].dev, pci_dev_descr[devno].func,
1232 PCI_VENDOR_ID_INTEL, pci_dev_descr[devno].dev_id);
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001233 pci_dev_put(pdev);
1234 return -ENODEV;
1235 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001236
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001237 i7core_dev->pdev[devno] = pdev;
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001238
1239 /* Sanity check */
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001240 if (unlikely(PCI_SLOT(pdev->devfn) != pci_dev_descr[devno].dev ||
1241 PCI_FUNC(pdev->devfn) != pci_dev_descr[devno].func)) {
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001242 i7core_printk(KERN_ERR,
1243 "Device PCI ID %04x:%04x "
1244 "has dev %02x:%02x.%d instead of dev %02x:%02x.%d\n",
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001245 PCI_VENDOR_ID_INTEL, pci_dev_descr[devno].dev_id,
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001246 bus, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001247 bus, pci_dev_descr[devno].dev, pci_dev_descr[devno].func);
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001248 return -ENODEV;
1249 }
1250
1251 /* Be sure that the device is enabled */
1252 if (unlikely(pci_enable_device(pdev) < 0)) {
1253 i7core_printk(KERN_ERR,
1254 "Couldn't enable "
1255 "dev %02x:%02x.%d PCI ID %04x:%04x\n",
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001256 bus, pci_dev_descr[devno].dev, pci_dev_descr[devno].func,
1257 PCI_VENDOR_ID_INTEL, pci_dev_descr[devno].dev_id);
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001258 return -ENODEV;
1259 }
1260
Mauro Carvalho Chehabd4c27792009-09-05 04:12:02 -03001261 debugf0("Detected socket %d dev %02x:%02x.%d PCI ID %04x:%04x\n",
1262 socket, bus, pci_dev_descr[devno].dev,
1263 pci_dev_descr[devno].func,
1264 PCI_VENDOR_ID_INTEL, pci_dev_descr[devno].dev_id);
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001265
1266 *prev = pdev;
1267
1268 return 0;
1269}
1270
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001271static int i7core_get_devices(void)
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001272{
1273 int i;
1274 struct pci_dev *pdev = NULL;
1275
1276 for (i = 0; i < N_DEVS; i++) {
1277 pdev = NULL;
1278 do {
1279 if (i7core_get_onedevice(&pdev, i) < 0) {
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001280 i7core_put_all_devices();
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001281 return -ENODEV;
1282 }
1283 } while (pdev);
1284 }
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001285
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001286 return 0;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001287}
1288
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001289static int mci_bind_devs(struct mem_ctl_info *mci,
1290 struct i7core_dev *i7core_dev)
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001291{
1292 struct i7core_pvt *pvt = mci->pvt_info;
1293 struct pci_dev *pdev;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001294 int i, func, slot;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001295
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001296 /* Associates i7core_dev and mci for future usage */
1297 pvt->i7core_dev = i7core_dev;
1298 i7core_dev->mci = mci;
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001299
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001300 pvt->is_registered = 0;
1301 for (i = 0; i < N_DEVS; i++) {
1302 pdev = i7core_dev->pdev[i];
1303 if (!pdev)
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001304 continue;
1305
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001306 func = PCI_FUNC(pdev->devfn);
1307 slot = PCI_SLOT(pdev->devfn);
1308 if (slot == 3) {
1309 if (unlikely(func > MAX_MCR_FUNC))
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001310 goto error;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001311 pvt->pci_mcr[func] = pdev;
1312 } else if (likely(slot >= 4 && slot < 4 + NUM_CHANS)) {
1313 if (unlikely(func > MAX_CHAN_FUNC))
1314 goto error;
1315 pvt->pci_ch[slot - 4][func] = pdev;
1316 } else if (!slot && !func)
1317 pvt->pci_noncore = pdev;
1318 else
1319 goto error;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001320
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001321 debugf0("Associated fn %d.%d, dev = %p, socket %d\n",
1322 PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
1323 pdev, i7core_dev->socket);
Mauro Carvalho Chehab14d2c082009-09-02 23:52:36 -03001324
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001325 if (PCI_SLOT(pdev->devfn) == 3 &&
1326 PCI_FUNC(pdev->devfn) == 2)
1327 pvt->is_registered = 1;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001328 }
Mauro Carvalho Chehabe9bd2e72009-07-09 22:14:35 -03001329
Mauro Carvalho Chehabf338d732009-09-24 17:25:43 -03001330 /*
1331 * Add extra nodes to count errors on udimm
1332 * For registered memory, this is not needed, since the counters
1333 * are already displayed at the standard locations
1334 */
1335 if (!pvt->is_registered)
1336 i7core_sysfs_attrs[ARRAY_SIZE(i7core_sysfs_attrs)-2].grp =
1337 &i7core_udimm_counters;
1338
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001339 return 0;
1340
1341error:
1342 i7core_printk(KERN_ERR, "Device %d, function %d "
1343 "is out of the expected range\n",
1344 slot, func);
1345 return -EINVAL;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001346}
1347
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001348/****************************************************************************
1349 Error check routines
1350 ****************************************************************************/
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001351static void i7core_rdimm_update_csrow(struct mem_ctl_info *mci,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001352 int chan, int dimm, int add)
1353{
1354 char *msg;
1355 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001356 int row = pvt->csrow_map[chan][dimm], i;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001357
1358 for (i = 0; i < add; i++) {
1359 msg = kasprintf(GFP_KERNEL, "Corrected error "
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001360 "(Socket=%d channel=%d dimm=%d)",
1361 pvt->i7core_dev->socket, chan, dimm);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001362
1363 edac_mc_handle_fbd_ce(mci, row, 0, msg);
1364 kfree (msg);
1365 }
1366}
1367
1368static void i7core_rdimm_update_ce_count(struct mem_ctl_info *mci,
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001369 int chan, int new0, int new1, int new2)
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001370{
1371 struct i7core_pvt *pvt = mci->pvt_info;
1372 int add0 = 0, add1 = 0, add2 = 0;
1373 /* Updates CE counters if it is not the first time here */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001374 if (pvt->ce_count_available) {
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001375 /* Updates CE counters */
1376
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001377 add2 = new2 - pvt->rdimm_last_ce_count[chan][2];
1378 add1 = new1 - pvt->rdimm_last_ce_count[chan][1];
1379 add0 = new0 - pvt->rdimm_last_ce_count[chan][0];
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001380
1381 if (add2 < 0)
1382 add2 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001383 pvt->rdimm_ce_count[chan][2] += add2;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001384
1385 if (add1 < 0)
1386 add1 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001387 pvt->rdimm_ce_count[chan][1] += add1;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001388
1389 if (add0 < 0)
1390 add0 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001391 pvt->rdimm_ce_count[chan][0] += add0;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001392 } else
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001393 pvt->ce_count_available = 1;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001394
1395 /* Store the new values */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001396 pvt->rdimm_last_ce_count[chan][2] = new2;
1397 pvt->rdimm_last_ce_count[chan][1] = new1;
1398 pvt->rdimm_last_ce_count[chan][0] = new0;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001399
1400 /*updated the edac core */
1401 if (add0 != 0)
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001402 i7core_rdimm_update_csrow(mci, chan, 0, add0);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001403 if (add1 != 0)
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001404 i7core_rdimm_update_csrow(mci, chan, 1, add1);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001405 if (add2 != 0)
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001406 i7core_rdimm_update_csrow(mci, chan, 2, add2);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001407
1408}
1409
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001410static void i7core_rdimm_check_mc_ecc_err(struct mem_ctl_info *mci)
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001411{
1412 struct i7core_pvt *pvt = mci->pvt_info;
1413 u32 rcv[3][2];
1414 int i, new0, new1, new2;
1415
1416 /*Read DEV 3: FUN 2: MC_COR_ECC_CNT regs directly*/
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001417 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_0,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001418 &rcv[0][0]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001419 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_1,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001420 &rcv[0][1]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001421 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_2,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001422 &rcv[1][0]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001423 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_3,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001424 &rcv[1][1]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001425 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_4,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001426 &rcv[2][0]);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001427 pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_5,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001428 &rcv[2][1]);
1429 for (i = 0 ; i < 3; i++) {
1430 debugf3("MC_COR_ECC_CNT%d = 0x%x; MC_COR_ECC_CNT%d = 0x%x\n",
1431 (i * 2), rcv[i][0], (i * 2) + 1, rcv[i][1]);
1432 /*if the channel has 3 dimms*/
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001433 if (pvt->channel[i].dimms > 2) {
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001434 new0 = DIMM_BOT_COR_ERR(rcv[i][0]);
1435 new1 = DIMM_TOP_COR_ERR(rcv[i][0]);
1436 new2 = DIMM_BOT_COR_ERR(rcv[i][1]);
1437 } else {
1438 new0 = DIMM_TOP_COR_ERR(rcv[i][0]) +
1439 DIMM_BOT_COR_ERR(rcv[i][0]);
1440 new1 = DIMM_TOP_COR_ERR(rcv[i][1]) +
1441 DIMM_BOT_COR_ERR(rcv[i][1]);
1442 new2 = 0;
1443 }
1444
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001445 i7core_rdimm_update_ce_count(mci, i, new0, new1, new2);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001446 }
1447}
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001448
1449/* This function is based on the device 3 function 4 registers as described on:
1450 * Intel Xeon Processor 5500 Series Datasheet Volume 2
1451 * http://www.intel.com/Assets/PDF/datasheet/321322.pdf
1452 * also available at:
1453 * http://www.arrownac.com/manufacturers/intel/s/nehalem/5500-datasheet-v2.pdf
1454 */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001455static void i7core_udimm_check_mc_ecc_err(struct mem_ctl_info *mci)
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001456{
1457 struct i7core_pvt *pvt = mci->pvt_info;
1458 u32 rcv1, rcv0;
1459 int new0, new1, new2;
1460
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001461 if (!pvt->pci_mcr[4]) {
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -03001462 debugf0("%s MCR registers not found\n", __func__);
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001463 return;
1464 }
1465
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001466 /* Corrected test errors */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001467 pci_read_config_dword(pvt->pci_mcr[4], MC_TEST_ERR_RCV1, &rcv1);
1468 pci_read_config_dword(pvt->pci_mcr[4], MC_TEST_ERR_RCV0, &rcv0);
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001469
1470 /* Store the new values */
1471 new2 = DIMM2_COR_ERR(rcv1);
1472 new1 = DIMM1_COR_ERR(rcv0);
1473 new0 = DIMM0_COR_ERR(rcv0);
1474
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001475 /* Updates CE counters if it is not the first time here */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001476 if (pvt->ce_count_available) {
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001477 /* Updates CE counters */
1478 int add0, add1, add2;
1479
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001480 add2 = new2 - pvt->udimm_last_ce_count[2];
1481 add1 = new1 - pvt->udimm_last_ce_count[1];
1482 add0 = new0 - pvt->udimm_last_ce_count[0];
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001483
1484 if (add2 < 0)
1485 add2 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001486 pvt->udimm_ce_count[2] += add2;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001487
1488 if (add1 < 0)
1489 add1 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001490 pvt->udimm_ce_count[1] += add1;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001491
1492 if (add0 < 0)
1493 add0 += 0x7fff;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001494 pvt->udimm_ce_count[0] += add0;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001495
1496 if (add0 | add1 | add2)
1497 i7core_printk(KERN_ERR, "New Corrected error(s): "
1498 "dimm0: +%d, dimm1: +%d, dimm2 +%d\n",
1499 add0, add1, add2);
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001500 } else
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001501 pvt->ce_count_available = 1;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001502
1503 /* Store the new values */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001504 pvt->udimm_last_ce_count[2] = new2;
1505 pvt->udimm_last_ce_count[1] = new1;
1506 pvt->udimm_last_ce_count[0] = new0;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001507}
1508
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001509/*
1510 * According with tables E-11 and E-12 of chapter E.3.3 of Intel 64 and IA-32
1511 * Architectures Software Developer’s Manual Volume 3B.
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001512 * Nehalem are defined as family 0x06, model 0x1a
1513 *
1514 * The MCA registers used here are the following ones:
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001515 * struct mce field MCA Register
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001516 * m->status MSR_IA32_MC8_STATUS
1517 * m->addr MSR_IA32_MC8_ADDR
1518 * m->misc MSR_IA32_MC8_MISC
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001519 * In the case of Nehalem, the error information is masked at .status and .misc
1520 * fields
1521 */
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001522static void i7core_mce_output_error(struct mem_ctl_info *mci,
1523 struct mce *m)
1524{
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001525 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001526 char *type, *optype, *err, *msg;
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001527 unsigned long error = m->status & 0x1ff0000l;
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001528 u32 optypenum = (m->status >> 4) & 0x07;
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001529 u32 core_err_cnt = (m->status >> 38) && 0x7fff;
1530 u32 dimm = (m->misc >> 16) & 0x3;
1531 u32 channel = (m->misc >> 18) & 0x3;
1532 u32 syndrome = m->misc >> 32;
1533 u32 errnum = find_first_bit(&error, 32);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001534 int csrow;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001535
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001536 if (m->mcgstatus & 1)
1537 type = "FATAL";
1538 else
1539 type = "NON_FATAL";
1540
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001541 switch (optypenum) {
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -03001542 case 0:
1543 optype = "generic undef request";
1544 break;
1545 case 1:
1546 optype = "read error";
1547 break;
1548 case 2:
1549 optype = "write error";
1550 break;
1551 case 3:
1552 optype = "addr/cmd error";
1553 break;
1554 case 4:
1555 optype = "scrubbing error";
1556 break;
1557 default:
1558 optype = "reserved";
1559 break;
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001560 }
1561
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001562 switch (errnum) {
1563 case 16:
1564 err = "read ECC error";
1565 break;
1566 case 17:
1567 err = "RAS ECC error";
1568 break;
1569 case 18:
1570 err = "write parity error";
1571 break;
1572 case 19:
1573 err = "redundacy loss";
1574 break;
1575 case 20:
1576 err = "reserved";
1577 break;
1578 case 21:
1579 err = "memory range error";
1580 break;
1581 case 22:
1582 err = "RTID out of range";
1583 break;
1584 case 23:
1585 err = "address parity error";
1586 break;
1587 case 24:
1588 err = "byte enable parity error";
1589 break;
1590 default:
1591 err = "unknown";
1592 }
1593
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001594 /* FIXME: should convert addr into bank and rank information */
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001595 msg = kasprintf(GFP_ATOMIC,
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001596 "%s (addr = 0x%08llx, cpu=%d, Dimm=%d, Channel=%d, "
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001597 "syndrome=0x%08x, count=%d, Err=%08llx:%08llx (%s: %s))\n",
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001598 type, (long long) m->addr, m->cpu, dimm, channel,
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001599 syndrome, core_err_cnt, (long long)m->status,
1600 (long long)m->misc, optype, err);
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001601
1602 debugf0("%s", msg);
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001603
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001604 csrow = pvt->csrow_map[channel][dimm];
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001605
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001606 /* Call the helper to output message */
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001607 if (m->mcgstatus & 1)
1608 edac_mc_handle_fbd_ue(mci, csrow, 0,
1609 0 /* FIXME: should be channel here */, msg);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001610 else if (!pvt->is_registered)
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001611 edac_mc_handle_fbd_ce(mci, csrow,
1612 0 /* FIXME: should be channel here */, msg);
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001613
1614 kfree(msg);
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001615}
1616
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001617/*
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001618 * i7core_check_error Retrieve and process errors reported by the
1619 * hardware. Called by the Core module.
1620 */
1621static void i7core_check_error(struct mem_ctl_info *mci)
1622{
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001623 struct i7core_pvt *pvt = mci->pvt_info;
1624 int i;
1625 unsigned count = 0;
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001626 struct mce *m;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001627
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001628 /*
1629 * MCE first step: Copy all mce errors into a temporary buffer
1630 * We use a double buffering here, to reduce the risk of
1631 * loosing an error.
1632 */
1633 smp_rmb();
1634 count = (pvt->mce_out + sizeof(mce_entry) - pvt->mce_in)
1635 % sizeof(mce_entry);
1636 if (!count)
1637 return;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001638
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001639 m = pvt->mce_outentry;
1640 if (pvt->mce_in + count > sizeof(mce_entry)) {
1641 unsigned l = sizeof(mce_entry) - pvt->mce_in;
1642
1643 memcpy(m, &pvt->mce_entry[pvt->mce_in], sizeof(*m) * l);
1644 smp_wmb();
1645 pvt->mce_in = 0;
1646 count -= l;
1647 m += l;
1648 }
1649 memcpy(m, &pvt->mce_entry[pvt->mce_in], sizeof(*m) * count);
1650 smp_wmb();
1651 pvt->mce_in += count;
1652
1653 smp_rmb();
1654 if (pvt->mce_overrun) {
1655 i7core_printk(KERN_ERR, "Lost %d memory errors\n",
1656 pvt->mce_overrun);
1657 smp_wmb();
1658 pvt->mce_overrun = 0;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001659 }
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001660
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001661 /*
1662 * MCE second step: parse errors and display
1663 */
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001664 for (i = 0; i < count; i++)
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001665 i7core_mce_output_error(mci, &pvt->mce_outentry[i]);
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001666
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001667 /*
1668 * Now, let's increment CE error counts
1669 */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001670 if (!pvt->is_registered)
1671 i7core_udimm_check_mc_ecc_err(mci);
1672 else
1673 i7core_rdimm_check_mc_ecc_err(mci);
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001674}
1675
1676/*
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001677 * i7core_mce_check_error Replicates mcelog routine to get errors
1678 * This routine simply queues mcelog errors, and
1679 * return. The error itself should be handled later
1680 * by i7core_check_error.
1681 */
1682static int i7core_mce_check_error(void *priv, struct mce *mce)
1683{
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001684 struct mem_ctl_info *mci = priv;
1685 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001686
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001687 /*
1688 * Just let mcelog handle it if the error is
1689 * outside the memory controller
1690 */
1691 if (((mce->status & 0xffff) >> 7) != 1)
1692 return 0;
1693
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001694 /* Bank 8 registers are the only ones that we know how to handle */
1695 if (mce->bank != 8)
1696 return 0;
1697
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001698 /* Only handle if it is the right mc controller */
Mauro Carvalho Chehab6c6aa3a2009-09-05 03:27:04 -03001699 if (cpu_data(mce->cpu).phys_proc_id != pvt->i7core_dev->socket) {
1700 debugf0("mc%d: ignoring mce log for socket %d. "
1701 "Another mc should get it.\n",
1702 pvt->i7core_dev->socket,
1703 cpu_data(mce->cpu).phys_proc_id);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001704 return 0;
Mauro Carvalho Chehab6c6aa3a2009-09-05 03:27:04 -03001705 }
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001706
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001707 smp_rmb();
1708 if ((pvt->mce_out + 1) % sizeof(mce_entry) == pvt->mce_in) {
1709 smp_wmb();
1710 pvt->mce_overrun++;
1711 return 0;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001712 }
Mauro Carvalho Chehabca9c90b2009-10-04 10:15:40 -03001713 smp_wmb();
1714 pvt->mce_out = (pvt->mce_out + 1) % sizeof(mce_entry);
1715 memcpy(&pvt->mce_entry[pvt->mce_out], mce, sizeof(*mce));
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001716
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001717 /* Handle fatal errors immediately */
1718 if (mce->mcgstatus & 1)
1719 i7core_check_error(mci);
1720
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001721 /* Advice mcelog that the error were handled */
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001722 return 1;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001723}
1724
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001725static int i7core_register_mci(struct i7core_dev *i7core_dev,
1726 int num_channels, int num_csrows)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001727{
1728 struct mem_ctl_info *mci;
1729 struct i7core_pvt *pvt;
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -03001730 int csrow = 0;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001731 int rc;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001732
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001733 /* allocate a new MC control structure */
Mauro Carvalho Chehabd4c27792009-09-05 04:12:02 -03001734 mci = edac_mc_alloc(sizeof(*pvt), num_csrows, num_channels,
1735 i7core_dev->socket);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001736 if (unlikely(!mci))
1737 return -ENOMEM;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001738
1739 debugf0("MC: " __FILE__ ": %s(): mci = %p\n", __func__, mci);
1740
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001741 /* record ptr to the generic device */
1742 mci->dev = &i7core_dev->pdev[0]->dev;
1743
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001744 pvt = mci->pvt_info;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001745 memset(pvt, 0, sizeof(*pvt));
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001746
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001747 /*
1748 * FIXME: how to handle RDDR3 at MCI level? It is possible to have
1749 * Mixed RDDR3/UDDR3 with Nehalem, provided that they are on different
1750 * memory channels
1751 */
1752 mci->mtype_cap = MEM_FLAG_DDR3;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001753 mci->edac_ctl_cap = EDAC_FLAG_NONE;
1754 mci->edac_cap = EDAC_FLAG_NONE;
1755 mci->mod_name = "i7core_edac.c";
1756 mci->mod_ver = I7CORE_REVISION;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001757 mci->ctl_name = kasprintf(GFP_KERNEL, "i7 core #%d",
1758 i7core_dev->socket);
1759 mci->dev_name = pci_name(i7core_dev->pdev[0]);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001760 mci->ctl_page_to_phys = NULL;
Mauro Carvalho Chehaba5538e52009-09-23 18:56:47 -03001761 mci->mc_driver_sysfs_attributes = i7core_sysfs_attrs;
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001762 /* Set the function pointer to an actual operation function */
1763 mci->edac_check = i7core_check_error;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001764
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001765 /* Store pci devices at mci for faster access */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001766 rc = mci_bind_devs(mci, i7core_dev);
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001767 if (unlikely(rc < 0))
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001768 goto fail;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001769
1770 /* Get dimm basic config */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001771 get_dimm_config(mci, &csrow);
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001772
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001773 /* add this new MC control structure to EDAC's list of MCs */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001774 if (unlikely(edac_mc_add_mc(mci))) {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001775 debugf0("MC: " __FILE__
1776 ": %s(): failed edac_mc_add_mc()\n", __func__);
1777 /* FIXME: perhaps some code should go here that disables error
1778 * reporting if we just enabled it
1779 */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001780
1781 rc = -EINVAL;
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001782 goto fail;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001783 }
1784
1785 /* allocating generic PCI control info */
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001786 i7core_pci = edac_pci_create_generic_ctl(&i7core_dev->pdev[0]->dev,
1787 EDAC_MOD_STR);
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001788 if (unlikely(!i7core_pci)) {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001789 printk(KERN_WARNING
1790 "%s(): Unable to create PCI control\n",
1791 __func__);
1792 printk(KERN_WARNING
1793 "%s(): PCI error report via EDAC not setup\n",
1794 __func__);
1795 }
1796
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001797 /* Default error mask is any memory */
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001798 pvt->inject.channel = 0;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001799 pvt->inject.dimm = -1;
1800 pvt->inject.rank = -1;
1801 pvt->inject.bank = -1;
1802 pvt->inject.page = -1;
1803 pvt->inject.col = -1;
1804
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001805 /* Registers on edac_mce in order to receive memory errors */
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001806 pvt->edac_mce.priv = mci;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001807 pvt->edac_mce.check_error = i7core_mce_check_error;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001808
1809 rc = edac_mce_register(&pvt->edac_mce);
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -03001810 if (unlikely(rc < 0)) {
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001811 debugf0("MC: " __FILE__
1812 ": %s(): failed edac_mce_register()\n", __func__);
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001813 }
1814
1815fail:
1816 edac_mc_free(mci);
1817 return rc;
1818}
1819
1820/*
1821 * i7core_probe Probe for ONE instance of device to see if it is
1822 * present.
1823 * return:
1824 * 0 for FOUND a device
1825 * < 0 for error code
1826 */
1827static int __devinit i7core_probe(struct pci_dev *pdev,
1828 const struct pci_device_id *id)
1829{
1830 int dev_idx = id->driver_data;
1831 int rc;
1832 struct i7core_dev *i7core_dev;
1833
1834 /*
Mauro Carvalho Chehabd4c27792009-09-05 04:12:02 -03001835 * All memory controllers are allocated at the first pass.
Mauro Carvalho Chehabf4742942009-09-05 02:35:08 -03001836 */
1837 if (unlikely(dev_idx >= 1))
1838 return -EINVAL;
1839
1840 /* get the pci devices we want to reserve for our use */
1841 mutex_lock(&i7core_edac_lock);
1842 rc = i7core_get_devices();
1843 if (unlikely(rc < 0))
1844 goto fail0;
1845
1846 list_for_each_entry(i7core_dev, &i7core_edac_list, list) {
1847 int channels;
1848 int csrows;
1849
1850 /* Check the number of active and not disabled channels */
1851 rc = i7core_get_active_channels(i7core_dev->socket,
1852 &channels, &csrows);
1853 if (unlikely(rc < 0))
1854 goto fail1;
1855
Mauro Carvalho Chehabd4c27792009-09-05 04:12:02 -03001856 rc = i7core_register_mci(i7core_dev, channels, csrows);
1857 if (unlikely(rc < 0))
1858 goto fail1;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001859 }
1860
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001861 i7core_printk(KERN_INFO, "Driver loaded.\n");
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001862
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001863 mutex_unlock(&i7core_edac_lock);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001864 return 0;
1865
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001866fail1:
Mauro Carvalho Chehab13d6e9b2009-09-05 12:15:20 -03001867 i7core_put_all_devices();
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001868fail0:
1869 mutex_unlock(&i7core_edac_lock);
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001870 return rc;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001871}
1872
1873/*
1874 * i7core_remove destructor for one instance of device
1875 *
1876 */
1877static void __devexit i7core_remove(struct pci_dev *pdev)
1878{
1879 struct mem_ctl_info *mci;
Mauro Carvalho Chehab22e6bcb2009-09-05 23:06:50 -03001880 struct i7core_dev *i7core_dev, *tmp;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001881
1882 debugf0(__FILE__ ": %s()\n", __func__);
1883
1884 if (i7core_pci)
1885 edac_pci_release_generic_ctl(i7core_pci);
1886
Mauro Carvalho Chehab22e6bcb2009-09-05 23:06:50 -03001887 /*
1888 * we have a trouble here: pdev value for removal will be wrong, since
1889 * it will point to the X58 register used to detect that the machine
1890 * is a Nehalem or upper design. However, due to the way several PCI
1891 * devices are grouped together to provide MC functionality, we need
1892 * to use a different method for releasing the devices
1893 */
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001894
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001895 mutex_lock(&i7core_edac_lock);
Mauro Carvalho Chehab22e6bcb2009-09-05 23:06:50 -03001896 list_for_each_entry_safe(i7core_dev, tmp, &i7core_edac_list, list) {
1897 mci = edac_mc_del_mc(&i7core_dev->pdev[0]->dev);
1898 if (mci) {
1899 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001900
Mauro Carvalho Chehab22e6bcb2009-09-05 23:06:50 -03001901 i7core_dev = pvt->i7core_dev;
1902 edac_mce_unregister(&pvt->edac_mce);
1903 kfree(mci->ctl_name);
1904 edac_mc_free(mci);
1905 i7core_put_devices(i7core_dev);
1906 } else {
1907 i7core_printk(KERN_ERR,
1908 "Couldn't find mci for socket %d\n",
1909 i7core_dev->socket);
1910 }
1911 }
1912 mutex_unlock(&i7core_edac_lock);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001913}
1914
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001915MODULE_DEVICE_TABLE(pci, i7core_pci_tbl);
1916
1917/*
1918 * i7core_driver pci_driver structure for this module
1919 *
1920 */
1921static struct pci_driver i7core_driver = {
1922 .name = "i7core_edac",
1923 .probe = i7core_probe,
1924 .remove = __devexit_p(i7core_remove),
1925 .id_table = i7core_pci_tbl,
1926};
1927
1928/*
1929 * i7core_init Module entry function
1930 * Try to initialize this module for its devices
1931 */
1932static int __init i7core_init(void)
1933{
1934 int pci_rc;
1935
1936 debugf2("MC: " __FILE__ ": %s()\n", __func__);
1937
1938 /* Ensure that the OPSTATE is set correctly for POLL or NMI */
1939 opstate_init();
1940
Keith Manntheybc2d7242009-09-03 00:05:05 -03001941 i7core_xeon_pci_fixup();
1942
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001943 pci_rc = pci_register_driver(&i7core_driver);
1944
Mauro Carvalho Chehab3ef288a2009-09-02 23:43:33 -03001945 if (pci_rc >= 0)
1946 return 0;
1947
1948 i7core_printk(KERN_ERR, "Failed to register device with error %d.\n",
1949 pci_rc);
1950
1951 return pci_rc;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001952}
1953
1954/*
1955 * i7core_exit() Module exit function
1956 * Unregister the driver
1957 */
1958static void __exit i7core_exit(void)
1959{
1960 debugf2("MC: " __FILE__ ": %s()\n", __func__);
1961 pci_unregister_driver(&i7core_driver);
1962}
1963
1964module_init(i7core_init);
1965module_exit(i7core_exit);
1966
1967MODULE_LICENSE("GPL");
1968MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
1969MODULE_AUTHOR("Red Hat Inc. (http://www.redhat.com)");
1970MODULE_DESCRIPTION("MC Driver for Intel i7 Core memory controllers - "
1971 I7CORE_REVISION);
1972
1973module_param(edac_op_state, int, 0444);
1974MODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI");