blob: 4758c208f39a3fdb7347f2f7fbf9b5824e6b0783 [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 Chehabb4e8f0b2009-09-02 23:49:59 -030076/* OFFSETS for Device 3 Function 2, as inicated on Xeon 5500 datasheet */
77#define MC_COR_ECC_CNT_0 0x80
78#define MC_COR_ECC_CNT_1 0x84
79#define MC_COR_ECC_CNT_2 0x88
80#define MC_COR_ECC_CNT_3 0x8c
81#define MC_COR_ECC_CNT_4 0x90
82#define MC_COR_ECC_CNT_5 0x94
83
84#define DIMM_TOP_COR_ERR(r) (((r) >> 16) & 0x7fff)
85#define DIMM_BOT_COR_ERR(r) ((r) & 0x7fff)
86
87
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030088 /* OFFSETS for Devices 4,5 and 6 Function 0 */
89
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -030090#define MC_CHANNEL_DIMM_INIT_PARAMS 0x58
91 #define THREE_DIMMS_PRESENT (1 << 24)
92 #define SINGLE_QUAD_RANK_PRESENT (1 << 23)
93 #define QUAD_RANK_PRESENT (1 << 22)
94 #define REGISTERED_DIMM (1 << 15)
95
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -030096#define MC_CHANNEL_MAPPER 0x60
97 #define RDLCH(r, ch) ((((r) >> (3 + (ch * 6))) & 0x07) - 1)
98 #define WRLCH(r, ch) ((((r) >> (ch * 6)) & 0x07) - 1)
99
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300100#define MC_CHANNEL_RANK_PRESENT 0x7c
101 #define RANK_PRESENT_MASK 0xffff
102
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300103#define MC_CHANNEL_ADDR_MATCH 0xf0
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300104#define MC_CHANNEL_ERROR_MASK 0xf8
105#define MC_CHANNEL_ERROR_INJECT 0xfc
106 #define INJECT_ADDR_PARITY 0x10
107 #define INJECT_ECC 0x08
108 #define MASK_CACHELINE 0x06
109 #define MASK_FULL_CACHELINE 0x06
110 #define MASK_MSB32_CACHELINE 0x04
111 #define MASK_LSB32_CACHELINE 0x02
112 #define NO_MASK_CACHELINE 0x00
113 #define REPEAT_EN 0x01
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300114
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300115 /* OFFSETS for Devices 4,5 and 6 Function 1 */
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -0300116
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300117#define MC_DOD_CH_DIMM0 0x48
118#define MC_DOD_CH_DIMM1 0x4c
119#define MC_DOD_CH_DIMM2 0x50
120 #define RANKOFFSET_MASK ((1 << 12) | (1 << 11) | (1 << 10))
121 #define RANKOFFSET(x) ((x & RANKOFFSET_MASK) >> 10)
122 #define DIMM_PRESENT_MASK (1 << 9)
123 #define DIMM_PRESENT(x) (((x) & DIMM_PRESENT_MASK) >> 9)
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300124 #define MC_DOD_NUMBANK_MASK ((1 << 8) | (1 << 7))
125 #define MC_DOD_NUMBANK(x) (((x) & MC_DOD_NUMBANK_MASK) >> 7)
126 #define MC_DOD_NUMRANK_MASK ((1 << 6) | (1 << 5))
127 #define MC_DOD_NUMRANK(x) (((x) & MC_DOD_NUMRANK_MASK) >> 5)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300128 #define MC_DOD_NUMROW_MASK ((1 << 4) | (1 << 3) | (1 << 2))
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300129 #define MC_DOD_NUMROW(x) (((x) & MC_DOD_NUMROW_MASK) >> 2)
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300130 #define MC_DOD_NUMCOL_MASK 3
131 #define MC_DOD_NUMCOL(x) ((x) & MC_DOD_NUMCOL_MASK)
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300132
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300133#define MC_RANK_PRESENT 0x7c
134
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300135#define MC_SAG_CH_0 0x80
136#define MC_SAG_CH_1 0x84
137#define MC_SAG_CH_2 0x88
138#define MC_SAG_CH_3 0x8c
139#define MC_SAG_CH_4 0x90
140#define MC_SAG_CH_5 0x94
141#define MC_SAG_CH_6 0x98
142#define MC_SAG_CH_7 0x9c
143
144#define MC_RIR_LIMIT_CH_0 0x40
145#define MC_RIR_LIMIT_CH_1 0x44
146#define MC_RIR_LIMIT_CH_2 0x48
147#define MC_RIR_LIMIT_CH_3 0x4C
148#define MC_RIR_LIMIT_CH_4 0x50
149#define MC_RIR_LIMIT_CH_5 0x54
150#define MC_RIR_LIMIT_CH_6 0x58
151#define MC_RIR_LIMIT_CH_7 0x5C
152#define MC_RIR_LIMIT_MASK ((1 << 10) - 1)
153
154#define MC_RIR_WAY_CH 0x80
155 #define MC_RIR_WAY_OFFSET_MASK (((1 << 14) - 1) & ~0x7)
156 #define MC_RIR_WAY_RANK_MASK 0x7
157
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300158/*
159 * i7core structs
160 */
161
162#define NUM_CHANS 3
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -0300163#define MAX_DIMMS 3 /* Max DIMMS per channel */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300164#define NUM_SOCKETS 2 /* Max number of MC sockets */
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -0300165#define MAX_MCR_FUNC 4
166#define MAX_CHAN_FUNC 3
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300167
168struct i7core_info {
169 u32 mc_control;
170 u32 mc_status;
171 u32 max_dod;
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300172 u32 ch_map;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300173};
174
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300175
176struct i7core_inject {
177 int enable;
178
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300179 u8 socket;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300180 u32 section;
181 u32 type;
182 u32 eccmask;
183
184 /* Error address mask */
185 int channel, dimm, rank, bank, page, col;
186};
187
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300188struct i7core_channel {
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -0300189 u32 ranks;
190 u32 dimms;
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300191};
192
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300193struct pci_id_descr {
194 int dev;
195 int func;
196 int dev_id;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300197 struct pci_dev *pdev[NUM_SOCKETS];
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300198};
199
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300200struct i7core_pvt {
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300201 struct pci_dev *pci_noncore[NUM_SOCKETS];
202 struct pci_dev *pci_mcr[NUM_SOCKETS][MAX_MCR_FUNC + 1];
203 struct pci_dev *pci_ch[NUM_SOCKETS][NUM_CHANS][MAX_CHAN_FUNC + 1];
204
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300205 struct i7core_info info;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300206 struct i7core_inject inject;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300207 struct i7core_channel channel[NUM_SOCKETS][NUM_CHANS];
208
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -0300209 unsigned int is_registered:1; /* true if all memories are RDIMMs */
210
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300211 int sockets; /* Number of sockets */
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300212 int channels; /* Number of active channels */
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -0300213
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300214 int ce_count_available[NUM_SOCKETS];
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -0300215 int csrow_map[NUM_SOCKETS][NUM_CHANS][MAX_DIMMS];
216
217 /* ECC corrected errors counts per udimm */
218 unsigned long udimm_ce_count[NUM_SOCKETS][MAX_DIMMS];
219 int udimm_last_ce_count[NUM_SOCKETS][MAX_DIMMS];
220 /* ECC corrected errors counts per rdimm */
221 unsigned long rdimm_ce_count[NUM_SOCKETS][NUM_CHANS][MAX_DIMMS];
222 int rdimm_last_ce_count[NUM_SOCKETS][NUM_CHANS][MAX_DIMMS];
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -0300223
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -0300224 /* mcelog glue */
225 struct edac_mce edac_mce;
226 struct mce mce_entry[MCE_LOG_LEN];
227 unsigned mce_count;
228 spinlock_t mce_lock;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300229};
230
231/* Device name and register DID (Device ID) */
232struct i7core_dev_info {
233 const char *ctl_name; /* name for this device */
234 u16 fsb_mapping_errors; /* DID for the branchmap,control */
235};
236
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300237#define PCI_DESCR(device, function, device_id) \
238 .dev = (device), \
239 .func = (function), \
240 .dev_id = (device_id)
241
242struct pci_id_descr pci_devs[] = {
243 /* Memory controller */
244 { PCI_DESCR(3, 0, PCI_DEVICE_ID_INTEL_I7_MCR) },
245 { PCI_DESCR(3, 1, PCI_DEVICE_ID_INTEL_I7_MC_TAD) },
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -0300246 { PCI_DESCR(3, 2, PCI_DEVICE_ID_INTEL_I7_MC_RAS) }, /* if RDIMM */
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300247 { PCI_DESCR(3, 4, PCI_DEVICE_ID_INTEL_I7_MC_TEST) },
248
249 /* Channel 0 */
250 { PCI_DESCR(4, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH0_CTRL) },
251 { PCI_DESCR(4, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH0_ADDR) },
252 { PCI_DESCR(4, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH0_RANK) },
253 { PCI_DESCR(4, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH0_TC) },
254
255 /* Channel 1 */
256 { PCI_DESCR(5, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH1_CTRL) },
257 { PCI_DESCR(5, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH1_ADDR) },
258 { PCI_DESCR(5, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH1_RANK) },
259 { PCI_DESCR(5, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH1_TC) },
260
261 /* Channel 2 */
262 { PCI_DESCR(6, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH2_CTRL) },
263 { PCI_DESCR(6, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH2_ADDR) },
264 { PCI_DESCR(6, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH2_RANK) },
265 { PCI_DESCR(6, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH2_TC) },
Mauro Carvalho Chehab310cbb72009-07-17 00:09:10 -0300266
267 /* Generic Non-core registers */
268 /*
269 * This is the PCI device on i7core and on Xeon 35xx (8086:2c41)
270 * On Xeon 55xx, however, it has a different id (8086:2c40). So,
271 * the probing code needs to test for the other address in case of
272 * failure of this one
273 */
274 { PCI_DESCR(0, 0, PCI_DEVICE_ID_INTEL_I7_NOCORE) },
275
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300276};
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300277#define N_DEVS ARRAY_SIZE(pci_devs)
278
279/*
280 * pci_device_id table for which devices we are looking for
281 * This should match the first device at pci_devs table
282 */
283static const struct pci_device_id i7core_pci_tbl[] __devinitdata = {
Mauro Carvalho Chehabd1fd4fb2009-07-10 18:39:53 -0300284 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_X58_HUB_MGMT)},
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300285 {0,} /* 0 terminated list. */
286};
287
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300288
289/* Table of devices attributes supported by this driver */
290static const struct i7core_dev_info i7core_devs[] = {
291 {
292 .ctl_name = "i7 Core",
293 .fsb_mapping_errors = PCI_DEVICE_ID_INTEL_I7_MCR,
294 },
295};
296
297static struct edac_pci_ctl_info *i7core_pci;
298
299/****************************************************************************
300 Anciliary status routines
301 ****************************************************************************/
302
303 /* MC_CONTROL bits */
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300304#define CH_ACTIVE(pvt, ch) ((pvt)->info.mc_control & (1 << (8 + ch)))
305#define ECCx8(pvt) ((pvt)->info.mc_control & (1 << 1))
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300306
307 /* MC_STATUS bits */
Keith Mannthey61053fd2009-09-02 23:46:59 -0300308#define ECC_ENABLED(pvt) ((pvt)->info.mc_status & (1 << 4))
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300309#define CH_DISABLED(pvt, ch) ((pvt)->info.mc_status & (1 << ch))
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300310
311 /* MC_MAX_DOD read functions */
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300312static inline int numdimms(u32 dimms)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300313{
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300314 return (dimms & 0x3) + 1;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300315}
316
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300317static inline int numrank(u32 rank)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300318{
319 static int ranks[4] = { 1, 2, 4, -EINVAL };
320
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300321 return ranks[rank & 0x3];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300322}
323
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300324static inline int numbank(u32 bank)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300325{
326 static int banks[4] = { 4, 8, 16, -EINVAL };
327
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300328 return banks[bank & 0x3];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300329}
330
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300331static inline int numrow(u32 row)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300332{
333 static int rows[8] = {
334 1 << 12, 1 << 13, 1 << 14, 1 << 15,
335 1 << 16, -EINVAL, -EINVAL, -EINVAL,
336 };
337
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300338 return rows[row & 0x7];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300339}
340
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300341static inline int numcol(u32 col)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300342{
343 static int cols[8] = {
344 1 << 10, 1 << 11, 1 << 12, -EINVAL,
345 };
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300346 return cols[col & 0x3];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300347}
348
349/****************************************************************************
350 Memory check routines
351 ****************************************************************************/
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300352static struct pci_dev *get_pdev_slot_func(u8 socket, unsigned slot,
353 unsigned func)
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300354{
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300355 int i;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300356
357 for (i = 0; i < N_DEVS; i++) {
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300358 if (!pci_devs[i].pdev[socket])
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300359 continue;
360
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300361 if (PCI_SLOT(pci_devs[i].pdev[socket]->devfn) == slot &&
362 PCI_FUNC(pci_devs[i].pdev[socket]->devfn) == func) {
363 return pci_devs[i].pdev[socket];
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300364 }
365 }
366
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300367 return NULL;
368}
369
Mauro Carvalho Chehabec6df242009-07-18 10:44:30 -0300370/**
371 * i7core_get_active_channels() - gets the number of channels and csrows
372 * @socket: Quick Path Interconnect socket
373 * @channels: Number of channels that will be returned
374 * @csrows: Number of csrows found
375 *
376 * Since EDAC core needs to know in advance the number of available channels
377 * and csrows, in order to allocate memory for csrows/channels, it is needed
378 * to run two similar steps. At the first step, implemented on this function,
379 * it checks the number of csrows/channels present at one socket.
380 * this is used in order to properly allocate the size of mci components.
381 *
382 * It should be noticed that none of the current available datasheets explain
383 * or even mention how csrows are seen by the memory controller. So, we need
384 * to add a fake description for csrows.
385 * So, this driver is attributing one DIMM memory for one csrow.
386 */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300387static int i7core_get_active_channels(u8 socket, unsigned *channels,
388 unsigned *csrows)
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300389{
390 struct pci_dev *pdev = NULL;
391 int i, j;
392 u32 status, control;
393
394 *channels = 0;
395 *csrows = 0;
396
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300397 pdev = get_pdev_slot_func(socket, 3, 0);
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -0300398 if (!pdev) {
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300399 i7core_printk(KERN_ERR, "Couldn't find socket %d fn 3.0!!!\n",
400 socket);
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300401 return -ENODEV;
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -0300402 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300403
404 /* Device 3 function 0 reads */
405 pci_read_config_dword(pdev, MC_STATUS, &status);
406 pci_read_config_dword(pdev, MC_CONTROL, &control);
407
408 for (i = 0; i < NUM_CHANS; i++) {
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300409 u32 dimm_dod[3];
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300410 /* Check if the channel is active */
411 if (!(control & (1 << (8 + i))))
412 continue;
413
414 /* Check if the channel is disabled */
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300415 if (status & (1 << i))
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300416 continue;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300417
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300418 pdev = get_pdev_slot_func(socket, i + 4, 1);
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300419 if (!pdev) {
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300420 i7core_printk(KERN_ERR, "Couldn't find socket %d "
421 "fn %d.%d!!!\n",
422 socket, i + 4, 1);
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300423 return -ENODEV;
424 }
425 /* Devices 4-6 function 1 */
426 pci_read_config_dword(pdev,
427 MC_DOD_CH_DIMM0, &dimm_dod[0]);
428 pci_read_config_dword(pdev,
429 MC_DOD_CH_DIMM1, &dimm_dod[1]);
430 pci_read_config_dword(pdev,
431 MC_DOD_CH_DIMM2, &dimm_dod[2]);
432
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300433 (*channels)++;
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300434
435 for (j = 0; j < 3; j++) {
436 if (!DIMM_PRESENT(dimm_dod[j]))
437 continue;
438 (*csrows)++;
439 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300440 }
441
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -0300442 debugf0("Number of active channels on socket %d: %d\n",
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300443 socket, *channels);
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300444
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300445 return 0;
446}
447
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -0300448static int get_dimm_config(struct mem_ctl_info *mci, int *csrow, u8 socket)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300449{
450 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300451 struct csrow_info *csr;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300452 struct pci_dev *pdev;
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -0300453 int i, j;
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300454 unsigned long last_page = 0;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300455 enum edac_type mode;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300456 enum mem_type mtype;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300457
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300458 /* Get data from the MC register, function 0 */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300459 pdev = pvt->pci_mcr[socket][0];
Mauro Carvalho Chehab7dd69532009-06-22 22:48:30 -0300460 if (!pdev)
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300461 return -ENODEV;
462
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300463 /* Device 3 function 0 reads */
Mauro Carvalho Chehab7dd69532009-06-22 22:48:30 -0300464 pci_read_config_dword(pdev, MC_CONTROL, &pvt->info.mc_control);
465 pci_read_config_dword(pdev, MC_STATUS, &pvt->info.mc_status);
466 pci_read_config_dword(pdev, MC_MAX_DOD, &pvt->info.max_dod);
467 pci_read_config_dword(pdev, MC_CHANNEL_MAPPER, &pvt->info.ch_map);
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300468
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300469 debugf0("QPI %d control=0x%08x status=0x%08x dod=0x%08x map=0x%08x\n",
470 socket, pvt->info.mc_control, pvt->info.mc_status,
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300471 pvt->info.max_dod, pvt->info.ch_map);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300472
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300473 if (ECC_ENABLED(pvt)) {
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300474 debugf0("ECC enabled with x%d SDCC\n", ECCx8(pvt) ? 8 : 4);
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300475 if (ECCx8(pvt))
476 mode = EDAC_S8ECD8ED;
477 else
478 mode = EDAC_S4ECD4ED;
479 } else {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300480 debugf0("ECC disabled\n");
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300481 mode = EDAC_NONE;
482 }
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300483
484 /* FIXME: need to handle the error codes */
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300485 debugf0("DOD Max limits: DIMMS: %d, %d-ranked, %d-banked "
486 "x%x x 0x%x\n",
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300487 numdimms(pvt->info.max_dod),
488 numrank(pvt->info.max_dod >> 2),
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300489 numbank(pvt->info.max_dod >> 4),
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300490 numrow(pvt->info.max_dod >> 6),
491 numcol(pvt->info.max_dod >> 9));
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300492
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -0300493 pvt->is_registered = 1;
494
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300495 for (i = 0; i < NUM_CHANS; i++) {
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300496 u32 data, dimm_dod[3], value[8];
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300497
498 if (!CH_ACTIVE(pvt, i)) {
499 debugf0("Channel %i is not active\n", i);
500 continue;
501 }
502 if (CH_DISABLED(pvt, i)) {
503 debugf0("Channel %i is disabled\n", i);
504 continue;
505 }
506
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300507 /* Devices 4-6 function 0 */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300508 pci_read_config_dword(pvt->pci_ch[socket][i][0],
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300509 MC_CHANNEL_DIMM_INIT_PARAMS, &data);
510
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300511 pvt->channel[socket][i].ranks = (data & QUAD_RANK_PRESENT) ?
512 4 : 2;
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300513
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300514 if (data & REGISTERED_DIMM)
515 mtype = MEM_RDDR3;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -0300516 else {
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300517 mtype = MEM_DDR3;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -0300518 /*
519 * FIXME: Currently, the driver will use dev 3:2
520 * counter registers only if all memories are registered
521 */
522 pvt->is_registered = 0;
523 }
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300524#if 0
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300525 if (data & THREE_DIMMS_PRESENT)
526 pvt->channel[i].dimms = 3;
527 else if (data & SINGLE_QUAD_RANK_PRESENT)
528 pvt->channel[i].dimms = 1;
529 else
530 pvt->channel[i].dimms = 2;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300531#endif
532
533 /* Devices 4-6 function 1 */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300534 pci_read_config_dword(pvt->pci_ch[socket][i][1],
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300535 MC_DOD_CH_DIMM0, &dimm_dod[0]);
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300536 pci_read_config_dword(pvt->pci_ch[socket][i][1],
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300537 MC_DOD_CH_DIMM1, &dimm_dod[1]);
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300538 pci_read_config_dword(pvt->pci_ch[socket][i][1],
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300539 MC_DOD_CH_DIMM2, &dimm_dod[2]);
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300540
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300541 debugf0("Ch%d phy rd%d, wr%d (0x%08x): "
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300542 "%d ranks, %cDIMMs\n",
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300543 i,
544 RDLCH(pvt->info.ch_map, i), WRLCH(pvt->info.ch_map, i),
545 data,
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300546 pvt->channel[socket][i].ranks,
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300547 (data & REGISTERED_DIMM) ? 'R' : 'U');
Mauro Carvalho Chehab7dd69532009-06-22 22:48:30 -0300548
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300549 for (j = 0; j < 3; j++) {
550 u32 banks, ranks, rows, cols;
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300551 u32 size, npages;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300552
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300553 if (!DIMM_PRESENT(dimm_dod[j]))
554 continue;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300555
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300556 banks = numbank(MC_DOD_NUMBANK(dimm_dod[j]));
557 ranks = numrank(MC_DOD_NUMRANK(dimm_dod[j]));
558 rows = numrow(MC_DOD_NUMROW(dimm_dod[j]));
559 cols = numcol(MC_DOD_NUMCOL(dimm_dod[j]));
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300560
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300561 /* DDR3 has 8 I/O banks */
562 size = (rows * cols * banks * ranks) >> (20 - 3);
563
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300564 pvt->channel[socket][i].dimms++;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300565
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300566 debugf0("\tdimm %d %d Mb offset: %x, "
567 "bank: %d, rank: %d, row: %#x, col: %#x\n",
568 j, size,
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300569 RANKOFFSET(dimm_dod[j]),
570 banks, ranks, rows, cols);
571
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300572#if PAGE_SHIFT > 20
573 npages = size >> (PAGE_SHIFT - 20);
574#else
575 npages = size << (20 - PAGE_SHIFT);
576#endif
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300577
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -0300578 csr = &mci->csrows[*csrow];
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300579 csr->first_page = last_page + 1;
580 last_page += npages;
581 csr->last_page = last_page;
582 csr->nr_pages = npages;
583
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300584 csr->page_mask = 0;
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300585 csr->grain = 8;
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -0300586 csr->csrow_idx = *csrow;
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300587 csr->nr_channels = 1;
588
589 csr->channels[0].chan_idx = i;
590 csr->channels[0].ce_count = 0;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300591
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -0300592 pvt->csrow_map[socket][i][j] = *csrow;
593
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300594 switch (banks) {
595 case 4:
596 csr->dtype = DEV_X4;
597 break;
598 case 8:
599 csr->dtype = DEV_X8;
600 break;
601 case 16:
602 csr->dtype = DEV_X16;
603 break;
604 default:
605 csr->dtype = DEV_UNKNOWN;
606 }
607
608 csr->edac_mode = mode;
609 csr->mtype = mtype;
610
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -0300611 (*csrow)++;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300612 }
613
614 pci_read_config_dword(pdev, MC_SAG_CH_0, &value[0]);
615 pci_read_config_dword(pdev, MC_SAG_CH_1, &value[1]);
616 pci_read_config_dword(pdev, MC_SAG_CH_2, &value[2]);
617 pci_read_config_dword(pdev, MC_SAG_CH_3, &value[3]);
618 pci_read_config_dword(pdev, MC_SAG_CH_4, &value[4]);
619 pci_read_config_dword(pdev, MC_SAG_CH_5, &value[5]);
620 pci_read_config_dword(pdev, MC_SAG_CH_6, &value[6]);
621 pci_read_config_dword(pdev, MC_SAG_CH_7, &value[7]);
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300622 debugf1("\t[%i] DIVBY3\tREMOVED\tOFFSET\n", i);
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300623 for (j = 0; j < 8; j++)
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300624 debugf1("\t\t%#x\t%#x\t%#x\n",
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300625 (value[j] >> 27) & 0x1,
626 (value[j] >> 24) & 0x7,
627 (value[j] && ((1 << 24) - 1)));
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300628 }
629
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300630 return 0;
631}
632
633/****************************************************************************
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300634 Error insertion routines
635 ****************************************************************************/
636
637/* The i7core has independent error injection features per channel.
638 However, to have a simpler code, we don't allow enabling error injection
639 on more than one channel.
640 Also, since a change at an inject parameter will be applied only at enable,
641 we're disabling error injection on all write calls to the sysfs nodes that
642 controls the error code injection.
643 */
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300644static int disable_inject(struct mem_ctl_info *mci)
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300645{
646 struct i7core_pvt *pvt = mci->pvt_info;
647
648 pvt->inject.enable = 0;
649
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300650 if (!pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0])
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300651 return -ENODEV;
652
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300653 pci_write_config_dword(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0],
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -0300654 MC_CHANNEL_ERROR_INJECT, 0);
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300655
656 return 0;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300657}
658
659/*
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300660 * i7core inject inject.socket
661 *
662 * accept and store error injection inject.socket value
663 */
664static ssize_t i7core_inject_socket_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 rc = strict_strtoul(data, 10, &value);
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300672 if ((rc < 0) || (value >= pvt->sockets))
Mauro Carvalho Chehab2068def2009-08-05 19:28:27 -0300673 return -EIO;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300674
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -0300675 pvt->inject.socket = (u32) value;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300676 return count;
677}
678
679static ssize_t i7core_inject_socket_show(struct mem_ctl_info *mci,
680 char *data)
681{
682 struct i7core_pvt *pvt = mci->pvt_info;
683 return sprintf(data, "%d\n", pvt->inject.socket);
684}
685
686/*
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300687 * i7core inject inject.section
688 *
689 * accept and store error injection inject.section value
690 * bit 0 - refers to the lower 32-byte half cacheline
691 * bit 1 - refers to the upper 32-byte half cacheline
692 */
693static ssize_t i7core_inject_section_store(struct mem_ctl_info *mci,
694 const char *data, size_t count)
695{
696 struct i7core_pvt *pvt = mci->pvt_info;
697 unsigned long value;
698 int rc;
699
700 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300701 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300702
703 rc = strict_strtoul(data, 10, &value);
704 if ((rc < 0) || (value > 3))
Mauro Carvalho Chehab2068def2009-08-05 19:28:27 -0300705 return -EIO;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300706
707 pvt->inject.section = (u32) value;
708 return count;
709}
710
711static ssize_t i7core_inject_section_show(struct mem_ctl_info *mci,
712 char *data)
713{
714 struct i7core_pvt *pvt = mci->pvt_info;
715 return sprintf(data, "0x%08x\n", pvt->inject.section);
716}
717
718/*
719 * i7core inject.type
720 *
721 * accept and store error injection inject.section value
722 * bit 0 - repeat enable - Enable error repetition
723 * bit 1 - inject ECC error
724 * bit 2 - inject parity error
725 */
726static ssize_t i7core_inject_type_store(struct mem_ctl_info *mci,
727 const char *data, size_t count)
728{
729 struct i7core_pvt *pvt = mci->pvt_info;
730 unsigned long value;
731 int rc;
732
733 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300734 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300735
736 rc = strict_strtoul(data, 10, &value);
737 if ((rc < 0) || (value > 7))
Mauro Carvalho Chehab2068def2009-08-05 19:28:27 -0300738 return -EIO;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300739
740 pvt->inject.type = (u32) value;
741 return count;
742}
743
744static ssize_t i7core_inject_type_show(struct mem_ctl_info *mci,
745 char *data)
746{
747 struct i7core_pvt *pvt = mci->pvt_info;
748 return sprintf(data, "0x%08x\n", pvt->inject.type);
749}
750
751/*
752 * i7core_inject_inject.eccmask_store
753 *
754 * The type of error (UE/CE) will depend on the inject.eccmask value:
755 * Any bits set to a 1 will flip the corresponding ECC bit
756 * Correctable errors can be injected by flipping 1 bit or the bits within
757 * a symbol pair (2 consecutive aligned 8-bit pairs - i.e. 7:0 and 15:8 or
758 * 23:16 and 31:24). Flipping bits in two symbol pairs will cause an
759 * uncorrectable error to be injected.
760 */
761static ssize_t i7core_inject_eccmask_store(struct mem_ctl_info *mci,
762 const char *data, size_t count)
763{
764 struct i7core_pvt *pvt = mci->pvt_info;
765 unsigned long value;
766 int rc;
767
768 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300769 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300770
771 rc = strict_strtoul(data, 10, &value);
772 if (rc < 0)
Mauro Carvalho Chehab2068def2009-08-05 19:28:27 -0300773 return -EIO;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300774
775 pvt->inject.eccmask = (u32) value;
776 return count;
777}
778
779static ssize_t i7core_inject_eccmask_show(struct mem_ctl_info *mci,
780 char *data)
781{
782 struct i7core_pvt *pvt = mci->pvt_info;
783 return sprintf(data, "0x%08x\n", pvt->inject.eccmask);
784}
785
786/*
787 * i7core_addrmatch
788 *
789 * The type of error (UE/CE) will depend on the inject.eccmask value:
790 * Any bits set to a 1 will flip the corresponding ECC bit
791 * Correctable errors can be injected by flipping 1 bit or the bits within
792 * a symbol pair (2 consecutive aligned 8-bit pairs - i.e. 7:0 and 15:8 or
793 * 23:16 and 31:24). Flipping bits in two symbol pairs will cause an
794 * uncorrectable error to be injected.
795 */
796static ssize_t i7core_inject_addrmatch_store(struct mem_ctl_info *mci,
797 const char *data, size_t count)
798{
799 struct i7core_pvt *pvt = mci->pvt_info;
800 char *cmd, *val;
801 long value;
802 int rc;
803
804 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300805 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300806
807 do {
808 cmd = strsep((char **) &data, ":");
809 if (!cmd)
810 break;
811 val = strsep((char **) &data, " \n\t");
812 if (!val)
813 return cmd - data;
814
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300815 if (!strcasecmp(val, "any"))
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300816 value = -1;
817 else {
818 rc = strict_strtol(val, 10, &value);
819 if ((rc < 0) || (value < 0))
820 return cmd - data;
821 }
822
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300823 if (!strcasecmp(cmd, "channel")) {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300824 if (value < 3)
825 pvt->inject.channel = value;
826 else
827 return cmd - data;
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300828 } else if (!strcasecmp(cmd, "dimm")) {
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300829 if (value < 3)
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300830 pvt->inject.dimm = value;
831 else
832 return cmd - data;
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300833 } else if (!strcasecmp(cmd, "rank")) {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300834 if (value < 4)
835 pvt->inject.rank = value;
836 else
837 return cmd - data;
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300838 } else if (!strcasecmp(cmd, "bank")) {
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300839 if (value < 32)
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300840 pvt->inject.bank = value;
841 else
842 return cmd - data;
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300843 } else if (!strcasecmp(cmd, "page")) {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300844 if (value <= 0xffff)
845 pvt->inject.page = value;
846 else
847 return cmd - data;
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300848 } else if (!strcasecmp(cmd, "col") ||
849 !strcasecmp(cmd, "column")) {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300850 if (value <= 0x3fff)
851 pvt->inject.col = value;
852 else
853 return cmd - data;
854 }
855 } while (1);
856
857 return count;
858}
859
860static ssize_t i7core_inject_addrmatch_show(struct mem_ctl_info *mci,
861 char *data)
862{
863 struct i7core_pvt *pvt = mci->pvt_info;
864 char channel[4], dimm[4], bank[4], rank[4], page[7], col[7];
865
866 if (pvt->inject.channel < 0)
867 sprintf(channel, "any");
868 else
869 sprintf(channel, "%d", pvt->inject.channel);
870 if (pvt->inject.dimm < 0)
871 sprintf(dimm, "any");
872 else
873 sprintf(dimm, "%d", pvt->inject.dimm);
874 if (pvt->inject.bank < 0)
875 sprintf(bank, "any");
876 else
877 sprintf(bank, "%d", pvt->inject.bank);
878 if (pvt->inject.rank < 0)
879 sprintf(rank, "any");
880 else
881 sprintf(rank, "%d", pvt->inject.rank);
882 if (pvt->inject.page < 0)
883 sprintf(page, "any");
884 else
885 sprintf(page, "0x%04x", pvt->inject.page);
886 if (pvt->inject.col < 0)
887 sprintf(col, "any");
888 else
889 sprintf(col, "0x%04x", pvt->inject.col);
890
891 return sprintf(data, "channel: %s\ndimm: %s\nbank: %s\n"
892 "rank: %s\npage: %s\ncolumn: %s\n",
893 channel, dimm, bank, rank, page, col);
894}
895
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300896static int write_and_test(struct pci_dev *dev, int where, u32 val)
897{
898 u32 read;
899 int count;
900
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -0300901 debugf0("setting pci %02x:%02x.%x reg=%02x value=%08x\n",
902 dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn),
903 where, val);
904
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300905 for (count = 0; count < 10; count++) {
906 if (count)
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -0300907 msleep(100);
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300908 pci_write_config_dword(dev, where, val);
909 pci_read_config_dword(dev, where, &read);
910
911 if (read == val)
912 return 0;
913 }
914
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -0300915 i7core_printk(KERN_ERR, "Error during set pci %02x:%02x.%x reg=%02x "
916 "write=%08x. Read=%08x\n",
917 dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn),
918 where, val, read);
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300919
920 return -EINVAL;
921}
922
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300923/*
924 * This routine prepares the Memory Controller for error injection.
925 * The error will be injected when some process tries to write to the
926 * memory that matches the given criteria.
927 * The criteria can be set in terms of a mask where dimm, rank, bank, page
928 * and col can be specified.
929 * A -1 value for any of the mask items will make the MCU to ignore
930 * that matching criteria for error injection.
931 *
932 * It should be noticed that the error will only happen after a write operation
933 * on a memory that matches the condition. if REPEAT_EN is not enabled at
934 * inject mask, then it will produce just one error. Otherwise, it will repeat
935 * until the injectmask would be cleaned.
936 *
937 * FIXME: This routine assumes that MAXNUMDIMMS value of MC_MAX_DOD
938 * is reliable enough to check if the MC is using the
939 * three channels. However, this is not clear at the datasheet.
940 */
941static ssize_t i7core_inject_enable_store(struct mem_ctl_info *mci,
942 const char *data, size_t count)
943{
944 struct i7core_pvt *pvt = mci->pvt_info;
945 u32 injectmask;
946 u64 mask = 0;
947 int rc;
948 long enable;
949
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300950 if (!pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0])
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300951 return 0;
952
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300953 rc = strict_strtoul(data, 10, &enable);
954 if ((rc < 0))
955 return 0;
956
957 if (enable) {
958 pvt->inject.enable = 1;
959 } else {
960 disable_inject(mci);
961 return count;
962 }
963
964 /* Sets pvt->inject.dimm mask */
965 if (pvt->inject.dimm < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300966 mask |= 1L << 41;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300967 else {
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300968 if (pvt->channel[pvt->inject.socket][pvt->inject.channel].dimms > 2)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300969 mask |= (pvt->inject.dimm & 0x3L) << 35;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300970 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300971 mask |= (pvt->inject.dimm & 0x1L) << 36;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300972 }
973
974 /* Sets pvt->inject.rank mask */
975 if (pvt->inject.rank < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300976 mask |= 1L << 40;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300977 else {
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300978 if (pvt->channel[pvt->inject.socket][pvt->inject.channel].dimms > 2)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300979 mask |= (pvt->inject.rank & 0x1L) << 34;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300980 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300981 mask |= (pvt->inject.rank & 0x3L) << 34;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300982 }
983
984 /* Sets pvt->inject.bank mask */
985 if (pvt->inject.bank < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300986 mask |= 1L << 39;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300987 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300988 mask |= (pvt->inject.bank & 0x15L) << 30;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300989
990 /* Sets pvt->inject.page mask */
991 if (pvt->inject.page < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300992 mask |= 1L << 38;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300993 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300994 mask |= (pvt->inject.page & 0xffffL) << 14;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300995
996 /* Sets pvt->inject.column mask */
997 if (pvt->inject.col < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300998 mask |= 1L << 37;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300999 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -03001000 mask |= (pvt->inject.col & 0x3fffL);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001001
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001002 /*
1003 * bit 0: REPEAT_EN
1004 * bits 1-2: MASK_HALF_CACHELINE
1005 * bit 3: INJECT_ECC
1006 * bit 4: INJECT_ADDR_PARITY
1007 */
1008
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -03001009 injectmask = (pvt->inject.type & 1) |
1010 (pvt->inject.section & 0x3) << 1 |
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001011 (pvt->inject.type & 0x6) << (3 - 1);
1012
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -03001013 /* Unlock writes to registers - this register is write only */
1014 pci_write_config_dword(pvt->pci_noncore[pvt->inject.socket],
1015 MC_CFG_CONTROL, 0x2);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001016
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -03001017 write_and_test(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0],
1018 MC_CHANNEL_ADDR_MATCH, mask);
1019 write_and_test(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0],
1020 MC_CHANNEL_ADDR_MATCH + 4, mask >> 32L);
1021
1022 write_and_test(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0],
1023 MC_CHANNEL_ERROR_MASK, pvt->inject.eccmask);
1024
1025 write_and_test(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0],
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -03001026 MC_CHANNEL_ERROR_INJECT, injectmask);
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -03001027
1028 /*
1029 * This is something undocumented, based on my tests
1030 * Without writing 8 to this register, errors aren't injected. Not sure
1031 * why.
1032 */
1033 pci_write_config_dword(pvt->pci_noncore[pvt->inject.socket],
1034 MC_CFG_CONTROL, 8);
1035
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001036 debugf0("Error inject addr match 0x%016llx, ecc 0x%08x,"
1037 " inject 0x%08x\n",
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001038 mask, pvt->inject.eccmask, injectmask);
1039
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -03001040
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001041 return count;
1042}
1043
1044static ssize_t i7core_inject_enable_show(struct mem_ctl_info *mci,
1045 char *data)
1046{
1047 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -03001048 u32 injectmask;
1049
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001050 pci_read_config_dword(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0],
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -03001051 MC_CHANNEL_ERROR_INJECT, &injectmask);
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -03001052
1053 debugf0("Inject error read: 0x%018x\n", injectmask);
1054
1055 if (injectmask & 0x0c)
1056 pvt->inject.enable = 1;
1057
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001058 return sprintf(data, "%d\n", pvt->inject.enable);
1059}
1060
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001061static ssize_t i7core_ce_regs_show(struct mem_ctl_info *mci, char *data)
1062{
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001063 unsigned i, j, count, total = 0;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001064 struct i7core_pvt *pvt = mci->pvt_info;
1065
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001066 for (i = 0; i < pvt->sockets; i++) {
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001067 if (!pvt->ce_count_available[i]) {
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001068 count = sprintf(data, "socket 0 data unavailable\n");
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001069 continue;
1070 }
1071 if (!pvt->is_registered)
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001072 count = sprintf(data, "socket %d, dimm0: %lu\n"
1073 "dimm1: %lu\ndimm2: %lu\n",
1074 i,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001075 pvt->udimm_ce_count[i][0],
1076 pvt->udimm_ce_count[i][1],
1077 pvt->udimm_ce_count[i][2]);
1078 else
1079 for (j = 0; j < NUM_CHANS; j++) {
1080 count = sprintf(data, "socket %d, channel %d"
1081 "dimm0: %lu\n"
1082 "dimm1: %lu\ndimm2: %lu\n",
1083 i, j,
1084 pvt->rdimm_ce_count[i][j][0],
1085 pvt->rdimm_ce_count[i][j][1],
1086 pvt->rdimm_ce_count[i][j][2]);
1087 }
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001088 data += count;
1089 total += count;
1090 }
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001091
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001092 return total;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001093}
1094
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001095/*
1096 * Sysfs struct
1097 */
1098static struct mcidev_sysfs_attribute i7core_inj_attrs[] = {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001099 {
1100 .attr = {
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001101 .name = "inject_socket",
1102 .mode = (S_IRUGO | S_IWUSR)
1103 },
1104 .show = i7core_inject_socket_show,
1105 .store = i7core_inject_socket_store,
1106 }, {
1107 .attr = {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001108 .name = "inject_section",
1109 .mode = (S_IRUGO | S_IWUSR)
1110 },
1111 .show = i7core_inject_section_show,
1112 .store = i7core_inject_section_store,
1113 }, {
1114 .attr = {
1115 .name = "inject_type",
1116 .mode = (S_IRUGO | S_IWUSR)
1117 },
1118 .show = i7core_inject_type_show,
1119 .store = i7core_inject_type_store,
1120 }, {
1121 .attr = {
1122 .name = "inject_eccmask",
1123 .mode = (S_IRUGO | S_IWUSR)
1124 },
1125 .show = i7core_inject_eccmask_show,
1126 .store = i7core_inject_eccmask_store,
1127 }, {
1128 .attr = {
1129 .name = "inject_addrmatch",
1130 .mode = (S_IRUGO | S_IWUSR)
1131 },
1132 .show = i7core_inject_addrmatch_show,
1133 .store = i7core_inject_addrmatch_store,
1134 }, {
1135 .attr = {
1136 .name = "inject_enable",
1137 .mode = (S_IRUGO | S_IWUSR)
1138 },
1139 .show = i7core_inject_enable_show,
1140 .store = i7core_inject_enable_store,
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001141 }, {
1142 .attr = {
1143 .name = "corrected_error_counts",
1144 .mode = (S_IRUGO | S_IWUSR)
1145 },
1146 .show = i7core_ce_regs_show,
1147 .store = NULL,
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001148 },
1149};
1150
1151/****************************************************************************
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001152 Device initialization routines: put/get, init/exit
1153 ****************************************************************************/
1154
1155/*
1156 * i7core_put_devices 'put' all the devices that we have
1157 * reserved via 'get'
1158 */
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001159static void i7core_put_devices(void)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001160{
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001161 int i, j;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001162
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001163 for (i = 0; i < NUM_SOCKETS; i++)
1164 for (j = 0; j < N_DEVS; j++)
1165 pci_dev_put(pci_devs[j].pdev[i]);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001166}
1167
1168/*
1169 * i7core_get_devices Find and perform 'get' operation on the MCH's
1170 * device/functions we want to reference for this driver
1171 *
1172 * Need to 'get' device 16 func 1 and func 2
1173 */
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001174int i7core_get_onedevice(struct pci_dev **prev, int devno)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001175{
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001176 struct pci_dev *pdev = NULL;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001177 u8 bus = 0;
1178 u8 socket = 0;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001179
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001180 pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
1181 pci_devs[devno].dev_id, *prev);
1182
1183 /*
1184 * On Xeon 55xx, the Intel Quckpath Arch Generic Non-core pci buses
1185 * aren't announced by acpi. So, we need to use a legacy scan probing
1186 * to detect them
1187 */
1188 if (unlikely(!pdev && !devno && !prev)) {
1189 pcibios_scan_specific_bus(254);
1190 pcibios_scan_specific_bus(255);
1191
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001192 pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001193 pci_devs[devno].dev_id, *prev);
1194 }
Mauro Carvalho Chehabd1fd4fb2009-07-10 18:39:53 -03001195
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001196 /*
1197 * On Xeon 55xx, the Intel Quckpath Arch Generic Non-core regs
1198 * is at addr 8086:2c40, instead of 8086:2c41. So, we need
1199 * to probe for the alternate address in case of failure
1200 */
1201 if (pci_devs[devno].dev_id == PCI_DEVICE_ID_INTEL_I7_NOCORE && !pdev)
1202 pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
1203 PCI_DEVICE_ID_INTEL_I7_NOCORE_ALT, *prev);
Mauro Carvalho Chehabd1fd4fb2009-07-10 18:39:53 -03001204
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001205 if (!pdev) {
1206 if (*prev) {
1207 *prev = pdev;
1208 return 0;
Mauro Carvalho Chehabd1fd4fb2009-07-10 18:39:53 -03001209 }
1210
Mauro Carvalho Chehab310cbb72009-07-17 00:09:10 -03001211 /*
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001212 * Dev 3 function 2 only exists on chips with RDIMMs
1213 * so, it is ok to not found it
Mauro Carvalho Chehab310cbb72009-07-17 00:09:10 -03001214 */
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001215 if ((pci_devs[devno].dev == 3) && (pci_devs[devno].func == 2)) {
1216 *prev = pdev;
1217 return 0;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001218 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001219
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001220 i7core_printk(KERN_ERR,
1221 "Device not found: dev %02x.%d PCI ID %04x:%04x\n",
1222 pci_devs[devno].dev, pci_devs[devno].func,
1223 PCI_VENDOR_ID_INTEL, pci_devs[devno].dev_id);
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001224
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001225 /* End of list, leave */
1226 return -ENODEV;
1227 }
1228 bus = pdev->bus->number;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001229
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001230 if (bus == 0x3f)
1231 socket = 0;
1232 else
1233 socket = 255 - bus;
1234
1235 if (socket >= NUM_SOCKETS) {
1236 i7core_printk(KERN_ERR,
1237 "Unexpected socket for "
1238 "dev %02x:%02x.%d PCI ID %04x:%04x\n",
1239 bus, pci_devs[devno].dev, pci_devs[devno].func,
1240 PCI_VENDOR_ID_INTEL, pci_devs[devno].dev_id);
1241 pci_dev_put(pdev);
1242 return -ENODEV;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001243 }
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001244
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001245 if (pci_devs[devno].pdev[socket]) {
1246 i7core_printk(KERN_ERR,
1247 "Duplicated device for "
1248 "dev %02x:%02x.%d PCI ID %04x:%04x\n",
1249 bus, pci_devs[devno].dev, pci_devs[devno].func,
1250 PCI_VENDOR_ID_INTEL, pci_devs[devno].dev_id);
1251 pci_dev_put(pdev);
1252 return -ENODEV;
1253 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001254
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001255 pci_devs[devno].pdev[socket] = pdev;
1256
1257 /* Sanity check */
1258 if (unlikely(PCI_SLOT(pdev->devfn) != pci_devs[devno].dev ||
1259 PCI_FUNC(pdev->devfn) != pci_devs[devno].func)) {
1260 i7core_printk(KERN_ERR,
1261 "Device PCI ID %04x:%04x "
1262 "has dev %02x:%02x.%d instead of dev %02x:%02x.%d\n",
1263 PCI_VENDOR_ID_INTEL, pci_devs[devno].dev_id,
1264 bus, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
1265 bus, pci_devs[devno].dev, pci_devs[devno].func);
1266 return -ENODEV;
1267 }
1268
1269 /* Be sure that the device is enabled */
1270 if (unlikely(pci_enable_device(pdev) < 0)) {
1271 i7core_printk(KERN_ERR,
1272 "Couldn't enable "
1273 "dev %02x:%02x.%d PCI ID %04x:%04x\n",
1274 bus, pci_devs[devno].dev, pci_devs[devno].func,
1275 PCI_VENDOR_ID_INTEL, pci_devs[devno].dev_id);
1276 return -ENODEV;
1277 }
1278
1279 i7core_printk(KERN_INFO,
1280 "Registered socket %d "
1281 "dev %02x:%02x.%d PCI ID %04x:%04x\n",
1282 socket, bus, pci_devs[devno].dev, pci_devs[devno].func,
1283 PCI_VENDOR_ID_INTEL, pci_devs[devno].dev_id);
1284
1285 *prev = pdev;
1286
1287 return 0;
1288}
1289
1290static int i7core_get_devices(void)
1291{
1292 int i;
1293 struct pci_dev *pdev = NULL;
1294
1295 for (i = 0; i < N_DEVS; i++) {
1296 pdev = NULL;
1297 do {
1298 if (i7core_get_onedevice(&pdev, i) < 0) {
1299 i7core_put_devices();
1300 return -ENODEV;
1301 }
1302 } while (pdev);
1303 }
1304 return 0;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001305}
1306
1307static int mci_bind_devs(struct mem_ctl_info *mci)
1308{
1309 struct i7core_pvt *pvt = mci->pvt_info;
1310 struct pci_dev *pdev;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001311 int i, j, func, slot;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001312
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001313 for (i = 0; i < pvt->sockets; i++) {
1314 for (j = 0; j < N_DEVS; j++) {
1315 pdev = pci_devs[j].pdev[i];
1316 if (!pdev)
1317 continue;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001318
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001319 func = PCI_FUNC(pdev->devfn);
1320 slot = PCI_SLOT(pdev->devfn);
1321 if (slot == 3) {
1322 if (unlikely(func > MAX_MCR_FUNC))
1323 goto error;
1324 pvt->pci_mcr[i][func] = pdev;
1325 } else if (likely(slot >= 4 && slot < 4 + NUM_CHANS)) {
1326 if (unlikely(func > MAX_CHAN_FUNC))
1327 goto error;
1328 pvt->pci_ch[i][slot - 4][func] = pdev;
1329 } else if (!slot && !func)
1330 pvt->pci_noncore[i] = pdev;
1331 else
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001332 goto error;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001333
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001334 debugf0("Associated fn %d.%d, dev = %p, socket %d\n",
1335 PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
1336 pdev, i);
1337 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001338 }
Mauro Carvalho Chehabe9bd2e72009-07-09 22:14:35 -03001339
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001340 return 0;
1341
1342error:
1343 i7core_printk(KERN_ERR, "Device %d, function %d "
1344 "is out of the expected range\n",
1345 slot, func);
1346 return -EINVAL;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001347}
1348
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001349/****************************************************************************
1350 Error check routines
1351 ****************************************************************************/
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001352static void i7core_rdimm_update_csrow(struct mem_ctl_info *mci, int socket,
1353 int chan, int dimm, int add)
1354{
1355 char *msg;
1356 struct i7core_pvt *pvt = mci->pvt_info;
1357 int row = pvt->csrow_map[socket][chan][dimm], i;
1358
1359 for (i = 0; i < add; i++) {
1360 msg = kasprintf(GFP_KERNEL, "Corrected error "
1361 "(Socket=%d channel=%d dimm=%d",
1362 socket, chan, dimm);
1363
1364 edac_mc_handle_fbd_ce(mci, row, 0, msg);
1365 kfree (msg);
1366 }
1367}
1368
1369static void i7core_rdimm_update_ce_count(struct mem_ctl_info *mci,
1370 int socket, int chan, int new0, int new1, int new2)
1371{
1372 struct i7core_pvt *pvt = mci->pvt_info;
1373 int add0 = 0, add1 = 0, add2 = 0;
1374 /* Updates CE counters if it is not the first time here */
1375 if (pvt->ce_count_available[socket]) {
1376 /* Updates CE counters */
1377
1378 add2 = new2 - pvt->rdimm_last_ce_count[socket][chan][2];
1379 add1 = new1 - pvt->rdimm_last_ce_count[socket][chan][1];
1380 add0 = new0 - pvt->rdimm_last_ce_count[socket][chan][0];
1381
1382 if (add2 < 0)
1383 add2 += 0x7fff;
1384 pvt->rdimm_ce_count[socket][chan][2] += add2;
1385
1386 if (add1 < 0)
1387 add1 += 0x7fff;
1388 pvt->rdimm_ce_count[socket][chan][1] += add1;
1389
1390 if (add0 < 0)
1391 add0 += 0x7fff;
1392 pvt->rdimm_ce_count[socket][chan][0] += add0;
1393 } else
1394 pvt->ce_count_available[socket] = 1;
1395
1396 /* Store the new values */
1397 pvt->rdimm_last_ce_count[socket][chan][2] = new2;
1398 pvt->rdimm_last_ce_count[socket][chan][1] = new1;
1399 pvt->rdimm_last_ce_count[socket][chan][0] = new0;
1400
1401 /*updated the edac core */
1402 if (add0 != 0)
1403 i7core_rdimm_update_csrow(mci, socket, chan, 0, add0);
1404 if (add1 != 0)
1405 i7core_rdimm_update_csrow(mci, socket, chan, 1, add1);
1406 if (add2 != 0)
1407 i7core_rdimm_update_csrow(mci, socket, chan, 2, add2);
1408
1409}
1410
1411static void i7core_rdimm_check_mc_ecc_err(struct mem_ctl_info *mci, u8 socket)
1412{
1413 struct i7core_pvt *pvt = mci->pvt_info;
1414 u32 rcv[3][2];
1415 int i, new0, new1, new2;
1416
1417 /*Read DEV 3: FUN 2: MC_COR_ECC_CNT regs directly*/
1418 pci_read_config_dword(pvt->pci_mcr[socket][2], MC_COR_ECC_CNT_0,
1419 &rcv[0][0]);
1420 pci_read_config_dword(pvt->pci_mcr[socket][2], MC_COR_ECC_CNT_1,
1421 &rcv[0][1]);
1422 pci_read_config_dword(pvt->pci_mcr[socket][2], MC_COR_ECC_CNT_2,
1423 &rcv[1][0]);
1424 pci_read_config_dword(pvt->pci_mcr[socket][2], MC_COR_ECC_CNT_3,
1425 &rcv[1][1]);
1426 pci_read_config_dword(pvt->pci_mcr[socket][2], MC_COR_ECC_CNT_4,
1427 &rcv[2][0]);
1428 pci_read_config_dword(pvt->pci_mcr[socket][2], MC_COR_ECC_CNT_5,
1429 &rcv[2][1]);
1430 for (i = 0 ; i < 3; i++) {
1431 debugf3("MC_COR_ECC_CNT%d = 0x%x; MC_COR_ECC_CNT%d = 0x%x\n",
1432 (i * 2), rcv[i][0], (i * 2) + 1, rcv[i][1]);
1433 /*if the channel has 3 dimms*/
1434 if (pvt->channel[socket][i].dimms > 2) {
1435 new0 = DIMM_BOT_COR_ERR(rcv[i][0]);
1436 new1 = DIMM_TOP_COR_ERR(rcv[i][0]);
1437 new2 = DIMM_BOT_COR_ERR(rcv[i][1]);
1438 } else {
1439 new0 = DIMM_TOP_COR_ERR(rcv[i][0]) +
1440 DIMM_BOT_COR_ERR(rcv[i][0]);
1441 new1 = DIMM_TOP_COR_ERR(rcv[i][1]) +
1442 DIMM_BOT_COR_ERR(rcv[i][1]);
1443 new2 = 0;
1444 }
1445
1446 i7core_rdimm_update_ce_count(mci, socket, i, new0, new1, new2);
1447 }
1448}
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001449
1450/* This function is based on the device 3 function 4 registers as described on:
1451 * Intel Xeon Processor 5500 Series Datasheet Volume 2
1452 * http://www.intel.com/Assets/PDF/datasheet/321322.pdf
1453 * also available at:
1454 * http://www.arrownac.com/manufacturers/intel/s/nehalem/5500-datasheet-v2.pdf
1455 */
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001456static void i7core_udimm_check_mc_ecc_err(struct mem_ctl_info *mci, u8 socket)
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001457{
1458 struct i7core_pvt *pvt = mci->pvt_info;
1459 u32 rcv1, rcv0;
1460 int new0, new1, new2;
1461
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001462 if (!pvt->pci_mcr[socket][4]) {
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -03001463 debugf0("%s MCR registers not found\n", __func__);
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001464 return;
1465 }
1466
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001467 /* Corrected test errors */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001468 pci_read_config_dword(pvt->pci_mcr[socket][4], MC_TEST_ERR_RCV1, &rcv1);
1469 pci_read_config_dword(pvt->pci_mcr[socket][4], MC_TEST_ERR_RCV0, &rcv0);
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001470
1471 /* Store the new values */
1472 new2 = DIMM2_COR_ERR(rcv1);
1473 new1 = DIMM1_COR_ERR(rcv0);
1474 new0 = DIMM0_COR_ERR(rcv0);
1475
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001476 /* Updates CE counters if it is not the first time here */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001477 if (pvt->ce_count_available[socket]) {
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001478 /* Updates CE counters */
1479 int add0, add1, add2;
1480
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001481 add2 = new2 - pvt->udimm_last_ce_count[socket][2];
1482 add1 = new1 - pvt->udimm_last_ce_count[socket][1];
1483 add0 = new0 - pvt->udimm_last_ce_count[socket][0];
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001484
1485 if (add2 < 0)
1486 add2 += 0x7fff;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001487 pvt->udimm_ce_count[socket][2] += add2;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001488
1489 if (add1 < 0)
1490 add1 += 0x7fff;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001491 pvt->udimm_ce_count[socket][1] += add1;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001492
1493 if (add0 < 0)
1494 add0 += 0x7fff;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001495 pvt->udimm_ce_count[socket][0] += add0;
1496
1497 if (add0 | add1 | add2)
1498 i7core_printk(KERN_ERR, "New Corrected error(s): "
1499 "dimm0: +%d, dimm1: +%d, dimm2 +%d\n",
1500 add0, add1, add2);
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001501 } else
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001502 pvt->ce_count_available[socket] = 1;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001503
1504 /* Store the new values */
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001505 pvt->udimm_last_ce_count[socket][2] = new2;
1506 pvt->udimm_last_ce_count[socket][1] = new1;
1507 pvt->udimm_last_ce_count[socket][0] = new0;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001508}
1509
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001510/*
1511 * According with tables E-11 and E-12 of chapter E.3.3 of Intel 64 and IA-32
1512 * Architectures Software Developer’s Manual Volume 3B.
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001513 * Nehalem are defined as family 0x06, model 0x1a
1514 *
1515 * The MCA registers used here are the following ones:
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001516 * struct mce field MCA Register
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001517 * m->status MSR_IA32_MC8_STATUS
1518 * m->addr MSR_IA32_MC8_ADDR
1519 * m->misc MSR_IA32_MC8_MISC
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001520 * In the case of Nehalem, the error information is masked at .status and .misc
1521 * fields
1522 */
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001523static void i7core_mce_output_error(struct mem_ctl_info *mci,
1524 struct mce *m)
1525{
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001526 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001527 char *type, *optype, *err, *msg;
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001528 unsigned long error = m->status & 0x1ff0000l;
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001529 u32 optypenum = (m->status >> 4) & 0x07;
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001530 u32 core_err_cnt = (m->status >> 38) && 0x7fff;
1531 u32 dimm = (m->misc >> 16) & 0x3;
1532 u32 channel = (m->misc >> 18) & 0x3;
1533 u32 syndrome = m->misc >> 32;
1534 u32 errnum = find_first_bit(&error, 32);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001535 int csrow;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001536
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001537 if (m->mcgstatus & 1)
1538 type = "FATAL";
1539 else
1540 type = "NON_FATAL";
1541
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001542 switch (optypenum) {
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -03001543 case 0:
1544 optype = "generic undef request";
1545 break;
1546 case 1:
1547 optype = "read error";
1548 break;
1549 case 2:
1550 optype = "write error";
1551 break;
1552 case 3:
1553 optype = "addr/cmd error";
1554 break;
1555 case 4:
1556 optype = "scrubbing error";
1557 break;
1558 default:
1559 optype = "reserved";
1560 break;
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001561 }
1562
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001563 switch (errnum) {
1564 case 16:
1565 err = "read ECC error";
1566 break;
1567 case 17:
1568 err = "RAS ECC error";
1569 break;
1570 case 18:
1571 err = "write parity error";
1572 break;
1573 case 19:
1574 err = "redundacy loss";
1575 break;
1576 case 20:
1577 err = "reserved";
1578 break;
1579 case 21:
1580 err = "memory range error";
1581 break;
1582 case 22:
1583 err = "RTID out of range";
1584 break;
1585 case 23:
1586 err = "address parity error";
1587 break;
1588 case 24:
1589 err = "byte enable parity error";
1590 break;
1591 default:
1592 err = "unknown";
1593 }
1594
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001595 /* FIXME: should convert addr into bank and rank information */
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001596 msg = kasprintf(GFP_ATOMIC,
Mauro Carvalho Chehab3a7dde72009-07-18 12:20:04 -03001597 "%s (addr = 0x%08llx, socket=%d, Dimm=%d, Channel=%d, "
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001598 "syndrome=0x%08x, count=%d, Err=%08llx:%08llx (%s: %s))\n",
Mauro Carvalho Chehab3a7dde72009-07-18 12:20:04 -03001599 type, (long long) m->addr, m->cpu, dimm, channel,
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001600 syndrome, core_err_cnt, (long long)m->status,
1601 (long long)m->misc, optype, err);
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001602
1603 debugf0("%s", msg);
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001604
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001605 csrow = pvt->csrow_map[m->cpu][channel][dimm];
1606
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001607 /* Call the helper to output message */
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001608 if (m->mcgstatus & 1)
1609 edac_mc_handle_fbd_ue(mci, csrow, 0,
1610 0 /* FIXME: should be channel here */, msg);
1611 else if (!pvt->is_registered)
1612 edac_mc_handle_fbd_ce(mci, csrow,
1613 0 /* FIXME: should be channel here */, msg);
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001614
1615 kfree(msg);
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001616}
1617
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001618/*
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001619 * i7core_check_error Retrieve and process errors reported by the
1620 * hardware. Called by the Core module.
1621 */
1622static void i7core_check_error(struct mem_ctl_info *mci)
1623{
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001624 struct i7core_pvt *pvt = mci->pvt_info;
1625 int i;
1626 unsigned count = 0;
1627 struct mce *m = NULL;
1628 unsigned long flags;
1629
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001630 /* Copy all mce errors into a temporary buffer */
1631 spin_lock_irqsave(&pvt->mce_lock, flags);
1632 if (pvt->mce_count) {
1633 m = kmalloc(sizeof(*m) * pvt->mce_count, GFP_ATOMIC);
1634 if (m) {
1635 count = pvt->mce_count;
1636 memcpy(m, &pvt->mce_entry, sizeof(*m) * count);
1637 }
1638 pvt->mce_count = 0;
1639 }
1640 spin_unlock_irqrestore(&pvt->mce_lock, flags);
1641
1642 /* proccess mcelog errors */
1643 for (i = 0; i < count; i++)
1644 i7core_mce_output_error(mci, &m[i]);
1645
1646 kfree(m);
1647
1648 /* check memory count errors */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001649 for (i = 0; i < pvt->sockets; i++)
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001650 if (!pvt->is_registered)
1651 i7core_udimm_check_mc_ecc_err(mci, i);
1652 else
1653 i7core_rdimm_check_mc_ecc_err(mci, i);
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001654}
1655
1656/*
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001657 * i7core_mce_check_error Replicates mcelog routine to get errors
1658 * This routine simply queues mcelog errors, and
1659 * return. The error itself should be handled later
1660 * by i7core_check_error.
1661 */
1662static int i7core_mce_check_error(void *priv, struct mce *mce)
1663{
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001664 struct mem_ctl_info *mci = priv;
1665 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001666 unsigned long flags;
1667
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001668 /*
1669 * Just let mcelog handle it if the error is
1670 * outside the memory controller
1671 */
1672 if (((mce->status & 0xffff) >> 7) != 1)
1673 return 0;
1674
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001675 /* Bank 8 registers are the only ones that we know how to handle */
1676 if (mce->bank != 8)
1677 return 0;
1678
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001679 spin_lock_irqsave(&pvt->mce_lock, flags);
1680 if (pvt->mce_count < MCE_LOG_LEN) {
1681 memcpy(&pvt->mce_entry[pvt->mce_count], mce, sizeof(*mce));
1682 pvt->mce_count++;
1683 }
1684 spin_unlock_irqrestore(&pvt->mce_lock, flags);
1685
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001686 /* Handle fatal errors immediately */
1687 if (mce->mcgstatus & 1)
1688 i7core_check_error(mci);
1689
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001690 /* Advice mcelog that the error were handled */
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001691 return 1;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001692}
1693
1694/*
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001695 * i7core_probe Probe for ONE instance of device to see if it is
1696 * present.
1697 * return:
1698 * 0 for FOUND a device
1699 * < 0 for error code
1700 */
1701static int __devinit i7core_probe(struct pci_dev *pdev,
1702 const struct pci_device_id *id)
1703{
1704 struct mem_ctl_info *mci;
1705 struct i7core_pvt *pvt;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001706 int num_channels = 0;
1707 int num_csrows = 0;
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -03001708 int csrow = 0;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001709 int dev_idx = id->driver_data;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001710 int rc, i;
1711 u8 sockets;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001712
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001713 if (unlikely(dev_idx >= ARRAY_SIZE(i7core_devs)))
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001714 return -EINVAL;
1715
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001716 /* get the pci devices we want to reserve for our use */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001717 rc = i7core_get_devices();
1718 if (unlikely(rc < 0))
1719 return rc;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001720
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001721 sockets = 1;
1722 for (i = NUM_SOCKETS - 1; i > 0; i--)
1723 if (pci_devs[0].pdev[i]) {
1724 sockets = i + 1;
1725 break;
1726 }
1727
1728 for (i = 0; i < sockets; i++) {
1729 int channels;
1730 int csrows;
1731
1732 /* Check the number of active and not disabled channels */
1733 rc = i7core_get_active_channels(i, &channels, &csrows);
1734 if (unlikely(rc < 0))
1735 goto fail0;
1736
1737 num_channels += channels;
1738 num_csrows += csrows;
1739 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001740
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001741 /* allocate a new MC control structure */
1742 mci = edac_mc_alloc(sizeof(*pvt), num_csrows, num_channels, 0);
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001743 if (unlikely(!mci)) {
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001744 rc = -ENOMEM;
1745 goto fail0;
1746 }
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001747
1748 debugf0("MC: " __FILE__ ": %s(): mci = %p\n", __func__, mci);
1749
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001750 mci->dev = &pdev->dev; /* record ptr to the generic device */
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001751 pvt = mci->pvt_info;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001752 memset(pvt, 0, sizeof(*pvt));
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001753 pvt->sockets = sockets;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001754 mci->mc_idx = 0;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001755
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001756 /*
1757 * FIXME: how to handle RDDR3 at MCI level? It is possible to have
1758 * Mixed RDDR3/UDDR3 with Nehalem, provided that they are on different
1759 * memory channels
1760 */
1761 mci->mtype_cap = MEM_FLAG_DDR3;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001762 mci->edac_ctl_cap = EDAC_FLAG_NONE;
1763 mci->edac_cap = EDAC_FLAG_NONE;
1764 mci->mod_name = "i7core_edac.c";
1765 mci->mod_ver = I7CORE_REVISION;
1766 mci->ctl_name = i7core_devs[dev_idx].ctl_name;
1767 mci->dev_name = pci_name(pdev);
1768 mci->ctl_page_to_phys = NULL;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001769 mci->mc_driver_sysfs_attributes = i7core_inj_attrs;
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001770 /* Set the function pointer to an actual operation function */
1771 mci->edac_check = i7core_check_error;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001772
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001773 /* Store pci devices at mci for faster access */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001774 rc = mci_bind_devs(mci);
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001775 if (unlikely(rc < 0))
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001776 goto fail1;
1777
1778 /* Get dimm basic config */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001779 for (i = 0; i < sockets; i++)
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -03001780 get_dimm_config(mci, &csrow, i);
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001781
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001782 /* add this new MC control structure to EDAC's list of MCs */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001783 if (unlikely(edac_mc_add_mc(mci))) {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001784 debugf0("MC: " __FILE__
1785 ": %s(): failed edac_mc_add_mc()\n", __func__);
1786 /* FIXME: perhaps some code should go here that disables error
1787 * reporting if we just enabled it
1788 */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001789
1790 rc = -EINVAL;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001791 goto fail1;
1792 }
1793
1794 /* allocating generic PCI control info */
1795 i7core_pci = edac_pci_create_generic_ctl(&pdev->dev, EDAC_MOD_STR);
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001796 if (unlikely(!i7core_pci)) {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001797 printk(KERN_WARNING
1798 "%s(): Unable to create PCI control\n",
1799 __func__);
1800 printk(KERN_WARNING
1801 "%s(): PCI error report via EDAC not setup\n",
1802 __func__);
1803 }
1804
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001805 /* Default error mask is any memory */
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001806 pvt->inject.channel = 0;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001807 pvt->inject.dimm = -1;
1808 pvt->inject.rank = -1;
1809 pvt->inject.bank = -1;
1810 pvt->inject.page = -1;
1811 pvt->inject.col = -1;
1812
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001813 /* Registers on edac_mce in order to receive memory errors */
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001814 pvt->edac_mce.priv = mci;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001815 pvt->edac_mce.check_error = i7core_mce_check_error;
1816 spin_lock_init(&pvt->mce_lock);
1817
1818 rc = edac_mce_register(&pvt->edac_mce);
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -03001819 if (unlikely(rc < 0)) {
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001820 debugf0("MC: " __FILE__
1821 ": %s(): failed edac_mce_register()\n", __func__);
1822 goto fail1;
1823 }
1824
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001825 i7core_printk(KERN_INFO, "Driver loaded.\n");
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001826
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001827 return 0;
1828
1829fail1:
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001830 edac_mc_free(mci);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001831
1832fail0:
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001833 i7core_put_devices();
1834 return rc;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001835}
1836
1837/*
1838 * i7core_remove destructor for one instance of device
1839 *
1840 */
1841static void __devexit i7core_remove(struct pci_dev *pdev)
1842{
1843 struct mem_ctl_info *mci;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001844 struct i7core_pvt *pvt;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001845
1846 debugf0(__FILE__ ": %s()\n", __func__);
1847
1848 if (i7core_pci)
1849 edac_pci_release_generic_ctl(i7core_pci);
1850
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001851
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001852 mci = edac_mc_del_mc(&pdev->dev);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001853 if (!mci)
1854 return;
1855
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001856 /* Unregisters on edac_mce in order to receive memory errors */
1857 pvt = mci->pvt_info;
1858 edac_mce_unregister(&pvt->edac_mce);
1859
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001860 /* retrieve references to resources, and free those resources */
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001861 i7core_put_devices();
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001862
1863 edac_mc_free(mci);
1864}
1865
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001866MODULE_DEVICE_TABLE(pci, i7core_pci_tbl);
1867
1868/*
1869 * i7core_driver pci_driver structure for this module
1870 *
1871 */
1872static struct pci_driver i7core_driver = {
1873 .name = "i7core_edac",
1874 .probe = i7core_probe,
1875 .remove = __devexit_p(i7core_remove),
1876 .id_table = i7core_pci_tbl,
1877};
1878
1879/*
1880 * i7core_init Module entry function
1881 * Try to initialize this module for its devices
1882 */
1883static int __init i7core_init(void)
1884{
1885 int pci_rc;
1886
1887 debugf2("MC: " __FILE__ ": %s()\n", __func__);
1888
1889 /* Ensure that the OPSTATE is set correctly for POLL or NMI */
1890 opstate_init();
1891
1892 pci_rc = pci_register_driver(&i7core_driver);
1893
Mauro Carvalho Chehab3ef288a2009-09-02 23:43:33 -03001894 if (pci_rc >= 0)
1895 return 0;
1896
1897 i7core_printk(KERN_ERR, "Failed to register device with error %d.\n",
1898 pci_rc);
1899
1900 return pci_rc;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001901}
1902
1903/*
1904 * i7core_exit() Module exit function
1905 * Unregister the driver
1906 */
1907static void __exit i7core_exit(void)
1908{
1909 debugf2("MC: " __FILE__ ": %s()\n", __func__);
1910 pci_unregister_driver(&i7core_driver);
1911}
1912
1913module_init(i7core_init);
1914module_exit(i7core_exit);
1915
1916MODULE_LICENSE("GPL");
1917MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
1918MODULE_AUTHOR("Red Hat Inc. (http://www.redhat.com)");
1919MODULE_DESCRIPTION("MC Driver for Intel i7 Core memory controllers - "
1920 I7CORE_REVISION);
1921
1922module_param(edac_op_state, int, 0444);
1923MODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI");