xref: /openbmc/linux/drivers/edac/i7core_edac.c (revision 45bc6098)
112237550SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
252707f91SMauro Carvalho Chehab /* Intel i7 core/Nehalem Memory Controller kernel module
352707f91SMauro Carvalho Chehab  *
4e7bf068aSDavid Sterba  * This driver supports the memory controllers found on the Intel
552707f91SMauro Carvalho Chehab  * processor families i7core, i7core 7xx/8xx, i5core, Xeon 35xx,
652707f91SMauro Carvalho Chehab  * Xeon 55xx and Xeon 56xx also known as Nehalem, Nehalem-EP, Lynnfield
752707f91SMauro Carvalho Chehab  * and Westmere-EP.
8a0c36a1fSMauro Carvalho Chehab  *
952707f91SMauro Carvalho Chehab  * Copyright (c) 2009-2010 by:
1037e59f87SMauro Carvalho Chehab  *	 Mauro Carvalho Chehab
11a0c36a1fSMauro Carvalho Chehab  *
12a0c36a1fSMauro Carvalho Chehab  * Red Hat Inc. https://www.redhat.com
13a0c36a1fSMauro Carvalho Chehab  *
14a0c36a1fSMauro Carvalho Chehab  * Forked and adapted from the i5400_edac driver
15a0c36a1fSMauro Carvalho Chehab  *
16a0c36a1fSMauro Carvalho Chehab  * Based on the following public Intel datasheets:
17a0c36a1fSMauro Carvalho Chehab  * Intel Core i7 Processor Extreme Edition and Intel Core i7 Processor
18a0c36a1fSMauro Carvalho Chehab  * Datasheet, Volume 2:
19a0c36a1fSMauro Carvalho Chehab  *	http://download.intel.com/design/processor/datashts/320835.pdf
20a0c36a1fSMauro Carvalho Chehab  * Intel Xeon Processor 5500 Series Datasheet Volume 2
21a0c36a1fSMauro Carvalho Chehab  *	http://www.intel.com/Assets/PDF/datasheet/321322.pdf
22a0c36a1fSMauro Carvalho Chehab  * also available at:
23a0c36a1fSMauro Carvalho Chehab  * 	http://www.arrownac.com/manufacturers/intel/s/nehalem/5500-datasheet-v2.pdf
24a0c36a1fSMauro Carvalho Chehab  */
25a0c36a1fSMauro Carvalho Chehab 
26a0c36a1fSMauro Carvalho Chehab #include <linux/module.h>
27a0c36a1fSMauro Carvalho Chehab #include <linux/init.h>
28a0c36a1fSMauro Carvalho Chehab #include <linux/pci.h>
29a0c36a1fSMauro Carvalho Chehab #include <linux/pci_ids.h>
30a0c36a1fSMauro Carvalho Chehab #include <linux/slab.h>
313b918c12SRandy Dunlap #include <linux/delay.h>
32535e9c78SNils Carlson #include <linux/dmi.h>
33a0c36a1fSMauro Carvalho Chehab #include <linux/edac.h>
34a0c36a1fSMauro Carvalho Chehab #include <linux/mmzone.h>
35f4742949SMauro Carvalho Chehab #include <linux/smp.h>
364140c542SBorislav Petkov #include <asm/mce.h>
3714d2c083SMauro Carvalho Chehab #include <asm/processor.h>
384fad8098SSedat Dilek #include <asm/div64.h>
39a0c36a1fSMauro Carvalho Chehab 
4078d88e8aSMauro Carvalho Chehab #include "edac_module.h"
41a0c36a1fSMauro Carvalho Chehab 
4218c29002SMauro Carvalho Chehab /* Static vars */
4318c29002SMauro Carvalho Chehab static LIST_HEAD(i7core_edac_list);
4418c29002SMauro Carvalho Chehab static DEFINE_MUTEX(i7core_edac_lock);
4518c29002SMauro Carvalho Chehab static int probed;
4618c29002SMauro Carvalho Chehab 
4754a08ab1SMauro Carvalho Chehab static int use_pci_fixup;
4854a08ab1SMauro Carvalho Chehab module_param(use_pci_fixup, int, 0444);
4954a08ab1SMauro Carvalho Chehab MODULE_PARM_DESC(use_pci_fixup, "Enable PCI fixup to seek for hidden devices");
50a0c36a1fSMauro Carvalho Chehab /*
51f4742949SMauro Carvalho Chehab  * This is used for Nehalem-EP and Nehalem-EX devices, where the non-core
52f4742949SMauro Carvalho Chehab  * registers start at bus 255, and are not reported by BIOS.
53f4742949SMauro Carvalho Chehab  * We currently find devices with only 2 sockets. In order to support more QPI
54f4742949SMauro Carvalho Chehab  * Quick Path Interconnect, just increment this number.
55f4742949SMauro Carvalho Chehab  */
56f4742949SMauro Carvalho Chehab #define MAX_SOCKET_BUSES	2
57f4742949SMauro Carvalho Chehab 
58f4742949SMauro Carvalho Chehab 
59f4742949SMauro Carvalho Chehab /*
60a0c36a1fSMauro Carvalho Chehab  * Alter this version for the module when modifications are made
61a0c36a1fSMauro Carvalho Chehab  */
62152ba394SMichal Marek #define I7CORE_REVISION    " Ver: 1.0.0"
63a0c36a1fSMauro Carvalho Chehab #define EDAC_MOD_STR      "i7core_edac"
64a0c36a1fSMauro Carvalho Chehab 
65a0c36a1fSMauro Carvalho Chehab /*
66a0c36a1fSMauro Carvalho Chehab  * Debug macros
67a0c36a1fSMauro Carvalho Chehab  */
68a0c36a1fSMauro Carvalho Chehab #define i7core_printk(level, fmt, arg...)			\
69a0c36a1fSMauro Carvalho Chehab 	edac_printk(level, "i7core", fmt, ##arg)
70a0c36a1fSMauro Carvalho Chehab 
71a0c36a1fSMauro Carvalho Chehab #define i7core_mc_printk(mci, level, fmt, arg...)		\
72a0c36a1fSMauro Carvalho Chehab 	edac_mc_chipset_printk(mci, level, "i7core", fmt, ##arg)
73a0c36a1fSMauro Carvalho Chehab 
74a0c36a1fSMauro Carvalho Chehab /*
75a0c36a1fSMauro Carvalho Chehab  * i7core Memory Controller Registers
76a0c36a1fSMauro Carvalho Chehab  */
77a0c36a1fSMauro Carvalho Chehab 
78e9bd2e73SMauro Carvalho Chehab 	/* OFFSETS for Device 0 Function 0 */
79e9bd2e73SMauro Carvalho Chehab 
80e9bd2e73SMauro Carvalho Chehab #define MC_CFG_CONTROL	0x90
81e8b6a127SSamuel Gabrielsson   #define MC_CFG_UNLOCK		0x02
82e8b6a127SSamuel Gabrielsson   #define MC_CFG_LOCK		0x00
83e9bd2e73SMauro Carvalho Chehab 
84a0c36a1fSMauro Carvalho Chehab 	/* OFFSETS for Device 3 Function 0 */
85a0c36a1fSMauro Carvalho Chehab 
86a0c36a1fSMauro Carvalho Chehab #define MC_CONTROL	0x48
87a0c36a1fSMauro Carvalho Chehab #define MC_STATUS	0x4c
88a0c36a1fSMauro Carvalho Chehab #define MC_MAX_DOD	0x64
89a0c36a1fSMauro Carvalho Chehab 
90442305b1SMauro Carvalho Chehab /*
9115ed103aSDavid Mackey  * OFFSETS for Device 3 Function 4, as indicated on Xeon 5500 datasheet:
92442305b1SMauro Carvalho Chehab  * http://www.arrownac.com/manufacturers/intel/s/nehalem/5500-datasheet-v2.pdf
93442305b1SMauro Carvalho Chehab  */
94442305b1SMauro Carvalho Chehab 
95442305b1SMauro Carvalho Chehab #define MC_TEST_ERR_RCV1	0x60
96442305b1SMauro Carvalho Chehab   #define DIMM2_COR_ERR(r)			((r) & 0x7fff)
97442305b1SMauro Carvalho Chehab 
98442305b1SMauro Carvalho Chehab #define MC_TEST_ERR_RCV0	0x64
99442305b1SMauro Carvalho Chehab   #define DIMM1_COR_ERR(r)			(((r) >> 16) & 0x7fff)
100442305b1SMauro Carvalho Chehab   #define DIMM0_COR_ERR(r)			((r) & 0x7fff)
101442305b1SMauro Carvalho Chehab 
10215ed103aSDavid Mackey /* OFFSETS for Device 3 Function 2, as indicated on Xeon 5500 datasheet */
103e8b6a127SSamuel Gabrielsson #define MC_SSRCONTROL		0x48
104e8b6a127SSamuel Gabrielsson   #define SSR_MODE_DISABLE	0x00
105e8b6a127SSamuel Gabrielsson   #define SSR_MODE_ENABLE	0x01
106e8b6a127SSamuel Gabrielsson   #define SSR_MODE_MASK		0x03
107e8b6a127SSamuel Gabrielsson 
108e8b6a127SSamuel Gabrielsson #define MC_SCRUB_CONTROL	0x4c
109e8b6a127SSamuel Gabrielsson   #define STARTSCRUB		(1 << 24)
110535e9c78SNils Carlson   #define SCRUBINTERVAL_MASK    0xffffff
111e8b6a127SSamuel Gabrielsson 
112b4e8f0b6SMauro Carvalho Chehab #define MC_COR_ECC_CNT_0	0x80
113b4e8f0b6SMauro Carvalho Chehab #define MC_COR_ECC_CNT_1	0x84
114b4e8f0b6SMauro Carvalho Chehab #define MC_COR_ECC_CNT_2	0x88
115b4e8f0b6SMauro Carvalho Chehab #define MC_COR_ECC_CNT_3	0x8c
116b4e8f0b6SMauro Carvalho Chehab #define MC_COR_ECC_CNT_4	0x90
117b4e8f0b6SMauro Carvalho Chehab #define MC_COR_ECC_CNT_5	0x94
118b4e8f0b6SMauro Carvalho Chehab 
119b4e8f0b6SMauro Carvalho Chehab #define DIMM_TOP_COR_ERR(r)			(((r) >> 16) & 0x7fff)
120b4e8f0b6SMauro Carvalho Chehab #define DIMM_BOT_COR_ERR(r)			((r) & 0x7fff)
121b4e8f0b6SMauro Carvalho Chehab 
122b4e8f0b6SMauro Carvalho Chehab 
123a0c36a1fSMauro Carvalho Chehab 	/* OFFSETS for Devices 4,5 and 6 Function 0 */
124a0c36a1fSMauro Carvalho Chehab 
1250b2b7b7eSMauro Carvalho Chehab #define MC_CHANNEL_DIMM_INIT_PARAMS 0x58
1260b2b7b7eSMauro Carvalho Chehab   #define THREE_DIMMS_PRESENT		(1 << 24)
1270b2b7b7eSMauro Carvalho Chehab   #define SINGLE_QUAD_RANK_PRESENT	(1 << 23)
1280b2b7b7eSMauro Carvalho Chehab   #define QUAD_RANK_PRESENT		(1 << 22)
1290b2b7b7eSMauro Carvalho Chehab   #define REGISTERED_DIMM		(1 << 15)
1300b2b7b7eSMauro Carvalho Chehab 
131f122a892SMauro Carvalho Chehab #define MC_CHANNEL_MAPPER	0x60
132f122a892SMauro Carvalho Chehab   #define RDLCH(r, ch)		((((r) >> (3 + (ch * 6))) & 0x07) - 1)
133f122a892SMauro Carvalho Chehab   #define WRLCH(r, ch)		((((r) >> (ch * 6)) & 0x07) - 1)
134f122a892SMauro Carvalho Chehab 
1350b2b7b7eSMauro Carvalho Chehab #define MC_CHANNEL_RANK_PRESENT 0x7c
1360b2b7b7eSMauro Carvalho Chehab   #define RANK_PRESENT_MASK		0xffff
1370b2b7b7eSMauro Carvalho Chehab 
138a0c36a1fSMauro Carvalho Chehab #define MC_CHANNEL_ADDR_MATCH	0xf0
139194a40feSMauro Carvalho Chehab #define MC_CHANNEL_ERROR_MASK	0xf8
140194a40feSMauro Carvalho Chehab #define MC_CHANNEL_ERROR_INJECT	0xfc
141194a40feSMauro Carvalho Chehab   #define INJECT_ADDR_PARITY	0x10
142194a40feSMauro Carvalho Chehab   #define INJECT_ECC		0x08
143194a40feSMauro Carvalho Chehab   #define MASK_CACHELINE	0x06
144194a40feSMauro Carvalho Chehab   #define MASK_FULL_CACHELINE	0x06
145194a40feSMauro Carvalho Chehab   #define MASK_MSB32_CACHELINE	0x04
146194a40feSMauro Carvalho Chehab   #define MASK_LSB32_CACHELINE	0x02
147194a40feSMauro Carvalho Chehab   #define NO_MASK_CACHELINE	0x00
148194a40feSMauro Carvalho Chehab   #define REPEAT_EN		0x01
149a0c36a1fSMauro Carvalho Chehab 
1500b2b7b7eSMauro Carvalho Chehab 	/* OFFSETS for Devices 4,5 and 6 Function 1 */
151b990538aSMauro Carvalho Chehab 
1520b2b7b7eSMauro Carvalho Chehab #define MC_DOD_CH_DIMM0		0x48
1530b2b7b7eSMauro Carvalho Chehab #define MC_DOD_CH_DIMM1		0x4c
1540b2b7b7eSMauro Carvalho Chehab #define MC_DOD_CH_DIMM2		0x50
1550b2b7b7eSMauro Carvalho Chehab   #define RANKOFFSET_MASK	((1 << 12) | (1 << 11) | (1 << 10))
1560b2b7b7eSMauro Carvalho Chehab   #define RANKOFFSET(x)		((x & RANKOFFSET_MASK) >> 10)
1570b2b7b7eSMauro Carvalho Chehab   #define DIMM_PRESENT_MASK	(1 << 9)
1580b2b7b7eSMauro Carvalho Chehab   #define DIMM_PRESENT(x)	(((x) & DIMM_PRESENT_MASK) >> 9)
159854d3349SMauro Carvalho Chehab   #define MC_DOD_NUMBANK_MASK		((1 << 8) | (1 << 7))
160854d3349SMauro Carvalho Chehab   #define MC_DOD_NUMBANK(x)		(((x) & MC_DOD_NUMBANK_MASK) >> 7)
161854d3349SMauro Carvalho Chehab   #define MC_DOD_NUMRANK_MASK		((1 << 6) | (1 << 5))
162854d3349SMauro Carvalho Chehab   #define MC_DOD_NUMRANK(x)		(((x) & MC_DOD_NUMRANK_MASK) >> 5)
1635566cb7cSMauro Carvalho Chehab   #define MC_DOD_NUMROW_MASK		((1 << 4) | (1 << 3) | (1 << 2))
1645566cb7cSMauro Carvalho Chehab   #define MC_DOD_NUMROW(x)		(((x) & MC_DOD_NUMROW_MASK) >> 2)
165854d3349SMauro Carvalho Chehab   #define MC_DOD_NUMCOL_MASK		3
166854d3349SMauro Carvalho Chehab   #define MC_DOD_NUMCOL(x)		((x) & MC_DOD_NUMCOL_MASK)
1670b2b7b7eSMauro Carvalho Chehab 
168f122a892SMauro Carvalho Chehab #define MC_RANK_PRESENT		0x7c
169f122a892SMauro Carvalho Chehab 
1700b2b7b7eSMauro Carvalho Chehab #define MC_SAG_CH_0	0x80
1710b2b7b7eSMauro Carvalho Chehab #define MC_SAG_CH_1	0x84
1720b2b7b7eSMauro Carvalho Chehab #define MC_SAG_CH_2	0x88
1730b2b7b7eSMauro Carvalho Chehab #define MC_SAG_CH_3	0x8c
1740b2b7b7eSMauro Carvalho Chehab #define MC_SAG_CH_4	0x90
1750b2b7b7eSMauro Carvalho Chehab #define MC_SAG_CH_5	0x94
1760b2b7b7eSMauro Carvalho Chehab #define MC_SAG_CH_6	0x98
1770b2b7b7eSMauro Carvalho Chehab #define MC_SAG_CH_7	0x9c
1780b2b7b7eSMauro Carvalho Chehab 
1790b2b7b7eSMauro Carvalho Chehab #define MC_RIR_LIMIT_CH_0	0x40
1800b2b7b7eSMauro Carvalho Chehab #define MC_RIR_LIMIT_CH_1	0x44
1810b2b7b7eSMauro Carvalho Chehab #define MC_RIR_LIMIT_CH_2	0x48
1820b2b7b7eSMauro Carvalho Chehab #define MC_RIR_LIMIT_CH_3	0x4C
1830b2b7b7eSMauro Carvalho Chehab #define MC_RIR_LIMIT_CH_4	0x50
1840b2b7b7eSMauro Carvalho Chehab #define MC_RIR_LIMIT_CH_5	0x54
1850b2b7b7eSMauro Carvalho Chehab #define MC_RIR_LIMIT_CH_6	0x58
1860b2b7b7eSMauro Carvalho Chehab #define MC_RIR_LIMIT_CH_7	0x5C
1870b2b7b7eSMauro Carvalho Chehab #define MC_RIR_LIMIT_MASK	((1 << 10) - 1)
1880b2b7b7eSMauro Carvalho Chehab 
1890b2b7b7eSMauro Carvalho Chehab #define MC_RIR_WAY_CH		0x80
1900b2b7b7eSMauro Carvalho Chehab   #define MC_RIR_WAY_OFFSET_MASK	(((1 << 14) - 1) & ~0x7)
1910b2b7b7eSMauro Carvalho Chehab   #define MC_RIR_WAY_RANK_MASK		0x7
1920b2b7b7eSMauro Carvalho Chehab 
193a0c36a1fSMauro Carvalho Chehab /*
194a0c36a1fSMauro Carvalho Chehab  * i7core structs
195a0c36a1fSMauro Carvalho Chehab  */
196a0c36a1fSMauro Carvalho Chehab 
197a0c36a1fSMauro Carvalho Chehab #define NUM_CHANS 3
198442305b1SMauro Carvalho Chehab #define MAX_DIMMS 3		/* Max DIMMS per channel */
199442305b1SMauro Carvalho Chehab #define MAX_MCR_FUNC  4
200442305b1SMauro Carvalho Chehab #define MAX_CHAN_FUNC 3
201a0c36a1fSMauro Carvalho Chehab 
202a0c36a1fSMauro Carvalho Chehab struct i7core_info {
203a0c36a1fSMauro Carvalho Chehab 	u32	mc_control;
204a0c36a1fSMauro Carvalho Chehab 	u32	mc_status;
205a0c36a1fSMauro Carvalho Chehab 	u32	max_dod;
206f122a892SMauro Carvalho Chehab 	u32	ch_map;
207a0c36a1fSMauro Carvalho Chehab };
208a0c36a1fSMauro Carvalho Chehab 
209194a40feSMauro Carvalho Chehab 
210194a40feSMauro Carvalho Chehab struct i7core_inject {
211194a40feSMauro Carvalho Chehab 	int	enable;
212194a40feSMauro Carvalho Chehab 
213194a40feSMauro Carvalho Chehab 	u32	section;
214194a40feSMauro Carvalho Chehab 	u32	type;
215194a40feSMauro Carvalho Chehab 	u32	eccmask;
216194a40feSMauro Carvalho Chehab 
217194a40feSMauro Carvalho Chehab 	/* Error address mask */
218194a40feSMauro Carvalho Chehab 	int channel, dimm, rank, bank, page, col;
219194a40feSMauro Carvalho Chehab };
220194a40feSMauro Carvalho Chehab 
2210b2b7b7eSMauro Carvalho Chehab struct i7core_channel {
2220bf09e82SMauro Carvalho Chehab 	bool		is_3dimms_present;
2230bf09e82SMauro Carvalho Chehab 	bool		is_single_4rank;
2240bf09e82SMauro Carvalho Chehab 	bool		has_4rank;
2250b2b7b7eSMauro Carvalho Chehab 	u32		dimms;
2260b2b7b7eSMauro Carvalho Chehab };
2270b2b7b7eSMauro Carvalho Chehab 
2288f331907SMauro Carvalho Chehab struct pci_id_descr {
2298f331907SMauro Carvalho Chehab 	int			dev;
2308f331907SMauro Carvalho Chehab 	int			func;
2318f331907SMauro Carvalho Chehab 	int 			dev_id;
232de06eeefSMauro Carvalho Chehab 	int			optional;
2338f331907SMauro Carvalho Chehab };
2348f331907SMauro Carvalho Chehab 
235bd9e19caSVernon Mauery struct pci_id_table {
2361288c18fSMauro Carvalho Chehab 	const struct pci_id_descr	*descr;
237bd9e19caSVernon Mauery 	int				n_devs;
238bd9e19caSVernon Mauery };
239bd9e19caSVernon Mauery 
240f4742949SMauro Carvalho Chehab struct i7core_dev {
241f4742949SMauro Carvalho Chehab 	struct list_head	list;
242f4742949SMauro Carvalho Chehab 	u8			socket;
243f4742949SMauro Carvalho Chehab 	struct pci_dev		**pdev;
244de06eeefSMauro Carvalho Chehab 	int			n_devs;
245f4742949SMauro Carvalho Chehab 	struct mem_ctl_info	*mci;
246f4742949SMauro Carvalho Chehab };
247f4742949SMauro Carvalho Chehab 
248a0c36a1fSMauro Carvalho Chehab struct i7core_pvt {
249356f0a30SMauro Carvalho Chehab 	struct device *addrmatch_dev, *chancounts_dev;
2505c4cdb5aSMauro Carvalho Chehab 
251f4742949SMauro Carvalho Chehab 	struct pci_dev	*pci_noncore;
252f4742949SMauro Carvalho Chehab 	struct pci_dev	*pci_mcr[MAX_MCR_FUNC + 1];
253f4742949SMauro Carvalho Chehab 	struct pci_dev	*pci_ch[NUM_CHANS][MAX_CHAN_FUNC + 1];
254f4742949SMauro Carvalho Chehab 
255f4742949SMauro Carvalho Chehab 	struct i7core_dev *i7core_dev;
25667166af4SMauro Carvalho Chehab 
257a0c36a1fSMauro Carvalho Chehab 	struct i7core_info	info;
258194a40feSMauro Carvalho Chehab 	struct i7core_inject	inject;
259f4742949SMauro Carvalho Chehab 	struct i7core_channel	channel[NUM_CHANS];
26067166af4SMauro Carvalho Chehab 
261f4742949SMauro Carvalho Chehab 	int		ce_count_available;
262b4e8f0b6SMauro Carvalho Chehab 
263b4e8f0b6SMauro Carvalho Chehab 			/* ECC corrected errors counts per udimm */
264f4742949SMauro Carvalho Chehab 	unsigned long	udimm_ce_count[MAX_DIMMS];
265f4742949SMauro Carvalho Chehab 	int		udimm_last_ce_count[MAX_DIMMS];
266b4e8f0b6SMauro Carvalho Chehab 			/* ECC corrected errors counts per rdimm */
267f4742949SMauro Carvalho Chehab 	unsigned long	rdimm_ce_count[NUM_CHANS][MAX_DIMMS];
268f4742949SMauro Carvalho Chehab 	int		rdimm_last_ce_count[NUM_CHANS][MAX_DIMMS];
269442305b1SMauro Carvalho Chehab 
27027100db0SMauro Carvalho Chehab 	bool		is_registered, enable_scrub;
27114d2c083SMauro Carvalho Chehab 
272535e9c78SNils Carlson 	/* DCLK Frequency used for computing scrub rate */
273535e9c78SNils Carlson 	int			dclk_freq;
274535e9c78SNils Carlson 
275939747bdSMauro Carvalho Chehab 	/* Struct to control EDAC polling */
276939747bdSMauro Carvalho Chehab 	struct edac_pci_ctl_info *i7core_pci;
277a0c36a1fSMauro Carvalho Chehab };
278a0c36a1fSMauro Carvalho Chehab 
2798f331907SMauro Carvalho Chehab #define PCI_DESCR(device, function, device_id)	\
2808f331907SMauro Carvalho Chehab 	.dev = (device),			\
2818f331907SMauro Carvalho Chehab 	.func = (function),			\
2828f331907SMauro Carvalho Chehab 	.dev_id = (device_id)
2838f331907SMauro Carvalho Chehab 
2841288c18fSMauro Carvalho Chehab static const struct pci_id_descr pci_dev_descr_i7core_nehalem[] = {
2858f331907SMauro Carvalho Chehab 		/* Memory controller */
2868f331907SMauro Carvalho Chehab 	{ PCI_DESCR(3, 0, PCI_DEVICE_ID_INTEL_I7_MCR)     },
2878f331907SMauro Carvalho Chehab 	{ PCI_DESCR(3, 1, PCI_DEVICE_ID_INTEL_I7_MC_TAD)  },
288de06eeefSMauro Carvalho Chehab 			/* Exists only for RDIMM */
289de06eeefSMauro Carvalho Chehab 	{ PCI_DESCR(3, 2, PCI_DEVICE_ID_INTEL_I7_MC_RAS), .optional = 1  },
2908f331907SMauro Carvalho Chehab 	{ PCI_DESCR(3, 4, PCI_DEVICE_ID_INTEL_I7_MC_TEST) },
2918f331907SMauro Carvalho Chehab 
2928f331907SMauro Carvalho Chehab 		/* Channel 0 */
2938f331907SMauro Carvalho Chehab 	{ PCI_DESCR(4, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH0_CTRL) },
2948f331907SMauro Carvalho Chehab 	{ PCI_DESCR(4, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH0_ADDR) },
2958f331907SMauro Carvalho Chehab 	{ PCI_DESCR(4, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH0_RANK) },
2968f331907SMauro Carvalho Chehab 	{ PCI_DESCR(4, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH0_TC)   },
2978f331907SMauro Carvalho Chehab 
2988f331907SMauro Carvalho Chehab 		/* Channel 1 */
2998f331907SMauro Carvalho Chehab 	{ PCI_DESCR(5, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH1_CTRL) },
3008f331907SMauro Carvalho Chehab 	{ PCI_DESCR(5, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH1_ADDR) },
3018f331907SMauro Carvalho Chehab 	{ PCI_DESCR(5, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH1_RANK) },
3028f331907SMauro Carvalho Chehab 	{ PCI_DESCR(5, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH1_TC)   },
3038f331907SMauro Carvalho Chehab 
3048f331907SMauro Carvalho Chehab 		/* Channel 2 */
3058f331907SMauro Carvalho Chehab 	{ PCI_DESCR(6, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH2_CTRL) },
3068f331907SMauro Carvalho Chehab 	{ PCI_DESCR(6, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH2_ADDR) },
3078f331907SMauro Carvalho Chehab 	{ PCI_DESCR(6, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH2_RANK) },
3088f331907SMauro Carvalho Chehab 	{ PCI_DESCR(6, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH2_TC)   },
309224e871fSMauro Carvalho Chehab 
310224e871fSMauro Carvalho Chehab 		/* Generic Non-core registers */
311224e871fSMauro Carvalho Chehab 	/*
312224e871fSMauro Carvalho Chehab 	 * This is the PCI device on i7core and on Xeon 35xx (8086:2c41)
313224e871fSMauro Carvalho Chehab 	 * On Xeon 55xx, however, it has a different id (8086:2c40). So,
314224e871fSMauro Carvalho Chehab 	 * the probing code needs to test for the other address in case of
315224e871fSMauro Carvalho Chehab 	 * failure of this one
316224e871fSMauro Carvalho Chehab 	 */
317224e871fSMauro Carvalho Chehab 	{ PCI_DESCR(0, 0, PCI_DEVICE_ID_INTEL_I7_NONCORE)  },
318224e871fSMauro Carvalho Chehab 
319a0c36a1fSMauro Carvalho Chehab };
3208f331907SMauro Carvalho Chehab 
3211288c18fSMauro Carvalho Chehab static const struct pci_id_descr pci_dev_descr_lynnfield[] = {
32252a2e4fcSMauro Carvalho Chehab 	{ PCI_DESCR( 3, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_MCR)         },
32352a2e4fcSMauro Carvalho Chehab 	{ PCI_DESCR( 3, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_TAD)      },
32452a2e4fcSMauro Carvalho Chehab 	{ PCI_DESCR( 3, 4, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_TEST)     },
32552a2e4fcSMauro Carvalho Chehab 
32652a2e4fcSMauro Carvalho Chehab 	{ PCI_DESCR( 4, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_CTRL) },
32752a2e4fcSMauro Carvalho Chehab 	{ PCI_DESCR( 4, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_ADDR) },
32852a2e4fcSMauro Carvalho Chehab 	{ PCI_DESCR( 4, 2, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_RANK) },
32952a2e4fcSMauro Carvalho Chehab 	{ PCI_DESCR( 4, 3, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_TC)   },
33052a2e4fcSMauro Carvalho Chehab 
331508fa179SMauro Carvalho Chehab 	{ PCI_DESCR( 5, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_CTRL) },
332508fa179SMauro Carvalho Chehab 	{ PCI_DESCR( 5, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_ADDR) },
333508fa179SMauro Carvalho Chehab 	{ PCI_DESCR( 5, 2, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_RANK) },
334508fa179SMauro Carvalho Chehab 	{ PCI_DESCR( 5, 3, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_TC)   },
335224e871fSMauro Carvalho Chehab 
336224e871fSMauro Carvalho Chehab 	/*
337224e871fSMauro Carvalho Chehab 	 * This is the PCI device has an alternate address on some
338224e871fSMauro Carvalho Chehab 	 * processors like Core i7 860
339224e871fSMauro Carvalho Chehab 	 */
340224e871fSMauro Carvalho Chehab 	{ PCI_DESCR( 0, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE)     },
34152a2e4fcSMauro Carvalho Chehab };
34252a2e4fcSMauro Carvalho Chehab 
3431288c18fSMauro Carvalho Chehab static const struct pci_id_descr pci_dev_descr_i7core_westmere[] = {
344bd9e19caSVernon Mauery 		/* Memory controller */
345bd9e19caSVernon Mauery 	{ PCI_DESCR(3, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_MCR_REV2)     },
346bd9e19caSVernon Mauery 	{ PCI_DESCR(3, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_TAD_REV2)  },
347bd9e19caSVernon Mauery 			/* Exists only for RDIMM */
348bd9e19caSVernon Mauery 	{ PCI_DESCR(3, 2, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_RAS_REV2), .optional = 1  },
349bd9e19caSVernon Mauery 	{ PCI_DESCR(3, 4, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_TEST_REV2) },
350bd9e19caSVernon Mauery 
351bd9e19caSVernon Mauery 		/* Channel 0 */
352bd9e19caSVernon Mauery 	{ PCI_DESCR(4, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_CTRL_REV2) },
353bd9e19caSVernon Mauery 	{ PCI_DESCR(4, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_ADDR_REV2) },
354bd9e19caSVernon Mauery 	{ PCI_DESCR(4, 2, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_RANK_REV2) },
355bd9e19caSVernon Mauery 	{ PCI_DESCR(4, 3, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_TC_REV2)   },
356bd9e19caSVernon Mauery 
357bd9e19caSVernon Mauery 		/* Channel 1 */
358bd9e19caSVernon Mauery 	{ PCI_DESCR(5, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_CTRL_REV2) },
359bd9e19caSVernon Mauery 	{ PCI_DESCR(5, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_ADDR_REV2) },
360bd9e19caSVernon Mauery 	{ PCI_DESCR(5, 2, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_RANK_REV2) },
361bd9e19caSVernon Mauery 	{ PCI_DESCR(5, 3, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_TC_REV2)   },
362bd9e19caSVernon Mauery 
363bd9e19caSVernon Mauery 		/* Channel 2 */
364bd9e19caSVernon Mauery 	{ PCI_DESCR(6, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH2_CTRL_REV2) },
365bd9e19caSVernon Mauery 	{ PCI_DESCR(6, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH2_ADDR_REV2) },
366bd9e19caSVernon Mauery 	{ PCI_DESCR(6, 2, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH2_RANK_REV2) },
367bd9e19caSVernon Mauery 	{ PCI_DESCR(6, 3, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH2_TC_REV2)   },
368224e871fSMauro Carvalho Chehab 
369224e871fSMauro Carvalho Chehab 		/* Generic Non-core registers */
370224e871fSMauro Carvalho Chehab 	{ PCI_DESCR(0, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE_REV2)  },
371224e871fSMauro Carvalho Chehab 
372bd9e19caSVernon Mauery };
373bd9e19caSVernon Mauery 
3741288c18fSMauro Carvalho Chehab #define PCI_ID_TABLE_ENTRY(A) { .descr=A, .n_devs = ARRAY_SIZE(A) }
3751288c18fSMauro Carvalho Chehab static const struct pci_id_table pci_dev_table[] = {
376bd9e19caSVernon Mauery 	PCI_ID_TABLE_ENTRY(pci_dev_descr_i7core_nehalem),
377bd9e19caSVernon Mauery 	PCI_ID_TABLE_ENTRY(pci_dev_descr_lynnfield),
378bd9e19caSVernon Mauery 	PCI_ID_TABLE_ENTRY(pci_dev_descr_i7core_westmere),
3793c52cc57SMauro Carvalho Chehab 	{0,}			/* 0 terminated list. */
380bd9e19caSVernon Mauery };
381bd9e19caSVernon Mauery 
3828f331907SMauro Carvalho Chehab /*
3838f331907SMauro Carvalho Chehab  *	pci_device_id	table for which devices we are looking for
3848f331907SMauro Carvalho Chehab  */
385ba935f40SJingoo Han static const struct pci_device_id i7core_pci_tbl[] = {
386d1fd4fb6SMauro Carvalho Chehab 	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_X58_HUB_MGMT)},
387f05da2f7SMauro Carvalho Chehab 	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_LYNNFIELD_QPI_LINK0)},
3888f331907SMauro Carvalho Chehab 	{0,}			/* 0 terminated list. */
3898f331907SMauro Carvalho Chehab };
3908f331907SMauro Carvalho Chehab 
391a0c36a1fSMauro Carvalho Chehab /****************************************************************************
39215ed103aSDavid Mackey 			Ancillary status routines
393a0c36a1fSMauro Carvalho Chehab  ****************************************************************************/
394a0c36a1fSMauro Carvalho Chehab 
395a0c36a1fSMauro Carvalho Chehab 	/* MC_CONTROL bits */
396ef708b53SMauro Carvalho Chehab #define CH_ACTIVE(pvt, ch)	((pvt)->info.mc_control & (1 << (8 + ch)))
397ef708b53SMauro Carvalho Chehab #define ECCx8(pvt)		((pvt)->info.mc_control & (1 << 1))
398a0c36a1fSMauro Carvalho Chehab 
399a0c36a1fSMauro Carvalho Chehab 	/* MC_STATUS bits */
40061053fdeSKeith Mannthey #define ECC_ENABLED(pvt)	((pvt)->info.mc_status & (1 << 4))
401ef708b53SMauro Carvalho Chehab #define CH_DISABLED(pvt, ch)	((pvt)->info.mc_status & (1 << ch))
402a0c36a1fSMauro Carvalho Chehab 
403a0c36a1fSMauro Carvalho Chehab 	/* MC_MAX_DOD read functions */
numdimms(u32 dimms)404854d3349SMauro Carvalho Chehab static inline int numdimms(u32 dimms)
405a0c36a1fSMauro Carvalho Chehab {
406854d3349SMauro Carvalho Chehab 	return (dimms & 0x3) + 1;
407a0c36a1fSMauro Carvalho Chehab }
408a0c36a1fSMauro Carvalho Chehab 
numrank(u32 rank)409854d3349SMauro Carvalho Chehab static inline int numrank(u32 rank)
410a0c36a1fSMauro Carvalho Chehab {
411c31d34feSNiklas Söderlund 	static const int ranks[] = { 1, 2, 4, -EINVAL };
412a0c36a1fSMauro Carvalho Chehab 
413854d3349SMauro Carvalho Chehab 	return ranks[rank & 0x3];
414a0c36a1fSMauro Carvalho Chehab }
415a0c36a1fSMauro Carvalho Chehab 
numbank(u32 bank)416854d3349SMauro Carvalho Chehab static inline int numbank(u32 bank)
417a0c36a1fSMauro Carvalho Chehab {
418c31d34feSNiklas Söderlund 	static const int banks[] = { 4, 8, 16, -EINVAL };
419a0c36a1fSMauro Carvalho Chehab 
420854d3349SMauro Carvalho Chehab 	return banks[bank & 0x3];
421a0c36a1fSMauro Carvalho Chehab }
422a0c36a1fSMauro Carvalho Chehab 
numrow(u32 row)423854d3349SMauro Carvalho Chehab static inline int numrow(u32 row)
424a0c36a1fSMauro Carvalho Chehab {
425c31d34feSNiklas Söderlund 	static const int rows[] = {
426a0c36a1fSMauro Carvalho Chehab 		1 << 12, 1 << 13, 1 << 14, 1 << 15,
427a0c36a1fSMauro Carvalho Chehab 		1 << 16, -EINVAL, -EINVAL, -EINVAL,
428a0c36a1fSMauro Carvalho Chehab 	};
429a0c36a1fSMauro Carvalho Chehab 
430854d3349SMauro Carvalho Chehab 	return rows[row & 0x7];
431a0c36a1fSMauro Carvalho Chehab }
432a0c36a1fSMauro Carvalho Chehab 
numcol(u32 col)433854d3349SMauro Carvalho Chehab static inline int numcol(u32 col)
434a0c36a1fSMauro Carvalho Chehab {
435c31d34feSNiklas Söderlund 	static const int cols[] = {
436a0c36a1fSMauro Carvalho Chehab 		1 << 10, 1 << 11, 1 << 12, -EINVAL,
437a0c36a1fSMauro Carvalho Chehab 	};
438854d3349SMauro Carvalho Chehab 	return cols[col & 0x3];
439a0c36a1fSMauro Carvalho Chehab }
440a0c36a1fSMauro Carvalho Chehab 
get_i7core_dev(u8 socket)441f4742949SMauro Carvalho Chehab static struct i7core_dev *get_i7core_dev(u8 socket)
44266607706SMauro Carvalho Chehab {
44366607706SMauro Carvalho Chehab 	struct i7core_dev *i7core_dev;
44466607706SMauro Carvalho Chehab 
44566607706SMauro Carvalho Chehab 	list_for_each_entry(i7core_dev, &i7core_edac_list, list) {
44666607706SMauro Carvalho Chehab 		if (i7core_dev->socket == socket)
44766607706SMauro Carvalho Chehab 			return i7core_dev;
44866607706SMauro Carvalho Chehab 	}
44966607706SMauro Carvalho Chehab 
45066607706SMauro Carvalho Chehab 	return NULL;
45166607706SMauro Carvalho Chehab }
45266607706SMauro Carvalho Chehab 
alloc_i7core_dev(u8 socket,const struct pci_id_table * table)453848b2f7eSHidetoshi Seto static struct i7core_dev *alloc_i7core_dev(u8 socket,
454848b2f7eSHidetoshi Seto 					   const struct pci_id_table *table)
455848b2f7eSHidetoshi Seto {
456848b2f7eSHidetoshi Seto 	struct i7core_dev *i7core_dev;
457848b2f7eSHidetoshi Seto 
458848b2f7eSHidetoshi Seto 	i7core_dev = kzalloc(sizeof(*i7core_dev), GFP_KERNEL);
459848b2f7eSHidetoshi Seto 	if (!i7core_dev)
460848b2f7eSHidetoshi Seto 		return NULL;
461848b2f7eSHidetoshi Seto 
4626396bb22SKees Cook 	i7core_dev->pdev = kcalloc(table->n_devs, sizeof(*i7core_dev->pdev),
463848b2f7eSHidetoshi Seto 				   GFP_KERNEL);
464848b2f7eSHidetoshi Seto 	if (!i7core_dev->pdev) {
465848b2f7eSHidetoshi Seto 		kfree(i7core_dev);
466848b2f7eSHidetoshi Seto 		return NULL;
467848b2f7eSHidetoshi Seto 	}
468848b2f7eSHidetoshi Seto 
469848b2f7eSHidetoshi Seto 	i7core_dev->socket = socket;
470848b2f7eSHidetoshi Seto 	i7core_dev->n_devs = table->n_devs;
471848b2f7eSHidetoshi Seto 	list_add_tail(&i7core_dev->list, &i7core_edac_list);
472848b2f7eSHidetoshi Seto 
473848b2f7eSHidetoshi Seto 	return i7core_dev;
474848b2f7eSHidetoshi Seto }
475848b2f7eSHidetoshi Seto 
free_i7core_dev(struct i7core_dev * i7core_dev)4762aa9be44SHidetoshi Seto static void free_i7core_dev(struct i7core_dev *i7core_dev)
4772aa9be44SHidetoshi Seto {
4782aa9be44SHidetoshi Seto 	list_del(&i7core_dev->list);
4792aa9be44SHidetoshi Seto 	kfree(i7core_dev->pdev);
4802aa9be44SHidetoshi Seto 	kfree(i7core_dev);
4812aa9be44SHidetoshi Seto }
4822aa9be44SHidetoshi Seto 
483a0c36a1fSMauro Carvalho Chehab /****************************************************************************
484a0c36a1fSMauro Carvalho Chehab 			Memory check routines
485a0c36a1fSMauro Carvalho Chehab  ****************************************************************************/
486ef708b53SMauro Carvalho Chehab 
get_dimm_config(struct mem_ctl_info * mci)487084a4fccSMauro Carvalho Chehab static int get_dimm_config(struct mem_ctl_info *mci)
488a0c36a1fSMauro Carvalho Chehab {
489a0c36a1fSMauro Carvalho Chehab 	struct i7core_pvt *pvt = mci->pvt_info;
490854d3349SMauro Carvalho Chehab 	struct pci_dev *pdev;
491ba6c5c62SMauro Carvalho Chehab 	int i, j;
4921c6fed80SMauro Carvalho Chehab 	enum edac_type mode;
493854d3349SMauro Carvalho Chehab 	enum mem_type mtype;
494084a4fccSMauro Carvalho Chehab 	struct dimm_info *dimm;
495a0c36a1fSMauro Carvalho Chehab 
496854d3349SMauro Carvalho Chehab 	/* Get data from the MC register, function 0 */
497f4742949SMauro Carvalho Chehab 	pdev = pvt->pci_mcr[0];
4987dd6953cSMauro Carvalho Chehab 	if (!pdev)
4998f331907SMauro Carvalho Chehab 		return -ENODEV;
5008f331907SMauro Carvalho Chehab 
501f122a892SMauro Carvalho Chehab 	/* Device 3 function 0 reads */
5027dd6953cSMauro Carvalho Chehab 	pci_read_config_dword(pdev, MC_CONTROL, &pvt->info.mc_control);
5037dd6953cSMauro Carvalho Chehab 	pci_read_config_dword(pdev, MC_STATUS, &pvt->info.mc_status);
5047dd6953cSMauro Carvalho Chehab 	pci_read_config_dword(pdev, MC_MAX_DOD, &pvt->info.max_dod);
5057dd6953cSMauro Carvalho Chehab 	pci_read_config_dword(pdev, MC_CHANNEL_MAPPER, &pvt->info.ch_map);
506f122a892SMauro Carvalho Chehab 
507956b9ba1SJoe Perches 	edac_dbg(0, "QPI %d control=0x%08x status=0x%08x dod=0x%08x map=0x%08x\n",
508956b9ba1SJoe Perches 		 pvt->i7core_dev->socket, pvt->info.mc_control,
509956b9ba1SJoe Perches 		 pvt->info.mc_status, pvt->info.max_dod, pvt->info.ch_map);
510a0c36a1fSMauro Carvalho Chehab 
5111c6fed80SMauro Carvalho Chehab 	if (ECC_ENABLED(pvt)) {
512956b9ba1SJoe Perches 		edac_dbg(0, "ECC enabled with x%d SDCC\n", ECCx8(pvt) ? 8 : 4);
5131c6fed80SMauro Carvalho Chehab 		if (ECCx8(pvt))
5141c6fed80SMauro Carvalho Chehab 			mode = EDAC_S8ECD8ED;
515a0c36a1fSMauro Carvalho Chehab 		else
5161c6fed80SMauro Carvalho Chehab 			mode = EDAC_S4ECD4ED;
5171c6fed80SMauro Carvalho Chehab 	} else {
518956b9ba1SJoe Perches 		edac_dbg(0, "ECC disabled\n");
5191c6fed80SMauro Carvalho Chehab 		mode = EDAC_NONE;
5201c6fed80SMauro Carvalho Chehab 	}
521a0c36a1fSMauro Carvalho Chehab 
522a0c36a1fSMauro Carvalho Chehab 	/* FIXME: need to handle the error codes */
523956b9ba1SJoe Perches 	edac_dbg(0, "DOD Max limits: DIMMS: %d, %d-ranked, %d-banked x%x x 0x%x\n",
524854d3349SMauro Carvalho Chehab 		 numdimms(pvt->info.max_dod),
525854d3349SMauro Carvalho Chehab 		 numrank(pvt->info.max_dod >> 2),
526276b824cSMauro Carvalho Chehab 		 numbank(pvt->info.max_dod >> 4),
527854d3349SMauro Carvalho Chehab 		 numrow(pvt->info.max_dod >> 6),
528854d3349SMauro Carvalho Chehab 		 numcol(pvt->info.max_dod >> 9));
529a0c36a1fSMauro Carvalho Chehab 
5300b2b7b7eSMauro Carvalho Chehab 	for (i = 0; i < NUM_CHANS; i++) {
531854d3349SMauro Carvalho Chehab 		u32 data, dimm_dod[3], value[8];
5320b2b7b7eSMauro Carvalho Chehab 
53352a2e4fcSMauro Carvalho Chehab 		if (!pvt->pci_ch[i][0])
53452a2e4fcSMauro Carvalho Chehab 			continue;
53552a2e4fcSMauro Carvalho Chehab 
5360b2b7b7eSMauro Carvalho Chehab 		if (!CH_ACTIVE(pvt, i)) {
537956b9ba1SJoe Perches 			edac_dbg(0, "Channel %i is not active\n", i);
5380b2b7b7eSMauro Carvalho Chehab 			continue;
5390b2b7b7eSMauro Carvalho Chehab 		}
5400b2b7b7eSMauro Carvalho Chehab 		if (CH_DISABLED(pvt, i)) {
541956b9ba1SJoe Perches 			edac_dbg(0, "Channel %i is disabled\n", i);
5420b2b7b7eSMauro Carvalho Chehab 			continue;
5430b2b7b7eSMauro Carvalho Chehab 		}
5440b2b7b7eSMauro Carvalho Chehab 
545f122a892SMauro Carvalho Chehab 		/* Devices 4-6 function 0 */
546f4742949SMauro Carvalho Chehab 		pci_read_config_dword(pvt->pci_ch[i][0],
5470b2b7b7eSMauro Carvalho Chehab 				MC_CHANNEL_DIMM_INIT_PARAMS, &data);
5480b2b7b7eSMauro Carvalho Chehab 
5490bf09e82SMauro Carvalho Chehab 
5500bf09e82SMauro Carvalho Chehab 		if (data & THREE_DIMMS_PRESENT)
5510bf09e82SMauro Carvalho Chehab 			pvt->channel[i].is_3dimms_present = true;
5520bf09e82SMauro Carvalho Chehab 
5530bf09e82SMauro Carvalho Chehab 		if (data & SINGLE_QUAD_RANK_PRESENT)
5540bf09e82SMauro Carvalho Chehab 			pvt->channel[i].is_single_4rank = true;
5550bf09e82SMauro Carvalho Chehab 
5560bf09e82SMauro Carvalho Chehab 		if (data & QUAD_RANK_PRESENT)
5570bf09e82SMauro Carvalho Chehab 			pvt->channel[i].has_4rank = true;
5580b2b7b7eSMauro Carvalho Chehab 
559854d3349SMauro Carvalho Chehab 		if (data & REGISTERED_DIMM)
560854d3349SMauro Carvalho Chehab 			mtype = MEM_RDDR3;
56114d2c083SMauro Carvalho Chehab 		else
562854d3349SMauro Carvalho Chehab 			mtype = MEM_DDR3;
563854d3349SMauro Carvalho Chehab 
564854d3349SMauro Carvalho Chehab 		/* Devices 4-6 function 1 */
565f4742949SMauro Carvalho Chehab 		pci_read_config_dword(pvt->pci_ch[i][1],
566854d3349SMauro Carvalho Chehab 				MC_DOD_CH_DIMM0, &dimm_dod[0]);
567f4742949SMauro Carvalho Chehab 		pci_read_config_dword(pvt->pci_ch[i][1],
568854d3349SMauro Carvalho Chehab 				MC_DOD_CH_DIMM1, &dimm_dod[1]);
569f4742949SMauro Carvalho Chehab 		pci_read_config_dword(pvt->pci_ch[i][1],
570854d3349SMauro Carvalho Chehab 				MC_DOD_CH_DIMM2, &dimm_dod[2]);
5710b2b7b7eSMauro Carvalho Chehab 
572956b9ba1SJoe Perches 		edac_dbg(0, "Ch%d phy rd%d, wr%d (0x%08x): %s%s%s%cDIMMs\n",
5731c6fed80SMauro Carvalho Chehab 			 i,
5741c6fed80SMauro Carvalho Chehab 			 RDLCH(pvt->info.ch_map, i), WRLCH(pvt->info.ch_map, i),
5751c6fed80SMauro Carvalho Chehab 			 data,
5760bf09e82SMauro Carvalho Chehab 			 pvt->channel[i].is_3dimms_present ? "3DIMMS " : "",
5770bf09e82SMauro Carvalho Chehab 			 pvt->channel[i].is_3dimms_present ? "SINGLE_4R " : "",
5780bf09e82SMauro Carvalho Chehab 			 pvt->channel[i].has_4rank ? "HAS_4R " : "",
579854d3349SMauro Carvalho Chehab 			 (data & REGISTERED_DIMM) ? 'R' : 'U');
580854d3349SMauro Carvalho Chehab 
581854d3349SMauro Carvalho Chehab 		for (j = 0; j < 3; j++) {
582854d3349SMauro Carvalho Chehab 			u32 banks, ranks, rows, cols;
5835566cb7cSMauro Carvalho Chehab 			u32 size, npages;
584854d3349SMauro Carvalho Chehab 
585854d3349SMauro Carvalho Chehab 			if (!DIMM_PRESENT(dimm_dod[j]))
586854d3349SMauro Carvalho Chehab 				continue;
587854d3349SMauro Carvalho Chehab 
588bc9ad9e4SRobert Richter 			dimm = edac_get_dimm(mci, i, j, 0);
589854d3349SMauro Carvalho Chehab 			banks = numbank(MC_DOD_NUMBANK(dimm_dod[j]));
590854d3349SMauro Carvalho Chehab 			ranks = numrank(MC_DOD_NUMRANK(dimm_dod[j]));
591854d3349SMauro Carvalho Chehab 			rows = numrow(MC_DOD_NUMROW(dimm_dod[j]));
592854d3349SMauro Carvalho Chehab 			cols = numcol(MC_DOD_NUMCOL(dimm_dod[j]));
593854d3349SMauro Carvalho Chehab 
5945566cb7cSMauro Carvalho Chehab 			/* DDR3 has 8 I/O banks */
5955566cb7cSMauro Carvalho Chehab 			size = (rows * cols * banks * ranks) >> (20 - 3);
5965566cb7cSMauro Carvalho Chehab 
5976f6da136SQiuxu Zhuo 			edac_dbg(0, "\tdimm %d %d MiB offset: %x, bank: %d, rank: %d, row: %#x, col: %#x\n",
59817cb7b0cSMauro Carvalho Chehab 				 j, size,
599854d3349SMauro Carvalho Chehab 				 RANKOFFSET(dimm_dod[j]),
600854d3349SMauro Carvalho Chehab 				 banks, ranks, rows, cols);
601854d3349SMauro Carvalho Chehab 
602e9144601SMauro Carvalho Chehab 			npages = MiB_TO_PAGES(size);
6035566cb7cSMauro Carvalho Chehab 
604a895bf8bSMauro Carvalho Chehab 			dimm->nr_pages = npages;
605b4e8f0b6SMauro Carvalho Chehab 
606854d3349SMauro Carvalho Chehab 			switch (banks) {
607854d3349SMauro Carvalho Chehab 			case 4:
608084a4fccSMauro Carvalho Chehab 				dimm->dtype = DEV_X4;
609854d3349SMauro Carvalho Chehab 				break;
610854d3349SMauro Carvalho Chehab 			case 8:
611084a4fccSMauro Carvalho Chehab 				dimm->dtype = DEV_X8;
612854d3349SMauro Carvalho Chehab 				break;
613854d3349SMauro Carvalho Chehab 			case 16:
614084a4fccSMauro Carvalho Chehab 				dimm->dtype = DEV_X16;
615854d3349SMauro Carvalho Chehab 				break;
616854d3349SMauro Carvalho Chehab 			default:
617084a4fccSMauro Carvalho Chehab 				dimm->dtype = DEV_UNKNOWN;
618854d3349SMauro Carvalho Chehab 			}
619854d3349SMauro Carvalho Chehab 
620084a4fccSMauro Carvalho Chehab 			snprintf(dimm->label, sizeof(dimm->label),
621767ba4a5SMauro Carvalho Chehab 				 "CPU#%uChannel#%u_DIMM#%u",
622767ba4a5SMauro Carvalho Chehab 				 pvt->i7core_dev->socket, i, j);
623084a4fccSMauro Carvalho Chehab 			dimm->grain = 8;
624084a4fccSMauro Carvalho Chehab 			dimm->edac_mode = mode;
625084a4fccSMauro Carvalho Chehab 			dimm->mtype = mtype;
626854d3349SMauro Carvalho Chehab 		}
6277dd6953cSMauro Carvalho Chehab 
6287dd6953cSMauro Carvalho Chehab 		pci_read_config_dword(pdev, MC_SAG_CH_0, &value[0]);
6297dd6953cSMauro Carvalho Chehab 		pci_read_config_dword(pdev, MC_SAG_CH_1, &value[1]);
6307dd6953cSMauro Carvalho Chehab 		pci_read_config_dword(pdev, MC_SAG_CH_2, &value[2]);
6317dd6953cSMauro Carvalho Chehab 		pci_read_config_dword(pdev, MC_SAG_CH_3, &value[3]);
6327dd6953cSMauro Carvalho Chehab 		pci_read_config_dword(pdev, MC_SAG_CH_4, &value[4]);
6337dd6953cSMauro Carvalho Chehab 		pci_read_config_dword(pdev, MC_SAG_CH_5, &value[5]);
6347dd6953cSMauro Carvalho Chehab 		pci_read_config_dword(pdev, MC_SAG_CH_6, &value[6]);
6357dd6953cSMauro Carvalho Chehab 		pci_read_config_dword(pdev, MC_SAG_CH_7, &value[7]);
636956b9ba1SJoe Perches 		edac_dbg(1, "\t[%i] DIVBY3\tREMOVED\tOFFSET\n", i);
6377dd6953cSMauro Carvalho Chehab 		for (j = 0; j < 8; j++)
638956b9ba1SJoe Perches 			edac_dbg(1, "\t\t%#x\t%#x\t%#x\n",
6397dd6953cSMauro Carvalho Chehab 				 (value[j] >> 27) & 0x1,
6407dd6953cSMauro Carvalho Chehab 				 (value[j] >> 24) & 0x7,
64180b8ce89SDavid Sterba 				 (value[j] & ((1 << 24) - 1)));
6420b2b7b7eSMauro Carvalho Chehab 	}
6430b2b7b7eSMauro Carvalho Chehab 
644a0c36a1fSMauro Carvalho Chehab 	return 0;
645a0c36a1fSMauro Carvalho Chehab }
646a0c36a1fSMauro Carvalho Chehab 
647a0c36a1fSMauro Carvalho Chehab /****************************************************************************
648194a40feSMauro Carvalho Chehab 			Error insertion routines
649194a40feSMauro Carvalho Chehab  ****************************************************************************/
650194a40feSMauro Carvalho Chehab 
6515c4cdb5aSMauro Carvalho Chehab #define to_mci(k) container_of(k, struct mem_ctl_info, dev)
6525c4cdb5aSMauro Carvalho Chehab 
653194a40feSMauro Carvalho Chehab /* The i7core has independent error injection features per channel.
654194a40feSMauro Carvalho Chehab    However, to have a simpler code, we don't allow enabling error injection
655194a40feSMauro Carvalho Chehab    on more than one channel.
656194a40feSMauro Carvalho Chehab    Also, since a change at an inject parameter will be applied only at enable,
657194a40feSMauro Carvalho Chehab    we're disabling error injection on all write calls to the sysfs nodes that
658194a40feSMauro Carvalho Chehab    controls the error code injection.
659194a40feSMauro Carvalho Chehab  */
disable_inject(const struct mem_ctl_info * mci)6601288c18fSMauro Carvalho Chehab static int disable_inject(const struct mem_ctl_info *mci)
661194a40feSMauro Carvalho Chehab {
662194a40feSMauro Carvalho Chehab 	struct i7core_pvt *pvt = mci->pvt_info;
663194a40feSMauro Carvalho Chehab 
664194a40feSMauro Carvalho Chehab 	pvt->inject.enable = 0;
665194a40feSMauro Carvalho Chehab 
666f4742949SMauro Carvalho Chehab 	if (!pvt->pci_ch[pvt->inject.channel][0])
6678f331907SMauro Carvalho Chehab 		return -ENODEV;
6688f331907SMauro Carvalho Chehab 
669f4742949SMauro Carvalho Chehab 	pci_write_config_dword(pvt->pci_ch[pvt->inject.channel][0],
6704157d9f5SMauro Carvalho Chehab 				MC_CHANNEL_ERROR_INJECT, 0);
6718f331907SMauro Carvalho Chehab 
6728f331907SMauro Carvalho Chehab 	return 0;
673194a40feSMauro Carvalho Chehab }
674194a40feSMauro Carvalho Chehab 
675194a40feSMauro Carvalho Chehab /*
676194a40feSMauro Carvalho Chehab  * i7core inject inject.section
677194a40feSMauro Carvalho Chehab  *
678194a40feSMauro Carvalho Chehab  *	accept and store error injection inject.section value
679194a40feSMauro Carvalho Chehab  *	bit 0 - refers to the lower 32-byte half cacheline
680194a40feSMauro Carvalho Chehab  *	bit 1 - refers to the upper 32-byte half cacheline
681194a40feSMauro Carvalho Chehab  */
i7core_inject_section_store(struct device * dev,struct device_attribute * mattr,const char * data,size_t count)6825c4cdb5aSMauro Carvalho Chehab static ssize_t i7core_inject_section_store(struct device *dev,
6835c4cdb5aSMauro Carvalho Chehab 					   struct device_attribute *mattr,
684194a40feSMauro Carvalho Chehab 					   const char *data, size_t count)
685194a40feSMauro Carvalho Chehab {
6865c4cdb5aSMauro Carvalho Chehab 	struct mem_ctl_info *mci = to_mci(dev);
687194a40feSMauro Carvalho Chehab 	struct i7core_pvt *pvt = mci->pvt_info;
688194a40feSMauro Carvalho Chehab 	unsigned long value;
689194a40feSMauro Carvalho Chehab 	int rc;
690194a40feSMauro Carvalho Chehab 
691194a40feSMauro Carvalho Chehab 	if (pvt->inject.enable)
692194a40feSMauro Carvalho Chehab 		disable_inject(mci);
693194a40feSMauro Carvalho Chehab 
694c7f62fc8SJingoo Han 	rc = kstrtoul(data, 10, &value);
695194a40feSMauro Carvalho Chehab 	if ((rc < 0) || (value > 3))
6962068def5SMauro Carvalho Chehab 		return -EIO;
697194a40feSMauro Carvalho Chehab 
698194a40feSMauro Carvalho Chehab 	pvt->inject.section = (u32) value;
699194a40feSMauro Carvalho Chehab 	return count;
700194a40feSMauro Carvalho Chehab }
701194a40feSMauro Carvalho Chehab 
i7core_inject_section_show(struct device * dev,struct device_attribute * mattr,char * data)7025c4cdb5aSMauro Carvalho Chehab static ssize_t i7core_inject_section_show(struct device *dev,
7035c4cdb5aSMauro Carvalho Chehab 					  struct device_attribute *mattr,
704194a40feSMauro Carvalho Chehab 					  char *data)
705194a40feSMauro Carvalho Chehab {
7065c4cdb5aSMauro Carvalho Chehab 	struct mem_ctl_info *mci = to_mci(dev);
707194a40feSMauro Carvalho Chehab 	struct i7core_pvt *pvt = mci->pvt_info;
708194a40feSMauro Carvalho Chehab 	return sprintf(data, "0x%08x\n", pvt->inject.section);
709194a40feSMauro Carvalho Chehab }
710194a40feSMauro Carvalho Chehab 
711194a40feSMauro Carvalho Chehab /*
712194a40feSMauro Carvalho Chehab  * i7core inject.type
713194a40feSMauro Carvalho Chehab  *
714194a40feSMauro Carvalho Chehab  *	accept and store error injection inject.section value
715194a40feSMauro Carvalho Chehab  *	bit 0 - repeat enable - Enable error repetition
716194a40feSMauro Carvalho Chehab  *	bit 1 - inject ECC error
717194a40feSMauro Carvalho Chehab  *	bit 2 - inject parity error
718194a40feSMauro Carvalho Chehab  */
i7core_inject_type_store(struct device * dev,struct device_attribute * mattr,const char * data,size_t count)7195c4cdb5aSMauro Carvalho Chehab static ssize_t i7core_inject_type_store(struct device *dev,
7205c4cdb5aSMauro Carvalho Chehab 					struct device_attribute *mattr,
721194a40feSMauro Carvalho Chehab 					const char *data, size_t count)
722194a40feSMauro Carvalho Chehab {
7235c4cdb5aSMauro Carvalho Chehab 	struct mem_ctl_info *mci = to_mci(dev);
724194a40feSMauro Carvalho Chehab 	struct i7core_pvt *pvt = mci->pvt_info;
725194a40feSMauro Carvalho Chehab 	unsigned long value;
726194a40feSMauro Carvalho Chehab 	int rc;
727194a40feSMauro Carvalho Chehab 
728194a40feSMauro Carvalho Chehab 	if (pvt->inject.enable)
729194a40feSMauro Carvalho Chehab 		disable_inject(mci);
730194a40feSMauro Carvalho Chehab 
731c7f62fc8SJingoo Han 	rc = kstrtoul(data, 10, &value);
732194a40feSMauro Carvalho Chehab 	if ((rc < 0) || (value > 7))
7332068def5SMauro Carvalho Chehab 		return -EIO;
734194a40feSMauro Carvalho Chehab 
735194a40feSMauro Carvalho Chehab 	pvt->inject.type = (u32) value;
736194a40feSMauro Carvalho Chehab 	return count;
737194a40feSMauro Carvalho Chehab }
738194a40feSMauro Carvalho Chehab 
i7core_inject_type_show(struct device * dev,struct device_attribute * mattr,char * data)7395c4cdb5aSMauro Carvalho Chehab static ssize_t i7core_inject_type_show(struct device *dev,
7405c4cdb5aSMauro Carvalho Chehab 				       struct device_attribute *mattr,
741194a40feSMauro Carvalho Chehab 				       char *data)
742194a40feSMauro Carvalho Chehab {
7435c4cdb5aSMauro Carvalho Chehab 	struct mem_ctl_info *mci = to_mci(dev);
744194a40feSMauro Carvalho Chehab 	struct i7core_pvt *pvt = mci->pvt_info;
7455c4cdb5aSMauro Carvalho Chehab 
746194a40feSMauro Carvalho Chehab 	return sprintf(data, "0x%08x\n", pvt->inject.type);
747194a40feSMauro Carvalho Chehab }
748194a40feSMauro Carvalho Chehab 
749194a40feSMauro Carvalho Chehab /*
750194a40feSMauro Carvalho Chehab  * i7core_inject_inject.eccmask_store
751194a40feSMauro Carvalho Chehab  *
752194a40feSMauro Carvalho Chehab  * The type of error (UE/CE) will depend on the inject.eccmask value:
753194a40feSMauro Carvalho Chehab  *   Any bits set to a 1 will flip the corresponding ECC bit
754194a40feSMauro Carvalho Chehab  *   Correctable errors can be injected by flipping 1 bit or the bits within
755194a40feSMauro Carvalho Chehab  *   a symbol pair (2 consecutive aligned 8-bit pairs - i.e. 7:0 and 15:8 or
756194a40feSMauro Carvalho Chehab  *   23:16 and 31:24). Flipping bits in two symbol pairs will cause an
757194a40feSMauro Carvalho Chehab  *   uncorrectable error to be injected.
758194a40feSMauro Carvalho Chehab  */
i7core_inject_eccmask_store(struct device * dev,struct device_attribute * mattr,const char * data,size_t count)7595c4cdb5aSMauro Carvalho Chehab static ssize_t i7core_inject_eccmask_store(struct device *dev,
7605c4cdb5aSMauro Carvalho Chehab 					   struct device_attribute *mattr,
761194a40feSMauro Carvalho Chehab 					   const char *data, size_t count)
762194a40feSMauro Carvalho Chehab {
7635c4cdb5aSMauro Carvalho Chehab 	struct mem_ctl_info *mci = to_mci(dev);
764194a40feSMauro Carvalho Chehab 	struct i7core_pvt *pvt = mci->pvt_info;
765194a40feSMauro Carvalho Chehab 	unsigned long value;
766194a40feSMauro Carvalho Chehab 	int rc;
767194a40feSMauro Carvalho Chehab 
768194a40feSMauro Carvalho Chehab 	if (pvt->inject.enable)
769194a40feSMauro Carvalho Chehab 		disable_inject(mci);
770194a40feSMauro Carvalho Chehab 
771c7f62fc8SJingoo Han 	rc = kstrtoul(data, 10, &value);
772194a40feSMauro Carvalho Chehab 	if (rc < 0)
7732068def5SMauro Carvalho Chehab 		return -EIO;
774194a40feSMauro Carvalho Chehab 
775194a40feSMauro Carvalho Chehab 	pvt->inject.eccmask = (u32) value;
776194a40feSMauro Carvalho Chehab 	return count;
777194a40feSMauro Carvalho Chehab }
778194a40feSMauro Carvalho Chehab 
i7core_inject_eccmask_show(struct device * dev,struct device_attribute * mattr,char * data)7795c4cdb5aSMauro Carvalho Chehab static ssize_t i7core_inject_eccmask_show(struct device *dev,
7805c4cdb5aSMauro Carvalho Chehab 					  struct device_attribute *mattr,
781194a40feSMauro Carvalho Chehab 					  char *data)
782194a40feSMauro Carvalho Chehab {
7835c4cdb5aSMauro Carvalho Chehab 	struct mem_ctl_info *mci = to_mci(dev);
784194a40feSMauro Carvalho Chehab 	struct i7core_pvt *pvt = mci->pvt_info;
7855c4cdb5aSMauro Carvalho Chehab 
786194a40feSMauro Carvalho Chehab 	return sprintf(data, "0x%08x\n", pvt->inject.eccmask);
787194a40feSMauro Carvalho Chehab }
788194a40feSMauro Carvalho Chehab 
789194a40feSMauro Carvalho Chehab /*
790194a40feSMauro Carvalho Chehab  * i7core_addrmatch
791194a40feSMauro Carvalho Chehab  *
792194a40feSMauro Carvalho Chehab  * The type of error (UE/CE) will depend on the inject.eccmask value:
793194a40feSMauro Carvalho Chehab  *   Any bits set to a 1 will flip the corresponding ECC bit
794194a40feSMauro Carvalho Chehab  *   Correctable errors can be injected by flipping 1 bit or the bits within
795194a40feSMauro Carvalho Chehab  *   a symbol pair (2 consecutive aligned 8-bit pairs - i.e. 7:0 and 15:8 or
796194a40feSMauro Carvalho Chehab  *   23:16 and 31:24). Flipping bits in two symbol pairs will cause an
797194a40feSMauro Carvalho Chehab  *   uncorrectable error to be injected.
798194a40feSMauro Carvalho Chehab  */
799194a40feSMauro Carvalho Chehab 
800a5538e53SMauro Carvalho Chehab #define DECLARE_ADDR_MATCH(param, limit)			\
801a5538e53SMauro Carvalho Chehab static ssize_t i7core_inject_store_##param(			\
8025c4cdb5aSMauro Carvalho Chehab 	struct device *dev,					\
8035c4cdb5aSMauro Carvalho Chehab 	struct device_attribute *mattr,				\
804a5538e53SMauro Carvalho Chehab 	const char *data, size_t count)				\
805a5538e53SMauro Carvalho Chehab {								\
80642709efbSPrarit Bhargava 	struct mem_ctl_info *mci = dev_get_drvdata(dev);	\
807cc301b3aSMauro Carvalho Chehab 	struct i7core_pvt *pvt;					\
808a5538e53SMauro Carvalho Chehab 	long value;						\
809a5538e53SMauro Carvalho Chehab 	int rc;							\
810a5538e53SMauro Carvalho Chehab 								\
811956b9ba1SJoe Perches 	edac_dbg(1, "\n");					\
812cc301b3aSMauro Carvalho Chehab 	pvt = mci->pvt_info;					\
813cc301b3aSMauro Carvalho Chehab 								\
814a5538e53SMauro Carvalho Chehab 	if (pvt->inject.enable)					\
815a5538e53SMauro Carvalho Chehab 		disable_inject(mci);				\
816a5538e53SMauro Carvalho Chehab 								\
8174f87fad1SMauro Carvalho Chehab 	if (!strcasecmp(data, "any") || !strcasecmp(data, "any\n"))\
818a5538e53SMauro Carvalho Chehab 		value = -1;					\
819a5538e53SMauro Carvalho Chehab 	else {							\
820c7f62fc8SJingoo Han 		rc = kstrtoul(data, 10, &value);		\
821a5538e53SMauro Carvalho Chehab 		if ((rc < 0) || (value >= limit))		\
822a5538e53SMauro Carvalho Chehab 			return -EIO;				\
823a5538e53SMauro Carvalho Chehab 	}							\
824a5538e53SMauro Carvalho Chehab 								\
825a5538e53SMauro Carvalho Chehab 	pvt->inject.param = value;				\
826a5538e53SMauro Carvalho Chehab 								\
827a5538e53SMauro Carvalho Chehab 	return count;						\
828a5538e53SMauro Carvalho Chehab }								\
829a5538e53SMauro Carvalho Chehab 								\
830a5538e53SMauro Carvalho Chehab static ssize_t i7core_inject_show_##param(			\
8315c4cdb5aSMauro Carvalho Chehab 	struct device *dev,					\
8325c4cdb5aSMauro Carvalho Chehab 	struct device_attribute *mattr,				\
833a5538e53SMauro Carvalho Chehab 	char *data)						\
834a5538e53SMauro Carvalho Chehab {								\
83542709efbSPrarit Bhargava 	struct mem_ctl_info *mci = dev_get_drvdata(dev);	\
836cc301b3aSMauro Carvalho Chehab 	struct i7core_pvt *pvt;					\
837cc301b3aSMauro Carvalho Chehab 								\
838cc301b3aSMauro Carvalho Chehab 	pvt = mci->pvt_info;					\
839956b9ba1SJoe Perches 	edac_dbg(1, "pvt=%p\n", pvt);				\
840a5538e53SMauro Carvalho Chehab 	if (pvt->inject.param < 0)				\
841a5538e53SMauro Carvalho Chehab 		return sprintf(data, "any\n");			\
842a5538e53SMauro Carvalho Chehab 	else							\
843a5538e53SMauro Carvalho Chehab 		return sprintf(data, "%d\n", pvt->inject.param);\
844194a40feSMauro Carvalho Chehab }
845194a40feSMauro Carvalho Chehab 
846a5538e53SMauro Carvalho Chehab #define ATTR_ADDR_MATCH(param)					\
8475c4cdb5aSMauro Carvalho Chehab 	static DEVICE_ATTR(param, S_IRUGO | S_IWUSR,		\
8485c4cdb5aSMauro Carvalho Chehab 		    i7core_inject_show_##param,			\
8495c4cdb5aSMauro Carvalho Chehab 		    i7core_inject_store_##param)
850194a40feSMauro Carvalho Chehab 
851a5538e53SMauro Carvalho Chehab DECLARE_ADDR_MATCH(channel, 3);
852a5538e53SMauro Carvalho Chehab DECLARE_ADDR_MATCH(dimm, 3);
853a5538e53SMauro Carvalho Chehab DECLARE_ADDR_MATCH(rank, 4);
854a5538e53SMauro Carvalho Chehab DECLARE_ADDR_MATCH(bank, 32);
855a5538e53SMauro Carvalho Chehab DECLARE_ADDR_MATCH(page, 0x10000);
856a5538e53SMauro Carvalho Chehab DECLARE_ADDR_MATCH(col, 0x4000);
857194a40feSMauro Carvalho Chehab 
8585c4cdb5aSMauro Carvalho Chehab ATTR_ADDR_MATCH(channel);
8595c4cdb5aSMauro Carvalho Chehab ATTR_ADDR_MATCH(dimm);
8605c4cdb5aSMauro Carvalho Chehab ATTR_ADDR_MATCH(rank);
8615c4cdb5aSMauro Carvalho Chehab ATTR_ADDR_MATCH(bank);
8625c4cdb5aSMauro Carvalho Chehab ATTR_ADDR_MATCH(page);
8635c4cdb5aSMauro Carvalho Chehab ATTR_ADDR_MATCH(col);
8645c4cdb5aSMauro Carvalho Chehab 
write_and_test(struct pci_dev * dev,const int where,const u32 val)8651288c18fSMauro Carvalho Chehab static int write_and_test(struct pci_dev *dev, const int where, const u32 val)
866276b824cSMauro Carvalho Chehab {
867276b824cSMauro Carvalho Chehab 	u32 read;
868276b824cSMauro Carvalho Chehab 	int count;
869276b824cSMauro Carvalho Chehab 
870956b9ba1SJoe Perches 	edac_dbg(0, "setting pci %02x:%02x.%x reg=%02x value=%08x\n",
8714157d9f5SMauro Carvalho Chehab 		 dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn),
8724157d9f5SMauro Carvalho Chehab 		 where, val);
8734157d9f5SMauro Carvalho Chehab 
874276b824cSMauro Carvalho Chehab 	for (count = 0; count < 10; count++) {
875276b824cSMauro Carvalho Chehab 		if (count)
876276b824cSMauro Carvalho Chehab 			msleep(100);
877276b824cSMauro Carvalho Chehab 		pci_write_config_dword(dev, where, val);
878276b824cSMauro Carvalho Chehab 		pci_read_config_dword(dev, where, &read);
879276b824cSMauro Carvalho Chehab 
880276b824cSMauro Carvalho Chehab 		if (read == val)
881276b824cSMauro Carvalho Chehab 			return 0;
882276b824cSMauro Carvalho Chehab 	}
883276b824cSMauro Carvalho Chehab 
8844157d9f5SMauro Carvalho Chehab 	i7core_printk(KERN_ERR, "Error during set pci %02x:%02x.%x reg=%02x "
8854157d9f5SMauro Carvalho Chehab 		"write=%08x. Read=%08x\n",
8864157d9f5SMauro Carvalho Chehab 		dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn),
8874157d9f5SMauro Carvalho Chehab 		where, val, read);
888276b824cSMauro Carvalho Chehab 
889276b824cSMauro Carvalho Chehab 	return -EINVAL;
890276b824cSMauro Carvalho Chehab }
891276b824cSMauro Carvalho Chehab 
892194a40feSMauro Carvalho Chehab /*
893194a40feSMauro Carvalho Chehab  * This routine prepares the Memory Controller for error injection.
894194a40feSMauro Carvalho Chehab  * The error will be injected when some process tries to write to the
895194a40feSMauro Carvalho Chehab  * memory that matches the given criteria.
896194a40feSMauro Carvalho Chehab  * The criteria can be set in terms of a mask where dimm, rank, bank, page
897194a40feSMauro Carvalho Chehab  * and col can be specified.
898194a40feSMauro Carvalho Chehab  * A -1 value for any of the mask items will make the MCU to ignore
899194a40feSMauro Carvalho Chehab  * that matching criteria for error injection.
900194a40feSMauro Carvalho Chehab  *
901194a40feSMauro Carvalho Chehab  * It should be noticed that the error will only happen after a write operation
902194a40feSMauro Carvalho Chehab  * on a memory that matches the condition. if REPEAT_EN is not enabled at
903194a40feSMauro Carvalho Chehab  * inject mask, then it will produce just one error. Otherwise, it will repeat
904194a40feSMauro Carvalho Chehab  * until the injectmask would be cleaned.
905194a40feSMauro Carvalho Chehab  *
906194a40feSMauro Carvalho Chehab  * FIXME: This routine assumes that MAXNUMDIMMS value of MC_MAX_DOD
907194a40feSMauro Carvalho Chehab  *    is reliable enough to check if the MC is using the
908194a40feSMauro Carvalho Chehab  *    three channels. However, this is not clear at the datasheet.
909194a40feSMauro Carvalho Chehab  */
i7core_inject_enable_store(struct device * dev,struct device_attribute * mattr,const char * data,size_t count)9105c4cdb5aSMauro Carvalho Chehab static ssize_t i7core_inject_enable_store(struct device *dev,
9115c4cdb5aSMauro Carvalho Chehab 					  struct device_attribute *mattr,
912194a40feSMauro Carvalho Chehab 					  const char *data, size_t count)
913194a40feSMauro Carvalho Chehab {
9145c4cdb5aSMauro Carvalho Chehab 	struct mem_ctl_info *mci = to_mci(dev);
915194a40feSMauro Carvalho Chehab 	struct i7core_pvt *pvt = mci->pvt_info;
916194a40feSMauro Carvalho Chehab 	u32 injectmask;
917194a40feSMauro Carvalho Chehab 	u64 mask = 0;
918194a40feSMauro Carvalho Chehab 	int  rc;
919194a40feSMauro Carvalho Chehab 	long enable;
920194a40feSMauro Carvalho Chehab 
921f4742949SMauro Carvalho Chehab 	if (!pvt->pci_ch[pvt->inject.channel][0])
9228f331907SMauro Carvalho Chehab 		return 0;
9238f331907SMauro Carvalho Chehab 
924c7f62fc8SJingoo Han 	rc = kstrtoul(data, 10, &enable);
925194a40feSMauro Carvalho Chehab 	if ((rc < 0))
926194a40feSMauro Carvalho Chehab 		return 0;
927194a40feSMauro Carvalho Chehab 
928194a40feSMauro Carvalho Chehab 	if (enable) {
929194a40feSMauro Carvalho Chehab 		pvt->inject.enable = 1;
930194a40feSMauro Carvalho Chehab 	} else {
931194a40feSMauro Carvalho Chehab 		disable_inject(mci);
932194a40feSMauro Carvalho Chehab 		return count;
933194a40feSMauro Carvalho Chehab 	}
934194a40feSMauro Carvalho Chehab 
935194a40feSMauro Carvalho Chehab 	/* Sets pvt->inject.dimm mask */
936194a40feSMauro Carvalho Chehab 	if (pvt->inject.dimm < 0)
937486dd09fSAlan Cox 		mask |= 1LL << 41;
938194a40feSMauro Carvalho Chehab 	else {
939f4742949SMauro Carvalho Chehab 		if (pvt->channel[pvt->inject.channel].dimms > 2)
940486dd09fSAlan Cox 			mask |= (pvt->inject.dimm & 0x3LL) << 35;
941194a40feSMauro Carvalho Chehab 		else
942486dd09fSAlan Cox 			mask |= (pvt->inject.dimm & 0x1LL) << 36;
943194a40feSMauro Carvalho Chehab 	}
944194a40feSMauro Carvalho Chehab 
945194a40feSMauro Carvalho Chehab 	/* Sets pvt->inject.rank mask */
946194a40feSMauro Carvalho Chehab 	if (pvt->inject.rank < 0)
947486dd09fSAlan Cox 		mask |= 1LL << 40;
948194a40feSMauro Carvalho Chehab 	else {
949f4742949SMauro Carvalho Chehab 		if (pvt->channel[pvt->inject.channel].dimms > 2)
950486dd09fSAlan Cox 			mask |= (pvt->inject.rank & 0x1LL) << 34;
951194a40feSMauro Carvalho Chehab 		else
952486dd09fSAlan Cox 			mask |= (pvt->inject.rank & 0x3LL) << 34;
953194a40feSMauro Carvalho Chehab 	}
954194a40feSMauro Carvalho Chehab 
955194a40feSMauro Carvalho Chehab 	/* Sets pvt->inject.bank mask */
956194a40feSMauro Carvalho Chehab 	if (pvt->inject.bank < 0)
957486dd09fSAlan Cox 		mask |= 1LL << 39;
958194a40feSMauro Carvalho Chehab 	else
959486dd09fSAlan Cox 		mask |= (pvt->inject.bank & 0x15LL) << 30;
960194a40feSMauro Carvalho Chehab 
961194a40feSMauro Carvalho Chehab 	/* Sets pvt->inject.page mask */
962194a40feSMauro Carvalho Chehab 	if (pvt->inject.page < 0)
963486dd09fSAlan Cox 		mask |= 1LL << 38;
964194a40feSMauro Carvalho Chehab 	else
965486dd09fSAlan Cox 		mask |= (pvt->inject.page & 0xffff) << 14;
966194a40feSMauro Carvalho Chehab 
967194a40feSMauro Carvalho Chehab 	/* Sets pvt->inject.column mask */
968194a40feSMauro Carvalho Chehab 	if (pvt->inject.col < 0)
969486dd09fSAlan Cox 		mask |= 1LL << 37;
970194a40feSMauro Carvalho Chehab 	else
971486dd09fSAlan Cox 		mask |= (pvt->inject.col & 0x3fff);
972194a40feSMauro Carvalho Chehab 
973194a40feSMauro Carvalho Chehab 	/*
974194a40feSMauro Carvalho Chehab 	 * bit    0: REPEAT_EN
975194a40feSMauro Carvalho Chehab 	 * bits 1-2: MASK_HALF_CACHELINE
976194a40feSMauro Carvalho Chehab 	 * bit    3: INJECT_ECC
977194a40feSMauro Carvalho Chehab 	 * bit    4: INJECT_ADDR_PARITY
978194a40feSMauro Carvalho Chehab 	 */
979194a40feSMauro Carvalho Chehab 
9807b029d03SMauro Carvalho Chehab 	injectmask = (pvt->inject.type & 1) |
9817b029d03SMauro Carvalho Chehab 		     (pvt->inject.section & 0x3) << 1 |
982194a40feSMauro Carvalho Chehab 		     (pvt->inject.type & 0x6) << (3 - 1);
983194a40feSMauro Carvalho Chehab 
984276b824cSMauro Carvalho Chehab 	/* Unlock writes to registers - this register is write only */
985f4742949SMauro Carvalho Chehab 	pci_write_config_dword(pvt->pci_noncore,
986276b824cSMauro Carvalho Chehab 			       MC_CFG_CONTROL, 0x2);
987194a40feSMauro Carvalho Chehab 
988f4742949SMauro Carvalho Chehab 	write_and_test(pvt->pci_ch[pvt->inject.channel][0],
989276b824cSMauro Carvalho Chehab 			       MC_CHANNEL_ADDR_MATCH, mask);
990f4742949SMauro Carvalho Chehab 	write_and_test(pvt->pci_ch[pvt->inject.channel][0],
991276b824cSMauro Carvalho Chehab 			       MC_CHANNEL_ADDR_MATCH + 4, mask >> 32L);
992276b824cSMauro Carvalho Chehab 
993f4742949SMauro Carvalho Chehab 	write_and_test(pvt->pci_ch[pvt->inject.channel][0],
994276b824cSMauro Carvalho Chehab 			       MC_CHANNEL_ERROR_MASK, pvt->inject.eccmask);
995276b824cSMauro Carvalho Chehab 
996f4742949SMauro Carvalho Chehab 	write_and_test(pvt->pci_ch[pvt->inject.channel][0],
9974157d9f5SMauro Carvalho Chehab 			       MC_CHANNEL_ERROR_INJECT, injectmask);
998276b824cSMauro Carvalho Chehab 
999276b824cSMauro Carvalho Chehab 	/*
1000276b824cSMauro Carvalho Chehab 	 * This is something undocumented, based on my tests
1001276b824cSMauro Carvalho Chehab 	 * Without writing 8 to this register, errors aren't injected. Not sure
1002276b824cSMauro Carvalho Chehab 	 * why.
1003276b824cSMauro Carvalho Chehab 	 */
1004f4742949SMauro Carvalho Chehab 	pci_write_config_dword(pvt->pci_noncore,
1005276b824cSMauro Carvalho Chehab 			       MC_CFG_CONTROL, 8);
1006276b824cSMauro Carvalho Chehab 
1007956b9ba1SJoe Perches 	edac_dbg(0, "Error inject addr match 0x%016llx, ecc 0x%08x, inject 0x%08x\n",
1008194a40feSMauro Carvalho Chehab 		 mask, pvt->inject.eccmask, injectmask);
1009194a40feSMauro Carvalho Chehab 
10107b029d03SMauro Carvalho Chehab 
1011194a40feSMauro Carvalho Chehab 	return count;
1012194a40feSMauro Carvalho Chehab }
1013194a40feSMauro Carvalho Chehab 
i7core_inject_enable_show(struct device * dev,struct device_attribute * mattr,char * data)10145c4cdb5aSMauro Carvalho Chehab static ssize_t i7core_inject_enable_show(struct device *dev,
10155c4cdb5aSMauro Carvalho Chehab 					 struct device_attribute *mattr,
1016194a40feSMauro Carvalho Chehab 					 char *data)
1017194a40feSMauro Carvalho Chehab {
10185c4cdb5aSMauro Carvalho Chehab 	struct mem_ctl_info *mci = to_mci(dev);
1019194a40feSMauro Carvalho Chehab 	struct i7core_pvt *pvt = mci->pvt_info;
10207b029d03SMauro Carvalho Chehab 	u32 injectmask;
10217b029d03SMauro Carvalho Chehab 
102252a2e4fcSMauro Carvalho Chehab 	if (!pvt->pci_ch[pvt->inject.channel][0])
102352a2e4fcSMauro Carvalho Chehab 		return 0;
102452a2e4fcSMauro Carvalho Chehab 
1025f4742949SMauro Carvalho Chehab 	pci_read_config_dword(pvt->pci_ch[pvt->inject.channel][0],
10264157d9f5SMauro Carvalho Chehab 			       MC_CHANNEL_ERROR_INJECT, &injectmask);
10277b029d03SMauro Carvalho Chehab 
1028956b9ba1SJoe Perches 	edac_dbg(0, "Inject error read: 0x%018x\n", injectmask);
10297b029d03SMauro Carvalho Chehab 
10307b029d03SMauro Carvalho Chehab 	if (injectmask & 0x0c)
10317b029d03SMauro Carvalho Chehab 		pvt->inject.enable = 1;
10327b029d03SMauro Carvalho Chehab 
1033194a40feSMauro Carvalho Chehab 	return sprintf(data, "%d\n", pvt->inject.enable);
1034194a40feSMauro Carvalho Chehab }
1035194a40feSMauro Carvalho Chehab 
1036f338d736SMauro Carvalho Chehab #define DECLARE_COUNTER(param)					\
1037f338d736SMauro Carvalho Chehab static ssize_t i7core_show_counter_##param(			\
10385c4cdb5aSMauro Carvalho Chehab 	struct device *dev,					\
10395c4cdb5aSMauro Carvalho Chehab 	struct device_attribute *mattr,				\
1040f338d736SMauro Carvalho Chehab 	char *data)						\
1041f338d736SMauro Carvalho Chehab {								\
104242709efbSPrarit Bhargava 	struct mem_ctl_info *mci = dev_get_drvdata(dev);	\
1043f338d736SMauro Carvalho Chehab 	struct i7core_pvt *pvt = mci->pvt_info;			\
1044f338d736SMauro Carvalho Chehab 								\
1045956b9ba1SJoe Perches 	edac_dbg(1, "\n");					\
1046f338d736SMauro Carvalho Chehab 	if (!pvt->ce_count_available || (pvt->is_registered))	\
1047f338d736SMauro Carvalho Chehab 		return sprintf(data, "data unavailable\n");	\
1048f338d736SMauro Carvalho Chehab 	return sprintf(data, "%lu\n",				\
1049f338d736SMauro Carvalho Chehab 			pvt->udimm_ce_count[param]);		\
1050d88b8507SMauro Carvalho Chehab }
1051442305b1SMauro Carvalho Chehab 
1052f338d736SMauro Carvalho Chehab #define ATTR_COUNTER(param)					\
10535c4cdb5aSMauro Carvalho Chehab 	static DEVICE_ATTR(udimm##param, S_IRUGO | S_IWUSR,	\
10545c4cdb5aSMauro Carvalho Chehab 		    i7core_show_counter_##param,		\
10555c4cdb5aSMauro Carvalho Chehab 		    NULL)
1056442305b1SMauro Carvalho Chehab 
1057f338d736SMauro Carvalho Chehab DECLARE_COUNTER(0);
1058f338d736SMauro Carvalho Chehab DECLARE_COUNTER(1);
1059f338d736SMauro Carvalho Chehab DECLARE_COUNTER(2);
1060f338d736SMauro Carvalho Chehab 
10615c4cdb5aSMauro Carvalho Chehab ATTR_COUNTER(0);
10625c4cdb5aSMauro Carvalho Chehab ATTR_COUNTER(1);
10635c4cdb5aSMauro Carvalho Chehab ATTR_COUNTER(2);
10645c4cdb5aSMauro Carvalho Chehab 
1065194a40feSMauro Carvalho Chehab /*
10665c4cdb5aSMauro Carvalho Chehab  * inject_addrmatch device sysfs struct
1067194a40feSMauro Carvalho Chehab  */
1068a5538e53SMauro Carvalho Chehab 
10695c4cdb5aSMauro Carvalho Chehab static struct attribute *i7core_addrmatch_attrs[] = {
10705c4cdb5aSMauro Carvalho Chehab 	&dev_attr_channel.attr,
10715c4cdb5aSMauro Carvalho Chehab 	&dev_attr_dimm.attr,
10725c4cdb5aSMauro Carvalho Chehab 	&dev_attr_rank.attr,
10735c4cdb5aSMauro Carvalho Chehab 	&dev_attr_bank.attr,
10745c4cdb5aSMauro Carvalho Chehab 	&dev_attr_page.attr,
10755c4cdb5aSMauro Carvalho Chehab 	&dev_attr_col.attr,
10765c4cdb5aSMauro Carvalho Chehab 	NULL
1077a5538e53SMauro Carvalho Chehab };
1078a5538e53SMauro Carvalho Chehab 
10791c18be5aSArvind Yadav static const struct attribute_group addrmatch_grp = {
10805c4cdb5aSMauro Carvalho Chehab 	.attrs	= i7core_addrmatch_attrs,
1081a5538e53SMauro Carvalho Chehab };
1082a5538e53SMauro Carvalho Chehab 
10835c4cdb5aSMauro Carvalho Chehab static const struct attribute_group *addrmatch_groups[] = {
10845c4cdb5aSMauro Carvalho Chehab 	&addrmatch_grp,
10855c4cdb5aSMauro Carvalho Chehab 	NULL
1086f338d736SMauro Carvalho Chehab };
1087f338d736SMauro Carvalho Chehab 
addrmatch_release(struct device * device)10885c4cdb5aSMauro Carvalho Chehab static void addrmatch_release(struct device *device)
1089194a40feSMauro Carvalho Chehab {
1090956b9ba1SJoe Perches 	edac_dbg(1, "Releasing device %s\n", dev_name(device));
1091356f0a30SMauro Carvalho Chehab 	kfree(device);
10925c4cdb5aSMauro Carvalho Chehab }
10935c4cdb5aSMauro Carvalho Chehab 
1094b2b3e736SBhumika Goyal static const struct device_type addrmatch_type = {
10955c4cdb5aSMauro Carvalho Chehab 	.groups		= addrmatch_groups,
10965c4cdb5aSMauro Carvalho Chehab 	.release	= addrmatch_release,
10971288c18fSMauro Carvalho Chehab };
10981288c18fSMauro Carvalho Chehab 
10995c4cdb5aSMauro Carvalho Chehab /*
11005c4cdb5aSMauro Carvalho Chehab  * all_channel_counts sysfs struct
11015c4cdb5aSMauro Carvalho Chehab  */
11025c4cdb5aSMauro Carvalho Chehab 
11035c4cdb5aSMauro Carvalho Chehab static struct attribute *i7core_udimm_counters_attrs[] = {
11045c4cdb5aSMauro Carvalho Chehab 	&dev_attr_udimm0.attr,
11055c4cdb5aSMauro Carvalho Chehab 	&dev_attr_udimm1.attr,
11065c4cdb5aSMauro Carvalho Chehab 	&dev_attr_udimm2.attr,
11075c4cdb5aSMauro Carvalho Chehab 	NULL
1108194a40feSMauro Carvalho Chehab };
1109194a40feSMauro Carvalho Chehab 
11101c18be5aSArvind Yadav static const struct attribute_group all_channel_counts_grp = {
11115c4cdb5aSMauro Carvalho Chehab 	.attrs	= i7core_udimm_counters_attrs,
11125c4cdb5aSMauro Carvalho Chehab };
11135c4cdb5aSMauro Carvalho Chehab 
11145c4cdb5aSMauro Carvalho Chehab static const struct attribute_group *all_channel_counts_groups[] = {
11155c4cdb5aSMauro Carvalho Chehab 	&all_channel_counts_grp,
11165c4cdb5aSMauro Carvalho Chehab 	NULL
11175c4cdb5aSMauro Carvalho Chehab };
11185c4cdb5aSMauro Carvalho Chehab 
all_channel_counts_release(struct device * device)11195c4cdb5aSMauro Carvalho Chehab static void all_channel_counts_release(struct device *device)
11205c4cdb5aSMauro Carvalho Chehab {
1121956b9ba1SJoe Perches 	edac_dbg(1, "Releasing device %s\n", dev_name(device));
1122356f0a30SMauro Carvalho Chehab 	kfree(device);
11235c4cdb5aSMauro Carvalho Chehab }
11245c4cdb5aSMauro Carvalho Chehab 
1125b2b3e736SBhumika Goyal static const struct device_type all_channel_counts_type = {
11265c4cdb5aSMauro Carvalho Chehab 	.groups		= all_channel_counts_groups,
11275c4cdb5aSMauro Carvalho Chehab 	.release	= all_channel_counts_release,
11285c4cdb5aSMauro Carvalho Chehab };
11295c4cdb5aSMauro Carvalho Chehab 
11305c4cdb5aSMauro Carvalho Chehab /*
11315c4cdb5aSMauro Carvalho Chehab  * inject sysfs attributes
11325c4cdb5aSMauro Carvalho Chehab  */
11335c4cdb5aSMauro Carvalho Chehab 
11345c4cdb5aSMauro Carvalho Chehab static DEVICE_ATTR(inject_section, S_IRUGO | S_IWUSR,
11355c4cdb5aSMauro Carvalho Chehab 		   i7core_inject_section_show, i7core_inject_section_store);
11365c4cdb5aSMauro Carvalho Chehab 
11375c4cdb5aSMauro Carvalho Chehab static DEVICE_ATTR(inject_type, S_IRUGO | S_IWUSR,
11385c4cdb5aSMauro Carvalho Chehab 		   i7core_inject_type_show, i7core_inject_type_store);
11395c4cdb5aSMauro Carvalho Chehab 
11405c4cdb5aSMauro Carvalho Chehab 
11415c4cdb5aSMauro Carvalho Chehab static DEVICE_ATTR(inject_eccmask, S_IRUGO | S_IWUSR,
11425c4cdb5aSMauro Carvalho Chehab 		   i7core_inject_eccmask_show, i7core_inject_eccmask_store);
11435c4cdb5aSMauro Carvalho Chehab 
11445c4cdb5aSMauro Carvalho Chehab static DEVICE_ATTR(inject_enable, S_IRUGO | S_IWUSR,
11455c4cdb5aSMauro Carvalho Chehab 		   i7core_inject_enable_show, i7core_inject_enable_store);
11465c4cdb5aSMauro Carvalho Chehab 
11472eace188STakashi Iwai static struct attribute *i7core_dev_attrs[] = {
11482eace188STakashi Iwai 	&dev_attr_inject_section.attr,
11492eace188STakashi Iwai 	&dev_attr_inject_type.attr,
11502eace188STakashi Iwai 	&dev_attr_inject_eccmask.attr,
11512eace188STakashi Iwai 	&dev_attr_inject_enable.attr,
11522eace188STakashi Iwai 	NULL
11532eace188STakashi Iwai };
11542eace188STakashi Iwai 
11552eace188STakashi Iwai ATTRIBUTE_GROUPS(i7core_dev);
11562eace188STakashi Iwai 
i7core_create_sysfs_devices(struct mem_ctl_info * mci)11575c4cdb5aSMauro Carvalho Chehab static int i7core_create_sysfs_devices(struct mem_ctl_info *mci)
11585c4cdb5aSMauro Carvalho Chehab {
11595c4cdb5aSMauro Carvalho Chehab 	struct i7core_pvt *pvt = mci->pvt_info;
11605c4cdb5aSMauro Carvalho Chehab 	int rc;
11615c4cdb5aSMauro Carvalho Chehab 
1162356f0a30SMauro Carvalho Chehab 	pvt->addrmatch_dev = kzalloc(sizeof(*pvt->addrmatch_dev), GFP_KERNEL);
1163356f0a30SMauro Carvalho Chehab 	if (!pvt->addrmatch_dev)
1164e97d7e38STakashi Iwai 		return -ENOMEM;
1165356f0a30SMauro Carvalho Chehab 
1166356f0a30SMauro Carvalho Chehab 	pvt->addrmatch_dev->type = &addrmatch_type;
1167356f0a30SMauro Carvalho Chehab 	pvt->addrmatch_dev->bus = mci->dev.bus;
1168356f0a30SMauro Carvalho Chehab 	device_initialize(pvt->addrmatch_dev);
1169356f0a30SMauro Carvalho Chehab 	pvt->addrmatch_dev->parent = &mci->dev;
1170356f0a30SMauro Carvalho Chehab 	dev_set_name(pvt->addrmatch_dev, "inject_addrmatch");
1171356f0a30SMauro Carvalho Chehab 	dev_set_drvdata(pvt->addrmatch_dev, mci);
11725c4cdb5aSMauro Carvalho Chehab 
1173956b9ba1SJoe Perches 	edac_dbg(1, "creating %s\n", dev_name(pvt->addrmatch_dev));
11745c4cdb5aSMauro Carvalho Chehab 
1175356f0a30SMauro Carvalho Chehab 	rc = device_add(pvt->addrmatch_dev);
11765c4cdb5aSMauro Carvalho Chehab 	if (rc < 0)
11776c974d4dSJohan Hovold 		goto err_put_addrmatch;
11785c4cdb5aSMauro Carvalho Chehab 
11795c4cdb5aSMauro Carvalho Chehab 	if (!pvt->is_registered) {
1180356f0a30SMauro Carvalho Chehab 		pvt->chancounts_dev = kzalloc(sizeof(*pvt->chancounts_dev),
1181356f0a30SMauro Carvalho Chehab 					      GFP_KERNEL);
1182356f0a30SMauro Carvalho Chehab 		if (!pvt->chancounts_dev) {
11836c974d4dSJohan Hovold 			rc = -ENOMEM;
11846c974d4dSJohan Hovold 			goto err_del_addrmatch;
1185356f0a30SMauro Carvalho Chehab 		}
1186356f0a30SMauro Carvalho Chehab 
1187356f0a30SMauro Carvalho Chehab 		pvt->chancounts_dev->type = &all_channel_counts_type;
1188356f0a30SMauro Carvalho Chehab 		pvt->chancounts_dev->bus = mci->dev.bus;
1189356f0a30SMauro Carvalho Chehab 		device_initialize(pvt->chancounts_dev);
1190356f0a30SMauro Carvalho Chehab 		pvt->chancounts_dev->parent = &mci->dev;
1191356f0a30SMauro Carvalho Chehab 		dev_set_name(pvt->chancounts_dev, "all_channel_counts");
1192356f0a30SMauro Carvalho Chehab 		dev_set_drvdata(pvt->chancounts_dev, mci);
11935c4cdb5aSMauro Carvalho Chehab 
1194956b9ba1SJoe Perches 		edac_dbg(1, "creating %s\n", dev_name(pvt->chancounts_dev));
11955c4cdb5aSMauro Carvalho Chehab 
1196356f0a30SMauro Carvalho Chehab 		rc = device_add(pvt->chancounts_dev);
11975c4cdb5aSMauro Carvalho Chehab 		if (rc < 0)
11986c974d4dSJohan Hovold 			goto err_put_chancounts;
11995c4cdb5aSMauro Carvalho Chehab 	}
12005c4cdb5aSMauro Carvalho Chehab 	return 0;
12016c974d4dSJohan Hovold 
12026c974d4dSJohan Hovold err_put_chancounts:
12036c974d4dSJohan Hovold 	put_device(pvt->chancounts_dev);
12046c974d4dSJohan Hovold err_del_addrmatch:
12056c974d4dSJohan Hovold 	device_del(pvt->addrmatch_dev);
12066c974d4dSJohan Hovold err_put_addrmatch:
12076c974d4dSJohan Hovold 	put_device(pvt->addrmatch_dev);
12086c974d4dSJohan Hovold 
12096c974d4dSJohan Hovold 	return rc;
12105c4cdb5aSMauro Carvalho Chehab }
12115c4cdb5aSMauro Carvalho Chehab 
i7core_delete_sysfs_devices(struct mem_ctl_info * mci)12125c4cdb5aSMauro Carvalho Chehab static void i7core_delete_sysfs_devices(struct mem_ctl_info *mci)
12135c4cdb5aSMauro Carvalho Chehab {
12145c4cdb5aSMauro Carvalho Chehab 	struct i7core_pvt *pvt = mci->pvt_info;
12155c4cdb5aSMauro Carvalho Chehab 
1216956b9ba1SJoe Perches 	edac_dbg(1, "\n");
12175c4cdb5aSMauro Carvalho Chehab 
12185c4cdb5aSMauro Carvalho Chehab 	if (!pvt->is_registered) {
1219356f0a30SMauro Carvalho Chehab 		device_del(pvt->chancounts_dev);
12206c974d4dSJohan Hovold 		put_device(pvt->chancounts_dev);
12215c4cdb5aSMauro Carvalho Chehab 	}
1222356f0a30SMauro Carvalho Chehab 	device_del(pvt->addrmatch_dev);
12236c974d4dSJohan Hovold 	put_device(pvt->addrmatch_dev);
12245c4cdb5aSMauro Carvalho Chehab }
12255c4cdb5aSMauro Carvalho Chehab 
1226194a40feSMauro Carvalho Chehab /****************************************************************************
1227a0c36a1fSMauro Carvalho Chehab 	Device initialization routines: put/get, init/exit
1228a0c36a1fSMauro Carvalho Chehab  ****************************************************************************/
1229a0c36a1fSMauro Carvalho Chehab 
1230a0c36a1fSMauro Carvalho Chehab /*
123164c10f6eSHidetoshi Seto  *	i7core_put_all_devices	'put' all the devices that we have
1232a0c36a1fSMauro Carvalho Chehab  *				reserved via 'get'
1233a0c36a1fSMauro Carvalho Chehab  */
i7core_put_devices(struct i7core_dev * i7core_dev)123413d6e9b6SMauro Carvalho Chehab static void i7core_put_devices(struct i7core_dev *i7core_dev)
1235a0c36a1fSMauro Carvalho Chehab {
123613d6e9b6SMauro Carvalho Chehab 	int i;
1237a0c36a1fSMauro Carvalho Chehab 
1238956b9ba1SJoe Perches 	edac_dbg(0, "\n");
1239de06eeefSMauro Carvalho Chehab 	for (i = 0; i < i7core_dev->n_devs; i++) {
124022e6bcbdSMauro Carvalho Chehab 		struct pci_dev *pdev = i7core_dev->pdev[i];
124122e6bcbdSMauro Carvalho Chehab 		if (!pdev)
124222e6bcbdSMauro Carvalho Chehab 			continue;
1243956b9ba1SJoe Perches 		edac_dbg(0, "Removing dev %02x:%02x.%d\n",
124422e6bcbdSMauro Carvalho Chehab 			 pdev->bus->number,
124522e6bcbdSMauro Carvalho Chehab 			 PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
124622e6bcbdSMauro Carvalho Chehab 		pci_dev_put(pdev);
124722e6bcbdSMauro Carvalho Chehab 	}
124866607706SMauro Carvalho Chehab }
124913d6e9b6SMauro Carvalho Chehab 
i7core_put_all_devices(void)125013d6e9b6SMauro Carvalho Chehab static void i7core_put_all_devices(void)
125113d6e9b6SMauro Carvalho Chehab {
125242538680SMauro Carvalho Chehab 	struct i7core_dev *i7core_dev, *tmp;
125313d6e9b6SMauro Carvalho Chehab 
125439300e71SMauro Carvalho Chehab 	list_for_each_entry_safe(i7core_dev, tmp, &i7core_edac_list, list) {
125513d6e9b6SMauro Carvalho Chehab 		i7core_put_devices(i7core_dev);
12562aa9be44SHidetoshi Seto 		free_i7core_dev(i7core_dev);
125739300e71SMauro Carvalho Chehab 	}
1258a0c36a1fSMauro Carvalho Chehab }
1259a0c36a1fSMauro Carvalho Chehab 
i7core_xeon_pci_fixup(const struct pci_id_table * table)12601288c18fSMauro Carvalho Chehab static void __init i7core_xeon_pci_fixup(const struct pci_id_table *table)
1261bc2d7245SKeith Mannthey {
1262bc2d7245SKeith Mannthey 	struct pci_dev *pdev = NULL;
1263bc2d7245SKeith Mannthey 	int i;
126454a08ab1SMauro Carvalho Chehab 
1265bc2d7245SKeith Mannthey 	/*
1266e7bf068aSDavid Sterba 	 * On Xeon 55xx, the Intel Quick Path Arch Generic Non-core pci buses
1267bc2d7245SKeith Mannthey 	 * aren't announced by acpi. So, we need to use a legacy scan probing
1268bc2d7245SKeith Mannthey 	 * to detect them
1269bc2d7245SKeith Mannthey 	 */
1270bd9e19caSVernon Mauery 	while (table && table->descr) {
1271bd9e19caSVernon Mauery 		pdev = pci_get_device(PCI_VENDOR_ID_INTEL, table->descr[0].dev_id, NULL);
1272bc2d7245SKeith Mannthey 		if (unlikely(!pdev)) {
1273f4742949SMauro Carvalho Chehab 			for (i = 0; i < MAX_SOCKET_BUSES; i++)
1274bc2d7245SKeith Mannthey 				pcibios_scan_specific_bus(255-i);
1275bc2d7245SKeith Mannthey 		}
1276bda14289SMauro Carvalho Chehab 		pci_dev_put(pdev);
1277bd9e19caSVernon Mauery 		table++;
1278bd9e19caSVernon Mauery 	}
1279bc2d7245SKeith Mannthey }
1280bc2d7245SKeith Mannthey 
i7core_pci_lastbus(void)1281bda14289SMauro Carvalho Chehab static unsigned i7core_pci_lastbus(void)
1282bda14289SMauro Carvalho Chehab {
1283bda14289SMauro Carvalho Chehab 	int last_bus = 0, bus;
1284bda14289SMauro Carvalho Chehab 	struct pci_bus *b = NULL;
1285bda14289SMauro Carvalho Chehab 
1286bda14289SMauro Carvalho Chehab 	while ((b = pci_find_next_bus(b)) != NULL) {
1287bda14289SMauro Carvalho Chehab 		bus = b->number;
1288956b9ba1SJoe Perches 		edac_dbg(0, "Found bus %d\n", bus);
1289bda14289SMauro Carvalho Chehab 		if (bus > last_bus)
1290bda14289SMauro Carvalho Chehab 			last_bus = bus;
1291bda14289SMauro Carvalho Chehab 	}
1292bda14289SMauro Carvalho Chehab 
1293956b9ba1SJoe Perches 	edac_dbg(0, "Last bus %d\n", last_bus);
1294bda14289SMauro Carvalho Chehab 
1295bda14289SMauro Carvalho Chehab 	return last_bus;
1296bda14289SMauro Carvalho Chehab }
1297bda14289SMauro Carvalho Chehab 
1298a0c36a1fSMauro Carvalho Chehab /*
129964c10f6eSHidetoshi Seto  *	i7core_get_all_devices	Find and perform 'get' operation on the MCH's
1300a0c36a1fSMauro Carvalho Chehab  *			device/functions we want to reference for this driver
1301a0c36a1fSMauro Carvalho Chehab  *
1302a0c36a1fSMauro Carvalho Chehab  *			Need to 'get' device 16 func 1 and func 2
1303a0c36a1fSMauro Carvalho Chehab  */
i7core_get_onedevice(struct pci_dev ** prev,const struct pci_id_table * table,const unsigned devno,const unsigned last_bus)1304b197cba0SHidetoshi Seto static int i7core_get_onedevice(struct pci_dev **prev,
1305b197cba0SHidetoshi Seto 				const struct pci_id_table *table,
1306b197cba0SHidetoshi Seto 				const unsigned devno,
13071288c18fSMauro Carvalho Chehab 				const unsigned last_bus)
1308a0c36a1fSMauro Carvalho Chehab {
130966607706SMauro Carvalho Chehab 	struct i7core_dev *i7core_dev;
1310b197cba0SHidetoshi Seto 	const struct pci_id_descr *dev_descr = &table->descr[devno];
131166607706SMauro Carvalho Chehab 
13128f331907SMauro Carvalho Chehab 	struct pci_dev *pdev = NULL;
131367166af4SMauro Carvalho Chehab 	u8 bus = 0;
131467166af4SMauro Carvalho Chehab 	u8 socket = 0;
1315a0c36a1fSMauro Carvalho Chehab 
1316a0c36a1fSMauro Carvalho Chehab 	pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
1317de06eeefSMauro Carvalho Chehab 			      dev_descr->dev_id, *prev);
1318d1fd4fb6SMauro Carvalho Chehab 
1319224e871fSMauro Carvalho Chehab 	/*
132015ed103aSDavid Mackey 	 * On Xeon 55xx, the Intel QuickPath Arch Generic Non-core regs
1321224e871fSMauro Carvalho Chehab 	 * is at addr 8086:2c40, instead of 8086:2c41. So, we need
1322224e871fSMauro Carvalho Chehab 	 * to probe for the alternate address in case of failure
1323224e871fSMauro Carvalho Chehab 	 */
1324c0f5eeedSJean Delvare 	if (dev_descr->dev_id == PCI_DEVICE_ID_INTEL_I7_NONCORE && !pdev) {
1325c0f5eeedSJean Delvare 		pci_dev_get(*prev);	/* pci_get_device will put it */
1326224e871fSMauro Carvalho Chehab 		pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
1327224e871fSMauro Carvalho Chehab 				      PCI_DEVICE_ID_INTEL_I7_NONCORE_ALT, *prev);
1328c0f5eeedSJean Delvare 	}
1329224e871fSMauro Carvalho Chehab 
1330c0f5eeedSJean Delvare 	if (dev_descr->dev_id == PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE &&
1331c0f5eeedSJean Delvare 	    !pdev) {
1332c0f5eeedSJean Delvare 		pci_dev_get(*prev);	/* pci_get_device will put it */
1333224e871fSMauro Carvalho Chehab 		pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
1334224e871fSMauro Carvalho Chehab 				      PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE_ALT,
1335224e871fSMauro Carvalho Chehab 				      *prev);
1336c0f5eeedSJean Delvare 	}
1337224e871fSMauro Carvalho Chehab 
1338c77720b9SMauro Carvalho Chehab 	if (!pdev) {
1339c77720b9SMauro Carvalho Chehab 		if (*prev) {
1340c77720b9SMauro Carvalho Chehab 			*prev = pdev;
1341c77720b9SMauro Carvalho Chehab 			return 0;
1342c77720b9SMauro Carvalho Chehab 		}
1343c77720b9SMauro Carvalho Chehab 
1344de06eeefSMauro Carvalho Chehab 		if (dev_descr->optional)
1345c77720b9SMauro Carvalho Chehab 			return 0;
1346c77720b9SMauro Carvalho Chehab 
1347bd9e19caSVernon Mauery 		if (devno == 0)
1348bd9e19caSVernon Mauery 			return -ENODEV;
1349bd9e19caSVernon Mauery 
1350ab089374SDaniel J Blueman 		i7core_printk(KERN_INFO,
1351c77720b9SMauro Carvalho Chehab 			"Device not found: dev %02x.%d PCI ID %04x:%04x\n",
1352de06eeefSMauro Carvalho Chehab 			dev_descr->dev, dev_descr->func,
1353de06eeefSMauro Carvalho Chehab 			PCI_VENDOR_ID_INTEL, dev_descr->dev_id);
1354c77720b9SMauro Carvalho Chehab 
1355c77720b9SMauro Carvalho Chehab 		/* End of list, leave */
1356c77720b9SMauro Carvalho Chehab 		return -ENODEV;
1357c77720b9SMauro Carvalho Chehab 	}
135867166af4SMauro Carvalho Chehab 	bus = pdev->bus->number;
135967166af4SMauro Carvalho Chehab 
1360bda14289SMauro Carvalho Chehab 	socket = last_bus - bus;
136167166af4SMauro Carvalho Chehab 
136266607706SMauro Carvalho Chehab 	i7core_dev = get_i7core_dev(socket);
136366607706SMauro Carvalho Chehab 	if (!i7core_dev) {
1364848b2f7eSHidetoshi Seto 		i7core_dev = alloc_i7core_dev(socket, table);
13652896637bSHidetoshi Seto 		if (!i7core_dev) {
13662896637bSHidetoshi Seto 			pci_dev_put(pdev);
136766607706SMauro Carvalho Chehab 			return -ENOMEM;
136867166af4SMauro Carvalho Chehab 		}
13692896637bSHidetoshi Seto 	}
137067166af4SMauro Carvalho Chehab 
137166607706SMauro Carvalho Chehab 	if (i7core_dev->pdev[devno]) {
137267166af4SMauro Carvalho Chehab 		i7core_printk(KERN_ERR,
1373c77720b9SMauro Carvalho Chehab 			"Duplicated device for "
137467166af4SMauro Carvalho Chehab 			"dev %02x:%02x.%d PCI ID %04x:%04x\n",
1375de06eeefSMauro Carvalho Chehab 			bus, dev_descr->dev, dev_descr->func,
1376de06eeefSMauro Carvalho Chehab 			PCI_VENDOR_ID_INTEL, dev_descr->dev_id);
1377c77720b9SMauro Carvalho Chehab 		pci_dev_put(pdev);
1378c77720b9SMauro Carvalho Chehab 		return -ENODEV;
13798f331907SMauro Carvalho Chehab 	}
1380ef708b53SMauro Carvalho Chehab 
138166607706SMauro Carvalho Chehab 	i7core_dev->pdev[devno] = pdev;
1382c77720b9SMauro Carvalho Chehab 
13838f331907SMauro Carvalho Chehab 	/* Sanity check */
1384de06eeefSMauro Carvalho Chehab 	if (unlikely(PCI_SLOT(pdev->devfn) != dev_descr->dev ||
1385de06eeefSMauro Carvalho Chehab 			PCI_FUNC(pdev->devfn) != dev_descr->func)) {
13868f331907SMauro Carvalho Chehab 		i7core_printk(KERN_ERR,
13878f331907SMauro Carvalho Chehab 			"Device PCI ID %04x:%04x "
1388c77720b9SMauro Carvalho Chehab 			"has dev %02x:%02x.%d instead of dev %02x:%02x.%d\n",
1389de06eeefSMauro Carvalho Chehab 			PCI_VENDOR_ID_INTEL, dev_descr->dev_id,
1390c77720b9SMauro Carvalho Chehab 			bus, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
1391de06eeefSMauro Carvalho Chehab 			bus, dev_descr->dev, dev_descr->func);
1392c77720b9SMauro Carvalho Chehab 		return -ENODEV;
1393ef708b53SMauro Carvalho Chehab 	}
1394ef708b53SMauro Carvalho Chehab 
1395ef708b53SMauro Carvalho Chehab 	/* Be sure that the device is enabled */
1396c77720b9SMauro Carvalho Chehab 	if (unlikely(pci_enable_device(pdev) < 0)) {
1397ef708b53SMauro Carvalho Chehab 		i7core_printk(KERN_ERR,
1398c77720b9SMauro Carvalho Chehab 			"Couldn't enable "
1399c77720b9SMauro Carvalho Chehab 			"dev %02x:%02x.%d PCI ID %04x:%04x\n",
1400de06eeefSMauro Carvalho Chehab 			bus, dev_descr->dev, dev_descr->func,
1401de06eeefSMauro Carvalho Chehab 			PCI_VENDOR_ID_INTEL, dev_descr->dev_id);
1402c77720b9SMauro Carvalho Chehab 		return -ENODEV;
1403ef708b53SMauro Carvalho Chehab 	}
1404ef708b53SMauro Carvalho Chehab 
1405956b9ba1SJoe Perches 	edac_dbg(0, "Detected socket %d dev %02x:%02x.%d PCI ID %04x:%04x\n",
1406de06eeefSMauro Carvalho Chehab 		 socket, bus, dev_descr->dev,
1407de06eeefSMauro Carvalho Chehab 		 dev_descr->func,
1408de06eeefSMauro Carvalho Chehab 		 PCI_VENDOR_ID_INTEL, dev_descr->dev_id);
1409c77720b9SMauro Carvalho Chehab 
1410a3e15416SMauro Carvalho Chehab 	/*
1411a3e15416SMauro Carvalho Chehab 	 * As stated on drivers/pci/search.c, the reference count for
1412a3e15416SMauro Carvalho Chehab 	 * @from is always decremented if it is not %NULL. So, as we need
1413a3e15416SMauro Carvalho Chehab 	 * to get all devices up to null, we need to do a get for the device
1414a3e15416SMauro Carvalho Chehab 	 */
1415a3e15416SMauro Carvalho Chehab 	pci_dev_get(pdev);
1416a3e15416SMauro Carvalho Chehab 
1417c77720b9SMauro Carvalho Chehab 	*prev = pdev;
1418ef708b53SMauro Carvalho Chehab 
1419ef708b53SMauro Carvalho Chehab 	return 0;
1420c77720b9SMauro Carvalho Chehab }
1421ef708b53SMauro Carvalho Chehab 
i7core_get_all_devices(void)142264c10f6eSHidetoshi Seto static int i7core_get_all_devices(void)
1423c77720b9SMauro Carvalho Chehab {
14243c52cc57SMauro Carvalho Chehab 	int i, rc, last_bus;
1425c77720b9SMauro Carvalho Chehab 	struct pci_dev *pdev = NULL;
14263c52cc57SMauro Carvalho Chehab 	const struct pci_id_table *table = pci_dev_table;
1427c77720b9SMauro Carvalho Chehab 
1428bda14289SMauro Carvalho Chehab 	last_bus = i7core_pci_lastbus();
1429bda14289SMauro Carvalho Chehab 
14303c52cc57SMauro Carvalho Chehab 	while (table && table->descr) {
1431bd9e19caSVernon Mauery 		for (i = 0; i < table->n_devs; i++) {
1432c77720b9SMauro Carvalho Chehab 			pdev = NULL;
1433c77720b9SMauro Carvalho Chehab 			do {
1434b197cba0SHidetoshi Seto 				rc = i7core_get_onedevice(&pdev, table, i,
1435bda14289SMauro Carvalho Chehab 							  last_bus);
1436de06eeefSMauro Carvalho Chehab 				if (rc < 0) {
1437bd9e19caSVernon Mauery 					if (i == 0) {
1438bd9e19caSVernon Mauery 						i = table->n_devs;
1439bd9e19caSVernon Mauery 						break;
1440bd9e19caSVernon Mauery 					}
144113d6e9b6SMauro Carvalho Chehab 					i7core_put_all_devices();
1442c77720b9SMauro Carvalho Chehab 					return -ENODEV;
1443c77720b9SMauro Carvalho Chehab 				}
1444c77720b9SMauro Carvalho Chehab 			} while (pdev);
1445c77720b9SMauro Carvalho Chehab 		}
14463c52cc57SMauro Carvalho Chehab 		table++;
1447bd9e19caSVernon Mauery 	}
144866607706SMauro Carvalho Chehab 
1449c77720b9SMauro Carvalho Chehab 	return 0;
14508f331907SMauro Carvalho Chehab }
14518f331907SMauro Carvalho Chehab 
mci_bind_devs(struct mem_ctl_info * mci,struct i7core_dev * i7core_dev)1452f4742949SMauro Carvalho Chehab static int mci_bind_devs(struct mem_ctl_info *mci,
1453f4742949SMauro Carvalho Chehab 			 struct i7core_dev *i7core_dev)
1454ef708b53SMauro Carvalho Chehab {
1455ef708b53SMauro Carvalho Chehab 	struct i7core_pvt *pvt = mci->pvt_info;
1456ef708b53SMauro Carvalho Chehab 	struct pci_dev *pdev;
1457f4742949SMauro Carvalho Chehab 	int i, func, slot;
145827100db0SMauro Carvalho Chehab 	char *family;
1459ef708b53SMauro Carvalho Chehab 
146027100db0SMauro Carvalho Chehab 	pvt->is_registered = false;
146127100db0SMauro Carvalho Chehab 	pvt->enable_scrub  = false;
1462de06eeefSMauro Carvalho Chehab 	for (i = 0; i < i7core_dev->n_devs; i++) {
1463f4742949SMauro Carvalho Chehab 		pdev = i7core_dev->pdev[i];
1464ef708b53SMauro Carvalho Chehab 		if (!pdev)
1465ef708b53SMauro Carvalho Chehab 			continue;
14668f331907SMauro Carvalho Chehab 
14678f331907SMauro Carvalho Chehab 		func = PCI_FUNC(pdev->devfn);
1468ef708b53SMauro Carvalho Chehab 		slot = PCI_SLOT(pdev->devfn);
1469ef708b53SMauro Carvalho Chehab 		if (slot == 3) {
1470ef708b53SMauro Carvalho Chehab 			if (unlikely(func > MAX_MCR_FUNC))
1471ef708b53SMauro Carvalho Chehab 				goto error;
1472f4742949SMauro Carvalho Chehab 			pvt->pci_mcr[func] = pdev;
1473ef708b53SMauro Carvalho Chehab 		} else if (likely(slot >= 4 && slot < 4 + NUM_CHANS)) {
1474ef708b53SMauro Carvalho Chehab 			if (unlikely(func > MAX_CHAN_FUNC))
1475ef708b53SMauro Carvalho Chehab 				goto error;
1476f4742949SMauro Carvalho Chehab 			pvt->pci_ch[slot - 4][func] = pdev;
147727100db0SMauro Carvalho Chehab 		} else if (!slot && !func) {
1478f4742949SMauro Carvalho Chehab 			pvt->pci_noncore = pdev;
147927100db0SMauro Carvalho Chehab 
148027100db0SMauro Carvalho Chehab 			/* Detect the processor family */
148127100db0SMauro Carvalho Chehab 			switch (pdev->device) {
148227100db0SMauro Carvalho Chehab 			case PCI_DEVICE_ID_INTEL_I7_NONCORE:
148327100db0SMauro Carvalho Chehab 				family = "Xeon 35xx/ i7core";
148427100db0SMauro Carvalho Chehab 				pvt->enable_scrub = false;
148527100db0SMauro Carvalho Chehab 				break;
148627100db0SMauro Carvalho Chehab 			case PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE_ALT:
148727100db0SMauro Carvalho Chehab 				family = "i7-800/i5-700";
148827100db0SMauro Carvalho Chehab 				pvt->enable_scrub = false;
148927100db0SMauro Carvalho Chehab 				break;
149027100db0SMauro Carvalho Chehab 			case PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE:
149127100db0SMauro Carvalho Chehab 				family = "Xeon 34xx";
149227100db0SMauro Carvalho Chehab 				pvt->enable_scrub = false;
149327100db0SMauro Carvalho Chehab 				break;
149427100db0SMauro Carvalho Chehab 			case PCI_DEVICE_ID_INTEL_I7_NONCORE_ALT:
149527100db0SMauro Carvalho Chehab 				family = "Xeon 55xx";
149627100db0SMauro Carvalho Chehab 				pvt->enable_scrub = true;
149727100db0SMauro Carvalho Chehab 				break;
149827100db0SMauro Carvalho Chehab 			case PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE_REV2:
149927100db0SMauro Carvalho Chehab 				family = "Xeon 56xx / i7-900";
150027100db0SMauro Carvalho Chehab 				pvt->enable_scrub = true;
150127100db0SMauro Carvalho Chehab 				break;
150227100db0SMauro Carvalho Chehab 			default:
150327100db0SMauro Carvalho Chehab 				family = "unknown";
150427100db0SMauro Carvalho Chehab 				pvt->enable_scrub = false;
150527100db0SMauro Carvalho Chehab 			}
1506956b9ba1SJoe Perches 			edac_dbg(0, "Detected a processor type %s\n", family);
150727100db0SMauro Carvalho Chehab 		} else
1508ef708b53SMauro Carvalho Chehab 			goto error;
15098f331907SMauro Carvalho Chehab 
1510956b9ba1SJoe Perches 		edac_dbg(0, "Associated fn %d.%d, dev = %p, socket %d\n",
151167166af4SMauro Carvalho Chehab 			 PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
1512f4742949SMauro Carvalho Chehab 			 pdev, i7core_dev->socket);
151314d2c083SMauro Carvalho Chehab 
151414d2c083SMauro Carvalho Chehab 		if (PCI_SLOT(pdev->devfn) == 3 &&
151514d2c083SMauro Carvalho Chehab 			PCI_FUNC(pdev->devfn) == 2)
151627100db0SMauro Carvalho Chehab 			pvt->is_registered = true;
1517ef708b53SMauro Carvalho Chehab 	}
1518e9bd2e73SMauro Carvalho Chehab 
1519a0c36a1fSMauro Carvalho Chehab 	return 0;
1520ef708b53SMauro Carvalho Chehab 
1521ef708b53SMauro Carvalho Chehab error:
1522ef708b53SMauro Carvalho Chehab 	i7core_printk(KERN_ERR, "Device %d, function %d "
1523ef708b53SMauro Carvalho Chehab 		      "is out of the expected range\n",
1524ef708b53SMauro Carvalho Chehab 		      slot, func);
1525ef708b53SMauro Carvalho Chehab 	return -EINVAL;
1526a0c36a1fSMauro Carvalho Chehab }
1527a0c36a1fSMauro Carvalho Chehab 
1528442305b1SMauro Carvalho Chehab /****************************************************************************
1529442305b1SMauro Carvalho Chehab 			Error check routines
1530442305b1SMauro Carvalho Chehab  ****************************************************************************/
1531b4e8f0b6SMauro Carvalho Chehab 
i7core_rdimm_update_ce_count(struct mem_ctl_info * mci,const int chan,const int new0,const int new1,const int new2)1532b4e8f0b6SMauro Carvalho Chehab static void i7core_rdimm_update_ce_count(struct mem_ctl_info *mci,
15331288c18fSMauro Carvalho Chehab 					 const int chan,
15341288c18fSMauro Carvalho Chehab 					 const int new0,
15351288c18fSMauro Carvalho Chehab 					 const int new1,
15361288c18fSMauro Carvalho Chehab 					 const int new2)
1537b4e8f0b6SMauro Carvalho Chehab {
1538b4e8f0b6SMauro Carvalho Chehab 	struct i7core_pvt *pvt = mci->pvt_info;
1539b4e8f0b6SMauro Carvalho Chehab 	int add0 = 0, add1 = 0, add2 = 0;
1540b4e8f0b6SMauro Carvalho Chehab 	/* Updates CE counters if it is not the first time here */
1541f4742949SMauro Carvalho Chehab 	if (pvt->ce_count_available) {
1542b4e8f0b6SMauro Carvalho Chehab 		/* Updates CE counters */
1543b4e8f0b6SMauro Carvalho Chehab 
1544f4742949SMauro Carvalho Chehab 		add2 = new2 - pvt->rdimm_last_ce_count[chan][2];
1545f4742949SMauro Carvalho Chehab 		add1 = new1 - pvt->rdimm_last_ce_count[chan][1];
1546f4742949SMauro Carvalho Chehab 		add0 = new0 - pvt->rdimm_last_ce_count[chan][0];
1547b4e8f0b6SMauro Carvalho Chehab 
1548b4e8f0b6SMauro Carvalho Chehab 		if (add2 < 0)
1549b4e8f0b6SMauro Carvalho Chehab 			add2 += 0x7fff;
1550f4742949SMauro Carvalho Chehab 		pvt->rdimm_ce_count[chan][2] += add2;
1551b4e8f0b6SMauro Carvalho Chehab 
1552b4e8f0b6SMauro Carvalho Chehab 		if (add1 < 0)
1553b4e8f0b6SMauro Carvalho Chehab 			add1 += 0x7fff;
1554f4742949SMauro Carvalho Chehab 		pvt->rdimm_ce_count[chan][1] += add1;
1555b4e8f0b6SMauro Carvalho Chehab 
1556b4e8f0b6SMauro Carvalho Chehab 		if (add0 < 0)
1557b4e8f0b6SMauro Carvalho Chehab 			add0 += 0x7fff;
1558f4742949SMauro Carvalho Chehab 		pvt->rdimm_ce_count[chan][0] += add0;
1559b4e8f0b6SMauro Carvalho Chehab 	} else
1560f4742949SMauro Carvalho Chehab 		pvt->ce_count_available = 1;
1561b4e8f0b6SMauro Carvalho Chehab 
1562b4e8f0b6SMauro Carvalho Chehab 	/* Store the new values */
1563f4742949SMauro Carvalho Chehab 	pvt->rdimm_last_ce_count[chan][2] = new2;
1564f4742949SMauro Carvalho Chehab 	pvt->rdimm_last_ce_count[chan][1] = new1;
1565f4742949SMauro Carvalho Chehab 	pvt->rdimm_last_ce_count[chan][0] = new0;
1566b4e8f0b6SMauro Carvalho Chehab 
1567b4e8f0b6SMauro Carvalho Chehab 	/*updated the edac core */
1568b4e8f0b6SMauro Carvalho Chehab 	if (add0 != 0)
156900d18339SMauro Carvalho Chehab 		edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, add0,
157000d18339SMauro Carvalho Chehab 				     0, 0, 0,
157100d18339SMauro Carvalho Chehab 				     chan, 0, -1, "error", "");
1572b4e8f0b6SMauro Carvalho Chehab 	if (add1 != 0)
157300d18339SMauro Carvalho Chehab 		edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, add1,
157400d18339SMauro Carvalho Chehab 				     0, 0, 0,
157500d18339SMauro Carvalho Chehab 				     chan, 1, -1, "error", "");
1576b4e8f0b6SMauro Carvalho Chehab 	if (add2 != 0)
157700d18339SMauro Carvalho Chehab 		edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, add2,
157800d18339SMauro Carvalho Chehab 				     0, 0, 0,
157900d18339SMauro Carvalho Chehab 				     chan, 2, -1, "error", "");
1580b4e8f0b6SMauro Carvalho Chehab }
1581b4e8f0b6SMauro Carvalho Chehab 
i7core_rdimm_check_mc_ecc_err(struct mem_ctl_info * mci)1582f4742949SMauro Carvalho Chehab static void i7core_rdimm_check_mc_ecc_err(struct mem_ctl_info *mci)
1583b4e8f0b6SMauro Carvalho Chehab {
1584b4e8f0b6SMauro Carvalho Chehab 	struct i7core_pvt *pvt = mci->pvt_info;
1585b4e8f0b6SMauro Carvalho Chehab 	u32 rcv[3][2];
1586b4e8f0b6SMauro Carvalho Chehab 	int i, new0, new1, new2;
1587b4e8f0b6SMauro Carvalho Chehab 
1588b4e8f0b6SMauro Carvalho Chehab 	/*Read DEV 3: FUN 2:  MC_COR_ECC_CNT regs directly*/
1589f4742949SMauro Carvalho Chehab 	pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_0,
1590b4e8f0b6SMauro Carvalho Chehab 								&rcv[0][0]);
1591f4742949SMauro Carvalho Chehab 	pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_1,
1592b4e8f0b6SMauro Carvalho Chehab 								&rcv[0][1]);
1593f4742949SMauro Carvalho Chehab 	pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_2,
1594b4e8f0b6SMauro Carvalho Chehab 								&rcv[1][0]);
1595f4742949SMauro Carvalho Chehab 	pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_3,
1596b4e8f0b6SMauro Carvalho Chehab 								&rcv[1][1]);
1597f4742949SMauro Carvalho Chehab 	pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_4,
1598b4e8f0b6SMauro Carvalho Chehab 								&rcv[2][0]);
1599f4742949SMauro Carvalho Chehab 	pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_5,
1600b4e8f0b6SMauro Carvalho Chehab 								&rcv[2][1]);
1601b4e8f0b6SMauro Carvalho Chehab 	for (i = 0 ; i < 3; i++) {
1602956b9ba1SJoe Perches 		edac_dbg(3, "MC_COR_ECC_CNT%d = 0x%x; MC_COR_ECC_CNT%d = 0x%x\n",
1603b4e8f0b6SMauro Carvalho Chehab 			 (i * 2), rcv[i][0], (i * 2) + 1, rcv[i][1]);
1604b4e8f0b6SMauro Carvalho Chehab 		/*if the channel has 3 dimms*/
1605f4742949SMauro Carvalho Chehab 		if (pvt->channel[i].dimms > 2) {
1606b4e8f0b6SMauro Carvalho Chehab 			new0 = DIMM_BOT_COR_ERR(rcv[i][0]);
1607b4e8f0b6SMauro Carvalho Chehab 			new1 = DIMM_TOP_COR_ERR(rcv[i][0]);
1608b4e8f0b6SMauro Carvalho Chehab 			new2 = DIMM_BOT_COR_ERR(rcv[i][1]);
1609b4e8f0b6SMauro Carvalho Chehab 		} else {
1610b4e8f0b6SMauro Carvalho Chehab 			new0 = DIMM_TOP_COR_ERR(rcv[i][0]) +
1611b4e8f0b6SMauro Carvalho Chehab 					DIMM_BOT_COR_ERR(rcv[i][0]);
1612b4e8f0b6SMauro Carvalho Chehab 			new1 = DIMM_TOP_COR_ERR(rcv[i][1]) +
1613b4e8f0b6SMauro Carvalho Chehab 					DIMM_BOT_COR_ERR(rcv[i][1]);
1614b4e8f0b6SMauro Carvalho Chehab 			new2 = 0;
1615b4e8f0b6SMauro Carvalho Chehab 		}
1616b4e8f0b6SMauro Carvalho Chehab 
1617f4742949SMauro Carvalho Chehab 		i7core_rdimm_update_ce_count(mci, i, new0, new1, new2);
1618b4e8f0b6SMauro Carvalho Chehab 	}
1619b4e8f0b6SMauro Carvalho Chehab }
1620442305b1SMauro Carvalho Chehab 
1621442305b1SMauro Carvalho Chehab /* This function is based on the device 3 function 4 registers as described on:
1622442305b1SMauro Carvalho Chehab  * Intel Xeon Processor 5500 Series Datasheet Volume 2
1623442305b1SMauro Carvalho Chehab  *	http://www.intel.com/Assets/PDF/datasheet/321322.pdf
1624442305b1SMauro Carvalho Chehab  * also available at:
1625442305b1SMauro Carvalho Chehab  * 	http://www.arrownac.com/manufacturers/intel/s/nehalem/5500-datasheet-v2.pdf
1626442305b1SMauro Carvalho Chehab  */
i7core_udimm_check_mc_ecc_err(struct mem_ctl_info * mci)1627f4742949SMauro Carvalho Chehab static void i7core_udimm_check_mc_ecc_err(struct mem_ctl_info *mci)
1628442305b1SMauro Carvalho Chehab {
1629442305b1SMauro Carvalho Chehab 	struct i7core_pvt *pvt = mci->pvt_info;
1630442305b1SMauro Carvalho Chehab 	u32 rcv1, rcv0;
1631442305b1SMauro Carvalho Chehab 	int new0, new1, new2;
1632442305b1SMauro Carvalho Chehab 
1633f4742949SMauro Carvalho Chehab 	if (!pvt->pci_mcr[4]) {
1634956b9ba1SJoe Perches 		edac_dbg(0, "MCR registers not found\n");
1635442305b1SMauro Carvalho Chehab 		return;
1636442305b1SMauro Carvalho Chehab 	}
1637442305b1SMauro Carvalho Chehab 
1638b4e8f0b6SMauro Carvalho Chehab 	/* Corrected test errors */
1639f4742949SMauro Carvalho Chehab 	pci_read_config_dword(pvt->pci_mcr[4], MC_TEST_ERR_RCV1, &rcv1);
1640f4742949SMauro Carvalho Chehab 	pci_read_config_dword(pvt->pci_mcr[4], MC_TEST_ERR_RCV0, &rcv0);
1641442305b1SMauro Carvalho Chehab 
1642442305b1SMauro Carvalho Chehab 	/* Store the new values */
1643442305b1SMauro Carvalho Chehab 	new2 = DIMM2_COR_ERR(rcv1);
1644442305b1SMauro Carvalho Chehab 	new1 = DIMM1_COR_ERR(rcv0);
1645442305b1SMauro Carvalho Chehab 	new0 = DIMM0_COR_ERR(rcv0);
1646442305b1SMauro Carvalho Chehab 
1647442305b1SMauro Carvalho Chehab 	/* Updates CE counters if it is not the first time here */
1648f4742949SMauro Carvalho Chehab 	if (pvt->ce_count_available) {
1649442305b1SMauro Carvalho Chehab 		/* Updates CE counters */
1650442305b1SMauro Carvalho Chehab 		int add0, add1, add2;
1651442305b1SMauro Carvalho Chehab 
1652f4742949SMauro Carvalho Chehab 		add2 = new2 - pvt->udimm_last_ce_count[2];
1653f4742949SMauro Carvalho Chehab 		add1 = new1 - pvt->udimm_last_ce_count[1];
1654f4742949SMauro Carvalho Chehab 		add0 = new0 - pvt->udimm_last_ce_count[0];
1655442305b1SMauro Carvalho Chehab 
1656442305b1SMauro Carvalho Chehab 		if (add2 < 0)
1657442305b1SMauro Carvalho Chehab 			add2 += 0x7fff;
1658f4742949SMauro Carvalho Chehab 		pvt->udimm_ce_count[2] += add2;
1659442305b1SMauro Carvalho Chehab 
1660442305b1SMauro Carvalho Chehab 		if (add1 < 0)
1661442305b1SMauro Carvalho Chehab 			add1 += 0x7fff;
1662f4742949SMauro Carvalho Chehab 		pvt->udimm_ce_count[1] += add1;
1663442305b1SMauro Carvalho Chehab 
1664442305b1SMauro Carvalho Chehab 		if (add0 < 0)
1665442305b1SMauro Carvalho Chehab 			add0 += 0x7fff;
1666f4742949SMauro Carvalho Chehab 		pvt->udimm_ce_count[0] += add0;
1667b4e8f0b6SMauro Carvalho Chehab 
1668b4e8f0b6SMauro Carvalho Chehab 		if (add0 | add1 | add2)
1669b4e8f0b6SMauro Carvalho Chehab 			i7core_printk(KERN_ERR, "New Corrected error(s): "
1670b4e8f0b6SMauro Carvalho Chehab 				      "dimm0: +%d, dimm1: +%d, dimm2 +%d\n",
1671b4e8f0b6SMauro Carvalho Chehab 				      add0, add1, add2);
1672442305b1SMauro Carvalho Chehab 	} else
1673f4742949SMauro Carvalho Chehab 		pvt->ce_count_available = 1;
1674442305b1SMauro Carvalho Chehab 
1675442305b1SMauro Carvalho Chehab 	/* Store the new values */
1676f4742949SMauro Carvalho Chehab 	pvt->udimm_last_ce_count[2] = new2;
1677f4742949SMauro Carvalho Chehab 	pvt->udimm_last_ce_count[1] = new1;
1678f4742949SMauro Carvalho Chehab 	pvt->udimm_last_ce_count[0] = new0;
1679442305b1SMauro Carvalho Chehab }
1680442305b1SMauro Carvalho Chehab 
16818a2f118eSMauro Carvalho Chehab /*
16828a2f118eSMauro Carvalho Chehab  * According with tables E-11 and E-12 of chapter E.3.3 of Intel 64 and IA-32
16838a2f118eSMauro Carvalho Chehab  * Architectures Software Developer’s Manual Volume 3B.
1684f237fcf2SMauro Carvalho Chehab  * Nehalem are defined as family 0x06, model 0x1a
1685f237fcf2SMauro Carvalho Chehab  *
1686f237fcf2SMauro Carvalho Chehab  * The MCA registers used here are the following ones:
16878a2f118eSMauro Carvalho Chehab  *     struct mce field	MCA Register
1688f237fcf2SMauro Carvalho Chehab  *     m->status	MSR_IA32_MC8_STATUS
1689f237fcf2SMauro Carvalho Chehab  *     m->addr		MSR_IA32_MC8_ADDR
1690f237fcf2SMauro Carvalho Chehab  *     m->misc		MSR_IA32_MC8_MISC
16918a2f118eSMauro Carvalho Chehab  * In the case of Nehalem, the error information is masked at .status and .misc
16928a2f118eSMauro Carvalho Chehab  * fields
16938a2f118eSMauro Carvalho Chehab  */
i7core_mce_output_error(struct mem_ctl_info * mci,const struct mce * m)1694d5381642SMauro Carvalho Chehab static void i7core_mce_output_error(struct mem_ctl_info *mci,
16951288c18fSMauro Carvalho Chehab 				    const struct mce *m)
1696d5381642SMauro Carvalho Chehab {
1697b4e8f0b6SMauro Carvalho Chehab 	struct i7core_pvt *pvt = mci->pvt_info;
1698f118920bSJean Delvare 	char *optype, *err;
16990975c16fSMauro Carvalho Chehab 	enum hw_event_mc_err_type tp_event;
17008a2f118eSMauro Carvalho Chehab 	unsigned long error = m->status & 0x1ff0000l;
17010975c16fSMauro Carvalho Chehab 	bool uncorrected_error = m->mcgstatus & 1ll << 61;
17020975c16fSMauro Carvalho Chehab 	bool ripv = m->mcgstatus & 1;
1703a639539fSMauro Carvalho Chehab 	u32 optypenum = (m->status >> 4) & 0x07;
17048cf2d239SMathias Krause 	u32 core_err_cnt = (m->status >> 38) & 0x7fff;
17058a2f118eSMauro Carvalho Chehab 	u32 dimm = (m->misc >> 16) & 0x3;
17068a2f118eSMauro Carvalho Chehab 	u32 channel = (m->misc >> 18) & 0x3;
17078a2f118eSMauro Carvalho Chehab 	u32 syndrome = m->misc >> 32;
17088a2f118eSMauro Carvalho Chehab 	u32 errnum = find_first_bit(&error, 32);
1709d5381642SMauro Carvalho Chehab 
17100975c16fSMauro Carvalho Chehab 	if (uncorrected_error) {
1711432de7fdSTony Luck 		core_err_cnt = 1;
1712f118920bSJean Delvare 		if (ripv)
17130975c16fSMauro Carvalho Chehab 			tp_event = HW_EVENT_ERR_UNCORRECTED;
171445bc6098STony Luck 		else
171545bc6098STony Luck 			tp_event = HW_EVENT_ERR_FATAL;
17160975c16fSMauro Carvalho Chehab 	} else {
17170975c16fSMauro Carvalho Chehab 		tp_event = HW_EVENT_ERR_CORRECTED;
17180975c16fSMauro Carvalho Chehab 	}
1719c5d34528SMauro Carvalho Chehab 
1720a639539fSMauro Carvalho Chehab 	switch (optypenum) {
1721a639539fSMauro Carvalho Chehab 	case 0:
1722a639539fSMauro Carvalho Chehab 		optype = "generic undef request";
1723a639539fSMauro Carvalho Chehab 		break;
1724a639539fSMauro Carvalho Chehab 	case 1:
1725a639539fSMauro Carvalho Chehab 		optype = "read error";
1726a639539fSMauro Carvalho Chehab 		break;
1727a639539fSMauro Carvalho Chehab 	case 2:
1728a639539fSMauro Carvalho Chehab 		optype = "write error";
1729a639539fSMauro Carvalho Chehab 		break;
1730a639539fSMauro Carvalho Chehab 	case 3:
1731a639539fSMauro Carvalho Chehab 		optype = "addr/cmd error";
1732a639539fSMauro Carvalho Chehab 		break;
1733a639539fSMauro Carvalho Chehab 	case 4:
1734a639539fSMauro Carvalho Chehab 		optype = "scrubbing error";
1735a639539fSMauro Carvalho Chehab 		break;
1736a639539fSMauro Carvalho Chehab 	default:
1737a639539fSMauro Carvalho Chehab 		optype = "reserved";
1738a639539fSMauro Carvalho Chehab 		break;
1739a639539fSMauro Carvalho Chehab 	}
1740a639539fSMauro Carvalho Chehab 
17418a2f118eSMauro Carvalho Chehab 	switch (errnum) {
17428a2f118eSMauro Carvalho Chehab 	case 16:
17438a2f118eSMauro Carvalho Chehab 		err = "read ECC error";
17448a2f118eSMauro Carvalho Chehab 		break;
17458a2f118eSMauro Carvalho Chehab 	case 17:
17468a2f118eSMauro Carvalho Chehab 		err = "RAS ECC error";
17478a2f118eSMauro Carvalho Chehab 		break;
17488a2f118eSMauro Carvalho Chehab 	case 18:
17498a2f118eSMauro Carvalho Chehab 		err = "write parity error";
17508a2f118eSMauro Carvalho Chehab 		break;
17518a2f118eSMauro Carvalho Chehab 	case 19:
175283e548beSColin Ian King 		err = "redundancy loss";
17538a2f118eSMauro Carvalho Chehab 		break;
17548a2f118eSMauro Carvalho Chehab 	case 20:
17558a2f118eSMauro Carvalho Chehab 		err = "reserved";
17568a2f118eSMauro Carvalho Chehab 		break;
17578a2f118eSMauro Carvalho Chehab 	case 21:
17588a2f118eSMauro Carvalho Chehab 		err = "memory range error";
17598a2f118eSMauro Carvalho Chehab 		break;
17608a2f118eSMauro Carvalho Chehab 	case 22:
17618a2f118eSMauro Carvalho Chehab 		err = "RTID out of range";
17628a2f118eSMauro Carvalho Chehab 		break;
17638a2f118eSMauro Carvalho Chehab 	case 23:
17648a2f118eSMauro Carvalho Chehab 		err = "address parity error";
17658a2f118eSMauro Carvalho Chehab 		break;
17668a2f118eSMauro Carvalho Chehab 	case 24:
17678a2f118eSMauro Carvalho Chehab 		err = "byte enable parity error";
17688a2f118eSMauro Carvalho Chehab 		break;
17698a2f118eSMauro Carvalho Chehab 	default:
17708a2f118eSMauro Carvalho Chehab 		err = "unknown";
17718a2f118eSMauro Carvalho Chehab 	}
17728a2f118eSMauro Carvalho Chehab 
17730975c16fSMauro Carvalho Chehab 	/*
17740975c16fSMauro Carvalho Chehab 	 * Call the helper to output message
17750975c16fSMauro Carvalho Chehab 	 * FIXME: what to do if core_err_cnt > 1? Currently, it generates
17760975c16fSMauro Carvalho Chehab 	 * only one event
17770975c16fSMauro Carvalho Chehab 	 */
17780975c16fSMauro Carvalho Chehab 	if (uncorrected_error || !pvt->is_registered)
177900d18339SMauro Carvalho Chehab 		edac_mc_handle_error(tp_event, mci, core_err_cnt,
17800975c16fSMauro Carvalho Chehab 				     m->addr >> PAGE_SHIFT,
17810975c16fSMauro Carvalho Chehab 				     m->addr & ~PAGE_MASK,
17820975c16fSMauro Carvalho Chehab 				     syndrome,
17830975c16fSMauro Carvalho Chehab 				     channel, dimm, -1,
178400d18339SMauro Carvalho Chehab 				     err, optype);
1785d5381642SMauro Carvalho Chehab }
1786d5381642SMauro Carvalho Chehab 
1787a0c36a1fSMauro Carvalho Chehab /*
178887d1d272SMauro Carvalho Chehab  *	i7core_check_error	Retrieve and process errors reported by the
178987d1d272SMauro Carvalho Chehab  *				hardware. Called by the Core module.
179087d1d272SMauro Carvalho Chehab  */
i7core_check_error(struct mem_ctl_info * mci,struct mce * m)179153595345STony Luck static void i7core_check_error(struct mem_ctl_info *mci, struct mce *m)
179287d1d272SMauro Carvalho Chehab {
1793d5381642SMauro Carvalho Chehab 	struct i7core_pvt *pvt = mci->pvt_info;
1794d5381642SMauro Carvalho Chehab 
179553595345STony Luck 	i7core_mce_output_error(mci, m);
1796d5381642SMauro Carvalho Chehab 
1797ca9c90baSMauro Carvalho Chehab 	/*
1798ca9c90baSMauro Carvalho Chehab 	 * Now, let's increment CE error counts
1799ca9c90baSMauro Carvalho Chehab 	 */
1800f4742949SMauro Carvalho Chehab 	if (!pvt->is_registered)
1801f4742949SMauro Carvalho Chehab 		i7core_udimm_check_mc_ecc_err(mci);
1802b4e8f0b6SMauro Carvalho Chehab 	else
1803f4742949SMauro Carvalho Chehab 		i7core_rdimm_check_mc_ecc_err(mci);
180487d1d272SMauro Carvalho Chehab }
180587d1d272SMauro Carvalho Chehab 
180687d1d272SMauro Carvalho Chehab /*
180753595345STony Luck  * Check that logging is enabled and that this is the right type
180853595345STony Luck  * of error for us to handle.
1809d5381642SMauro Carvalho Chehab  */
i7core_mce_check_error(struct notifier_block * nb,unsigned long val,void * data)18104140c542SBorislav Petkov static int i7core_mce_check_error(struct notifier_block *nb, unsigned long val,
18114140c542SBorislav Petkov 				  void *data)
1812d5381642SMauro Carvalho Chehab {
18134140c542SBorislav Petkov 	struct mce *mce = (struct mce *)data;
18144140c542SBorislav Petkov 	struct i7core_dev *i7_dev;
18154140c542SBorislav Petkov 	struct mem_ctl_info *mci;
18164140c542SBorislav Petkov 
18174140c542SBorislav Petkov 	i7_dev = get_i7core_dev(mce->socketid);
181823ba710aSTony Luck 	if (!i7_dev || (mce->kflags & MCE_HANDLED_CEC))
1819c4fc1956STony Luck 		return NOTIFY_DONE;
18204140c542SBorislav Petkov 
18214140c542SBorislav Petkov 	mci = i7_dev->mci;
1822d5381642SMauro Carvalho Chehab 
18238a2f118eSMauro Carvalho Chehab 	/*
18248a2f118eSMauro Carvalho Chehab 	 * Just let mcelog handle it if the error is
18258a2f118eSMauro Carvalho Chehab 	 * outside the memory controller
18268a2f118eSMauro Carvalho Chehab 	 */
18278a2f118eSMauro Carvalho Chehab 	if (((mce->status & 0xffff) >> 7) != 1)
18284140c542SBorislav Petkov 		return NOTIFY_DONE;
18298a2f118eSMauro Carvalho Chehab 
1830f237fcf2SMauro Carvalho Chehab 	/* Bank 8 registers are the only ones that we know how to handle */
1831f237fcf2SMauro Carvalho Chehab 	if (mce->bank != 8)
18324140c542SBorislav Petkov 		return NOTIFY_DONE;
1833f237fcf2SMauro Carvalho Chehab 
183453595345STony Luck 	i7core_check_error(mci, mce);
1835c5d34528SMauro Carvalho Chehab 
1836e7bf068aSDavid Sterba 	/* Advise mcelog that the errors were handled */
183723ba710aSTony Luck 	mce->kflags |= MCE_HANDLED_EDAC;
183823ba710aSTony Luck 	return NOTIFY_OK;
1839d5381642SMauro Carvalho Chehab }
1840d5381642SMauro Carvalho Chehab 
18414140c542SBorislav Petkov static struct notifier_block i7_mce_dec = {
18424140c542SBorislav Petkov 	.notifier_call	= i7core_mce_check_error,
18439026cc82SBorislav Petkov 	.priority	= MCE_PRIO_EDAC,
18444140c542SBorislav Petkov };
18454140c542SBorislav Petkov 
1846535e9c78SNils Carlson struct memdev_dmi_entry {
1847535e9c78SNils Carlson 	u8 type;
1848535e9c78SNils Carlson 	u8 length;
1849535e9c78SNils Carlson 	u16 handle;
1850535e9c78SNils Carlson 	u16 phys_mem_array_handle;
1851535e9c78SNils Carlson 	u16 mem_err_info_handle;
1852535e9c78SNils Carlson 	u16 total_width;
1853535e9c78SNils Carlson 	u16 data_width;
1854535e9c78SNils Carlson 	u16 size;
1855535e9c78SNils Carlson 	u8 form;
1856535e9c78SNils Carlson 	u8 device_set;
1857535e9c78SNils Carlson 	u8 device_locator;
1858535e9c78SNils Carlson 	u8 bank_locator;
1859535e9c78SNils Carlson 	u8 memory_type;
1860535e9c78SNils Carlson 	u16 type_detail;
1861535e9c78SNils Carlson 	u16 speed;
1862535e9c78SNils Carlson 	u8 manufacturer;
1863535e9c78SNils Carlson 	u8 serial_number;
1864535e9c78SNils Carlson 	u8 asset_tag;
1865535e9c78SNils Carlson 	u8 part_number;
1866535e9c78SNils Carlson 	u8 attributes;
1867535e9c78SNils Carlson 	u32 extended_size;
1868535e9c78SNils Carlson 	u16 conf_mem_clk_speed;
1869535e9c78SNils Carlson } __attribute__((__packed__));
1870535e9c78SNils Carlson 
1871535e9c78SNils Carlson 
1872535e9c78SNils Carlson /*
1873535e9c78SNils Carlson  * Decode the DRAM Clock Frequency, be paranoid, make sure that all
1874535e9c78SNils Carlson  * memory devices show the same speed, and if they don't then consider
1875535e9c78SNils Carlson  * all speeds to be invalid.
1876535e9c78SNils Carlson  */
decode_dclk(const struct dmi_header * dh,void * _dclk_freq)1877535e9c78SNils Carlson static void decode_dclk(const struct dmi_header *dh, void *_dclk_freq)
1878535e9c78SNils Carlson {
1879535e9c78SNils Carlson 	int *dclk_freq = _dclk_freq;
1880535e9c78SNils Carlson 	u16 dmi_mem_clk_speed;
1881535e9c78SNils Carlson 
1882535e9c78SNils Carlson 	if (*dclk_freq == -1)
1883535e9c78SNils Carlson 		return;
1884535e9c78SNils Carlson 
1885535e9c78SNils Carlson 	if (dh->type == DMI_ENTRY_MEM_DEVICE) {
1886535e9c78SNils Carlson 		struct memdev_dmi_entry *memdev_dmi_entry =
1887535e9c78SNils Carlson 			(struct memdev_dmi_entry *)dh;
1888535e9c78SNils Carlson 		unsigned long conf_mem_clk_speed_offset =
1889535e9c78SNils Carlson 			(unsigned long)&memdev_dmi_entry->conf_mem_clk_speed -
1890535e9c78SNils Carlson 			(unsigned long)&memdev_dmi_entry->type;
1891535e9c78SNils Carlson 		unsigned long speed_offset =
1892535e9c78SNils Carlson 			(unsigned long)&memdev_dmi_entry->speed -
1893535e9c78SNils Carlson 			(unsigned long)&memdev_dmi_entry->type;
1894535e9c78SNils Carlson 
1895535e9c78SNils Carlson 		/* Check that a DIMM is present */
1896535e9c78SNils Carlson 		if (memdev_dmi_entry->size == 0)
1897535e9c78SNils Carlson 			return;
1898535e9c78SNils Carlson 
1899535e9c78SNils Carlson 		/*
1900535e9c78SNils Carlson 		 * Pick the configured speed if it's available, otherwise
1901535e9c78SNils Carlson 		 * pick the DIMM speed, or we don't have a speed.
1902535e9c78SNils Carlson 		 */
1903535e9c78SNils Carlson 		if (memdev_dmi_entry->length > conf_mem_clk_speed_offset) {
1904535e9c78SNils Carlson 			dmi_mem_clk_speed =
1905535e9c78SNils Carlson 				memdev_dmi_entry->conf_mem_clk_speed;
1906535e9c78SNils Carlson 		} else if (memdev_dmi_entry->length > speed_offset) {
1907535e9c78SNils Carlson 			dmi_mem_clk_speed = memdev_dmi_entry->speed;
1908535e9c78SNils Carlson 		} else {
1909535e9c78SNils Carlson 			*dclk_freq = -1;
1910535e9c78SNils Carlson 			return;
1911535e9c78SNils Carlson 		}
1912535e9c78SNils Carlson 
1913535e9c78SNils Carlson 		if (*dclk_freq == 0) {
1914535e9c78SNils Carlson 			/* First pass, speed was 0 */
1915535e9c78SNils Carlson 			if (dmi_mem_clk_speed > 0) {
1916535e9c78SNils Carlson 				/* Set speed if a valid speed is read */
1917535e9c78SNils Carlson 				*dclk_freq = dmi_mem_clk_speed;
1918535e9c78SNils Carlson 			} else {
1919535e9c78SNils Carlson 				/* Otherwise we don't have a valid speed */
1920535e9c78SNils Carlson 				*dclk_freq = -1;
1921535e9c78SNils Carlson 			}
1922535e9c78SNils Carlson 		} else if (*dclk_freq > 0 &&
1923535e9c78SNils Carlson 			   *dclk_freq != dmi_mem_clk_speed) {
1924535e9c78SNils Carlson 			/*
1925535e9c78SNils Carlson 			 * If we have a speed, check that all DIMMS are the same
1926535e9c78SNils Carlson 			 * speed, otherwise set the speed as invalid.
1927535e9c78SNils Carlson 			 */
1928535e9c78SNils Carlson 			*dclk_freq = -1;
1929535e9c78SNils Carlson 		}
1930535e9c78SNils Carlson 	}
1931535e9c78SNils Carlson }
1932535e9c78SNils Carlson 
1933535e9c78SNils Carlson /*
1934535e9c78SNils Carlson  * The default DCLK frequency is used as a fallback if we
1935535e9c78SNils Carlson  * fail to find anything reliable in the DMI. The value
1936535e9c78SNils Carlson  * is taken straight from the datasheet.
1937535e9c78SNils Carlson  */
1938535e9c78SNils Carlson #define DEFAULT_DCLK_FREQ 800
1939535e9c78SNils Carlson 
get_dclk_freq(void)1940535e9c78SNils Carlson static int get_dclk_freq(void)
1941535e9c78SNils Carlson {
1942535e9c78SNils Carlson 	int dclk_freq = 0;
1943535e9c78SNils Carlson 
1944535e9c78SNils Carlson 	dmi_walk(decode_dclk, (void *)&dclk_freq);
1945535e9c78SNils Carlson 
1946535e9c78SNils Carlson 	if (dclk_freq < 1)
1947535e9c78SNils Carlson 		return DEFAULT_DCLK_FREQ;
1948535e9c78SNils Carlson 
1949535e9c78SNils Carlson 	return dclk_freq;
1950535e9c78SNils Carlson }
1951535e9c78SNils Carlson 
1952e8b6a127SSamuel Gabrielsson /*
1953e8b6a127SSamuel Gabrielsson  * set_sdram_scrub_rate		This routine sets byte/sec bandwidth scrub rate
1954e8b6a127SSamuel Gabrielsson  *				to hardware according to SCRUBINTERVAL formula
1955e8b6a127SSamuel Gabrielsson  *				found in datasheet.
1956e8b6a127SSamuel Gabrielsson  */
set_sdram_scrub_rate(struct mem_ctl_info * mci,u32 new_bw)1957e8b6a127SSamuel Gabrielsson static int set_sdram_scrub_rate(struct mem_ctl_info *mci, u32 new_bw)
1958e8b6a127SSamuel Gabrielsson {
1959e8b6a127SSamuel Gabrielsson 	struct i7core_pvt *pvt = mci->pvt_info;
1960e8b6a127SSamuel Gabrielsson 	struct pci_dev *pdev;
1961e8b6a127SSamuel Gabrielsson 	u32 dw_scrub;
1962e8b6a127SSamuel Gabrielsson 	u32 dw_ssr;
1963e8b6a127SSamuel Gabrielsson 
1964e8b6a127SSamuel Gabrielsson 	/* Get data from the MC register, function 2 */
1965e8b6a127SSamuel Gabrielsson 	pdev = pvt->pci_mcr[2];
1966e8b6a127SSamuel Gabrielsson 	if (!pdev)
1967e8b6a127SSamuel Gabrielsson 		return -ENODEV;
1968e8b6a127SSamuel Gabrielsson 
1969e8b6a127SSamuel Gabrielsson 	pci_read_config_dword(pdev, MC_SCRUB_CONTROL, &dw_scrub);
1970e8b6a127SSamuel Gabrielsson 
1971e8b6a127SSamuel Gabrielsson 	if (new_bw == 0) {
1972e8b6a127SSamuel Gabrielsson 		/* Prepare to disable petrol scrub */
1973e8b6a127SSamuel Gabrielsson 		dw_scrub &= ~STARTSCRUB;
1974e8b6a127SSamuel Gabrielsson 		/* Stop the patrol scrub engine */
1975535e9c78SNils Carlson 		write_and_test(pdev, MC_SCRUB_CONTROL,
1976535e9c78SNils Carlson 			       dw_scrub & ~SCRUBINTERVAL_MASK);
1977e8b6a127SSamuel Gabrielsson 
1978e8b6a127SSamuel Gabrielsson 		/* Get current status of scrub rate and set bit to disable */
1979e8b6a127SSamuel Gabrielsson 		pci_read_config_dword(pdev, MC_SSRCONTROL, &dw_ssr);
1980e8b6a127SSamuel Gabrielsson 		dw_ssr &= ~SSR_MODE_MASK;
1981e8b6a127SSamuel Gabrielsson 		dw_ssr |= SSR_MODE_DISABLE;
1982e8b6a127SSamuel Gabrielsson 	} else {
1983535e9c78SNils Carlson 		const int cache_line_size = 64;
1984535e9c78SNils Carlson 		const u32 freq_dclk_mhz = pvt->dclk_freq;
1985535e9c78SNils Carlson 		unsigned long long scrub_interval;
1986e8b6a127SSamuel Gabrielsson 		/*
1987e8b6a127SSamuel Gabrielsson 		 * Translate the desired scrub rate to a register value and
1988535e9c78SNils Carlson 		 * program the corresponding register value.
1989e8b6a127SSamuel Gabrielsson 		 */
1990535e9c78SNils Carlson 		scrub_interval = (unsigned long long)freq_dclk_mhz *
19914fad8098SSedat Dilek 			cache_line_size * 1000000;
19924fad8098SSedat Dilek 		do_div(scrub_interval, new_bw);
1993535e9c78SNils Carlson 
1994535e9c78SNils Carlson 		if (!scrub_interval || scrub_interval > SCRUBINTERVAL_MASK)
1995535e9c78SNils Carlson 			return -EINVAL;
1996535e9c78SNils Carlson 
1997535e9c78SNils Carlson 		dw_scrub = SCRUBINTERVAL_MASK & scrub_interval;
1998e8b6a127SSamuel Gabrielsson 
1999e8b6a127SSamuel Gabrielsson 		/* Start the patrol scrub engine */
2000e8b6a127SSamuel Gabrielsson 		pci_write_config_dword(pdev, MC_SCRUB_CONTROL,
2001e8b6a127SSamuel Gabrielsson 				       STARTSCRUB | dw_scrub);
2002e8b6a127SSamuel Gabrielsson 
2003e8b6a127SSamuel Gabrielsson 		/* Get current status of scrub rate and set bit to enable */
2004e8b6a127SSamuel Gabrielsson 		pci_read_config_dword(pdev, MC_SSRCONTROL, &dw_ssr);
2005e8b6a127SSamuel Gabrielsson 		dw_ssr &= ~SSR_MODE_MASK;
2006e8b6a127SSamuel Gabrielsson 		dw_ssr |= SSR_MODE_ENABLE;
2007e8b6a127SSamuel Gabrielsson 	}
2008e8b6a127SSamuel Gabrielsson 	/* Disable or enable scrubbing */
2009e8b6a127SSamuel Gabrielsson 	pci_write_config_dword(pdev, MC_SSRCONTROL, dw_ssr);
2010e8b6a127SSamuel Gabrielsson 
2011e8b6a127SSamuel Gabrielsson 	return new_bw;
2012e8b6a127SSamuel Gabrielsson }
2013e8b6a127SSamuel Gabrielsson 
2014e8b6a127SSamuel Gabrielsson /*
2015e8b6a127SSamuel Gabrielsson  * get_sdram_scrub_rate		This routine convert current scrub rate value
201615ed103aSDavid Mackey  *				into byte/sec bandwidth according to
2017e8b6a127SSamuel Gabrielsson  *				SCRUBINTERVAL formula found in datasheet.
2018e8b6a127SSamuel Gabrielsson  */
get_sdram_scrub_rate(struct mem_ctl_info * mci)2019e8b6a127SSamuel Gabrielsson static int get_sdram_scrub_rate(struct mem_ctl_info *mci)
2020e8b6a127SSamuel Gabrielsson {
2021e8b6a127SSamuel Gabrielsson 	struct i7core_pvt *pvt = mci->pvt_info;
2022e8b6a127SSamuel Gabrielsson 	struct pci_dev *pdev;
2023e8b6a127SSamuel Gabrielsson 	const u32 cache_line_size = 64;
2024535e9c78SNils Carlson 	const u32 freq_dclk_mhz = pvt->dclk_freq;
2025535e9c78SNils Carlson 	unsigned long long scrub_rate;
2026e8b6a127SSamuel Gabrielsson 	u32 scrubval;
2027e8b6a127SSamuel Gabrielsson 
2028e8b6a127SSamuel Gabrielsson 	/* Get data from the MC register, function 2 */
2029e8b6a127SSamuel Gabrielsson 	pdev = pvt->pci_mcr[2];
2030e8b6a127SSamuel Gabrielsson 	if (!pdev)
2031e8b6a127SSamuel Gabrielsson 		return -ENODEV;
2032e8b6a127SSamuel Gabrielsson 
2033e8b6a127SSamuel Gabrielsson 	/* Get current scrub control data */
2034e8b6a127SSamuel Gabrielsson 	pci_read_config_dword(pdev, MC_SCRUB_CONTROL, &scrubval);
2035e8b6a127SSamuel Gabrielsson 
2036e8b6a127SSamuel Gabrielsson 	/* Mask highest 8-bits to 0 */
2037535e9c78SNils Carlson 	scrubval &=  SCRUBINTERVAL_MASK;
2038e8b6a127SSamuel Gabrielsson 	if (!scrubval)
2039e8b6a127SSamuel Gabrielsson 		return 0;
2040e8b6a127SSamuel Gabrielsson 
2041e8b6a127SSamuel Gabrielsson 	/* Calculate scrub rate value into byte/sec bandwidth */
2042535e9c78SNils Carlson 	scrub_rate =  (unsigned long long)freq_dclk_mhz *
20434fad8098SSedat Dilek 		1000000 * cache_line_size;
20444fad8098SSedat Dilek 	do_div(scrub_rate, scrubval);
2045535e9c78SNils Carlson 	return (int)scrub_rate;
2046e8b6a127SSamuel Gabrielsson }
2047e8b6a127SSamuel Gabrielsson 
enable_sdram_scrub_setting(struct mem_ctl_info * mci)2048e8b6a127SSamuel Gabrielsson static void enable_sdram_scrub_setting(struct mem_ctl_info *mci)
2049e8b6a127SSamuel Gabrielsson {
2050e8b6a127SSamuel Gabrielsson 	struct i7core_pvt *pvt = mci->pvt_info;
2051e8b6a127SSamuel Gabrielsson 	u32 pci_lock;
2052e8b6a127SSamuel Gabrielsson 
2053e8b6a127SSamuel Gabrielsson 	/* Unlock writes to pci registers */
2054e8b6a127SSamuel Gabrielsson 	pci_read_config_dword(pvt->pci_noncore, MC_CFG_CONTROL, &pci_lock);
2055e8b6a127SSamuel Gabrielsson 	pci_lock &= ~0x3;
2056e8b6a127SSamuel Gabrielsson 	pci_write_config_dword(pvt->pci_noncore, MC_CFG_CONTROL,
2057e8b6a127SSamuel Gabrielsson 			       pci_lock | MC_CFG_UNLOCK);
2058e8b6a127SSamuel Gabrielsson 
2059e8b6a127SSamuel Gabrielsson 	mci->set_sdram_scrub_rate = set_sdram_scrub_rate;
2060e8b6a127SSamuel Gabrielsson 	mci->get_sdram_scrub_rate = get_sdram_scrub_rate;
2061e8b6a127SSamuel Gabrielsson }
2062e8b6a127SSamuel Gabrielsson 
disable_sdram_scrub_setting(struct mem_ctl_info * mci)2063e8b6a127SSamuel Gabrielsson static void disable_sdram_scrub_setting(struct mem_ctl_info *mci)
2064e8b6a127SSamuel Gabrielsson {
2065e8b6a127SSamuel Gabrielsson 	struct i7core_pvt *pvt = mci->pvt_info;
2066e8b6a127SSamuel Gabrielsson 	u32 pci_lock;
2067e8b6a127SSamuel Gabrielsson 
2068e8b6a127SSamuel Gabrielsson 	/* Lock writes to pci registers */
2069e8b6a127SSamuel Gabrielsson 	pci_read_config_dword(pvt->pci_noncore, MC_CFG_CONTROL, &pci_lock);
2070e8b6a127SSamuel Gabrielsson 	pci_lock &= ~0x3;
2071e8b6a127SSamuel Gabrielsson 	pci_write_config_dword(pvt->pci_noncore, MC_CFG_CONTROL,
2072e8b6a127SSamuel Gabrielsson 			       pci_lock | MC_CFG_LOCK);
2073e8b6a127SSamuel Gabrielsson }
2074e8b6a127SSamuel Gabrielsson 
i7core_pci_ctl_create(struct i7core_pvt * pvt)2075a3aa0a4aSHidetoshi Seto static void i7core_pci_ctl_create(struct i7core_pvt *pvt)
2076a3aa0a4aSHidetoshi Seto {
2077a3aa0a4aSHidetoshi Seto 	pvt->i7core_pci = edac_pci_create_generic_ctl(
2078a3aa0a4aSHidetoshi Seto 						&pvt->i7core_dev->pdev[0]->dev,
2079a3aa0a4aSHidetoshi Seto 						EDAC_MOD_STR);
2080a3aa0a4aSHidetoshi Seto 	if (unlikely(!pvt->i7core_pci))
2081f9902f24SMauro Carvalho Chehab 		i7core_printk(KERN_WARNING,
2082f9902f24SMauro Carvalho Chehab 			      "Unable to setup PCI error report via EDAC\n");
2083a3aa0a4aSHidetoshi Seto }
2084a3aa0a4aSHidetoshi Seto 
i7core_pci_ctl_release(struct i7core_pvt * pvt)2085a3aa0a4aSHidetoshi Seto static void i7core_pci_ctl_release(struct i7core_pvt *pvt)
2086a3aa0a4aSHidetoshi Seto {
2087a3aa0a4aSHidetoshi Seto 	if (likely(pvt->i7core_pci))
2088a3aa0a4aSHidetoshi Seto 		edac_pci_release_generic_ctl(pvt->i7core_pci);
2089a3aa0a4aSHidetoshi Seto 	else
2090a3aa0a4aSHidetoshi Seto 		i7core_printk(KERN_ERR,
2091a3aa0a4aSHidetoshi Seto 				"Couldn't find mem_ctl_info for socket %d\n",
2092a3aa0a4aSHidetoshi Seto 				pvt->i7core_dev->socket);
2093a3aa0a4aSHidetoshi Seto 	pvt->i7core_pci = NULL;
2094a3aa0a4aSHidetoshi Seto }
2095a3aa0a4aSHidetoshi Seto 
i7core_unregister_mci(struct i7core_dev * i7core_dev)20961c6edbbeSHidetoshi Seto static void i7core_unregister_mci(struct i7core_dev *i7core_dev)
20971c6edbbeSHidetoshi Seto {
20981c6edbbeSHidetoshi Seto 	struct mem_ctl_info *mci = i7core_dev->mci;
20991c6edbbeSHidetoshi Seto 	struct i7core_pvt *pvt;
21001c6edbbeSHidetoshi Seto 
21011c6edbbeSHidetoshi Seto 	if (unlikely(!mci || !mci->pvt_info)) {
2102956b9ba1SJoe Perches 		edac_dbg(0, "MC: dev = %p\n", &i7core_dev->pdev[0]->dev);
21031c6edbbeSHidetoshi Seto 
21041c6edbbeSHidetoshi Seto 		i7core_printk(KERN_ERR, "Couldn't find mci handler\n");
21051c6edbbeSHidetoshi Seto 		return;
21061c6edbbeSHidetoshi Seto 	}
21071c6edbbeSHidetoshi Seto 
21081c6edbbeSHidetoshi Seto 	pvt = mci->pvt_info;
21091c6edbbeSHidetoshi Seto 
2110956b9ba1SJoe Perches 	edac_dbg(0, "MC: mci = %p, dev = %p\n", mci, &i7core_dev->pdev[0]->dev);
21111c6edbbeSHidetoshi Seto 
2112e8b6a127SSamuel Gabrielsson 	/* Disable scrubrate setting */
211327100db0SMauro Carvalho Chehab 	if (pvt->enable_scrub)
2114e8b6a127SSamuel Gabrielsson 		disable_sdram_scrub_setting(mci);
2115e8b6a127SSamuel Gabrielsson 
21161c6edbbeSHidetoshi Seto 	/* Disable EDAC polling */
21171c6edbbeSHidetoshi Seto 	i7core_pci_ctl_release(pvt);
21181c6edbbeSHidetoshi Seto 
21191c6edbbeSHidetoshi Seto 	/* Remove MC sysfs nodes */
21205c4cdb5aSMauro Carvalho Chehab 	i7core_delete_sysfs_devices(mci);
2121fd687502SMauro Carvalho Chehab 	edac_mc_del_mc(mci->pdev);
21221c6edbbeSHidetoshi Seto 
2123956b9ba1SJoe Perches 	edac_dbg(1, "%s: free mci struct\n", mci->ctl_name);
21241c6edbbeSHidetoshi Seto 	kfree(mci->ctl_name);
21251c6edbbeSHidetoshi Seto 	edac_mc_free(mci);
21261c6edbbeSHidetoshi Seto 	i7core_dev->mci = NULL;
21271c6edbbeSHidetoshi Seto }
21281c6edbbeSHidetoshi Seto 
i7core_register_mci(struct i7core_dev * i7core_dev)2129aace4283SHidetoshi Seto static int i7core_register_mci(struct i7core_dev *i7core_dev)
2130a0c36a1fSMauro Carvalho Chehab {
2131a0c36a1fSMauro Carvalho Chehab 	struct mem_ctl_info *mci;
2132a0c36a1fSMauro Carvalho Chehab 	struct i7core_pvt *pvt;
21330975c16fSMauro Carvalho Chehab 	int rc;
21340975c16fSMauro Carvalho Chehab 	struct edac_mc_layer layers[2];
213567166af4SMauro Carvalho Chehab 
2136a0c36a1fSMauro Carvalho Chehab 	/* allocate a new MC control structure */
21370975c16fSMauro Carvalho Chehab 
21380975c16fSMauro Carvalho Chehab 	layers[0].type = EDAC_MC_LAYER_CHANNEL;
21390975c16fSMauro Carvalho Chehab 	layers[0].size = NUM_CHANS;
21400975c16fSMauro Carvalho Chehab 	layers[0].is_virt_csrow = false;
21410975c16fSMauro Carvalho Chehab 	layers[1].type = EDAC_MC_LAYER_SLOT;
21420975c16fSMauro Carvalho Chehab 	layers[1].size = MAX_DIMMS;
21430975c16fSMauro Carvalho Chehab 	layers[1].is_virt_csrow = true;
2144ca0907b9SMauro Carvalho Chehab 	mci = edac_mc_alloc(i7core_dev->socket, ARRAY_SIZE(layers), layers,
21450975c16fSMauro Carvalho Chehab 			    sizeof(*pvt));
2146f4742949SMauro Carvalho Chehab 	if (unlikely(!mci))
2147f4742949SMauro Carvalho Chehab 		return -ENOMEM;
2148a0c36a1fSMauro Carvalho Chehab 
2149956b9ba1SJoe Perches 	edac_dbg(0, "MC: mci = %p, dev = %p\n", mci, &i7core_dev->pdev[0]->dev);
2150a0c36a1fSMauro Carvalho Chehab 
2151a0c36a1fSMauro Carvalho Chehab 	pvt = mci->pvt_info;
2152ef708b53SMauro Carvalho Chehab 	memset(pvt, 0, sizeof(*pvt));
215367166af4SMauro Carvalho Chehab 
21546d37d240SMauro Carvalho Chehab 	/* Associates i7core_dev and mci for future usage */
21556d37d240SMauro Carvalho Chehab 	pvt->i7core_dev = i7core_dev;
21566d37d240SMauro Carvalho Chehab 	i7core_dev->mci = mci;
21576d37d240SMauro Carvalho Chehab 
215841fcb7feSMauro Carvalho Chehab 	/*
215941fcb7feSMauro Carvalho Chehab 	 * FIXME: how to handle RDDR3 at MCI level? It is possible to have
216041fcb7feSMauro Carvalho Chehab 	 * Mixed RDDR3/UDDR3 with Nehalem, provided that they are on different
216141fcb7feSMauro Carvalho Chehab 	 * memory channels
216241fcb7feSMauro Carvalho Chehab 	 */
216341fcb7feSMauro Carvalho Chehab 	mci->mtype_cap = MEM_FLAG_DDR3;
2164a0c36a1fSMauro Carvalho Chehab 	mci->edac_ctl_cap = EDAC_FLAG_NONE;
2165a0c36a1fSMauro Carvalho Chehab 	mci->edac_cap = EDAC_FLAG_NONE;
2166a0c36a1fSMauro Carvalho Chehab 	mci->mod_name = "i7core_edac.c";
216775f029c3SArvind Yadav 
216875f029c3SArvind Yadav 	mci->ctl_name = kasprintf(GFP_KERNEL, "i7 core #%d", i7core_dev->socket);
216975f029c3SArvind Yadav 	if (!mci->ctl_name) {
217075f029c3SArvind Yadav 		rc = -ENOMEM;
217175f029c3SArvind Yadav 		goto fail1;
217275f029c3SArvind Yadav 	}
217375f029c3SArvind Yadav 
2174f4742949SMauro Carvalho Chehab 	mci->dev_name = pci_name(i7core_dev->pdev[0]);
2175a0c36a1fSMauro Carvalho Chehab 	mci->ctl_page_to_phys = NULL;
21761288c18fSMauro Carvalho Chehab 
2177ef708b53SMauro Carvalho Chehab 	/* Store pci devices at mci for faster access */
2178f4742949SMauro Carvalho Chehab 	rc = mci_bind_devs(mci, i7core_dev);
2179b7c76151SMauro Carvalho Chehab 	if (unlikely(rc < 0))
2180628c5ddfSHidetoshi Seto 		goto fail0;
2181ef708b53SMauro Carvalho Chehab 
21825939813bSHidetoshi Seto 
2183ef708b53SMauro Carvalho Chehab 	/* Get dimm basic config */
21842e5185f7SHidetoshi Seto 	get_dimm_config(mci);
21855939813bSHidetoshi Seto 	/* record ptr to the generic device */
2186fd687502SMauro Carvalho Chehab 	mci->pdev = &i7core_dev->pdev[0]->dev;
2187ef708b53SMauro Carvalho Chehab 
2188e8b6a127SSamuel Gabrielsson 	/* Enable scrubrate setting */
218927100db0SMauro Carvalho Chehab 	if (pvt->enable_scrub)
2190e8b6a127SSamuel Gabrielsson 		enable_sdram_scrub_setting(mci);
2191e8b6a127SSamuel Gabrielsson 
2192a0c36a1fSMauro Carvalho Chehab 	/* add this new MC control structure to EDAC's list of MCs */
21932eace188STakashi Iwai 	if (unlikely(edac_mc_add_mc_with_groups(mci, i7core_dev_groups))) {
2194956b9ba1SJoe Perches 		edac_dbg(0, "MC: failed edac_mc_add_mc()\n");
2195a0c36a1fSMauro Carvalho Chehab 		/* FIXME: perhaps some code should go here that disables error
2196a0c36a1fSMauro Carvalho Chehab 		 * reporting if we just enabled it
2197a0c36a1fSMauro Carvalho Chehab 		 */
2198b7c76151SMauro Carvalho Chehab 
2199b7c76151SMauro Carvalho Chehab 		rc = -EINVAL;
2200628c5ddfSHidetoshi Seto 		goto fail0;
2201a0c36a1fSMauro Carvalho Chehab 	}
22025c4cdb5aSMauro Carvalho Chehab 	if (i7core_create_sysfs_devices(mci)) {
2203956b9ba1SJoe Perches 		edac_dbg(0, "MC: failed to create sysfs nodes\n");
22045c4cdb5aSMauro Carvalho Chehab 		edac_mc_del_mc(mci->pdev);
22055c4cdb5aSMauro Carvalho Chehab 		rc = -EINVAL;
22065c4cdb5aSMauro Carvalho Chehab 		goto fail0;
22075c4cdb5aSMauro Carvalho Chehab 	}
2208a0c36a1fSMauro Carvalho Chehab 
2209194a40feSMauro Carvalho Chehab 	/* Default error mask is any memory */
2210ef708b53SMauro Carvalho Chehab 	pvt->inject.channel = 0;
2211194a40feSMauro Carvalho Chehab 	pvt->inject.dimm = -1;
2212194a40feSMauro Carvalho Chehab 	pvt->inject.rank = -1;
2213194a40feSMauro Carvalho Chehab 	pvt->inject.bank = -1;
2214194a40feSMauro Carvalho Chehab 	pvt->inject.page = -1;
2215194a40feSMauro Carvalho Chehab 	pvt->inject.col = -1;
2216194a40feSMauro Carvalho Chehab 
2217a3aa0a4aSHidetoshi Seto 	/* allocating generic PCI control info */
2218a3aa0a4aSHidetoshi Seto 	i7core_pci_ctl_create(pvt);
2219a3aa0a4aSHidetoshi Seto 
2220535e9c78SNils Carlson 	/* DCLK for scrub rate setting */
2221535e9c78SNils Carlson 	pvt->dclk_freq = get_dclk_freq();
2222535e9c78SNils Carlson 
2223628c5ddfSHidetoshi Seto 	return 0;
2224628c5ddfSHidetoshi Seto 
2225628c5ddfSHidetoshi Seto fail0:
2226628c5ddfSHidetoshi Seto 	kfree(mci->ctl_name);
222775f029c3SArvind Yadav 
222875f029c3SArvind Yadav fail1:
2229f4742949SMauro Carvalho Chehab 	edac_mc_free(mci);
22301c6edbbeSHidetoshi Seto 	i7core_dev->mci = NULL;
2231f4742949SMauro Carvalho Chehab 	return rc;
2232f4742949SMauro Carvalho Chehab }
2233f4742949SMauro Carvalho Chehab 
2234f4742949SMauro Carvalho Chehab /*
2235f4742949SMauro Carvalho Chehab  *	i7core_probe	Probe for ONE instance of device to see if it is
2236f4742949SMauro Carvalho Chehab  *			present.
2237f4742949SMauro Carvalho Chehab  *	return:
2238f4742949SMauro Carvalho Chehab  *		0 for FOUND a device
2239f4742949SMauro Carvalho Chehab  *		< 0 for error code
2240f4742949SMauro Carvalho Chehab  */
22412d95d815SMauro Carvalho Chehab 
i7core_probe(struct pci_dev * pdev,const struct pci_device_id * id)22429b3c6e85SGreg Kroah-Hartman static int i7core_probe(struct pci_dev *pdev, const struct pci_device_id *id)
2243f4742949SMauro Carvalho Chehab {
224440557591SMauro Carvalho Chehab 	int rc, count = 0;
2245f4742949SMauro Carvalho Chehab 	struct i7core_dev *i7core_dev;
2246f4742949SMauro Carvalho Chehab 
22472d95d815SMauro Carvalho Chehab 	/* get the pci devices we want to reserve for our use */
22482d95d815SMauro Carvalho Chehab 	mutex_lock(&i7core_edac_lock);
22492d95d815SMauro Carvalho Chehab 
2250f4742949SMauro Carvalho Chehab 	/*
2251d4c27795SMauro Carvalho Chehab 	 * All memory controllers are allocated at the first pass.
2252f4742949SMauro Carvalho Chehab 	 */
22532d95d815SMauro Carvalho Chehab 	if (unlikely(probed >= 1)) {
22542d95d815SMauro Carvalho Chehab 		mutex_unlock(&i7core_edac_lock);
225576a7bd81SMauro Carvalho Chehab 		return -ENODEV;
22562d95d815SMauro Carvalho Chehab 	}
22572d95d815SMauro Carvalho Chehab 	probed++;
2258de06eeefSMauro Carvalho Chehab 
225964c10f6eSHidetoshi Seto 	rc = i7core_get_all_devices();
2260f4742949SMauro Carvalho Chehab 	if (unlikely(rc < 0))
2261f4742949SMauro Carvalho Chehab 		goto fail0;
2262f4742949SMauro Carvalho Chehab 
2263f4742949SMauro Carvalho Chehab 	list_for_each_entry(i7core_dev, &i7core_edac_list, list) {
226440557591SMauro Carvalho Chehab 		count++;
2265aace4283SHidetoshi Seto 		rc = i7core_register_mci(i7core_dev);
2266d4c27795SMauro Carvalho Chehab 		if (unlikely(rc < 0))
2267d4c27795SMauro Carvalho Chehab 			goto fail1;
2268d5381642SMauro Carvalho Chehab 	}
2269d5381642SMauro Carvalho Chehab 
227040557591SMauro Carvalho Chehab 	/*
227140557591SMauro Carvalho Chehab 	 * Nehalem-EX uses a different memory controller. However, as the
227240557591SMauro Carvalho Chehab 	 * memory controller is not visible on some Nehalem/Nehalem-EP, we
227340557591SMauro Carvalho Chehab 	 * need to indirectly probe via a X58 PCI device. The same devices
227440557591SMauro Carvalho Chehab 	 * are found on (some) Nehalem-EX. So, on those machines, the
227540557591SMauro Carvalho Chehab 	 * probe routine needs to return -ENODEV, as the actual Memory
227640557591SMauro Carvalho Chehab 	 * Controller registers won't be detected.
227740557591SMauro Carvalho Chehab 	 */
227840557591SMauro Carvalho Chehab 	if (!count) {
227940557591SMauro Carvalho Chehab 		rc = -ENODEV;
228040557591SMauro Carvalho Chehab 		goto fail1;
228140557591SMauro Carvalho Chehab 	}
228240557591SMauro Carvalho Chehab 
228340557591SMauro Carvalho Chehab 	i7core_printk(KERN_INFO,
228440557591SMauro Carvalho Chehab 		      "Driver loaded, %d memory controller(s) found.\n",
228540557591SMauro Carvalho Chehab 		      count);
22868f331907SMauro Carvalho Chehab 
228766607706SMauro Carvalho Chehab 	mutex_unlock(&i7core_edac_lock);
2288a0c36a1fSMauro Carvalho Chehab 	return 0;
2289a0c36a1fSMauro Carvalho Chehab 
229066607706SMauro Carvalho Chehab fail1:
229188ef5ea9SMauro Carvalho Chehab 	list_for_each_entry(i7core_dev, &i7core_edac_list, list)
22921c6edbbeSHidetoshi Seto 		i7core_unregister_mci(i7core_dev);
229388ef5ea9SMauro Carvalho Chehab 
229413d6e9b6SMauro Carvalho Chehab 	i7core_put_all_devices();
229566607706SMauro Carvalho Chehab fail0:
229666607706SMauro Carvalho Chehab 	mutex_unlock(&i7core_edac_lock);
2297b7c76151SMauro Carvalho Chehab 	return rc;
2298a0c36a1fSMauro Carvalho Chehab }
2299a0c36a1fSMauro Carvalho Chehab 
2300a0c36a1fSMauro Carvalho Chehab /*
2301a0c36a1fSMauro Carvalho Chehab  *	i7core_remove	destructor for one instance of device
2302a0c36a1fSMauro Carvalho Chehab  *
2303a0c36a1fSMauro Carvalho Chehab  */
i7core_remove(struct pci_dev * pdev)23049b3c6e85SGreg Kroah-Hartman static void i7core_remove(struct pci_dev *pdev)
2305a0c36a1fSMauro Carvalho Chehab {
230664c10f6eSHidetoshi Seto 	struct i7core_dev *i7core_dev;
2307a0c36a1fSMauro Carvalho Chehab 
2308956b9ba1SJoe Perches 	edac_dbg(0, "\n");
2309a0c36a1fSMauro Carvalho Chehab 
231022e6bcbdSMauro Carvalho Chehab 	/*
231122e6bcbdSMauro Carvalho Chehab 	 * we have a trouble here: pdev value for removal will be wrong, since
231222e6bcbdSMauro Carvalho Chehab 	 * it will point to the X58 register used to detect that the machine
231322e6bcbdSMauro Carvalho Chehab 	 * is a Nehalem or upper design. However, due to the way several PCI
231422e6bcbdSMauro Carvalho Chehab 	 * devices are grouped together to provide MC functionality, we need
231522e6bcbdSMauro Carvalho Chehab 	 * to use a different method for releasing the devices
231622e6bcbdSMauro Carvalho Chehab 	 */
231787d1d272SMauro Carvalho Chehab 
231822e6bcbdSMauro Carvalho Chehab 	mutex_lock(&i7core_edac_lock);
231971fe0170SHidetoshi Seto 
232071fe0170SHidetoshi Seto 	if (unlikely(!probed)) {
232171fe0170SHidetoshi Seto 		mutex_unlock(&i7core_edac_lock);
232271fe0170SHidetoshi Seto 		return;
232371fe0170SHidetoshi Seto 	}
232471fe0170SHidetoshi Seto 
232588ef5ea9SMauro Carvalho Chehab 	list_for_each_entry(i7core_dev, &i7core_edac_list, list)
23261c6edbbeSHidetoshi Seto 		i7core_unregister_mci(i7core_dev);
232741ba6c10SMauro Carvalho Chehab 
232841ba6c10SMauro Carvalho Chehab 	/* Release PCI resources */
232964c10f6eSHidetoshi Seto 	i7core_put_all_devices();
233064c10f6eSHidetoshi Seto 
23312d95d815SMauro Carvalho Chehab 	probed--;
23322d95d815SMauro Carvalho Chehab 
233322e6bcbdSMauro Carvalho Chehab 	mutex_unlock(&i7core_edac_lock);
2334a0c36a1fSMauro Carvalho Chehab }
2335a0c36a1fSMauro Carvalho Chehab 
2336a0c36a1fSMauro Carvalho Chehab MODULE_DEVICE_TABLE(pci, i7core_pci_tbl);
2337a0c36a1fSMauro Carvalho Chehab 
2338a0c36a1fSMauro Carvalho Chehab /*
2339a0c36a1fSMauro Carvalho Chehab  *	i7core_driver	pci_driver structure for this module
2340a0c36a1fSMauro Carvalho Chehab  *
2341a0c36a1fSMauro Carvalho Chehab  */
2342a0c36a1fSMauro Carvalho Chehab static struct pci_driver i7core_driver = {
2343a0c36a1fSMauro Carvalho Chehab 	.name     = "i7core_edac",
2344a0c36a1fSMauro Carvalho Chehab 	.probe    = i7core_probe,
23459b3c6e85SGreg Kroah-Hartman 	.remove   = i7core_remove,
2346a0c36a1fSMauro Carvalho Chehab 	.id_table = i7core_pci_tbl,
2347a0c36a1fSMauro Carvalho Chehab };
2348a0c36a1fSMauro Carvalho Chehab 
2349a0c36a1fSMauro Carvalho Chehab /*
2350a0c36a1fSMauro Carvalho Chehab  *	i7core_init		Module entry function
2351a0c36a1fSMauro Carvalho Chehab  *			Try to initialize this module for its devices
2352a0c36a1fSMauro Carvalho Chehab  */
i7core_init(void)2353a0c36a1fSMauro Carvalho Chehab static int __init i7core_init(void)
2354a0c36a1fSMauro Carvalho Chehab {
2355a0c36a1fSMauro Carvalho Chehab 	int pci_rc;
2356a0c36a1fSMauro Carvalho Chehab 
2357956b9ba1SJoe Perches 	edac_dbg(2, "\n");
2358a0c36a1fSMauro Carvalho Chehab 
2359a0c36a1fSMauro Carvalho Chehab 	/* Ensure that the OPSTATE is set correctly for POLL or NMI */
2360a0c36a1fSMauro Carvalho Chehab 	opstate_init();
2361a0c36a1fSMauro Carvalho Chehab 
236254a08ab1SMauro Carvalho Chehab 	if (use_pci_fixup)
2363bd9e19caSVernon Mauery 		i7core_xeon_pci_fixup(pci_dev_table);
2364bc2d7245SKeith Mannthey 
2365a0c36a1fSMauro Carvalho Chehab 	pci_rc = pci_register_driver(&i7core_driver);
2366a0c36a1fSMauro Carvalho Chehab 
2367e35fca47SChen Gong 	if (pci_rc >= 0) {
2368e35fca47SChen Gong 		mce_register_decode_chain(&i7_mce_dec);
23693ef288a9SMauro Carvalho Chehab 		return 0;
2370e35fca47SChen Gong 	}
23713ef288a9SMauro Carvalho Chehab 
23723ef288a9SMauro Carvalho Chehab 	i7core_printk(KERN_ERR, "Failed to register device with error %d.\n",
23733ef288a9SMauro Carvalho Chehab 		      pci_rc);
23743ef288a9SMauro Carvalho Chehab 
23753ef288a9SMauro Carvalho Chehab 	return pci_rc;
2376a0c36a1fSMauro Carvalho Chehab }
2377a0c36a1fSMauro Carvalho Chehab 
2378a0c36a1fSMauro Carvalho Chehab /*
2379a0c36a1fSMauro Carvalho Chehab  *	i7core_exit()	Module exit function
2380a0c36a1fSMauro Carvalho Chehab  *			Unregister the driver
2381a0c36a1fSMauro Carvalho Chehab  */
i7core_exit(void)2382a0c36a1fSMauro Carvalho Chehab static void __exit i7core_exit(void)
2383a0c36a1fSMauro Carvalho Chehab {
2384956b9ba1SJoe Perches 	edac_dbg(2, "\n");
2385a0c36a1fSMauro Carvalho Chehab 	pci_unregister_driver(&i7core_driver);
2386e35fca47SChen Gong 	mce_unregister_decode_chain(&i7_mce_dec);
2387a0c36a1fSMauro Carvalho Chehab }
2388a0c36a1fSMauro Carvalho Chehab 
2389a0c36a1fSMauro Carvalho Chehab module_init(i7core_init);
2390a0c36a1fSMauro Carvalho Chehab module_exit(i7core_exit);
2391a0c36a1fSMauro Carvalho Chehab 
2392a0c36a1fSMauro Carvalho Chehab MODULE_LICENSE("GPL");
239337e59f87SMauro Carvalho Chehab MODULE_AUTHOR("Mauro Carvalho Chehab");
2394a0c36a1fSMauro Carvalho Chehab MODULE_AUTHOR("Red Hat Inc. (https://www.redhat.com)");
2395a0c36a1fSMauro Carvalho Chehab MODULE_DESCRIPTION("MC Driver for Intel i7 Core memory controllers - "
2396a0c36a1fSMauro Carvalho Chehab 		   I7CORE_REVISION);
2397a0c36a1fSMauro Carvalho Chehab 
2398a0c36a1fSMauro Carvalho Chehab module_param(edac_op_state, int, 0444);
2399a0c36a1fSMauro Carvalho Chehab MODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI");
2400