Commit | Line | Data |
---|---|---|
d4dc89d0 QZ |
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"); |