blob: 9f4e9d7d4dbf9db4d8f3242cdc929bf66cc13ff4 [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 {
195 int dev;
196 int func;
197 int dev_id;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300198 struct pci_dev *pdev[NUM_SOCKETS];
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300199};
200
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300201struct i7core_pvt {
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300202 struct pci_dev *pci_noncore[NUM_SOCKETS];
203 struct pci_dev *pci_mcr[NUM_SOCKETS][MAX_MCR_FUNC + 1];
204 struct pci_dev *pci_ch[NUM_SOCKETS][NUM_CHANS][MAX_CHAN_FUNC + 1];
205
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300206 struct i7core_info info;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300207 struct i7core_inject inject;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300208 struct i7core_channel channel[NUM_SOCKETS][NUM_CHANS];
209
210 int sockets; /* Number of sockets */
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300211 int channels; /* Number of active channels */
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -0300212
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300213 int ce_count_available[NUM_SOCKETS];
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -0300214 int csrow_map[NUM_SOCKETS][NUM_CHANS][MAX_DIMMS];
215
216 /* ECC corrected errors counts per udimm */
217 unsigned long udimm_ce_count[NUM_SOCKETS][MAX_DIMMS];
218 int udimm_last_ce_count[NUM_SOCKETS][MAX_DIMMS];
219 /* ECC corrected errors counts per rdimm */
220 unsigned long rdimm_ce_count[NUM_SOCKETS][NUM_CHANS][MAX_DIMMS];
221 int rdimm_last_ce_count[NUM_SOCKETS][NUM_CHANS][MAX_DIMMS];
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -0300222
Mauro Carvalho Chehab14d2c082009-09-02 23:52:36 -0300223 unsigned int is_registered[NUM_SOCKETS];
224
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -0300225 /* mcelog glue */
226 struct edac_mce edac_mce;
227 struct mce mce_entry[MCE_LOG_LEN];
228 unsigned mce_count;
229 spinlock_t mce_lock;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300230};
231
232/* Device name and register DID (Device ID) */
233struct i7core_dev_info {
234 const char *ctl_name; /* name for this device */
235 u16 fsb_mapping_errors; /* DID for the branchmap,control */
236};
237
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300238#define PCI_DESCR(device, function, device_id) \
239 .dev = (device), \
240 .func = (function), \
241 .dev_id = (device_id)
242
243struct pci_id_descr pci_devs[] = {
244 /* Memory controller */
245 { PCI_DESCR(3, 0, PCI_DEVICE_ID_INTEL_I7_MCR) },
246 { PCI_DESCR(3, 1, PCI_DEVICE_ID_INTEL_I7_MC_TAD) },
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -0300247 { PCI_DESCR(3, 2, PCI_DEVICE_ID_INTEL_I7_MC_RAS) }, /* if RDIMM */
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300248 { PCI_DESCR(3, 4, PCI_DEVICE_ID_INTEL_I7_MC_TEST) },
249
250 /* Channel 0 */
251 { PCI_DESCR(4, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH0_CTRL) },
252 { PCI_DESCR(4, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH0_ADDR) },
253 { PCI_DESCR(4, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH0_RANK) },
254 { PCI_DESCR(4, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH0_TC) },
255
256 /* Channel 1 */
257 { PCI_DESCR(5, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH1_CTRL) },
258 { PCI_DESCR(5, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH1_ADDR) },
259 { PCI_DESCR(5, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH1_RANK) },
260 { PCI_DESCR(5, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH1_TC) },
261
262 /* Channel 2 */
263 { PCI_DESCR(6, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH2_CTRL) },
264 { PCI_DESCR(6, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH2_ADDR) },
265 { PCI_DESCR(6, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH2_RANK) },
266 { PCI_DESCR(6, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH2_TC) },
Mauro Carvalho Chehab310cbb72009-07-17 00:09:10 -0300267
268 /* Generic Non-core registers */
269 /*
270 * This is the PCI device on i7core and on Xeon 35xx (8086:2c41)
271 * On Xeon 55xx, however, it has a different id (8086:2c40). So,
272 * the probing code needs to test for the other address in case of
273 * failure of this one
274 */
275 { PCI_DESCR(0, 0, PCI_DEVICE_ID_INTEL_I7_NOCORE) },
276
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300277};
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300278#define N_DEVS ARRAY_SIZE(pci_devs)
279
280/*
281 * pci_device_id table for which devices we are looking for
282 * This should match the first device at pci_devs table
283 */
284static const struct pci_device_id i7core_pci_tbl[] __devinitdata = {
Mauro Carvalho Chehabd1fd4fb2009-07-10 18:39:53 -0300285 {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_X58_HUB_MGMT)},
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300286 {0,} /* 0 terminated list. */
287};
288
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300289
290/* Table of devices attributes supported by this driver */
291static const struct i7core_dev_info i7core_devs[] = {
292 {
293 .ctl_name = "i7 Core",
294 .fsb_mapping_errors = PCI_DEVICE_ID_INTEL_I7_MCR,
295 },
296};
297
298static struct edac_pci_ctl_info *i7core_pci;
299
300/****************************************************************************
301 Anciliary status routines
302 ****************************************************************************/
303
304 /* MC_CONTROL bits */
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300305#define CH_ACTIVE(pvt, ch) ((pvt)->info.mc_control & (1 << (8 + ch)))
306#define ECCx8(pvt) ((pvt)->info.mc_control & (1 << 1))
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300307
308 /* MC_STATUS bits */
Keith Mannthey61053fd2009-09-02 23:46:59 -0300309#define ECC_ENABLED(pvt) ((pvt)->info.mc_status & (1 << 4))
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300310#define CH_DISABLED(pvt, ch) ((pvt)->info.mc_status & (1 << ch))
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300311
312 /* MC_MAX_DOD read functions */
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300313static inline int numdimms(u32 dimms)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300314{
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300315 return (dimms & 0x3) + 1;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300316}
317
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300318static inline int numrank(u32 rank)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300319{
320 static int ranks[4] = { 1, 2, 4, -EINVAL };
321
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300322 return ranks[rank & 0x3];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300323}
324
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300325static inline int numbank(u32 bank)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300326{
327 static int banks[4] = { 4, 8, 16, -EINVAL };
328
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300329 return banks[bank & 0x3];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300330}
331
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300332static inline int numrow(u32 row)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300333{
334 static int rows[8] = {
335 1 << 12, 1 << 13, 1 << 14, 1 << 15,
336 1 << 16, -EINVAL, -EINVAL, -EINVAL,
337 };
338
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300339 return rows[row & 0x7];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300340}
341
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300342static inline int numcol(u32 col)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300343{
344 static int cols[8] = {
345 1 << 10, 1 << 11, 1 << 12, -EINVAL,
346 };
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300347 return cols[col & 0x3];
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300348}
349
350/****************************************************************************
351 Memory check routines
352 ****************************************************************************/
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300353static struct pci_dev *get_pdev_slot_func(u8 socket, unsigned slot,
354 unsigned func)
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300355{
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300356 int i;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300357
358 for (i = 0; i < N_DEVS; i++) {
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300359 if (!pci_devs[i].pdev[socket])
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300360 continue;
361
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300362 if (PCI_SLOT(pci_devs[i].pdev[socket]->devfn) == slot &&
363 PCI_FUNC(pci_devs[i].pdev[socket]->devfn) == func) {
364 return pci_devs[i].pdev[socket];
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300365 }
366 }
367
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300368 return NULL;
369}
370
Mauro Carvalho Chehabec6df242009-07-18 10:44:30 -0300371/**
372 * i7core_get_active_channels() - gets the number of channels and csrows
373 * @socket: Quick Path Interconnect socket
374 * @channels: Number of channels that will be returned
375 * @csrows: Number of csrows found
376 *
377 * Since EDAC core needs to know in advance the number of available channels
378 * and csrows, in order to allocate memory for csrows/channels, it is needed
379 * to run two similar steps. At the first step, implemented on this function,
380 * it checks the number of csrows/channels present at one socket.
381 * this is used in order to properly allocate the size of mci components.
382 *
383 * It should be noticed that none of the current available datasheets explain
384 * or even mention how csrows are seen by the memory controller. So, we need
385 * to add a fake description for csrows.
386 * So, this driver is attributing one DIMM memory for one csrow.
387 */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300388static int i7core_get_active_channels(u8 socket, unsigned *channels,
389 unsigned *csrows)
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300390{
391 struct pci_dev *pdev = NULL;
392 int i, j;
393 u32 status, control;
394
395 *channels = 0;
396 *csrows = 0;
397
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300398 pdev = get_pdev_slot_func(socket, 3, 0);
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -0300399 if (!pdev) {
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300400 i7core_printk(KERN_ERR, "Couldn't find socket %d fn 3.0!!!\n",
401 socket);
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300402 return -ENODEV;
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -0300403 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300404
405 /* Device 3 function 0 reads */
406 pci_read_config_dword(pdev, MC_STATUS, &status);
407 pci_read_config_dword(pdev, MC_CONTROL, &control);
408
409 for (i = 0; i < NUM_CHANS; i++) {
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300410 u32 dimm_dod[3];
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300411 /* Check if the channel is active */
412 if (!(control & (1 << (8 + i))))
413 continue;
414
415 /* Check if the channel is disabled */
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300416 if (status & (1 << i))
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300417 continue;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300418
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300419 pdev = get_pdev_slot_func(socket, i + 4, 1);
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300420 if (!pdev) {
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300421 i7core_printk(KERN_ERR, "Couldn't find socket %d "
422 "fn %d.%d!!!\n",
423 socket, i + 4, 1);
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300424 return -ENODEV;
425 }
426 /* Devices 4-6 function 1 */
427 pci_read_config_dword(pdev,
428 MC_DOD_CH_DIMM0, &dimm_dod[0]);
429 pci_read_config_dword(pdev,
430 MC_DOD_CH_DIMM1, &dimm_dod[1]);
431 pci_read_config_dword(pdev,
432 MC_DOD_CH_DIMM2, &dimm_dod[2]);
433
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300434 (*channels)++;
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300435
436 for (j = 0; j < 3; j++) {
437 if (!DIMM_PRESENT(dimm_dod[j]))
438 continue;
439 (*csrows)++;
440 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300441 }
442
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -0300443 debugf0("Number of active channels on socket %d: %d\n",
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300444 socket, *channels);
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300445
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -0300446 return 0;
447}
448
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -0300449static int get_dimm_config(struct mem_ctl_info *mci, int *csrow, u8 socket)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300450{
451 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300452 struct csrow_info *csr;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300453 struct pci_dev *pdev;
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -0300454 int i, j;
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300455 unsigned long last_page = 0;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300456 enum edac_type mode;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300457 enum mem_type mtype;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300458
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300459 /* Get data from the MC register, function 0 */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300460 pdev = pvt->pci_mcr[socket][0];
Mauro Carvalho Chehab7dd69532009-06-22 22:48:30 -0300461 if (!pdev)
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300462 return -ENODEV;
463
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300464 /* Device 3 function 0 reads */
Mauro Carvalho Chehab7dd69532009-06-22 22:48:30 -0300465 pci_read_config_dword(pdev, MC_CONTROL, &pvt->info.mc_control);
466 pci_read_config_dword(pdev, MC_STATUS, &pvt->info.mc_status);
467 pci_read_config_dword(pdev, MC_MAX_DOD, &pvt->info.max_dod);
468 pci_read_config_dword(pdev, MC_CHANNEL_MAPPER, &pvt->info.ch_map);
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300469
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300470 debugf0("QPI %d control=0x%08x status=0x%08x dod=0x%08x map=0x%08x\n",
471 socket, pvt->info.mc_control, pvt->info.mc_status,
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300472 pvt->info.max_dod, pvt->info.ch_map);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300473
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300474 if (ECC_ENABLED(pvt)) {
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300475 debugf0("ECC enabled with x%d SDCC\n", ECCx8(pvt) ? 8 : 4);
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300476 if (ECCx8(pvt))
477 mode = EDAC_S8ECD8ED;
478 else
479 mode = EDAC_S4ECD4ED;
480 } else {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300481 debugf0("ECC disabled\n");
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300482 mode = EDAC_NONE;
483 }
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300484
485 /* FIXME: need to handle the error codes */
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300486 debugf0("DOD Max limits: DIMMS: %d, %d-ranked, %d-banked "
487 "x%x x 0x%x\n",
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300488 numdimms(pvt->info.max_dod),
489 numrank(pvt->info.max_dod >> 2),
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300490 numbank(pvt->info.max_dod >> 4),
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300491 numrow(pvt->info.max_dod >> 6),
492 numcol(pvt->info.max_dod >> 9));
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300493
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300494 for (i = 0; i < NUM_CHANS; i++) {
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300495 u32 data, dimm_dod[3], value[8];
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300496
497 if (!CH_ACTIVE(pvt, i)) {
498 debugf0("Channel %i is not active\n", i);
499 continue;
500 }
501 if (CH_DISABLED(pvt, i)) {
502 debugf0("Channel %i is disabled\n", i);
503 continue;
504 }
505
Mauro Carvalho Chehabf122a892009-06-22 22:48:29 -0300506 /* Devices 4-6 function 0 */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300507 pci_read_config_dword(pvt->pci_ch[socket][i][0],
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300508 MC_CHANNEL_DIMM_INIT_PARAMS, &data);
509
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300510 pvt->channel[socket][i].ranks = (data & QUAD_RANK_PRESENT) ?
511 4 : 2;
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300512
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300513 if (data & REGISTERED_DIMM)
514 mtype = MEM_RDDR3;
Mauro Carvalho Chehab14d2c082009-09-02 23:52:36 -0300515 else
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300516 mtype = MEM_DDR3;
517#if 0
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300518 if (data & THREE_DIMMS_PRESENT)
519 pvt->channel[i].dimms = 3;
520 else if (data & SINGLE_QUAD_RANK_PRESENT)
521 pvt->channel[i].dimms = 1;
522 else
523 pvt->channel[i].dimms = 2;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300524#endif
525
526 /* Devices 4-6 function 1 */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300527 pci_read_config_dword(pvt->pci_ch[socket][i][1],
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300528 MC_DOD_CH_DIMM0, &dimm_dod[0]);
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300529 pci_read_config_dword(pvt->pci_ch[socket][i][1],
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300530 MC_DOD_CH_DIMM1, &dimm_dod[1]);
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300531 pci_read_config_dword(pvt->pci_ch[socket][i][1],
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300532 MC_DOD_CH_DIMM2, &dimm_dod[2]);
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300533
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300534 debugf0("Ch%d phy rd%d, wr%d (0x%08x): "
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300535 "%d ranks, %cDIMMs\n",
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300536 i,
537 RDLCH(pvt->info.ch_map, i), WRLCH(pvt->info.ch_map, i),
538 data,
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300539 pvt->channel[socket][i].ranks,
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300540 (data & REGISTERED_DIMM) ? 'R' : 'U');
Mauro Carvalho Chehab7dd69532009-06-22 22:48:30 -0300541
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300542 for (j = 0; j < 3; j++) {
543 u32 banks, ranks, rows, cols;
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300544 u32 size, npages;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300545
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300546 if (!DIMM_PRESENT(dimm_dod[j]))
547 continue;
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300548
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300549 banks = numbank(MC_DOD_NUMBANK(dimm_dod[j]));
550 ranks = numrank(MC_DOD_NUMRANK(dimm_dod[j]));
551 rows = numrow(MC_DOD_NUMROW(dimm_dod[j]));
552 cols = numcol(MC_DOD_NUMCOL(dimm_dod[j]));
Mauro Carvalho Chehab1c6fed82009-06-22 22:48:30 -0300553
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300554 /* DDR3 has 8 I/O banks */
555 size = (rows * cols * banks * ranks) >> (20 - 3);
556
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300557 pvt->channel[socket][i].dimms++;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300558
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300559 debugf0("\tdimm %d %d Mb offset: %x, "
560 "bank: %d, rank: %d, row: %#x, col: %#x\n",
561 j, size,
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300562 RANKOFFSET(dimm_dod[j]),
563 banks, ranks, rows, cols);
564
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300565#if PAGE_SHIFT > 20
566 npages = size >> (PAGE_SHIFT - 20);
567#else
568 npages = size << (20 - PAGE_SHIFT);
569#endif
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300570
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -0300571 csr = &mci->csrows[*csrow];
Mauro Carvalho Chehab5566cb72009-06-22 22:48:31 -0300572 csr->first_page = last_page + 1;
573 last_page += npages;
574 csr->last_page = last_page;
575 csr->nr_pages = npages;
576
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300577 csr->page_mask = 0;
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300578 csr->grain = 8;
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -0300579 csr->csrow_idx = *csrow;
Mauro Carvalho Chehabeb94fc42009-06-22 22:48:31 -0300580 csr->nr_channels = 1;
581
582 csr->channels[0].chan_idx = i;
583 csr->channels[0].ce_count = 0;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300584
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -0300585 pvt->csrow_map[socket][i][j] = *csrow;
586
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300587 switch (banks) {
588 case 4:
589 csr->dtype = DEV_X4;
590 break;
591 case 8:
592 csr->dtype = DEV_X8;
593 break;
594 case 16:
595 csr->dtype = DEV_X16;
596 break;
597 default:
598 csr->dtype = DEV_UNKNOWN;
599 }
600
601 csr->edac_mode = mode;
602 csr->mtype = mtype;
603
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -0300604 (*csrow)++;
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300605 }
606
607 pci_read_config_dword(pdev, MC_SAG_CH_0, &value[0]);
608 pci_read_config_dword(pdev, MC_SAG_CH_1, &value[1]);
609 pci_read_config_dword(pdev, MC_SAG_CH_2, &value[2]);
610 pci_read_config_dword(pdev, MC_SAG_CH_3, &value[3]);
611 pci_read_config_dword(pdev, MC_SAG_CH_4, &value[4]);
612 pci_read_config_dword(pdev, MC_SAG_CH_5, &value[5]);
613 pci_read_config_dword(pdev, MC_SAG_CH_6, &value[6]);
614 pci_read_config_dword(pdev, MC_SAG_CH_7, &value[7]);
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300615 debugf1("\t[%i] DIVBY3\tREMOVED\tOFFSET\n", i);
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300616 for (j = 0; j < 8; j++)
Mauro Carvalho Chehab17cb7b02009-07-20 18:48:18 -0300617 debugf1("\t\t%#x\t%#x\t%#x\n",
Mauro Carvalho Chehab854d3342009-06-22 22:48:30 -0300618 (value[j] >> 27) & 0x1,
619 (value[j] >> 24) & 0x7,
620 (value[j] && ((1 << 24) - 1)));
Mauro Carvalho Chehab0b2b7b72009-06-22 22:48:29 -0300621 }
622
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -0300623 return 0;
624}
625
626/****************************************************************************
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300627 Error insertion routines
628 ****************************************************************************/
629
630/* The i7core has independent error injection features per channel.
631 However, to have a simpler code, we don't allow enabling error injection
632 on more than one channel.
633 Also, since a change at an inject parameter will be applied only at enable,
634 we're disabling error injection on all write calls to the sysfs nodes that
635 controls the error code injection.
636 */
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300637static int disable_inject(struct mem_ctl_info *mci)
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300638{
639 struct i7core_pvt *pvt = mci->pvt_info;
640
641 pvt->inject.enable = 0;
642
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300643 if (!pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0])
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300644 return -ENODEV;
645
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300646 pci_write_config_dword(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0],
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -0300647 MC_CHANNEL_ERROR_INJECT, 0);
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300648
649 return 0;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300650}
651
652/*
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300653 * i7core inject inject.socket
654 *
655 * accept and store error injection inject.socket value
656 */
657static ssize_t i7core_inject_socket_store(struct mem_ctl_info *mci,
658 const char *data, size_t count)
659{
660 struct i7core_pvt *pvt = mci->pvt_info;
661 unsigned long value;
662 int rc;
663
664 rc = strict_strtoul(data, 10, &value);
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300665 if ((rc < 0) || (value >= pvt->sockets))
Mauro Carvalho Chehab2068def2009-08-05 19:28:27 -0300666 return -EIO;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300667
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -0300668 pvt->inject.socket = (u32) value;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300669 return count;
670}
671
672static ssize_t i7core_inject_socket_show(struct mem_ctl_info *mci,
673 char *data)
674{
675 struct i7core_pvt *pvt = mci->pvt_info;
676 return sprintf(data, "%d\n", pvt->inject.socket);
677}
678
679/*
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300680 * i7core inject inject.section
681 *
682 * accept and store error injection inject.section value
683 * bit 0 - refers to the lower 32-byte half cacheline
684 * bit 1 - refers to the upper 32-byte half cacheline
685 */
686static ssize_t i7core_inject_section_store(struct mem_ctl_info *mci,
687 const char *data, size_t count)
688{
689 struct i7core_pvt *pvt = mci->pvt_info;
690 unsigned long value;
691 int rc;
692
693 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300694 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300695
696 rc = strict_strtoul(data, 10, &value);
697 if ((rc < 0) || (value > 3))
Mauro Carvalho Chehab2068def2009-08-05 19:28:27 -0300698 return -EIO;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300699
700 pvt->inject.section = (u32) value;
701 return count;
702}
703
704static ssize_t i7core_inject_section_show(struct mem_ctl_info *mci,
705 char *data)
706{
707 struct i7core_pvt *pvt = mci->pvt_info;
708 return sprintf(data, "0x%08x\n", pvt->inject.section);
709}
710
711/*
712 * i7core inject.type
713 *
714 * accept and store error injection inject.section value
715 * bit 0 - repeat enable - Enable error repetition
716 * bit 1 - inject ECC error
717 * bit 2 - inject parity error
718 */
719static ssize_t i7core_inject_type_store(struct mem_ctl_info *mci,
720 const char *data, size_t count)
721{
722 struct i7core_pvt *pvt = mci->pvt_info;
723 unsigned long value;
724 int rc;
725
726 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300727 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300728
729 rc = strict_strtoul(data, 10, &value);
730 if ((rc < 0) || (value > 7))
Mauro Carvalho Chehab2068def2009-08-05 19:28:27 -0300731 return -EIO;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300732
733 pvt->inject.type = (u32) value;
734 return count;
735}
736
737static ssize_t i7core_inject_type_show(struct mem_ctl_info *mci,
738 char *data)
739{
740 struct i7core_pvt *pvt = mci->pvt_info;
741 return sprintf(data, "0x%08x\n", pvt->inject.type);
742}
743
744/*
745 * i7core_inject_inject.eccmask_store
746 *
747 * The type of error (UE/CE) will depend on the inject.eccmask value:
748 * Any bits set to a 1 will flip the corresponding ECC bit
749 * Correctable errors can be injected by flipping 1 bit or the bits within
750 * a symbol pair (2 consecutive aligned 8-bit pairs - i.e. 7:0 and 15:8 or
751 * 23:16 and 31:24). Flipping bits in two symbol pairs will cause an
752 * uncorrectable error to be injected.
753 */
754static ssize_t i7core_inject_eccmask_store(struct mem_ctl_info *mci,
755 const char *data, size_t count)
756{
757 struct i7core_pvt *pvt = mci->pvt_info;
758 unsigned long value;
759 int rc;
760
761 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300762 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300763
764 rc = strict_strtoul(data, 10, &value);
765 if (rc < 0)
Mauro Carvalho Chehab2068def2009-08-05 19:28:27 -0300766 return -EIO;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300767
768 pvt->inject.eccmask = (u32) value;
769 return count;
770}
771
772static ssize_t i7core_inject_eccmask_show(struct mem_ctl_info *mci,
773 char *data)
774{
775 struct i7core_pvt *pvt = mci->pvt_info;
776 return sprintf(data, "0x%08x\n", pvt->inject.eccmask);
777}
778
779/*
780 * i7core_addrmatch
781 *
782 * The type of error (UE/CE) will depend on the inject.eccmask value:
783 * Any bits set to a 1 will flip the corresponding ECC bit
784 * Correctable errors can be injected by flipping 1 bit or the bits within
785 * a symbol pair (2 consecutive aligned 8-bit pairs - i.e. 7:0 and 15:8 or
786 * 23:16 and 31:24). Flipping bits in two symbol pairs will cause an
787 * uncorrectable error to be injected.
788 */
789static ssize_t i7core_inject_addrmatch_store(struct mem_ctl_info *mci,
790 const char *data, size_t count)
791{
792 struct i7core_pvt *pvt = mci->pvt_info;
793 char *cmd, *val;
794 long value;
795 int rc;
796
797 if (pvt->inject.enable)
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300798 disable_inject(mci);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300799
800 do {
801 cmd = strsep((char **) &data, ":");
802 if (!cmd)
803 break;
804 val = strsep((char **) &data, " \n\t");
805 if (!val)
806 return cmd - data;
807
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300808 if (!strcasecmp(val, "any"))
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300809 value = -1;
810 else {
811 rc = strict_strtol(val, 10, &value);
812 if ((rc < 0) || (value < 0))
813 return cmd - data;
814 }
815
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300816 if (!strcasecmp(cmd, "channel")) {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300817 if (value < 3)
818 pvt->inject.channel = value;
819 else
820 return cmd - data;
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300821 } else if (!strcasecmp(cmd, "dimm")) {
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300822 if (value < 3)
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300823 pvt->inject.dimm = value;
824 else
825 return cmd - data;
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300826 } else if (!strcasecmp(cmd, "rank")) {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300827 if (value < 4)
828 pvt->inject.rank = value;
829 else
830 return cmd - data;
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300831 } else if (!strcasecmp(cmd, "bank")) {
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300832 if (value < 32)
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300833 pvt->inject.bank = value;
834 else
835 return cmd - data;
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300836 } else if (!strcasecmp(cmd, "page")) {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300837 if (value <= 0xffff)
838 pvt->inject.page = value;
839 else
840 return cmd - data;
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -0300841 } else if (!strcasecmp(cmd, "col") ||
842 !strcasecmp(cmd, "column")) {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300843 if (value <= 0x3fff)
844 pvt->inject.col = value;
845 else
846 return cmd - data;
847 }
848 } while (1);
849
850 return count;
851}
852
853static ssize_t i7core_inject_addrmatch_show(struct mem_ctl_info *mci,
854 char *data)
855{
856 struct i7core_pvt *pvt = mci->pvt_info;
857 char channel[4], dimm[4], bank[4], rank[4], page[7], col[7];
858
859 if (pvt->inject.channel < 0)
860 sprintf(channel, "any");
861 else
862 sprintf(channel, "%d", pvt->inject.channel);
863 if (pvt->inject.dimm < 0)
864 sprintf(dimm, "any");
865 else
866 sprintf(dimm, "%d", pvt->inject.dimm);
867 if (pvt->inject.bank < 0)
868 sprintf(bank, "any");
869 else
870 sprintf(bank, "%d", pvt->inject.bank);
871 if (pvt->inject.rank < 0)
872 sprintf(rank, "any");
873 else
874 sprintf(rank, "%d", pvt->inject.rank);
875 if (pvt->inject.page < 0)
876 sprintf(page, "any");
877 else
878 sprintf(page, "0x%04x", pvt->inject.page);
879 if (pvt->inject.col < 0)
880 sprintf(col, "any");
881 else
882 sprintf(col, "0x%04x", pvt->inject.col);
883
884 return sprintf(data, "channel: %s\ndimm: %s\nbank: %s\n"
885 "rank: %s\npage: %s\ncolumn: %s\n",
886 channel, dimm, bank, rank, page, col);
887}
888
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300889static int write_and_test(struct pci_dev *dev, int where, u32 val)
890{
891 u32 read;
892 int count;
893
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -0300894 debugf0("setting pci %02x:%02x.%x reg=%02x value=%08x\n",
895 dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn),
896 where, val);
897
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300898 for (count = 0; count < 10; count++) {
899 if (count)
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -0300900 msleep(100);
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300901 pci_write_config_dword(dev, where, val);
902 pci_read_config_dword(dev, where, &read);
903
904 if (read == val)
905 return 0;
906 }
907
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -0300908 i7core_printk(KERN_ERR, "Error during set pci %02x:%02x.%x reg=%02x "
909 "write=%08x. Read=%08x\n",
910 dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn),
911 where, val, read);
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -0300912
913 return -EINVAL;
914}
915
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300916/*
917 * This routine prepares the Memory Controller for error injection.
918 * The error will be injected when some process tries to write to the
919 * memory that matches the given criteria.
920 * The criteria can be set in terms of a mask where dimm, rank, bank, page
921 * and col can be specified.
922 * A -1 value for any of the mask items will make the MCU to ignore
923 * that matching criteria for error injection.
924 *
925 * It should be noticed that the error will only happen after a write operation
926 * on a memory that matches the condition. if REPEAT_EN is not enabled at
927 * inject mask, then it will produce just one error. Otherwise, it will repeat
928 * until the injectmask would be cleaned.
929 *
930 * FIXME: This routine assumes that MAXNUMDIMMS value of MC_MAX_DOD
931 * is reliable enough to check if the MC is using the
932 * three channels. However, this is not clear at the datasheet.
933 */
934static ssize_t i7core_inject_enable_store(struct mem_ctl_info *mci,
935 const char *data, size_t count)
936{
937 struct i7core_pvt *pvt = mci->pvt_info;
938 u32 injectmask;
939 u64 mask = 0;
940 int rc;
941 long enable;
942
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300943 if (!pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0])
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -0300944 return 0;
945
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300946 rc = strict_strtoul(data, 10, &enable);
947 if ((rc < 0))
948 return 0;
949
950 if (enable) {
951 pvt->inject.enable = 1;
952 } else {
953 disable_inject(mci);
954 return count;
955 }
956
957 /* Sets pvt->inject.dimm mask */
958 if (pvt->inject.dimm < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300959 mask |= 1L << 41;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300960 else {
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300961 if (pvt->channel[pvt->inject.socket][pvt->inject.channel].dimms > 2)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300962 mask |= (pvt->inject.dimm & 0x3L) << 35;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300963 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300964 mask |= (pvt->inject.dimm & 0x1L) << 36;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300965 }
966
967 /* Sets pvt->inject.rank mask */
968 if (pvt->inject.rank < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300969 mask |= 1L << 40;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300970 else {
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -0300971 if (pvt->channel[pvt->inject.socket][pvt->inject.channel].dimms > 2)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300972 mask |= (pvt->inject.rank & 0x1L) << 34;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300973 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300974 mask |= (pvt->inject.rank & 0x3L) << 34;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300975 }
976
977 /* Sets pvt->inject.bank mask */
978 if (pvt->inject.bank < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300979 mask |= 1L << 39;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300980 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300981 mask |= (pvt->inject.bank & 0x15L) << 30;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300982
983 /* Sets pvt->inject.page mask */
984 if (pvt->inject.page < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300985 mask |= 1L << 38;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300986 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300987 mask |= (pvt->inject.page & 0xffffL) << 14;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300988
989 /* Sets pvt->inject.column mask */
990 if (pvt->inject.col < 0)
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300991 mask |= 1L << 37;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300992 else
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -0300993 mask |= (pvt->inject.col & 0x3fffL);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300994
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -0300995 /*
996 * bit 0: REPEAT_EN
997 * bits 1-2: MASK_HALF_CACHELINE
998 * bit 3: INJECT_ECC
999 * bit 4: INJECT_ADDR_PARITY
1000 */
1001
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -03001002 injectmask = (pvt->inject.type & 1) |
1003 (pvt->inject.section & 0x3) << 1 |
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001004 (pvt->inject.type & 0x6) << (3 - 1);
1005
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -03001006 /* Unlock writes to registers - this register is write only */
1007 pci_write_config_dword(pvt->pci_noncore[pvt->inject.socket],
1008 MC_CFG_CONTROL, 0x2);
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001009
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -03001010 write_and_test(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0],
1011 MC_CHANNEL_ADDR_MATCH, mask);
1012 write_and_test(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0],
1013 MC_CHANNEL_ADDR_MATCH + 4, mask >> 32L);
1014
1015 write_and_test(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0],
1016 MC_CHANNEL_ERROR_MASK, pvt->inject.eccmask);
1017
1018 write_and_test(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0],
Mauro Carvalho Chehab4157d9f2009-08-05 20:27:15 -03001019 MC_CHANNEL_ERROR_INJECT, injectmask);
Mauro Carvalho Chehab276b8242009-07-22 21:45:50 -03001020
1021 /*
1022 * This is something undocumented, based on my tests
1023 * Without writing 8 to this register, errors aren't injected. Not sure
1024 * why.
1025 */
1026 pci_write_config_dword(pvt->pci_noncore[pvt->inject.socket],
1027 MC_CFG_CONTROL, 8);
1028
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001029 debugf0("Error inject addr match 0x%016llx, ecc 0x%08x,"
1030 " inject 0x%08x\n",
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001031 mask, pvt->inject.eccmask, injectmask);
1032
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -03001033
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001034 return count;
1035}
1036
1037static ssize_t i7core_inject_enable_show(struct mem_ctl_info *mci,
1038 char *data)
1039{
1040 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehab7b029d02009-06-22 22:48:29 -03001041 u32 injectmask;
1042
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001043 pci_read_config_dword(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 Chehab7b029d02009-06-22 22:48:29 -03001045
1046 debugf0("Inject error read: 0x%018x\n", injectmask);
1047
1048 if (injectmask & 0x0c)
1049 pvt->inject.enable = 1;
1050
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001051 return sprintf(data, "%d\n", pvt->inject.enable);
1052}
1053
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001054static ssize_t i7core_ce_regs_show(struct mem_ctl_info *mci, char *data)
1055{
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001056 unsigned i, j, count, total = 0;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001057 struct i7core_pvt *pvt = mci->pvt_info;
1058
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001059 for (i = 0; i < pvt->sockets; i++) {
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001060 if (!pvt->ce_count_available[i]) {
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001061 count = sprintf(data, "socket 0 data unavailable\n");
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001062 continue;
1063 }
Mauro Carvalho Chehab14d2c082009-09-02 23:52:36 -03001064 if (!pvt->is_registered[i])
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001065 count = sprintf(data, "socket %d, dimm0: %lu\n"
1066 "dimm1: %lu\ndimm2: %lu\n",
1067 i,
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001068 pvt->udimm_ce_count[i][0],
1069 pvt->udimm_ce_count[i][1],
1070 pvt->udimm_ce_count[i][2]);
1071 else
1072 for (j = 0; j < NUM_CHANS; j++) {
1073 count = sprintf(data, "socket %d, channel %d"
1074 "dimm0: %lu\n"
1075 "dimm1: %lu\ndimm2: %lu\n",
1076 i, j,
1077 pvt->rdimm_ce_count[i][j][0],
1078 pvt->rdimm_ce_count[i][j][1],
1079 pvt->rdimm_ce_count[i][j][2]);
1080 }
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001081 data += count;
1082 total += count;
1083 }
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001084
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001085 return total;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001086}
1087
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001088/*
1089 * Sysfs struct
1090 */
1091static struct mcidev_sysfs_attribute i7core_inj_attrs[] = {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001092 {
1093 .attr = {
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001094 .name = "inject_socket",
1095 .mode = (S_IRUGO | S_IWUSR)
1096 },
1097 .show = i7core_inject_socket_show,
1098 .store = i7core_inject_socket_store,
1099 }, {
1100 .attr = {
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001101 .name = "inject_section",
1102 .mode = (S_IRUGO | S_IWUSR)
1103 },
1104 .show = i7core_inject_section_show,
1105 .store = i7core_inject_section_store,
1106 }, {
1107 .attr = {
1108 .name = "inject_type",
1109 .mode = (S_IRUGO | S_IWUSR)
1110 },
1111 .show = i7core_inject_type_show,
1112 .store = i7core_inject_type_store,
1113 }, {
1114 .attr = {
1115 .name = "inject_eccmask",
1116 .mode = (S_IRUGO | S_IWUSR)
1117 },
1118 .show = i7core_inject_eccmask_show,
1119 .store = i7core_inject_eccmask_store,
1120 }, {
1121 .attr = {
1122 .name = "inject_addrmatch",
1123 .mode = (S_IRUGO | S_IWUSR)
1124 },
1125 .show = i7core_inject_addrmatch_show,
1126 .store = i7core_inject_addrmatch_store,
1127 }, {
1128 .attr = {
1129 .name = "inject_enable",
1130 .mode = (S_IRUGO | S_IWUSR)
1131 },
1132 .show = i7core_inject_enable_show,
1133 .store = i7core_inject_enable_store,
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001134 }, {
1135 .attr = {
1136 .name = "corrected_error_counts",
1137 .mode = (S_IRUGO | S_IWUSR)
1138 },
1139 .show = i7core_ce_regs_show,
1140 .store = NULL,
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001141 },
1142};
1143
1144/****************************************************************************
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001145 Device initialization routines: put/get, init/exit
1146 ****************************************************************************/
1147
1148/*
1149 * i7core_put_devices 'put' all the devices that we have
1150 * reserved via 'get'
1151 */
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001152static void i7core_put_devices(void)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001153{
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001154 int i, j;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001155
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001156 for (i = 0; i < NUM_SOCKETS; i++)
1157 for (j = 0; j < N_DEVS; j++)
1158 pci_dev_put(pci_devs[j].pdev[i]);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001159}
1160
1161/*
1162 * i7core_get_devices Find and perform 'get' operation on the MCH's
1163 * device/functions we want to reference for this driver
1164 *
1165 * Need to 'get' device 16 func 1 and func 2
1166 */
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001167int i7core_get_onedevice(struct pci_dev **prev, int devno)
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001168{
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001169 struct pci_dev *pdev = NULL;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001170 u8 bus = 0;
1171 u8 socket = 0;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001172
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001173 pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
1174 pci_devs[devno].dev_id, *prev);
1175
1176 /*
1177 * On Xeon 55xx, the Intel Quckpath Arch Generic Non-core pci buses
1178 * aren't announced by acpi. So, we need to use a legacy scan probing
1179 * to detect them
1180 */
1181 if (unlikely(!pdev && !devno && !prev)) {
1182 pcibios_scan_specific_bus(254);
1183 pcibios_scan_specific_bus(255);
1184
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001185 pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001186 pci_devs[devno].dev_id, *prev);
1187 }
Mauro Carvalho Chehabd1fd4fb2009-07-10 18:39:53 -03001188
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001189 /*
1190 * On Xeon 55xx, the Intel Quckpath Arch Generic Non-core regs
1191 * is at addr 8086:2c40, instead of 8086:2c41. So, we need
1192 * to probe for the alternate address in case of failure
1193 */
1194 if (pci_devs[devno].dev_id == PCI_DEVICE_ID_INTEL_I7_NOCORE && !pdev)
1195 pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
1196 PCI_DEVICE_ID_INTEL_I7_NOCORE_ALT, *prev);
Mauro Carvalho Chehabd1fd4fb2009-07-10 18:39:53 -03001197
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001198 if (!pdev) {
1199 if (*prev) {
1200 *prev = pdev;
1201 return 0;
Mauro Carvalho Chehabd1fd4fb2009-07-10 18:39:53 -03001202 }
1203
Mauro Carvalho Chehab310cbb72009-07-17 00:09:10 -03001204 /*
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001205 * Dev 3 function 2 only exists on chips with RDIMMs
1206 * so, it is ok to not found it
Mauro Carvalho Chehab310cbb72009-07-17 00:09:10 -03001207 */
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001208 if ((pci_devs[devno].dev == 3) && (pci_devs[devno].func == 2)) {
1209 *prev = pdev;
1210 return 0;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001211 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001212
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001213 i7core_printk(KERN_ERR,
1214 "Device not found: dev %02x.%d PCI ID %04x:%04x\n",
1215 pci_devs[devno].dev, pci_devs[devno].func,
1216 PCI_VENDOR_ID_INTEL, pci_devs[devno].dev_id);
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001217
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001218 /* End of list, leave */
1219 return -ENODEV;
1220 }
1221 bus = pdev->bus->number;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001222
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001223 if (bus == 0x3f)
1224 socket = 0;
1225 else
1226 socket = 255 - bus;
1227
1228 if (socket >= NUM_SOCKETS) {
1229 i7core_printk(KERN_ERR,
1230 "Unexpected socket for "
1231 "dev %02x:%02x.%d PCI ID %04x:%04x\n",
1232 bus, pci_devs[devno].dev, pci_devs[devno].func,
1233 PCI_VENDOR_ID_INTEL, pci_devs[devno].dev_id);
1234 pci_dev_put(pdev);
1235 return -ENODEV;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001236 }
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001237
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001238 if (pci_devs[devno].pdev[socket]) {
1239 i7core_printk(KERN_ERR,
1240 "Duplicated device for "
1241 "dev %02x:%02x.%d PCI ID %04x:%04x\n",
1242 bus, pci_devs[devno].dev, pci_devs[devno].func,
1243 PCI_VENDOR_ID_INTEL, pci_devs[devno].dev_id);
1244 pci_dev_put(pdev);
1245 return -ENODEV;
1246 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001247
Mauro Carvalho Chehabc77720b2009-07-18 10:43:08 -03001248 pci_devs[devno].pdev[socket] = pdev;
1249
1250 /* Sanity check */
1251 if (unlikely(PCI_SLOT(pdev->devfn) != pci_devs[devno].dev ||
1252 PCI_FUNC(pdev->devfn) != pci_devs[devno].func)) {
1253 i7core_printk(KERN_ERR,
1254 "Device PCI ID %04x:%04x "
1255 "has dev %02x:%02x.%d instead of dev %02x:%02x.%d\n",
1256 PCI_VENDOR_ID_INTEL, pci_devs[devno].dev_id,
1257 bus, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
1258 bus, pci_devs[devno].dev, pci_devs[devno].func);
1259 return -ENODEV;
1260 }
1261
1262 /* Be sure that the device is enabled */
1263 if (unlikely(pci_enable_device(pdev) < 0)) {
1264 i7core_printk(KERN_ERR,
1265 "Couldn't enable "
1266 "dev %02x:%02x.%d PCI ID %04x:%04x\n",
1267 bus, pci_devs[devno].dev, pci_devs[devno].func,
1268 PCI_VENDOR_ID_INTEL, pci_devs[devno].dev_id);
1269 return -ENODEV;
1270 }
1271
1272 i7core_printk(KERN_INFO,
1273 "Registered socket %d "
1274 "dev %02x:%02x.%d PCI ID %04x:%04x\n",
1275 socket, bus, pci_devs[devno].dev, pci_devs[devno].func,
1276 PCI_VENDOR_ID_INTEL, pci_devs[devno].dev_id);
1277
1278 *prev = pdev;
1279
1280 return 0;
1281}
1282
1283static int i7core_get_devices(void)
1284{
1285 int i;
1286 struct pci_dev *pdev = NULL;
1287
1288 for (i = 0; i < N_DEVS; i++) {
1289 pdev = NULL;
1290 do {
1291 if (i7core_get_onedevice(&pdev, i) < 0) {
1292 i7core_put_devices();
1293 return -ENODEV;
1294 }
1295 } while (pdev);
1296 }
1297 return 0;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001298}
1299
1300static int mci_bind_devs(struct mem_ctl_info *mci)
1301{
1302 struct i7core_pvt *pvt = mci->pvt_info;
1303 struct pci_dev *pdev;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001304 int i, j, func, slot;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001305
Mauro Carvalho Chehab14d2c082009-09-02 23:52:36 -03001306
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001307 for (i = 0; i < pvt->sockets; i++) {
Mauro Carvalho Chehab14d2c082009-09-02 23:52:36 -03001308 pvt->is_registered[i] = 0;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001309 for (j = 0; j < N_DEVS; j++) {
1310 pdev = pci_devs[j].pdev[i];
1311 if (!pdev)
1312 continue;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001313
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001314 func = PCI_FUNC(pdev->devfn);
1315 slot = PCI_SLOT(pdev->devfn);
1316 if (slot == 3) {
1317 if (unlikely(func > MAX_MCR_FUNC))
1318 goto error;
1319 pvt->pci_mcr[i][func] = pdev;
1320 } else if (likely(slot >= 4 && slot < 4 + NUM_CHANS)) {
1321 if (unlikely(func > MAX_CHAN_FUNC))
1322 goto error;
1323 pvt->pci_ch[i][slot - 4][func] = pdev;
1324 } else if (!slot && !func)
1325 pvt->pci_noncore[i] = pdev;
1326 else
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001327 goto error;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001328
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001329 debugf0("Associated fn %d.%d, dev = %p, socket %d\n",
1330 PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
1331 pdev, i);
Mauro Carvalho Chehab14d2c082009-09-02 23:52:36 -03001332
1333 if (PCI_SLOT(pdev->devfn) == 3 &&
1334 PCI_FUNC(pdev->devfn) == 2)
1335 pvt->is_registered[i] = 1;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001336 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001337 }
Mauro Carvalho Chehabe9bd2e72009-07-09 22:14:35 -03001338
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001339 return 0;
1340
1341error:
1342 i7core_printk(KERN_ERR, "Device %d, function %d "
1343 "is out of the expected range\n",
1344 slot, func);
1345 return -EINVAL;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001346}
1347
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001348/****************************************************************************
1349 Error check routines
1350 ****************************************************************************/
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001351static void i7core_rdimm_update_csrow(struct mem_ctl_info *mci, int socket,
1352 int chan, int dimm, int add)
1353{
1354 char *msg;
1355 struct i7core_pvt *pvt = mci->pvt_info;
1356 int row = pvt->csrow_map[socket][chan][dimm], i;
1357
1358 for (i = 0; i < add; i++) {
1359 msg = kasprintf(GFP_KERNEL, "Corrected error "
1360 "(Socket=%d channel=%d dimm=%d",
1361 socket, chan, dimm);
1362
1363 edac_mc_handle_fbd_ce(mci, row, 0, msg);
1364 kfree (msg);
1365 }
1366}
1367
1368static void i7core_rdimm_update_ce_count(struct mem_ctl_info *mci,
1369 int socket, int chan, int new0, int new1, int new2)
1370{
1371 struct i7core_pvt *pvt = mci->pvt_info;
1372 int add0 = 0, add1 = 0, add2 = 0;
1373 /* Updates CE counters if it is not the first time here */
1374 if (pvt->ce_count_available[socket]) {
1375 /* Updates CE counters */
1376
1377 add2 = new2 - pvt->rdimm_last_ce_count[socket][chan][2];
1378 add1 = new1 - pvt->rdimm_last_ce_count[socket][chan][1];
1379 add0 = new0 - pvt->rdimm_last_ce_count[socket][chan][0];
1380
1381 if (add2 < 0)
1382 add2 += 0x7fff;
1383 pvt->rdimm_ce_count[socket][chan][2] += add2;
1384
1385 if (add1 < 0)
1386 add1 += 0x7fff;
1387 pvt->rdimm_ce_count[socket][chan][1] += add1;
1388
1389 if (add0 < 0)
1390 add0 += 0x7fff;
1391 pvt->rdimm_ce_count[socket][chan][0] += add0;
1392 } else
1393 pvt->ce_count_available[socket] = 1;
1394
1395 /* Store the new values */
1396 pvt->rdimm_last_ce_count[socket][chan][2] = new2;
1397 pvt->rdimm_last_ce_count[socket][chan][1] = new1;
1398 pvt->rdimm_last_ce_count[socket][chan][0] = new0;
1399
1400 /*updated the edac core */
1401 if (add0 != 0)
1402 i7core_rdimm_update_csrow(mci, socket, chan, 0, add0);
1403 if (add1 != 0)
1404 i7core_rdimm_update_csrow(mci, socket, chan, 1, add1);
1405 if (add2 != 0)
1406 i7core_rdimm_update_csrow(mci, socket, chan, 2, add2);
1407
1408}
1409
1410static void i7core_rdimm_check_mc_ecc_err(struct mem_ctl_info *mci, u8 socket)
1411{
1412 struct i7core_pvt *pvt = mci->pvt_info;
1413 u32 rcv[3][2];
1414 int i, new0, new1, new2;
1415
1416 /*Read DEV 3: FUN 2: MC_COR_ECC_CNT regs directly*/
1417 pci_read_config_dword(pvt->pci_mcr[socket][2], MC_COR_ECC_CNT_0,
1418 &rcv[0][0]);
1419 pci_read_config_dword(pvt->pci_mcr[socket][2], MC_COR_ECC_CNT_1,
1420 &rcv[0][1]);
1421 pci_read_config_dword(pvt->pci_mcr[socket][2], MC_COR_ECC_CNT_2,
1422 &rcv[1][0]);
1423 pci_read_config_dword(pvt->pci_mcr[socket][2], MC_COR_ECC_CNT_3,
1424 &rcv[1][1]);
1425 pci_read_config_dword(pvt->pci_mcr[socket][2], MC_COR_ECC_CNT_4,
1426 &rcv[2][0]);
1427 pci_read_config_dword(pvt->pci_mcr[socket][2], MC_COR_ECC_CNT_5,
1428 &rcv[2][1]);
1429 for (i = 0 ; i < 3; i++) {
1430 debugf3("MC_COR_ECC_CNT%d = 0x%x; MC_COR_ECC_CNT%d = 0x%x\n",
1431 (i * 2), rcv[i][0], (i * 2) + 1, rcv[i][1]);
1432 /*if the channel has 3 dimms*/
1433 if (pvt->channel[socket][i].dimms > 2) {
1434 new0 = DIMM_BOT_COR_ERR(rcv[i][0]);
1435 new1 = DIMM_TOP_COR_ERR(rcv[i][0]);
1436 new2 = DIMM_BOT_COR_ERR(rcv[i][1]);
1437 } else {
1438 new0 = DIMM_TOP_COR_ERR(rcv[i][0]) +
1439 DIMM_BOT_COR_ERR(rcv[i][0]);
1440 new1 = DIMM_TOP_COR_ERR(rcv[i][1]) +
1441 DIMM_BOT_COR_ERR(rcv[i][1]);
1442 new2 = 0;
1443 }
1444
1445 i7core_rdimm_update_ce_count(mci, socket, i, new0, new1, new2);
1446 }
1447}
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001448
1449/* This function is based on the device 3 function 4 registers as described on:
1450 * Intel Xeon Processor 5500 Series Datasheet Volume 2
1451 * http://www.intel.com/Assets/PDF/datasheet/321322.pdf
1452 * also available at:
1453 * http://www.arrownac.com/manufacturers/intel/s/nehalem/5500-datasheet-v2.pdf
1454 */
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001455static void i7core_udimm_check_mc_ecc_err(struct mem_ctl_info *mci, u8 socket)
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001456{
1457 struct i7core_pvt *pvt = mci->pvt_info;
1458 u32 rcv1, rcv0;
1459 int new0, new1, new2;
1460
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001461 if (!pvt->pci_mcr[socket][4]) {
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -03001462 debugf0("%s MCR registers not found\n", __func__);
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001463 return;
1464 }
1465
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001466 /* Corrected test errors */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001467 pci_read_config_dword(pvt->pci_mcr[socket][4], MC_TEST_ERR_RCV1, &rcv1);
1468 pci_read_config_dword(pvt->pci_mcr[socket][4], MC_TEST_ERR_RCV0, &rcv0);
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001469
1470 /* Store the new values */
1471 new2 = DIMM2_COR_ERR(rcv1);
1472 new1 = DIMM1_COR_ERR(rcv0);
1473 new0 = DIMM0_COR_ERR(rcv0);
1474
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001475 /* Updates CE counters if it is not the first time here */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001476 if (pvt->ce_count_available[socket]) {
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001477 /* Updates CE counters */
1478 int add0, add1, add2;
1479
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001480 add2 = new2 - pvt->udimm_last_ce_count[socket][2];
1481 add1 = new1 - pvt->udimm_last_ce_count[socket][1];
1482 add0 = new0 - pvt->udimm_last_ce_count[socket][0];
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001483
1484 if (add2 < 0)
1485 add2 += 0x7fff;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001486 pvt->udimm_ce_count[socket][2] += add2;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001487
1488 if (add1 < 0)
1489 add1 += 0x7fff;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001490 pvt->udimm_ce_count[socket][1] += add1;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001491
1492 if (add0 < 0)
1493 add0 += 0x7fff;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001494 pvt->udimm_ce_count[socket][0] += add0;
1495
1496 if (add0 | add1 | add2)
1497 i7core_printk(KERN_ERR, "New Corrected error(s): "
1498 "dimm0: +%d, dimm1: +%d, dimm2 +%d\n",
1499 add0, add1, add2);
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001500 } else
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001501 pvt->ce_count_available[socket] = 1;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001502
1503 /* Store the new values */
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001504 pvt->udimm_last_ce_count[socket][2] = new2;
1505 pvt->udimm_last_ce_count[socket][1] = new1;
1506 pvt->udimm_last_ce_count[socket][0] = new0;
Mauro Carvalho Chehab442305b2009-06-22 22:48:29 -03001507}
1508
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001509/*
1510 * According with tables E-11 and E-12 of chapter E.3.3 of Intel 64 and IA-32
1511 * Architectures Software Developer’s Manual Volume 3B.
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001512 * Nehalem are defined as family 0x06, model 0x1a
1513 *
1514 * The MCA registers used here are the following ones:
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001515 * struct mce field MCA Register
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001516 * m->status MSR_IA32_MC8_STATUS
1517 * m->addr MSR_IA32_MC8_ADDR
1518 * m->misc MSR_IA32_MC8_MISC
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001519 * In the case of Nehalem, the error information is masked at .status and .misc
1520 * fields
1521 */
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001522static void i7core_mce_output_error(struct mem_ctl_info *mci,
1523 struct mce *m)
1524{
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001525 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001526 char *type, *optype, *err, *msg;
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001527 unsigned long error = m->status & 0x1ff0000l;
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001528 u32 optypenum = (m->status >> 4) & 0x07;
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001529 u32 core_err_cnt = (m->status >> 38) && 0x7fff;
1530 u32 dimm = (m->misc >> 16) & 0x3;
1531 u32 channel = (m->misc >> 18) & 0x3;
1532 u32 syndrome = m->misc >> 32;
1533 u32 errnum = find_first_bit(&error, 32);
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001534 int csrow;
Mauro Carvalho Chehab14d2c082009-09-02 23:52:36 -03001535#ifdef CONFIG_SMP
1536 u32 socket_id = cpu_data[m->cpu].phys_proc_id;
1537#else
1538 u32 socket_id = 0;
1539#endif
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001540
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001541 if (m->mcgstatus & 1)
1542 type = "FATAL";
1543 else
1544 type = "NON_FATAL";
1545
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001546 switch (optypenum) {
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -03001547 case 0:
1548 optype = "generic undef request";
1549 break;
1550 case 1:
1551 optype = "read error";
1552 break;
1553 case 2:
1554 optype = "write error";
1555 break;
1556 case 3:
1557 optype = "addr/cmd error";
1558 break;
1559 case 4:
1560 optype = "scrubbing error";
1561 break;
1562 default:
1563 optype = "reserved";
1564 break;
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001565 }
1566
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001567 switch (errnum) {
1568 case 16:
1569 err = "read ECC error";
1570 break;
1571 case 17:
1572 err = "RAS ECC error";
1573 break;
1574 case 18:
1575 err = "write parity error";
1576 break;
1577 case 19:
1578 err = "redundacy loss";
1579 break;
1580 case 20:
1581 err = "reserved";
1582 break;
1583 case 21:
1584 err = "memory range error";
1585 break;
1586 case 22:
1587 err = "RTID out of range";
1588 break;
1589 case 23:
1590 err = "address parity error";
1591 break;
1592 case 24:
1593 err = "byte enable parity error";
1594 break;
1595 default:
1596 err = "unknown";
1597 }
1598
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001599 /* FIXME: should convert addr into bank and rank information */
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001600 msg = kasprintf(GFP_ATOMIC,
Mauro Carvalho Chehab3a7dde72009-07-18 12:20:04 -03001601 "%s (addr = 0x%08llx, socket=%d, Dimm=%d, Channel=%d, "
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001602 "syndrome=0x%08x, count=%d, Err=%08llx:%08llx (%s: %s))\n",
Mauro Carvalho Chehab14d2c082009-09-02 23:52:36 -03001603 type, (long long) m->addr, socket_id, dimm, channel,
Mauro Carvalho Chehaba6395392009-07-17 10:54:23 -03001604 syndrome, core_err_cnt, (long long)m->status,
1605 (long long)m->misc, optype, err);
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001606
1607 debugf0("%s", msg);
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001608
Mauro Carvalho Chehab14d2c082009-09-02 23:52:36 -03001609 if (socket_id < NUM_SOCKETS)
1610 csrow = pvt->csrow_map[socket_id][channel][dimm];
1611 else
1612 csrow = -1;
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001613
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001614 /* Call the helper to output message */
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001615 if (m->mcgstatus & 1)
1616 edac_mc_handle_fbd_ue(mci, csrow, 0,
1617 0 /* FIXME: should be channel here */, msg);
Mauro Carvalho Chehab14d2c082009-09-02 23:52:36 -03001618 else if (!pvt->is_registered[socket_id])
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001619 edac_mc_handle_fbd_ce(mci, csrow,
1620 0 /* FIXME: should be channel here */, msg);
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001621
1622 kfree(msg);
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001623}
1624
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001625/*
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001626 * i7core_check_error Retrieve and process errors reported by the
1627 * hardware. Called by the Core module.
1628 */
1629static void i7core_check_error(struct mem_ctl_info *mci)
1630{
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001631 struct i7core_pvt *pvt = mci->pvt_info;
1632 int i;
1633 unsigned count = 0;
1634 struct mce *m = NULL;
1635 unsigned long flags;
1636
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001637 /* Copy all mce errors into a temporary buffer */
1638 spin_lock_irqsave(&pvt->mce_lock, flags);
1639 if (pvt->mce_count) {
1640 m = kmalloc(sizeof(*m) * pvt->mce_count, GFP_ATOMIC);
1641 if (m) {
1642 count = pvt->mce_count;
1643 memcpy(m, &pvt->mce_entry, sizeof(*m) * count);
1644 }
1645 pvt->mce_count = 0;
1646 }
1647 spin_unlock_irqrestore(&pvt->mce_lock, flags);
1648
1649 /* proccess mcelog errors */
1650 for (i = 0; i < count; i++)
1651 i7core_mce_output_error(mci, &m[i]);
1652
1653 kfree(m);
1654
1655 /* check memory count errors */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001656 for (i = 0; i < pvt->sockets; i++)
Mauro Carvalho Chehab14d2c082009-09-02 23:52:36 -03001657 if (!pvt->is_registered[i])
Mauro Carvalho Chehabb4e8f0b2009-09-02 23:49:59 -03001658 i7core_udimm_check_mc_ecc_err(mci, i);
1659 else
1660 i7core_rdimm_check_mc_ecc_err(mci, i);
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001661}
1662
1663/*
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001664 * i7core_mce_check_error Replicates mcelog routine to get errors
1665 * This routine simply queues mcelog errors, and
1666 * return. The error itself should be handled later
1667 * by i7core_check_error.
1668 */
1669static int i7core_mce_check_error(void *priv, struct mce *mce)
1670{
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001671 struct mem_ctl_info *mci = priv;
1672 struct i7core_pvt *pvt = mci->pvt_info;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001673 unsigned long flags;
1674
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001675 /*
1676 * Just let mcelog handle it if the error is
1677 * outside the memory controller
1678 */
1679 if (((mce->status & 0xffff) >> 7) != 1)
1680 return 0;
1681
Mauro Carvalho Chehabf237fcf2009-07-15 19:53:24 -03001682 /* Bank 8 registers are the only ones that we know how to handle */
1683 if (mce->bank != 8)
1684 return 0;
1685
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001686 spin_lock_irqsave(&pvt->mce_lock, flags);
1687 if (pvt->mce_count < MCE_LOG_LEN) {
1688 memcpy(&pvt->mce_entry[pvt->mce_count], mce, sizeof(*mce));
1689 pvt->mce_count++;
1690 }
1691 spin_unlock_irqrestore(&pvt->mce_lock, flags);
1692
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001693 /* Handle fatal errors immediately */
1694 if (mce->mcgstatus & 1)
1695 i7core_check_error(mci);
1696
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001697 /* Advice mcelog that the error were handled */
Mauro Carvalho Chehab8a2f1182009-07-15 19:01:08 -03001698 return 1;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001699}
1700
1701/*
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001702 * i7core_probe Probe for ONE instance of device to see if it is
1703 * present.
1704 * return:
1705 * 0 for FOUND a device
1706 * < 0 for error code
1707 */
1708static int __devinit i7core_probe(struct pci_dev *pdev,
1709 const struct pci_device_id *id)
1710{
1711 struct mem_ctl_info *mci;
1712 struct i7core_pvt *pvt;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001713 int num_channels = 0;
1714 int num_csrows = 0;
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -03001715 int csrow = 0;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001716 int dev_idx = id->driver_data;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001717 int rc, i;
1718 u8 sockets;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001719
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001720 if (unlikely(dev_idx >= ARRAY_SIZE(i7core_devs)))
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001721 return -EINVAL;
1722
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001723 /* get the pci devices we want to reserve for our use */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001724 rc = i7core_get_devices();
1725 if (unlikely(rc < 0))
1726 return rc;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001727
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001728 sockets = 1;
1729 for (i = NUM_SOCKETS - 1; i > 0; i--)
1730 if (pci_devs[0].pdev[i]) {
1731 sockets = i + 1;
1732 break;
1733 }
1734
1735 for (i = 0; i < sockets; i++) {
1736 int channels;
1737 int csrows;
1738
1739 /* Check the number of active and not disabled channels */
1740 rc = i7core_get_active_channels(i, &channels, &csrows);
1741 if (unlikely(rc < 0))
1742 goto fail0;
1743
1744 num_channels += channels;
1745 num_csrows += csrows;
1746 }
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001747
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001748 /* allocate a new MC control structure */
1749 mci = edac_mc_alloc(sizeof(*pvt), num_csrows, num_channels, 0);
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001750 if (unlikely(!mci)) {
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001751 rc = -ENOMEM;
1752 goto fail0;
1753 }
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001754
1755 debugf0("MC: " __FILE__ ": %s(): mci = %p\n", __func__, mci);
1756
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001757 mci->dev = &pdev->dev; /* record ptr to the generic device */
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001758 pvt = mci->pvt_info;
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001759 memset(pvt, 0, sizeof(*pvt));
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001760 pvt->sockets = sockets;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001761 mci->mc_idx = 0;
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001762
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001763 /*
1764 * FIXME: how to handle RDDR3 at MCI level? It is possible to have
1765 * Mixed RDDR3/UDDR3 with Nehalem, provided that they are on different
1766 * memory channels
1767 */
1768 mci->mtype_cap = MEM_FLAG_DDR3;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001769 mci->edac_ctl_cap = EDAC_FLAG_NONE;
1770 mci->edac_cap = EDAC_FLAG_NONE;
1771 mci->mod_name = "i7core_edac.c";
1772 mci->mod_ver = I7CORE_REVISION;
1773 mci->ctl_name = i7core_devs[dev_idx].ctl_name;
1774 mci->dev_name = pci_name(pdev);
1775 mci->ctl_page_to_phys = NULL;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001776 mci->mc_driver_sysfs_attributes = i7core_inj_attrs;
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001777 /* Set the function pointer to an actual operation function */
1778 mci->edac_check = i7core_check_error;
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001779
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001780 /* Store pci devices at mci for faster access */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001781 rc = mci_bind_devs(mci);
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001782 if (unlikely(rc < 0))
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001783 goto fail1;
1784
1785 /* Get dimm basic config */
Mauro Carvalho Chehab67166af2009-07-15 06:56:23 -03001786 for (i = 0; i < sockets; i++)
Mauro Carvalho Chehabba6c5c62009-07-15 09:02:32 -03001787 get_dimm_config(mci, &csrow, i);
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001788
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001789 /* add this new MC control structure to EDAC's list of MCs */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001790 if (unlikely(edac_mc_add_mc(mci))) {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001791 debugf0("MC: " __FILE__
1792 ": %s(): failed edac_mc_add_mc()\n", __func__);
1793 /* FIXME: perhaps some code should go here that disables error
1794 * reporting if we just enabled it
1795 */
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001796
1797 rc = -EINVAL;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001798 goto fail1;
1799 }
1800
1801 /* allocating generic PCI control info */
1802 i7core_pci = edac_pci_create_generic_ctl(&pdev->dev, EDAC_MOD_STR);
Mauro Carvalho Chehab41fcb7f2009-06-22 22:48:31 -03001803 if (unlikely(!i7core_pci)) {
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001804 printk(KERN_WARNING
1805 "%s(): Unable to create PCI control\n",
1806 __func__);
1807 printk(KERN_WARNING
1808 "%s(): PCI error report via EDAC not setup\n",
1809 __func__);
1810 }
1811
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001812 /* Default error mask is any memory */
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001813 pvt->inject.channel = 0;
Mauro Carvalho Chehab194a40f2009-06-22 22:48:28 -03001814 pvt->inject.dimm = -1;
1815 pvt->inject.rank = -1;
1816 pvt->inject.bank = -1;
1817 pvt->inject.page = -1;
1818 pvt->inject.col = -1;
1819
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001820 /* Registers on edac_mce in order to receive memory errors */
Mauro Carvalho Chehabc5d34522009-07-17 10:28:15 -03001821 pvt->edac_mce.priv = mci;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001822 pvt->edac_mce.check_error = i7core_mce_check_error;
1823 spin_lock_init(&pvt->mce_lock);
1824
1825 rc = edac_mce_register(&pvt->edac_mce);
Mauro Carvalho Chehabb9905382009-08-05 21:36:35 -03001826 if (unlikely(rc < 0)) {
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001827 debugf0("MC: " __FILE__
1828 ": %s(): failed edac_mce_register()\n", __func__);
1829 goto fail1;
1830 }
1831
Mauro Carvalho Chehabef708b52009-06-22 22:48:30 -03001832 i7core_printk(KERN_INFO, "Driver loaded.\n");
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001833
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001834 return 0;
1835
1836fail1:
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001837 edac_mc_free(mci);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001838
1839fail0:
Mauro Carvalho Chehabb7c76152009-06-22 22:48:30 -03001840 i7core_put_devices();
1841 return rc;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001842}
1843
1844/*
1845 * i7core_remove destructor for one instance of device
1846 *
1847 */
1848static void __devexit i7core_remove(struct pci_dev *pdev)
1849{
1850 struct mem_ctl_info *mci;
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001851 struct i7core_pvt *pvt;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001852
1853 debugf0(__FILE__ ": %s()\n", __func__);
1854
1855 if (i7core_pci)
1856 edac_pci_release_generic_ctl(i7core_pci);
1857
Mauro Carvalho Chehab87d1d272009-06-22 22:48:29 -03001858
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001859 mci = edac_mc_del_mc(&pdev->dev);
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001860 if (!mci)
1861 return;
1862
Mauro Carvalho Chehabd5381642009-07-09 22:06:41 -03001863 /* Unregisters on edac_mce in order to receive memory errors */
1864 pvt = mci->pvt_info;
1865 edac_mce_unregister(&pvt->edac_mce);
1866
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001867 /* retrieve references to resources, and free those resources */
Mauro Carvalho Chehab8f331902009-06-22 22:48:29 -03001868 i7core_put_devices();
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001869
1870 edac_mc_free(mci);
1871}
1872
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001873MODULE_DEVICE_TABLE(pci, i7core_pci_tbl);
1874
1875/*
1876 * i7core_driver pci_driver structure for this module
1877 *
1878 */
1879static struct pci_driver i7core_driver = {
1880 .name = "i7core_edac",
1881 .probe = i7core_probe,
1882 .remove = __devexit_p(i7core_remove),
1883 .id_table = i7core_pci_tbl,
1884};
1885
1886/*
1887 * i7core_init Module entry function
1888 * Try to initialize this module for its devices
1889 */
1890static int __init i7core_init(void)
1891{
1892 int pci_rc;
1893
1894 debugf2("MC: " __FILE__ ": %s()\n", __func__);
1895
1896 /* Ensure that the OPSTATE is set correctly for POLL or NMI */
1897 opstate_init();
1898
1899 pci_rc = pci_register_driver(&i7core_driver);
1900
Mauro Carvalho Chehab3ef288a2009-09-02 23:43:33 -03001901 if (pci_rc >= 0)
1902 return 0;
1903
1904 i7core_printk(KERN_ERR, "Failed to register device with error %d.\n",
1905 pci_rc);
1906
1907 return pci_rc;
Mauro Carvalho Chehaba0c36a12009-06-22 22:41:15 -03001908}
1909
1910/*
1911 * i7core_exit() Module exit function
1912 * Unregister the driver
1913 */
1914static void __exit i7core_exit(void)
1915{
1916 debugf2("MC: " __FILE__ ": %s()\n", __func__);
1917 pci_unregister_driver(&i7core_driver);
1918}
1919
1920module_init(i7core_init);
1921module_exit(i7core_exit);
1922
1923MODULE_LICENSE("GPL");
1924MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
1925MODULE_AUTHOR("Red Hat Inc. (http://www.redhat.com)");
1926MODULE_DESCRIPTION("MC Driver for Intel i7 Core memory controllers - "
1927 I7CORE_REVISION);
1928
1929module_param(edac_op_state, int, 0444);
1930MODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI");