18f421c59SArthur Jones /*
28f421c59SArthur Jones * Intel 5100 Memory Controllers kernel module
38f421c59SArthur Jones *
48f421c59SArthur Jones * This file may be distributed under the terms of the
58f421c59SArthur Jones * GNU General Public License.
68f421c59SArthur Jones *
78f421c59SArthur Jones * This module is based on the following document:
88f421c59SArthur Jones *
98f421c59SArthur Jones * Intel 5100X Chipset Memory Controller Hub (MCH) - Datasheet
108f421c59SArthur Jones * http://download.intel.com/design/chipsets/datashts/318378.pdf
118f421c59SArthur Jones *
12bbead210SNils Carlson * The intel 5100 has two independent channels. EDAC core currently
13bbead210SNils Carlson * can not reflect this configuration so instead the chip-select
1425985edcSLucas De Marchi * rows for each respective channel are laid out one after another,
15bbead210SNils Carlson * the first half belonging to channel 0, the second half belonging
16bbead210SNils Carlson * to channel 1.
17d1afaa0aSMauro Carvalho Chehab *
18d1afaa0aSMauro Carvalho Chehab * This driver is for DDR2 DIMMs, and it uses chip select to select among the
19d1afaa0aSMauro Carvalho Chehab * several ranks. However, instead of showing memories as ranks, it outputs
20d1afaa0aSMauro Carvalho Chehab * them as DIMM's. An internal table creates the association between ranks
21d1afaa0aSMauro Carvalho Chehab * and DIMM's.
228f421c59SArthur Jones */
238f421c59SArthur Jones #include <linux/module.h>
248f421c59SArthur Jones #include <linux/init.h>
258f421c59SArthur Jones #include <linux/pci.h>
268f421c59SArthur Jones #include <linux/pci_ids.h>
278f421c59SArthur Jones #include <linux/edac.h>
288f421c59SArthur Jones #include <linux/delay.h>
298f421c59SArthur Jones #include <linux/mmzone.h>
309cbc6d38SNiklas Söderlund #include <linux/debugfs.h>
318f421c59SArthur Jones
3252019e40SBorislav Petkov #include "edac_module.h"
338f421c59SArthur Jones
34b238e577SArthur Jones /* register addresses */
358f421c59SArthur Jones
368f421c59SArthur Jones /* device 16, func 1 */
3743920a59SArthur Jones #define I5100_MC 0x40 /* Memory Control Register */
38295439f2SNils Carlson #define I5100_MC_SCRBEN_MASK (1 << 7)
39295439f2SNils Carlson #define I5100_MC_SCRBDONE_MASK (1 << 4)
408f421c59SArthur Jones #define I5100_MS 0x44 /* Memory Status Register */
418f421c59SArthur Jones #define I5100_SPDDATA 0x48 /* Serial Presence Detect Status Reg */
428f421c59SArthur Jones #define I5100_SPDCMD 0x4c /* Serial Presence Detect Command Reg */
438f421c59SArthur Jones #define I5100_TOLM 0x6c /* Top of Low Memory */
448f421c59SArthur Jones #define I5100_MIR0 0x80 /* Memory Interleave Range 0 */
458f421c59SArthur Jones #define I5100_MIR1 0x84 /* Memory Interleave Range 1 */
468f421c59SArthur Jones #define I5100_AMIR_0 0x8c /* Adjusted Memory Interleave Range 0 */
478f421c59SArthur Jones #define I5100_AMIR_1 0x90 /* Adjusted Memory Interleave Range 1 */
488f421c59SArthur Jones #define I5100_FERR_NF_MEM 0xa0 /* MC First Non Fatal Errors */
498f421c59SArthur Jones #define I5100_FERR_NF_MEM_M16ERR_MASK (1 << 16)
508f421c59SArthur Jones #define I5100_FERR_NF_MEM_M15ERR_MASK (1 << 15)
518f421c59SArthur Jones #define I5100_FERR_NF_MEM_M14ERR_MASK (1 << 14)
52f7952ffcSArthur Jones #define I5100_FERR_NF_MEM_M12ERR_MASK (1 << 12)
53f7952ffcSArthur Jones #define I5100_FERR_NF_MEM_M11ERR_MASK (1 << 11)
54f7952ffcSArthur Jones #define I5100_FERR_NF_MEM_M10ERR_MASK (1 << 10)
55f7952ffcSArthur Jones #define I5100_FERR_NF_MEM_M6ERR_MASK (1 << 6)
56f7952ffcSArthur Jones #define I5100_FERR_NF_MEM_M5ERR_MASK (1 << 5)
57f7952ffcSArthur Jones #define I5100_FERR_NF_MEM_M4ERR_MASK (1 << 4)
58b6378cb3SNiklas Söderlund #define I5100_FERR_NF_MEM_M1ERR_MASK (1 << 1)
598f421c59SArthur Jones #define I5100_FERR_NF_MEM_ANY_MASK \
608f421c59SArthur Jones (I5100_FERR_NF_MEM_M16ERR_MASK | \
618f421c59SArthur Jones I5100_FERR_NF_MEM_M15ERR_MASK | \
62f7952ffcSArthur Jones I5100_FERR_NF_MEM_M14ERR_MASK | \
63f7952ffcSArthur Jones I5100_FERR_NF_MEM_M12ERR_MASK | \
64f7952ffcSArthur Jones I5100_FERR_NF_MEM_M11ERR_MASK | \
65f7952ffcSArthur Jones I5100_FERR_NF_MEM_M10ERR_MASK | \
66f7952ffcSArthur Jones I5100_FERR_NF_MEM_M6ERR_MASK | \
67f7952ffcSArthur Jones I5100_FERR_NF_MEM_M5ERR_MASK | \
68f7952ffcSArthur Jones I5100_FERR_NF_MEM_M4ERR_MASK | \
69f7952ffcSArthur Jones I5100_FERR_NF_MEM_M1ERR_MASK)
708f421c59SArthur Jones #define I5100_NERR_NF_MEM 0xa4 /* MC Next Non-Fatal Errors */
71178d5a74SArthur Jones #define I5100_EMASK_MEM 0xa8 /* MC Error Mask Register */
7253ceafd6SNiklas Söderlund #define I5100_MEM0EINJMSK0 0x200 /* Injection Mask0 Register Channel 0 */
7353ceafd6SNiklas Söderlund #define I5100_MEM1EINJMSK0 0x208 /* Injection Mask0 Register Channel 1 */
7453ceafd6SNiklas Söderlund #define I5100_MEMXEINJMSK0_EINJEN (1 << 27)
7553ceafd6SNiklas Söderlund #define I5100_MEM0EINJMSK1 0x204 /* Injection Mask1 Register Channel 0 */
7653ceafd6SNiklas Söderlund #define I5100_MEM1EINJMSK1 0x206 /* Injection Mask1 Register Channel 1 */
7753ceafd6SNiklas Söderlund
7853ceafd6SNiklas Söderlund /* Device 19, Function 0 */
7953ceafd6SNiklas Söderlund #define I5100_DINJ0 0x9a
808f421c59SArthur Jones
818f421c59SArthur Jones /* device 21 and 22, func 0 */
828f421c59SArthur Jones #define I5100_MTR_0 0x154 /* Memory Technology Registers 0-3 */
838f421c59SArthur Jones #define I5100_DMIR 0x15c /* DIMM Interleave Range */
848f421c59SArthur Jones #define I5100_VALIDLOG 0x18c /* Valid Log Markers */
858f421c59SArthur Jones #define I5100_NRECMEMA 0x190 /* Non-Recoverable Memory Error Log Reg A */
868f421c59SArthur Jones #define I5100_NRECMEMB 0x194 /* Non-Recoverable Memory Error Log Reg B */
878f421c59SArthur Jones #define I5100_REDMEMA 0x198 /* Recoverable Memory Data Error Log Reg A */
888f421c59SArthur Jones #define I5100_REDMEMB 0x19c /* Recoverable Memory Data Error Log Reg B */
898f421c59SArthur Jones #define I5100_RECMEMA 0x1a0 /* Recoverable Memory Error Log Reg A */
908f421c59SArthur Jones #define I5100_RECMEMB 0x1a4 /* Recoverable Memory Error Log Reg B */
91b238e577SArthur Jones #define I5100_MTR_4 0x1b0 /* Memory Technology Registers 4,5 */
92b238e577SArthur Jones
93b238e577SArthur Jones /* bit field accessors */
94b238e577SArthur Jones
i5100_mc_scrben(u32 mc)95295439f2SNils Carlson static inline u32 i5100_mc_scrben(u32 mc)
96295439f2SNils Carlson {
97295439f2SNils Carlson return mc >> 7 & 1;
98295439f2SNils Carlson }
99295439f2SNils Carlson
i5100_mc_errdeten(u32 mc)100b238e577SArthur Jones static inline u32 i5100_mc_errdeten(u32 mc)
101b238e577SArthur Jones {
102b238e577SArthur Jones return mc >> 5 & 1;
103b238e577SArthur Jones }
104b238e577SArthur Jones
i5100_mc_scrbdone(u32 mc)105295439f2SNils Carlson static inline u32 i5100_mc_scrbdone(u32 mc)
106295439f2SNils Carlson {
107295439f2SNils Carlson return mc >> 4 & 1;
108295439f2SNils Carlson }
109295439f2SNils Carlson
i5100_spddata_rdo(u16 a)110b238e577SArthur Jones static inline u16 i5100_spddata_rdo(u16 a)
111b238e577SArthur Jones {
112b238e577SArthur Jones return a >> 15 & 1;
113b238e577SArthur Jones }
114b238e577SArthur Jones
i5100_spddata_sbe(u16 a)115b238e577SArthur Jones static inline u16 i5100_spddata_sbe(u16 a)
116b238e577SArthur Jones {
117b238e577SArthur Jones return a >> 13 & 1;
118b238e577SArthur Jones }
119b238e577SArthur Jones
i5100_spddata_busy(u16 a)120b238e577SArthur Jones static inline u16 i5100_spddata_busy(u16 a)
121b238e577SArthur Jones {
122b238e577SArthur Jones return a >> 12 & 1;
123b238e577SArthur Jones }
124b238e577SArthur Jones
i5100_spddata_data(u16 a)125b238e577SArthur Jones static inline u16 i5100_spddata_data(u16 a)
126b238e577SArthur Jones {
127b238e577SArthur Jones return a & ((1 << 8) - 1);
128b238e577SArthur Jones }
129b238e577SArthur Jones
i5100_spdcmd_create(u32 dti,u32 ckovrd,u32 sa,u32 ba,u32 data,u32 cmd)130b238e577SArthur Jones static inline u32 i5100_spdcmd_create(u32 dti, u32 ckovrd, u32 sa, u32 ba,
131b238e577SArthur Jones u32 data, u32 cmd)
132b238e577SArthur Jones {
133b238e577SArthur Jones return ((dti & ((1 << 4) - 1)) << 28) |
134b238e577SArthur Jones ((ckovrd & 1) << 27) |
135b238e577SArthur Jones ((sa & ((1 << 3) - 1)) << 24) |
136b238e577SArthur Jones ((ba & ((1 << 8) - 1)) << 16) |
137b238e577SArthur Jones ((data & ((1 << 8) - 1)) << 8) |
138b238e577SArthur Jones (cmd & 1);
139b238e577SArthur Jones }
140b238e577SArthur Jones
i5100_tolm_tolm(u16 a)141b238e577SArthur Jones static inline u16 i5100_tolm_tolm(u16 a)
142b238e577SArthur Jones {
143b238e577SArthur Jones return a >> 12 & ((1 << 4) - 1);
144b238e577SArthur Jones }
145b238e577SArthur Jones
i5100_mir_limit(u16 a)146b238e577SArthur Jones static inline u16 i5100_mir_limit(u16 a)
147b238e577SArthur Jones {
148b238e577SArthur Jones return a >> 4 & ((1 << 12) - 1);
149b238e577SArthur Jones }
150b238e577SArthur Jones
i5100_mir_way1(u16 a)151b238e577SArthur Jones static inline u16 i5100_mir_way1(u16 a)
152b238e577SArthur Jones {
153b238e577SArthur Jones return a >> 1 & 1;
154b238e577SArthur Jones }
155b238e577SArthur Jones
i5100_mir_way0(u16 a)156b238e577SArthur Jones static inline u16 i5100_mir_way0(u16 a)
157b238e577SArthur Jones {
158b238e577SArthur Jones return a & 1;
159b238e577SArthur Jones }
160b238e577SArthur Jones
i5100_ferr_nf_mem_chan_indx(u32 a)161b238e577SArthur Jones static inline u32 i5100_ferr_nf_mem_chan_indx(u32 a)
162b238e577SArthur Jones {
163b238e577SArthur Jones return a >> 28 & 1;
164b238e577SArthur Jones }
165b238e577SArthur Jones
i5100_ferr_nf_mem_any(u32 a)166b238e577SArthur Jones static inline u32 i5100_ferr_nf_mem_any(u32 a)
167b238e577SArthur Jones {
168b238e577SArthur Jones return a & I5100_FERR_NF_MEM_ANY_MASK;
169b238e577SArthur Jones }
170b238e577SArthur Jones
i5100_nerr_nf_mem_any(u32 a)171b238e577SArthur Jones static inline u32 i5100_nerr_nf_mem_any(u32 a)
172b238e577SArthur Jones {
173b238e577SArthur Jones return i5100_ferr_nf_mem_any(a);
174b238e577SArthur Jones }
175b238e577SArthur Jones
i5100_dmir_limit(u32 a)176b238e577SArthur Jones static inline u32 i5100_dmir_limit(u32 a)
177b238e577SArthur Jones {
178b238e577SArthur Jones return a >> 16 & ((1 << 11) - 1);
179b238e577SArthur Jones }
180b238e577SArthur Jones
i5100_dmir_rank(u32 a,u32 i)181b238e577SArthur Jones static inline u32 i5100_dmir_rank(u32 a, u32 i)
182b238e577SArthur Jones {
183b238e577SArthur Jones return a >> (4 * i) & ((1 << 2) - 1);
184b238e577SArthur Jones }
185b238e577SArthur Jones
i5100_mtr_present(u16 a)186b238e577SArthur Jones static inline u16 i5100_mtr_present(u16 a)
187b238e577SArthur Jones {
188b238e577SArthur Jones return a >> 10 & 1;
189b238e577SArthur Jones }
190b238e577SArthur Jones
i5100_mtr_ethrottle(u16 a)191b238e577SArthur Jones static inline u16 i5100_mtr_ethrottle(u16 a)
192b238e577SArthur Jones {
193b238e577SArthur Jones return a >> 9 & 1;
194b238e577SArthur Jones }
195b238e577SArthur Jones
i5100_mtr_width(u16 a)196b238e577SArthur Jones static inline u16 i5100_mtr_width(u16 a)
197b238e577SArthur Jones {
198b238e577SArthur Jones return a >> 8 & 1;
199b238e577SArthur Jones }
200b238e577SArthur Jones
i5100_mtr_numbank(u16 a)201b238e577SArthur Jones static inline u16 i5100_mtr_numbank(u16 a)
202b238e577SArthur Jones {
203b238e577SArthur Jones return a >> 6 & 1;
204b238e577SArthur Jones }
205b238e577SArthur Jones
i5100_mtr_numrow(u16 a)206b238e577SArthur Jones static inline u16 i5100_mtr_numrow(u16 a)
207b238e577SArthur Jones {
208b238e577SArthur Jones return a >> 2 & ((1 << 2) - 1);
209b238e577SArthur Jones }
210b238e577SArthur Jones
i5100_mtr_numcol(u16 a)211b238e577SArthur Jones static inline u16 i5100_mtr_numcol(u16 a)
212b238e577SArthur Jones {
213b238e577SArthur Jones return a & ((1 << 2) - 1);
214b238e577SArthur Jones }
215b238e577SArthur Jones
216b238e577SArthur Jones
i5100_validlog_redmemvalid(u32 a)217b238e577SArthur Jones static inline u32 i5100_validlog_redmemvalid(u32 a)
218b238e577SArthur Jones {
219b238e577SArthur Jones return a >> 2 & 1;
220b238e577SArthur Jones }
221b238e577SArthur Jones
i5100_validlog_recmemvalid(u32 a)222b238e577SArthur Jones static inline u32 i5100_validlog_recmemvalid(u32 a)
223b238e577SArthur Jones {
224b238e577SArthur Jones return a >> 1 & 1;
225b238e577SArthur Jones }
226b238e577SArthur Jones
i5100_validlog_nrecmemvalid(u32 a)227b238e577SArthur Jones static inline u32 i5100_validlog_nrecmemvalid(u32 a)
228b238e577SArthur Jones {
229b238e577SArthur Jones return a & 1;
230b238e577SArthur Jones }
231b238e577SArthur Jones
i5100_nrecmema_merr(u32 a)232b238e577SArthur Jones static inline u32 i5100_nrecmema_merr(u32 a)
233b238e577SArthur Jones {
234b238e577SArthur Jones return a >> 15 & ((1 << 5) - 1);
235b238e577SArthur Jones }
236b238e577SArthur Jones
i5100_nrecmema_bank(u32 a)237b238e577SArthur Jones static inline u32 i5100_nrecmema_bank(u32 a)
238b238e577SArthur Jones {
239b238e577SArthur Jones return a >> 12 & ((1 << 3) - 1);
240b238e577SArthur Jones }
241b238e577SArthur Jones
i5100_nrecmema_rank(u32 a)242b238e577SArthur Jones static inline u32 i5100_nrecmema_rank(u32 a)
243b238e577SArthur Jones {
244b238e577SArthur Jones return a >> 8 & ((1 << 3) - 1);
245b238e577SArthur Jones }
246b238e577SArthur Jones
i5100_nrecmemb_cas(u32 a)247b238e577SArthur Jones static inline u32 i5100_nrecmemb_cas(u32 a)
248b238e577SArthur Jones {
249b238e577SArthur Jones return a >> 16 & ((1 << 13) - 1);
250b238e577SArthur Jones }
251b238e577SArthur Jones
i5100_nrecmemb_ras(u32 a)252b238e577SArthur Jones static inline u32 i5100_nrecmemb_ras(u32 a)
253b238e577SArthur Jones {
254b238e577SArthur Jones return a & ((1 << 16) - 1);
255b238e577SArthur Jones }
256b238e577SArthur Jones
i5100_recmema_merr(u32 a)257b238e577SArthur Jones static inline u32 i5100_recmema_merr(u32 a)
258b238e577SArthur Jones {
259b238e577SArthur Jones return i5100_nrecmema_merr(a);
260b238e577SArthur Jones }
261b238e577SArthur Jones
i5100_recmema_bank(u32 a)262b238e577SArthur Jones static inline u32 i5100_recmema_bank(u32 a)
263b238e577SArthur Jones {
264b238e577SArthur Jones return i5100_nrecmema_bank(a);
265b238e577SArthur Jones }
266b238e577SArthur Jones
i5100_recmema_rank(u32 a)267b238e577SArthur Jones static inline u32 i5100_recmema_rank(u32 a)
268b238e577SArthur Jones {
269b238e577SArthur Jones return i5100_nrecmema_rank(a);
270b238e577SArthur Jones }
271b238e577SArthur Jones
i5100_recmemb_cas(u32 a)272b238e577SArthur Jones static inline u32 i5100_recmemb_cas(u32 a)
273b238e577SArthur Jones {
274b238e577SArthur Jones return i5100_nrecmemb_cas(a);
275b238e577SArthur Jones }
276b238e577SArthur Jones
i5100_recmemb_ras(u32 a)277b238e577SArthur Jones static inline u32 i5100_recmemb_ras(u32 a)
278b238e577SArthur Jones {
279b238e577SArthur Jones return i5100_nrecmemb_ras(a);
280b238e577SArthur Jones }
2818f421c59SArthur Jones
2828f421c59SArthur Jones /* some generic limits */
283b18dfd05SNils Carlson #define I5100_MAX_RANKS_PER_CHAN 6
284b18dfd05SNils Carlson #define I5100_CHANNELS 2
2858f421c59SArthur Jones #define I5100_MAX_RANKS_PER_DIMM 4
2868f421c59SArthur Jones #define I5100_DIMM_ADDR_LINES (6 - 3) /* 64 bits / 8 bits per byte */
287b18dfd05SNils Carlson #define I5100_MAX_DIMM_SLOTS_PER_CHAN 4
2888f421c59SArthur Jones #define I5100_MAX_RANK_INTERLEAVE 4
2898f421c59SArthur Jones #define I5100_MAX_DMIRS 5
290295439f2SNils Carlson #define I5100_SCRUB_REFRESH_RATE (5 * 60 * HZ)
2918f421c59SArthur Jones
2928f421c59SArthur Jones struct i5100_priv {
2938f421c59SArthur Jones /* ranks on each dimm -- 0 maps to not present -- obtained via SPD */
294b18dfd05SNils Carlson int dimm_numrank[I5100_CHANNELS][I5100_MAX_DIMM_SLOTS_PER_CHAN];
2958f421c59SArthur Jones
2968f421c59SArthur Jones /*
2978f421c59SArthur Jones * mainboard chip select map -- maps i5100 chip selects to
2988f421c59SArthur Jones * DIMM slot chip selects. In the case of only 4 ranks per
299b18dfd05SNils Carlson * channel, the mapping is fairly obvious but not unique.
300b18dfd05SNils Carlson * we map -1 -> NC and assume both channels use the same
3018f421c59SArthur Jones * map...
3028f421c59SArthur Jones *
3038f421c59SArthur Jones */
304b18dfd05SNils Carlson int dimm_csmap[I5100_MAX_DIMM_SLOTS_PER_CHAN][I5100_MAX_RANKS_PER_DIMM];
3058f421c59SArthur Jones
3068f421c59SArthur Jones /* memory interleave range */
3078f421c59SArthur Jones struct {
3088f421c59SArthur Jones u64 limit;
3098f421c59SArthur Jones unsigned way[2];
310b18dfd05SNils Carlson } mir[I5100_CHANNELS];
3118f421c59SArthur Jones
3128f421c59SArthur Jones /* adjusted memory interleave range register */
313b18dfd05SNils Carlson unsigned amir[I5100_CHANNELS];
3148f421c59SArthur Jones
3158f421c59SArthur Jones /* dimm interleave range */
3168f421c59SArthur Jones struct {
3178f421c59SArthur Jones unsigned rank[I5100_MAX_RANK_INTERLEAVE];
3188f421c59SArthur Jones u64 limit;
319b18dfd05SNils Carlson } dmir[I5100_CHANNELS][I5100_MAX_DMIRS];
3208f421c59SArthur Jones
3218f421c59SArthur Jones /* memory technology registers... */
3228f421c59SArthur Jones struct {
3238f421c59SArthur Jones unsigned present; /* 0 or 1 */
3248f421c59SArthur Jones unsigned ethrottle; /* 0 or 1 */
3258f421c59SArthur Jones unsigned width; /* 4 or 8 bits */
3268f421c59SArthur Jones unsigned numbank; /* 2 or 3 lines */
3278f421c59SArthur Jones unsigned numrow; /* 13 .. 16 lines */
3288f421c59SArthur Jones unsigned numcol; /* 11 .. 12 lines */
329b18dfd05SNils Carlson } mtr[I5100_CHANNELS][I5100_MAX_RANKS_PER_CHAN];
3308f421c59SArthur Jones
3318f421c59SArthur Jones u64 tolm; /* top of low memory in bytes */
332b18dfd05SNils Carlson unsigned ranksperchan; /* number of ranks per channel */
3338f421c59SArthur Jones
3348f421c59SArthur Jones struct pci_dev *mc; /* device 16 func 1 */
33552608ba2SNiklas Söderlund struct pci_dev *einj; /* device 19 func 0 */
3368f421c59SArthur Jones struct pci_dev *ch0mm; /* device 21 func 0 */
3378f421c59SArthur Jones struct pci_dev *ch1mm; /* device 22 func 0 */
338295439f2SNils Carlson
339295439f2SNils Carlson struct delayed_work i5100_scrubbing;
340295439f2SNils Carlson int scrub_enable;
34153ceafd6SNiklas Söderlund
34253ceafd6SNiklas Söderlund /* Error injection */
34353ceafd6SNiklas Söderlund u8 inject_channel;
34453ceafd6SNiklas Söderlund u8 inject_hlinesel;
34553ceafd6SNiklas Söderlund u8 inject_deviceptr1;
34653ceafd6SNiklas Söderlund u8 inject_deviceptr2;
34753ceafd6SNiklas Söderlund u16 inject_eccmask1;
34853ceafd6SNiklas Söderlund u16 inject_eccmask2;
3499cbc6d38SNiklas Söderlund
3509cbc6d38SNiklas Söderlund struct dentry *debugfs;
3518f421c59SArthur Jones };
3528f421c59SArthur Jones
3539cbc6d38SNiklas Söderlund static struct dentry *i5100_debugfs;
3549cbc6d38SNiklas Söderlund
355b18dfd05SNils Carlson /* map a rank/chan to a slot number on the mainboard */
i5100_rank_to_slot(const struct mem_ctl_info * mci,int chan,int rank)3568f421c59SArthur Jones static int i5100_rank_to_slot(const struct mem_ctl_info *mci,
357b18dfd05SNils Carlson int chan, int rank)
3588f421c59SArthur Jones {
3598f421c59SArthur Jones const struct i5100_priv *priv = mci->pvt_info;
3608f421c59SArthur Jones int i;
3618f421c59SArthur Jones
362b18dfd05SNils Carlson for (i = 0; i < I5100_MAX_DIMM_SLOTS_PER_CHAN; i++) {
3638f421c59SArthur Jones int j;
364b18dfd05SNils Carlson const int numrank = priv->dimm_numrank[chan][i];
3658f421c59SArthur Jones
3668f421c59SArthur Jones for (j = 0; j < numrank; j++)
3678f421c59SArthur Jones if (priv->dimm_csmap[i][j] == rank)
368b18dfd05SNils Carlson return i * 2 + chan;
3698f421c59SArthur Jones }
3708f421c59SArthur Jones
3718f421c59SArthur Jones return -1;
3728f421c59SArthur Jones }
3738f421c59SArthur Jones
i5100_err_msg(unsigned err)3748f421c59SArthur Jones static const char *i5100_err_msg(unsigned err)
3758f421c59SArthur Jones {
376b238e577SArthur Jones static const char *merrs[] = {
3778f421c59SArthur Jones "unknown", /* 0 */
3788f421c59SArthur Jones "uncorrectable data ECC on replay", /* 1 */
3798f421c59SArthur Jones "unknown", /* 2 */
3808f421c59SArthur Jones "unknown", /* 3 */
3818f421c59SArthur Jones "aliased uncorrectable demand data ECC", /* 4 */
3828f421c59SArthur Jones "aliased uncorrectable spare-copy data ECC", /* 5 */
3838f421c59SArthur Jones "aliased uncorrectable patrol data ECC", /* 6 */
3848f421c59SArthur Jones "unknown", /* 7 */
3858f421c59SArthur Jones "unknown", /* 8 */
3868f421c59SArthur Jones "unknown", /* 9 */
3878f421c59SArthur Jones "non-aliased uncorrectable demand data ECC", /* 10 */
3888f421c59SArthur Jones "non-aliased uncorrectable spare-copy data ECC", /* 11 */
3898f421c59SArthur Jones "non-aliased uncorrectable patrol data ECC", /* 12 */
3908f421c59SArthur Jones "unknown", /* 13 */
3918f421c59SArthur Jones "correctable demand data ECC", /* 14 */
3928f421c59SArthur Jones "correctable spare-copy data ECC", /* 15 */
3938f421c59SArthur Jones "correctable patrol data ECC", /* 16 */
3948f421c59SArthur Jones "unknown", /* 17 */
3958f421c59SArthur Jones "SPD protocol error", /* 18 */
3968f421c59SArthur Jones "unknown", /* 19 */
3978f421c59SArthur Jones "spare copy initiated", /* 20 */
3988f421c59SArthur Jones "spare copy completed", /* 21 */
3998f421c59SArthur Jones };
4008f421c59SArthur Jones unsigned i;
4018f421c59SArthur Jones
4028f421c59SArthur Jones for (i = 0; i < ARRAY_SIZE(merrs); i++)
4038f421c59SArthur Jones if (1 << i & err)
4048f421c59SArthur Jones return merrs[i];
4058f421c59SArthur Jones
4068f421c59SArthur Jones return "none";
4078f421c59SArthur Jones }
4088f421c59SArthur Jones
409b18dfd05SNils Carlson /* convert csrow index into a rank (per channel -- 0..5) */
i5100_csrow_to_rank(const struct mem_ctl_info * mci,unsigned int csrow)410d55c79acSRobert Richter static unsigned int i5100_csrow_to_rank(const struct mem_ctl_info *mci,
411d55c79acSRobert Richter unsigned int csrow)
4128f421c59SArthur Jones {
4138f421c59SArthur Jones const struct i5100_priv *priv = mci->pvt_info;
4148f421c59SArthur Jones
415b18dfd05SNils Carlson return csrow % priv->ranksperchan;
4168f421c59SArthur Jones }
4178f421c59SArthur Jones
418b18dfd05SNils Carlson /* convert csrow index into a channel (0..1) */
i5100_csrow_to_chan(const struct mem_ctl_info * mci,unsigned int csrow)419d55c79acSRobert Richter static unsigned int i5100_csrow_to_chan(const struct mem_ctl_info *mci,
420d55c79acSRobert Richter unsigned int csrow)
4218f421c59SArthur Jones {
4228f421c59SArthur Jones const struct i5100_priv *priv = mci->pvt_info;
4238f421c59SArthur Jones
424b18dfd05SNils Carlson return csrow / priv->ranksperchan;
4258f421c59SArthur Jones }
4268f421c59SArthur Jones
i5100_handle_ce(struct mem_ctl_info * mci,int chan,unsigned bank,unsigned rank,unsigned long syndrome,unsigned cas,unsigned ras,const char * msg)4278f421c59SArthur Jones static void i5100_handle_ce(struct mem_ctl_info *mci,
428b18dfd05SNils Carlson int chan,
4298f421c59SArthur Jones unsigned bank,
4308f421c59SArthur Jones unsigned rank,
4318f421c59SArthur Jones unsigned long syndrome,
4328f421c59SArthur Jones unsigned cas,
4338f421c59SArthur Jones unsigned ras,
4348f421c59SArthur Jones const char *msg)
4358f421c59SArthur Jones {
436d1afaa0aSMauro Carvalho Chehab char detail[80];
437084a4fccSMauro Carvalho Chehab
438d1afaa0aSMauro Carvalho Chehab /* Form out message */
439d1afaa0aSMauro Carvalho Chehab snprintf(detail, sizeof(detail),
440d1afaa0aSMauro Carvalho Chehab "bank %u, cas %u, ras %u\n",
441d1afaa0aSMauro Carvalho Chehab bank, cas, ras);
4428f421c59SArthur Jones
4439eb07a7fSMauro Carvalho Chehab edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1,
444d1afaa0aSMauro Carvalho Chehab 0, 0, syndrome,
445d1afaa0aSMauro Carvalho Chehab chan, rank, -1,
44603f7eae8SMauro Carvalho Chehab msg, detail);
4478f421c59SArthur Jones }
4488f421c59SArthur Jones
i5100_handle_ue(struct mem_ctl_info * mci,int chan,unsigned bank,unsigned rank,unsigned long syndrome,unsigned cas,unsigned ras,const char * msg)4498f421c59SArthur Jones static void i5100_handle_ue(struct mem_ctl_info *mci,
450b18dfd05SNils Carlson int chan,
4518f421c59SArthur Jones unsigned bank,
4528f421c59SArthur Jones unsigned rank,
4538f421c59SArthur Jones unsigned long syndrome,
4548f421c59SArthur Jones unsigned cas,
4558f421c59SArthur Jones unsigned ras,
4568f421c59SArthur Jones const char *msg)
4578f421c59SArthur Jones {
458d1afaa0aSMauro Carvalho Chehab char detail[80];
459084a4fccSMauro Carvalho Chehab
460d1afaa0aSMauro Carvalho Chehab /* Form out message */
461d1afaa0aSMauro Carvalho Chehab snprintf(detail, sizeof(detail),
462d1afaa0aSMauro Carvalho Chehab "bank %u, cas %u, ras %u\n",
463d1afaa0aSMauro Carvalho Chehab bank, cas, ras);
4648f421c59SArthur Jones
4659eb07a7fSMauro Carvalho Chehab edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1,
466d1afaa0aSMauro Carvalho Chehab 0, 0, syndrome,
467d1afaa0aSMauro Carvalho Chehab chan, rank, -1,
46803f7eae8SMauro Carvalho Chehab msg, detail);
4698f421c59SArthur Jones }
4708f421c59SArthur Jones
i5100_read_log(struct mem_ctl_info * mci,int chan,u32 ferr,u32 nerr)471b18dfd05SNils Carlson static void i5100_read_log(struct mem_ctl_info *mci, int chan,
4728f421c59SArthur Jones u32 ferr, u32 nerr)
4738f421c59SArthur Jones {
4748f421c59SArthur Jones struct i5100_priv *priv = mci->pvt_info;
475b18dfd05SNils Carlson struct pci_dev *pdev = (chan) ? priv->ch1mm : priv->ch0mm;
4768f421c59SArthur Jones u32 dw;
4778f421c59SArthur Jones u32 dw2;
4788f421c59SArthur Jones unsigned syndrome = 0;
4798f421c59SArthur Jones unsigned merr;
4808f421c59SArthur Jones unsigned bank;
4818f421c59SArthur Jones unsigned rank;
4828f421c59SArthur Jones unsigned cas;
4838f421c59SArthur Jones unsigned ras;
4848f421c59SArthur Jones
4858f421c59SArthur Jones pci_read_config_dword(pdev, I5100_VALIDLOG, &dw);
4868f421c59SArthur Jones
487b238e577SArthur Jones if (i5100_validlog_redmemvalid(dw)) {
4888f421c59SArthur Jones pci_read_config_dword(pdev, I5100_REDMEMA, &dw2);
489b238e577SArthur Jones syndrome = dw2;
4908f421c59SArthur Jones pci_read_config_dword(pdev, I5100_REDMEMB, &dw2);
4918f421c59SArthur Jones }
4928f421c59SArthur Jones
493b238e577SArthur Jones if (i5100_validlog_recmemvalid(dw)) {
4948f421c59SArthur Jones const char *msg;
4958f421c59SArthur Jones
4968f421c59SArthur Jones pci_read_config_dword(pdev, I5100_RECMEMA, &dw2);
497b238e577SArthur Jones merr = i5100_recmema_merr(dw2);
498b238e577SArthur Jones bank = i5100_recmema_bank(dw2);
499b238e577SArthur Jones rank = i5100_recmema_rank(dw2);
5008f421c59SArthur Jones
5018f421c59SArthur Jones pci_read_config_dword(pdev, I5100_RECMEMB, &dw2);
502b238e577SArthur Jones cas = i5100_recmemb_cas(dw2);
503b238e577SArthur Jones ras = i5100_recmemb_ras(dw2);
5048f421c59SArthur Jones
5058f421c59SArthur Jones /* FIXME: not really sure if this is what merr is...
5068f421c59SArthur Jones */
5078f421c59SArthur Jones if (!merr)
5088f421c59SArthur Jones msg = i5100_err_msg(ferr);
5098f421c59SArthur Jones else
5108f421c59SArthur Jones msg = i5100_err_msg(nerr);
5118f421c59SArthur Jones
512b18dfd05SNils Carlson i5100_handle_ce(mci, chan, bank, rank, syndrome, cas, ras, msg);
5138f421c59SArthur Jones }
5148f421c59SArthur Jones
515b238e577SArthur Jones if (i5100_validlog_nrecmemvalid(dw)) {
5168f421c59SArthur Jones const char *msg;
5178f421c59SArthur Jones
5188f421c59SArthur Jones pci_read_config_dword(pdev, I5100_NRECMEMA, &dw2);
519b238e577SArthur Jones merr = i5100_nrecmema_merr(dw2);
520b238e577SArthur Jones bank = i5100_nrecmema_bank(dw2);
521b238e577SArthur Jones rank = i5100_nrecmema_rank(dw2);
5228f421c59SArthur Jones
5238f421c59SArthur Jones pci_read_config_dword(pdev, I5100_NRECMEMB, &dw2);
524b238e577SArthur Jones cas = i5100_nrecmemb_cas(dw2);
525b238e577SArthur Jones ras = i5100_nrecmemb_ras(dw2);
5268f421c59SArthur Jones
5278f421c59SArthur Jones /* FIXME: not really sure if this is what merr is...
5288f421c59SArthur Jones */
5298f421c59SArthur Jones if (!merr)
5308f421c59SArthur Jones msg = i5100_err_msg(ferr);
5318f421c59SArthur Jones else
5328f421c59SArthur Jones msg = i5100_err_msg(nerr);
5338f421c59SArthur Jones
534b18dfd05SNils Carlson i5100_handle_ue(mci, chan, bank, rank, syndrome, cas, ras, msg);
5358f421c59SArthur Jones }
5368f421c59SArthur Jones
5378f421c59SArthur Jones pci_write_config_dword(pdev, I5100_VALIDLOG, dw);
5388f421c59SArthur Jones }
5398f421c59SArthur Jones
i5100_check_error(struct mem_ctl_info * mci)5408f421c59SArthur Jones static void i5100_check_error(struct mem_ctl_info *mci)
5418f421c59SArthur Jones {
5428f421c59SArthur Jones struct i5100_priv *priv = mci->pvt_info;
543df95e42eSNiklas Söderlund u32 dw, dw2;
5448f421c59SArthur Jones
5458f421c59SArthur Jones pci_read_config_dword(priv->mc, I5100_FERR_NF_MEM, &dw);
546b238e577SArthur Jones if (i5100_ferr_nf_mem_any(dw)) {
5478f421c59SArthur Jones
5488f421c59SArthur Jones pci_read_config_dword(priv->mc, I5100_NERR_NF_MEM, &dw2);
5498f421c59SArthur Jones
550b238e577SArthur Jones i5100_read_log(mci, i5100_ferr_nf_mem_chan_indx(dw),
551b238e577SArthur Jones i5100_ferr_nf_mem_any(dw),
552b238e577SArthur Jones i5100_nerr_nf_mem_any(dw2));
553df95e42eSNiklas Söderlund
554df95e42eSNiklas Söderlund pci_write_config_dword(priv->mc, I5100_NERR_NF_MEM, dw2);
5558f421c59SArthur Jones }
556df95e42eSNiklas Söderlund pci_write_config_dword(priv->mc, I5100_FERR_NF_MEM, dw);
5578f421c59SArthur Jones }
5588f421c59SArthur Jones
559295439f2SNils Carlson /* The i5100 chipset will scrub the entire memory once, then
560295439f2SNils Carlson * set a done bit. Continuous scrubbing is achieved by enqueing
561295439f2SNils Carlson * delayed work to a workqueue, checking every few minutes if
562295439f2SNils Carlson * the scrubbing has completed and if so reinitiating it.
563295439f2SNils Carlson */
564295439f2SNils Carlson
i5100_refresh_scrubbing(struct work_struct * work)565295439f2SNils Carlson static void i5100_refresh_scrubbing(struct work_struct *work)
566295439f2SNils Carlson {
5671cac5503SGeliang Tang struct delayed_work *i5100_scrubbing = to_delayed_work(work);
568295439f2SNils Carlson struct i5100_priv *priv = container_of(i5100_scrubbing,
569295439f2SNils Carlson struct i5100_priv,
570295439f2SNils Carlson i5100_scrubbing);
571295439f2SNils Carlson u32 dw;
572295439f2SNils Carlson
573295439f2SNils Carlson pci_read_config_dword(priv->mc, I5100_MC, &dw);
574295439f2SNils Carlson
575295439f2SNils Carlson if (priv->scrub_enable) {
576295439f2SNils Carlson
577295439f2SNils Carlson pci_read_config_dword(priv->mc, I5100_MC, &dw);
578295439f2SNils Carlson
579295439f2SNils Carlson if (i5100_mc_scrbdone(dw)) {
580295439f2SNils Carlson dw |= I5100_MC_SCRBEN_MASK;
581295439f2SNils Carlson pci_write_config_dword(priv->mc, I5100_MC, dw);
582295439f2SNils Carlson pci_read_config_dword(priv->mc, I5100_MC, &dw);
583295439f2SNils Carlson }
584295439f2SNils Carlson
585295439f2SNils Carlson schedule_delayed_work(&(priv->i5100_scrubbing),
586295439f2SNils Carlson I5100_SCRUB_REFRESH_RATE);
587295439f2SNils Carlson }
588295439f2SNils Carlson }
589295439f2SNils Carlson /*
590295439f2SNils Carlson * The bandwidth is based on experimentation, feel free to refine it.
591295439f2SNils Carlson */
i5100_set_scrub_rate(struct mem_ctl_info * mci,u32 bandwidth)592eba042a8SBorislav Petkov static int i5100_set_scrub_rate(struct mem_ctl_info *mci, u32 bandwidth)
593295439f2SNils Carlson {
594295439f2SNils Carlson struct i5100_priv *priv = mci->pvt_info;
595295439f2SNils Carlson u32 dw;
596295439f2SNils Carlson
597295439f2SNils Carlson pci_read_config_dword(priv->mc, I5100_MC, &dw);
598eba042a8SBorislav Petkov if (bandwidth) {
599295439f2SNils Carlson priv->scrub_enable = 1;
600295439f2SNils Carlson dw |= I5100_MC_SCRBEN_MASK;
601295439f2SNils Carlson schedule_delayed_work(&(priv->i5100_scrubbing),
602295439f2SNils Carlson I5100_SCRUB_REFRESH_RATE);
603295439f2SNils Carlson } else {
604295439f2SNils Carlson priv->scrub_enable = 0;
605295439f2SNils Carlson dw &= ~I5100_MC_SCRBEN_MASK;
606295439f2SNils Carlson cancel_delayed_work(&(priv->i5100_scrubbing));
607295439f2SNils Carlson }
608295439f2SNils Carlson pci_write_config_dword(priv->mc, I5100_MC, dw);
609295439f2SNils Carlson
610295439f2SNils Carlson pci_read_config_dword(priv->mc, I5100_MC, &dw);
611295439f2SNils Carlson
612eba042a8SBorislav Petkov bandwidth = 5900000 * i5100_mc_scrben(dw);
613295439f2SNils Carlson
61439094443SBorislav Petkov return bandwidth;
615295439f2SNils Carlson }
616295439f2SNils Carlson
i5100_get_scrub_rate(struct mem_ctl_info * mci)61739094443SBorislav Petkov static int i5100_get_scrub_rate(struct mem_ctl_info *mci)
618295439f2SNils Carlson {
619295439f2SNils Carlson struct i5100_priv *priv = mci->pvt_info;
620295439f2SNils Carlson u32 dw;
621295439f2SNils Carlson
622295439f2SNils Carlson pci_read_config_dword(priv->mc, I5100_MC, &dw);
623295439f2SNils Carlson
62439094443SBorislav Petkov return 5900000 * i5100_mc_scrben(dw);
625295439f2SNils Carlson }
626295439f2SNils Carlson
pci_get_device_func(unsigned vendor,unsigned device,unsigned func)6278f421c59SArthur Jones static struct pci_dev *pci_get_device_func(unsigned vendor,
6288f421c59SArthur Jones unsigned device,
6298f421c59SArthur Jones unsigned func)
6308f421c59SArthur Jones {
6318f421c59SArthur Jones struct pci_dev *ret = NULL;
6328f421c59SArthur Jones
6338f421c59SArthur Jones while (1) {
6348f421c59SArthur Jones ret = pci_get_device(vendor, device, ret);
6358f421c59SArthur Jones
6368f421c59SArthur Jones if (!ret)
6378f421c59SArthur Jones break;
6388f421c59SArthur Jones
6398f421c59SArthur Jones if (PCI_FUNC(ret->devfn) == func)
6408f421c59SArthur Jones break;
6418f421c59SArthur Jones }
6428f421c59SArthur Jones
6438f421c59SArthur Jones return ret;
6448f421c59SArthur Jones }
6458f421c59SArthur Jones
i5100_npages(struct mem_ctl_info * mci,unsigned int csrow)646d55c79acSRobert Richter static unsigned long i5100_npages(struct mem_ctl_info *mci, unsigned int csrow)
6478f421c59SArthur Jones {
6488f421c59SArthur Jones struct i5100_priv *priv = mci->pvt_info;
649d55c79acSRobert Richter const unsigned int chan_rank = i5100_csrow_to_rank(mci, csrow);
650d55c79acSRobert Richter const unsigned int chan = i5100_csrow_to_chan(mci, csrow);
6518f421c59SArthur Jones unsigned addr_lines;
6528f421c59SArthur Jones
6538f421c59SArthur Jones /* dimm present? */
654b18dfd05SNils Carlson if (!priv->mtr[chan][chan_rank].present)
6558f421c59SArthur Jones return 0ULL;
6568f421c59SArthur Jones
6578f421c59SArthur Jones addr_lines =
6588f421c59SArthur Jones I5100_DIMM_ADDR_LINES +
659b18dfd05SNils Carlson priv->mtr[chan][chan_rank].numcol +
660b18dfd05SNils Carlson priv->mtr[chan][chan_rank].numrow +
661b18dfd05SNils Carlson priv->mtr[chan][chan_rank].numbank;
6628f421c59SArthur Jones
6638f421c59SArthur Jones return (unsigned long)
6648f421c59SArthur Jones ((unsigned long long) (1ULL << addr_lines) / PAGE_SIZE);
6658f421c59SArthur Jones }
6668f421c59SArthur Jones
i5100_init_mtr(struct mem_ctl_info * mci)6679b3c6e85SGreg Kroah-Hartman static void i5100_init_mtr(struct mem_ctl_info *mci)
6688f421c59SArthur Jones {
6698f421c59SArthur Jones struct i5100_priv *priv = mci->pvt_info;
6708f421c59SArthur Jones struct pci_dev *mms[2] = { priv->ch0mm, priv->ch1mm };
6718f421c59SArthur Jones int i;
6728f421c59SArthur Jones
673b18dfd05SNils Carlson for (i = 0; i < I5100_CHANNELS; i++) {
6748f421c59SArthur Jones int j;
6758f421c59SArthur Jones struct pci_dev *pdev = mms[i];
6768f421c59SArthur Jones
677b18dfd05SNils Carlson for (j = 0; j < I5100_MAX_RANKS_PER_CHAN; j++) {
6788f421c59SArthur Jones const unsigned addr =
6798f421c59SArthur Jones (j < 4) ? I5100_MTR_0 + j * 2 :
6808f421c59SArthur Jones I5100_MTR_4 + (j - 4) * 2;
6818f421c59SArthur Jones u16 w;
6828f421c59SArthur Jones
6838f421c59SArthur Jones pci_read_config_word(pdev, addr, &w);
6848f421c59SArthur Jones
685b238e577SArthur Jones priv->mtr[i][j].present = i5100_mtr_present(w);
686b238e577SArthur Jones priv->mtr[i][j].ethrottle = i5100_mtr_ethrottle(w);
687b238e577SArthur Jones priv->mtr[i][j].width = 4 + 4 * i5100_mtr_width(w);
688b238e577SArthur Jones priv->mtr[i][j].numbank = 2 + i5100_mtr_numbank(w);
689b238e577SArthur Jones priv->mtr[i][j].numrow = 13 + i5100_mtr_numrow(w);
690b238e577SArthur Jones priv->mtr[i][j].numcol = 10 + i5100_mtr_numcol(w);
6918f421c59SArthur Jones }
6928f421c59SArthur Jones }
6938f421c59SArthur Jones }
6948f421c59SArthur Jones
6958f421c59SArthur Jones /*
6968f421c59SArthur Jones * FIXME: make this into a real i2c adapter (so that dimm-decode
6978f421c59SArthur Jones * will work)?
6988f421c59SArthur Jones */
i5100_read_spd_byte(const struct mem_ctl_info * mci,u8 ch,u8 slot,u8 addr,u8 * byte)6998f421c59SArthur Jones static int i5100_read_spd_byte(const struct mem_ctl_info *mci,
7008f421c59SArthur Jones u8 ch, u8 slot, u8 addr, u8 *byte)
7018f421c59SArthur Jones {
7028f421c59SArthur Jones struct i5100_priv *priv = mci->pvt_info;
7038f421c59SArthur Jones u16 w;
7048f421c59SArthur Jones
7058f421c59SArthur Jones pci_read_config_word(priv->mc, I5100_SPDDATA, &w);
706b238e577SArthur Jones if (i5100_spddata_busy(w))
7078f421c59SArthur Jones return -1;
7088f421c59SArthur Jones
709b238e577SArthur Jones pci_write_config_dword(priv->mc, I5100_SPDCMD,
710b238e577SArthur Jones i5100_spdcmd_create(0xa, 1, ch * 4 + slot, addr,
711b238e577SArthur Jones 0, 0));
7128f421c59SArthur Jones
7138f421c59SArthur Jones /* wait up to 100ms */
7148f421c59SArthur Jones udelay(100);
7158f421c59SArthur Jones while (1) {
7168f421c59SArthur Jones pci_read_config_word(priv->mc, I5100_SPDDATA, &w);
717b238e577SArthur Jones if (!i5100_spddata_busy(w))
7188f421c59SArthur Jones break;
7198f421c59SArthur Jones udelay(100);
7208f421c59SArthur Jones }
7218f421c59SArthur Jones
722b238e577SArthur Jones if (!i5100_spddata_rdo(w) || i5100_spddata_sbe(w))
7238f421c59SArthur Jones return -1;
7248f421c59SArthur Jones
725b238e577SArthur Jones *byte = i5100_spddata_data(w);
7268f421c59SArthur Jones
7278f421c59SArthur Jones return 0;
7288f421c59SArthur Jones }
7298f421c59SArthur Jones
7308f421c59SArthur Jones /*
7318f421c59SArthur Jones * fill dimm chip select map
7328f421c59SArthur Jones *
7338f421c59SArthur Jones * FIXME:
7348f421c59SArthur Jones * o not the only way to may chip selects to dimm slots
7358f421c59SArthur Jones * o investigate if there is some way to obtain this map from the bios
7368f421c59SArthur Jones */
i5100_init_dimm_csmap(struct mem_ctl_info * mci)7379b3c6e85SGreg Kroah-Hartman static void i5100_init_dimm_csmap(struct mem_ctl_info *mci)
7388f421c59SArthur Jones {
7398f421c59SArthur Jones struct i5100_priv *priv = mci->pvt_info;
7408f421c59SArthur Jones int i;
7418f421c59SArthur Jones
742b18dfd05SNils Carlson for (i = 0; i < I5100_MAX_DIMM_SLOTS_PER_CHAN; i++) {
7438f421c59SArthur Jones int j;
7448f421c59SArthur Jones
7458f421c59SArthur Jones for (j = 0; j < I5100_MAX_RANKS_PER_DIMM; j++)
7468f421c59SArthur Jones priv->dimm_csmap[i][j] = -1; /* default NC */
7478f421c59SArthur Jones }
7488f421c59SArthur Jones
7498f421c59SArthur Jones /* only 2 chip selects per slot... */
750bbead210SNils Carlson if (priv->ranksperchan == 4) {
7518f421c59SArthur Jones priv->dimm_csmap[0][0] = 0;
7528f421c59SArthur Jones priv->dimm_csmap[0][1] = 3;
7538f421c59SArthur Jones priv->dimm_csmap[1][0] = 1;
7548f421c59SArthur Jones priv->dimm_csmap[1][1] = 2;
7558f421c59SArthur Jones priv->dimm_csmap[2][0] = 2;
7568f421c59SArthur Jones priv->dimm_csmap[3][0] = 3;
757bbead210SNils Carlson } else {
758bbead210SNils Carlson priv->dimm_csmap[0][0] = 0;
759bbead210SNils Carlson priv->dimm_csmap[0][1] = 1;
760bbead210SNils Carlson priv->dimm_csmap[1][0] = 2;
761bbead210SNils Carlson priv->dimm_csmap[1][1] = 3;
762bbead210SNils Carlson priv->dimm_csmap[2][0] = 4;
763bbead210SNils Carlson priv->dimm_csmap[2][1] = 5;
764bbead210SNils Carlson }
7658f421c59SArthur Jones }
7668f421c59SArthur Jones
i5100_init_dimm_layout(struct pci_dev * pdev,struct mem_ctl_info * mci)7679b3c6e85SGreg Kroah-Hartman static void i5100_init_dimm_layout(struct pci_dev *pdev,
7688f421c59SArthur Jones struct mem_ctl_info *mci)
7698f421c59SArthur Jones {
7708f421c59SArthur Jones struct i5100_priv *priv = mci->pvt_info;
7718f421c59SArthur Jones int i;
7728f421c59SArthur Jones
773b18dfd05SNils Carlson for (i = 0; i < I5100_CHANNELS; i++) {
7748f421c59SArthur Jones int j;
7758f421c59SArthur Jones
776b18dfd05SNils Carlson for (j = 0; j < I5100_MAX_DIMM_SLOTS_PER_CHAN; j++) {
7778f421c59SArthur Jones u8 rank;
7788f421c59SArthur Jones
7798f421c59SArthur Jones if (i5100_read_spd_byte(mci, i, j, 5, &rank) < 0)
7808f421c59SArthur Jones priv->dimm_numrank[i][j] = 0;
7818f421c59SArthur Jones else
7828f421c59SArthur Jones priv->dimm_numrank[i][j] = (rank & 3) + 1;
7838f421c59SArthur Jones }
7848f421c59SArthur Jones }
7858f421c59SArthur Jones
7868f421c59SArthur Jones i5100_init_dimm_csmap(mci);
7878f421c59SArthur Jones }
7888f421c59SArthur Jones
i5100_init_interleaving(struct pci_dev * pdev,struct mem_ctl_info * mci)7899b3c6e85SGreg Kroah-Hartman static void i5100_init_interleaving(struct pci_dev *pdev,
7908f421c59SArthur Jones struct mem_ctl_info *mci)
7918f421c59SArthur Jones {
7928f421c59SArthur Jones u16 w;
7938f421c59SArthur Jones u32 dw;
7948f421c59SArthur Jones struct i5100_priv *priv = mci->pvt_info;
7958f421c59SArthur Jones struct pci_dev *mms[2] = { priv->ch0mm, priv->ch1mm };
7968f421c59SArthur Jones int i;
7978f421c59SArthur Jones
7988f421c59SArthur Jones pci_read_config_word(pdev, I5100_TOLM, &w);
799b238e577SArthur Jones priv->tolm = (u64) i5100_tolm_tolm(w) * 256 * 1024 * 1024;
8008f421c59SArthur Jones
8018f421c59SArthur Jones pci_read_config_word(pdev, I5100_MIR0, &w);
802b238e577SArthur Jones priv->mir[0].limit = (u64) i5100_mir_limit(w) << 28;
803b238e577SArthur Jones priv->mir[0].way[1] = i5100_mir_way1(w);
804b238e577SArthur Jones priv->mir[0].way[0] = i5100_mir_way0(w);
8058f421c59SArthur Jones
8068f421c59SArthur Jones pci_read_config_word(pdev, I5100_MIR1, &w);
807b238e577SArthur Jones priv->mir[1].limit = (u64) i5100_mir_limit(w) << 28;
808b238e577SArthur Jones priv->mir[1].way[1] = i5100_mir_way1(w);
809b238e577SArthur Jones priv->mir[1].way[0] = i5100_mir_way0(w);
8108f421c59SArthur Jones
8118f421c59SArthur Jones pci_read_config_word(pdev, I5100_AMIR_0, &w);
8128f421c59SArthur Jones priv->amir[0] = w;
8138f421c59SArthur Jones pci_read_config_word(pdev, I5100_AMIR_1, &w);
8148f421c59SArthur Jones priv->amir[1] = w;
8158f421c59SArthur Jones
816b18dfd05SNils Carlson for (i = 0; i < I5100_CHANNELS; i++) {
8178f421c59SArthur Jones int j;
8188f421c59SArthur Jones
8198f421c59SArthur Jones for (j = 0; j < 5; j++) {
8208f421c59SArthur Jones int k;
8218f421c59SArthur Jones
8228f421c59SArthur Jones pci_read_config_dword(mms[i], I5100_DMIR + j * 4, &dw);
8238f421c59SArthur Jones
8248f421c59SArthur Jones priv->dmir[i][j].limit =
825b238e577SArthur Jones (u64) i5100_dmir_limit(dw) << 28;
8268f421c59SArthur Jones for (k = 0; k < I5100_MAX_RANKS_PER_DIMM; k++)
8278f421c59SArthur Jones priv->dmir[i][j].rank[k] =
828b238e577SArthur Jones i5100_dmir_rank(dw, k);
8298f421c59SArthur Jones }
8308f421c59SArthur Jones }
8318f421c59SArthur Jones
8328f421c59SArthur Jones i5100_init_mtr(mci);
8338f421c59SArthur Jones }
8348f421c59SArthur Jones
i5100_init_csrows(struct mem_ctl_info * mci)8359b3c6e85SGreg Kroah-Hartman static void i5100_init_csrows(struct mem_ctl_info *mci)
8368f421c59SArthur Jones {
8378f421c59SArthur Jones struct i5100_priv *priv = mci->pvt_info;
838d1afaa0aSMauro Carvalho Chehab struct dimm_info *dimm;
839c498afafSRobert Richter
840c498afafSRobert Richter mci_for_each_dimm(mci, dimm) {
841c498afafSRobert Richter const unsigned long npages = i5100_npages(mci, dimm->idx);
842c498afafSRobert Richter const unsigned int chan = i5100_csrow_to_chan(mci, dimm->idx);
843c498afafSRobert Richter const unsigned int rank = i5100_csrow_to_rank(mci, dimm->idx);
8448f421c59SArthur Jones
8458f421c59SArthur Jones if (!npages)
8468f421c59SArthur Jones continue;
8478f421c59SArthur Jones
848a895bf8bSMauro Carvalho Chehab dimm->nr_pages = npages;
849084a4fccSMauro Carvalho Chehab dimm->grain = 32;
850084a4fccSMauro Carvalho Chehab dimm->dtype = (priv->mtr[chan][rank].width == 4) ?
851084a4fccSMauro Carvalho Chehab DEV_X4 : DEV_X8;
852084a4fccSMauro Carvalho Chehab dimm->mtype = MEM_RDDR2;
853084a4fccSMauro Carvalho Chehab dimm->edac_mode = EDAC_SECDED;
8549d6c7cbeSDan Carpenter snprintf(dimm->label, sizeof(dimm->label), "DIMM%u",
855084a4fccSMauro Carvalho Chehab i5100_rank_to_slot(mci, chan, rank));
856d1afaa0aSMauro Carvalho Chehab
857956b9ba1SJoe Perches edac_dbg(2, "dimm channel %d, rank %d, size %ld\n",
8589f70d08aSMauro Carvalho Chehab chan, rank, (long)PAGES_TO_MiB(npages));
8598f421c59SArthur Jones }
860a895bf8bSMauro Carvalho Chehab }
8618f421c59SArthur Jones
86253ceafd6SNiklas Söderlund /****************************************************************************
86353ceafd6SNiklas Söderlund * Error injection routines
86453ceafd6SNiklas Söderlund ****************************************************************************/
86553ceafd6SNiklas Söderlund
i5100_do_inject(struct mem_ctl_info * mci)86653ceafd6SNiklas Söderlund static void i5100_do_inject(struct mem_ctl_info *mci)
86753ceafd6SNiklas Söderlund {
86853ceafd6SNiklas Söderlund struct i5100_priv *priv = mci->pvt_info;
86953ceafd6SNiklas Söderlund u32 mask0;
87053ceafd6SNiklas Söderlund u16 mask1;
87153ceafd6SNiklas Söderlund
87253ceafd6SNiklas Söderlund /* MEM[1:0]EINJMSK0
87353ceafd6SNiklas Söderlund * 31 - ADDRMATCHEN
87453ceafd6SNiklas Söderlund * 29:28 - HLINESEL
87553ceafd6SNiklas Söderlund * 00 Reserved
87653ceafd6SNiklas Söderlund * 01 Lower half of cache line
87753ceafd6SNiklas Söderlund * 10 Upper half of cache line
87853ceafd6SNiklas Söderlund * 11 Both upper and lower parts of cache line
87953ceafd6SNiklas Söderlund * 27 - EINJEN
88053ceafd6SNiklas Söderlund * 25:19 - XORMASK1 for deviceptr1
88153ceafd6SNiklas Söderlund * 9:5 - SEC2RAM for deviceptr2
88253ceafd6SNiklas Söderlund * 4:0 - FIR2RAM for deviceptr1
88353ceafd6SNiklas Söderlund */
88453ceafd6SNiklas Söderlund mask0 = ((priv->inject_hlinesel & 0x3) << 28) |
88553ceafd6SNiklas Söderlund I5100_MEMXEINJMSK0_EINJEN |
88653ceafd6SNiklas Söderlund ((priv->inject_eccmask1 & 0xffff) << 10) |
88753ceafd6SNiklas Söderlund ((priv->inject_deviceptr2 & 0x1f) << 5) |
88853ceafd6SNiklas Söderlund (priv->inject_deviceptr1 & 0x1f);
88953ceafd6SNiklas Söderlund
89053ceafd6SNiklas Söderlund /* MEM[1:0]EINJMSK1
89153ceafd6SNiklas Söderlund * 15:0 - XORMASK2 for deviceptr2
89253ceafd6SNiklas Söderlund */
89353ceafd6SNiklas Söderlund mask1 = priv->inject_eccmask2;
89453ceafd6SNiklas Söderlund
89553ceafd6SNiklas Söderlund if (priv->inject_channel == 0) {
89653ceafd6SNiklas Söderlund pci_write_config_dword(priv->mc, I5100_MEM0EINJMSK0, mask0);
89753ceafd6SNiklas Söderlund pci_write_config_word(priv->mc, I5100_MEM0EINJMSK1, mask1);
89853ceafd6SNiklas Söderlund } else {
89953ceafd6SNiklas Söderlund pci_write_config_dword(priv->mc, I5100_MEM1EINJMSK0, mask0);
90053ceafd6SNiklas Söderlund pci_write_config_word(priv->mc, I5100_MEM1EINJMSK1, mask1);
90153ceafd6SNiklas Söderlund }
90253ceafd6SNiklas Söderlund
90353ceafd6SNiklas Söderlund /* Error Injection Response Function
90453ceafd6SNiklas Söderlund * Intel 5100 Memory Controller Hub Chipset (318378) datasheet
90553ceafd6SNiklas Söderlund * hints about this register but carry no data about them. All
90653ceafd6SNiklas Söderlund * data regarding device 19 is based on experimentation and the
90753ceafd6SNiklas Söderlund * Intel 7300 Chipset Memory Controller Hub (318082) datasheet
90853ceafd6SNiklas Söderlund * which appears to be accurate for the i5100 in this area.
90953ceafd6SNiklas Söderlund *
91053ceafd6SNiklas Söderlund * The injection code don't work without setting this register.
91153ceafd6SNiklas Söderlund * The register needs to be flipped off then on else the hardware
9125b6cb450SJongwoo Han * will only perform the first injection.
91353ceafd6SNiklas Söderlund *
91453ceafd6SNiklas Söderlund * Stop condition bits 7:4
91553ceafd6SNiklas Söderlund * 1010 - Stop after one injection
91653ceafd6SNiklas Söderlund * 1011 - Never stop injecting faults
91753ceafd6SNiklas Söderlund *
91853ceafd6SNiklas Söderlund * Start condition bits 3:0
91953ceafd6SNiklas Söderlund * 1010 - Never start
92053ceafd6SNiklas Söderlund * 1011 - Start immediately
92153ceafd6SNiklas Söderlund */
92253ceafd6SNiklas Söderlund pci_write_config_byte(priv->einj, I5100_DINJ0, 0xaa);
92353ceafd6SNiklas Söderlund pci_write_config_byte(priv->einj, I5100_DINJ0, 0xab);
92453ceafd6SNiklas Söderlund }
92553ceafd6SNiklas Söderlund
9269cbc6d38SNiklas Söderlund #define to_mci(k) container_of(k, struct mem_ctl_info, dev)
inject_enable_write(struct file * file,const char __user * data,size_t count,loff_t * ppos)9279cbc6d38SNiklas Söderlund static ssize_t inject_enable_write(struct file *file, const char __user *data,
9289cbc6d38SNiklas Söderlund size_t count, loff_t *ppos)
9299cbc6d38SNiklas Söderlund {
9309cbc6d38SNiklas Söderlund struct device *dev = file->private_data;
9319cbc6d38SNiklas Söderlund struct mem_ctl_info *mci = to_mci(dev);
9329cbc6d38SNiklas Söderlund
9339cbc6d38SNiklas Söderlund i5100_do_inject(mci);
9349cbc6d38SNiklas Söderlund
9359cbc6d38SNiklas Söderlund return count;
9369cbc6d38SNiklas Söderlund }
9379cbc6d38SNiklas Söderlund
9389cbc6d38SNiklas Söderlund static const struct file_operations i5100_inject_enable_fops = {
939b0769891SWei Yongjun .open = simple_open,
9409cbc6d38SNiklas Söderlund .write = inject_enable_write,
9419cbc6d38SNiklas Söderlund .llseek = generic_file_llseek,
9429cbc6d38SNiklas Söderlund };
9439cbc6d38SNiklas Söderlund
i5100_setup_debugfs(struct mem_ctl_info * mci)9449cbc6d38SNiklas Söderlund static int i5100_setup_debugfs(struct mem_ctl_info *mci)
9459cbc6d38SNiklas Söderlund {
9469cbc6d38SNiklas Söderlund struct i5100_priv *priv = mci->pvt_info;
9479cbc6d38SNiklas Söderlund
9489cbc6d38SNiklas Söderlund if (!i5100_debugfs)
9499cbc6d38SNiklas Söderlund return -ENODEV;
9509cbc6d38SNiklas Söderlund
95152019e40SBorislav Petkov priv->debugfs = edac_debugfs_create_dir_at(mci->bus->name, i5100_debugfs);
9529cbc6d38SNiklas Söderlund
9539cbc6d38SNiklas Söderlund if (!priv->debugfs)
9549cbc6d38SNiklas Söderlund return -ENOMEM;
9559cbc6d38SNiklas Söderlund
95652019e40SBorislav Petkov edac_debugfs_create_x8("inject_channel", S_IRUGO | S_IWUSR, priv->debugfs,
9579cbc6d38SNiklas Söderlund &priv->inject_channel);
95852019e40SBorislav Petkov edac_debugfs_create_x8("inject_hlinesel", S_IRUGO | S_IWUSR, priv->debugfs,
9599cbc6d38SNiklas Söderlund &priv->inject_hlinesel);
96052019e40SBorislav Petkov edac_debugfs_create_x8("inject_deviceptr1", S_IRUGO | S_IWUSR, priv->debugfs,
9619cbc6d38SNiklas Söderlund &priv->inject_deviceptr1);
96252019e40SBorislav Petkov edac_debugfs_create_x8("inject_deviceptr2", S_IRUGO | S_IWUSR, priv->debugfs,
9639cbc6d38SNiklas Söderlund &priv->inject_deviceptr2);
96452019e40SBorislav Petkov edac_debugfs_create_x16("inject_eccmask1", S_IRUGO | S_IWUSR, priv->debugfs,
9659cbc6d38SNiklas Söderlund &priv->inject_eccmask1);
96652019e40SBorislav Petkov edac_debugfs_create_x16("inject_eccmask2", S_IRUGO | S_IWUSR, priv->debugfs,
9679cbc6d38SNiklas Söderlund &priv->inject_eccmask2);
96852019e40SBorislav Petkov edac_debugfs_create_file("inject_enable", S_IWUSR, priv->debugfs,
9699cbc6d38SNiklas Söderlund &mci->dev, &i5100_inject_enable_fops);
9709cbc6d38SNiklas Söderlund
9719cbc6d38SNiklas Söderlund return 0;
9729cbc6d38SNiklas Söderlund
9739cbc6d38SNiklas Söderlund }
9749cbc6d38SNiklas Söderlund
i5100_init_one(struct pci_dev * pdev,const struct pci_device_id * id)9759b3c6e85SGreg Kroah-Hartman static int i5100_init_one(struct pci_dev *pdev, const struct pci_device_id *id)
9768f421c59SArthur Jones {
9778f421c59SArthur Jones int rc;
9788f421c59SArthur Jones struct mem_ctl_info *mci;
979d1afaa0aSMauro Carvalho Chehab struct edac_mc_layer layers[2];
9808f421c59SArthur Jones struct i5100_priv *priv;
98152608ba2SNiklas Söderlund struct pci_dev *ch0mm, *ch1mm, *einj;
9828f421c59SArthur Jones int ret = 0;
9838f421c59SArthur Jones u32 dw;
9848f421c59SArthur Jones int ranksperch;
9858f421c59SArthur Jones
9868f421c59SArthur Jones if (PCI_FUNC(pdev->devfn) != 1)
9878f421c59SArthur Jones return -ENODEV;
9888f421c59SArthur Jones
9898f421c59SArthur Jones rc = pci_enable_device(pdev);
9908f421c59SArthur Jones if (rc < 0) {
9918f421c59SArthur Jones ret = rc;
9928f421c59SArthur Jones goto bail;
9938f421c59SArthur Jones }
9948f421c59SArthur Jones
99543920a59SArthur Jones /* ECC enabled? */
99643920a59SArthur Jones pci_read_config_dword(pdev, I5100_MC, &dw);
997b238e577SArthur Jones if (!i5100_mc_errdeten(dw)) {
99843920a59SArthur Jones printk(KERN_INFO "i5100_edac: ECC not enabled.\n");
99943920a59SArthur Jones ret = -ENODEV;
1000b238e577SArthur Jones goto bail_pdev;
100143920a59SArthur Jones }
100243920a59SArthur Jones
10038f421c59SArthur Jones /* figure out how many ranks, from strapped state of 48GB_Mode input */
10048f421c59SArthur Jones pci_read_config_dword(pdev, I5100_MS, &dw);
10058f421c59SArthur Jones ranksperch = !!(dw & (1 << 8)) * 2 + 4;
10068f421c59SArthur Jones
1007178d5a74SArthur Jones /* enable error reporting... */
1008178d5a74SArthur Jones pci_read_config_dword(pdev, I5100_EMASK_MEM, &dw);
1009178d5a74SArthur Jones dw &= ~I5100_FERR_NF_MEM_ANY_MASK;
1010178d5a74SArthur Jones pci_write_config_dword(pdev, I5100_EMASK_MEM, dw);
1011178d5a74SArthur Jones
10128f421c59SArthur Jones /* device 21, func 0, Channel 0 Memory Map, Error Flag/Mask, etc... */
10138f421c59SArthur Jones ch0mm = pci_get_device_func(PCI_VENDOR_ID_INTEL,
10148f421c59SArthur Jones PCI_DEVICE_ID_INTEL_5100_21, 0);
1015b238e577SArthur Jones if (!ch0mm) {
1016b238e577SArthur Jones ret = -ENODEV;
1017b238e577SArthur Jones goto bail_pdev;
1018b238e577SArthur Jones }
10198f421c59SArthur Jones
10208f421c59SArthur Jones rc = pci_enable_device(ch0mm);
10218f421c59SArthur Jones if (rc < 0) {
10228f421c59SArthur Jones ret = rc;
10238f421c59SArthur Jones goto bail_ch0;
10248f421c59SArthur Jones }
10258f421c59SArthur Jones
10268f421c59SArthur Jones /* device 22, func 0, Channel 1 Memory Map, Error Flag/Mask, etc... */
10278f421c59SArthur Jones ch1mm = pci_get_device_func(PCI_VENDOR_ID_INTEL,
10288f421c59SArthur Jones PCI_DEVICE_ID_INTEL_5100_22, 0);
10298f421c59SArthur Jones if (!ch1mm) {
10308f421c59SArthur Jones ret = -ENODEV;
1031b238e577SArthur Jones goto bail_disable_ch0;
10328f421c59SArthur Jones }
10338f421c59SArthur Jones
10348f421c59SArthur Jones rc = pci_enable_device(ch1mm);
10358f421c59SArthur Jones if (rc < 0) {
10368f421c59SArthur Jones ret = rc;
10378f421c59SArthur Jones goto bail_ch1;
10388f421c59SArthur Jones }
10398f421c59SArthur Jones
1040d1afaa0aSMauro Carvalho Chehab layers[0].type = EDAC_MC_LAYER_CHANNEL;
1041d1afaa0aSMauro Carvalho Chehab layers[0].size = 2;
1042d1afaa0aSMauro Carvalho Chehab layers[0].is_virt_csrow = false;
1043d1afaa0aSMauro Carvalho Chehab layers[1].type = EDAC_MC_LAYER_SLOT;
1044d1afaa0aSMauro Carvalho Chehab layers[1].size = ranksperch;
1045d1afaa0aSMauro Carvalho Chehab layers[1].is_virt_csrow = true;
1046ca0907b9SMauro Carvalho Chehab mci = edac_mc_alloc(0, ARRAY_SIZE(layers), layers,
1047d1afaa0aSMauro Carvalho Chehab sizeof(*priv));
10488f421c59SArthur Jones if (!mci) {
10498f421c59SArthur Jones ret = -ENOMEM;
1050b238e577SArthur Jones goto bail_disable_ch1;
10518f421c59SArthur Jones }
10528f421c59SArthur Jones
105352608ba2SNiklas Söderlund
105452608ba2SNiklas Söderlund /* device 19, func 0, Error injection */
105552608ba2SNiklas Söderlund einj = pci_get_device_func(PCI_VENDOR_ID_INTEL,
105652608ba2SNiklas Söderlund PCI_DEVICE_ID_INTEL_5100_19, 0);
105752608ba2SNiklas Söderlund if (!einj) {
105852608ba2SNiklas Söderlund ret = -ENODEV;
1059857a3139SDinghao Liu goto bail_mc_free;
106052608ba2SNiklas Söderlund }
106152608ba2SNiklas Söderlund
106252608ba2SNiklas Söderlund rc = pci_enable_device(einj);
106352608ba2SNiklas Söderlund if (rc < 0) {
106452608ba2SNiklas Söderlund ret = rc;
1065857a3139SDinghao Liu goto bail_einj;
106652608ba2SNiklas Söderlund }
106752608ba2SNiklas Söderlund
1068fd687502SMauro Carvalho Chehab mci->pdev = &pdev->dev;
10698f421c59SArthur Jones
10708f421c59SArthur Jones priv = mci->pvt_info;
1071b18dfd05SNils Carlson priv->ranksperchan = ranksperch;
10728f421c59SArthur Jones priv->mc = pdev;
10738f421c59SArthur Jones priv->ch0mm = ch0mm;
10748f421c59SArthur Jones priv->ch1mm = ch1mm;
107552608ba2SNiklas Söderlund priv->einj = einj;
10768f421c59SArthur Jones
1077295439f2SNils Carlson INIT_DELAYED_WORK(&(priv->i5100_scrubbing), i5100_refresh_scrubbing);
1078295439f2SNils Carlson
1079295439f2SNils Carlson /* If scrubbing was already enabled by the bios, start maintaining it */
1080295439f2SNils Carlson pci_read_config_dword(pdev, I5100_MC, &dw);
1081295439f2SNils Carlson if (i5100_mc_scrben(dw)) {
1082295439f2SNils Carlson priv->scrub_enable = 1;
1083295439f2SNils Carlson schedule_delayed_work(&(priv->i5100_scrubbing),
1084295439f2SNils Carlson I5100_SCRUB_REFRESH_RATE);
1085295439f2SNils Carlson }
1086295439f2SNils Carlson
10878f421c59SArthur Jones i5100_init_dimm_layout(pdev, mci);
10888f421c59SArthur Jones i5100_init_interleaving(pdev, mci);
10898f421c59SArthur Jones
10908f421c59SArthur Jones mci->mtype_cap = MEM_FLAG_FB_DDR2;
10918f421c59SArthur Jones mci->edac_ctl_cap = EDAC_FLAG_SECDED;
10928f421c59SArthur Jones mci->edac_cap = EDAC_FLAG_SECDED;
10938f421c59SArthur Jones mci->mod_name = "i5100_edac.c";
10948f421c59SArthur Jones mci->ctl_name = "i5100";
10958f421c59SArthur Jones mci->dev_name = pci_name(pdev);
1096b238e577SArthur Jones mci->ctl_page_to_phys = NULL;
10978f421c59SArthur Jones
10988f421c59SArthur Jones mci->edac_check = i5100_check_error;
1099295439f2SNils Carlson mci->set_sdram_scrub_rate = i5100_set_scrub_rate;
1100295439f2SNils Carlson mci->get_sdram_scrub_rate = i5100_get_scrub_rate;
11018f421c59SArthur Jones
110253ceafd6SNiklas Söderlund priv->inject_channel = 0;
110353ceafd6SNiklas Söderlund priv->inject_hlinesel = 0;
110453ceafd6SNiklas Söderlund priv->inject_deviceptr1 = 0;
110553ceafd6SNiklas Söderlund priv->inject_deviceptr2 = 0;
110653ceafd6SNiklas Söderlund priv->inject_eccmask1 = 0;
110753ceafd6SNiklas Söderlund priv->inject_eccmask2 = 0;
110853ceafd6SNiklas Söderlund
11098f421c59SArthur Jones i5100_init_csrows(mci);
11108f421c59SArthur Jones
11118f421c59SArthur Jones /* this strange construction seems to be in every driver, dunno why */
11128f421c59SArthur Jones switch (edac_op_state) {
11138f421c59SArthur Jones case EDAC_OPSTATE_POLL:
11148f421c59SArthur Jones case EDAC_OPSTATE_NMI:
11158f421c59SArthur Jones break;
11168f421c59SArthur Jones default:
11178f421c59SArthur Jones edac_op_state = EDAC_OPSTATE_POLL;
11188f421c59SArthur Jones break;
11198f421c59SArthur Jones }
11208f421c59SArthur Jones
11218f421c59SArthur Jones if (edac_mc_add_mc(mci)) {
11228f421c59SArthur Jones ret = -ENODEV;
1123295439f2SNils Carlson goto bail_scrub;
11248f421c59SArthur Jones }
11258f421c59SArthur Jones
11269cbc6d38SNiklas Söderlund i5100_setup_debugfs(mci);
11279cbc6d38SNiklas Söderlund
1128b238e577SArthur Jones return ret;
11298f421c59SArthur Jones
1130295439f2SNils Carlson bail_scrub:
1131295439f2SNils Carlson priv->scrub_enable = 0;
1132295439f2SNils Carlson cancel_delayed_work_sync(&(priv->i5100_scrubbing));
113352608ba2SNiklas Söderlund pci_disable_device(einj);
113452608ba2SNiklas Söderlund
113552608ba2SNiklas Söderlund bail_einj:
113652608ba2SNiklas Söderlund pci_dev_put(einj);
113752608ba2SNiklas Söderlund
1138857a3139SDinghao Liu bail_mc_free:
1139857a3139SDinghao Liu edac_mc_free(mci);
1140857a3139SDinghao Liu
1141b238e577SArthur Jones bail_disable_ch1:
1142b238e577SArthur Jones pci_disable_device(ch1mm);
1143b238e577SArthur Jones
11448f421c59SArthur Jones bail_ch1:
11458f421c59SArthur Jones pci_dev_put(ch1mm);
11468f421c59SArthur Jones
1147b238e577SArthur Jones bail_disable_ch0:
1148b238e577SArthur Jones pci_disable_device(ch0mm);
1149b238e577SArthur Jones
11508f421c59SArthur Jones bail_ch0:
11518f421c59SArthur Jones pci_dev_put(ch0mm);
11528f421c59SArthur Jones
1153b238e577SArthur Jones bail_pdev:
1154b238e577SArthur Jones pci_disable_device(pdev);
1155b238e577SArthur Jones
11568f421c59SArthur Jones bail:
11578f421c59SArthur Jones return ret;
11588f421c59SArthur Jones }
11598f421c59SArthur Jones
i5100_remove_one(struct pci_dev * pdev)11609b3c6e85SGreg Kroah-Hartman static void i5100_remove_one(struct pci_dev *pdev)
11618f421c59SArthur Jones {
11628f421c59SArthur Jones struct mem_ctl_info *mci;
11638f421c59SArthur Jones struct i5100_priv *priv;
11648f421c59SArthur Jones
11658f421c59SArthur Jones mci = edac_mc_del_mc(&pdev->dev);
11668f421c59SArthur Jones
11678f421c59SArthur Jones if (!mci)
11688f421c59SArthur Jones return;
11698f421c59SArthur Jones
11708f421c59SArthur Jones priv = mci->pvt_info;
1171295439f2SNils Carlson
117252019e40SBorislav Petkov edac_debugfs_remove_recursive(priv->debugfs);
11739cbc6d38SNiklas Söderlund
1174295439f2SNils Carlson priv->scrub_enable = 0;
1175295439f2SNils Carlson cancel_delayed_work_sync(&(priv->i5100_scrubbing));
1176295439f2SNils Carlson
1177b238e577SArthur Jones pci_disable_device(pdev);
1178b238e577SArthur Jones pci_disable_device(priv->ch0mm);
1179b238e577SArthur Jones pci_disable_device(priv->ch1mm);
118052608ba2SNiklas Söderlund pci_disable_device(priv->einj);
11818f421c59SArthur Jones pci_dev_put(priv->ch0mm);
11828f421c59SArthur Jones pci_dev_put(priv->ch1mm);
118352608ba2SNiklas Söderlund pci_dev_put(priv->einj);
11848f421c59SArthur Jones
11858f421c59SArthur Jones edac_mc_free(mci);
11868f421c59SArthur Jones }
11878f421c59SArthur Jones
1188ba935f40SJingoo Han static const struct pci_device_id i5100_pci_tbl[] = {
11898f421c59SArthur Jones /* Device 16, Function 0, Channel 0 Memory Map, Error Flag/Mask, ... */
11908f421c59SArthur Jones { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_5100_16) },
11918f421c59SArthur Jones { 0, }
11928f421c59SArthur Jones };
11938f421c59SArthur Jones MODULE_DEVICE_TABLE(pci, i5100_pci_tbl);
11948f421c59SArthur Jones
11958f421c59SArthur Jones static struct pci_driver i5100_driver = {
11968f421c59SArthur Jones .name = KBUILD_BASENAME,
11978f421c59SArthur Jones .probe = i5100_init_one,
11989b3c6e85SGreg Kroah-Hartman .remove = i5100_remove_one,
11998f421c59SArthur Jones .id_table = i5100_pci_tbl,
12008f421c59SArthur Jones };
12018f421c59SArthur Jones
i5100_init(void)12028f421c59SArthur Jones static int __init i5100_init(void)
12038f421c59SArthur Jones {
12048f421c59SArthur Jones int pci_rc;
12058f421c59SArthur Jones
120652019e40SBorislav Petkov i5100_debugfs = edac_debugfs_create_dir_at("i5100_edac", NULL);
12078f421c59SArthur Jones
12089cbc6d38SNiklas Söderlund pci_rc = pci_register_driver(&i5100_driver);
12098f421c59SArthur Jones return (pci_rc < 0) ? pci_rc : 0;
12108f421c59SArthur Jones }
12118f421c59SArthur Jones
i5100_exit(void)12128f421c59SArthur Jones static void __exit i5100_exit(void)
12138f421c59SArthur Jones {
121452019e40SBorislav Petkov edac_debugfs_remove(i5100_debugfs);
12159cbc6d38SNiklas Söderlund
12168f421c59SArthur Jones pci_unregister_driver(&i5100_driver);
12178f421c59SArthur Jones }
12188f421c59SArthur Jones
12198f421c59SArthur Jones module_init(i5100_init);
12208f421c59SArthur Jones module_exit(i5100_exit);
12218f421c59SArthur Jones
12228f421c59SArthur Jones MODULE_LICENSE("GPL");
1223*371b27f2SBorislav Petkov (AMD) MODULE_AUTHOR("Arthur Jones <ajones@riverbed.com>");
12248f421c59SArthur Jones MODULE_DESCRIPTION("MC Driver for Intel I5100 memory controllers");
1225