Skip to content

Commit d4dc89d

Browse files
qzhuo2suryasaimadhu
authored andcommitted
EDAC, i10nm: Add a driver for Intel 10nm server processors
This driver supports the Intel 10nm series server integrated memory controller. It gets the memory capacity and topology information by reading the registers in PCI configuration space and memory-mapped I/O. It decodes the memory error address to the platform specific address by using the ACPI Address Translation (ADXL) Device Specific Method (DSM). Co-developed-by: Tony Luck <tony.luck@intel.com> Signed-off-by: Qiuxu Zhuo <qiuxu.zhuo@intel.com> Signed-off-by: Tony Luck <tony.luck@intel.com> Signed-off-by: Borislav Petkov <bp@suse.de> Cc: James Morse <james.morse@arm.com> Cc: Mauro Carvalho Chehab <mchehab@kernel.org> Cc: linux-edac <linux-edac@vger.kernel.org> Link: https://lkml.kernel.org/r/20190130191519.15393-5-tony.luck@intel.com
1 parent 98f2fc8 commit d4dc89d

File tree

3 files changed

+290
-0
lines changed

3 files changed

+290
-0
lines changed

drivers/edac/Kconfig

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,18 @@ config EDAC_SKX
241241
system has non-volatile DIMMs you should also manually
242242
select CONFIG_ACPI_NFIT.
243243

244+
config EDAC_I10NM
245+
tristate "Intel 10nm server Integrated MC"
246+
depends on PCI && X86_64 && X86_MCE_INTEL && PCI_MMCONFIG
247+
depends on ACPI_NFIT || !ACPI_NFIT # if ACPI_NFIT=m, EDAC_I10NM can't be y
248+
select DMI
249+
select ACPI_ADXL if ACPI
250+
help
251+
Support for error detection and correction the Intel
252+
10nm server Integrated Memory Controllers. If your
253+
system has non-volatile DIMMs you should also manually
254+
select CONFIG_ACPI_NFIT.
255+
244256
config EDAC_PND2
245257
tristate "Intel Pondicherry2"
246258
depends on PCI && X86_64 && X86_MCE_INTEL

drivers/edac/Makefile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,9 @@ obj-$(CONFIG_EDAC_LAYERSCAPE) += layerscape_edac_mod.o
6060
skx_edac-y := skx_common.o skx_base.o
6161
obj-$(CONFIG_EDAC_SKX) += skx_edac.o
6262

63+
i10nm_edac-y := skx_common.o i10nm_base.o
64+
obj-$(CONFIG_EDAC_I10NM) += i10nm_edac.o
65+
6366
obj-$(CONFIG_EDAC_MV64X60) += mv64x60_edac.o
6467
obj-$(CONFIG_EDAC_CELL) += cell_edac.o
6568
obj-$(CONFIG_EDAC_PPC4XX) += ppc4xx_edac.o

drivers/edac/i10nm_base.c

Lines changed: 275 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,275 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/*
3+
* Driver for Intel(R) 10nm server memory controller.
4+
* Copyright (c) 2019, Intel Corporation.
5+
*
6+
*/
7+
8+
#include <linux/kernel.h>
9+
#include <asm/cpu_device_id.h>
10+
#include <asm/intel-family.h>
11+
#include <asm/mce.h>
12+
#include "edac_module.h"
13+
#include "skx_common.h"
14+
15+
#define I10NM_REVISION "v0.0.3"
16+
#define EDAC_MOD_STR "i10nm_edac"
17+
18+
/* Debug macros */
19+
#define i10nm_printk(level, fmt, arg...) \
20+
edac_printk(level, "i10nm", fmt, ##arg)
21+
22+
#define I10NM_GET_SCK_BAR(d, reg) \
23+
pci_read_config_dword((d)->uracu, 0xd0, &(reg))
24+
#define I10NM_GET_IMC_BAR(d, i, reg) \
25+
pci_read_config_dword((d)->uracu, 0xd8 + (i) * 4, &(reg))
26+
#define I10NM_GET_DIMMMTR(m, i, j) \
27+
(*(u32 *)((m)->mbase + 0x2080c + (i) * 0x4000 + (j) * 4))
28+
#define I10NM_GET_MCDDRTCFG(m, i, j) \
29+
(*(u32 *)((m)->mbase + 0x20970 + (i) * 0x4000 + (j) * 4))
30+
31+
#define I10NM_GET_SCK_MMIO_BASE(reg) (GET_BITFIELD(reg, 0, 28) << 23)
32+
#define I10NM_GET_IMC_MMIO_OFFSET(reg) (GET_BITFIELD(reg, 0, 10) << 12)
33+
#define I10NM_GET_IMC_MMIO_SIZE(reg) ((GET_BITFIELD(reg, 13, 23) - \
34+
GET_BITFIELD(reg, 0, 10) + 1) << 12)
35+
36+
static struct list_head *i10nm_edac_list;
37+
38+
static struct pci_dev *pci_get_dev_wrapper(int dom, unsigned int bus,
39+
unsigned int dev, unsigned int fun)
40+
{
41+
struct pci_dev *pdev;
42+
43+
pdev = pci_get_domain_bus_and_slot(dom, bus, PCI_DEVFN(dev, fun));
44+
if (!pdev) {
45+
edac_dbg(2, "No device %02x:%02x.%x\n",
46+
bus, dev, fun);
47+
return NULL;
48+
}
49+
50+
if (unlikely(pci_enable_device(pdev) < 0)) {
51+
edac_dbg(2, "Failed to enable device %02x:%02x.%x\n",
52+
bus, dev, fun);
53+
return NULL;
54+
}
55+
56+
pci_dev_get(pdev);
57+
58+
return pdev;
59+
}
60+
61+
static int i10nm_get_all_munits(void)
62+
{
63+
struct pci_dev *mdev;
64+
void __iomem *mbase;
65+
unsigned long size;
66+
struct skx_dev *d;
67+
int i, j = 0;
68+
u32 reg, off;
69+
u64 base;
70+
71+
list_for_each_entry(d, i10nm_edac_list, list) {
72+
d->util_all = pci_get_dev_wrapper(d->seg, d->bus[1], 29, 1);
73+
if (!d->util_all)
74+
return -ENODEV;
75+
76+
d->uracu = pci_get_dev_wrapper(d->seg, d->bus[0], 0, 1);
77+
if (!d->uracu)
78+
return -ENODEV;
79+
80+
if (I10NM_GET_SCK_BAR(d, reg)) {
81+
i10nm_printk(KERN_ERR, "Failed to socket bar\n");
82+
return -ENODEV;
83+
}
84+
85+
base = I10NM_GET_SCK_MMIO_BASE(reg);
86+
edac_dbg(2, "socket%d mmio base 0x%llx (reg 0x%x)\n",
87+
j++, base, reg);
88+
89+
for (i = 0; i < I10NM_NUM_IMC; i++) {
90+
mdev = pci_get_dev_wrapper(d->seg, d->bus[0],
91+
12 + i, 0);
92+
if (i == 0 && !mdev) {
93+
i10nm_printk(KERN_ERR, "No IMC found\n");
94+
return -ENODEV;
95+
}
96+
if (!mdev)
97+
continue;
98+
99+
d->imc[i].mdev = mdev;
100+
101+
if (I10NM_GET_IMC_BAR(d, i, reg)) {
102+
i10nm_printk(KERN_ERR, "Failed to get mc bar\n");
103+
return -ENODEV;
104+
}
105+
106+
off = I10NM_GET_IMC_MMIO_OFFSET(reg);
107+
size = I10NM_GET_IMC_MMIO_SIZE(reg);
108+
edac_dbg(2, "mc%d mmio base 0x%llx size 0x%lx (reg 0x%x)\n",
109+
i, base + off, size, reg);
110+
111+
mbase = ioremap(base + off, size);
112+
if (!mbase) {
113+
i10nm_printk(KERN_ERR, "Failed to ioremap 0x%llx\n",
114+
base + off);
115+
return -ENODEV;
116+
}
117+
118+
d->imc[i].mbase = mbase;
119+
}
120+
}
121+
122+
return 0;
123+
}
124+
125+
static const struct x86_cpu_id i10nm_cpuids[] = {
126+
{ X86_VENDOR_INTEL, 6, INTEL_FAM6_ATOM_TREMONT_X, 0, 0 },
127+
{ }
128+
};
129+
MODULE_DEVICE_TABLE(x86cpu, i10nm_cpuids);
130+
131+
static bool i10nm_check_ecc(struct skx_imc *imc, int chan)
132+
{
133+
u32 mcmtr;
134+
135+
mcmtr = *(u32 *)(imc->mbase + 0x20ef8 + chan * 0x4000);
136+
edac_dbg(1, "ch%d mcmtr reg %x\n", chan, mcmtr);
137+
138+
return !!GET_BITFIELD(mcmtr, 2, 2);
139+
}
140+
141+
static int i10nm_get_dimm_config(struct mem_ctl_info *mci)
142+
{
143+
struct skx_pvt *pvt = mci->pvt_info;
144+
struct skx_imc *imc = pvt->imc;
145+
struct dimm_info *dimm;
146+
u32 mtr, mcddrtcfg;
147+
int i, j, ndimms;
148+
149+
for (i = 0; i < I10NM_NUM_CHANNELS; i++) {
150+
if (!imc->mbase)
151+
continue;
152+
153+
ndimms = 0;
154+
for (j = 0; j < I10NM_NUM_DIMMS; j++) {
155+
dimm = EDAC_DIMM_PTR(mci->layers, mci->dimms,
156+
mci->n_layers, i, j, 0);
157+
mtr = I10NM_GET_DIMMMTR(imc, i, j);
158+
mcddrtcfg = I10NM_GET_MCDDRTCFG(imc, i, j);
159+
edac_dbg(1, "dimmmtr 0x%x mcddrtcfg 0x%x (mc%d ch%d dimm%d)\n",
160+
mtr, mcddrtcfg, imc->mc, i, j);
161+
162+
if (IS_DIMM_PRESENT(mtr))
163+
ndimms += skx_get_dimm_info(mtr, 0, dimm,
164+
imc, i, j);
165+
else if (IS_NVDIMM_PRESENT(mcddrtcfg, j))
166+
ndimms += skx_get_nvdimm_info(dimm, imc, i, j,
167+
EDAC_MOD_STR);
168+
}
169+
if (ndimms && !i10nm_check_ecc(imc, 0)) {
170+
i10nm_printk(KERN_ERR, "ECC is disabled on imc %d\n",
171+
imc->mc);
172+
return -ENODEV;
173+
}
174+
}
175+
176+
return 0;
177+
}
178+
179+
static struct notifier_block i10nm_mce_dec = {
180+
.notifier_call = skx_mce_check_error,
181+
.priority = MCE_PRIO_EDAC,
182+
};
183+
184+
static int __init i10nm_init(void)
185+
{
186+
u8 mc = 0, src_id = 0, node_id = 0;
187+
const struct x86_cpu_id *id;
188+
const char *owner;
189+
struct skx_dev *d;
190+
int rc, i, off[3] = {0xd0, 0xc8, 0xcc};
191+
u64 tolm, tohm;
192+
193+
edac_dbg(2, "\n");
194+
195+
owner = edac_get_owner();
196+
if (owner && strncmp(owner, EDAC_MOD_STR, sizeof(EDAC_MOD_STR)))
197+
return -EBUSY;
198+
199+
id = x86_match_cpu(i10nm_cpuids);
200+
if (!id)
201+
return -ENODEV;
202+
203+
rc = skx_get_hi_lo(0x09a2, off, &tolm, &tohm);
204+
if (rc)
205+
return rc;
206+
207+
rc = skx_get_all_bus_mappings(0x3452, 0xcc, I10NM, &i10nm_edac_list);
208+
if (rc < 0)
209+
goto fail;
210+
if (rc == 0) {
211+
i10nm_printk(KERN_ERR, "No memory controllers found\n");
212+
return -ENODEV;
213+
}
214+
215+
rc = i10nm_get_all_munits();
216+
if (rc < 0)
217+
goto fail;
218+
219+
list_for_each_entry(d, i10nm_edac_list, list) {
220+
rc = skx_get_src_id(d, &src_id);
221+
if (rc < 0)
222+
goto fail;
223+
224+
rc = skx_get_node_id(d, &node_id);
225+
if (rc < 0)
226+
goto fail;
227+
228+
edac_dbg(2, "src_id = %d node_id = %d\n", src_id, node_id);
229+
for (i = 0; i < I10NM_NUM_IMC; i++) {
230+
if (!d->imc[i].mdev)
231+
continue;
232+
233+
d->imc[i].mc = mc++;
234+
d->imc[i].lmc = i;
235+
d->imc[i].src_id = src_id;
236+
d->imc[i].node_id = node_id;
237+
238+
rc = skx_register_mci(&d->imc[i], d->imc[i].mdev,
239+
"Intel_10nm Socket", EDAC_MOD_STR,
240+
i10nm_get_dimm_config);
241+
if (rc < 0)
242+
goto fail;
243+
}
244+
}
245+
246+
rc = skx_adxl_get();
247+
if (rc)
248+
goto fail;
249+
250+
opstate_init();
251+
mce_register_decode_chain(&i10nm_mce_dec);
252+
setup_skx_debug("i10nm_test");
253+
254+
i10nm_printk(KERN_INFO, "%s\n", I10NM_REVISION);
255+
256+
return 0;
257+
fail:
258+
skx_remove();
259+
return rc;
260+
}
261+
262+
static void __exit i10nm_exit(void)
263+
{
264+
edac_dbg(2, "\n");
265+
teardown_skx_debug();
266+
mce_unregister_decode_chain(&i10nm_mce_dec);
267+
skx_adxl_put();
268+
skx_remove();
269+
}
270+
271+
module_init(i10nm_init);
272+
module_exit(i10nm_exit);
273+
274+
MODULE_LICENSE("GPL v2");
275+
MODULE_DESCRIPTION("MC Driver for Intel 10nm server processors");

0 commit comments

Comments
 (0)