]>
Commit | Line | Data |
---|---|---|
2210cf39 DM |
1 | From 953dee9bbd245f5515173126b9cc8b1a2c340797 Mon Sep 17 00:00:00 2001 |
2 | From: Jason Baron <jbaron@akamai.com> | |
3 | Date: Fri, 6 May 2016 11:18:47 -0400 | |
4 | Subject: EDAC, ie31200_edac: Add Skylake support | |
5 | ||
6 | Skylake adjusts some register locations, but otherwise follows the | |
7 | existing model quite closely. I was able to verify that the 'ce_count' | |
8 | increments when 'bad dimms' are used. The accounting of 'ce_count' and | |
9 | 'ue_count' is the primary functionality of interest for us. Tested on | |
10 | Intel(R) Xeon(R) CPU E3-1260L v5 @ 2.90GHz. | |
11 | ||
12 | Signed-off-by: Jason Baron <jbaron@akamai.com> | |
13 | Acked-by: Tony Luck <tony.luck@intel.com> | |
14 | Cc: linux-edac <linux-edac@vger.kernel.org> | |
15 | Link: http://lkml.kernel.org/r/1462547927-22679-1-git-send-email-jbaron@akamai.com | |
16 | Signed-off-by: Borislav Petkov <bp@suse.de> | |
17 | --- | |
18 | drivers/edac/ie31200_edac.c | 121 ++++++++++++++++++++++++++++++++------------ | |
19 | 1 file changed, 90 insertions(+), 31 deletions(-) | |
20 | ||
21 | diff --git a/drivers/edac/ie31200_edac.c b/drivers/edac/ie31200_edac.c | |
22 | index 18d77ac..1c88d97 100644 | |
23 | --- a/drivers/edac/ie31200_edac.c | |
24 | +++ b/drivers/edac/ie31200_edac.c | |
25 | @@ -17,6 +17,7 @@ | |
26 | * 015c: Xeon E3-1200 v2/3rd Gen Core processor DRAM Controller | |
27 | * 0c04: Xeon E3-1200 v3/4th Gen Core Processor DRAM Controller | |
28 | * 0c08: Xeon E3-1200 v3 Processor DRAM Controller | |
29 | + * 1918: Xeon E3-1200 v5 Skylake Host Bridge/DRAM Registers | |
30 | * | |
31 | * Based on Intel specification: | |
32 | * http://www.intel.com/content/dam/www/public/us/en/documents/datasheets/xeon-e3-1200v3-vol-2-datasheet.pdf | |
33 | @@ -55,6 +56,7 @@ | |
34 | #define PCI_DEVICE_ID_INTEL_IE31200_HB_5 0x015c | |
35 | #define PCI_DEVICE_ID_INTEL_IE31200_HB_6 0x0c04 | |
36 | #define PCI_DEVICE_ID_INTEL_IE31200_HB_7 0x0c08 | |
37 | +#define PCI_DEVICE_ID_INTEL_IE31200_HB_8 0x1918 | |
38 | ||
39 | #define IE31200_DIMMS 4 | |
40 | #define IE31200_RANKS 8 | |
41 | @@ -105,8 +107,11 @@ | |
42 | * 1 Multiple Bit Error Status (MERRSTS) | |
43 | * 0 Correctable Error Status (CERRSTS) | |
44 | */ | |
45 | + | |
46 | #define IE31200_C0ECCERRLOG 0x40c8 | |
47 | #define IE31200_C1ECCERRLOG 0x44c8 | |
48 | +#define IE31200_C0ECCERRLOG_SKL 0x4048 | |
49 | +#define IE31200_C1ECCERRLOG_SKL 0x4448 | |
50 | #define IE31200_ECCERRLOG_CE BIT(0) | |
51 | #define IE31200_ECCERRLOG_UE BIT(1) | |
52 | #define IE31200_ECCERRLOG_RANK_BITS GENMASK_ULL(28, 27) | |
53 | @@ -123,17 +128,28 @@ | |
54 | #define IE31200_CAPID0_DDPCD BIT(6) | |
55 | #define IE31200_CAPID0_ECC BIT(1) | |
56 | ||
57 | -#define IE31200_MAD_DIMM_0_OFFSET 0x5004 | |
58 | -#define IE31200_MAD_DIMM_SIZE GENMASK_ULL(7, 0) | |
59 | -#define IE31200_MAD_DIMM_A_RANK BIT(17) | |
60 | -#define IE31200_MAD_DIMM_A_WIDTH BIT(19) | |
61 | - | |
62 | -#define IE31200_PAGES(n) (n << (28 - PAGE_SHIFT)) | |
63 | +#define IE31200_MAD_DIMM_0_OFFSET 0x5004 | |
64 | +#define IE31200_MAD_DIMM_0_OFFSET_SKL 0x500C | |
65 | +#define IE31200_MAD_DIMM_SIZE GENMASK_ULL(7, 0) | |
66 | +#define IE31200_MAD_DIMM_A_RANK BIT(17) | |
67 | +#define IE31200_MAD_DIMM_A_RANK_SHIFT 17 | |
68 | +#define IE31200_MAD_DIMM_A_RANK_SKL BIT(10) | |
69 | +#define IE31200_MAD_DIMM_A_RANK_SKL_SHIFT 10 | |
70 | +#define IE31200_MAD_DIMM_A_WIDTH BIT(19) | |
71 | +#define IE31200_MAD_DIMM_A_WIDTH_SHIFT 19 | |
72 | +#define IE31200_MAD_DIMM_A_WIDTH_SKL GENMASK_ULL(9, 8) | |
73 | +#define IE31200_MAD_DIMM_A_WIDTH_SKL_SHIFT 8 | |
74 | + | |
75 | +/* Skylake reports 1GB increments, everything else is 256MB */ | |
76 | +#define IE31200_PAGES(n, skl) \ | |
77 | + (n << (28 + (2 * skl) - PAGE_SHIFT)) | |
78 | ||
79 | static int nr_channels; | |
80 | ||
81 | struct ie31200_priv { | |
82 | void __iomem *window; | |
83 | + void __iomem *c0errlog; | |
84 | + void __iomem *c1errlog; | |
85 | }; | |
86 | ||
87 | enum ie31200_chips { | |
88 | @@ -157,9 +173,9 @@ static const struct ie31200_dev_info ie31200_devs[] = { | |
89 | }; | |
90 | ||
91 | struct dimm_data { | |
92 | - u8 size; /* in 256MB multiples */ | |
93 | + u8 size; /* in multiples of 256MB, except Skylake is 1GB */ | |
94 | u8 dual_rank : 1, | |
95 | - x16_width : 1; /* 0 means x8 width */ | |
96 | + x16_width : 2; /* 0 means x8 width */ | |
97 | }; | |
98 | ||
99 | static int how_many_channels(struct pci_dev *pdev) | |
100 | @@ -197,11 +213,10 @@ static bool ecc_capable(struct pci_dev *pdev) | |
101 | return true; | |
102 | } | |
103 | ||
104 | -static int eccerrlog_row(int channel, u64 log) | |
105 | +static int eccerrlog_row(u64 log) | |
106 | { | |
107 | - int rank = ((log & IE31200_ECCERRLOG_RANK_BITS) >> | |
108 | - IE31200_ECCERRLOG_RANK_SHIFT); | |
109 | - return rank | (channel * IE31200_RANKS_PER_CHANNEL); | |
110 | + return ((log & IE31200_ECCERRLOG_RANK_BITS) >> | |
111 | + IE31200_ECCERRLOG_RANK_SHIFT); | |
112 | } | |
113 | ||
114 | static void ie31200_clear_error_info(struct mem_ctl_info *mci) | |
115 | @@ -219,7 +234,6 @@ static void ie31200_get_and_clear_error_info(struct mem_ctl_info *mci, | |
116 | { | |
117 | struct pci_dev *pdev; | |
118 | struct ie31200_priv *priv = mci->pvt_info; | |
119 | - void __iomem *window = priv->window; | |
120 | ||
121 | pdev = to_pci_dev(mci->pdev); | |
122 | ||
123 | @@ -232,9 +246,9 @@ static void ie31200_get_and_clear_error_info(struct mem_ctl_info *mci, | |
124 | if (!(info->errsts & IE31200_ERRSTS_BITS)) | |
125 | return; | |
126 | ||
127 | - info->eccerrlog[0] = lo_hi_readq(window + IE31200_C0ECCERRLOG); | |
128 | + info->eccerrlog[0] = lo_hi_readq(priv->c0errlog); | |
129 | if (nr_channels == 2) | |
130 | - info->eccerrlog[1] = lo_hi_readq(window + IE31200_C1ECCERRLOG); | |
131 | + info->eccerrlog[1] = lo_hi_readq(priv->c1errlog); | |
132 | ||
133 | pci_read_config_word(pdev, IE31200_ERRSTS, &info->errsts2); | |
134 | ||
135 | @@ -245,10 +259,10 @@ static void ie31200_get_and_clear_error_info(struct mem_ctl_info *mci, | |
136 | * should be UE info. | |
137 | */ | |
138 | if ((info->errsts ^ info->errsts2) & IE31200_ERRSTS_BITS) { | |
139 | - info->eccerrlog[0] = lo_hi_readq(window + IE31200_C0ECCERRLOG); | |
140 | + info->eccerrlog[0] = lo_hi_readq(priv->c0errlog); | |
141 | if (nr_channels == 2) | |
142 | info->eccerrlog[1] = | |
143 | - lo_hi_readq(window + IE31200_C1ECCERRLOG); | |
144 | + lo_hi_readq(priv->c1errlog); | |
145 | } | |
146 | ||
147 | ie31200_clear_error_info(mci); | |
148 | @@ -274,14 +288,14 @@ static void ie31200_process_error_info(struct mem_ctl_info *mci, | |
149 | if (log & IE31200_ECCERRLOG_UE) { | |
150 | edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, | |
151 | 0, 0, 0, | |
152 | - eccerrlog_row(channel, log), | |
153 | + eccerrlog_row(log), | |
154 | channel, -1, | |
155 | "ie31200 UE", ""); | |
156 | } else if (log & IE31200_ECCERRLOG_CE) { | |
157 | edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1, | |
158 | 0, 0, | |
159 | IE31200_ECCERRLOG_SYNDROME(log), | |
160 | - eccerrlog_row(channel, log), | |
161 | + eccerrlog_row(log), | |
162 | channel, -1, | |
163 | "ie31200 CE", ""); | |
164 | } | |
165 | @@ -326,6 +340,33 @@ static void __iomem *ie31200_map_mchbar(struct pci_dev *pdev) | |
166 | return window; | |
167 | } | |
168 | ||
169 | +static void __skl_populate_dimm_info(struct dimm_data *dd, u32 addr_decode, | |
170 | + int chan) | |
171 | +{ | |
172 | + dd->size = (addr_decode >> (chan << 4)) & IE31200_MAD_DIMM_SIZE; | |
173 | + dd->dual_rank = (addr_decode & (IE31200_MAD_DIMM_A_RANK_SKL << (chan << 4))) ? 1 : 0; | |
174 | + dd->x16_width = ((addr_decode & (IE31200_MAD_DIMM_A_WIDTH_SKL << (chan << 4))) >> | |
175 | + (IE31200_MAD_DIMM_A_WIDTH_SKL_SHIFT + (chan << 4))); | |
176 | +} | |
177 | + | |
178 | +static void __populate_dimm_info(struct dimm_data *dd, u32 addr_decode, | |
179 | + int chan) | |
180 | +{ | |
181 | + dd->size = (addr_decode >> (chan << 3)) & IE31200_MAD_DIMM_SIZE; | |
182 | + dd->dual_rank = (addr_decode & (IE31200_MAD_DIMM_A_RANK << chan)) ? 1 : 0; | |
183 | + dd->x16_width = (addr_decode & (IE31200_MAD_DIMM_A_WIDTH << chan)) ? 1 : 0; | |
184 | +} | |
185 | + | |
186 | +static void populate_dimm_info(struct dimm_data *dd, u32 addr_decode, int chan, | |
187 | + bool skl) | |
188 | +{ | |
189 | + if (skl) | |
190 | + __skl_populate_dimm_info(dd, addr_decode, chan); | |
191 | + else | |
192 | + __populate_dimm_info(dd, addr_decode, chan); | |
193 | +} | |
194 | + | |
195 | + | |
196 | static int ie31200_probe1(struct pci_dev *pdev, int dev_idx) | |
197 | { | |
198 | int i, j, ret; | |
199 | @@ -334,7 +375,8 @@ static int ie31200_probe1(struct pci_dev *pdev, int dev_idx) | |
200 | struct dimm_data dimm_info[IE31200_CHANNELS][IE31200_DIMMS_PER_CHANNEL]; | |
201 | void __iomem *window; | |
202 | struct ie31200_priv *priv; | |
203 | - u32 addr_decode; | |
204 | + u32 addr_decode, mad_offset; | |
205 | + bool skl = (pdev->device == PCI_DEVICE_ID_INTEL_IE31200_HB_8); | |
206 | ||
207 | edac_dbg(0, "MC:\n"); | |
208 | ||
209 | @@ -363,7 +405,10 @@ static int ie31200_probe1(struct pci_dev *pdev, int dev_idx) | |
210 | ||
211 | edac_dbg(3, "MC: init mci\n"); | |
212 | mci->pdev = &pdev->dev; | |
213 | - mci->mtype_cap = MEM_FLAG_DDR3; | |
214 | + if (skl) | |
215 | + mci->mtype_cap = MEM_FLAG_DDR4; | |
216 | + else | |
217 | + mci->mtype_cap = MEM_FLAG_DDR3; | |
218 | mci->edac_ctl_cap = EDAC_FLAG_SECDED; | |
219 | mci->edac_cap = EDAC_FLAG_SECDED; | |
220 | mci->mod_name = EDAC_MOD_STR; | |
221 | @@ -374,19 +419,24 @@ static int ie31200_probe1(struct pci_dev *pdev, int dev_idx) | |
222 | mci->ctl_page_to_phys = NULL; | |
223 | priv = mci->pvt_info; | |
224 | priv->window = window; | |
225 | + if (skl) { | |
226 | + priv->c0errlog = window + IE31200_C0ECCERRLOG_SKL; | |
227 | + priv->c1errlog = window + IE31200_C1ECCERRLOG_SKL; | |
228 | + mad_offset = IE31200_MAD_DIMM_0_OFFSET_SKL; | |
229 | + } else { | |
230 | + priv->c0errlog = window + IE31200_C0ECCERRLOG; | |
231 | + priv->c1errlog = window + IE31200_C1ECCERRLOG; | |
232 | + mad_offset = IE31200_MAD_DIMM_0_OFFSET; | |
233 | + } | |
234 | ||
235 | /* populate DIMM info */ | |
236 | for (i = 0; i < IE31200_CHANNELS; i++) { | |
237 | - addr_decode = readl(window + IE31200_MAD_DIMM_0_OFFSET + | |
238 | + addr_decode = readl(window + mad_offset + | |
239 | (i * 4)); | |
240 | edac_dbg(0, "addr_decode: 0x%x\n", addr_decode); | |
241 | for (j = 0; j < IE31200_DIMMS_PER_CHANNEL; j++) { | |
242 | - dimm_info[i][j].size = (addr_decode >> (j * 8)) & | |
243 | - IE31200_MAD_DIMM_SIZE; | |
244 | - dimm_info[i][j].dual_rank = (addr_decode & | |
245 | - (IE31200_MAD_DIMM_A_RANK << j)) ? 1 : 0; | |
246 | - dimm_info[i][j].x16_width = (addr_decode & | |
247 | - (IE31200_MAD_DIMM_A_WIDTH << j)) ? 1 : 0; | |
248 | + populate_dimm_info(&dimm_info[i][j], addr_decode, j, | |
249 | + skl); | |
250 | edac_dbg(0, "size: 0x%x, rank: %d, width: %d\n", | |
251 | dimm_info[i][j].size, | |
252 | dimm_info[i][j].dual_rank, | |
253 | @@ -405,7 +455,7 @@ static int ie31200_probe1(struct pci_dev *pdev, int dev_idx) | |
254 | struct dimm_info *dimm; | |
255 | unsigned long nr_pages; | |
256 | ||
257 | - nr_pages = IE31200_PAGES(dimm_info[j][i].size); | |
258 | + nr_pages = IE31200_PAGES(dimm_info[j][i].size, skl); | |
259 | if (nr_pages == 0) | |
260 | continue; | |
261 | ||
262 | @@ -417,7 +467,10 @@ static int ie31200_probe1(struct pci_dev *pdev, int dev_idx) | |
263 | dimm->nr_pages = nr_pages; | |
264 | edac_dbg(0, "set nr pages: 0x%lx\n", nr_pages); | |
265 | dimm->grain = 8; /* just a guess */ | |
266 | - dimm->mtype = MEM_DDR3; | |
267 | + if (skl) | |
268 | + dimm->mtype = MEM_DDR4; | |
269 | + else | |
270 | + dimm->mtype = MEM_DDR3; | |
271 | dimm->dtype = DEV_UNKNOWN; | |
272 | dimm->edac_mode = EDAC_UNKNOWN; | |
273 | } | |
274 | @@ -426,7 +479,10 @@ static int ie31200_probe1(struct pci_dev *pdev, int dev_idx) | |
275 | dimm->nr_pages = nr_pages; | |
276 | edac_dbg(0, "set nr pages: 0x%lx\n", nr_pages); | |
277 | dimm->grain = 8; /* same guess */ | |
278 | - dimm->mtype = MEM_DDR3; | |
279 | + if (skl) | |
280 | + dimm->mtype = MEM_DDR4; | |
281 | + else | |
282 | + dimm->mtype = MEM_DDR3; | |
283 | dimm->dtype = DEV_UNKNOWN; | |
284 | dimm->edac_mode = EDAC_UNKNOWN; | |
285 | } | |
286 | @@ -501,6 +557,9 @@ static const struct pci_device_id ie31200_pci_tbl[] = { | |
287 | PCI_VEND_DEV(INTEL, IE31200_HB_7), PCI_ANY_ID, PCI_ANY_ID, 0, 0, | |
288 | IE31200}, | |
289 | { | |
290 | + PCI_VEND_DEV(INTEL, IE31200_HB_8), PCI_ANY_ID, PCI_ANY_ID, 0, 0, | |
291 | + IE31200}, | |
292 | + { | |
293 | 0, | |
294 | } /* 0 terminated list. */ | |
295 | }; | |
296 | -- | |
297 | cgit v0.12 | |
298 |