1 | // SPDX-License-Identifier: GPL-2.0-only |
---|---|
2 | /* |
3 | * Intel E3-1200 |
4 | * Copyright (C) 2014 Jason Baron <jbaron@akamai.com> |
5 | * |
6 | * Support for the E3-1200 processor family. Heavily based on previous |
7 | * Intel EDAC drivers. |
8 | * |
9 | * Since the DRAM controller is on the cpu chip, we can use its PCI device |
10 | * id to identify these processors. |
11 | * |
12 | * PCI DRAM controller device ids (Taken from The PCI ID Repository - https://pci-ids.ucw.cz/) |
13 | * |
14 | * 0108: Xeon E3-1200 Processor Family DRAM Controller |
15 | * 010c: Xeon E3-1200/2nd Generation Core Processor Family DRAM Controller |
16 | * 0150: Xeon E3-1200 v2/3rd Gen Core processor DRAM Controller |
17 | * 0158: Xeon E3-1200 v2/Ivy Bridge DRAM Controller |
18 | * 015c: Xeon E3-1200 v2/3rd Gen Core processor DRAM Controller |
19 | * 0c04: Xeon E3-1200 v3/4th Gen Core Processor DRAM Controller |
20 | * 0c08: Xeon E3-1200 v3 Processor DRAM Controller |
21 | * 1918: Xeon E3-1200 v5 Skylake Host Bridge/DRAM Registers |
22 | * 590f: Xeon E3-1200 v6/7th Gen Core Processor Host Bridge/DRAM Registers |
23 | * 5918: Xeon E3-1200 v6/7th Gen Core Processor Host Bridge/DRAM Registers |
24 | * 190f: 6th Gen Core Dual-Core Processor Host Bridge/DRAM Registers |
25 | * 191f: 6th Gen Core Quad-Core Processor Host Bridge/DRAM Registers |
26 | * 3e..: 8th/9th Gen Core Processor Host Bridge/DRAM Registers |
27 | * |
28 | * Based on Intel specification: |
29 | * https://www.intel.com/content/dam/www/public/us/en/documents/datasheets/xeon-e3-1200v3-vol-2-datasheet.pdf |
30 | * http://www.intel.com/content/www/us/en/processors/xeon/xeon-e3-1200-family-vol-2-datasheet.html |
31 | * https://www.intel.com/content/dam/www/public/us/en/documents/datasheets/desktop-6th-gen-core-family-datasheet-vol-2.pdf |
32 | * https://www.intel.com/content/dam/www/public/us/en/documents/datasheets/xeon-e3-1200v6-vol-2-datasheet.pdf |
33 | * https://www.intel.com/content/www/us/en/processors/core/7th-gen-core-family-mobile-h-processor-lines-datasheet-vol-2.html |
34 | * https://www.intel.com/content/www/us/en/products/docs/processors/core/8th-gen-core-family-datasheet-vol-2.html |
35 | * |
36 | * According to the above datasheet (p.16): |
37 | * " |
38 | * 6. Software must not access B0/D0/F0 32-bit memory-mapped registers with |
39 | * requests that cross a DW boundary. |
40 | * " |
41 | * |
42 | * Thus, we make use of the explicit: lo_hi_readq(), which breaks the readq into |
43 | * 2 readl() calls. This restriction may be lifted in subsequent chip releases, |
44 | * but lo_hi_readq() ensures that we are safe across all e3-1200 processors. |
45 | */ |
46 | |
47 | #include <linux/module.h> |
48 | #include <linux/init.h> |
49 | #include <linux/pci.h> |
50 | #include <linux/pci_ids.h> |
51 | #include <linux/edac.h> |
52 | |
53 | #include <linux/io-64-nonatomic-lo-hi.h> |
54 | #include <asm/mce.h> |
55 | #include <asm/msr.h> |
56 | #include "edac_module.h" |
57 | |
58 | #define EDAC_MOD_STR "ie31200_edac" |
59 | |
60 | #define ie31200_printk(level, fmt, arg...) \ |
61 | edac_printk(level, "ie31200", fmt, ##arg) |
62 | |
63 | #define PCI_DEVICE_ID_INTEL_IE31200_HB_1 0x0108 |
64 | #define PCI_DEVICE_ID_INTEL_IE31200_HB_2 0x010c |
65 | #define PCI_DEVICE_ID_INTEL_IE31200_HB_3 0x0150 |
66 | #define PCI_DEVICE_ID_INTEL_IE31200_HB_4 0x0158 |
67 | #define PCI_DEVICE_ID_INTEL_IE31200_HB_5 0x015c |
68 | #define PCI_DEVICE_ID_INTEL_IE31200_HB_6 0x0c04 |
69 | #define PCI_DEVICE_ID_INTEL_IE31200_HB_7 0x0c08 |
70 | #define PCI_DEVICE_ID_INTEL_IE31200_HB_8 0x190F |
71 | #define PCI_DEVICE_ID_INTEL_IE31200_HB_9 0x1918 |
72 | #define PCI_DEVICE_ID_INTEL_IE31200_HB_10 0x191F |
73 | #define PCI_DEVICE_ID_INTEL_IE31200_HB_11 0x590f |
74 | #define PCI_DEVICE_ID_INTEL_IE31200_HB_12 0x5918 |
75 | |
76 | /* Coffee Lake-S */ |
77 | #define PCI_DEVICE_ID_INTEL_IE31200_HB_CFL_MASK 0x3e00 |
78 | #define PCI_DEVICE_ID_INTEL_IE31200_HB_CFL_1 0x3e0f |
79 | #define PCI_DEVICE_ID_INTEL_IE31200_HB_CFL_2 0x3e18 |
80 | #define PCI_DEVICE_ID_INTEL_IE31200_HB_CFL_3 0x3e1f |
81 | #define PCI_DEVICE_ID_INTEL_IE31200_HB_CFL_4 0x3e30 |
82 | #define PCI_DEVICE_ID_INTEL_IE31200_HB_CFL_5 0x3e31 |
83 | #define PCI_DEVICE_ID_INTEL_IE31200_HB_CFL_6 0x3e32 |
84 | #define PCI_DEVICE_ID_INTEL_IE31200_HB_CFL_7 0x3e33 |
85 | #define PCI_DEVICE_ID_INTEL_IE31200_HB_CFL_8 0x3ec2 |
86 | #define PCI_DEVICE_ID_INTEL_IE31200_HB_CFL_9 0x3ec6 |
87 | #define PCI_DEVICE_ID_INTEL_IE31200_HB_CFL_10 0x3eca |
88 | |
89 | /* Raptor Lake-S */ |
90 | #define PCI_DEVICE_ID_INTEL_IE31200_RPL_S_1 0xa703 |
91 | #define PCI_DEVICE_ID_INTEL_IE31200_RPL_S_2 0x4640 |
92 | #define PCI_DEVICE_ID_INTEL_IE31200_RPL_S_3 0x4630 |
93 | #define PCI_DEVICE_ID_INTEL_IE31200_RPL_S_4 0xa700 |
94 | |
95 | /* Alder Lake-S */ |
96 | #define PCI_DEVICE_ID_INTEL_IE31200_ADL_S_1 0x4660 |
97 | |
98 | #define IE31200_RANKS_PER_CHANNEL 8 |
99 | #define IE31200_DIMMS_PER_CHANNEL 2 |
100 | #define IE31200_CHANNELS 2 |
101 | #define IE31200_IMC_NUM 2 |
102 | |
103 | /* Intel IE31200 register addresses - device 0 function 0 - DRAM Controller */ |
104 | #define IE31200_MCHBAR_LOW 0x48 |
105 | #define IE31200_MCHBAR_HIGH 0x4c |
106 | |
107 | /* |
108 | * Error Status Register (16b) |
109 | * |
110 | * 1 Multi-bit DRAM ECC Error Flag (DMERR) |
111 | * 0 Single-bit DRAM ECC Error Flag (DSERR) |
112 | */ |
113 | #define IE31200_ERRSTS 0xc8 |
114 | #define IE31200_ERRSTS_UE BIT(1) |
115 | #define IE31200_ERRSTS_CE BIT(0) |
116 | #define IE31200_ERRSTS_BITS (IE31200_ERRSTS_UE | IE31200_ERRSTS_CE) |
117 | |
118 | #define IE31200_CAPID0 0xe4 |
119 | #define IE31200_CAPID0_PDCD BIT(4) |
120 | #define IE31200_CAPID0_DDPCD BIT(6) |
121 | #define IE31200_CAPID0_ECC BIT(1) |
122 | |
123 | /* Non-constant mask variant of FIELD_GET() */ |
124 | #define field_get(_mask, _reg) (((_reg) & (_mask)) >> (ffs(_mask) - 1)) |
125 | |
126 | static int nr_channels; |
127 | static struct pci_dev *mci_pdev; |
128 | static int ie31200_registered = 1; |
129 | |
130 | struct res_config { |
131 | enum mem_type mtype; |
132 | bool cmci; |
133 | int imc_num; |
134 | /* Host MMIO configuration register */ |
135 | u64 reg_mchbar_mask; |
136 | u64 reg_mchbar_window_size; |
137 | /* ECC error log register */ |
138 | u64 reg_eccerrlog_offset[IE31200_CHANNELS]; |
139 | u64 reg_eccerrlog_ce_mask; |
140 | u64 reg_eccerrlog_ce_ovfl_mask; |
141 | u64 reg_eccerrlog_ue_mask; |
142 | u64 reg_eccerrlog_ue_ovfl_mask; |
143 | u64 reg_eccerrlog_rank_mask; |
144 | u64 reg_eccerrlog_syndrome_mask; |
145 | /* MSR to clear ECC error log register */ |
146 | u32 msr_clear_eccerrlog_offset; |
147 | /* DIMM characteristics register */ |
148 | u64 reg_mad_dimm_size_granularity; |
149 | u64 reg_mad_dimm_offset[IE31200_CHANNELS]; |
150 | u32 reg_mad_dimm_size_mask[IE31200_DIMMS_PER_CHANNEL]; |
151 | u32 reg_mad_dimm_rank_mask[IE31200_DIMMS_PER_CHANNEL]; |
152 | u32 reg_mad_dimm_width_mask[IE31200_DIMMS_PER_CHANNEL]; |
153 | }; |
154 | |
155 | struct ie31200_priv { |
156 | void __iomem *window; |
157 | void __iomem *c0errlog; |
158 | void __iomem *c1errlog; |
159 | struct res_config *cfg; |
160 | struct mem_ctl_info *mci; |
161 | struct pci_dev *pdev; |
162 | struct device dev; |
163 | }; |
164 | |
165 | static struct ie31200_pvt { |
166 | struct ie31200_priv *priv[IE31200_IMC_NUM]; |
167 | } ie31200_pvt; |
168 | |
169 | enum ie31200_chips { |
170 | IE31200 = 0, |
171 | IE31200_1 = 1, |
172 | }; |
173 | |
174 | struct ie31200_dev_info { |
175 | const char *ctl_name; |
176 | }; |
177 | |
178 | struct ie31200_error_info { |
179 | u16 errsts; |
180 | u16 errsts2; |
181 | u64 eccerrlog[IE31200_CHANNELS]; |
182 | u64 erraddr; |
183 | }; |
184 | |
185 | static const struct ie31200_dev_info ie31200_devs[] = { |
186 | [IE31200] = { |
187 | .ctl_name = "IE31200" |
188 | }, |
189 | [IE31200_1] = { |
190 | .ctl_name = "IE31200_1" |
191 | }, |
192 | }; |
193 | |
194 | struct dimm_data { |
195 | u64 size; /* in bytes */ |
196 | u8 ranks; |
197 | enum dev_type dtype; |
198 | }; |
199 | |
200 | static int how_many_channels(struct pci_dev *pdev) |
201 | { |
202 | int n_channels; |
203 | unsigned char capid0_2b; /* 2nd byte of CAPID0 */ |
204 | |
205 | pci_read_config_byte(dev: pdev, IE31200_CAPID0 + 1, val: &capid0_2b); |
206 | |
207 | /* check PDCD: Dual Channel Disable */ |
208 | if (capid0_2b & IE31200_CAPID0_PDCD) { |
209 | edac_dbg(0, "In single channel mode\n"); |
210 | n_channels = 1; |
211 | } else { |
212 | edac_dbg(0, "In dual channel mode\n"); |
213 | n_channels = 2; |
214 | } |
215 | |
216 | /* check DDPCD - check if both channels are filled */ |
217 | if (capid0_2b & IE31200_CAPID0_DDPCD) |
218 | edac_dbg(0, "2 DIMMS per channel disabled\n"); |
219 | else |
220 | edac_dbg(0, "2 DIMMS per channel enabled\n"); |
221 | |
222 | return n_channels; |
223 | } |
224 | |
225 | static bool ecc_capable(struct pci_dev *pdev) |
226 | { |
227 | unsigned char capid0_4b; /* 4th byte of CAPID0 */ |
228 | |
229 | pci_read_config_byte(dev: pdev, IE31200_CAPID0 + 3, val: &capid0_4b); |
230 | if (capid0_4b & IE31200_CAPID0_ECC) |
231 | return false; |
232 | return true; |
233 | } |
234 | |
235 | #define mci_to_pci_dev(mci) (((struct ie31200_priv *)(mci)->pvt_info)->pdev) |
236 | |
237 | static void ie31200_clear_error_info(struct mem_ctl_info *mci) |
238 | { |
239 | struct ie31200_priv *priv = mci->pvt_info; |
240 | struct res_config *cfg = priv->cfg; |
241 | |
242 | /* |
243 | * The PCI ERRSTS register is deprecated. Write the MSR to clear |
244 | * the ECC error log registers in all memory controllers. |
245 | */ |
246 | if (cfg->msr_clear_eccerrlog_offset) { |
247 | if (wrmsr_safe(msr: cfg->msr_clear_eccerrlog_offset, |
248 | low: cfg->reg_eccerrlog_ce_mask | |
249 | cfg->reg_eccerrlog_ce_ovfl_mask | |
250 | cfg->reg_eccerrlog_ue_mask | |
251 | cfg->reg_eccerrlog_ue_ovfl_mask, high: 0) < 0) |
252 | ie31200_printk(KERN_ERR, "Failed to wrmsr.\n"); |
253 | |
254 | return; |
255 | } |
256 | |
257 | /* |
258 | * Clear any error bits. |
259 | * (Yes, we really clear bits by writing 1 to them.) |
260 | */ |
261 | pci_write_bits16(mci_to_pci_dev(mci), IE31200_ERRSTS, |
262 | IE31200_ERRSTS_BITS, IE31200_ERRSTS_BITS); |
263 | } |
264 | |
265 | static void ie31200_get_and_clear_error_info(struct mem_ctl_info *mci, |
266 | struct ie31200_error_info *info) |
267 | { |
268 | struct pci_dev *pdev = mci_to_pci_dev(mci); |
269 | struct ie31200_priv *priv = mci->pvt_info; |
270 | |
271 | /* |
272 | * The PCI ERRSTS register is deprecated, directly read the |
273 | * MMIO-mapped ECC error log registers. |
274 | */ |
275 | if (priv->cfg->msr_clear_eccerrlog_offset) { |
276 | info->eccerrlog[0] = lo_hi_readq(addr: priv->c0errlog); |
277 | if (nr_channels == 2) |
278 | info->eccerrlog[1] = lo_hi_readq(addr: priv->c1errlog); |
279 | |
280 | ie31200_clear_error_info(mci); |
281 | return; |
282 | } |
283 | |
284 | /* |
285 | * This is a mess because there is no atomic way to read all the |
286 | * registers at once and the registers can transition from CE being |
287 | * overwritten by UE. |
288 | */ |
289 | pci_read_config_word(dev: pdev, IE31200_ERRSTS, val: &info->errsts); |
290 | if (!(info->errsts & IE31200_ERRSTS_BITS)) |
291 | return; |
292 | |
293 | info->eccerrlog[0] = lo_hi_readq(addr: priv->c0errlog); |
294 | if (nr_channels == 2) |
295 | info->eccerrlog[1] = lo_hi_readq(addr: priv->c1errlog); |
296 | |
297 | pci_read_config_word(dev: pdev, IE31200_ERRSTS, val: &info->errsts2); |
298 | |
299 | /* |
300 | * If the error is the same for both reads then the first set |
301 | * of reads is valid. If there is a change then there is a CE |
302 | * with no info and the second set of reads is valid and |
303 | * should be UE info. |
304 | */ |
305 | if ((info->errsts ^ info->errsts2) & IE31200_ERRSTS_BITS) { |
306 | info->eccerrlog[0] = lo_hi_readq(addr: priv->c0errlog); |
307 | if (nr_channels == 2) |
308 | info->eccerrlog[1] = |
309 | lo_hi_readq(addr: priv->c1errlog); |
310 | } |
311 | |
312 | ie31200_clear_error_info(mci); |
313 | } |
314 | |
315 | static void ie31200_process_error_info(struct mem_ctl_info *mci, |
316 | struct ie31200_error_info *info) |
317 | { |
318 | struct ie31200_priv *priv = mci->pvt_info; |
319 | struct res_config *cfg = priv->cfg; |
320 | int channel; |
321 | u64 log; |
322 | |
323 | if (!cfg->msr_clear_eccerrlog_offset) { |
324 | if (!(info->errsts & IE31200_ERRSTS_BITS)) |
325 | return; |
326 | |
327 | if ((info->errsts ^ info->errsts2) & IE31200_ERRSTS_BITS) { |
328 | edac_mc_handle_error(type: HW_EVENT_ERR_UNCORRECTED, mci, error_count: 1, page_frame_number: 0, offset_in_page: 0, syndrome: 0, |
329 | top_layer: -1, mid_layer: -1, low_layer: -1, msg: "UE overwrote CE", other_detail: ""); |
330 | info->errsts = info->errsts2; |
331 | } |
332 | } |
333 | |
334 | for (channel = 0; channel < nr_channels; channel++) { |
335 | log = info->eccerrlog[channel]; |
336 | if (log & cfg->reg_eccerrlog_ue_mask) { |
337 | edac_mc_handle_error(type: HW_EVENT_ERR_UNCORRECTED, mci, error_count: 1, |
338 | page_frame_number: info->erraddr >> PAGE_SHIFT, offset_in_page: 0, syndrome: 0, |
339 | field_get(cfg->reg_eccerrlog_rank_mask, log), |
340 | mid_layer: channel, low_layer: -1, |
341 | msg: "ie31200 UE", other_detail: ""); |
342 | } else if (log & cfg->reg_eccerrlog_ce_mask) { |
343 | edac_mc_handle_error(type: HW_EVENT_ERR_CORRECTED, mci, error_count: 1, |
344 | page_frame_number: info->erraddr >> PAGE_SHIFT, offset_in_page: 0, |
345 | field_get(cfg->reg_eccerrlog_syndrome_mask, log), |
346 | field_get(cfg->reg_eccerrlog_rank_mask, log), |
347 | mid_layer: channel, low_layer: -1, |
348 | msg: "ie31200 CE", other_detail: ""); |
349 | } |
350 | } |
351 | } |
352 | |
353 | static void __ie31200_check(struct mem_ctl_info *mci, struct mce *mce) |
354 | { |
355 | struct ie31200_error_info info; |
356 | |
357 | info.erraddr = mce ? mce->addr : 0; |
358 | ie31200_get_and_clear_error_info(mci, info: &info); |
359 | ie31200_process_error_info(mci, info: &info); |
360 | } |
361 | |
362 | static void ie31200_check(struct mem_ctl_info *mci) |
363 | { |
364 | __ie31200_check(mci, NULL); |
365 | } |
366 | |
367 | static void __iomem *ie31200_map_mchbar(struct pci_dev *pdev, struct res_config *cfg, int mc) |
368 | { |
369 | union { |
370 | u64 mchbar; |
371 | struct { |
372 | u32 mchbar_low; |
373 | u32 mchbar_high; |
374 | }; |
375 | } u; |
376 | void __iomem *window; |
377 | |
378 | pci_read_config_dword(dev: pdev, IE31200_MCHBAR_LOW, val: &u.mchbar_low); |
379 | pci_read_config_dword(dev: pdev, IE31200_MCHBAR_HIGH, val: &u.mchbar_high); |
380 | u.mchbar &= cfg->reg_mchbar_mask; |
381 | u.mchbar += cfg->reg_mchbar_window_size * mc; |
382 | |
383 | if (u.mchbar != (resource_size_t)u.mchbar) { |
384 | ie31200_printk(KERN_ERR, "mmio space beyond accessible range (0x%llx)\n", |
385 | (unsigned long long)u.mchbar); |
386 | return NULL; |
387 | } |
388 | |
389 | window = ioremap(offset: u.mchbar, size: cfg->reg_mchbar_window_size); |
390 | if (!window) |
391 | ie31200_printk(KERN_ERR, "Cannot map mmio space at 0x%llx\n", |
392 | (unsigned long long)u.mchbar); |
393 | |
394 | return window; |
395 | } |
396 | |
397 | static void populate_dimm_info(struct dimm_data *dd, u32 addr_decode, int dimm, |
398 | struct res_config *cfg) |
399 | { |
400 | dd->size = field_get(cfg->reg_mad_dimm_size_mask[dimm], addr_decode) * cfg->reg_mad_dimm_size_granularity; |
401 | dd->ranks = field_get(cfg->reg_mad_dimm_rank_mask[dimm], addr_decode) + 1; |
402 | dd->dtype = field_get(cfg->reg_mad_dimm_width_mask[dimm], addr_decode) + DEV_X8; |
403 | } |
404 | |
405 | static void ie31200_get_dimm_config(struct mem_ctl_info *mci, void __iomem *window, |
406 | struct res_config *cfg, int mc) |
407 | { |
408 | struct dimm_data dimm_info; |
409 | struct dimm_info *dimm; |
410 | unsigned long nr_pages; |
411 | u32 addr_decode; |
412 | int i, j, k; |
413 | |
414 | for (i = 0; i < IE31200_CHANNELS; i++) { |
415 | addr_decode = readl(addr: window + cfg->reg_mad_dimm_offset[i]); |
416 | edac_dbg(0, "addr_decode: 0x%x\n", addr_decode); |
417 | |
418 | for (j = 0; j < IE31200_DIMMS_PER_CHANNEL; j++) { |
419 | populate_dimm_info(dd: &dimm_info, addr_decode, dimm: j, cfg); |
420 | edac_dbg(0, "mc: %d, channel: %d, dimm: %d, size: %lld MiB, ranks: %d, DRAM chip type: %d\n", |
421 | mc, i, j, dimm_info.size >> 20, |
422 | dimm_info.ranks, |
423 | dimm_info.dtype); |
424 | |
425 | nr_pages = MiB_TO_PAGES(dimm_info.size >> 20); |
426 | if (nr_pages == 0) |
427 | continue; |
428 | |
429 | nr_pages = nr_pages / dimm_info.ranks; |
430 | for (k = 0; k < dimm_info.ranks; k++) { |
431 | dimm = edac_get_dimm(mci, layer0: (j * dimm_info.ranks) + k, layer1: i, layer2: 0); |
432 | dimm->nr_pages = nr_pages; |
433 | edac_dbg(0, "set nr pages: 0x%lx\n", nr_pages); |
434 | dimm->grain = 8; /* just a guess */ |
435 | dimm->mtype = cfg->mtype; |
436 | dimm->dtype = dimm_info.dtype; |
437 | dimm->edac_mode = EDAC_UNKNOWN; |
438 | } |
439 | } |
440 | } |
441 | } |
442 | |
443 | static int ie31200_register_mci(struct pci_dev *pdev, struct res_config *cfg, int mc) |
444 | { |
445 | struct edac_mc_layer layers[2]; |
446 | struct ie31200_priv *priv; |
447 | struct mem_ctl_info *mci; |
448 | void __iomem *window; |
449 | int ret; |
450 | |
451 | nr_channels = how_many_channels(pdev); |
452 | layers[0].type = EDAC_MC_LAYER_CHIP_SELECT; |
453 | layers[0].size = IE31200_RANKS_PER_CHANNEL; |
454 | layers[0].is_virt_csrow = true; |
455 | layers[1].type = EDAC_MC_LAYER_CHANNEL; |
456 | layers[1].size = nr_channels; |
457 | layers[1].is_virt_csrow = false; |
458 | mci = edac_mc_alloc(mc_num: mc, ARRAY_SIZE(layers), layers, |
459 | sz_pvt: sizeof(struct ie31200_priv)); |
460 | if (!mci) |
461 | return -ENOMEM; |
462 | |
463 | window = ie31200_map_mchbar(pdev, cfg, mc); |
464 | if (!window) { |
465 | ret = -ENODEV; |
466 | goto fail_free; |
467 | } |
468 | |
469 | edac_dbg(3, "MC: init mci\n"); |
470 | mci->mtype_cap = BIT(cfg->mtype); |
471 | mci->edac_ctl_cap = EDAC_FLAG_SECDED; |
472 | mci->edac_cap = EDAC_FLAG_SECDED; |
473 | mci->mod_name = EDAC_MOD_STR; |
474 | mci->ctl_name = ie31200_devs[mc].ctl_name; |
475 | mci->dev_name = pci_name(pdev); |
476 | mci->edac_check = cfg->cmci ? NULL : ie31200_check; |
477 | mci->ctl_page_to_phys = NULL; |
478 | priv = mci->pvt_info; |
479 | priv->window = window; |
480 | priv->c0errlog = window + cfg->reg_eccerrlog_offset[0]; |
481 | priv->c1errlog = window + cfg->reg_eccerrlog_offset[1]; |
482 | priv->cfg = cfg; |
483 | priv->mci = mci; |
484 | priv->pdev = pdev; |
485 | device_initialize(dev: &priv->dev); |
486 | /* |
487 | * The EDAC core uses mci->pdev (pointer to the structure device) |
488 | * as the memory controller ID. The SoCs attach one or more memory |
489 | * controllers to a single pci_dev (a single pci_dev->dev can |
490 | * correspond to multiple memory controllers). |
491 | * |
492 | * To make mci->pdev unique, assign pci_dev->dev to mci->pdev |
493 | * for the first memory controller and assign a unique priv->dev |
494 | * to mci->pdev for each additional memory controller. |
495 | */ |
496 | mci->pdev = mc ? &priv->dev : &pdev->dev; |
497 | |
498 | ie31200_get_dimm_config(mci, window, cfg, mc); |
499 | ie31200_clear_error_info(mci); |
500 | |
501 | if (edac_mc_add_mc(mci)) { |
502 | edac_dbg(3, "MC: failed edac_mc_add_mc()\n"); |
503 | ret = -ENODEV; |
504 | goto fail_unmap; |
505 | } |
506 | |
507 | ie31200_pvt.priv[mc] = priv; |
508 | return 0; |
509 | fail_unmap: |
510 | iounmap(addr: window); |
511 | fail_free: |
512 | edac_mc_free(mci); |
513 | return ret; |
514 | } |
515 | |
516 | static void mce_check(struct mce *mce) |
517 | { |
518 | struct ie31200_priv *priv; |
519 | int i; |
520 | |
521 | for (i = 0; i < IE31200_IMC_NUM; i++) { |
522 | priv = ie31200_pvt.priv[i]; |
523 | if (!priv) |
524 | continue; |
525 | |
526 | __ie31200_check(mci: priv->mci, mce); |
527 | } |
528 | } |
529 | |
530 | static int mce_handler(struct notifier_block *nb, unsigned long val, void *data) |
531 | { |
532 | struct mce *mce = (struct mce *)data; |
533 | char *type; |
534 | |
535 | if (mce->kflags & MCE_HANDLED_CEC) |
536 | return NOTIFY_DONE; |
537 | |
538 | /* |
539 | * Ignore unless this is a memory related error. |
540 | * Don't check MCI_STATUS_ADDRV since it's not set on some CPUs. |
541 | */ |
542 | if ((mce->status & 0xefff) >> 7 != 1) |
543 | return NOTIFY_DONE; |
544 | |
545 | type = mce->mcgstatus & MCG_STATUS_MCIP ? "Exception": "Event"; |
546 | |
547 | edac_dbg(0, "CPU %d: Machine Check %s: 0x%llx Bank %d: 0x%llx\n", |
548 | mce->extcpu, type, mce->mcgstatus, |
549 | mce->bank, mce->status); |
550 | edac_dbg(0, "TSC 0x%llx\n", mce->tsc); |
551 | edac_dbg(0, "ADDR 0x%llx\n", mce->addr); |
552 | edac_dbg(0, "MISC 0x%llx\n", mce->misc); |
553 | edac_dbg(0, "PROCESSOR %u:0x%x TIME %llu SOCKET %u APIC 0x%x\n", |
554 | mce->cpuvendor, mce->cpuid, mce->time, |
555 | mce->socketid, mce->apicid); |
556 | |
557 | mce_check(mce); |
558 | mce->kflags |= MCE_HANDLED_EDAC; |
559 | |
560 | return NOTIFY_DONE; |
561 | } |
562 | |
563 | static struct notifier_block ie31200_mce_dec = { |
564 | .notifier_call = mce_handler, |
565 | .priority = MCE_PRIO_EDAC, |
566 | }; |
567 | |
568 | static void ie31200_unregister_mcis(void) |
569 | { |
570 | struct ie31200_priv *priv; |
571 | struct mem_ctl_info *mci; |
572 | int i; |
573 | |
574 | for (i = 0; i < IE31200_IMC_NUM; i++) { |
575 | priv = ie31200_pvt.priv[i]; |
576 | if (!priv) |
577 | continue; |
578 | |
579 | mci = priv->mci; |
580 | edac_mc_del_mc(dev: mci->pdev); |
581 | iounmap(addr: priv->window); |
582 | edac_mc_free(mci); |
583 | } |
584 | } |
585 | |
586 | static int ie31200_probe1(struct pci_dev *pdev, struct res_config *cfg) |
587 | { |
588 | int i, ret; |
589 | |
590 | edac_dbg(0, "MC:\n"); |
591 | |
592 | if (!ecc_capable(pdev)) { |
593 | ie31200_printk(KERN_INFO, "No ECC support\n"); |
594 | return -ENODEV; |
595 | } |
596 | |
597 | for (i = 0; i < cfg->imc_num; i++) { |
598 | ret = ie31200_register_mci(pdev, cfg, mc: i); |
599 | if (ret) |
600 | goto fail_register; |
601 | } |
602 | |
603 | if (cfg->cmci) { |
604 | mce_register_decode_chain(nb: &ie31200_mce_dec); |
605 | edac_op_state = EDAC_OPSTATE_INT; |
606 | } else { |
607 | edac_op_state = EDAC_OPSTATE_POLL; |
608 | } |
609 | |
610 | /* get this far and it's successful. */ |
611 | edac_dbg(3, "MC: success\n"); |
612 | return 0; |
613 | |
614 | fail_register: |
615 | ie31200_unregister_mcis(); |
616 | return ret; |
617 | } |
618 | |
619 | static int ie31200_init_one(struct pci_dev *pdev, |
620 | const struct pci_device_id *ent) |
621 | { |
622 | int rc; |
623 | |
624 | edac_dbg(0, "MC:\n"); |
625 | if (pci_enable_device(dev: pdev) < 0) |
626 | return -EIO; |
627 | rc = ie31200_probe1(pdev, cfg: (struct res_config *)ent->driver_data); |
628 | if (rc == 0 && !mci_pdev) |
629 | mci_pdev = pci_dev_get(dev: pdev); |
630 | |
631 | return rc; |
632 | } |
633 | |
634 | static void ie31200_remove_one(struct pci_dev *pdev) |
635 | { |
636 | struct ie31200_priv *priv = ie31200_pvt.priv[0]; |
637 | |
638 | edac_dbg(0, "\n"); |
639 | pci_dev_put(dev: mci_pdev); |
640 | mci_pdev = NULL; |
641 | if (priv->cfg->cmci) |
642 | mce_unregister_decode_chain(nb: &ie31200_mce_dec); |
643 | ie31200_unregister_mcis(); |
644 | } |
645 | |
646 | static struct res_config snb_cfg = { |
647 | .mtype = MEM_DDR3, |
648 | .imc_num = 1, |
649 | .reg_mchbar_mask = GENMASK_ULL(38, 15), |
650 | .reg_mchbar_window_size = BIT_ULL(15), |
651 | .reg_eccerrlog_offset[0] = 0x40c8, |
652 | .reg_eccerrlog_offset[1] = 0x44c8, |
653 | .reg_eccerrlog_ce_mask = BIT_ULL(0), |
654 | .reg_eccerrlog_ue_mask = BIT_ULL(1), |
655 | .reg_eccerrlog_rank_mask = GENMASK_ULL(28, 27), |
656 | .reg_eccerrlog_syndrome_mask = GENMASK_ULL(23, 16), |
657 | .reg_mad_dimm_size_granularity = BIT_ULL(28), |
658 | .reg_mad_dimm_offset[0] = 0x5004, |
659 | .reg_mad_dimm_offset[1] = 0x5008, |
660 | .reg_mad_dimm_size_mask[0] = GENMASK(7, 0), |
661 | .reg_mad_dimm_size_mask[1] = GENMASK(15, 8), |
662 | .reg_mad_dimm_rank_mask[0] = BIT(17), |
663 | .reg_mad_dimm_rank_mask[1] = BIT(18), |
664 | .reg_mad_dimm_width_mask[0] = BIT(19), |
665 | .reg_mad_dimm_width_mask[1] = BIT(20), |
666 | }; |
667 | |
668 | static struct res_config skl_cfg = { |
669 | .mtype = MEM_DDR4, |
670 | .imc_num = 1, |
671 | .reg_mchbar_mask = GENMASK_ULL(38, 15), |
672 | .reg_mchbar_window_size = BIT_ULL(15), |
673 | .reg_eccerrlog_offset[0] = 0x4048, |
674 | .reg_eccerrlog_offset[1] = 0x4448, |
675 | .reg_eccerrlog_ce_mask = BIT_ULL(0), |
676 | .reg_eccerrlog_ue_mask = BIT_ULL(1), |
677 | .reg_eccerrlog_rank_mask = GENMASK_ULL(28, 27), |
678 | .reg_eccerrlog_syndrome_mask = GENMASK_ULL(23, 16), |
679 | .reg_mad_dimm_size_granularity = BIT_ULL(30), |
680 | .reg_mad_dimm_offset[0] = 0x500c, |
681 | .reg_mad_dimm_offset[1] = 0x5010, |
682 | .reg_mad_dimm_size_mask[0] = GENMASK(5, 0), |
683 | .reg_mad_dimm_size_mask[1] = GENMASK(21, 16), |
684 | .reg_mad_dimm_rank_mask[0] = BIT(10), |
685 | .reg_mad_dimm_rank_mask[1] = BIT(26), |
686 | .reg_mad_dimm_width_mask[0] = GENMASK(9, 8), |
687 | .reg_mad_dimm_width_mask[1] = GENMASK(25, 24), |
688 | }; |
689 | |
690 | struct res_config rpl_s_cfg = { |
691 | .mtype = MEM_DDR5, |
692 | .cmci = true, |
693 | .imc_num = 2, |
694 | .reg_mchbar_mask = GENMASK_ULL(41, 17), |
695 | .reg_mchbar_window_size = BIT_ULL(16), |
696 | .reg_eccerrlog_offset[0] = 0xe048, |
697 | .reg_eccerrlog_offset[1] = 0xe848, |
698 | .reg_eccerrlog_ce_mask = BIT_ULL(0), |
699 | .reg_eccerrlog_ce_ovfl_mask = BIT_ULL(1), |
700 | .reg_eccerrlog_ue_mask = BIT_ULL(2), |
701 | .reg_eccerrlog_ue_ovfl_mask = BIT_ULL(3), |
702 | .reg_eccerrlog_rank_mask = GENMASK_ULL(28, 27), |
703 | .reg_eccerrlog_syndrome_mask = GENMASK_ULL(23, 16), |
704 | .msr_clear_eccerrlog_offset = 0x791, |
705 | .reg_mad_dimm_offset[0] = 0xd80c, |
706 | .reg_mad_dimm_offset[1] = 0xd810, |
707 | .reg_mad_dimm_size_granularity = BIT_ULL(29), |
708 | .reg_mad_dimm_size_mask[0] = GENMASK(6, 0), |
709 | .reg_mad_dimm_size_mask[1] = GENMASK(22, 16), |
710 | .reg_mad_dimm_rank_mask[0] = GENMASK(10, 9), |
711 | .reg_mad_dimm_rank_mask[1] = GENMASK(27, 26), |
712 | .reg_mad_dimm_width_mask[0] = GENMASK(8, 7), |
713 | .reg_mad_dimm_width_mask[1] = GENMASK(25, 24), |
714 | }; |
715 | |
716 | static const struct pci_device_id ie31200_pci_tbl[] = { |
717 | { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_HB_1), (kernel_ulong_t)&snb_cfg }, |
718 | { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_HB_2), .vendor: (kernel_ulong_t)&snb_cfg }, |
719 | { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_HB_3), (kernel_ulong_t)&snb_cfg }, |
720 | { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_HB_4), .vendor: (kernel_ulong_t)&snb_cfg }, |
721 | { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_HB_5), (kernel_ulong_t)&snb_cfg }, |
722 | { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_HB_6), .vendor: (kernel_ulong_t)&snb_cfg }, |
723 | { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_HB_7), (kernel_ulong_t)&snb_cfg }, |
724 | { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_HB_8), .vendor: (kernel_ulong_t)&skl_cfg }, |
725 | { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_HB_9), (kernel_ulong_t)&skl_cfg }, |
726 | { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_HB_10), .vendor: (kernel_ulong_t)&skl_cfg }, |
727 | { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_HB_11), (kernel_ulong_t)&skl_cfg }, |
728 | { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_HB_12), .vendor: (kernel_ulong_t)&skl_cfg }, |
729 | { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_HB_CFL_1), (kernel_ulong_t)&skl_cfg }, |
730 | { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_HB_CFL_2), .vendor: (kernel_ulong_t)&skl_cfg }, |
731 | { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_HB_CFL_3), (kernel_ulong_t)&skl_cfg }, |
732 | { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_HB_CFL_4), .vendor: (kernel_ulong_t)&skl_cfg }, |
733 | { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_HB_CFL_5), (kernel_ulong_t)&skl_cfg }, |
734 | { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_HB_CFL_6), .vendor: (kernel_ulong_t)&skl_cfg }, |
735 | { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_HB_CFL_7), (kernel_ulong_t)&skl_cfg }, |
736 | { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_HB_CFL_8), .vendor: (kernel_ulong_t)&skl_cfg }, |
737 | { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_HB_CFL_9), (kernel_ulong_t)&skl_cfg }, |
738 | { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_HB_CFL_10), .vendor: (kernel_ulong_t)&skl_cfg }, |
739 | { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_RPL_S_1), (kernel_ulong_t)&rpl_s_cfg}, |
740 | { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_RPL_S_2), .vendor: (kernel_ulong_t)&rpl_s_cfg}, |
741 | { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_RPL_S_3), (kernel_ulong_t)&rpl_s_cfg}, |
742 | { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_RPL_S_4), .vendor: (kernel_ulong_t)&rpl_s_cfg}, |
743 | { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IE31200_ADL_S_1), (kernel_ulong_t)&rpl_s_cfg}, |
744 | { 0, } /* 0 terminated list. */ |
745 | }; |
746 | MODULE_DEVICE_TABLE(pci, ie31200_pci_tbl); |
747 | |
748 | static struct pci_driver ie31200_driver = { |
749 | .name = EDAC_MOD_STR, |
750 | .probe = ie31200_init_one, |
751 | .remove = ie31200_remove_one, |
752 | .id_table = ie31200_pci_tbl, |
753 | }; |
754 | |
755 | static int __init ie31200_init(void) |
756 | { |
757 | int pci_rc, i; |
758 | |
759 | edac_dbg(3, "MC:\n"); |
760 | |
761 | pci_rc = pci_register_driver(&ie31200_driver); |
762 | if (pci_rc < 0) |
763 | return pci_rc; |
764 | |
765 | if (!mci_pdev) { |
766 | ie31200_registered = 0; |
767 | for (i = 0; ie31200_pci_tbl[i].vendor != 0; i++) { |
768 | mci_pdev = pci_get_device(vendor: ie31200_pci_tbl[i].vendor, |
769 | device: ie31200_pci_tbl[i].device, |
770 | NULL); |
771 | if (mci_pdev) |
772 | break; |
773 | } |
774 | |
775 | if (!mci_pdev) { |
776 | edac_dbg(0, "ie31200 pci_get_device fail\n"); |
777 | pci_rc = -ENODEV; |
778 | goto fail0; |
779 | } |
780 | |
781 | pci_rc = ie31200_init_one(pdev: mci_pdev, ent: &ie31200_pci_tbl[i]); |
782 | if (pci_rc < 0) { |
783 | edac_dbg(0, "ie31200 init fail\n"); |
784 | pci_rc = -ENODEV; |
785 | goto fail1; |
786 | } |
787 | } |
788 | |
789 | return 0; |
790 | fail1: |
791 | pci_dev_put(dev: mci_pdev); |
792 | fail0: |
793 | pci_unregister_driver(dev: &ie31200_driver); |
794 | |
795 | return pci_rc; |
796 | } |
797 | |
798 | static void __exit ie31200_exit(void) |
799 | { |
800 | edac_dbg(3, "MC:\n"); |
801 | pci_unregister_driver(dev: &ie31200_driver); |
802 | if (!ie31200_registered) |
803 | ie31200_remove_one(pdev: mci_pdev); |
804 | } |
805 | |
806 | module_init(ie31200_init); |
807 | module_exit(ie31200_exit); |
808 | |
809 | MODULE_LICENSE("GPL"); |
810 | MODULE_AUTHOR("Jason Baron <jbaron@akamai.com>"); |
811 | MODULE_DESCRIPTION("MC support for Intel Processor E31200 memory hub controllers"); |
812 |
Definitions
- nr_channels
- mci_pdev
- ie31200_registered
- res_config
- ie31200_priv
- ie31200_pvt
- ie31200_pvt
- ie31200_chips
- ie31200_dev_info
- ie31200_error_info
- ie31200_devs
- dimm_data
- how_many_channels
- ecc_capable
- ie31200_clear_error_info
- ie31200_get_and_clear_error_info
- ie31200_process_error_info
- __ie31200_check
- ie31200_check
- ie31200_map_mchbar
- populate_dimm_info
- ie31200_get_dimm_config
- ie31200_register_mci
- mce_check
- mce_handler
- ie31200_mce_dec
- ie31200_unregister_mcis
- ie31200_probe1
- ie31200_init_one
- ie31200_remove_one
- snb_cfg
- skl_cfg
- rpl_s_cfg
- ie31200_pci_tbl
- ie31200_driver
- ie31200_init
Improve your Profiling and Debugging skills
Find out more