blob: 7bcb5993b501f7edf88670932b5fb730cb63ac0d [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 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/*
37 * Alter this version for the module when modifications are made
38 */
39#define I7CORE_REVISION " Ver: 1.0.0 " __DATE__
40#define EDAC_MOD_STR "i7core_edac"
41
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030042/*
43 * Debug macros
44 */
45#define i7core_printk(level, fmt, arg...) \
46 edac_printk(level, "i7core", fmt, ##arg)
47
48#define i7core_mc_printk(mci, level, fmt, arg...) \
49 edac_mc_chipset_printk(mci, level, "i7core", fmt, ##arg)
50
51/*
52 * i7core Memory Controller Registers
53 */
54
Mauro Carvalho Chehabe9bd2e72009-07-09 22:14:35 -030055 /* OFFSETS for Device 0 Function 0 */
56
57#define MC_CFG_CONTROL 0x90
58
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030059 /* OFFSETS for Device 3 Function 0 */
60
61#define MC_CONTROL 0x48
62#define MC_STATUS 0x4c
63#define MC_MAX_DOD 0x64
64
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -030065/*
66 * OFFSETS for Device 3 Function 4, as inicated on Xeon 5500 datasheet:
67 * http://www.arrownac.com/manufacturers/intel/s/nehalem/5500-datasheet-v2.pdf
68 */
69
70#define MC_TEST_ERR_RCV1 0x60
71 #define DIMM2_COR_ERR(r) ((r) & 0x7fff)
72
73#define MC_TEST_ERR_RCV0 0x64
74 #define DIMM1_COR_ERR(r) (((r) >> 16) & 0x7fff)
75 #define DIMM0_COR_ERR(r) ((r) & 0x7fff)
76
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -030077/* OFFSETS for Device 3 Function 2, as inicated on Xeon 5500 datasheet */
78#define MC_COR_ECC_CNT_0 0x80
79#define MC_COR_ECC_CNT_1 0x84
80#define MC_COR_ECC_CNT_2 0x88
81#define MC_COR_ECC_CNT_3 0x8c
82#define MC_COR_ECC_CNT_4 0x90
83#define MC_COR_ECC_CNT_5 0x94
84
85#define DIMM_TOP_COR_ERR(r) (((r) >> 16) & 0x7fff)
86#define DIMM_BOT_COR_ERR(r) ((r) & 0x7fff)
87
88
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -030089 /* OFFSETS for Devices 4,5 and 6 Function 0 */
90
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -030091#define MC_CHANNEL_DIMM_INIT_PARAMS 0x58
92 #define THREE_DIMMS_PRESENT (1 << 24)
93 #define SINGLE_QUAD_RANK_PRESENT (1 << 23)
94 #define QUAD_RANK_PRESENT (1 << 22)
95 #define REGISTERED_DIMM (1 << 15)
96
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -030097#define MC_CHANNEL_MAPPER 0x60
98 #define RDLCH(r, ch) ((((r) >> (3 + (ch * 6))) & 0x07) - 1)
99 #define WRLCH(r, ch) ((((r) >> (ch * 6)) & 0x07) - 1)
100
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300101#define MC_CHANNEL_RANK_PRESENT 0x7c
102 #define RANK_PRESENT_MASK 0xffff
103
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300104#define MC_CHANNEL_ADDR_MATCH 0xf0
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300105#define MC_CHANNEL_ERROR_MASK 0xf8
106#define MC_CHANNEL_ERROR_INJECT 0xfc
107 #define INJECT_ADDR_PARITY 0x10
108 #define INJECT_ECC 0x08
109 #define MASK_CACHELINE 0x06
110 #define MASK_FULL_CACHELINE 0x06
111 #define MASK_MSB32_CACHELINE 0x04
112 #define MASK_LSB32_CACHELINE 0x02
113 #define NO_MASK_CACHELINE 0x00
114 #define REPEAT_EN 0x01
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300115
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300116 /* OFFSETS for Devices 4,5 and 6 Function 1 */
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -0300117
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300118#define MC_DOD_CH_DIMM0 0x48
119#define MC_DOD_CH_DIMM1 0x4c
120#define MC_DOD_CH_DIMM2 0x50
121 #define RANKOFFSET_MASK ((1 << 12) | (1 << 11) | (1 << 10))
122 #define RANKOFFSET(x) ((x & RANKOFFSET_MASK) >> 10)
123 #define DIMM_PRESENT_MASK (1 << 9)
124 #define DIMM_PRESENT(x) (((x) & DIMM_PRESENT_MASK) >> 9)
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300125 #define MC_DOD_NUMBANK_MASK ((1 << 8) | (1 << 7))
126 #define MC_DOD_NUMBANK(x) (((x) & MC_DOD_NUMBANK_MASK) >> 7)
127 #define MC_DOD_NUMRANK_MASK ((1 << 6) | (1 << 5))
128 #define MC_DOD_NUMRANK(x) (((x) & MC_DOD_NUMRANK_MASK) >> 5)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300129 #define MC_DOD_NUMROW_MASK ((1 << 4) | (1 << 3) | (1 << 2))
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300130 #define MC_DOD_NUMROW(x) (((x) & MC_DOD_NUMROW_MASK) >> 2)
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300131 #define MC_DOD_NUMCOL_MASK 3
132 #define MC_DOD_NUMCOL(x) ((x) & MC_DOD_NUMCOL_MASK)
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300133
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300134#define MC_RANK_PRESENT 0x7c
135
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300136#define MC_SAG_CH_0 0x80
137#define MC_SAG_CH_1 0x84
138#define MC_SAG_CH_2 0x88
139#define MC_SAG_CH_3 0x8c
140#define MC_SAG_CH_4 0x90
141#define MC_SAG_CH_5 0x94
142#define MC_SAG_CH_6 0x98
143#define MC_SAG_CH_7 0x9c
144
145#define MC_RIR_LIMIT_CH_0 0x40
146#define MC_RIR_LIMIT_CH_1 0x44
147#define MC_RIR_LIMIT_CH_2 0x48
148#define MC_RIR_LIMIT_CH_3 0x4C
149#define MC_RIR_LIMIT_CH_4 0x50
150#define MC_RIR_LIMIT_CH_5 0x54
151#define MC_RIR_LIMIT_CH_6 0x58
152#define MC_RIR_LIMIT_CH_7 0x5C
153#define MC_RIR_LIMIT_MASK ((1 << 10) - 1)
154
155#define MC_RIR_WAY_CH 0x80
156 #define MC_RIR_WAY_OFFSET_MASK (((1 << 14) - 1) & ~0x7)
157 #define MC_RIR_WAY_RANK_MASK 0x7
158
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300159/*
160 * i7core structs
161 */
162
163#define NUM_CHANS 3
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -0300164#define MAX_DIMMS 3 /* Max DIMMS per channel */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300165#define NUM_SOCKETS 2 /* Max number of MC sockets */
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -0300166#define MAX_MCR_FUNC 4
167#define MAX_CHAN_FUNC 3
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300168
169struct i7core_info {
170 u32 mc_control;
171 u32 mc_status;
172 u32 max_dod;
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300173 u32 ch_map;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300174};
175
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300176
177struct i7core_inject {
178 int enable;
179
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300180 u8 socket;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300181 u32 section;
182 u32 type;
183 u32 eccmask;
184
185 /* Error address mask */
186 int channel, dimm, rank, bank, page, col;
187};
188
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300189struct i7core_channel {
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -0300190 u32 ranks;
191 u32 dimms;
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300192};
193
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300194struct pci_id_descr {
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300195 int dev;
196 int func;
197 int dev_id;
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
209 int sockets; /* Number of sockets */
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300210 int channels; /* Number of active channels */
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -0300211
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300212 int ce_count_available[NUM_SOCKETS];
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -0300213 int csrow_map[NUM_SOCKETS][NUM_CHANS][MAX_DIMMS];
214
215 /* ECC corrected errors counts per udimm */
216 unsigned long udimm_ce_count[NUM_SOCKETS][MAX_DIMMS];
217 int udimm_last_ce_count[NUM_SOCKETS][MAX_DIMMS];
218 /* ECC corrected errors counts per rdimm */
219 unsigned long rdimm_ce_count[NUM_SOCKETS][NUM_CHANS][MAX_DIMMS];
220 int rdimm_last_ce_count[NUM_SOCKETS][NUM_CHANS][MAX_DIMMS];
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -0300221
Mauro Carvalho Chehab14d2c082009-09-02 23:52:36 -0300222 unsigned int is_registered[NUM_SOCKETS];
223
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
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300231struct i7core_dev {
232 struct list_head list;
233
234 int socket;
235 struct pci_dev **pdev;
236};
237
238/* Static vars */
239static LIST_HEAD(i7core_edac_list);
240static DEFINE_MUTEX(i7core_edac_lock);
241
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300242/* Device name and register DID (Device ID) */
243struct i7core_dev_info {
244 const char *ctl_name; /* name for this device */
245 u16 fsb_mapping_errors; /* DID for the branchmap,control */
246};
247
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300248#define PCI_DESCR(device, function, device_id) \
249 .dev = (device), \
250 .func = (function), \
251 .dev_id = (device_id)
252
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300253struct pci_id_descr pci_dev_descr[] = {
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300254 /* Memory controller */
255 { PCI_DESCR(3, 0, PCI_DEVICE_ID_INTEL_I7_MCR) },
256 { PCI_DESCR(3, 1, PCI_DEVICE_ID_INTEL_I7_MC_TAD) },
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -0300257 { PCI_DESCR(3, 2, PCI_DEVICE_ID_INTEL_I7_MC_RAS) }, /* if RDIMM */
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300258 { PCI_DESCR(3, 4, PCI_DEVICE_ID_INTEL_I7_MC_TEST) },
259
260 /* Channel 0 */
261 { PCI_DESCR(4, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH0_CTRL) },
262 { PCI_DESCR(4, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH0_ADDR) },
263 { PCI_DESCR(4, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH0_RANK) },
264 { PCI_DESCR(4, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH0_TC) },
265
266 /* Channel 1 */
267 { PCI_DESCR(5, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH1_CTRL) },
268 { PCI_DESCR(5, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH1_ADDR) },
269 { PCI_DESCR(5, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH1_RANK) },
270 { PCI_DESCR(5, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH1_TC) },
271
272 /* Channel 2 */
273 { PCI_DESCR(6, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH2_CTRL) },
274 { PCI_DESCR(6, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH2_ADDR) },
275 { PCI_DESCR(6, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH2_RANK) },
276 { PCI_DESCR(6, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH2_TC) },
Mauro Carvalho Chehab310cbb72009-07-17 00:09:10 -0300277
278 /* Generic Non-core registers */
279 /*
280 * This is the PCI device on i7core and on Xeon 35xx (8086:2c41)
281 * On Xeon 55xx, however, it has a different id (8086:2c40). So,
282 * the probing code needs to test for the other address in case of
283 * failure of this one
284 */
285 { PCI_DESCR(0, 0, PCI_DEVICE_ID_INTEL_I7_NOCORE) },
286
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300287};
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300288#define N_DEVS ARRAY_SIZE(pci_dev_descr)
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300289
290/*
291 * pci_device_id table for which devices we are looking for
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300292 */
293static const struct pci_device_id i7core_pci_tbl[] __devinitdata = {
Mauro Carvalho Chehabd1fd4fb2009-07-10 18:39:53 -0300294 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_X58_HUB_MGMT)},
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300295 {0,} /* 0 terminated list. */
296};
297
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300298
299/* Table of devices attributes supported by this driver */
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -0300300static const struct i7core_dev_info i7core_probe_devs[] = {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300301 {
302 .ctl_name = "i7 Core",
303 .fsb_mapping_errors = PCI_DEVICE_ID_INTEL_I7_MCR,
304 },
305};
306
307static 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 Chehab66607702009-09-05 00:52:11 -0300359static struct i7core_dev *get_i7core_dev(int socket)
360{
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 Chehabba6c5c62009-07-15 09:02:32 -0300474static int get_dimm_config(struct mem_ctl_info *mci, int *csrow, u8 socket)
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 Chehab67166af2009-07-15 06:56:23 -0300485 pdev = pvt->pci_mcr[socket][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",
496 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 Chehab67166af2009-07-15 06:56:23 -0300532 pci_read_config_dword(pvt->pci_ch[socket][i][0],
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300533 MC_CHANNEL_DIMM_INIT_PARAMS, &data);
534
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300535 pvt->channel[socket][i].ranks = (data & QUAD_RANK_PRESENT) ?
536 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 Chehab67166af2009-07-15 06:56:23 -0300552 pci_read_config_dword(pvt->pci_ch[socket][i][1],
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300553 MC_DOD_CH_DIMM0, &dimm_dod[0]);
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300554 pci_read_config_dword(pvt->pci_ch[socket][i][1],
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300555 MC_DOD_CH_DIMM1, &dimm_dod[1]);
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300556 pci_read_config_dword(pvt->pci_ch[socket][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 Chehab67166af2009-07-15 06:56:23 -0300564 pvt->channel[socket][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 Chehab67166af2009-07-15 06:56:23 -0300582 pvt->channel[socket][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 Chehabb4e8f0b2009-09-02 23:49:59 -0300610 pvt->csrow_map[socket][i][j] = *csrow;
611
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 Chehab67166af2009-07-15 06:56:23 -0300668 if (!pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0])
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300669 return -ENODEV;
670
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300671 pci_write_config_dword(pvt->pci_ch[pvt->inject.socket][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/*
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300678 * i7core inject inject.socket
679 *
680 * accept and store error injection inject.socket value
681 */
682static ssize_t i7core_inject_socket_store(struct mem_ctl_info *mci,
683 const char *data, size_t count)
684{
685 struct i7core_pvt *pvt = mci->pvt_info;
686 unsigned long value;
687 int rc;
688
689 rc = strict_strtoul(data, 10, &value);
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300690 if ((rc < 0) || (value >= pvt->sockets))
Mauro Carvalho Chehab2068def2009-08-05 19:28:27 -0300691 return -EIO;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300692
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -0300693 pvt->inject.socket = (u32) value;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300694 return count;
695}
696
697static ssize_t i7core_inject_socket_show(struct mem_ctl_info *mci,
698 char *data)
699{
700 struct i7core_pvt *pvt = mci->pvt_info;
701 return sprintf(data, "%d\n", pvt->inject.socket);
702}
703
704/*
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300705 * i7core inject inject.section
706 *
707 * accept and store error injection inject.section value
708 * bit 0 - refers to the lower 32-byte half cacheline
709 * bit 1 - refers to the upper 32-byte half cacheline
710 */
711static ssize_t i7core_inject_section_store(struct mem_ctl_info *mci,
712 const char *data, size_t count)
713{
714 struct i7core_pvt *pvt = mci->pvt_info;
715 unsigned long value;
716 int rc;
717
718 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300719 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300720
721 rc = strict_strtoul(data, 10, &value);
722 if ((rc < 0) || (value > 3))
Mauro Carvalho Chehab2068def2009-08-05 19:28:27 -0300723 return -EIO;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300724
725 pvt->inject.section = (u32) value;
726 return count;
727}
728
729static ssize_t i7core_inject_section_show(struct mem_ctl_info *mci,
730 char *data)
731{
732 struct i7core_pvt *pvt = mci->pvt_info;
733 return sprintf(data, "0x%08x\n", pvt->inject.section);
734}
735
736/*
737 * i7core inject.type
738 *
739 * accept and store error injection inject.section value
740 * bit 0 - repeat enable - Enable error repetition
741 * bit 1 - inject ECC error
742 * bit 2 - inject parity error
743 */
744static ssize_t i7core_inject_type_store(struct mem_ctl_info *mci,
745 const char *data, size_t count)
746{
747 struct i7core_pvt *pvt = mci->pvt_info;
748 unsigned long value;
749 int rc;
750
751 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300752 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300753
754 rc = strict_strtoul(data, 10, &value);
755 if ((rc < 0) || (value > 7))
Mauro Carvalho Chehab2068def2009-08-05 19:28:27 -0300756 return -EIO;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300757
758 pvt->inject.type = (u32) value;
759 return count;
760}
761
762static ssize_t i7core_inject_type_show(struct mem_ctl_info *mci,
763 char *data)
764{
765 struct i7core_pvt *pvt = mci->pvt_info;
766 return sprintf(data, "0x%08x\n", pvt->inject.type);
767}
768
769/*
770 * i7core_inject_inject.eccmask_store
771 *
772 * The type of error (UE/CE) will depend on the inject.eccmask value:
773 * Any bits set to a 1 will flip the corresponding ECC bit
774 * Correctable errors can be injected by flipping 1 bit or the bits within
775 * a symbol pair (2 consecutive aligned 8-bit pairs - i.e. 7:0 and 15:8 or
776 * 23:16 and 31:24). Flipping bits in two symbol pairs will cause an
777 * uncorrectable error to be injected.
778 */
779static ssize_t i7core_inject_eccmask_store(struct mem_ctl_info *mci,
780 const char *data, size_t count)
781{
782 struct i7core_pvt *pvt = mci->pvt_info;
783 unsigned long value;
784 int rc;
785
786 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300787 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300788
789 rc = strict_strtoul(data, 10, &value);
790 if (rc < 0)
Mauro Carvalho Chehab2068def2009-08-05 19:28:27 -0300791 return -EIO;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300792
793 pvt->inject.eccmask = (u32) value;
794 return count;
795}
796
797static ssize_t i7core_inject_eccmask_show(struct mem_ctl_info *mci,
798 char *data)
799{
800 struct i7core_pvt *pvt = mci->pvt_info;
801 return sprintf(data, "0x%08x\n", pvt->inject.eccmask);
802}
803
804/*
805 * i7core_addrmatch
806 *
807 * The type of error (UE/CE) will depend on the inject.eccmask value:
808 * Any bits set to a 1 will flip the corresponding ECC bit
809 * Correctable errors can be injected by flipping 1 bit or the bits within
810 * a symbol pair (2 consecutive aligned 8-bit pairs - i.e. 7:0 and 15:8 or
811 * 23:16 and 31:24). Flipping bits in two symbol pairs will cause an
812 * uncorrectable error to be injected.
813 */
814static ssize_t i7core_inject_addrmatch_store(struct mem_ctl_info *mci,
815 const char *data, size_t count)
816{
817 struct i7core_pvt *pvt = mci->pvt_info;
818 char *cmd, *val;
819 long value;
820 int rc;
821
822 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300823 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300824
825 do {
826 cmd = strsep((char **) &data, ":");
827 if (!cmd)
828 break;
829 val = strsep((char **) &data, " \n\t");
830 if (!val)
831 return cmd - data;
832
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300833 if (!strcasecmp(val, "any"))
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300834 value = -1;
835 else {
836 rc = strict_strtol(val, 10, &value);
837 if ((rc < 0) || (value < 0))
838 return cmd - data;
839 }
840
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300841 if (!strcasecmp(cmd, "channel")) {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300842 if (value < 3)
843 pvt->inject.channel = value;
844 else
845 return cmd - data;
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300846 } else if (!strcasecmp(cmd, "dimm")) {
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300847 if (value < 3)
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300848 pvt->inject.dimm = value;
849 else
850 return cmd - data;
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300851 } else if (!strcasecmp(cmd, "rank")) {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300852 if (value < 4)
853 pvt->inject.rank = value;
854 else
855 return cmd - data;
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300856 } else if (!strcasecmp(cmd, "bank")) {
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300857 if (value < 32)
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300858 pvt->inject.bank = value;
859 else
860 return cmd - data;
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300861 } else if (!strcasecmp(cmd, "page")) {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300862 if (value <= 0xffff)
863 pvt->inject.page = value;
864 else
865 return cmd - data;
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300866 } else if (!strcasecmp(cmd, "col") ||
867 !strcasecmp(cmd, "column")) {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300868 if (value <= 0x3fff)
869 pvt->inject.col = value;
870 else
871 return cmd - data;
872 }
873 } while (1);
874
875 return count;
876}
877
878static ssize_t i7core_inject_addrmatch_show(struct mem_ctl_info *mci,
879 char *data)
880{
881 struct i7core_pvt *pvt = mci->pvt_info;
882 char channel[4], dimm[4], bank[4], rank[4], page[7], col[7];
883
884 if (pvt->inject.channel < 0)
885 sprintf(channel, "any");
886 else
887 sprintf(channel, "%d", pvt->inject.channel);
888 if (pvt->inject.dimm < 0)
889 sprintf(dimm, "any");
890 else
891 sprintf(dimm, "%d", pvt->inject.dimm);
892 if (pvt->inject.bank < 0)
893 sprintf(bank, "any");
894 else
895 sprintf(bank, "%d", pvt->inject.bank);
896 if (pvt->inject.rank < 0)
897 sprintf(rank, "any");
898 else
899 sprintf(rank, "%d", pvt->inject.rank);
900 if (pvt->inject.page < 0)
901 sprintf(page, "any");
902 else
903 sprintf(page, "0x%04x", pvt->inject.page);
904 if (pvt->inject.col < 0)
905 sprintf(col, "any");
906 else
907 sprintf(col, "0x%04x", pvt->inject.col);
908
909 return sprintf(data, "channel: %s\ndimm: %s\nbank: %s\n"
910 "rank: %s\npage: %s\ncolumn: %s\n",
911 channel, dimm, bank, rank, page, col);
912}
913
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300914static int write_and_test(struct pci_dev *dev, int where, u32 val)
915{
916 u32 read;
917 int count;
918
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -0300919 debugf0("setting pci %02x:%02x.%x reg=%02x value=%08x\n",
920 dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn),
921 where, val);
922
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300923 for (count = 0; count < 10; count++) {
924 if (count)
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -0300925 msleep(100);
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300926 pci_write_config_dword(dev, where, val);
927 pci_read_config_dword(dev, where, &read);
928
929 if (read == val)
930 return 0;
931 }
932
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -0300933 i7core_printk(KERN_ERR, "Error during set pci %02x:%02x.%x reg=%02x "
934 "write=%08x. Read=%08x\n",
935 dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn),
936 where, val, read);
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300937
938 return -EINVAL;
939}
940
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300941/*
942 * This routine prepares the Memory Controller for error injection.
943 * The error will be injected when some process tries to write to the
944 * memory that matches the given criteria.
945 * The criteria can be set in terms of a mask where dimm, rank, bank, page
946 * and col can be specified.
947 * A -1 value for any of the mask items will make the MCU to ignore
948 * that matching criteria for error injection.
949 *
950 * It should be noticed that the error will only happen after a write operation
951 * on a memory that matches the condition. if REPEAT_EN is not enabled at
952 * inject mask, then it will produce just one error. Otherwise, it will repeat
953 * until the injectmask would be cleaned.
954 *
955 * FIXME: This routine assumes that MAXNUMDIMMS value of MC_MAX_DOD
956 * is reliable enough to check if the MC is using the
957 * three channels. However, this is not clear at the datasheet.
958 */
959static ssize_t i7core_inject_enable_store(struct mem_ctl_info *mci,
960 const char *data, size_t count)
961{
962 struct i7core_pvt *pvt = mci->pvt_info;
963 u32 injectmask;
964 u64 mask = 0;
965 int rc;
966 long enable;
967
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300968 if (!pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0])
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300969 return 0;
970
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300971 rc = strict_strtoul(data, 10, &enable);
972 if ((rc < 0))
973 return 0;
974
975 if (enable) {
976 pvt->inject.enable = 1;
977 } else {
978 disable_inject(mci);
979 return count;
980 }
981
982 /* Sets pvt->inject.dimm mask */
983 if (pvt->inject.dimm < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300984 mask |= 1L << 41;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300985 else {
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300986 if (pvt->channel[pvt->inject.socket][pvt->inject.channel].dimms > 2)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300987 mask |= (pvt->inject.dimm & 0x3L) << 35;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300988 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300989 mask |= (pvt->inject.dimm & 0x1L) << 36;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300990 }
991
992 /* Sets pvt->inject.rank mask */
993 if (pvt->inject.rank < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300994 mask |= 1L << 40;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300995 else {
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300996 if (pvt->channel[pvt->inject.socket][pvt->inject.channel].dimms > 2)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300997 mask |= (pvt->inject.rank & 0x1L) << 34;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300998 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300999 mask |= (pvt->inject.rank & 0x3L) << 34;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001000 }
1001
1002 /* Sets pvt->inject.bank mask */
1003 if (pvt->inject.bank < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -03001004 mask |= 1L << 39;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001005 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -03001006 mask |= (pvt->inject.bank & 0x15L) << 30;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001007
1008 /* Sets pvt->inject.page mask */
1009 if (pvt->inject.page < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -03001010 mask |= 1L << 38;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001011 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -03001012 mask |= (pvt->inject.page & 0xffffL) << 14;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001013
1014 /* Sets pvt->inject.column mask */
1015 if (pvt->inject.col < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -03001016 mask |= 1L << 37;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001017 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -03001018 mask |= (pvt->inject.col & 0x3fffL);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001019
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001020 /*
1021 * bit 0: REPEAT_EN
1022 * bits 1-2: MASK_HALF_CACHELINE
1023 * bit 3: INJECT_ECC
1024 * bit 4: INJECT_ADDR_PARITY
1025 */
1026
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -03001027 injectmask = (pvt->inject.type & 1) |
1028 (pvt->inject.section & 0x3) << 1 |
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001029 (pvt->inject.type & 0x6) << (3 - 1);
1030
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -03001031 /* Unlock writes to registers - this register is write only */
1032 pci_write_config_dword(pvt->pci_noncore[pvt->inject.socket],
1033 MC_CFG_CONTROL, 0x2);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001034
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -03001035 write_and_test(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0],
1036 MC_CHANNEL_ADDR_MATCH, mask);
1037 write_and_test(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0],
1038 MC_CHANNEL_ADDR_MATCH + 4, mask >> 32L);
1039
1040 write_and_test(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0],
1041 MC_CHANNEL_ERROR_MASK, pvt->inject.eccmask);
1042
1043 write_and_test(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0],
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -03001044 MC_CHANNEL_ERROR_INJECT, injectmask);
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -03001045
1046 /*
1047 * This is something undocumented, based on my tests
1048 * Without writing 8 to this register, errors aren't injected. Not sure
1049 * why.
1050 */
1051 pci_write_config_dword(pvt->pci_noncore[pvt->inject.socket],
1052 MC_CFG_CONTROL, 8);
1053
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001054 debugf0("Error inject addr match 0x%016llx, ecc 0x%08x,"
1055 " inject 0x%08x\n",
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001056 mask, pvt->inject.eccmask, injectmask);
1057
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -03001058
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001059 return count;
1060}
1061
1062static ssize_t i7core_inject_enable_show(struct mem_ctl_info *mci,
1063 char *data)
1064{
1065 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -03001066 u32 injectmask;
1067
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001068 pci_read_config_dword(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0],
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -03001069 MC_CHANNEL_ERROR_INJECT, &injectmask);
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -03001070
1071 debugf0("Inject error read: 0x%018x\n", injectmask);
1072
1073 if (injectmask & 0x0c)
1074 pvt->inject.enable = 1;
1075
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001076 return sprintf(data, "%d\n", pvt->inject.enable);
1077}
1078
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001079static ssize_t i7core_ce_regs_show(struct mem_ctl_info *mci, char *data)
1080{
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001081 unsigned i, j, count, total = 0;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001082 struct i7core_pvt *pvt = mci->pvt_info;
1083
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001084 for (i = 0; i < pvt->sockets; i++) {
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001085 if (!pvt->ce_count_available[i]) {
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001086 count = sprintf(data, "socket 0 data unavailable\n");
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001087 continue;
1088 }
Mauro Carvalho Chehab14d2c082009-09-02 23:52:36 -03001089 if (!pvt->is_registered[i])
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001090 count = sprintf(data, "socket %d, dimm0: %lu\n"
1091 "dimm1: %lu\ndimm2: %lu\n",
1092 i,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001093 pvt->udimm_ce_count[i][0],
1094 pvt->udimm_ce_count[i][1],
1095 pvt->udimm_ce_count[i][2]);
1096 else
1097 for (j = 0; j < NUM_CHANS; j++) {
Mauro Carvalho Chehab3a3bb4a2009-09-03 20:17:26 -03001098 count = sprintf(data, "socket %d, channel %d "
1099 "RDIMM0: %lu "
1100 "RDIMM1: %lu RDIMM2: %lu\n",
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001101 i, j,
1102 pvt->rdimm_ce_count[i][j][0],
1103 pvt->rdimm_ce_count[i][j][1],
1104 pvt->rdimm_ce_count[i][j][2]);
1105 }
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001106 data += count;
1107 total += count;
1108 }
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001109
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001110 return total;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001111}
1112
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001113/*
1114 * Sysfs struct
1115 */
1116static struct mcidev_sysfs_attribute i7core_inj_attrs[] = {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001117 {
1118 .attr = {
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001119 .name = "inject_socket",
1120 .mode = (S_IRUGO | S_IWUSR)
1121 },
1122 .show = i7core_inject_socket_show,
1123 .store = i7core_inject_socket_store,
1124 }, {
1125 .attr = {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001126 .name = "inject_section",
1127 .mode = (S_IRUGO | S_IWUSR)
1128 },
1129 .show = i7core_inject_section_show,
1130 .store = i7core_inject_section_store,
1131 }, {
1132 .attr = {
1133 .name = "inject_type",
1134 .mode = (S_IRUGO | S_IWUSR)
1135 },
1136 .show = i7core_inject_type_show,
1137 .store = i7core_inject_type_store,
1138 }, {
1139 .attr = {
1140 .name = "inject_eccmask",
1141 .mode = (S_IRUGO | S_IWUSR)
1142 },
1143 .show = i7core_inject_eccmask_show,
1144 .store = i7core_inject_eccmask_store,
1145 }, {
1146 .attr = {
1147 .name = "inject_addrmatch",
1148 .mode = (S_IRUGO | S_IWUSR)
1149 },
1150 .show = i7core_inject_addrmatch_show,
1151 .store = i7core_inject_addrmatch_store,
1152 }, {
1153 .attr = {
1154 .name = "inject_enable",
1155 .mode = (S_IRUGO | S_IWUSR)
1156 },
1157 .show = i7core_inject_enable_show,
1158 .store = i7core_inject_enable_store,
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001159 }, {
1160 .attr = {
1161 .name = "corrected_error_counts",
1162 .mode = (S_IRUGO | S_IWUSR)
1163 },
1164 .show = i7core_ce_regs_show,
1165 .store = NULL,
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001166 },
1167};
1168
1169/****************************************************************************
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001170 Device initialization routines: put/get, init/exit
1171 ****************************************************************************/
1172
1173/*
1174 * i7core_put_devices 'put' all the devices that we have
1175 * reserved via 'get'
1176 */
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001177static void i7core_put_devices(void)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001178{
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001179 int i, j;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001180
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001181 for (i = 0; i < NUM_SOCKETS; i++) {
1182 struct i7core_dev *i7core_dev = get_i7core_dev(i);
1183 if (!i7core_dev)
1184 continue;
1185
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001186 for (j = 0; j < N_DEVS; j++)
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001187 pci_dev_put(i7core_dev->pdev[j]);
1188
1189 list_del(&i7core_dev->list);
1190 kfree(i7core_dev->pdev);
1191 kfree(i7core_dev);
1192 }
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001193}
1194
Keith Manntheybc2d7242009-09-03 00:05:05 -03001195static void i7core_xeon_pci_fixup(void)
1196{
1197 struct pci_dev *pdev = NULL;
1198 int i;
1199 /*
1200 * On Xeon 55xx, the Intel Quckpath Arch Generic Non-core pci buses
1201 * aren't announced by acpi. So, we need to use a legacy scan probing
1202 * to detect them
1203 */
1204 pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001205 pci_dev_descr[0].dev_id, NULL);
Keith Manntheybc2d7242009-09-03 00:05:05 -03001206 if (unlikely(!pdev)) {
1207 for (i = 0; i < NUM_SOCKETS; i ++)
1208 pcibios_scan_specific_bus(255-i);
1209 }
1210}
1211
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001212/*
1213 * i7core_get_devices Find and perform 'get' operation on the MCH's
1214 * device/functions we want to reference for this driver
1215 *
1216 * Need to 'get' device 16 func 1 and func 2
1217 */
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001218int i7core_get_onedevice(struct pci_dev **prev, int devno)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001219{
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001220 struct i7core_dev *i7core_dev;
1221
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001222 struct pci_dev *pdev = NULL;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001223 u8 bus = 0;
1224 u8 socket = 0;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001225
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001226 pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001227 pci_dev_descr[devno].dev_id, *prev);
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001228
1229 /*
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001230 * On Xeon 55xx, the Intel Quckpath Arch Generic Non-core regs
1231 * is at addr 8086:2c40, instead of 8086:2c41. So, we need
1232 * to probe for the alternate address in case of failure
1233 */
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001234 if (pci_dev_descr[devno].dev_id == PCI_DEVICE_ID_INTEL_I7_NOCORE && !pdev)
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001235 pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
1236 PCI_DEVICE_ID_INTEL_I7_NOCORE_ALT, *prev);
Mauro Carvalho Chehabd1fd4fb2009-07-10 18:39:53 -03001237
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001238 if (!pdev) {
1239 if (*prev) {
1240 *prev = pdev;
1241 return 0;
Mauro Carvalho Chehabd1fd4fb2009-07-10 18:39:53 -03001242 }
1243
Mauro Carvalho Chehab310cbb72009-07-17 00:09:10 -03001244 /*
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001245 * Dev 3 function 2 only exists on chips with RDIMMs
1246 * so, it is ok to not found it
Mauro Carvalho Chehab310cbb72009-07-17 00:09:10 -03001247 */
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001248 if ((pci_dev_descr[devno].dev == 3) && (pci_dev_descr[devno].func == 2)) {
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001249 *prev = pdev;
1250 return 0;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001251 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001252
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001253 i7core_printk(KERN_ERR,
1254 "Device not found: dev %02x.%d PCI ID %04x:%04x\n",
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001255 pci_dev_descr[devno].dev, pci_dev_descr[devno].func,
1256 PCI_VENDOR_ID_INTEL, pci_dev_descr[devno].dev_id);
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001257
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001258 /* End of list, leave */
1259 return -ENODEV;
1260 }
1261 bus = pdev->bus->number;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001262
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001263 if (bus == 0x3f)
1264 socket = 0;
1265 else
1266 socket = 255 - bus;
1267
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001268 i7core_dev = get_i7core_dev(socket);
1269 if (!i7core_dev) {
1270 i7core_dev = kzalloc(sizeof(*i7core_dev), GFP_KERNEL);
1271 if (!i7core_dev)
1272 return -ENOMEM;
1273 i7core_dev->pdev = kzalloc(sizeof(*i7core_dev->pdev) * N_DEVS,
1274 GFP_KERNEL);
1275 if (!i7core_dev->pdev)
1276 return -ENOMEM;
1277 i7core_dev->socket = socket;
1278 list_add_tail(&i7core_dev->list, &i7core_edac_list);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001279 }
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001280
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001281 if (i7core_dev->pdev[devno]) {
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001282 i7core_printk(KERN_ERR,
1283 "Duplicated device for "
1284 "dev %02x:%02x.%d PCI ID %04x:%04x\n",
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001285 bus, pci_dev_descr[devno].dev, pci_dev_descr[devno].func,
1286 PCI_VENDOR_ID_INTEL, pci_dev_descr[devno].dev_id);
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001287 pci_dev_put(pdev);
1288 return -ENODEV;
1289 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001290
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001291 i7core_dev->pdev[devno] = pdev;
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001292
1293 /* Sanity check */
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001294 if (unlikely(PCI_SLOT(pdev->devfn) != pci_dev_descr[devno].dev ||
1295 PCI_FUNC(pdev->devfn) != pci_dev_descr[devno].func)) {
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001296 i7core_printk(KERN_ERR,
1297 "Device PCI ID %04x:%04x "
1298 "has dev %02x:%02x.%d instead of dev %02x:%02x.%d\n",
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001299 PCI_VENDOR_ID_INTEL, pci_dev_descr[devno].dev_id,
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001300 bus, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001301 bus, pci_dev_descr[devno].dev, pci_dev_descr[devno].func);
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001302 return -ENODEV;
1303 }
1304
1305 /* Be sure that the device is enabled */
1306 if (unlikely(pci_enable_device(pdev) < 0)) {
1307 i7core_printk(KERN_ERR,
1308 "Couldn't enable "
1309 "dev %02x:%02x.%d PCI ID %04x:%04x\n",
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001310 bus, pci_dev_descr[devno].dev, pci_dev_descr[devno].func,
1311 PCI_VENDOR_ID_INTEL, pci_dev_descr[devno].dev_id);
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001312 return -ENODEV;
1313 }
1314
1315 i7core_printk(KERN_INFO,
1316 "Registered socket %d "
1317 "dev %02x:%02x.%d PCI ID %04x:%04x\n",
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001318 socket, bus, pci_dev_descr[devno].dev, pci_dev_descr[devno].func,
1319 PCI_VENDOR_ID_INTEL, pci_dev_descr[devno].dev_id);
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001320
1321 *prev = pdev;
1322
1323 return 0;
1324}
1325
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001326static int i7core_get_devices(u8 *sockets)
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001327{
1328 int i;
1329 struct pci_dev *pdev = NULL;
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001330 struct i7core_dev *i7core_dev = NULL;
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001331
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001332 *sockets = 0;
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001333 for (i = 0; i < N_DEVS; i++) {
1334 pdev = NULL;
1335 do {
1336 if (i7core_get_onedevice(&pdev, i) < 0) {
1337 i7core_put_devices();
1338 return -ENODEV;
1339 }
1340 } while (pdev);
1341 }
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001342
1343 list_for_each_entry(i7core_dev, &i7core_edac_list, list) {
1344 if (i7core_dev->socket + 1 > *sockets)
1345 *sockets = i7core_dev->socket + 1;
1346 }
1347
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001348 return 0;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001349}
1350
1351static int mci_bind_devs(struct mem_ctl_info *mci)
1352{
1353 struct i7core_pvt *pvt = mci->pvt_info;
1354 struct pci_dev *pdev;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001355 int i, j, func, slot;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001356
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001357 for (i = 0; i < pvt->sockets; i++) {
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001358 struct i7core_dev *i7core_dev = get_i7core_dev(i);
1359
1360 if (!i7core_dev)
1361 continue;
1362
Mauro Carvalho Chehab14d2c082009-09-02 23:52:36 -03001363 pvt->is_registered[i] = 0;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001364 for (j = 0; j < N_DEVS; j++) {
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001365 pdev = i7core_dev->pdev[j];
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001366 if (!pdev)
1367 continue;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001368
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001369 func = PCI_FUNC(pdev->devfn);
1370 slot = PCI_SLOT(pdev->devfn);
1371 if (slot == 3) {
1372 if (unlikely(func > MAX_MCR_FUNC))
1373 goto error;
1374 pvt->pci_mcr[i][func] = pdev;
1375 } else if (likely(slot >= 4 && slot < 4 + NUM_CHANS)) {
1376 if (unlikely(func > MAX_CHAN_FUNC))
1377 goto error;
1378 pvt->pci_ch[i][slot - 4][func] = pdev;
1379 } else if (!slot && !func)
1380 pvt->pci_noncore[i] = pdev;
1381 else
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001382 goto error;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001383
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001384 debugf0("Associated fn %d.%d, dev = %p, socket %d\n",
1385 PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
1386 pdev, i);
Mauro Carvalho Chehab14d2c082009-09-02 23:52:36 -03001387
1388 if (PCI_SLOT(pdev->devfn) == 3 &&
1389 PCI_FUNC(pdev->devfn) == 2)
1390 pvt->is_registered[i] = 1;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001391 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001392 }
Mauro Carvalho Chehabe9bd2e72009-07-09 22:14:35 -03001393
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001394 return 0;
1395
1396error:
1397 i7core_printk(KERN_ERR, "Device %d, function %d "
1398 "is out of the expected range\n",
1399 slot, func);
1400 return -EINVAL;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001401}
1402
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001403/****************************************************************************
1404 Error check routines
1405 ****************************************************************************/
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001406static void i7core_rdimm_update_csrow(struct mem_ctl_info *mci, int socket,
1407 int chan, int dimm, int add)
1408{
1409 char *msg;
1410 struct i7core_pvt *pvt = mci->pvt_info;
1411 int row = pvt->csrow_map[socket][chan][dimm], i;
1412
1413 for (i = 0; i < add; i++) {
1414 msg = kasprintf(GFP_KERNEL, "Corrected error "
1415 "(Socket=%d channel=%d dimm=%d",
1416 socket, chan, dimm);
1417
1418 edac_mc_handle_fbd_ce(mci, row, 0, msg);
1419 kfree (msg);
1420 }
1421}
1422
1423static void i7core_rdimm_update_ce_count(struct mem_ctl_info *mci,
1424 int socket, int chan, int new0, int new1, int new2)
1425{
1426 struct i7core_pvt *pvt = mci->pvt_info;
1427 int add0 = 0, add1 = 0, add2 = 0;
1428 /* Updates CE counters if it is not the first time here */
1429 if (pvt->ce_count_available[socket]) {
1430 /* Updates CE counters */
1431
1432 add2 = new2 - pvt->rdimm_last_ce_count[socket][chan][2];
1433 add1 = new1 - pvt->rdimm_last_ce_count[socket][chan][1];
1434 add0 = new0 - pvt->rdimm_last_ce_count[socket][chan][0];
1435
1436 if (add2 < 0)
1437 add2 += 0x7fff;
1438 pvt->rdimm_ce_count[socket][chan][2] += add2;
1439
1440 if (add1 < 0)
1441 add1 += 0x7fff;
1442 pvt->rdimm_ce_count[socket][chan][1] += add1;
1443
1444 if (add0 < 0)
1445 add0 += 0x7fff;
1446 pvt->rdimm_ce_count[socket][chan][0] += add0;
1447 } else
1448 pvt->ce_count_available[socket] = 1;
1449
1450 /* Store the new values */
1451 pvt->rdimm_last_ce_count[socket][chan][2] = new2;
1452 pvt->rdimm_last_ce_count[socket][chan][1] = new1;
1453 pvt->rdimm_last_ce_count[socket][chan][0] = new0;
1454
1455 /*updated the edac core */
1456 if (add0 != 0)
1457 i7core_rdimm_update_csrow(mci, socket, chan, 0, add0);
1458 if (add1 != 0)
1459 i7core_rdimm_update_csrow(mci, socket, chan, 1, add1);
1460 if (add2 != 0)
1461 i7core_rdimm_update_csrow(mci, socket, chan, 2, add2);
1462
1463}
1464
1465static void i7core_rdimm_check_mc_ecc_err(struct mem_ctl_info *mci, u8 socket)
1466{
1467 struct i7core_pvt *pvt = mci->pvt_info;
1468 u32 rcv[3][2];
1469 int i, new0, new1, new2;
1470
1471 /*Read DEV 3: FUN 2: MC_COR_ECC_CNT regs directly*/
1472 pci_read_config_dword(pvt->pci_mcr[socket][2], MC_COR_ECC_CNT_0,
1473 &rcv[0][0]);
1474 pci_read_config_dword(pvt->pci_mcr[socket][2], MC_COR_ECC_CNT_1,
1475 &rcv[0][1]);
1476 pci_read_config_dword(pvt->pci_mcr[socket][2], MC_COR_ECC_CNT_2,
1477 &rcv[1][0]);
1478 pci_read_config_dword(pvt->pci_mcr[socket][2], MC_COR_ECC_CNT_3,
1479 &rcv[1][1]);
1480 pci_read_config_dword(pvt->pci_mcr[socket][2], MC_COR_ECC_CNT_4,
1481 &rcv[2][0]);
1482 pci_read_config_dword(pvt->pci_mcr[socket][2], MC_COR_ECC_CNT_5,
1483 &rcv[2][1]);
1484 for (i = 0 ; i < 3; i++) {
1485 debugf3("MC_COR_ECC_CNT%d = 0x%x; MC_COR_ECC_CNT%d = 0x%x\n",
1486 (i * 2), rcv[i][0], (i * 2) + 1, rcv[i][1]);
1487 /*if the channel has 3 dimms*/
1488 if (pvt->channel[socket][i].dimms > 2) {
1489 new0 = DIMM_BOT_COR_ERR(rcv[i][0]);
1490 new1 = DIMM_TOP_COR_ERR(rcv[i][0]);
1491 new2 = DIMM_BOT_COR_ERR(rcv[i][1]);
1492 } else {
1493 new0 = DIMM_TOP_COR_ERR(rcv[i][0]) +
1494 DIMM_BOT_COR_ERR(rcv[i][0]);
1495 new1 = DIMM_TOP_COR_ERR(rcv[i][1]) +
1496 DIMM_BOT_COR_ERR(rcv[i][1]);
1497 new2 = 0;
1498 }
1499
1500 i7core_rdimm_update_ce_count(mci, socket, i, new0, new1, new2);
1501 }
1502}
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001503
1504/* This function is based on the device 3 function 4 registers as described on:
1505 * Intel Xeon Processor 5500 Series Datasheet Volume 2
1506 * http://www.intel.com/Assets/PDF/datasheet/321322.pdf
1507 * also available at:
1508 * http://www.arrownac.com/manufacturers/intel/s/nehalem/5500-datasheet-v2.pdf
1509 */
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001510static void i7core_udimm_check_mc_ecc_err(struct mem_ctl_info *mci, u8 socket)
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001511{
1512 struct i7core_pvt *pvt = mci->pvt_info;
1513 u32 rcv1, rcv0;
1514 int new0, new1, new2;
1515
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001516 if (!pvt->pci_mcr[socket][4]) {
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -03001517 debugf0("%s MCR registers not found\n", __func__);
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001518 return;
1519 }
1520
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001521 /* Corrected test errors */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001522 pci_read_config_dword(pvt->pci_mcr[socket][4], MC_TEST_ERR_RCV1, &rcv1);
1523 pci_read_config_dword(pvt->pci_mcr[socket][4], MC_TEST_ERR_RCV0, &rcv0);
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001524
1525 /* Store the new values */
1526 new2 = DIMM2_COR_ERR(rcv1);
1527 new1 = DIMM1_COR_ERR(rcv0);
1528 new0 = DIMM0_COR_ERR(rcv0);
1529
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001530 /* Updates CE counters if it is not the first time here */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001531 if (pvt->ce_count_available[socket]) {
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001532 /* Updates CE counters */
1533 int add0, add1, add2;
1534
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001535 add2 = new2 - pvt->udimm_last_ce_count[socket][2];
1536 add1 = new1 - pvt->udimm_last_ce_count[socket][1];
1537 add0 = new0 - pvt->udimm_last_ce_count[socket][0];
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001538
1539 if (add2 < 0)
1540 add2 += 0x7fff;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001541 pvt->udimm_ce_count[socket][2] += add2;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001542
1543 if (add1 < 0)
1544 add1 += 0x7fff;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001545 pvt->udimm_ce_count[socket][1] += add1;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001546
1547 if (add0 < 0)
1548 add0 += 0x7fff;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001549 pvt->udimm_ce_count[socket][0] += add0;
1550
1551 if (add0 | add1 | add2)
1552 i7core_printk(KERN_ERR, "New Corrected error(s): "
1553 "dimm0: +%d, dimm1: +%d, dimm2 +%d\n",
1554 add0, add1, add2);
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001555 } else
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001556 pvt->ce_count_available[socket] = 1;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001557
1558 /* Store the new values */
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001559 pvt->udimm_last_ce_count[socket][2] = new2;
1560 pvt->udimm_last_ce_count[socket][1] = new1;
1561 pvt->udimm_last_ce_count[socket][0] = new0;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001562}
1563
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001564/*
1565 * According with tables E-11 and E-12 of chapter E.3.3 of Intel 64 and IA-32
1566 * Architectures Software Developer’s Manual Volume 3B.
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001567 * Nehalem are defined as family 0x06, model 0x1a
1568 *
1569 * The MCA registers used here are the following ones:
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001570 * struct mce field MCA Register
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001571 * m->status MSR_IA32_MC8_STATUS
1572 * m->addr MSR_IA32_MC8_ADDR
1573 * m->misc MSR_IA32_MC8_MISC
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001574 * In the case of Nehalem, the error information is masked at .status and .misc
1575 * fields
1576 */
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001577static void i7core_mce_output_error(struct mem_ctl_info *mci,
1578 struct mce *m)
1579{
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001580 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001581 char *type, *optype, *err, *msg;
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001582 unsigned long error = m->status & 0x1ff0000l;
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001583 u32 optypenum = (m->status >> 4) & 0x07;
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001584 u32 core_err_cnt = (m->status >> 38) && 0x7fff;
1585 u32 dimm = (m->misc >> 16) & 0x3;
1586 u32 channel = (m->misc >> 18) & 0x3;
1587 u32 syndrome = m->misc >> 32;
1588 u32 errnum = find_first_bit(&error, 32);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001589 int csrow;
Mauro Carvalho Chehaba55456f2009-09-05 00:47:21 -03001590/* FIXME */
1591//#ifdef CONFIG_SMP
1592#if 0
1593 u32 socket_id = per_cpu(cpu_data, cpu).phys_proc_id;
Mauro Carvalho Chehab14d2c082009-09-02 23:52:36 -03001594#else
1595 u32 socket_id = 0;
1596#endif
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001597
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001598 if (m->mcgstatus & 1)
1599 type = "FATAL";
1600 else
1601 type = "NON_FATAL";
1602
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001603 switch (optypenum) {
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -03001604 case 0:
1605 optype = "generic undef request";
1606 break;
1607 case 1:
1608 optype = "read error";
1609 break;
1610 case 2:
1611 optype = "write error";
1612 break;
1613 case 3:
1614 optype = "addr/cmd error";
1615 break;
1616 case 4:
1617 optype = "scrubbing error";
1618 break;
1619 default:
1620 optype = "reserved";
1621 break;
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001622 }
1623
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001624 switch (errnum) {
1625 case 16:
1626 err = "read ECC error";
1627 break;
1628 case 17:
1629 err = "RAS ECC error";
1630 break;
1631 case 18:
1632 err = "write parity error";
1633 break;
1634 case 19:
1635 err = "redundacy loss";
1636 break;
1637 case 20:
1638 err = "reserved";
1639 break;
1640 case 21:
1641 err = "memory range error";
1642 break;
1643 case 22:
1644 err = "RTID out of range";
1645 break;
1646 case 23:
1647 err = "address parity error";
1648 break;
1649 case 24:
1650 err = "byte enable parity error";
1651 break;
1652 default:
1653 err = "unknown";
1654 }
1655
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001656 /* FIXME: should convert addr into bank and rank information */
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001657 msg = kasprintf(GFP_ATOMIC,
Mauro Carvalho Chehab3a7dde72009-07-18 12:20:04 -03001658 "%s (addr = 0x%08llx, socket=%d, Dimm=%d, Channel=%d, "
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001659 "syndrome=0x%08x, count=%d, Err=%08llx:%08llx (%s: %s))\n",
Mauro Carvalho Chehab14d2c082009-09-02 23:52:36 -03001660 type, (long long) m->addr, socket_id, dimm, channel,
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001661 syndrome, core_err_cnt, (long long)m->status,
1662 (long long)m->misc, optype, err);
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001663
1664 debugf0("%s", msg);
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001665
Mauro Carvalho Chehab14d2c082009-09-02 23:52:36 -03001666 if (socket_id < NUM_SOCKETS)
1667 csrow = pvt->csrow_map[socket_id][channel][dimm];
1668 else
1669 csrow = -1;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001670
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001671 /* Call the helper to output message */
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001672 if (m->mcgstatus & 1)
1673 edac_mc_handle_fbd_ue(mci, csrow, 0,
1674 0 /* FIXME: should be channel here */, msg);
Mauro Carvalho Chehab14d2c082009-09-02 23:52:36 -03001675 else if (!pvt->is_registered[socket_id])
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001676 edac_mc_handle_fbd_ce(mci, csrow,
1677 0 /* FIXME: should be channel here */, msg);
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001678
1679 kfree(msg);
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001680}
1681
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001682/*
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001683 * i7core_check_error Retrieve and process errors reported by the
1684 * hardware. Called by the Core module.
1685 */
1686static void i7core_check_error(struct mem_ctl_info *mci)
1687{
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001688 struct i7core_pvt *pvt = mci->pvt_info;
1689 int i;
1690 unsigned count = 0;
1691 struct mce *m = NULL;
1692 unsigned long flags;
1693
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001694 /* Copy all mce errors into a temporary buffer */
1695 spin_lock_irqsave(&pvt->mce_lock, flags);
1696 if (pvt->mce_count) {
1697 m = kmalloc(sizeof(*m) * pvt->mce_count, GFP_ATOMIC);
1698 if (m) {
1699 count = pvt->mce_count;
1700 memcpy(m, &pvt->mce_entry, sizeof(*m) * count);
1701 }
1702 pvt->mce_count = 0;
1703 }
1704 spin_unlock_irqrestore(&pvt->mce_lock, flags);
1705
1706 /* proccess mcelog errors */
1707 for (i = 0; i < count; i++)
1708 i7core_mce_output_error(mci, &m[i]);
1709
1710 kfree(m);
1711
1712 /* check memory count errors */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001713 for (i = 0; i < pvt->sockets; i++)
Mauro Carvalho Chehab14d2c082009-09-02 23:52:36 -03001714 if (!pvt->is_registered[i])
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001715 i7core_udimm_check_mc_ecc_err(mci, i);
1716 else
1717 i7core_rdimm_check_mc_ecc_err(mci, i);
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001718}
1719
1720/*
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001721 * i7core_mce_check_error Replicates mcelog routine to get errors
1722 * This routine simply queues mcelog errors, and
1723 * return. The error itself should be handled later
1724 * by i7core_check_error.
1725 */
1726static int i7core_mce_check_error(void *priv, struct mce *mce)
1727{
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001728 struct mem_ctl_info *mci = priv;
1729 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001730 unsigned long flags;
1731
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001732 /*
1733 * Just let mcelog handle it if the error is
1734 * outside the memory controller
1735 */
1736 if (((mce->status & 0xffff) >> 7) != 1)
1737 return 0;
1738
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001739 /* Bank 8 registers are the only ones that we know how to handle */
1740 if (mce->bank != 8)
1741 return 0;
1742
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001743 spin_lock_irqsave(&pvt->mce_lock, flags);
1744 if (pvt->mce_count < MCE_LOG_LEN) {
1745 memcpy(&pvt->mce_entry[pvt->mce_count], mce, sizeof(*mce));
1746 pvt->mce_count++;
1747 }
1748 spin_unlock_irqrestore(&pvt->mce_lock, flags);
1749
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001750 /* Handle fatal errors immediately */
1751 if (mce->mcgstatus & 1)
1752 i7core_check_error(mci);
1753
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001754 /* Advice mcelog that the error were handled */
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001755 return 1;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001756}
1757
1758/*
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001759 * i7core_probe Probe for ONE instance of device to see if it is
1760 * present.
1761 * return:
1762 * 0 for FOUND a device
1763 * < 0 for error code
1764 */
1765static int __devinit i7core_probe(struct pci_dev *pdev,
1766 const struct pci_device_id *id)
1767{
1768 struct mem_ctl_info *mci;
1769 struct i7core_pvt *pvt;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001770 int num_channels = 0;
1771 int num_csrows = 0;
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -03001772 int csrow = 0;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001773 int dev_idx = id->driver_data;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001774 int rc, i;
1775 u8 sockets;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001776
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001777 /*
1778 * FIXME: All memory controllers are allocated at the first pass.
1779 */
1780 if (unlikely(dev_idx >= 1))
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001781 return -EINVAL;
1782
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001783 /* get the pci devices we want to reserve for our use */
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001784 mutex_lock(&i7core_edac_lock);
1785 rc = i7core_get_devices(&sockets);
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001786 if (unlikely(rc < 0))
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001787 goto fail0;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001788
1789 for (i = 0; i < sockets; i++) {
1790 int channels;
1791 int csrows;
1792
1793 /* Check the number of active and not disabled channels */
1794 rc = i7core_get_active_channels(i, &channels, &csrows);
1795 if (unlikely(rc < 0))
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001796 goto fail1;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001797
1798 num_channels += channels;
1799 num_csrows += csrows;
1800 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001801
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001802 /* allocate a new MC control structure */
1803 mci = edac_mc_alloc(sizeof(*pvt), num_csrows, num_channels, 0);
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001804 if (unlikely(!mci)) {
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001805 rc = -ENOMEM;
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001806 goto fail1;
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001807 }
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001808
1809 debugf0("MC: " __FILE__ ": %s(): mci = %p\n", __func__, mci);
1810
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001811 mci->dev = &pdev->dev; /* record ptr to the generic device */
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001812 pvt = mci->pvt_info;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001813 memset(pvt, 0, sizeof(*pvt));
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001814 pvt->sockets = sockets;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001815 mci->mc_idx = 0;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001816
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001817 /*
1818 * FIXME: how to handle RDDR3 at MCI level? It is possible to have
1819 * Mixed RDDR3/UDDR3 with Nehalem, provided that they are on different
1820 * memory channels
1821 */
1822 mci->mtype_cap = MEM_FLAG_DDR3;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001823 mci->edac_ctl_cap = EDAC_FLAG_NONE;
1824 mci->edac_cap = EDAC_FLAG_NONE;
1825 mci->mod_name = "i7core_edac.c";
1826 mci->mod_ver = I7CORE_REVISION;
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001827 mci->ctl_name = i7core_probe_devs[dev_idx].ctl_name;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001828 mci->dev_name = pci_name(pdev);
1829 mci->ctl_page_to_phys = NULL;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001830 mci->mc_driver_sysfs_attributes = i7core_inj_attrs;
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001831 /* Set the function pointer to an actual operation function */
1832 mci->edac_check = i7core_check_error;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001833
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001834 /* Store pci devices at mci for faster access */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001835 rc = mci_bind_devs(mci);
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001836 if (unlikely(rc < 0))
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001837 goto fail2;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001838
1839 /* Get dimm basic config */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001840 for (i = 0; i < sockets; i++)
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -03001841 get_dimm_config(mci, &csrow, i);
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001842
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001843 /* add this new MC control structure to EDAC's list of MCs */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001844 if (unlikely(edac_mc_add_mc(mci))) {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001845 debugf0("MC: " __FILE__
1846 ": %s(): failed edac_mc_add_mc()\n", __func__);
1847 /* FIXME: perhaps some code should go here that disables error
1848 * reporting if we just enabled it
1849 */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001850
1851 rc = -EINVAL;
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001852 goto fail2;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001853 }
1854
1855 /* allocating generic PCI control info */
1856 i7core_pci = edac_pci_create_generic_ctl(&pdev->dev, EDAC_MOD_STR);
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001857 if (unlikely(!i7core_pci)) {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001858 printk(KERN_WARNING
1859 "%s(): Unable to create PCI control\n",
1860 __func__);
1861 printk(KERN_WARNING
1862 "%s(): PCI error report via EDAC not setup\n",
1863 __func__);
1864 }
1865
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001866 /* Default error mask is any memory */
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001867 pvt->inject.channel = 0;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001868 pvt->inject.dimm = -1;
1869 pvt->inject.rank = -1;
1870 pvt->inject.bank = -1;
1871 pvt->inject.page = -1;
1872 pvt->inject.col = -1;
1873
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001874 /* Registers on edac_mce in order to receive memory errors */
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001875 pvt->edac_mce.priv = mci;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001876 pvt->edac_mce.check_error = i7core_mce_check_error;
1877 spin_lock_init(&pvt->mce_lock);
1878
1879 rc = edac_mce_register(&pvt->edac_mce);
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -03001880 if (unlikely(rc < 0)) {
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001881 debugf0("MC: " __FILE__
1882 ": %s(): failed edac_mce_register()\n", __func__);
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001883 goto fail2;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001884 }
1885
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001886 i7core_printk(KERN_INFO, "Driver loaded.\n");
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001887
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001888 mutex_unlock(&i7core_edac_lock);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001889 return 0;
1890
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001891fail2:
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001892 edac_mc_free(mci);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001893
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001894fail1:
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001895 i7core_put_devices();
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001896fail0:
1897 mutex_unlock(&i7core_edac_lock);
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001898 return rc;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001899}
1900
1901/*
1902 * i7core_remove destructor for one instance of device
1903 *
1904 */
1905static void __devexit i7core_remove(struct pci_dev *pdev)
1906{
1907 struct mem_ctl_info *mci;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001908 struct i7core_pvt *pvt;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001909
1910 debugf0(__FILE__ ": %s()\n", __func__);
1911
1912 if (i7core_pci)
1913 edac_pci_release_generic_ctl(i7core_pci);
1914
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001915
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001916 mci = edac_mc_del_mc(&pdev->dev);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001917 if (!mci)
1918 return;
1919
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001920 /* Unregisters on edac_mce in order to receive memory errors */
1921 pvt = mci->pvt_info;
1922 edac_mce_unregister(&pvt->edac_mce);
1923
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001924 /* retrieve references to resources, and free those resources */
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001925 mutex_lock(&i7core_edac_lock);
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001926 i7core_put_devices();
Mauro Carvalho Chehab66607702009-09-05 00:52:11 -03001927 mutex_unlock(&i7core_edac_lock);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001928
1929 edac_mc_free(mci);
1930}
1931
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001932MODULE_DEVICE_TABLE(pci, i7core_pci_tbl);
1933
1934/*
1935 * i7core_driver pci_driver structure for this module
1936 *
1937 */
1938static struct pci_driver i7core_driver = {
1939 .name = "i7core_edac",
1940 .probe = i7core_probe,
1941 .remove = __devexit_p(i7core_remove),
1942 .id_table = i7core_pci_tbl,
1943};
1944
1945/*
1946 * i7core_init Module entry function
1947 * Try to initialize this module for its devices
1948 */
1949static int __init i7core_init(void)
1950{
1951 int pci_rc;
1952
1953 debugf2("MC: " __FILE__ ": %s()\n", __func__);
1954
1955 /* Ensure that the OPSTATE is set correctly for POLL or NMI */
1956 opstate_init();
1957
Keith Manntheybc2d7242009-09-03 00:05:05 -03001958 i7core_xeon_pci_fixup();
1959
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001960 pci_rc = pci_register_driver(&i7core_driver);
1961
Mauro Carvalho Chehab3ef288a2009-09-02 23:43:33 -03001962 if (pci_rc >= 0)
1963 return 0;
1964
1965 i7core_printk(KERN_ERR, "Failed to register device with error %d.\n",
1966 pci_rc);
1967
1968 return pci_rc;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001969}
1970
1971/*
1972 * i7core_exit() Module exit function
1973 * Unregister the driver
1974 */
1975static void __exit i7core_exit(void)
1976{
1977 debugf2("MC: " __FILE__ ": %s()\n", __func__);
1978 pci_unregister_driver(&i7core_driver);
1979}
1980
1981module_init(i7core_init);
1982module_exit(i7core_exit);
1983
1984MODULE_LICENSE("GPL");
1985MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
1986MODULE_AUTHOR("Red Hat Inc. (http://www.redhat.com)");
1987MODULE_DESCRIPTION("MC Driver for Intel i7 Core memory controllers - "
1988 I7CORE_REVISION);
1989
1990module_param(edac_op_state, int, 0444);
1991MODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI");