xref: /openbmc/linux/drivers/edac/i5400_edac.c (revision 7ae9fb1b7ecbb5d85d07857943f677fd1a559b18)
1920c8df6SMauro Carvalho Chehab /*
28375d490SMauro Carvalho Chehab  * Intel 5400 class Memory Controllers kernel module (Seaburg)
3920c8df6SMauro Carvalho Chehab  *
4920c8df6SMauro Carvalho Chehab  * This file may be distributed under the terms of the
5920c8df6SMauro Carvalho Chehab  * GNU General Public License.
6920c8df6SMauro Carvalho Chehab  *
7920c8df6SMauro Carvalho Chehab  * Copyright (c) 2008 by:
8920c8df6SMauro Carvalho Chehab  *	 Ben Woodard <woodard@redhat.com>
937e59f87SMauro Carvalho Chehab  *	 Mauro Carvalho Chehab
10920c8df6SMauro Carvalho Chehab  *
117d4c1ea2SAlexander A. Klimov  * Red Hat Inc. https://www.redhat.com
12920c8df6SMauro Carvalho Chehab  *
13920c8df6SMauro Carvalho Chehab  * Forked and adapted from the i5000_edac driver which was
14920c8df6SMauro Carvalho Chehab  * written by Douglas Thompson Linux Networx <norsk5@xmission.com>
15920c8df6SMauro Carvalho Chehab  *
16920c8df6SMauro Carvalho Chehab  * This module is based on the following document:
17920c8df6SMauro Carvalho Chehab  *
18920c8df6SMauro Carvalho Chehab  * Intel 5400 Chipset Memory Controller Hub (MCH) - Datasheet
19920c8df6SMauro Carvalho Chehab  * 	http://developer.intel.com/design/chipsets/datashts/313070.htm
20920c8df6SMauro Carvalho Chehab  *
21296da591SMauro Carvalho Chehab  * This Memory Controller manages DDR2 FB-DIMMs. It has 2 branches, each with
22296da591SMauro Carvalho Chehab  * 2 channels operating in lockstep no-mirror mode. Each channel can have up to
23296da591SMauro Carvalho Chehab  * 4 dimm's, each with up to 8GB.
24296da591SMauro Carvalho Chehab  *
25920c8df6SMauro Carvalho Chehab  */
26920c8df6SMauro Carvalho Chehab 
27920c8df6SMauro Carvalho Chehab #include <linux/module.h>
28920c8df6SMauro Carvalho Chehab #include <linux/init.h>
29920c8df6SMauro Carvalho Chehab #include <linux/pci.h>
30920c8df6SMauro Carvalho Chehab #include <linux/pci_ids.h>
31920c8df6SMauro Carvalho Chehab #include <linux/slab.h>
32920c8df6SMauro Carvalho Chehab #include <linux/edac.h>
33920c8df6SMauro Carvalho Chehab #include <linux/mmzone.h>
34920c8df6SMauro Carvalho Chehab 
3578d88e8aSMauro Carvalho Chehab #include "edac_module.h"
36920c8df6SMauro Carvalho Chehab 
37920c8df6SMauro Carvalho Chehab /*
38920c8df6SMauro Carvalho Chehab  * Alter this version for the I5400 module when modifications are made
39920c8df6SMauro Carvalho Chehab  */
40152ba394SMichal Marek #define I5400_REVISION    " Ver: 1.0.0"
41920c8df6SMauro Carvalho Chehab 
42920c8df6SMauro Carvalho Chehab #define EDAC_MOD_STR      "i5400_edac"
43920c8df6SMauro Carvalho Chehab 
44920c8df6SMauro Carvalho Chehab #define i5400_printk(level, fmt, arg...) \
45920c8df6SMauro Carvalho Chehab 	edac_printk(level, "i5400", fmt, ##arg)
46920c8df6SMauro Carvalho Chehab 
47920c8df6SMauro Carvalho Chehab #define i5400_mc_printk(mci, level, fmt, arg...) \
48920c8df6SMauro Carvalho Chehab 	edac_mc_chipset_printk(mci, level, "i5400", fmt, ##arg)
49920c8df6SMauro Carvalho Chehab 
50920c8df6SMauro Carvalho Chehab /* Limits for i5400 */
51296da591SMauro Carvalho Chehab #define MAX_BRANCHES		2
52920c8df6SMauro Carvalho Chehab #define CHANNELS_PER_BRANCH	2
53296da591SMauro Carvalho Chehab #define DIMMS_PER_CHANNEL	4
54296da591SMauro Carvalho Chehab #define	MAX_CHANNELS		(MAX_BRANCHES * CHANNELS_PER_BRANCH)
55920c8df6SMauro Carvalho Chehab 
56920c8df6SMauro Carvalho Chehab /* Device 16,
57920c8df6SMauro Carvalho Chehab  * Function 0: System Address
58920c8df6SMauro Carvalho Chehab  * Function 1: Memory Branch Map, Control, Errors Register
59920c8df6SMauro Carvalho Chehab  * Function 2: FSB Error Registers
60920c8df6SMauro Carvalho Chehab  *
618375d490SMauro Carvalho Chehab  * All 3 functions of Device 16 (0,1,2) share the SAME DID and
628375d490SMauro Carvalho Chehab  * uses PCI_DEVICE_ID_INTEL_5400_ERR for device 16 (0,1,2),
638375d490SMauro Carvalho Chehab  * PCI_DEVICE_ID_INTEL_5400_FBD0 and PCI_DEVICE_ID_INTEL_5400_FBD1
648375d490SMauro Carvalho Chehab  * for device 21 (0,1).
65920c8df6SMauro Carvalho Chehab  */
66920c8df6SMauro Carvalho Chehab 
67920c8df6SMauro Carvalho Chehab 	/* OFFSETS for Function 0 */
68920c8df6SMauro Carvalho Chehab #define		AMBASE			0x48 /* AMB Mem Mapped Reg Region Base */
69920c8df6SMauro Carvalho Chehab #define		MAXCH			0x56 /* Max Channel Number */
70920c8df6SMauro Carvalho Chehab #define		MAXDIMMPERCH		0x57 /* Max DIMM PER Channel Number */
71920c8df6SMauro Carvalho Chehab 
72920c8df6SMauro Carvalho Chehab 	/* OFFSETS for Function 1 */
73920c8df6SMauro Carvalho Chehab #define		TOLM			0x6C
74920c8df6SMauro Carvalho Chehab #define		REDMEMB			0x7C
75920c8df6SMauro Carvalho Chehab #define			REC_ECC_LOCATOR_ODD(x)	((x) & 0x3fe00) /* bits [17:9] indicate ODD, [8:0]  indicate EVEN */
76920c8df6SMauro Carvalho Chehab #define		MIR0			0x80
77920c8df6SMauro Carvalho Chehab #define		MIR1			0x84
78920c8df6SMauro Carvalho Chehab #define		AMIR0			0x8c
79920c8df6SMauro Carvalho Chehab #define		AMIR1			0x90
80920c8df6SMauro Carvalho Chehab 
81920c8df6SMauro Carvalho Chehab 	/* Fatal error registers */
82920c8df6SMauro Carvalho Chehab #define		FERR_FAT_FBD		0x98	/* also called as FERR_FAT_FB_DIMM at datasheet */
83920c8df6SMauro Carvalho Chehab #define			FERR_FAT_FBDCHAN (3<<28)	/* channel index where the highest-order error occurred */
84920c8df6SMauro Carvalho Chehab 
85920c8df6SMauro Carvalho Chehab #define		NERR_FAT_FBD		0x9c
86920c8df6SMauro Carvalho Chehab #define		FERR_NF_FBD		0xa0	/* also called as FERR_NFAT_FB_DIMM at datasheet */
87920c8df6SMauro Carvalho Chehab 
88920c8df6SMauro Carvalho Chehab 	/* Non-fatal error register */
89920c8df6SMauro Carvalho Chehab #define		NERR_NF_FBD		0xa4
90920c8df6SMauro Carvalho Chehab 
91920c8df6SMauro Carvalho Chehab 	/* Enable error mask */
92920c8df6SMauro Carvalho Chehab #define		EMASK_FBD		0xa8
93920c8df6SMauro Carvalho Chehab 
94920c8df6SMauro Carvalho Chehab #define		ERR0_FBD		0xac
95920c8df6SMauro Carvalho Chehab #define		ERR1_FBD		0xb0
96920c8df6SMauro Carvalho Chehab #define		ERR2_FBD		0xb4
97920c8df6SMauro Carvalho Chehab #define		MCERR_FBD		0xb8
98920c8df6SMauro Carvalho Chehab 
99920c8df6SMauro Carvalho Chehab 	/* No OFFSETS for Device 16 Function 2 */
100920c8df6SMauro Carvalho Chehab 
101920c8df6SMauro Carvalho Chehab /*
102920c8df6SMauro Carvalho Chehab  * Device 21,
103920c8df6SMauro Carvalho Chehab  * Function 0: Memory Map Branch 0
104920c8df6SMauro Carvalho Chehab  *
105920c8df6SMauro Carvalho Chehab  * Device 22,
106920c8df6SMauro Carvalho Chehab  * Function 0: Memory Map Branch 1
107920c8df6SMauro Carvalho Chehab  */
108920c8df6SMauro Carvalho Chehab 
109920c8df6SMauro Carvalho Chehab 	/* OFFSETS for Function 0 */
110920c8df6SMauro Carvalho Chehab #define AMBPRESENT_0	0x64
111920c8df6SMauro Carvalho Chehab #define AMBPRESENT_1	0x66
112920c8df6SMauro Carvalho Chehab #define MTR0		0x80
113920c8df6SMauro Carvalho Chehab #define MTR1		0x82
114920c8df6SMauro Carvalho Chehab #define MTR2		0x84
115920c8df6SMauro Carvalho Chehab #define MTR3		0x86
116920c8df6SMauro Carvalho Chehab 
117920c8df6SMauro Carvalho Chehab 	/* OFFSETS for Function 1 */
118920c8df6SMauro Carvalho Chehab #define NRECFGLOG		0x74
119920c8df6SMauro Carvalho Chehab #define RECFGLOG		0x78
120920c8df6SMauro Carvalho Chehab #define NRECMEMA		0xbe
121920c8df6SMauro Carvalho Chehab #define NRECMEMB		0xc0
122920c8df6SMauro Carvalho Chehab #define NRECFB_DIMMA		0xc4
123920c8df6SMauro Carvalho Chehab #define NRECFB_DIMMB		0xc8
124920c8df6SMauro Carvalho Chehab #define NRECFB_DIMMC		0xcc
125920c8df6SMauro Carvalho Chehab #define NRECFB_DIMMD		0xd0
126920c8df6SMauro Carvalho Chehab #define NRECFB_DIMME		0xd4
127920c8df6SMauro Carvalho Chehab #define NRECFB_DIMMF		0xd8
128920c8df6SMauro Carvalho Chehab #define REDMEMA			0xdC
129920c8df6SMauro Carvalho Chehab #define RECMEMA			0xf0
130920c8df6SMauro Carvalho Chehab #define RECMEMB			0xf4
131920c8df6SMauro Carvalho Chehab #define RECFB_DIMMA		0xf8
132920c8df6SMauro Carvalho Chehab #define RECFB_DIMMB		0xec
133920c8df6SMauro Carvalho Chehab #define RECFB_DIMMC		0xf0
134920c8df6SMauro Carvalho Chehab #define RECFB_DIMMD		0xf4
135920c8df6SMauro Carvalho Chehab #define RECFB_DIMME		0xf8
136920c8df6SMauro Carvalho Chehab #define RECFB_DIMMF		0xfC
137920c8df6SMauro Carvalho Chehab 
138920c8df6SMauro Carvalho Chehab /*
139920c8df6SMauro Carvalho Chehab  * Error indicator bits and masks
140920c8df6SMauro Carvalho Chehab  * Error masks are according with Table 5-17 of i5400 datasheet
141920c8df6SMauro Carvalho Chehab  */
142920c8df6SMauro Carvalho Chehab 
143920c8df6SMauro Carvalho Chehab enum error_mask {
144920c8df6SMauro Carvalho Chehab 	EMASK_M1  = 1<<0,  /* Memory Write error on non-redundant retry */
145920c8df6SMauro Carvalho Chehab 	EMASK_M2  = 1<<1,  /* Memory or FB-DIMM configuration CRC read error */
146920c8df6SMauro Carvalho Chehab 	EMASK_M3  = 1<<2,  /* Reserved */
147920c8df6SMauro Carvalho Chehab 	EMASK_M4  = 1<<3,  /* Uncorrectable Data ECC on Replay */
148920c8df6SMauro Carvalho Chehab 	EMASK_M5  = 1<<4,  /* Aliased Uncorrectable Non-Mirrored Demand Data ECC */
149920c8df6SMauro Carvalho Chehab 	EMASK_M6  = 1<<5,  /* Unsupported on i5400 */
150920c8df6SMauro Carvalho Chehab 	EMASK_M7  = 1<<6,  /* Aliased Uncorrectable Resilver- or Spare-Copy Data ECC */
151920c8df6SMauro Carvalho Chehab 	EMASK_M8  = 1<<7,  /* Aliased Uncorrectable Patrol Data ECC */
152920c8df6SMauro Carvalho Chehab 	EMASK_M9  = 1<<8,  /* Non-Aliased Uncorrectable Non-Mirrored Demand Data ECC */
153920c8df6SMauro Carvalho Chehab 	EMASK_M10 = 1<<9,  /* Unsupported on i5400 */
154920c8df6SMauro Carvalho Chehab 	EMASK_M11 = 1<<10, /* Non-Aliased Uncorrectable Resilver- or Spare-Copy Data ECC  */
155920c8df6SMauro Carvalho Chehab 	EMASK_M12 = 1<<11, /* Non-Aliased Uncorrectable Patrol Data ECC */
156920c8df6SMauro Carvalho Chehab 	EMASK_M13 = 1<<12, /* Memory Write error on first attempt */
157920c8df6SMauro Carvalho Chehab 	EMASK_M14 = 1<<13, /* FB-DIMM Configuration Write error on first attempt */
158920c8df6SMauro Carvalho Chehab 	EMASK_M15 = 1<<14, /* Memory or FB-DIMM configuration CRC read error */
159920c8df6SMauro Carvalho Chehab 	EMASK_M16 = 1<<15, /* Channel Failed-Over Occurred */
160920c8df6SMauro Carvalho Chehab 	EMASK_M17 = 1<<16, /* Correctable Non-Mirrored Demand Data ECC */
161920c8df6SMauro Carvalho Chehab 	EMASK_M18 = 1<<17, /* Unsupported on i5400 */
162920c8df6SMauro Carvalho Chehab 	EMASK_M19 = 1<<18, /* Correctable Resilver- or Spare-Copy Data ECC */
163920c8df6SMauro Carvalho Chehab 	EMASK_M20 = 1<<19, /* Correctable Patrol Data ECC */
164920c8df6SMauro Carvalho Chehab 	EMASK_M21 = 1<<20, /* FB-DIMM Northbound parity error on FB-DIMM Sync Status */
165920c8df6SMauro Carvalho Chehab 	EMASK_M22 = 1<<21, /* SPD protocol Error */
166920c8df6SMauro Carvalho Chehab 	EMASK_M23 = 1<<22, /* Non-Redundant Fast Reset Timeout */
167920c8df6SMauro Carvalho Chehab 	EMASK_M24 = 1<<23, /* Refresh error */
168920c8df6SMauro Carvalho Chehab 	EMASK_M25 = 1<<24, /* Memory Write error on redundant retry */
169920c8df6SMauro Carvalho Chehab 	EMASK_M26 = 1<<25, /* Redundant Fast Reset Timeout */
170920c8df6SMauro Carvalho Chehab 	EMASK_M27 = 1<<26, /* Correctable Counter Threshold Exceeded */
171920c8df6SMauro Carvalho Chehab 	EMASK_M28 = 1<<27, /* DIMM-Spare Copy Completed */
172920c8df6SMauro Carvalho Chehab 	EMASK_M29 = 1<<28, /* DIMM-Isolation Completed */
173920c8df6SMauro Carvalho Chehab };
174920c8df6SMauro Carvalho Chehab 
175920c8df6SMauro Carvalho Chehab /*
176920c8df6SMauro Carvalho Chehab  * Names to translate bit error into something useful
177920c8df6SMauro Carvalho Chehab  */
1788375d490SMauro Carvalho Chehab static const char *error_name[] = {
179920c8df6SMauro Carvalho Chehab 	[0]  = "Memory Write error on non-redundant retry",
180920c8df6SMauro Carvalho Chehab 	[1]  = "Memory or FB-DIMM configuration CRC read error",
181920c8df6SMauro Carvalho Chehab 	/* Reserved */
182920c8df6SMauro Carvalho Chehab 	[3]  = "Uncorrectable Data ECC on Replay",
183920c8df6SMauro Carvalho Chehab 	[4]  = "Aliased Uncorrectable Non-Mirrored Demand Data ECC",
1848375d490SMauro Carvalho Chehab 	/* M6 Unsupported on i5400 */
185920c8df6SMauro Carvalho Chehab 	[6]  = "Aliased Uncorrectable Resilver- or Spare-Copy Data ECC",
186920c8df6SMauro Carvalho Chehab 	[7]  = "Aliased Uncorrectable Patrol Data ECC",
187920c8df6SMauro Carvalho Chehab 	[8]  = "Non-Aliased Uncorrectable Non-Mirrored Demand Data ECC",
1888375d490SMauro Carvalho Chehab 	/* M10 Unsupported on i5400 */
189920c8df6SMauro Carvalho Chehab 	[10] = "Non-Aliased Uncorrectable Resilver- or Spare-Copy Data ECC",
190920c8df6SMauro Carvalho Chehab 	[11] = "Non-Aliased Uncorrectable Patrol Data ECC",
191920c8df6SMauro Carvalho Chehab 	[12] = "Memory Write error on first attempt",
192920c8df6SMauro Carvalho Chehab 	[13] = "FB-DIMM Configuration Write error on first attempt",
193920c8df6SMauro Carvalho Chehab 	[14] = "Memory or FB-DIMM configuration CRC read error",
194920c8df6SMauro Carvalho Chehab 	[15] = "Channel Failed-Over Occurred",
195920c8df6SMauro Carvalho Chehab 	[16] = "Correctable Non-Mirrored Demand Data ECC",
1968375d490SMauro Carvalho Chehab 	/* M18 Unsupported on i5400 */
197920c8df6SMauro Carvalho Chehab 	[18] = "Correctable Resilver- or Spare-Copy Data ECC",
198920c8df6SMauro Carvalho Chehab 	[19] = "Correctable Patrol Data ECC",
199920c8df6SMauro Carvalho Chehab 	[20] = "FB-DIMM Northbound parity error on FB-DIMM Sync Status",
200920c8df6SMauro Carvalho Chehab 	[21] = "SPD protocol Error",
201920c8df6SMauro Carvalho Chehab 	[22] = "Non-Redundant Fast Reset Timeout",
202920c8df6SMauro Carvalho Chehab 	[23] = "Refresh error",
203920c8df6SMauro Carvalho Chehab 	[24] = "Memory Write error on redundant retry",
204920c8df6SMauro Carvalho Chehab 	[25] = "Redundant Fast Reset Timeout",
205920c8df6SMauro Carvalho Chehab 	[26] = "Correctable Counter Threshold Exceeded",
206920c8df6SMauro Carvalho Chehab 	[27] = "DIMM-Spare Copy Completed",
207920c8df6SMauro Carvalho Chehab 	[28] = "DIMM-Isolation Completed",
208920c8df6SMauro Carvalho Chehab };
209920c8df6SMauro Carvalho Chehab 
210920c8df6SMauro Carvalho Chehab /* Fatal errors */
211920c8df6SMauro Carvalho Chehab #define ERROR_FAT_MASK		(EMASK_M1 | \
212920c8df6SMauro Carvalho Chehab 				 EMASK_M2 | \
213920c8df6SMauro Carvalho Chehab 				 EMASK_M23)
214920c8df6SMauro Carvalho Chehab 
215920c8df6SMauro Carvalho Chehab /* Correctable errors */
216920c8df6SMauro Carvalho Chehab #define ERROR_NF_CORRECTABLE	(EMASK_M27 | \
217920c8df6SMauro Carvalho Chehab 				 EMASK_M20 | \
218920c8df6SMauro Carvalho Chehab 				 EMASK_M19 | \
219920c8df6SMauro Carvalho Chehab 				 EMASK_M18 | \
220920c8df6SMauro Carvalho Chehab 				 EMASK_M17 | \
221920c8df6SMauro Carvalho Chehab 				 EMASK_M16)
222920c8df6SMauro Carvalho Chehab #define ERROR_NF_DIMM_SPARE	(EMASK_M29 | \
223920c8df6SMauro Carvalho Chehab 				 EMASK_M28)
224920c8df6SMauro Carvalho Chehab #define ERROR_NF_SPD_PROTOCOL	(EMASK_M22)
225920c8df6SMauro Carvalho Chehab #define ERROR_NF_NORTH_CRC	(EMASK_M21)
226920c8df6SMauro Carvalho Chehab 
227920c8df6SMauro Carvalho Chehab /* Recoverable errors */
228920c8df6SMauro Carvalho Chehab #define ERROR_NF_RECOVERABLE	(EMASK_M26 | \
229920c8df6SMauro Carvalho Chehab 				 EMASK_M25 | \
230920c8df6SMauro Carvalho Chehab 				 EMASK_M24 | \
231920c8df6SMauro Carvalho Chehab 				 EMASK_M15 | \
232920c8df6SMauro Carvalho Chehab 				 EMASK_M14 | \
233920c8df6SMauro Carvalho Chehab 				 EMASK_M13 | \
234920c8df6SMauro Carvalho Chehab 				 EMASK_M12 | \
235920c8df6SMauro Carvalho Chehab 				 EMASK_M11 | \
236920c8df6SMauro Carvalho Chehab 				 EMASK_M9  | \
237920c8df6SMauro Carvalho Chehab 				 EMASK_M8  | \
238920c8df6SMauro Carvalho Chehab 				 EMASK_M7  | \
239920c8df6SMauro Carvalho Chehab 				 EMASK_M5)
240920c8df6SMauro Carvalho Chehab 
241920c8df6SMauro Carvalho Chehab /* uncorrectable errors */
242920c8df6SMauro Carvalho Chehab #define ERROR_NF_UNCORRECTABLE	(EMASK_M4)
243920c8df6SMauro Carvalho Chehab 
244920c8df6SMauro Carvalho Chehab /* mask to all non-fatal errors */
245920c8df6SMauro Carvalho Chehab #define ERROR_NF_MASK		(ERROR_NF_CORRECTABLE   | \
246920c8df6SMauro Carvalho Chehab 				 ERROR_NF_UNCORRECTABLE | \
247920c8df6SMauro Carvalho Chehab 				 ERROR_NF_RECOVERABLE   | \
248920c8df6SMauro Carvalho Chehab 				 ERROR_NF_DIMM_SPARE    | \
249920c8df6SMauro Carvalho Chehab 				 ERROR_NF_SPD_PROTOCOL  | \
250920c8df6SMauro Carvalho Chehab 				 ERROR_NF_NORTH_CRC)
251920c8df6SMauro Carvalho Chehab 
252920c8df6SMauro Carvalho Chehab /*
253920c8df6SMauro Carvalho Chehab  * Define error masks for the several registers
254920c8df6SMauro Carvalho Chehab  */
255920c8df6SMauro Carvalho Chehab 
256920c8df6SMauro Carvalho Chehab /* Enable all fatal and non fatal errors */
257920c8df6SMauro Carvalho Chehab #define ENABLE_EMASK_ALL	(ERROR_FAT_MASK | ERROR_NF_MASK)
258920c8df6SMauro Carvalho Chehab 
259920c8df6SMauro Carvalho Chehab /* mask for fatal error registers */
260920c8df6SMauro Carvalho Chehab #define FERR_FAT_MASK ERROR_FAT_MASK
261920c8df6SMauro Carvalho Chehab 
262920c8df6SMauro Carvalho Chehab /* masks for non-fatal error register */
to_nf_mask(unsigned int mask)2638375d490SMauro Carvalho Chehab static inline int to_nf_mask(unsigned int mask)
2648375d490SMauro Carvalho Chehab {
2658375d490SMauro Carvalho Chehab 	return (mask & EMASK_M29) | (mask >> 3);
2668375d490SMauro Carvalho Chehab };
267920c8df6SMauro Carvalho Chehab 
from_nf_ferr(unsigned int mask)2688375d490SMauro Carvalho Chehab static inline int from_nf_ferr(unsigned int mask)
2698375d490SMauro Carvalho Chehab {
2708375d490SMauro Carvalho Chehab 	return (mask & EMASK_M29) |		/* Bit 28 */
2718375d490SMauro Carvalho Chehab 	       (mask & ((1 << 28) - 1) << 3);	/* Bits 0 to 27 */
2728375d490SMauro Carvalho Chehab };
2738375d490SMauro Carvalho Chehab 
2748375d490SMauro Carvalho Chehab #define FERR_NF_MASK		to_nf_mask(ERROR_NF_MASK)
2758375d490SMauro Carvalho Chehab #define FERR_NF_CORRECTABLE	to_nf_mask(ERROR_NF_CORRECTABLE)
2768375d490SMauro Carvalho Chehab #define FERR_NF_DIMM_SPARE	to_nf_mask(ERROR_NF_DIMM_SPARE)
2778375d490SMauro Carvalho Chehab #define FERR_NF_SPD_PROTOCOL	to_nf_mask(ERROR_NF_SPD_PROTOCOL)
2788375d490SMauro Carvalho Chehab #define FERR_NF_NORTH_CRC	to_nf_mask(ERROR_NF_NORTH_CRC)
2798375d490SMauro Carvalho Chehab #define FERR_NF_RECOVERABLE	to_nf_mask(ERROR_NF_RECOVERABLE)
2808375d490SMauro Carvalho Chehab #define FERR_NF_UNCORRECTABLE	to_nf_mask(ERROR_NF_UNCORRECTABLE)
281920c8df6SMauro Carvalho Chehab 
282*b586a59eSChen Zhang /*
283*b586a59eSChen Zhang  * Defines to extract the various fields from the
284920c8df6SMauro Carvalho Chehab  *	MTRx - Memory Technology Registers
285920c8df6SMauro Carvalho Chehab  */
286920c8df6SMauro Carvalho Chehab #define MTR_DIMMS_PRESENT(mtr)		((mtr) & (1 << 10))
287920c8df6SMauro Carvalho Chehab #define MTR_DIMMS_ETHROTTLE(mtr)	((mtr) & (1 << 9))
288920c8df6SMauro Carvalho Chehab #define MTR_DRAM_WIDTH(mtr)		(((mtr) & (1 << 8)) ? 8 : 4)
289920c8df6SMauro Carvalho Chehab #define MTR_DRAM_BANKS(mtr)		(((mtr) & (1 << 6)) ? 8 : 4)
290920c8df6SMauro Carvalho Chehab #define MTR_DRAM_BANKS_ADDR_BITS(mtr)	((MTR_DRAM_BANKS(mtr) == 8) ? 3 : 2)
291920c8df6SMauro Carvalho Chehab #define MTR_DIMM_RANK(mtr)		(((mtr) >> 5) & 0x1)
292920c8df6SMauro Carvalho Chehab #define MTR_DIMM_RANK_ADDR_BITS(mtr)	(MTR_DIMM_RANK(mtr) ? 2 : 1)
293920c8df6SMauro Carvalho Chehab #define MTR_DIMM_ROWS(mtr)		(((mtr) >> 2) & 0x3)
294920c8df6SMauro Carvalho Chehab #define MTR_DIMM_ROWS_ADDR_BITS(mtr)	(MTR_DIMM_ROWS(mtr) + 13)
295920c8df6SMauro Carvalho Chehab #define MTR_DIMM_COLS(mtr)		((mtr) & 0x3)
296920c8df6SMauro Carvalho Chehab #define MTR_DIMM_COLS_ADDR_BITS(mtr)	(MTR_DIMM_COLS(mtr) + 10)
297920c8df6SMauro Carvalho Chehab 
298920c8df6SMauro Carvalho Chehab /* This applies to FERR_NF_FB-DIMM as well as FERR_FAT_FB-DIMM */
extract_fbdchan_indx(u32 x)299920c8df6SMauro Carvalho Chehab static inline int extract_fbdchan_indx(u32 x)
300920c8df6SMauro Carvalho Chehab {
301920c8df6SMauro Carvalho Chehab 	return (x>>28) & 0x3;
302920c8df6SMauro Carvalho Chehab }
303920c8df6SMauro Carvalho Chehab 
304920c8df6SMauro Carvalho Chehab /* Device name and register DID (Device ID) */
305920c8df6SMauro Carvalho Chehab struct i5400_dev_info {
306920c8df6SMauro Carvalho Chehab 	const char *ctl_name;	/* name for this device */
307920c8df6SMauro Carvalho Chehab 	u16 fsb_mapping_errors;	/* DID for the branchmap,control */
308920c8df6SMauro Carvalho Chehab };
309920c8df6SMauro Carvalho Chehab 
310920c8df6SMauro Carvalho Chehab /* Table of devices attributes supported by this driver */
311920c8df6SMauro Carvalho Chehab static const struct i5400_dev_info i5400_devs[] = {
312920c8df6SMauro Carvalho Chehab 	{
313920c8df6SMauro Carvalho Chehab 		.ctl_name = "I5400",
314920c8df6SMauro Carvalho Chehab 		.fsb_mapping_errors = PCI_DEVICE_ID_INTEL_5400_ERR,
315920c8df6SMauro Carvalho Chehab 	},
316920c8df6SMauro Carvalho Chehab };
317920c8df6SMauro Carvalho Chehab 
318920c8df6SMauro Carvalho Chehab struct i5400_dimm_info {
319920c8df6SMauro Carvalho Chehab 	int megabytes;		/* size, 0 means not present  */
320920c8df6SMauro Carvalho Chehab };
321920c8df6SMauro Carvalho Chehab 
322920c8df6SMauro Carvalho Chehab /* driver private data structure */
323920c8df6SMauro Carvalho Chehab struct i5400_pvt {
324920c8df6SMauro Carvalho Chehab 	struct pci_dev *system_address;		/* 16.0 */
325920c8df6SMauro Carvalho Chehab 	struct pci_dev *branchmap_werrors;	/* 16.1 */
326920c8df6SMauro Carvalho Chehab 	struct pci_dev *fsb_error_regs;		/* 16.2 */
327920c8df6SMauro Carvalho Chehab 	struct pci_dev *branch_0;		/* 21.0 */
328920c8df6SMauro Carvalho Chehab 	struct pci_dev *branch_1;		/* 22.0 */
329920c8df6SMauro Carvalho Chehab 
330920c8df6SMauro Carvalho Chehab 	u16 tolm;				/* top of low memory */
331f58d0deeSDan Carpenter 	union {
332920c8df6SMauro Carvalho Chehab 		u64 ambase;				/* AMB BAR */
333f58d0deeSDan Carpenter 		struct {
334f58d0deeSDan Carpenter 			u32 ambase_bottom;
335f58d0deeSDan Carpenter 			u32 ambase_top;
336f58d0deeSDan Carpenter 		} u __packed;
337f58d0deeSDan Carpenter 	};
338920c8df6SMauro Carvalho Chehab 
339920c8df6SMauro Carvalho Chehab 	u16 mir0, mir1;
340920c8df6SMauro Carvalho Chehab 
341296da591SMauro Carvalho Chehab 	u16 b0_mtr[DIMMS_PER_CHANNEL];	/* Memory Technlogy Reg */
342920c8df6SMauro Carvalho Chehab 	u16 b0_ambpresent0;			/* Branch 0, Channel 0 */
343920c8df6SMauro Carvalho Chehab 	u16 b0_ambpresent1;			/* Brnach 0, Channel 1 */
344920c8df6SMauro Carvalho Chehab 
345296da591SMauro Carvalho Chehab 	u16 b1_mtr[DIMMS_PER_CHANNEL];	/* Memory Technlogy Reg */
346920c8df6SMauro Carvalho Chehab 	u16 b1_ambpresent0;			/* Branch 1, Channel 8 */
347920c8df6SMauro Carvalho Chehab 	u16 b1_ambpresent1;			/* Branch 1, Channel 1 */
348920c8df6SMauro Carvalho Chehab 
349920c8df6SMauro Carvalho Chehab 	/* DIMM information matrix, allocating architecture maximums */
350296da591SMauro Carvalho Chehab 	struct i5400_dimm_info dimm_info[DIMMS_PER_CHANNEL][MAX_CHANNELS];
351920c8df6SMauro Carvalho Chehab 
352920c8df6SMauro Carvalho Chehab 	/* Actual values for this controller */
353920c8df6SMauro Carvalho Chehab 	int maxch;				/* Max channels */
354920c8df6SMauro Carvalho Chehab 	int maxdimmperch;			/* Max DIMMs per channel */
355920c8df6SMauro Carvalho Chehab };
356920c8df6SMauro Carvalho Chehab 
357920c8df6SMauro Carvalho Chehab /* I5400 MCH error information retrieved from Hardware */
358920c8df6SMauro Carvalho Chehab struct i5400_error_info {
359920c8df6SMauro Carvalho Chehab 	/* These registers are always read from the MC */
360920c8df6SMauro Carvalho Chehab 	u32 ferr_fat_fbd;	/* First Errors Fatal */
361920c8df6SMauro Carvalho Chehab 	u32 nerr_fat_fbd;	/* Next Errors Fatal */
362920c8df6SMauro Carvalho Chehab 	u32 ferr_nf_fbd;	/* First Errors Non-Fatal */
363920c8df6SMauro Carvalho Chehab 	u32 nerr_nf_fbd;	/* Next Errors Non-Fatal */
364920c8df6SMauro Carvalho Chehab 
365920c8df6SMauro Carvalho Chehab 	/* These registers are input ONLY if there was a Recoverable Error */
366920c8df6SMauro Carvalho Chehab 	u32 redmemb;		/* Recoverable Mem Data Error log B */
367920c8df6SMauro Carvalho Chehab 	u16 recmema;		/* Recoverable Mem Error log A */
368920c8df6SMauro Carvalho Chehab 	u32 recmemb;		/* Recoverable Mem Error log B */
369920c8df6SMauro Carvalho Chehab 
3708375d490SMauro Carvalho Chehab 	/* These registers are input ONLY if there was a Non-Rec Error */
371920c8df6SMauro Carvalho Chehab 	u16 nrecmema;		/* Non-Recoverable Mem log A */
372a8c82614SJérémy Lefaure 	u32 nrecmemb;		/* Non-Recoverable Mem log B */
373920c8df6SMauro Carvalho Chehab 
374920c8df6SMauro Carvalho Chehab };
375920c8df6SMauro Carvalho Chehab 
376920c8df6SMauro Carvalho Chehab /* note that nrec_rdwr changed from NRECMEMA to NRECMEMB between the 5000 and
377920c8df6SMauro Carvalho Chehab    5400 better to use an inline function than a macro in this case */
nrec_bank(struct i5400_error_info * info)378920c8df6SMauro Carvalho Chehab static inline int nrec_bank(struct i5400_error_info *info)
379920c8df6SMauro Carvalho Chehab {
380920c8df6SMauro Carvalho Chehab 	return ((info->nrecmema) >> 12) & 0x7;
381920c8df6SMauro Carvalho Chehab }
nrec_rank(struct i5400_error_info * info)382920c8df6SMauro Carvalho Chehab static inline int nrec_rank(struct i5400_error_info *info)
383920c8df6SMauro Carvalho Chehab {
384920c8df6SMauro Carvalho Chehab 	return ((info->nrecmema) >> 8) & 0xf;
385920c8df6SMauro Carvalho Chehab }
nrec_buf_id(struct i5400_error_info * info)386920c8df6SMauro Carvalho Chehab static inline int nrec_buf_id(struct i5400_error_info *info)
387920c8df6SMauro Carvalho Chehab {
388920c8df6SMauro Carvalho Chehab 	return ((info->nrecmema)) & 0xff;
389920c8df6SMauro Carvalho Chehab }
nrec_rdwr(struct i5400_error_info * info)390920c8df6SMauro Carvalho Chehab static inline int nrec_rdwr(struct i5400_error_info *info)
391920c8df6SMauro Carvalho Chehab {
392920c8df6SMauro Carvalho Chehab 	return (info->nrecmemb) >> 31;
393920c8df6SMauro Carvalho Chehab }
394920c8df6SMauro Carvalho Chehab /* This applies to both NREC and REC string so it can be used with nrec_rdwr
395920c8df6SMauro Carvalho Chehab    and rec_rdwr */
rdwr_str(int rdwr)396920c8df6SMauro Carvalho Chehab static inline const char *rdwr_str(int rdwr)
397920c8df6SMauro Carvalho Chehab {
398920c8df6SMauro Carvalho Chehab 	return rdwr ? "Write" : "Read";
399920c8df6SMauro Carvalho Chehab }
nrec_cas(struct i5400_error_info * info)400920c8df6SMauro Carvalho Chehab static inline int nrec_cas(struct i5400_error_info *info)
401920c8df6SMauro Carvalho Chehab {
402920c8df6SMauro Carvalho Chehab 	return ((info->nrecmemb) >> 16) & 0x1fff;
403920c8df6SMauro Carvalho Chehab }
nrec_ras(struct i5400_error_info * info)404920c8df6SMauro Carvalho Chehab static inline int nrec_ras(struct i5400_error_info *info)
405920c8df6SMauro Carvalho Chehab {
406920c8df6SMauro Carvalho Chehab 	return (info->nrecmemb) & 0xffff;
407920c8df6SMauro Carvalho Chehab }
rec_bank(struct i5400_error_info * info)408920c8df6SMauro Carvalho Chehab static inline int rec_bank(struct i5400_error_info *info)
409920c8df6SMauro Carvalho Chehab {
410920c8df6SMauro Carvalho Chehab 	return ((info->recmema) >> 12) & 0x7;
411920c8df6SMauro Carvalho Chehab }
rec_rank(struct i5400_error_info * info)412920c8df6SMauro Carvalho Chehab static inline int rec_rank(struct i5400_error_info *info)
413920c8df6SMauro Carvalho Chehab {
414920c8df6SMauro Carvalho Chehab 	return ((info->recmema) >> 8) & 0xf;
415920c8df6SMauro Carvalho Chehab }
rec_rdwr(struct i5400_error_info * info)416920c8df6SMauro Carvalho Chehab static inline int rec_rdwr(struct i5400_error_info *info)
417920c8df6SMauro Carvalho Chehab {
418920c8df6SMauro Carvalho Chehab 	return (info->recmemb) >> 31;
419920c8df6SMauro Carvalho Chehab }
rec_cas(struct i5400_error_info * info)420920c8df6SMauro Carvalho Chehab static inline int rec_cas(struct i5400_error_info *info)
421920c8df6SMauro Carvalho Chehab {
422920c8df6SMauro Carvalho Chehab 	return ((info->recmemb) >> 16) & 0x1fff;
423920c8df6SMauro Carvalho Chehab }
rec_ras(struct i5400_error_info * info)424920c8df6SMauro Carvalho Chehab static inline int rec_ras(struct i5400_error_info *info)
425920c8df6SMauro Carvalho Chehab {
426920c8df6SMauro Carvalho Chehab 	return (info->recmemb) & 0xffff;
427920c8df6SMauro Carvalho Chehab }
428920c8df6SMauro Carvalho Chehab 
429920c8df6SMauro Carvalho Chehab static struct edac_pci_ctl_info *i5400_pci;
430920c8df6SMauro Carvalho Chehab 
431920c8df6SMauro Carvalho Chehab /*
432920c8df6SMauro Carvalho Chehab  *	i5400_get_error_info	Retrieve the hardware error information from
433920c8df6SMauro Carvalho Chehab  *				the hardware and cache it in the 'info'
434920c8df6SMauro Carvalho Chehab  *				structure
435920c8df6SMauro Carvalho Chehab  */
i5400_get_error_info(struct mem_ctl_info * mci,struct i5400_error_info * info)436920c8df6SMauro Carvalho Chehab static void i5400_get_error_info(struct mem_ctl_info *mci,
437920c8df6SMauro Carvalho Chehab 				 struct i5400_error_info *info)
438920c8df6SMauro Carvalho Chehab {
439920c8df6SMauro Carvalho Chehab 	struct i5400_pvt *pvt;
440920c8df6SMauro Carvalho Chehab 	u32 value;
441920c8df6SMauro Carvalho Chehab 
442920c8df6SMauro Carvalho Chehab 	pvt = mci->pvt_info;
443920c8df6SMauro Carvalho Chehab 
444920c8df6SMauro Carvalho Chehab 	/* read in the 1st FATAL error register */
445920c8df6SMauro Carvalho Chehab 	pci_read_config_dword(pvt->branchmap_werrors, FERR_FAT_FBD, &value);
446920c8df6SMauro Carvalho Chehab 
447920c8df6SMauro Carvalho Chehab 	/* Mask only the bits that the doc says are valid
448920c8df6SMauro Carvalho Chehab 	 */
449920c8df6SMauro Carvalho Chehab 	value &= (FERR_FAT_FBDCHAN | FERR_FAT_MASK);
450920c8df6SMauro Carvalho Chehab 
451920c8df6SMauro Carvalho Chehab 	/* If there is an error, then read in the
452920c8df6SMauro Carvalho Chehab 	   NEXT FATAL error register and the Memory Error Log Register A
453920c8df6SMauro Carvalho Chehab 	 */
454920c8df6SMauro Carvalho Chehab 	if (value & FERR_FAT_MASK) {
455920c8df6SMauro Carvalho Chehab 		info->ferr_fat_fbd = value;
456920c8df6SMauro Carvalho Chehab 
457920c8df6SMauro Carvalho Chehab 		/* harvest the various error data we need */
458920c8df6SMauro Carvalho Chehab 		pci_read_config_dword(pvt->branchmap_werrors,
459920c8df6SMauro Carvalho Chehab 				NERR_FAT_FBD, &info->nerr_fat_fbd);
460920c8df6SMauro Carvalho Chehab 		pci_read_config_word(pvt->branchmap_werrors,
461920c8df6SMauro Carvalho Chehab 				NRECMEMA, &info->nrecmema);
462a8c82614SJérémy Lefaure 		pci_read_config_dword(pvt->branchmap_werrors,
463920c8df6SMauro Carvalho Chehab 				NRECMEMB, &info->nrecmemb);
464920c8df6SMauro Carvalho Chehab 
465920c8df6SMauro Carvalho Chehab 		/* Clear the error bits, by writing them back */
466920c8df6SMauro Carvalho Chehab 		pci_write_config_dword(pvt->branchmap_werrors,
467920c8df6SMauro Carvalho Chehab 				FERR_FAT_FBD, value);
468920c8df6SMauro Carvalho Chehab 	} else {
469920c8df6SMauro Carvalho Chehab 		info->ferr_fat_fbd = 0;
470920c8df6SMauro Carvalho Chehab 		info->nerr_fat_fbd = 0;
471920c8df6SMauro Carvalho Chehab 		info->nrecmema = 0;
472920c8df6SMauro Carvalho Chehab 		info->nrecmemb = 0;
473920c8df6SMauro Carvalho Chehab 	}
474920c8df6SMauro Carvalho Chehab 
475920c8df6SMauro Carvalho Chehab 	/* read in the 1st NON-FATAL error register */
476920c8df6SMauro Carvalho Chehab 	pci_read_config_dword(pvt->branchmap_werrors, FERR_NF_FBD, &value);
477920c8df6SMauro Carvalho Chehab 
478920c8df6SMauro Carvalho Chehab 	/* If there is an error, then read in the 1st NON-FATAL error
479920c8df6SMauro Carvalho Chehab 	 * register as well */
480920c8df6SMauro Carvalho Chehab 	if (value & FERR_NF_MASK) {
481920c8df6SMauro Carvalho Chehab 		info->ferr_nf_fbd = value;
482920c8df6SMauro Carvalho Chehab 
483920c8df6SMauro Carvalho Chehab 		/* harvest the various error data we need */
484920c8df6SMauro Carvalho Chehab 		pci_read_config_dword(pvt->branchmap_werrors,
485920c8df6SMauro Carvalho Chehab 				NERR_NF_FBD, &info->nerr_nf_fbd);
486920c8df6SMauro Carvalho Chehab 		pci_read_config_word(pvt->branchmap_werrors,
487920c8df6SMauro Carvalho Chehab 				RECMEMA, &info->recmema);
488920c8df6SMauro Carvalho Chehab 		pci_read_config_dword(pvt->branchmap_werrors,
489920c8df6SMauro Carvalho Chehab 				RECMEMB, &info->recmemb);
490920c8df6SMauro Carvalho Chehab 		pci_read_config_dword(pvt->branchmap_werrors,
491920c8df6SMauro Carvalho Chehab 				REDMEMB, &info->redmemb);
492920c8df6SMauro Carvalho Chehab 
493920c8df6SMauro Carvalho Chehab 		/* Clear the error bits, by writing them back */
494920c8df6SMauro Carvalho Chehab 		pci_write_config_dword(pvt->branchmap_werrors,
495920c8df6SMauro Carvalho Chehab 				FERR_NF_FBD, value);
496920c8df6SMauro Carvalho Chehab 	} else {
497920c8df6SMauro Carvalho Chehab 		info->ferr_nf_fbd = 0;
498920c8df6SMauro Carvalho Chehab 		info->nerr_nf_fbd = 0;
499920c8df6SMauro Carvalho Chehab 		info->recmema = 0;
500920c8df6SMauro Carvalho Chehab 		info->recmemb = 0;
501920c8df6SMauro Carvalho Chehab 		info->redmemb = 0;
502920c8df6SMauro Carvalho Chehab 	}
503920c8df6SMauro Carvalho Chehab }
504920c8df6SMauro Carvalho Chehab 
505920c8df6SMauro Carvalho Chehab /*
506920c8df6SMauro Carvalho Chehab  * i5400_proccess_non_recoverable_info(struct mem_ctl_info *mci,
507920c8df6SMauro Carvalho Chehab  * 					struct i5400_error_info *info,
508920c8df6SMauro Carvalho Chehab  * 					int handle_errors);
509920c8df6SMauro Carvalho Chehab  *
510920c8df6SMauro Carvalho Chehab  *	handle the Intel FATAL and unrecoverable errors, if any
511920c8df6SMauro Carvalho Chehab  */
i5400_proccess_non_recoverable_info(struct mem_ctl_info * mci,struct i5400_error_info * info,unsigned long allErrors)512920c8df6SMauro Carvalho Chehab static void i5400_proccess_non_recoverable_info(struct mem_ctl_info *mci,
513920c8df6SMauro Carvalho Chehab 				    struct i5400_error_info *info,
514920c8df6SMauro Carvalho Chehab 				    unsigned long allErrors)
515920c8df6SMauro Carvalho Chehab {
516920c8df6SMauro Carvalho Chehab 	char msg[EDAC_MC_LABEL_LEN + 1 + 90 + 80];
517920c8df6SMauro Carvalho Chehab 	int branch;
518920c8df6SMauro Carvalho Chehab 	int channel;
519920c8df6SMauro Carvalho Chehab 	int bank;
520920c8df6SMauro Carvalho Chehab 	int buf_id;
521920c8df6SMauro Carvalho Chehab 	int rank;
522920c8df6SMauro Carvalho Chehab 	int rdwr;
523920c8df6SMauro Carvalho Chehab 	int ras, cas;
524920c8df6SMauro Carvalho Chehab 	int errnum;
525920c8df6SMauro Carvalho Chehab 	char *type = NULL;
526296da591SMauro Carvalho Chehab 	enum hw_event_mc_err_type tp_event = HW_EVENT_ERR_UNCORRECTED;
527920c8df6SMauro Carvalho Chehab 
528920c8df6SMauro Carvalho Chehab 	if (!allErrors)
529920c8df6SMauro Carvalho Chehab 		return;		/* if no error, return now */
530920c8df6SMauro Carvalho Chehab 
531296da591SMauro Carvalho Chehab 	if (allErrors &  ERROR_FAT_MASK) {
532920c8df6SMauro Carvalho Chehab 		type = "FATAL";
533296da591SMauro Carvalho Chehab 		tp_event = HW_EVENT_ERR_FATAL;
534296da591SMauro Carvalho Chehab 	} else if (allErrors & FERR_NF_UNCORRECTABLE)
535920c8df6SMauro Carvalho Chehab 		type = "NON-FATAL uncorrected";
536920c8df6SMauro Carvalho Chehab 	else
537920c8df6SMauro Carvalho Chehab 		type = "NON-FATAL recoverable";
538920c8df6SMauro Carvalho Chehab 
539920c8df6SMauro Carvalho Chehab 	/* ONLY ONE of the possible error bits will be set, as per the docs */
540920c8df6SMauro Carvalho Chehab 
541920c8df6SMauro Carvalho Chehab 	branch = extract_fbdchan_indx(info->ferr_fat_fbd);
542920c8df6SMauro Carvalho Chehab 	channel = branch;
543920c8df6SMauro Carvalho Chehab 
544920c8df6SMauro Carvalho Chehab 	/* Use the NON-Recoverable macros to extract data */
545920c8df6SMauro Carvalho Chehab 	bank = nrec_bank(info);
546920c8df6SMauro Carvalho Chehab 	rank = nrec_rank(info);
547920c8df6SMauro Carvalho Chehab 	buf_id = nrec_buf_id(info);
548920c8df6SMauro Carvalho Chehab 	rdwr = nrec_rdwr(info);
549920c8df6SMauro Carvalho Chehab 	ras = nrec_ras(info);
550920c8df6SMauro Carvalho Chehab 	cas = nrec_cas(info);
551920c8df6SMauro Carvalho Chehab 
5521acd05e4SMauro Carvalho Chehab 	edac_dbg(0, "\t\t%s DIMM= %d  Channels= %d,%d  (Branch= %d DRAM Bank= %d Buffer ID = %d rdwr= %s ras= %d cas= %d)\n",
5531acd05e4SMauro Carvalho Chehab 		 type, rank, channel, channel + 1, branch >> 1, bank,
554920c8df6SMauro Carvalho Chehab 		 buf_id, rdwr_str(rdwr), ras, cas);
555920c8df6SMauro Carvalho Chehab 
556920c8df6SMauro Carvalho Chehab 	/* Only 1 bit will be on */
557920c8df6SMauro Carvalho Chehab 	errnum = find_first_bit(&allErrors, ARRAY_SIZE(error_name));
558920c8df6SMauro Carvalho Chehab 
559920c8df6SMauro Carvalho Chehab 	/* Form out message */
560920c8df6SMauro Carvalho Chehab 	snprintf(msg, sizeof(msg),
561296da591SMauro Carvalho Chehab 		 "Bank=%d Buffer ID = %d RAS=%d CAS=%d Err=0x%lx (%s)",
562296da591SMauro Carvalho Chehab 		 bank, buf_id, ras, cas, allErrors, error_name[errnum]);
563920c8df6SMauro Carvalho Chehab 
5649eb07a7fSMauro Carvalho Chehab 	edac_mc_handle_error(tp_event, mci, 1, 0, 0, 0,
565296da591SMauro Carvalho Chehab 			     branch >> 1, -1, rank,
566296da591SMauro Carvalho Chehab 			     rdwr ? "Write error" : "Read error",
56703f7eae8SMauro Carvalho Chehab 			     msg);
568920c8df6SMauro Carvalho Chehab }
569920c8df6SMauro Carvalho Chehab 
570920c8df6SMauro Carvalho Chehab /*
571920c8df6SMauro Carvalho Chehab  * i5400_process_fatal_error_info(struct mem_ctl_info *mci,
572920c8df6SMauro Carvalho Chehab  * 				struct i5400_error_info *info,
573920c8df6SMauro Carvalho Chehab  * 				int handle_errors);
574920c8df6SMauro Carvalho Chehab  *
575920c8df6SMauro Carvalho Chehab  *	handle the Intel NON-FATAL errors, if any
576920c8df6SMauro Carvalho Chehab  */
i5400_process_nonfatal_error_info(struct mem_ctl_info * mci,struct i5400_error_info * info)577920c8df6SMauro Carvalho Chehab static void i5400_process_nonfatal_error_info(struct mem_ctl_info *mci,
578920c8df6SMauro Carvalho Chehab 					struct i5400_error_info *info)
579920c8df6SMauro Carvalho Chehab {
580920c8df6SMauro Carvalho Chehab 	char msg[EDAC_MC_LABEL_LEN + 1 + 90 + 80];
581920c8df6SMauro Carvalho Chehab 	unsigned long allErrors;
582920c8df6SMauro Carvalho Chehab 	int branch;
583920c8df6SMauro Carvalho Chehab 	int channel;
584920c8df6SMauro Carvalho Chehab 	int bank;
585920c8df6SMauro Carvalho Chehab 	int rank;
586920c8df6SMauro Carvalho Chehab 	int rdwr;
587920c8df6SMauro Carvalho Chehab 	int ras, cas;
588920c8df6SMauro Carvalho Chehab 	int errnum;
589920c8df6SMauro Carvalho Chehab 
590920c8df6SMauro Carvalho Chehab 	/* mask off the Error bits that are possible */
5918375d490SMauro Carvalho Chehab 	allErrors = from_nf_ferr(info->ferr_nf_fbd & FERR_NF_MASK);
592920c8df6SMauro Carvalho Chehab 	if (!allErrors)
593920c8df6SMauro Carvalho Chehab 		return;		/* if no error, return now */
594920c8df6SMauro Carvalho Chehab 
595920c8df6SMauro Carvalho Chehab 	/* ONLY ONE of the possible error bits will be set, as per the docs */
596920c8df6SMauro Carvalho Chehab 
597920c8df6SMauro Carvalho Chehab 	if (allErrors & (ERROR_NF_UNCORRECTABLE | ERROR_NF_RECOVERABLE)) {
598920c8df6SMauro Carvalho Chehab 		i5400_proccess_non_recoverable_info(mci, info, allErrors);
599920c8df6SMauro Carvalho Chehab 		return;
600920c8df6SMauro Carvalho Chehab 	}
601920c8df6SMauro Carvalho Chehab 
602920c8df6SMauro Carvalho Chehab 	/* Correctable errors */
603920c8df6SMauro Carvalho Chehab 	if (allErrors & ERROR_NF_CORRECTABLE) {
604956b9ba1SJoe Perches 		edac_dbg(0, "\tCorrected bits= 0x%lx\n", allErrors);
605920c8df6SMauro Carvalho Chehab 
606920c8df6SMauro Carvalho Chehab 		branch = extract_fbdchan_indx(info->ferr_nf_fbd);
607920c8df6SMauro Carvalho Chehab 
608920c8df6SMauro Carvalho Chehab 		channel = 0;
609920c8df6SMauro Carvalho Chehab 		if (REC_ECC_LOCATOR_ODD(info->redmemb))
610920c8df6SMauro Carvalho Chehab 			channel = 1;
611920c8df6SMauro Carvalho Chehab 
612920c8df6SMauro Carvalho Chehab 		/* Convert channel to be based from zero, instead of
613920c8df6SMauro Carvalho Chehab 		 * from branch base of 0 */
614920c8df6SMauro Carvalho Chehab 		channel += branch;
615920c8df6SMauro Carvalho Chehab 
616920c8df6SMauro Carvalho Chehab 		bank = rec_bank(info);
617920c8df6SMauro Carvalho Chehab 		rank = rec_rank(info);
618920c8df6SMauro Carvalho Chehab 		rdwr = rec_rdwr(info);
619920c8df6SMauro Carvalho Chehab 		ras = rec_ras(info);
620920c8df6SMauro Carvalho Chehab 		cas = rec_cas(info);
621920c8df6SMauro Carvalho Chehab 
622920c8df6SMauro Carvalho Chehab 		/* Only 1 bit will be on */
623920c8df6SMauro Carvalho Chehab 		errnum = find_first_bit(&allErrors, ARRAY_SIZE(error_name));
624920c8df6SMauro Carvalho Chehab 
625956b9ba1SJoe Perches 		edac_dbg(0, "\t\tDIMM= %d Channel= %d  (Branch %d DRAM Bank= %d rdwr= %s ras= %d cas= %d)\n",
626920c8df6SMauro Carvalho Chehab 			 rank, channel, branch >> 1, bank,
627920c8df6SMauro Carvalho Chehab 			 rdwr_str(rdwr), ras, cas);
628920c8df6SMauro Carvalho Chehab 
629920c8df6SMauro Carvalho Chehab 		/* Form out message */
630920c8df6SMauro Carvalho Chehab 		snprintf(msg, sizeof(msg),
6318375d490SMauro Carvalho Chehab 			 "Corrected error (Branch=%d DRAM-Bank=%d RDWR=%s "
6328375d490SMauro Carvalho Chehab 			 "RAS=%d CAS=%d, CE Err=0x%lx (%s))",
6338375d490SMauro Carvalho Chehab 			 branch >> 1, bank, rdwr_str(rdwr), ras, cas,
6348375d490SMauro Carvalho Chehab 			 allErrors, error_name[errnum]);
635920c8df6SMauro Carvalho Chehab 
6369eb07a7fSMauro Carvalho Chehab 		edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1, 0, 0, 0,
637296da591SMauro Carvalho Chehab 				     branch >> 1, channel % 2, rank,
638296da591SMauro Carvalho Chehab 				     rdwr ? "Write error" : "Read error",
63903f7eae8SMauro Carvalho Chehab 				     msg);
640920c8df6SMauro Carvalho Chehab 
641920c8df6SMauro Carvalho Chehab 		return;
642920c8df6SMauro Carvalho Chehab 	}
643920c8df6SMauro Carvalho Chehab 
64425985edcSLucas De Marchi 	/* Miscellaneous errors */
645920c8df6SMauro Carvalho Chehab 	errnum = find_first_bit(&allErrors, ARRAY_SIZE(error_name));
646920c8df6SMauro Carvalho Chehab 
647920c8df6SMauro Carvalho Chehab 	branch = extract_fbdchan_indx(info->ferr_nf_fbd);
648920c8df6SMauro Carvalho Chehab 
649920c8df6SMauro Carvalho Chehab 	i5400_mc_printk(mci, KERN_EMERG,
650920c8df6SMauro Carvalho Chehab 			"Non-Fatal misc error (Branch=%d Err=%#lx (%s))",
651920c8df6SMauro Carvalho Chehab 			branch >> 1, allErrors, error_name[errnum]);
652920c8df6SMauro Carvalho Chehab }
653920c8df6SMauro Carvalho Chehab 
654920c8df6SMauro Carvalho Chehab /*
655920c8df6SMauro Carvalho Chehab  *	i5400_process_error_info	Process the error info that is
656920c8df6SMauro Carvalho Chehab  *	in the 'info' structure, previously retrieved from hardware
657920c8df6SMauro Carvalho Chehab  */
i5400_process_error_info(struct mem_ctl_info * mci,struct i5400_error_info * info)658920c8df6SMauro Carvalho Chehab static void i5400_process_error_info(struct mem_ctl_info *mci,
659920c8df6SMauro Carvalho Chehab 				struct i5400_error_info *info)
660920c8df6SMauro Carvalho Chehab {	u32 allErrors;
661920c8df6SMauro Carvalho Chehab 
662920c8df6SMauro Carvalho Chehab 	/* First handle any fatal errors that occurred */
663920c8df6SMauro Carvalho Chehab 	allErrors = (info->ferr_fat_fbd & FERR_FAT_MASK);
664920c8df6SMauro Carvalho Chehab 	i5400_proccess_non_recoverable_info(mci, info, allErrors);
665920c8df6SMauro Carvalho Chehab 
666920c8df6SMauro Carvalho Chehab 	/* now handle any non-fatal errors that occurred */
667920c8df6SMauro Carvalho Chehab 	i5400_process_nonfatal_error_info(mci, info);
668920c8df6SMauro Carvalho Chehab }
669920c8df6SMauro Carvalho Chehab 
670920c8df6SMauro Carvalho Chehab /*
671920c8df6SMauro Carvalho Chehab  *	i5400_clear_error	Retrieve any error from the hardware
672920c8df6SMauro Carvalho Chehab  *				but do NOT process that error.
673920c8df6SMauro Carvalho Chehab  *				Used for 'clearing' out of previous errors
674920c8df6SMauro Carvalho Chehab  *				Called by the Core module.
675920c8df6SMauro Carvalho Chehab  */
i5400_clear_error(struct mem_ctl_info * mci)676920c8df6SMauro Carvalho Chehab static void i5400_clear_error(struct mem_ctl_info *mci)
677920c8df6SMauro Carvalho Chehab {
678920c8df6SMauro Carvalho Chehab 	struct i5400_error_info info;
679920c8df6SMauro Carvalho Chehab 
680920c8df6SMauro Carvalho Chehab 	i5400_get_error_info(mci, &info);
681920c8df6SMauro Carvalho Chehab }
682920c8df6SMauro Carvalho Chehab 
683920c8df6SMauro Carvalho Chehab /*
684920c8df6SMauro Carvalho Chehab  *	i5400_check_error	Retrieve and process errors reported by the
685920c8df6SMauro Carvalho Chehab  *				hardware. Called by the Core module.
686920c8df6SMauro Carvalho Chehab  */
i5400_check_error(struct mem_ctl_info * mci)687920c8df6SMauro Carvalho Chehab static void i5400_check_error(struct mem_ctl_info *mci)
688920c8df6SMauro Carvalho Chehab {
689920c8df6SMauro Carvalho Chehab 	struct i5400_error_info info;
690f30795fbSBorislav Petkov 
691920c8df6SMauro Carvalho Chehab 	i5400_get_error_info(mci, &info);
692920c8df6SMauro Carvalho Chehab 	i5400_process_error_info(mci, &info);
693920c8df6SMauro Carvalho Chehab }
694920c8df6SMauro Carvalho Chehab 
695920c8df6SMauro Carvalho Chehab /*
696920c8df6SMauro Carvalho Chehab  *	i5400_put_devices	'put' all the devices that we have
697920c8df6SMauro Carvalho Chehab  *				reserved via 'get'
698920c8df6SMauro Carvalho Chehab  */
i5400_put_devices(struct mem_ctl_info * mci)699920c8df6SMauro Carvalho Chehab static void i5400_put_devices(struct mem_ctl_info *mci)
700920c8df6SMauro Carvalho Chehab {
701920c8df6SMauro Carvalho Chehab 	struct i5400_pvt *pvt;
702920c8df6SMauro Carvalho Chehab 
703920c8df6SMauro Carvalho Chehab 	pvt = mci->pvt_info;
704920c8df6SMauro Carvalho Chehab 
705920c8df6SMauro Carvalho Chehab 	/* Decrement usage count for devices */
706920c8df6SMauro Carvalho Chehab 	pci_dev_put(pvt->branch_1);
707920c8df6SMauro Carvalho Chehab 	pci_dev_put(pvt->branch_0);
708920c8df6SMauro Carvalho Chehab 	pci_dev_put(pvt->fsb_error_regs);
709920c8df6SMauro Carvalho Chehab 	pci_dev_put(pvt->branchmap_werrors);
710920c8df6SMauro Carvalho Chehab }
711920c8df6SMauro Carvalho Chehab 
712920c8df6SMauro Carvalho Chehab /*
713920c8df6SMauro Carvalho Chehab  *	i5400_get_devices	Find and perform 'get' operation on the MCH's
714920c8df6SMauro Carvalho Chehab  *			device/functions we want to reference for this driver
715920c8df6SMauro Carvalho Chehab  *
716920c8df6SMauro Carvalho Chehab  *			Need to 'get' device 16 func 1 and func 2
717920c8df6SMauro Carvalho Chehab  */
i5400_get_devices(struct mem_ctl_info * mci,int dev_idx)718920c8df6SMauro Carvalho Chehab static int i5400_get_devices(struct mem_ctl_info *mci, int dev_idx)
719920c8df6SMauro Carvalho Chehab {
720920c8df6SMauro Carvalho Chehab 	struct i5400_pvt *pvt;
721920c8df6SMauro Carvalho Chehab 	struct pci_dev *pdev;
722920c8df6SMauro Carvalho Chehab 
723920c8df6SMauro Carvalho Chehab 	pvt = mci->pvt_info;
724920c8df6SMauro Carvalho Chehab 	pvt->branchmap_werrors = NULL;
725920c8df6SMauro Carvalho Chehab 	pvt->fsb_error_regs = NULL;
726920c8df6SMauro Carvalho Chehab 	pvt->branch_0 = NULL;
727920c8df6SMauro Carvalho Chehab 	pvt->branch_1 = NULL;
728920c8df6SMauro Carvalho Chehab 
729920c8df6SMauro Carvalho Chehab 	/* Attempt to 'get' the MCH register we want */
730920c8df6SMauro Carvalho Chehab 	pdev = NULL;
7310142877aSMauro Carvalho Chehab 	while (1) {
732920c8df6SMauro Carvalho Chehab 		pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
733920c8df6SMauro Carvalho Chehab 				      PCI_DEVICE_ID_INTEL_5400_ERR, pdev);
734920c8df6SMauro Carvalho Chehab 		if (!pdev) {
735920c8df6SMauro Carvalho Chehab 			/* End of list, leave */
736920c8df6SMauro Carvalho Chehab 			i5400_printk(KERN_ERR,
737920c8df6SMauro Carvalho Chehab 				"'system address,Process Bus' "
738920c8df6SMauro Carvalho Chehab 				"device not found:"
7390142877aSMauro Carvalho Chehab 				"vendor 0x%x device 0x%x ERR func 1 "
740920c8df6SMauro Carvalho Chehab 				"(broken BIOS?)\n",
741920c8df6SMauro Carvalho Chehab 				PCI_VENDOR_ID_INTEL,
742920c8df6SMauro Carvalho Chehab 				PCI_DEVICE_ID_INTEL_5400_ERR);
7430142877aSMauro Carvalho Chehab 			return -ENODEV;
744920c8df6SMauro Carvalho Chehab 		}
745920c8df6SMauro Carvalho Chehab 
7460142877aSMauro Carvalho Chehab 		/* Store device 16 func 1 */
7470142877aSMauro Carvalho Chehab 		if (PCI_FUNC(pdev->devfn) == 1)
7480142877aSMauro Carvalho Chehab 			break;
7490142877aSMauro Carvalho Chehab 	}
750920c8df6SMauro Carvalho Chehab 	pvt->branchmap_werrors = pdev;
7510142877aSMauro Carvalho Chehab 
7520142877aSMauro Carvalho Chehab 	pdev = NULL;
7530142877aSMauro Carvalho Chehab 	while (1) {
7540142877aSMauro Carvalho Chehab 		pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
7550142877aSMauro Carvalho Chehab 				      PCI_DEVICE_ID_INTEL_5400_ERR, pdev);
7560142877aSMauro Carvalho Chehab 		if (!pdev) {
7570142877aSMauro Carvalho Chehab 			/* End of list, leave */
7580142877aSMauro Carvalho Chehab 			i5400_printk(KERN_ERR,
7590142877aSMauro Carvalho Chehab 				"'system address,Process Bus' "
7600142877aSMauro Carvalho Chehab 				"device not found:"
7610142877aSMauro Carvalho Chehab 				"vendor 0x%x device 0x%x ERR func 2 "
7620142877aSMauro Carvalho Chehab 				"(broken BIOS?)\n",
7630142877aSMauro Carvalho Chehab 				PCI_VENDOR_ID_INTEL,
7640142877aSMauro Carvalho Chehab 				PCI_DEVICE_ID_INTEL_5400_ERR);
7650142877aSMauro Carvalho Chehab 
7660142877aSMauro Carvalho Chehab 			pci_dev_put(pvt->branchmap_werrors);
7670142877aSMauro Carvalho Chehab 			return -ENODEV;
7680142877aSMauro Carvalho Chehab 		}
7690142877aSMauro Carvalho Chehab 
7700142877aSMauro Carvalho Chehab 		/* Store device 16 func 2 */
7710142877aSMauro Carvalho Chehab 		if (PCI_FUNC(pdev->devfn) == 2)
772920c8df6SMauro Carvalho Chehab 			break;
7730142877aSMauro Carvalho Chehab 	}
774920c8df6SMauro Carvalho Chehab 	pvt->fsb_error_regs = pdev;
775920c8df6SMauro Carvalho Chehab 
776956b9ba1SJoe Perches 	edac_dbg(1, "System Address, processor bus- PCI Bus ID: %s  %x:%x\n",
777920c8df6SMauro Carvalho Chehab 		 pci_name(pvt->system_address),
778920c8df6SMauro Carvalho Chehab 		 pvt->system_address->vendor, pvt->system_address->device);
779956b9ba1SJoe Perches 	edac_dbg(1, "Branchmap, control and errors - PCI Bus ID: %s  %x:%x\n",
780920c8df6SMauro Carvalho Chehab 		 pci_name(pvt->branchmap_werrors),
781956b9ba1SJoe Perches 		 pvt->branchmap_werrors->vendor,
782956b9ba1SJoe Perches 		 pvt->branchmap_werrors->device);
783956b9ba1SJoe Perches 	edac_dbg(1, "FSB Error Regs - PCI Bus ID: %s  %x:%x\n",
784920c8df6SMauro Carvalho Chehab 		 pci_name(pvt->fsb_error_regs),
785920c8df6SMauro Carvalho Chehab 		 pvt->fsb_error_regs->vendor, pvt->fsb_error_regs->device);
786920c8df6SMauro Carvalho Chehab 
787920c8df6SMauro Carvalho Chehab 	pvt->branch_0 = pci_get_device(PCI_VENDOR_ID_INTEL,
788920c8df6SMauro Carvalho Chehab 				       PCI_DEVICE_ID_INTEL_5400_FBD0, NULL);
789920c8df6SMauro Carvalho Chehab 	if (!pvt->branch_0) {
790920c8df6SMauro Carvalho Chehab 		i5400_printk(KERN_ERR,
791920c8df6SMauro Carvalho Chehab 			"MC: 'BRANCH 0' device not found:"
792920c8df6SMauro Carvalho Chehab 			"vendor 0x%x device 0x%x Func 0 (broken BIOS?)\n",
793920c8df6SMauro Carvalho Chehab 			PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_5400_FBD0);
7940142877aSMauro Carvalho Chehab 
7950142877aSMauro Carvalho Chehab 		pci_dev_put(pvt->fsb_error_regs);
7960142877aSMauro Carvalho Chehab 		pci_dev_put(pvt->branchmap_werrors);
7970142877aSMauro Carvalho Chehab 		return -ENODEV;
798920c8df6SMauro Carvalho Chehab 	}
799920c8df6SMauro Carvalho Chehab 
800920c8df6SMauro Carvalho Chehab 	/* If this device claims to have more than 2 channels then
801920c8df6SMauro Carvalho Chehab 	 * fetch Branch 1's information
802920c8df6SMauro Carvalho Chehab 	 */
803920c8df6SMauro Carvalho Chehab 	if (pvt->maxch < CHANNELS_PER_BRANCH)
804920c8df6SMauro Carvalho Chehab 		return 0;
805920c8df6SMauro Carvalho Chehab 
806920c8df6SMauro Carvalho Chehab 	pvt->branch_1 = pci_get_device(PCI_VENDOR_ID_INTEL,
807920c8df6SMauro Carvalho Chehab 				       PCI_DEVICE_ID_INTEL_5400_FBD1, NULL);
808920c8df6SMauro Carvalho Chehab 	if (!pvt->branch_1) {
809920c8df6SMauro Carvalho Chehab 		i5400_printk(KERN_ERR,
810920c8df6SMauro Carvalho Chehab 			"MC: 'BRANCH 1' device not found:"
811920c8df6SMauro Carvalho Chehab 			"vendor 0x%x device 0x%x Func 0 "
812920c8df6SMauro Carvalho Chehab 			"(broken BIOS?)\n",
813920c8df6SMauro Carvalho Chehab 			PCI_VENDOR_ID_INTEL,
814920c8df6SMauro Carvalho Chehab 			PCI_DEVICE_ID_INTEL_5400_FBD1);
8150142877aSMauro Carvalho Chehab 
8160142877aSMauro Carvalho Chehab 		pci_dev_put(pvt->branch_0);
8170142877aSMauro Carvalho Chehab 		pci_dev_put(pvt->fsb_error_regs);
8180142877aSMauro Carvalho Chehab 		pci_dev_put(pvt->branchmap_werrors);
8190142877aSMauro Carvalho Chehab 		return -ENODEV;
820920c8df6SMauro Carvalho Chehab 	}
821920c8df6SMauro Carvalho Chehab 
822920c8df6SMauro Carvalho Chehab 	return 0;
823920c8df6SMauro Carvalho Chehab }
824920c8df6SMauro Carvalho Chehab 
825920c8df6SMauro Carvalho Chehab /*
826920c8df6SMauro Carvalho Chehab  *	determine_amb_present
827920c8df6SMauro Carvalho Chehab  *
828296da591SMauro Carvalho Chehab  *		the information is contained in DIMMS_PER_CHANNEL different
829296da591SMauro Carvalho Chehab  *		registers determining which of the DIMMS_PER_CHANNEL requires
8308375d490SMauro Carvalho Chehab  *              knowing which channel is in question
831920c8df6SMauro Carvalho Chehab  *
832920c8df6SMauro Carvalho Chehab  *	2 branches, each with 2 channels
833920c8df6SMauro Carvalho Chehab  *		b0_ambpresent0 for channel '0'
834920c8df6SMauro Carvalho Chehab  *		b0_ambpresent1 for channel '1'
835920c8df6SMauro Carvalho Chehab  *		b1_ambpresent0 for channel '2'
836920c8df6SMauro Carvalho Chehab  *		b1_ambpresent1 for channel '3'
837920c8df6SMauro Carvalho Chehab  */
determine_amb_present_reg(struct i5400_pvt * pvt,int channel)838920c8df6SMauro Carvalho Chehab static int determine_amb_present_reg(struct i5400_pvt *pvt, int channel)
839920c8df6SMauro Carvalho Chehab {
840920c8df6SMauro Carvalho Chehab 	int amb_present;
841920c8df6SMauro Carvalho Chehab 
842920c8df6SMauro Carvalho Chehab 	if (channel < CHANNELS_PER_BRANCH) {
843920c8df6SMauro Carvalho Chehab 		if (channel & 0x1)
844920c8df6SMauro Carvalho Chehab 			amb_present = pvt->b0_ambpresent1;
845920c8df6SMauro Carvalho Chehab 		else
846920c8df6SMauro Carvalho Chehab 			amb_present = pvt->b0_ambpresent0;
847920c8df6SMauro Carvalho Chehab 	} else {
848920c8df6SMauro Carvalho Chehab 		if (channel & 0x1)
849920c8df6SMauro Carvalho Chehab 			amb_present = pvt->b1_ambpresent1;
850920c8df6SMauro Carvalho Chehab 		else
851920c8df6SMauro Carvalho Chehab 			amb_present = pvt->b1_ambpresent0;
852920c8df6SMauro Carvalho Chehab 	}
853920c8df6SMauro Carvalho Chehab 
854920c8df6SMauro Carvalho Chehab 	return amb_present;
855920c8df6SMauro Carvalho Chehab }
856920c8df6SMauro Carvalho Chehab 
857920c8df6SMauro Carvalho Chehab /*
858296da591SMauro Carvalho Chehab  * determine_mtr(pvt, dimm, channel)
859920c8df6SMauro Carvalho Chehab  *
860296da591SMauro Carvalho Chehab  * return the proper MTR register as determine by the dimm and desired channel
861920c8df6SMauro Carvalho Chehab  */
determine_mtr(struct i5400_pvt * pvt,int dimm,int channel)862296da591SMauro Carvalho Chehab static int determine_mtr(struct i5400_pvt *pvt, int dimm, int channel)
863920c8df6SMauro Carvalho Chehab {
864920c8df6SMauro Carvalho Chehab 	int mtr;
865920c8df6SMauro Carvalho Chehab 	int n;
866920c8df6SMauro Carvalho Chehab 
867920c8df6SMauro Carvalho Chehab 	/* There is one MTR for each slot pair of FB-DIMMs,
868920c8df6SMauro Carvalho Chehab 	   Each slot pair may be at branch 0 or branch 1.
869920c8df6SMauro Carvalho Chehab 	 */
870296da591SMauro Carvalho Chehab 	n = dimm;
871920c8df6SMauro Carvalho Chehab 
872296da591SMauro Carvalho Chehab 	if (n >= DIMMS_PER_CHANNEL) {
873956b9ba1SJoe Perches 		edac_dbg(0, "ERROR: trying to access an invalid dimm: %d\n",
874296da591SMauro Carvalho Chehab 			 dimm);
875920c8df6SMauro Carvalho Chehab 		return 0;
876920c8df6SMauro Carvalho Chehab 	}
877920c8df6SMauro Carvalho Chehab 
878920c8df6SMauro Carvalho Chehab 	if (channel < CHANNELS_PER_BRANCH)
879920c8df6SMauro Carvalho Chehab 		mtr = pvt->b0_mtr[n];
880920c8df6SMauro Carvalho Chehab 	else
881920c8df6SMauro Carvalho Chehab 		mtr = pvt->b1_mtr[n];
882920c8df6SMauro Carvalho Chehab 
883920c8df6SMauro Carvalho Chehab 	return mtr;
884920c8df6SMauro Carvalho Chehab }
885920c8df6SMauro Carvalho Chehab 
886920c8df6SMauro Carvalho Chehab /*
887920c8df6SMauro Carvalho Chehab  */
decode_mtr(int slot_row,u16 mtr)888920c8df6SMauro Carvalho Chehab static void decode_mtr(int slot_row, u16 mtr)
889920c8df6SMauro Carvalho Chehab {
890920c8df6SMauro Carvalho Chehab 	int ans;
891920c8df6SMauro Carvalho Chehab 
892920c8df6SMauro Carvalho Chehab 	ans = MTR_DIMMS_PRESENT(mtr);
893920c8df6SMauro Carvalho Chehab 
894956b9ba1SJoe Perches 	edac_dbg(2, "\tMTR%d=0x%x:  DIMMs are %sPresent\n",
895956b9ba1SJoe Perches 		 slot_row, mtr, ans ? "" : "NOT ");
896920c8df6SMauro Carvalho Chehab 	if (!ans)
897920c8df6SMauro Carvalho Chehab 		return;
898920c8df6SMauro Carvalho Chehab 
899956b9ba1SJoe Perches 	edac_dbg(2, "\t\tWIDTH: x%d\n", MTR_DRAM_WIDTH(mtr));
900920c8df6SMauro Carvalho Chehab 
901956b9ba1SJoe Perches 	edac_dbg(2, "\t\tELECTRICAL THROTTLING is %s\n",
902920c8df6SMauro Carvalho Chehab 		 MTR_DIMMS_ETHROTTLE(mtr) ? "enabled" : "disabled");
903920c8df6SMauro Carvalho Chehab 
904956b9ba1SJoe Perches 	edac_dbg(2, "\t\tNUMBANK: %d bank(s)\n", MTR_DRAM_BANKS(mtr));
905956b9ba1SJoe Perches 	edac_dbg(2, "\t\tNUMRANK: %s\n",
906956b9ba1SJoe Perches 		 MTR_DIMM_RANK(mtr) ? "double" : "single");
907956b9ba1SJoe Perches 	edac_dbg(2, "\t\tNUMROW: %s\n",
9087e881856SJoe Perches 		 MTR_DIMM_ROWS(mtr) == 0 ? "8,192 - 13 rows" :
9097e881856SJoe Perches 		 MTR_DIMM_ROWS(mtr) == 1 ? "16,384 - 14 rows" :
9107e881856SJoe Perches 		 MTR_DIMM_ROWS(mtr) == 2 ? "32,768 - 15 rows" :
9117e881856SJoe Perches 		 "65,536 - 16 rows");
912956b9ba1SJoe Perches 	edac_dbg(2, "\t\tNUMCOL: %s\n",
9137e881856SJoe Perches 		 MTR_DIMM_COLS(mtr) == 0 ? "1,024 - 10 columns" :
9147e881856SJoe Perches 		 MTR_DIMM_COLS(mtr) == 1 ? "2,048 - 11 columns" :
9157e881856SJoe Perches 		 MTR_DIMM_COLS(mtr) == 2 ? "4,096 - 12 columns" :
9167e881856SJoe Perches 		 "reserved");
917920c8df6SMauro Carvalho Chehab }
918920c8df6SMauro Carvalho Chehab 
handle_channel(struct i5400_pvt * pvt,int dimm,int channel,struct i5400_dimm_info * dinfo)919296da591SMauro Carvalho Chehab static void handle_channel(struct i5400_pvt *pvt, int dimm, int channel,
920920c8df6SMauro Carvalho Chehab 			struct i5400_dimm_info *dinfo)
921920c8df6SMauro Carvalho Chehab {
922920c8df6SMauro Carvalho Chehab 	int mtr;
923920c8df6SMauro Carvalho Chehab 	int amb_present_reg;
924920c8df6SMauro Carvalho Chehab 	int addrBits;
925920c8df6SMauro Carvalho Chehab 
926296da591SMauro Carvalho Chehab 	mtr = determine_mtr(pvt, dimm, channel);
927920c8df6SMauro Carvalho Chehab 	if (MTR_DIMMS_PRESENT(mtr)) {
928920c8df6SMauro Carvalho Chehab 		amb_present_reg = determine_amb_present_reg(pvt, channel);
929920c8df6SMauro Carvalho Chehab 
930920c8df6SMauro Carvalho Chehab 		/* Determine if there is a DIMM present in this DIMM slot */
931296da591SMauro Carvalho Chehab 		if (amb_present_reg & (1 << dimm)) {
932920c8df6SMauro Carvalho Chehab 			/* Start with the number of bits for a Bank
933920c8df6SMauro Carvalho Chehab 			 * on the DRAM */
934920c8df6SMauro Carvalho Chehab 			addrBits = MTR_DRAM_BANKS_ADDR_BITS(mtr);
935920c8df6SMauro Carvalho Chehab 			/* Add thenumber of ROW bits */
936920c8df6SMauro Carvalho Chehab 			addrBits += MTR_DIMM_ROWS_ADDR_BITS(mtr);
937920c8df6SMauro Carvalho Chehab 			/* add the number of COLUMN bits */
938920c8df6SMauro Carvalho Chehab 			addrBits += MTR_DIMM_COLS_ADDR_BITS(mtr);
939156edd4aSJeff Roberson 			/* add the number of RANK bits */
940156edd4aSJeff Roberson 			addrBits += MTR_DIMM_RANK(mtr);
941920c8df6SMauro Carvalho Chehab 
942920c8df6SMauro Carvalho Chehab 			addrBits += 6;	/* add 64 bits per DIMM */
943920c8df6SMauro Carvalho Chehab 			addrBits -= 20;	/* divide by 2^^20 */
944920c8df6SMauro Carvalho Chehab 			addrBits -= 3;	/* 8 bits per bytes */
945920c8df6SMauro Carvalho Chehab 
946920c8df6SMauro Carvalho Chehab 			dinfo->megabytes = 1 << addrBits;
947920c8df6SMauro Carvalho Chehab 		}
948920c8df6SMauro Carvalho Chehab 	}
949920c8df6SMauro Carvalho Chehab }
950920c8df6SMauro Carvalho Chehab 
951920c8df6SMauro Carvalho Chehab /*
952920c8df6SMauro Carvalho Chehab  *	calculate_dimm_size
953920c8df6SMauro Carvalho Chehab  *
954920c8df6SMauro Carvalho Chehab  *	also will output a DIMM matrix map, if debug is enabled, for viewing
955920c8df6SMauro Carvalho Chehab  *	how the DIMMs are populated
956920c8df6SMauro Carvalho Chehab  */
calculate_dimm_size(struct i5400_pvt * pvt)957920c8df6SMauro Carvalho Chehab static void calculate_dimm_size(struct i5400_pvt *pvt)
958920c8df6SMauro Carvalho Chehab {
959920c8df6SMauro Carvalho Chehab 	struct i5400_dimm_info *dinfo;
960296da591SMauro Carvalho Chehab 	int dimm, max_dimms;
961920c8df6SMauro Carvalho Chehab 	char *p, *mem_buffer;
962920c8df6SMauro Carvalho Chehab 	int space, n;
96368d086f8SMauro Carvalho Chehab 	int channel, branch;
964920c8df6SMauro Carvalho Chehab 
965920c8df6SMauro Carvalho Chehab 	/* ================= Generate some debug output ================= */
966920c8df6SMauro Carvalho Chehab 	space = PAGE_SIZE;
967920c8df6SMauro Carvalho Chehab 	mem_buffer = p = kmalloc(space, GFP_KERNEL);
968920c8df6SMauro Carvalho Chehab 	if (p == NULL) {
969920c8df6SMauro Carvalho Chehab 		i5400_printk(KERN_ERR, "MC: %s:%s() kmalloc() failed\n",
970920c8df6SMauro Carvalho Chehab 			__FILE__, __func__);
971920c8df6SMauro Carvalho Chehab 		return;
972920c8df6SMauro Carvalho Chehab 	}
973920c8df6SMauro Carvalho Chehab 
974296da591SMauro Carvalho Chehab 	/* Scan all the actual DIMMS
975920c8df6SMauro Carvalho Chehab 	 * and calculate the information for each DIMM
976296da591SMauro Carvalho Chehab 	 * Start with the highest dimm first, to display it first
977296da591SMauro Carvalho Chehab 	 * and work toward the 0th dimm
978920c8df6SMauro Carvalho Chehab 	 */
979296da591SMauro Carvalho Chehab 	max_dimms = pvt->maxdimmperch;
980296da591SMauro Carvalho Chehab 	for (dimm = max_dimms - 1; dimm >= 0; dimm--) {
981920c8df6SMauro Carvalho Chehab 
982296da591SMauro Carvalho Chehab 		/* on an odd dimm, first output a 'boundary' marker,
983920c8df6SMauro Carvalho Chehab 		 * then reset the message buffer  */
984296da591SMauro Carvalho Chehab 		if (dimm & 0x1) {
985920c8df6SMauro Carvalho Chehab 			n = snprintf(p, space, "---------------------------"
986296da591SMauro Carvalho Chehab 					"-------------------------------");
987920c8df6SMauro Carvalho Chehab 			p += n;
988920c8df6SMauro Carvalho Chehab 			space -= n;
989956b9ba1SJoe Perches 			edac_dbg(2, "%s\n", mem_buffer);
990920c8df6SMauro Carvalho Chehab 			p = mem_buffer;
991920c8df6SMauro Carvalho Chehab 			space = PAGE_SIZE;
992920c8df6SMauro Carvalho Chehab 		}
993296da591SMauro Carvalho Chehab 		n = snprintf(p, space, "dimm %2d    ", dimm);
994920c8df6SMauro Carvalho Chehab 		p += n;
995920c8df6SMauro Carvalho Chehab 		space -= n;
996920c8df6SMauro Carvalho Chehab 
997920c8df6SMauro Carvalho Chehab 		for (channel = 0; channel < pvt->maxch; channel++) {
998296da591SMauro Carvalho Chehab 			dinfo = &pvt->dimm_info[dimm][channel];
999296da591SMauro Carvalho Chehab 			handle_channel(pvt, dimm, channel, dinfo);
1000920c8df6SMauro Carvalho Chehab 			n = snprintf(p, space, "%4d MB   | ", dinfo->megabytes);
1001920c8df6SMauro Carvalho Chehab 			p += n;
1002920c8df6SMauro Carvalho Chehab 			space -= n;
1003920c8df6SMauro Carvalho Chehab 		}
1004956b9ba1SJoe Perches 		edac_dbg(2, "%s\n", mem_buffer);
1005920c8df6SMauro Carvalho Chehab 		p = mem_buffer;
1006920c8df6SMauro Carvalho Chehab 		space = PAGE_SIZE;
1007920c8df6SMauro Carvalho Chehab 	}
1008920c8df6SMauro Carvalho Chehab 
1009920c8df6SMauro Carvalho Chehab 	/* Output the last bottom 'boundary' marker */
1010920c8df6SMauro Carvalho Chehab 	n = snprintf(p, space, "---------------------------"
1011296da591SMauro Carvalho Chehab 			"-------------------------------");
1012920c8df6SMauro Carvalho Chehab 	p += n;
1013920c8df6SMauro Carvalho Chehab 	space -= n;
1014956b9ba1SJoe Perches 	edac_dbg(2, "%s\n", mem_buffer);
1015920c8df6SMauro Carvalho Chehab 	p = mem_buffer;
1016920c8df6SMauro Carvalho Chehab 	space = PAGE_SIZE;
1017920c8df6SMauro Carvalho Chehab 
1018920c8df6SMauro Carvalho Chehab 	/* now output the 'channel' labels */
1019920c8df6SMauro Carvalho Chehab 	n = snprintf(p, space, "           ");
1020920c8df6SMauro Carvalho Chehab 	p += n;
1021920c8df6SMauro Carvalho Chehab 	space -= n;
1022920c8df6SMauro Carvalho Chehab 	for (channel = 0; channel < pvt->maxch; channel++) {
1023920c8df6SMauro Carvalho Chehab 		n = snprintf(p, space, "channel %d | ", channel);
1024920c8df6SMauro Carvalho Chehab 		p += n;
1025920c8df6SMauro Carvalho Chehab 		space -= n;
1026920c8df6SMauro Carvalho Chehab 	}
1027920c8df6SMauro Carvalho Chehab 
102868d086f8SMauro Carvalho Chehab 	space -= n;
1029956b9ba1SJoe Perches 	edac_dbg(2, "%s\n", mem_buffer);
103068d086f8SMauro Carvalho Chehab 	p = mem_buffer;
103168d086f8SMauro Carvalho Chehab 	space = PAGE_SIZE;
103268d086f8SMauro Carvalho Chehab 
103368d086f8SMauro Carvalho Chehab 	n = snprintf(p, space, "           ");
103468d086f8SMauro Carvalho Chehab 	p += n;
103568d086f8SMauro Carvalho Chehab 	for (branch = 0; branch < MAX_BRANCHES; branch++) {
103668d086f8SMauro Carvalho Chehab 		n = snprintf(p, space, "       branch %d       | ", branch);
103768d086f8SMauro Carvalho Chehab 		p += n;
103868d086f8SMauro Carvalho Chehab 		space -= n;
103968d086f8SMauro Carvalho Chehab 	}
104068d086f8SMauro Carvalho Chehab 
1041920c8df6SMauro Carvalho Chehab 	/* output the last message and free buffer */
1042956b9ba1SJoe Perches 	edac_dbg(2, "%s\n", mem_buffer);
1043920c8df6SMauro Carvalho Chehab 	kfree(mem_buffer);
1044920c8df6SMauro Carvalho Chehab }
1045920c8df6SMauro Carvalho Chehab 
1046920c8df6SMauro Carvalho Chehab /*
1047920c8df6SMauro Carvalho Chehab  *	i5400_get_mc_regs	read in the necessary registers and
1048920c8df6SMauro Carvalho Chehab  *				cache locally
1049920c8df6SMauro Carvalho Chehab  *
1050920c8df6SMauro Carvalho Chehab  *			Fills in the private data members
1051920c8df6SMauro Carvalho Chehab  */
i5400_get_mc_regs(struct mem_ctl_info * mci)1052920c8df6SMauro Carvalho Chehab static void i5400_get_mc_regs(struct mem_ctl_info *mci)
1053920c8df6SMauro Carvalho Chehab {
1054920c8df6SMauro Carvalho Chehab 	struct i5400_pvt *pvt;
1055920c8df6SMauro Carvalho Chehab 	u32 actual_tolm;
1056920c8df6SMauro Carvalho Chehab 	u16 limit;
1057920c8df6SMauro Carvalho Chehab 	int slot_row;
1058920c8df6SMauro Carvalho Chehab 	int way0, way1;
1059920c8df6SMauro Carvalho Chehab 
1060920c8df6SMauro Carvalho Chehab 	pvt = mci->pvt_info;
1061920c8df6SMauro Carvalho Chehab 
1062920c8df6SMauro Carvalho Chehab 	pci_read_config_dword(pvt->system_address, AMBASE,
1063f58d0deeSDan Carpenter 			&pvt->u.ambase_bottom);
1064920c8df6SMauro Carvalho Chehab 	pci_read_config_dword(pvt->system_address, AMBASE + sizeof(u32),
1065f58d0deeSDan Carpenter 			&pvt->u.ambase_top);
1066920c8df6SMauro Carvalho Chehab 
1067956b9ba1SJoe Perches 	edac_dbg(2, "AMBASE= 0x%lx  MAXCH= %d  MAX-DIMM-Per-CH= %d\n",
1068920c8df6SMauro Carvalho Chehab 		 (long unsigned int)pvt->ambase, pvt->maxch, pvt->maxdimmperch);
1069920c8df6SMauro Carvalho Chehab 
1070920c8df6SMauro Carvalho Chehab 	/* Get the Branch Map regs */
1071920c8df6SMauro Carvalho Chehab 	pci_read_config_word(pvt->branchmap_werrors, TOLM, &pvt->tolm);
1072920c8df6SMauro Carvalho Chehab 	pvt->tolm >>= 12;
1073956b9ba1SJoe Perches 	edac_dbg(2, "\nTOLM (number of 256M regions) =%u (0x%x)\n",
1074956b9ba1SJoe Perches 		 pvt->tolm, pvt->tolm);
1075920c8df6SMauro Carvalho Chehab 
1076920c8df6SMauro Carvalho Chehab 	actual_tolm = (u32) ((1000l * pvt->tolm) >> (30 - 28));
1077956b9ba1SJoe Perches 	edac_dbg(2, "Actual TOLM byte addr=%u.%03u GB (0x%x)\n",
1078920c8df6SMauro Carvalho Chehab 		 actual_tolm/1000, actual_tolm % 1000, pvt->tolm << 28);
1079920c8df6SMauro Carvalho Chehab 
1080920c8df6SMauro Carvalho Chehab 	pci_read_config_word(pvt->branchmap_werrors, MIR0, &pvt->mir0);
1081920c8df6SMauro Carvalho Chehab 	pci_read_config_word(pvt->branchmap_werrors, MIR1, &pvt->mir1);
1082920c8df6SMauro Carvalho Chehab 
1083920c8df6SMauro Carvalho Chehab 	/* Get the MIR[0-1] regs */
1084920c8df6SMauro Carvalho Chehab 	limit = (pvt->mir0 >> 4) & 0x0fff;
1085920c8df6SMauro Carvalho Chehab 	way0 = pvt->mir0 & 0x1;
1086920c8df6SMauro Carvalho Chehab 	way1 = pvt->mir0 & 0x2;
1087956b9ba1SJoe Perches 	edac_dbg(2, "MIR0: limit= 0x%x  WAY1= %u  WAY0= %x\n",
1088956b9ba1SJoe Perches 		 limit, way1, way0);
1089920c8df6SMauro Carvalho Chehab 	limit = (pvt->mir1 >> 4) & 0xfff;
1090920c8df6SMauro Carvalho Chehab 	way0 = pvt->mir1 & 0x1;
1091920c8df6SMauro Carvalho Chehab 	way1 = pvt->mir1 & 0x2;
1092956b9ba1SJoe Perches 	edac_dbg(2, "MIR1: limit= 0x%x  WAY1= %u  WAY0= %x\n",
1093956b9ba1SJoe Perches 		 limit, way1, way0);
1094920c8df6SMauro Carvalho Chehab 
1095920c8df6SMauro Carvalho Chehab 	/* Get the set of MTR[0-3] regs by each branch */
1096296da591SMauro Carvalho Chehab 	for (slot_row = 0; slot_row < DIMMS_PER_CHANNEL; slot_row++) {
1097156edd4aSJeff Roberson 		int where = MTR0 + (slot_row * sizeof(u16));
1098920c8df6SMauro Carvalho Chehab 
1099920c8df6SMauro Carvalho Chehab 		/* Branch 0 set of MTR registers */
1100920c8df6SMauro Carvalho Chehab 		pci_read_config_word(pvt->branch_0, where,
1101920c8df6SMauro Carvalho Chehab 				&pvt->b0_mtr[slot_row]);
1102920c8df6SMauro Carvalho Chehab 
1103956b9ba1SJoe Perches 		edac_dbg(2, "MTR%d where=0x%x B0 value=0x%x\n",
1104956b9ba1SJoe Perches 			 slot_row, where, pvt->b0_mtr[slot_row]);
1105920c8df6SMauro Carvalho Chehab 
1106920c8df6SMauro Carvalho Chehab 		if (pvt->maxch < CHANNELS_PER_BRANCH) {
1107920c8df6SMauro Carvalho Chehab 			pvt->b1_mtr[slot_row] = 0;
1108920c8df6SMauro Carvalho Chehab 			continue;
1109920c8df6SMauro Carvalho Chehab 		}
1110920c8df6SMauro Carvalho Chehab 
1111920c8df6SMauro Carvalho Chehab 		/* Branch 1 set of MTR registers */
1112920c8df6SMauro Carvalho Chehab 		pci_read_config_word(pvt->branch_1, where,
1113920c8df6SMauro Carvalho Chehab 				&pvt->b1_mtr[slot_row]);
1114956b9ba1SJoe Perches 		edac_dbg(2, "MTR%d where=0x%x B1 value=0x%x\n",
1115956b9ba1SJoe Perches 			 slot_row, where, pvt->b1_mtr[slot_row]);
1116920c8df6SMauro Carvalho Chehab 	}
1117920c8df6SMauro Carvalho Chehab 
1118920c8df6SMauro Carvalho Chehab 	/* Read and dump branch 0's MTRs */
1119956b9ba1SJoe Perches 	edac_dbg(2, "Memory Technology Registers:\n");
1120956b9ba1SJoe Perches 	edac_dbg(2, "   Branch 0:\n");
1121296da591SMauro Carvalho Chehab 	for (slot_row = 0; slot_row < DIMMS_PER_CHANNEL; slot_row++)
1122920c8df6SMauro Carvalho Chehab 		decode_mtr(slot_row, pvt->b0_mtr[slot_row]);
1123920c8df6SMauro Carvalho Chehab 
1124920c8df6SMauro Carvalho Chehab 	pci_read_config_word(pvt->branch_0, AMBPRESENT_0,
1125920c8df6SMauro Carvalho Chehab 			&pvt->b0_ambpresent0);
1126956b9ba1SJoe Perches 	edac_dbg(2, "\t\tAMB-Branch 0-present0 0x%x:\n", pvt->b0_ambpresent0);
1127920c8df6SMauro Carvalho Chehab 	pci_read_config_word(pvt->branch_0, AMBPRESENT_1,
1128920c8df6SMauro Carvalho Chehab 			&pvt->b0_ambpresent1);
1129956b9ba1SJoe Perches 	edac_dbg(2, "\t\tAMB-Branch 0-present1 0x%x:\n", pvt->b0_ambpresent1);
1130920c8df6SMauro Carvalho Chehab 
1131920c8df6SMauro Carvalho Chehab 	/* Only if we have 2 branchs (4 channels) */
1132920c8df6SMauro Carvalho Chehab 	if (pvt->maxch < CHANNELS_PER_BRANCH) {
1133920c8df6SMauro Carvalho Chehab 		pvt->b1_ambpresent0 = 0;
1134920c8df6SMauro Carvalho Chehab 		pvt->b1_ambpresent1 = 0;
1135920c8df6SMauro Carvalho Chehab 	} else {
1136920c8df6SMauro Carvalho Chehab 		/* Read and dump  branch 1's MTRs */
1137956b9ba1SJoe Perches 		edac_dbg(2, "   Branch 1:\n");
1138296da591SMauro Carvalho Chehab 		for (slot_row = 0; slot_row < DIMMS_PER_CHANNEL; slot_row++)
1139920c8df6SMauro Carvalho Chehab 			decode_mtr(slot_row, pvt->b1_mtr[slot_row]);
1140920c8df6SMauro Carvalho Chehab 
1141920c8df6SMauro Carvalho Chehab 		pci_read_config_word(pvt->branch_1, AMBPRESENT_0,
1142920c8df6SMauro Carvalho Chehab 				&pvt->b1_ambpresent0);
1143956b9ba1SJoe Perches 		edac_dbg(2, "\t\tAMB-Branch 1-present0 0x%x:\n",
1144920c8df6SMauro Carvalho Chehab 			 pvt->b1_ambpresent0);
1145920c8df6SMauro Carvalho Chehab 		pci_read_config_word(pvt->branch_1, AMBPRESENT_1,
1146920c8df6SMauro Carvalho Chehab 				&pvt->b1_ambpresent1);
1147956b9ba1SJoe Perches 		edac_dbg(2, "\t\tAMB-Branch 1-present1 0x%x:\n",
1148920c8df6SMauro Carvalho Chehab 			 pvt->b1_ambpresent1);
1149920c8df6SMauro Carvalho Chehab 	}
1150920c8df6SMauro Carvalho Chehab 
1151920c8df6SMauro Carvalho Chehab 	/* Go and determine the size of each DIMM and place in an
1152920c8df6SMauro Carvalho Chehab 	 * orderly matrix */
1153920c8df6SMauro Carvalho Chehab 	calculate_dimm_size(pvt);
1154920c8df6SMauro Carvalho Chehab }
1155920c8df6SMauro Carvalho Chehab 
1156920c8df6SMauro Carvalho Chehab /*
1157296da591SMauro Carvalho Chehab  *	i5400_init_dimms	Initialize the 'dimms' table within
1158920c8df6SMauro Carvalho Chehab  *				the mci control	structure with the
1159920c8df6SMauro Carvalho Chehab  *				addressing of memory.
1160920c8df6SMauro Carvalho Chehab  *
1161920c8df6SMauro Carvalho Chehab  *	return:
1162920c8df6SMauro Carvalho Chehab  *		0	success
1163920c8df6SMauro Carvalho Chehab  *		1	no actual memory found on this MC
1164920c8df6SMauro Carvalho Chehab  */
i5400_init_dimms(struct mem_ctl_info * mci)1165296da591SMauro Carvalho Chehab static int i5400_init_dimms(struct mem_ctl_info *mci)
1166920c8df6SMauro Carvalho Chehab {
1167920c8df6SMauro Carvalho Chehab 	struct i5400_pvt *pvt;
1168296da591SMauro Carvalho Chehab 	struct dimm_info *dimm;
1169bb66f867SMauro Carvalho Chehab 	int ndimms;
1170920c8df6SMauro Carvalho Chehab 	int mtr;
1171a895bf8bSMauro Carvalho Chehab 	int size_mb;
1172296da591SMauro Carvalho Chehab 	int  channel, slot;
1173920c8df6SMauro Carvalho Chehab 
1174920c8df6SMauro Carvalho Chehab 	pvt = mci->pvt_info;
1175920c8df6SMauro Carvalho Chehab 
1176296da591SMauro Carvalho Chehab 	ndimms = 0;
1177920c8df6SMauro Carvalho Chehab 
1178296da591SMauro Carvalho Chehab 	/*
1179296da591SMauro Carvalho Chehab 	 * FIXME: remove  pvt->dimm_info[slot][channel] and use the 3
1180296da591SMauro Carvalho Chehab 	 * layers here.
1181296da591SMauro Carvalho Chehab 	 */
1182296da591SMauro Carvalho Chehab 	for (channel = 0; channel < mci->layers[0].size * mci->layers[1].size;
1183296da591SMauro Carvalho Chehab 	     channel++) {
1184296da591SMauro Carvalho Chehab 		for (slot = 0; slot < mci->layers[2].size; slot++) {
1185296da591SMauro Carvalho Chehab 			mtr = determine_mtr(pvt, slot, channel);
1186920c8df6SMauro Carvalho Chehab 
1187296da591SMauro Carvalho Chehab 			/* if no DIMMS on this slot, continue */
1188920c8df6SMauro Carvalho Chehab 			if (!MTR_DIMMS_PRESENT(mtr))
1189920c8df6SMauro Carvalho Chehab 				continue;
1190920c8df6SMauro Carvalho Chehab 
1191bc9ad9e4SRobert Richter 			dimm = edac_get_dimm(mci, channel / 2, channel % 2, slot);
1192920c8df6SMauro Carvalho Chehab 
1193296da591SMauro Carvalho Chehab 			size_mb =  pvt->dimm_info[slot][channel].megabytes;
1194296da591SMauro Carvalho Chehab 
1195956b9ba1SJoe Perches 			edac_dbg(2, "dimm (branch %d channel %d slot %d): %d.%03d GB\n",
1196296da591SMauro Carvalho Chehab 				 channel / 2, channel % 2, slot,
1197296da591SMauro Carvalho Chehab 				 size_mb / 1000, size_mb % 1000);
1198296da591SMauro Carvalho Chehab 
1199a895bf8bSMauro Carvalho Chehab 			dimm->nr_pages = size_mb << 8;
1200084a4fccSMauro Carvalho Chehab 			dimm->grain = 8;
1201e61555c2SJérémy Lefaure 			dimm->dtype = MTR_DRAM_WIDTH(mtr) == 8 ?
1202e61555c2SJérémy Lefaure 				      DEV_X8 : DEV_X4;
1203296da591SMauro Carvalho Chehab 			dimm->mtype = MEM_FB_DDR2;
1204296da591SMauro Carvalho Chehab 			/*
1205296da591SMauro Carvalho Chehab 			 * The eccc mechanism is SDDC (aka SECC), with
1206296da591SMauro Carvalho Chehab 			 * is similar to Chipkill.
1207296da591SMauro Carvalho Chehab 			 */
1208e61555c2SJérémy Lefaure 			dimm->edac_mode = MTR_DRAM_WIDTH(mtr) == 8 ?
1209296da591SMauro Carvalho Chehab 					  EDAC_S8ECD8ED : EDAC_S4ECD4ED;
1210296da591SMauro Carvalho Chehab 			ndimms++;
1211296da591SMauro Carvalho Chehab 		}
1212084a4fccSMauro Carvalho Chehab 	}
1213920c8df6SMauro Carvalho Chehab 
1214296da591SMauro Carvalho Chehab 	/*
1215296da591SMauro Carvalho Chehab 	 * When just one memory is provided, it should be at location (0,0,0).
1216296da591SMauro Carvalho Chehab 	 * With such single-DIMM mode, the SDCC algorithm degrades to SECDEC+.
1217296da591SMauro Carvalho Chehab 	 */
1218296da591SMauro Carvalho Chehab 	if (ndimms == 1)
1219de3910ebSMauro Carvalho Chehab 		mci->dimms[0]->edac_mode = EDAC_SECDED;
1220920c8df6SMauro Carvalho Chehab 
1221296da591SMauro Carvalho Chehab 	return (ndimms == 0);
1222920c8df6SMauro Carvalho Chehab }
1223920c8df6SMauro Carvalho Chehab 
1224920c8df6SMauro Carvalho Chehab /*
1225920c8df6SMauro Carvalho Chehab  *	i5400_enable_error_reporting
1226920c8df6SMauro Carvalho Chehab  *			Turn on the memory reporting features of the hardware
1227920c8df6SMauro Carvalho Chehab  */
i5400_enable_error_reporting(struct mem_ctl_info * mci)1228920c8df6SMauro Carvalho Chehab static void i5400_enable_error_reporting(struct mem_ctl_info *mci)
1229920c8df6SMauro Carvalho Chehab {
1230920c8df6SMauro Carvalho Chehab 	struct i5400_pvt *pvt;
1231920c8df6SMauro Carvalho Chehab 	u32 fbd_error_mask;
1232920c8df6SMauro Carvalho Chehab 
1233920c8df6SMauro Carvalho Chehab 	pvt = mci->pvt_info;
1234920c8df6SMauro Carvalho Chehab 
1235920c8df6SMauro Carvalho Chehab 	/* Read the FBD Error Mask Register */
1236920c8df6SMauro Carvalho Chehab 	pci_read_config_dword(pvt->branchmap_werrors, EMASK_FBD,
1237920c8df6SMauro Carvalho Chehab 			&fbd_error_mask);
1238920c8df6SMauro Carvalho Chehab 
1239920c8df6SMauro Carvalho Chehab 	/* Enable with a '0' */
1240920c8df6SMauro Carvalho Chehab 	fbd_error_mask &= ~(ENABLE_EMASK_ALL);
1241920c8df6SMauro Carvalho Chehab 
1242920c8df6SMauro Carvalho Chehab 	pci_write_config_dword(pvt->branchmap_werrors, EMASK_FBD,
1243920c8df6SMauro Carvalho Chehab 			fbd_error_mask);
1244920c8df6SMauro Carvalho Chehab }
1245920c8df6SMauro Carvalho Chehab 
1246920c8df6SMauro Carvalho Chehab /*
1247920c8df6SMauro Carvalho Chehab  *	i5400_probe1	Probe for ONE instance of device to see if it is
1248920c8df6SMauro Carvalho Chehab  *			present.
1249920c8df6SMauro Carvalho Chehab  *	return:
1250920c8df6SMauro Carvalho Chehab  *		0 for FOUND a device
1251920c8df6SMauro Carvalho Chehab  *		< 0 for error code
1252920c8df6SMauro Carvalho Chehab  */
i5400_probe1(struct pci_dev * pdev,int dev_idx)1253920c8df6SMauro Carvalho Chehab static int i5400_probe1(struct pci_dev *pdev, int dev_idx)
1254920c8df6SMauro Carvalho Chehab {
1255920c8df6SMauro Carvalho Chehab 	struct mem_ctl_info *mci;
1256920c8df6SMauro Carvalho Chehab 	struct i5400_pvt *pvt;
1257296da591SMauro Carvalho Chehab 	struct edac_mc_layer layers[3];
1258920c8df6SMauro Carvalho Chehab 
12598375d490SMauro Carvalho Chehab 	if (dev_idx >= ARRAY_SIZE(i5400_devs))
12608375d490SMauro Carvalho Chehab 		return -EINVAL;
12618375d490SMauro Carvalho Chehab 
1262956b9ba1SJoe Perches 	edac_dbg(0, "MC: pdev bus %u dev=0x%x fn=0x%x\n",
1263956b9ba1SJoe Perches 		 pdev->bus->number,
1264920c8df6SMauro Carvalho Chehab 		 PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
1265920c8df6SMauro Carvalho Chehab 
1266920c8df6SMauro Carvalho Chehab 	/* We only are looking for func 0 of the set */
1267920c8df6SMauro Carvalho Chehab 	if (PCI_FUNC(pdev->devfn) != 0)
1268920c8df6SMauro Carvalho Chehab 		return -ENODEV;
1269920c8df6SMauro Carvalho Chehab 
1270296da591SMauro Carvalho Chehab 	/*
1271296da591SMauro Carvalho Chehab 	 * allocate a new MC control structure
1272296da591SMauro Carvalho Chehab 	 *
1273296da591SMauro Carvalho Chehab 	 * This drivers uses the DIMM slot as "csrow" and the rest as "channel".
1274920c8df6SMauro Carvalho Chehab 	 */
1275296da591SMauro Carvalho Chehab 	layers[0].type = EDAC_MC_LAYER_BRANCH;
1276296da591SMauro Carvalho Chehab 	layers[0].size = MAX_BRANCHES;
1277296da591SMauro Carvalho Chehab 	layers[0].is_virt_csrow = false;
1278296da591SMauro Carvalho Chehab 	layers[1].type = EDAC_MC_LAYER_CHANNEL;
1279296da591SMauro Carvalho Chehab 	layers[1].size = CHANNELS_PER_BRANCH;
1280296da591SMauro Carvalho Chehab 	layers[1].is_virt_csrow = false;
1281296da591SMauro Carvalho Chehab 	layers[2].type = EDAC_MC_LAYER_SLOT;
1282296da591SMauro Carvalho Chehab 	layers[2].size = DIMMS_PER_CHANNEL;
1283296da591SMauro Carvalho Chehab 	layers[2].is_virt_csrow = true;
1284ca0907b9SMauro Carvalho Chehab 	mci = edac_mc_alloc(0, ARRAY_SIZE(layers), layers, sizeof(*pvt));
1285920c8df6SMauro Carvalho Chehab 	if (mci == NULL)
1286920c8df6SMauro Carvalho Chehab 		return -ENOMEM;
1287920c8df6SMauro Carvalho Chehab 
1288956b9ba1SJoe Perches 	edac_dbg(0, "MC: mci = %p\n", mci);
1289920c8df6SMauro Carvalho Chehab 
1290fd687502SMauro Carvalho Chehab 	mci->pdev = &pdev->dev;	/* record ptr  to the generic device */
1291920c8df6SMauro Carvalho Chehab 
1292920c8df6SMauro Carvalho Chehab 	pvt = mci->pvt_info;
1293920c8df6SMauro Carvalho Chehab 	pvt->system_address = pdev;	/* Record this device in our private */
1294296da591SMauro Carvalho Chehab 	pvt->maxch = MAX_CHANNELS;
1295296da591SMauro Carvalho Chehab 	pvt->maxdimmperch = DIMMS_PER_CHANNEL;
1296920c8df6SMauro Carvalho Chehab 
1297920c8df6SMauro Carvalho Chehab 	/* 'get' the pci devices we want to reserve for our use */
1298920c8df6SMauro Carvalho Chehab 	if (i5400_get_devices(mci, dev_idx))
1299920c8df6SMauro Carvalho Chehab 		goto fail0;
1300920c8df6SMauro Carvalho Chehab 
1301920c8df6SMauro Carvalho Chehab 	/* Time to get serious */
1302920c8df6SMauro Carvalho Chehab 	i5400_get_mc_regs(mci);	/* retrieve the hardware registers */
1303920c8df6SMauro Carvalho Chehab 
1304920c8df6SMauro Carvalho Chehab 	mci->mc_idx = 0;
1305920c8df6SMauro Carvalho Chehab 	mci->mtype_cap = MEM_FLAG_FB_DDR2;
1306920c8df6SMauro Carvalho Chehab 	mci->edac_ctl_cap = EDAC_FLAG_NONE;
1307920c8df6SMauro Carvalho Chehab 	mci->edac_cap = EDAC_FLAG_NONE;
1308920c8df6SMauro Carvalho Chehab 	mci->mod_name = "i5400_edac.c";
1309920c8df6SMauro Carvalho Chehab 	mci->ctl_name = i5400_devs[dev_idx].ctl_name;
1310920c8df6SMauro Carvalho Chehab 	mci->dev_name = pci_name(pdev);
1311920c8df6SMauro Carvalho Chehab 	mci->ctl_page_to_phys = NULL;
1312920c8df6SMauro Carvalho Chehab 
1313920c8df6SMauro Carvalho Chehab 	/* Set the function pointer to an actual operation function */
1314920c8df6SMauro Carvalho Chehab 	mci->edac_check = i5400_check_error;
1315920c8df6SMauro Carvalho Chehab 
1316296da591SMauro Carvalho Chehab 	/* initialize the MC control structure 'dimms' table
1317920c8df6SMauro Carvalho Chehab 	 * with the mapping and control information */
1318296da591SMauro Carvalho Chehab 	if (i5400_init_dimms(mci)) {
1319956b9ba1SJoe Perches 		edac_dbg(0, "MC: Setting mci->edac_cap to EDAC_FLAG_NONE because i5400_init_dimms() returned nonzero value\n");
1320296da591SMauro Carvalho Chehab 		mci->edac_cap = EDAC_FLAG_NONE;	/* no dimms found */
1321920c8df6SMauro Carvalho Chehab 	} else {
1322956b9ba1SJoe Perches 		edac_dbg(1, "MC: Enable error reporting now\n");
1323920c8df6SMauro Carvalho Chehab 		i5400_enable_error_reporting(mci);
1324920c8df6SMauro Carvalho Chehab 	}
1325920c8df6SMauro Carvalho Chehab 
1326920c8df6SMauro Carvalho Chehab 	/* add this new MC control structure to EDAC's list of MCs */
1327920c8df6SMauro Carvalho Chehab 	if (edac_mc_add_mc(mci)) {
1328956b9ba1SJoe Perches 		edac_dbg(0, "MC: failed edac_mc_add_mc()\n");
1329920c8df6SMauro Carvalho Chehab 		/* FIXME: perhaps some code should go here that disables error
1330920c8df6SMauro Carvalho Chehab 		 * reporting if we just enabled it
1331920c8df6SMauro Carvalho Chehab 		 */
1332920c8df6SMauro Carvalho Chehab 		goto fail1;
1333920c8df6SMauro Carvalho Chehab 	}
1334920c8df6SMauro Carvalho Chehab 
1335920c8df6SMauro Carvalho Chehab 	i5400_clear_error(mci);
1336920c8df6SMauro Carvalho Chehab 
1337920c8df6SMauro Carvalho Chehab 	/* allocating generic PCI control info */
1338920c8df6SMauro Carvalho Chehab 	i5400_pci = edac_pci_create_generic_ctl(&pdev->dev, EDAC_MOD_STR);
1339920c8df6SMauro Carvalho Chehab 	if (!i5400_pci) {
1340920c8df6SMauro Carvalho Chehab 		printk(KERN_WARNING
1341920c8df6SMauro Carvalho Chehab 			"%s(): Unable to create PCI control\n",
1342920c8df6SMauro Carvalho Chehab 			__func__);
1343920c8df6SMauro Carvalho Chehab 		printk(KERN_WARNING
1344920c8df6SMauro Carvalho Chehab 			"%s(): PCI error report via EDAC not setup\n",
1345920c8df6SMauro Carvalho Chehab 			__func__);
1346920c8df6SMauro Carvalho Chehab 	}
1347920c8df6SMauro Carvalho Chehab 
1348920c8df6SMauro Carvalho Chehab 	return 0;
1349920c8df6SMauro Carvalho Chehab 
1350920c8df6SMauro Carvalho Chehab 	/* Error exit unwinding stack */
1351920c8df6SMauro Carvalho Chehab fail1:
1352920c8df6SMauro Carvalho Chehab 
1353920c8df6SMauro Carvalho Chehab 	i5400_put_devices(mci);
1354920c8df6SMauro Carvalho Chehab 
1355920c8df6SMauro Carvalho Chehab fail0:
1356920c8df6SMauro Carvalho Chehab 	edac_mc_free(mci);
1357920c8df6SMauro Carvalho Chehab 	return -ENODEV;
1358920c8df6SMauro Carvalho Chehab }
1359920c8df6SMauro Carvalho Chehab 
1360920c8df6SMauro Carvalho Chehab /*
1361920c8df6SMauro Carvalho Chehab  *	i5400_init_one	constructor for one instance of device
1362920c8df6SMauro Carvalho Chehab  *
1363920c8df6SMauro Carvalho Chehab  * 	returns:
1364920c8df6SMauro Carvalho Chehab  *		negative on error
1365920c8df6SMauro Carvalho Chehab  *		count (>= 0)
1366920c8df6SMauro Carvalho Chehab  */
i5400_init_one(struct pci_dev * pdev,const struct pci_device_id * id)13679b3c6e85SGreg Kroah-Hartman static int i5400_init_one(struct pci_dev *pdev, const struct pci_device_id *id)
1368920c8df6SMauro Carvalho Chehab {
1369920c8df6SMauro Carvalho Chehab 	int rc;
1370920c8df6SMauro Carvalho Chehab 
1371956b9ba1SJoe Perches 	edac_dbg(0, "MC:\n");
1372920c8df6SMauro Carvalho Chehab 
1373920c8df6SMauro Carvalho Chehab 	/* wake up device */
1374920c8df6SMauro Carvalho Chehab 	rc = pci_enable_device(pdev);
1375b425d5c8SKulikov Vasiliy 	if (rc)
1376920c8df6SMauro Carvalho Chehab 		return rc;
1377920c8df6SMauro Carvalho Chehab 
1378920c8df6SMauro Carvalho Chehab 	/* now probe and enable the device */
1379920c8df6SMauro Carvalho Chehab 	return i5400_probe1(pdev, id->driver_data);
1380920c8df6SMauro Carvalho Chehab }
1381920c8df6SMauro Carvalho Chehab 
1382920c8df6SMauro Carvalho Chehab /*
1383920c8df6SMauro Carvalho Chehab  *	i5400_remove_one	destructor for one instance of device
1384920c8df6SMauro Carvalho Chehab  *
1385920c8df6SMauro Carvalho Chehab  */
i5400_remove_one(struct pci_dev * pdev)13869b3c6e85SGreg Kroah-Hartman static void i5400_remove_one(struct pci_dev *pdev)
1387920c8df6SMauro Carvalho Chehab {
1388920c8df6SMauro Carvalho Chehab 	struct mem_ctl_info *mci;
1389920c8df6SMauro Carvalho Chehab 
1390956b9ba1SJoe Perches 	edac_dbg(0, "\n");
1391920c8df6SMauro Carvalho Chehab 
1392920c8df6SMauro Carvalho Chehab 	if (i5400_pci)
1393920c8df6SMauro Carvalho Chehab 		edac_pci_release_generic_ctl(i5400_pci);
1394920c8df6SMauro Carvalho Chehab 
1395920c8df6SMauro Carvalho Chehab 	mci = edac_mc_del_mc(&pdev->dev);
1396920c8df6SMauro Carvalho Chehab 	if (!mci)
1397920c8df6SMauro Carvalho Chehab 		return;
1398920c8df6SMauro Carvalho Chehab 
1399920c8df6SMauro Carvalho Chehab 	/* retrieve references to resources, and free those resources */
1400920c8df6SMauro Carvalho Chehab 	i5400_put_devices(mci);
1401920c8df6SMauro Carvalho Chehab 
1402c2e650c4SAristeu Rozanski 	pci_disable_device(pdev);
1403c2e650c4SAristeu Rozanski 
1404920c8df6SMauro Carvalho Chehab 	edac_mc_free(mci);
1405920c8df6SMauro Carvalho Chehab }
1406920c8df6SMauro Carvalho Chehab 
1407920c8df6SMauro Carvalho Chehab /*
1408920c8df6SMauro Carvalho Chehab  *	pci_device_id	table for which devices we are looking for
1409920c8df6SMauro Carvalho Chehab  *
1410920c8df6SMauro Carvalho Chehab  *	The "E500P" device is the first device supported.
1411920c8df6SMauro Carvalho Chehab  */
1412ba935f40SJingoo Han static const struct pci_device_id i5400_pci_tbl[] = {
1413920c8df6SMauro Carvalho Chehab 	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_5400_ERR)},
1414920c8df6SMauro Carvalho Chehab 	{0,}			/* 0 terminated list. */
1415920c8df6SMauro Carvalho Chehab };
1416920c8df6SMauro Carvalho Chehab 
1417920c8df6SMauro Carvalho Chehab MODULE_DEVICE_TABLE(pci, i5400_pci_tbl);
1418920c8df6SMauro Carvalho Chehab 
1419920c8df6SMauro Carvalho Chehab /*
1420920c8df6SMauro Carvalho Chehab  *	i5400_driver	pci_driver structure for this module
1421920c8df6SMauro Carvalho Chehab  *
1422920c8df6SMauro Carvalho Chehab  */
1423920c8df6SMauro Carvalho Chehab static struct pci_driver i5400_driver = {
14248375d490SMauro Carvalho Chehab 	.name = "i5400_edac",
1425920c8df6SMauro Carvalho Chehab 	.probe = i5400_init_one,
14269b3c6e85SGreg Kroah-Hartman 	.remove = i5400_remove_one,
1427920c8df6SMauro Carvalho Chehab 	.id_table = i5400_pci_tbl,
1428920c8df6SMauro Carvalho Chehab };
1429920c8df6SMauro Carvalho Chehab 
1430920c8df6SMauro Carvalho Chehab /*
1431920c8df6SMauro Carvalho Chehab  *	i5400_init		Module entry function
1432920c8df6SMauro Carvalho Chehab  *			Try to initialize this module for its devices
1433920c8df6SMauro Carvalho Chehab  */
i5400_init(void)1434920c8df6SMauro Carvalho Chehab static int __init i5400_init(void)
1435920c8df6SMauro Carvalho Chehab {
1436920c8df6SMauro Carvalho Chehab 	int pci_rc;
1437920c8df6SMauro Carvalho Chehab 
1438956b9ba1SJoe Perches 	edac_dbg(2, "MC:\n");
1439920c8df6SMauro Carvalho Chehab 
1440920c8df6SMauro Carvalho Chehab 	/* Ensure that the OPSTATE is set correctly for POLL or NMI */
1441920c8df6SMauro Carvalho Chehab 	opstate_init();
1442920c8df6SMauro Carvalho Chehab 
1443920c8df6SMauro Carvalho Chehab 	pci_rc = pci_register_driver(&i5400_driver);
1444920c8df6SMauro Carvalho Chehab 
1445920c8df6SMauro Carvalho Chehab 	return (pci_rc < 0) ? pci_rc : 0;
1446920c8df6SMauro Carvalho Chehab }
1447920c8df6SMauro Carvalho Chehab 
1448920c8df6SMauro Carvalho Chehab /*
1449920c8df6SMauro Carvalho Chehab  *	i5400_exit()	Module exit function
1450920c8df6SMauro Carvalho Chehab  *			Unregister the driver
1451920c8df6SMauro Carvalho Chehab  */
i5400_exit(void)1452920c8df6SMauro Carvalho Chehab static void __exit i5400_exit(void)
1453920c8df6SMauro Carvalho Chehab {
1454956b9ba1SJoe Perches 	edac_dbg(2, "MC:\n");
1455920c8df6SMauro Carvalho Chehab 	pci_unregister_driver(&i5400_driver);
1456920c8df6SMauro Carvalho Chehab }
1457920c8df6SMauro Carvalho Chehab 
1458920c8df6SMauro Carvalho Chehab module_init(i5400_init);
1459920c8df6SMauro Carvalho Chehab module_exit(i5400_exit);
1460920c8df6SMauro Carvalho Chehab 
1461920c8df6SMauro Carvalho Chehab MODULE_LICENSE("GPL");
14628375d490SMauro Carvalho Chehab MODULE_AUTHOR("Ben Woodard <woodard@redhat.com>");
146337e59f87SMauro Carvalho Chehab MODULE_AUTHOR("Mauro Carvalho Chehab");
14647d4c1ea2SAlexander A. Klimov MODULE_AUTHOR("Red Hat Inc. (https://www.redhat.com)");
14658375d490SMauro Carvalho Chehab MODULE_DESCRIPTION("MC Driver for Intel I5400 memory controllers - "
14668375d490SMauro Carvalho Chehab 		   I5400_REVISION);
1467920c8df6SMauro Carvalho Chehab 
1468920c8df6SMauro Carvalho Chehab module_param(edac_op_state, int, 0444);
1469920c8df6SMauro Carvalho Chehab MODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI");
1470