18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Driver for Intel(R) 10nm server memory controller. 48c2ecf20Sopenharmony_ci * Copyright (c) 2019, Intel Corporation. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/kernel.h> 98c2ecf20Sopenharmony_ci#include <linux/io.h> 108c2ecf20Sopenharmony_ci#include <asm/cpu_device_id.h> 118c2ecf20Sopenharmony_ci#include <asm/intel-family.h> 128c2ecf20Sopenharmony_ci#include <asm/mce.h> 138c2ecf20Sopenharmony_ci#include "edac_module.h" 148c2ecf20Sopenharmony_ci#include "skx_common.h" 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#define I10NM_REVISION "v0.0.3" 178c2ecf20Sopenharmony_ci#define EDAC_MOD_STR "i10nm_edac" 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci/* Debug macros */ 208c2ecf20Sopenharmony_ci#define i10nm_printk(level, fmt, arg...) \ 218c2ecf20Sopenharmony_ci edac_printk(level, "i10nm", fmt, ##arg) 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#define I10NM_GET_SCK_BAR(d, reg) \ 248c2ecf20Sopenharmony_ci pci_read_config_dword((d)->uracu, 0xd0, &(reg)) 258c2ecf20Sopenharmony_ci#define I10NM_GET_IMC_BAR(d, i, reg) \ 268c2ecf20Sopenharmony_ci pci_read_config_dword((d)->uracu, 0xd8 + (i) * 4, &(reg)) 278c2ecf20Sopenharmony_ci#define I10NM_GET_DIMMMTR(m, i, j) \ 288c2ecf20Sopenharmony_ci readl((m)->mbase + 0x2080c + (i) * 0x4000 + (j) * 4) 298c2ecf20Sopenharmony_ci#define I10NM_GET_MCDDRTCFG(m, i) \ 308c2ecf20Sopenharmony_ci readl((m)->mbase + 0x20970 + (i) * 0x4000) 318c2ecf20Sopenharmony_ci#define I10NM_GET_MCMTR(m, i) \ 328c2ecf20Sopenharmony_ci readl((m)->mbase + 0x20ef8 + (i) * 0x4000) 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#define I10NM_GET_SCK_MMIO_BASE(reg) (GET_BITFIELD(reg, 0, 28) << 23) 358c2ecf20Sopenharmony_ci#define I10NM_GET_IMC_MMIO_OFFSET(reg) (GET_BITFIELD(reg, 0, 10) << 12) 368c2ecf20Sopenharmony_ci#define I10NM_GET_IMC_MMIO_SIZE(reg) ((GET_BITFIELD(reg, 13, 23) - \ 378c2ecf20Sopenharmony_ci GET_BITFIELD(reg, 0, 10) + 1) << 12) 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistatic struct list_head *i10nm_edac_list; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistatic struct pci_dev *pci_get_dev_wrapper(int dom, unsigned int bus, 428c2ecf20Sopenharmony_ci unsigned int dev, unsigned int fun) 438c2ecf20Sopenharmony_ci{ 448c2ecf20Sopenharmony_ci struct pci_dev *pdev; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci pdev = pci_get_domain_bus_and_slot(dom, bus, PCI_DEVFN(dev, fun)); 478c2ecf20Sopenharmony_ci if (!pdev) { 488c2ecf20Sopenharmony_ci edac_dbg(2, "No device %02x:%02x.%x\n", 498c2ecf20Sopenharmony_ci bus, dev, fun); 508c2ecf20Sopenharmony_ci return NULL; 518c2ecf20Sopenharmony_ci } 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci if (unlikely(pci_enable_device(pdev) < 0)) { 548c2ecf20Sopenharmony_ci edac_dbg(2, "Failed to enable device %02x:%02x.%x\n", 558c2ecf20Sopenharmony_ci bus, dev, fun); 568c2ecf20Sopenharmony_ci pci_dev_put(pdev); 578c2ecf20Sopenharmony_ci return NULL; 588c2ecf20Sopenharmony_ci } 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci return pdev; 618c2ecf20Sopenharmony_ci} 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_cistatic int i10nm_get_all_munits(void) 648c2ecf20Sopenharmony_ci{ 658c2ecf20Sopenharmony_ci struct pci_dev *mdev; 668c2ecf20Sopenharmony_ci void __iomem *mbase; 678c2ecf20Sopenharmony_ci unsigned long size; 688c2ecf20Sopenharmony_ci struct skx_dev *d; 698c2ecf20Sopenharmony_ci int i, j = 0; 708c2ecf20Sopenharmony_ci u32 reg, off; 718c2ecf20Sopenharmony_ci u64 base; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci list_for_each_entry(d, i10nm_edac_list, list) { 748c2ecf20Sopenharmony_ci d->util_all = pci_get_dev_wrapper(d->seg, d->bus[1], 29, 1); 758c2ecf20Sopenharmony_ci if (!d->util_all) 768c2ecf20Sopenharmony_ci return -ENODEV; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci d->uracu = pci_get_dev_wrapper(d->seg, d->bus[0], 0, 1); 798c2ecf20Sopenharmony_ci if (!d->uracu) 808c2ecf20Sopenharmony_ci return -ENODEV; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci if (I10NM_GET_SCK_BAR(d, reg)) { 838c2ecf20Sopenharmony_ci i10nm_printk(KERN_ERR, "Failed to socket bar\n"); 848c2ecf20Sopenharmony_ci return -ENODEV; 858c2ecf20Sopenharmony_ci } 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci base = I10NM_GET_SCK_MMIO_BASE(reg); 888c2ecf20Sopenharmony_ci edac_dbg(2, "socket%d mmio base 0x%llx (reg 0x%x)\n", 898c2ecf20Sopenharmony_ci j++, base, reg); 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci for (i = 0; i < I10NM_NUM_IMC; i++) { 928c2ecf20Sopenharmony_ci mdev = pci_get_dev_wrapper(d->seg, d->bus[0], 938c2ecf20Sopenharmony_ci 12 + i, 0); 948c2ecf20Sopenharmony_ci if (i == 0 && !mdev) { 958c2ecf20Sopenharmony_ci i10nm_printk(KERN_ERR, "No IMC found\n"); 968c2ecf20Sopenharmony_ci return -ENODEV; 978c2ecf20Sopenharmony_ci } 988c2ecf20Sopenharmony_ci if (!mdev) 998c2ecf20Sopenharmony_ci continue; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci d->imc[i].mdev = mdev; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci if (I10NM_GET_IMC_BAR(d, i, reg)) { 1048c2ecf20Sopenharmony_ci i10nm_printk(KERN_ERR, "Failed to get mc bar\n"); 1058c2ecf20Sopenharmony_ci return -ENODEV; 1068c2ecf20Sopenharmony_ci } 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci off = I10NM_GET_IMC_MMIO_OFFSET(reg); 1098c2ecf20Sopenharmony_ci size = I10NM_GET_IMC_MMIO_SIZE(reg); 1108c2ecf20Sopenharmony_ci edac_dbg(2, "mc%d mmio base 0x%llx size 0x%lx (reg 0x%x)\n", 1118c2ecf20Sopenharmony_ci i, base + off, size, reg); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci mbase = ioremap(base + off, size); 1148c2ecf20Sopenharmony_ci if (!mbase) { 1158c2ecf20Sopenharmony_ci i10nm_printk(KERN_ERR, "Failed to ioremap 0x%llx\n", 1168c2ecf20Sopenharmony_ci base + off); 1178c2ecf20Sopenharmony_ci return -ENODEV; 1188c2ecf20Sopenharmony_ci } 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci d->imc[i].mbase = mbase; 1218c2ecf20Sopenharmony_ci } 1228c2ecf20Sopenharmony_ci } 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci return 0; 1258c2ecf20Sopenharmony_ci} 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_cistatic struct res_config i10nm_cfg0 = { 1288c2ecf20Sopenharmony_ci .type = I10NM, 1298c2ecf20Sopenharmony_ci .decs_did = 0x3452, 1308c2ecf20Sopenharmony_ci .busno_cfg_offset = 0xcc, 1318c2ecf20Sopenharmony_ci}; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_cistatic struct res_config i10nm_cfg1 = { 1348c2ecf20Sopenharmony_ci .type = I10NM, 1358c2ecf20Sopenharmony_ci .decs_did = 0x3452, 1368c2ecf20Sopenharmony_ci .busno_cfg_offset = 0xd0, 1378c2ecf20Sopenharmony_ci}; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_cistatic const struct x86_cpu_id i10nm_cpuids[] = { 1408c2ecf20Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL_STEPPINGS(ATOM_TREMONT_D, X86_STEPPINGS(0x0, 0x3), &i10nm_cfg0), 1418c2ecf20Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL_STEPPINGS(ATOM_TREMONT_D, X86_STEPPINGS(0x4, 0xf), &i10nm_cfg1), 1428c2ecf20Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL_STEPPINGS(ICELAKE_X, X86_STEPPINGS(0x0, 0x3), &i10nm_cfg0), 1438c2ecf20Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL_STEPPINGS(ICELAKE_X, X86_STEPPINGS(0x4, 0xf), &i10nm_cfg1), 1448c2ecf20Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL_STEPPINGS(ICELAKE_D, X86_STEPPINGS(0x0, 0xf), &i10nm_cfg1), 1458c2ecf20Sopenharmony_ci {} 1468c2ecf20Sopenharmony_ci}; 1478c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(x86cpu, i10nm_cpuids); 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_cistatic bool i10nm_check_ecc(struct skx_imc *imc, int chan) 1508c2ecf20Sopenharmony_ci{ 1518c2ecf20Sopenharmony_ci u32 mcmtr; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci mcmtr = I10NM_GET_MCMTR(imc, chan); 1548c2ecf20Sopenharmony_ci edac_dbg(1, "ch%d mcmtr reg %x\n", chan, mcmtr); 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci return !!GET_BITFIELD(mcmtr, 2, 2); 1578c2ecf20Sopenharmony_ci} 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_cistatic int i10nm_get_dimm_config(struct mem_ctl_info *mci) 1608c2ecf20Sopenharmony_ci{ 1618c2ecf20Sopenharmony_ci struct skx_pvt *pvt = mci->pvt_info; 1628c2ecf20Sopenharmony_ci struct skx_imc *imc = pvt->imc; 1638c2ecf20Sopenharmony_ci struct dimm_info *dimm; 1648c2ecf20Sopenharmony_ci u32 mtr, mcddrtcfg; 1658c2ecf20Sopenharmony_ci int i, j, ndimms; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci for (i = 0; i < I10NM_NUM_CHANNELS; i++) { 1688c2ecf20Sopenharmony_ci if (!imc->mbase) 1698c2ecf20Sopenharmony_ci continue; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci ndimms = 0; 1728c2ecf20Sopenharmony_ci mcddrtcfg = I10NM_GET_MCDDRTCFG(imc, i); 1738c2ecf20Sopenharmony_ci for (j = 0; j < I10NM_NUM_DIMMS; j++) { 1748c2ecf20Sopenharmony_ci dimm = edac_get_dimm(mci, i, j, 0); 1758c2ecf20Sopenharmony_ci mtr = I10NM_GET_DIMMMTR(imc, i, j); 1768c2ecf20Sopenharmony_ci edac_dbg(1, "dimmmtr 0x%x mcddrtcfg 0x%x (mc%d ch%d dimm%d)\n", 1778c2ecf20Sopenharmony_ci mtr, mcddrtcfg, imc->mc, i, j); 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci if (IS_DIMM_PRESENT(mtr)) 1808c2ecf20Sopenharmony_ci ndimms += skx_get_dimm_info(mtr, 0, 0, dimm, 1818c2ecf20Sopenharmony_ci imc, i, j); 1828c2ecf20Sopenharmony_ci else if (IS_NVDIMM_PRESENT(mcddrtcfg, j)) 1838c2ecf20Sopenharmony_ci ndimms += skx_get_nvdimm_info(dimm, imc, i, j, 1848c2ecf20Sopenharmony_ci EDAC_MOD_STR); 1858c2ecf20Sopenharmony_ci } 1868c2ecf20Sopenharmony_ci if (ndimms && !i10nm_check_ecc(imc, i)) { 1878c2ecf20Sopenharmony_ci i10nm_printk(KERN_ERR, "ECC is disabled on imc %d channel %d\n", 1888c2ecf20Sopenharmony_ci imc->mc, i); 1898c2ecf20Sopenharmony_ci return -ENODEV; 1908c2ecf20Sopenharmony_ci } 1918c2ecf20Sopenharmony_ci } 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci return 0; 1948c2ecf20Sopenharmony_ci} 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_cistatic struct notifier_block i10nm_mce_dec = { 1978c2ecf20Sopenharmony_ci .notifier_call = skx_mce_check_error, 1988c2ecf20Sopenharmony_ci .priority = MCE_PRIO_EDAC, 1998c2ecf20Sopenharmony_ci}; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci#ifdef CONFIG_EDAC_DEBUG 2028c2ecf20Sopenharmony_ci/* 2038c2ecf20Sopenharmony_ci * Debug feature. 2048c2ecf20Sopenharmony_ci * Exercise the address decode logic by writing an address to 2058c2ecf20Sopenharmony_ci * /sys/kernel/debug/edac/i10nm_test/addr. 2068c2ecf20Sopenharmony_ci */ 2078c2ecf20Sopenharmony_cistatic struct dentry *i10nm_test; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_cistatic int debugfs_u64_set(void *data, u64 val) 2108c2ecf20Sopenharmony_ci{ 2118c2ecf20Sopenharmony_ci struct mce m; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci pr_warn_once("Fake error to 0x%llx injected via debugfs\n", val); 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci memset(&m, 0, sizeof(m)); 2168c2ecf20Sopenharmony_ci /* ADDRV + MemRd + Unknown channel */ 2178c2ecf20Sopenharmony_ci m.status = MCI_STATUS_ADDRV + 0x90; 2188c2ecf20Sopenharmony_ci /* One corrected error */ 2198c2ecf20Sopenharmony_ci m.status |= BIT_ULL(MCI_STATUS_CEC_SHIFT); 2208c2ecf20Sopenharmony_ci m.addr = val; 2218c2ecf20Sopenharmony_ci skx_mce_check_error(NULL, 0, &m); 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci return 0; 2248c2ecf20Sopenharmony_ci} 2258c2ecf20Sopenharmony_ciDEFINE_SIMPLE_ATTRIBUTE(fops_u64_wo, NULL, debugfs_u64_set, "%llu\n"); 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_cistatic void setup_i10nm_debug(void) 2288c2ecf20Sopenharmony_ci{ 2298c2ecf20Sopenharmony_ci i10nm_test = edac_debugfs_create_dir("i10nm_test"); 2308c2ecf20Sopenharmony_ci if (!i10nm_test) 2318c2ecf20Sopenharmony_ci return; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci if (!edac_debugfs_create_file("addr", 0200, i10nm_test, 2348c2ecf20Sopenharmony_ci NULL, &fops_u64_wo)) { 2358c2ecf20Sopenharmony_ci debugfs_remove(i10nm_test); 2368c2ecf20Sopenharmony_ci i10nm_test = NULL; 2378c2ecf20Sopenharmony_ci } 2388c2ecf20Sopenharmony_ci} 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_cistatic void teardown_i10nm_debug(void) 2418c2ecf20Sopenharmony_ci{ 2428c2ecf20Sopenharmony_ci debugfs_remove_recursive(i10nm_test); 2438c2ecf20Sopenharmony_ci} 2448c2ecf20Sopenharmony_ci#else 2458c2ecf20Sopenharmony_cistatic inline void setup_i10nm_debug(void) {} 2468c2ecf20Sopenharmony_cistatic inline void teardown_i10nm_debug(void) {} 2478c2ecf20Sopenharmony_ci#endif /*CONFIG_EDAC_DEBUG*/ 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_cistatic int __init i10nm_init(void) 2508c2ecf20Sopenharmony_ci{ 2518c2ecf20Sopenharmony_ci u8 mc = 0, src_id = 0, node_id = 0; 2528c2ecf20Sopenharmony_ci const struct x86_cpu_id *id; 2538c2ecf20Sopenharmony_ci struct res_config *cfg; 2548c2ecf20Sopenharmony_ci const char *owner; 2558c2ecf20Sopenharmony_ci struct skx_dev *d; 2568c2ecf20Sopenharmony_ci int rc, i, off[3] = {0xd0, 0xc8, 0xcc}; 2578c2ecf20Sopenharmony_ci u64 tolm, tohm; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci edac_dbg(2, "\n"); 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci owner = edac_get_owner(); 2628c2ecf20Sopenharmony_ci if (owner && strncmp(owner, EDAC_MOD_STR, sizeof(EDAC_MOD_STR))) 2638c2ecf20Sopenharmony_ci return -EBUSY; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci if (cpu_feature_enabled(X86_FEATURE_HYPERVISOR)) 2668c2ecf20Sopenharmony_ci return -ENODEV; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci id = x86_match_cpu(i10nm_cpuids); 2698c2ecf20Sopenharmony_ci if (!id) 2708c2ecf20Sopenharmony_ci return -ENODEV; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci cfg = (struct res_config *)id->driver_data; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci rc = skx_get_hi_lo(0x09a2, off, &tolm, &tohm); 2758c2ecf20Sopenharmony_ci if (rc) 2768c2ecf20Sopenharmony_ci return rc; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci rc = skx_get_all_bus_mappings(cfg, &i10nm_edac_list); 2798c2ecf20Sopenharmony_ci if (rc < 0) 2808c2ecf20Sopenharmony_ci goto fail; 2818c2ecf20Sopenharmony_ci if (rc == 0) { 2828c2ecf20Sopenharmony_ci i10nm_printk(KERN_ERR, "No memory controllers found\n"); 2838c2ecf20Sopenharmony_ci return -ENODEV; 2848c2ecf20Sopenharmony_ci } 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci rc = i10nm_get_all_munits(); 2878c2ecf20Sopenharmony_ci if (rc < 0) 2888c2ecf20Sopenharmony_ci goto fail; 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci list_for_each_entry(d, i10nm_edac_list, list) { 2918c2ecf20Sopenharmony_ci rc = skx_get_src_id(d, 0xf8, &src_id); 2928c2ecf20Sopenharmony_ci if (rc < 0) 2938c2ecf20Sopenharmony_ci goto fail; 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci rc = skx_get_node_id(d, &node_id); 2968c2ecf20Sopenharmony_ci if (rc < 0) 2978c2ecf20Sopenharmony_ci goto fail; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci edac_dbg(2, "src_id = %d node_id = %d\n", src_id, node_id); 3008c2ecf20Sopenharmony_ci for (i = 0; i < I10NM_NUM_IMC; i++) { 3018c2ecf20Sopenharmony_ci if (!d->imc[i].mdev) 3028c2ecf20Sopenharmony_ci continue; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci d->imc[i].mc = mc++; 3058c2ecf20Sopenharmony_ci d->imc[i].lmc = i; 3068c2ecf20Sopenharmony_ci d->imc[i].src_id = src_id; 3078c2ecf20Sopenharmony_ci d->imc[i].node_id = node_id; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci rc = skx_register_mci(&d->imc[i], d->imc[i].mdev, 3108c2ecf20Sopenharmony_ci "Intel_10nm Socket", EDAC_MOD_STR, 3118c2ecf20Sopenharmony_ci i10nm_get_dimm_config); 3128c2ecf20Sopenharmony_ci if (rc < 0) 3138c2ecf20Sopenharmony_ci goto fail; 3148c2ecf20Sopenharmony_ci } 3158c2ecf20Sopenharmony_ci } 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci rc = skx_adxl_get(); 3188c2ecf20Sopenharmony_ci if (rc) 3198c2ecf20Sopenharmony_ci goto fail; 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci opstate_init(); 3228c2ecf20Sopenharmony_ci mce_register_decode_chain(&i10nm_mce_dec); 3238c2ecf20Sopenharmony_ci setup_i10nm_debug(); 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci i10nm_printk(KERN_INFO, "%s\n", I10NM_REVISION); 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci return 0; 3288c2ecf20Sopenharmony_cifail: 3298c2ecf20Sopenharmony_ci skx_remove(); 3308c2ecf20Sopenharmony_ci return rc; 3318c2ecf20Sopenharmony_ci} 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_cistatic void __exit i10nm_exit(void) 3348c2ecf20Sopenharmony_ci{ 3358c2ecf20Sopenharmony_ci edac_dbg(2, "\n"); 3368c2ecf20Sopenharmony_ci teardown_i10nm_debug(); 3378c2ecf20Sopenharmony_ci mce_unregister_decode_chain(&i10nm_mce_dec); 3388c2ecf20Sopenharmony_ci skx_adxl_put(); 3398c2ecf20Sopenharmony_ci skx_remove(); 3408c2ecf20Sopenharmony_ci} 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_cimodule_init(i10nm_init); 3438c2ecf20Sopenharmony_cimodule_exit(i10nm_exit); 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 3468c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("MC Driver for Intel 10nm server processors"); 347