18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Shared code by both skx_edac and i10nm_edac. Originally split out 58c2ecf20Sopenharmony_ci * from the skx_edac driver. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * This file is linked into both skx_edac and i10nm_edac drivers. In 88c2ecf20Sopenharmony_ci * order to avoid link errors, this file must be like a pure library 98c2ecf20Sopenharmony_ci * without including symbols and defines which would otherwise conflict, 108c2ecf20Sopenharmony_ci * when linked once into a module and into a built-in object, at the 118c2ecf20Sopenharmony_ci * same time. For example, __this_module symbol references when that 128c2ecf20Sopenharmony_ci * file is being linked into a built-in object. 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * Copyright (c) 2018, Intel Corporation. 158c2ecf20Sopenharmony_ci */ 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include <linux/acpi.h> 188c2ecf20Sopenharmony_ci#include <linux/dmi.h> 198c2ecf20Sopenharmony_ci#include <linux/adxl.h> 208c2ecf20Sopenharmony_ci#include <acpi/nfit.h> 218c2ecf20Sopenharmony_ci#include <asm/mce.h> 228c2ecf20Sopenharmony_ci#include "edac_module.h" 238c2ecf20Sopenharmony_ci#include "skx_common.h" 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_cistatic const char * const component_names[] = { 268c2ecf20Sopenharmony_ci [INDEX_SOCKET] = "ProcessorSocketId", 278c2ecf20Sopenharmony_ci [INDEX_MEMCTRL] = "MemoryControllerId", 288c2ecf20Sopenharmony_ci [INDEX_CHANNEL] = "ChannelId", 298c2ecf20Sopenharmony_ci [INDEX_DIMM] = "DimmSlotId", 308c2ecf20Sopenharmony_ci}; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistatic int component_indices[ARRAY_SIZE(component_names)]; 338c2ecf20Sopenharmony_cistatic int adxl_component_count; 348c2ecf20Sopenharmony_cistatic const char * const *adxl_component_names; 358c2ecf20Sopenharmony_cistatic u64 *adxl_values; 368c2ecf20Sopenharmony_cistatic char *adxl_msg; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistatic char skx_msg[MSG_SIZE]; 398c2ecf20Sopenharmony_cistatic skx_decode_f skx_decode; 408c2ecf20Sopenharmony_cistatic skx_show_retry_log_f skx_show_retry_rd_err_log; 418c2ecf20Sopenharmony_cistatic u64 skx_tolm, skx_tohm; 428c2ecf20Sopenharmony_cistatic LIST_HEAD(dev_edac_list); 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ciint __init skx_adxl_get(void) 458c2ecf20Sopenharmony_ci{ 468c2ecf20Sopenharmony_ci const char * const *names; 478c2ecf20Sopenharmony_ci int i, j; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci names = adxl_get_component_names(); 508c2ecf20Sopenharmony_ci if (!names) { 518c2ecf20Sopenharmony_ci skx_printk(KERN_NOTICE, "No firmware support for address translation.\n"); 528c2ecf20Sopenharmony_ci return -ENODEV; 538c2ecf20Sopenharmony_ci } 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci for (i = 0; i < INDEX_MAX; i++) { 568c2ecf20Sopenharmony_ci for (j = 0; names[j]; j++) { 578c2ecf20Sopenharmony_ci if (!strcmp(component_names[i], names[j])) { 588c2ecf20Sopenharmony_ci component_indices[i] = j; 598c2ecf20Sopenharmony_ci break; 608c2ecf20Sopenharmony_ci } 618c2ecf20Sopenharmony_ci } 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci if (!names[j]) 648c2ecf20Sopenharmony_ci goto err; 658c2ecf20Sopenharmony_ci } 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci adxl_component_names = names; 688c2ecf20Sopenharmony_ci while (*names++) 698c2ecf20Sopenharmony_ci adxl_component_count++; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci adxl_values = kcalloc(adxl_component_count, sizeof(*adxl_values), 728c2ecf20Sopenharmony_ci GFP_KERNEL); 738c2ecf20Sopenharmony_ci if (!adxl_values) { 748c2ecf20Sopenharmony_ci adxl_component_count = 0; 758c2ecf20Sopenharmony_ci return -ENOMEM; 768c2ecf20Sopenharmony_ci } 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci adxl_msg = kzalloc(MSG_SIZE, GFP_KERNEL); 798c2ecf20Sopenharmony_ci if (!adxl_msg) { 808c2ecf20Sopenharmony_ci adxl_component_count = 0; 818c2ecf20Sopenharmony_ci kfree(adxl_values); 828c2ecf20Sopenharmony_ci return -ENOMEM; 838c2ecf20Sopenharmony_ci } 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci return 0; 868c2ecf20Sopenharmony_cierr: 878c2ecf20Sopenharmony_ci skx_printk(KERN_ERR, "'%s' is not matched from DSM parameters: ", 888c2ecf20Sopenharmony_ci component_names[i]); 898c2ecf20Sopenharmony_ci for (j = 0; names[j]; j++) 908c2ecf20Sopenharmony_ci skx_printk(KERN_CONT, "%s ", names[j]); 918c2ecf20Sopenharmony_ci skx_printk(KERN_CONT, "\n"); 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci return -ENODEV; 948c2ecf20Sopenharmony_ci} 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_civoid __exit skx_adxl_put(void) 978c2ecf20Sopenharmony_ci{ 988c2ecf20Sopenharmony_ci kfree(adxl_values); 998c2ecf20Sopenharmony_ci kfree(adxl_msg); 1008c2ecf20Sopenharmony_ci} 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_cistatic bool skx_adxl_decode(struct decoded_addr *res) 1038c2ecf20Sopenharmony_ci{ 1048c2ecf20Sopenharmony_ci struct skx_dev *d; 1058c2ecf20Sopenharmony_ci int i, len = 0; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci if (res->addr >= skx_tohm || (res->addr >= skx_tolm && 1088c2ecf20Sopenharmony_ci res->addr < BIT_ULL(32))) { 1098c2ecf20Sopenharmony_ci edac_dbg(0, "Address 0x%llx out of range\n", res->addr); 1108c2ecf20Sopenharmony_ci return false; 1118c2ecf20Sopenharmony_ci } 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci if (adxl_decode(res->addr, adxl_values)) { 1148c2ecf20Sopenharmony_ci edac_dbg(0, "Failed to decode 0x%llx\n", res->addr); 1158c2ecf20Sopenharmony_ci return false; 1168c2ecf20Sopenharmony_ci } 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci res->socket = (int)adxl_values[component_indices[INDEX_SOCKET]]; 1198c2ecf20Sopenharmony_ci res->imc = (int)adxl_values[component_indices[INDEX_MEMCTRL]]; 1208c2ecf20Sopenharmony_ci res->channel = (int)adxl_values[component_indices[INDEX_CHANNEL]]; 1218c2ecf20Sopenharmony_ci res->dimm = (int)adxl_values[component_indices[INDEX_DIMM]]; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci if (res->imc > NUM_IMC - 1) { 1248c2ecf20Sopenharmony_ci skx_printk(KERN_ERR, "Bad imc %d\n", res->imc); 1258c2ecf20Sopenharmony_ci return false; 1268c2ecf20Sopenharmony_ci } 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci list_for_each_entry(d, &dev_edac_list, list) { 1298c2ecf20Sopenharmony_ci if (d->imc[0].src_id == res->socket) { 1308c2ecf20Sopenharmony_ci res->dev = d; 1318c2ecf20Sopenharmony_ci break; 1328c2ecf20Sopenharmony_ci } 1338c2ecf20Sopenharmony_ci } 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci if (!res->dev) { 1368c2ecf20Sopenharmony_ci skx_printk(KERN_ERR, "No device for src_id %d imc %d\n", 1378c2ecf20Sopenharmony_ci res->socket, res->imc); 1388c2ecf20Sopenharmony_ci return false; 1398c2ecf20Sopenharmony_ci } 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci for (i = 0; i < adxl_component_count; i++) { 1428c2ecf20Sopenharmony_ci if (adxl_values[i] == ~0x0ull) 1438c2ecf20Sopenharmony_ci continue; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci len += snprintf(adxl_msg + len, MSG_SIZE - len, " %s:0x%llx", 1468c2ecf20Sopenharmony_ci adxl_component_names[i], adxl_values[i]); 1478c2ecf20Sopenharmony_ci if (MSG_SIZE - len <= 0) 1488c2ecf20Sopenharmony_ci break; 1498c2ecf20Sopenharmony_ci } 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci return true; 1528c2ecf20Sopenharmony_ci} 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_civoid skx_set_decode(skx_decode_f decode, skx_show_retry_log_f show_retry_log) 1558c2ecf20Sopenharmony_ci{ 1568c2ecf20Sopenharmony_ci skx_decode = decode; 1578c2ecf20Sopenharmony_ci skx_show_retry_rd_err_log = show_retry_log; 1588c2ecf20Sopenharmony_ci} 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ciint skx_get_src_id(struct skx_dev *d, int off, u8 *id) 1618c2ecf20Sopenharmony_ci{ 1628c2ecf20Sopenharmony_ci u32 reg; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci if (pci_read_config_dword(d->util_all, off, ®)) { 1658c2ecf20Sopenharmony_ci skx_printk(KERN_ERR, "Failed to read src id\n"); 1668c2ecf20Sopenharmony_ci return -ENODEV; 1678c2ecf20Sopenharmony_ci } 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci *id = GET_BITFIELD(reg, 12, 14); 1708c2ecf20Sopenharmony_ci return 0; 1718c2ecf20Sopenharmony_ci} 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ciint skx_get_node_id(struct skx_dev *d, u8 *id) 1748c2ecf20Sopenharmony_ci{ 1758c2ecf20Sopenharmony_ci u32 reg; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci if (pci_read_config_dword(d->util_all, 0xf4, ®)) { 1788c2ecf20Sopenharmony_ci skx_printk(KERN_ERR, "Failed to read node id\n"); 1798c2ecf20Sopenharmony_ci return -ENODEV; 1808c2ecf20Sopenharmony_ci } 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci *id = GET_BITFIELD(reg, 0, 2); 1838c2ecf20Sopenharmony_ci return 0; 1848c2ecf20Sopenharmony_ci} 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_cistatic int get_width(u32 mtr) 1878c2ecf20Sopenharmony_ci{ 1888c2ecf20Sopenharmony_ci switch (GET_BITFIELD(mtr, 8, 9)) { 1898c2ecf20Sopenharmony_ci case 0: 1908c2ecf20Sopenharmony_ci return DEV_X4; 1918c2ecf20Sopenharmony_ci case 1: 1928c2ecf20Sopenharmony_ci return DEV_X8; 1938c2ecf20Sopenharmony_ci case 2: 1948c2ecf20Sopenharmony_ci return DEV_X16; 1958c2ecf20Sopenharmony_ci } 1968c2ecf20Sopenharmony_ci return DEV_UNKNOWN; 1978c2ecf20Sopenharmony_ci} 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci/* 2008c2ecf20Sopenharmony_ci * We use the per-socket device @cfg->did to count how many sockets are present, 2018c2ecf20Sopenharmony_ci * and to detemine which PCI buses are associated with each socket. Allocate 2028c2ecf20Sopenharmony_ci * and build the full list of all the skx_dev structures that we need here. 2038c2ecf20Sopenharmony_ci */ 2048c2ecf20Sopenharmony_ciint skx_get_all_bus_mappings(struct res_config *cfg, struct list_head **list) 2058c2ecf20Sopenharmony_ci{ 2068c2ecf20Sopenharmony_ci struct pci_dev *pdev, *prev; 2078c2ecf20Sopenharmony_ci struct skx_dev *d; 2088c2ecf20Sopenharmony_ci u32 reg; 2098c2ecf20Sopenharmony_ci int ndev = 0; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci prev = NULL; 2128c2ecf20Sopenharmony_ci for (;;) { 2138c2ecf20Sopenharmony_ci pdev = pci_get_device(PCI_VENDOR_ID_INTEL, cfg->decs_did, prev); 2148c2ecf20Sopenharmony_ci if (!pdev) 2158c2ecf20Sopenharmony_ci break; 2168c2ecf20Sopenharmony_ci ndev++; 2178c2ecf20Sopenharmony_ci d = kzalloc(sizeof(*d), GFP_KERNEL); 2188c2ecf20Sopenharmony_ci if (!d) { 2198c2ecf20Sopenharmony_ci pci_dev_put(pdev); 2208c2ecf20Sopenharmony_ci return -ENOMEM; 2218c2ecf20Sopenharmony_ci } 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci if (pci_read_config_dword(pdev, cfg->busno_cfg_offset, ®)) { 2248c2ecf20Sopenharmony_ci kfree(d); 2258c2ecf20Sopenharmony_ci pci_dev_put(pdev); 2268c2ecf20Sopenharmony_ci skx_printk(KERN_ERR, "Failed to read bus idx\n"); 2278c2ecf20Sopenharmony_ci return -ENODEV; 2288c2ecf20Sopenharmony_ci } 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci d->bus[0] = GET_BITFIELD(reg, 0, 7); 2318c2ecf20Sopenharmony_ci d->bus[1] = GET_BITFIELD(reg, 8, 15); 2328c2ecf20Sopenharmony_ci if (cfg->type == SKX) { 2338c2ecf20Sopenharmony_ci d->seg = pci_domain_nr(pdev->bus); 2348c2ecf20Sopenharmony_ci d->bus[2] = GET_BITFIELD(reg, 16, 23); 2358c2ecf20Sopenharmony_ci d->bus[3] = GET_BITFIELD(reg, 24, 31); 2368c2ecf20Sopenharmony_ci } else { 2378c2ecf20Sopenharmony_ci d->seg = GET_BITFIELD(reg, 16, 23); 2388c2ecf20Sopenharmony_ci } 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci edac_dbg(2, "busses: 0x%x, 0x%x, 0x%x, 0x%x\n", 2418c2ecf20Sopenharmony_ci d->bus[0], d->bus[1], d->bus[2], d->bus[3]); 2428c2ecf20Sopenharmony_ci list_add_tail(&d->list, &dev_edac_list); 2438c2ecf20Sopenharmony_ci prev = pdev; 2448c2ecf20Sopenharmony_ci } 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci if (list) 2478c2ecf20Sopenharmony_ci *list = &dev_edac_list; 2488c2ecf20Sopenharmony_ci return ndev; 2498c2ecf20Sopenharmony_ci} 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ciint skx_get_hi_lo(unsigned int did, int off[], u64 *tolm, u64 *tohm) 2528c2ecf20Sopenharmony_ci{ 2538c2ecf20Sopenharmony_ci struct pci_dev *pdev; 2548c2ecf20Sopenharmony_ci u32 reg; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci pdev = pci_get_device(PCI_VENDOR_ID_INTEL, did, NULL); 2578c2ecf20Sopenharmony_ci if (!pdev) { 2588c2ecf20Sopenharmony_ci edac_dbg(2, "Can't get tolm/tohm\n"); 2598c2ecf20Sopenharmony_ci return -ENODEV; 2608c2ecf20Sopenharmony_ci } 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci if (pci_read_config_dword(pdev, off[0], ®)) { 2638c2ecf20Sopenharmony_ci skx_printk(KERN_ERR, "Failed to read tolm\n"); 2648c2ecf20Sopenharmony_ci goto fail; 2658c2ecf20Sopenharmony_ci } 2668c2ecf20Sopenharmony_ci skx_tolm = reg; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci if (pci_read_config_dword(pdev, off[1], ®)) { 2698c2ecf20Sopenharmony_ci skx_printk(KERN_ERR, "Failed to read lower tohm\n"); 2708c2ecf20Sopenharmony_ci goto fail; 2718c2ecf20Sopenharmony_ci } 2728c2ecf20Sopenharmony_ci skx_tohm = reg; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci if (pci_read_config_dword(pdev, off[2], ®)) { 2758c2ecf20Sopenharmony_ci skx_printk(KERN_ERR, "Failed to read upper tohm\n"); 2768c2ecf20Sopenharmony_ci goto fail; 2778c2ecf20Sopenharmony_ci } 2788c2ecf20Sopenharmony_ci skx_tohm |= (u64)reg << 32; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci pci_dev_put(pdev); 2818c2ecf20Sopenharmony_ci *tolm = skx_tolm; 2828c2ecf20Sopenharmony_ci *tohm = skx_tohm; 2838c2ecf20Sopenharmony_ci edac_dbg(2, "tolm = 0x%llx tohm = 0x%llx\n", skx_tolm, skx_tohm); 2848c2ecf20Sopenharmony_ci return 0; 2858c2ecf20Sopenharmony_cifail: 2868c2ecf20Sopenharmony_ci pci_dev_put(pdev); 2878c2ecf20Sopenharmony_ci return -ENODEV; 2888c2ecf20Sopenharmony_ci} 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_cistatic int skx_get_dimm_attr(u32 reg, int lobit, int hibit, int add, 2918c2ecf20Sopenharmony_ci int minval, int maxval, const char *name) 2928c2ecf20Sopenharmony_ci{ 2938c2ecf20Sopenharmony_ci u32 val = GET_BITFIELD(reg, lobit, hibit); 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci if (val < minval || val > maxval) { 2968c2ecf20Sopenharmony_ci edac_dbg(2, "bad %s = %d (raw=0x%x)\n", name, val, reg); 2978c2ecf20Sopenharmony_ci return -EINVAL; 2988c2ecf20Sopenharmony_ci } 2998c2ecf20Sopenharmony_ci return val + add; 3008c2ecf20Sopenharmony_ci} 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci#define numrank(reg) skx_get_dimm_attr(reg, 12, 13, 0, 0, 2, "ranks") 3038c2ecf20Sopenharmony_ci#define numrow(reg) skx_get_dimm_attr(reg, 2, 4, 12, 1, 6, "rows") 3048c2ecf20Sopenharmony_ci#define numcol(reg) skx_get_dimm_attr(reg, 0, 1, 10, 0, 2, "cols") 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ciint skx_get_dimm_info(u32 mtr, u32 mcmtr, u32 amap, struct dimm_info *dimm, 3078c2ecf20Sopenharmony_ci struct skx_imc *imc, int chan, int dimmno) 3088c2ecf20Sopenharmony_ci{ 3098c2ecf20Sopenharmony_ci int banks = 16, ranks, rows, cols, npages; 3108c2ecf20Sopenharmony_ci u64 size; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci ranks = numrank(mtr); 3138c2ecf20Sopenharmony_ci rows = numrow(mtr); 3148c2ecf20Sopenharmony_ci cols = numcol(mtr); 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci /* 3178c2ecf20Sopenharmony_ci * Compute size in 8-byte (2^3) words, then shift to MiB (2^20) 3188c2ecf20Sopenharmony_ci */ 3198c2ecf20Sopenharmony_ci size = ((1ull << (rows + cols + ranks)) * banks) >> (20 - 3); 3208c2ecf20Sopenharmony_ci npages = MiB_TO_PAGES(size); 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci edac_dbg(0, "mc#%d: channel %d, dimm %d, %lld MiB (%d pages) bank: %d, rank: %d, row: 0x%x, col: 0x%x\n", 3238c2ecf20Sopenharmony_ci imc->mc, chan, dimmno, size, npages, 3248c2ecf20Sopenharmony_ci banks, 1 << ranks, rows, cols); 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci imc->chan[chan].dimms[dimmno].close_pg = GET_BITFIELD(mcmtr, 0, 0); 3278c2ecf20Sopenharmony_ci imc->chan[chan].dimms[dimmno].bank_xor_enable = GET_BITFIELD(mcmtr, 9, 9); 3288c2ecf20Sopenharmony_ci imc->chan[chan].dimms[dimmno].fine_grain_bank = GET_BITFIELD(amap, 0, 0); 3298c2ecf20Sopenharmony_ci imc->chan[chan].dimms[dimmno].rowbits = rows; 3308c2ecf20Sopenharmony_ci imc->chan[chan].dimms[dimmno].colbits = cols; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci dimm->nr_pages = npages; 3338c2ecf20Sopenharmony_ci dimm->grain = 32; 3348c2ecf20Sopenharmony_ci dimm->dtype = get_width(mtr); 3358c2ecf20Sopenharmony_ci dimm->mtype = MEM_DDR4; 3368c2ecf20Sopenharmony_ci dimm->edac_mode = EDAC_SECDED; /* likely better than this */ 3378c2ecf20Sopenharmony_ci snprintf(dimm->label, sizeof(dimm->label), "CPU_SrcID#%u_MC#%u_Chan#%u_DIMM#%u", 3388c2ecf20Sopenharmony_ci imc->src_id, imc->lmc, chan, dimmno); 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci return 1; 3418c2ecf20Sopenharmony_ci} 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ciint skx_get_nvdimm_info(struct dimm_info *dimm, struct skx_imc *imc, 3448c2ecf20Sopenharmony_ci int chan, int dimmno, const char *mod_str) 3458c2ecf20Sopenharmony_ci{ 3468c2ecf20Sopenharmony_ci int smbios_handle; 3478c2ecf20Sopenharmony_ci u32 dev_handle; 3488c2ecf20Sopenharmony_ci u16 flags; 3498c2ecf20Sopenharmony_ci u64 size = 0; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci dev_handle = ACPI_NFIT_BUILD_DEVICE_HANDLE(dimmno, chan, imc->lmc, 3528c2ecf20Sopenharmony_ci imc->src_id, 0); 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci smbios_handle = nfit_get_smbios_id(dev_handle, &flags); 3558c2ecf20Sopenharmony_ci if (smbios_handle == -EOPNOTSUPP) { 3568c2ecf20Sopenharmony_ci pr_warn_once("%s: Can't find size of NVDIMM. Try enabling CONFIG_ACPI_NFIT\n", mod_str); 3578c2ecf20Sopenharmony_ci goto unknown_size; 3588c2ecf20Sopenharmony_ci } 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci if (smbios_handle < 0) { 3618c2ecf20Sopenharmony_ci skx_printk(KERN_ERR, "Can't find handle for NVDIMM ADR=0x%x\n", dev_handle); 3628c2ecf20Sopenharmony_ci goto unknown_size; 3638c2ecf20Sopenharmony_ci } 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci if (flags & ACPI_NFIT_MEM_MAP_FAILED) { 3668c2ecf20Sopenharmony_ci skx_printk(KERN_ERR, "NVDIMM ADR=0x%x is not mapped\n", dev_handle); 3678c2ecf20Sopenharmony_ci goto unknown_size; 3688c2ecf20Sopenharmony_ci } 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci size = dmi_memdev_size(smbios_handle); 3718c2ecf20Sopenharmony_ci if (size == ~0ull) 3728c2ecf20Sopenharmony_ci skx_printk(KERN_ERR, "Can't find size for NVDIMM ADR=0x%x/SMBIOS=0x%x\n", 3738c2ecf20Sopenharmony_ci dev_handle, smbios_handle); 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ciunknown_size: 3768c2ecf20Sopenharmony_ci dimm->nr_pages = size >> PAGE_SHIFT; 3778c2ecf20Sopenharmony_ci dimm->grain = 32; 3788c2ecf20Sopenharmony_ci dimm->dtype = DEV_UNKNOWN; 3798c2ecf20Sopenharmony_ci dimm->mtype = MEM_NVDIMM; 3808c2ecf20Sopenharmony_ci dimm->edac_mode = EDAC_SECDED; /* likely better than this */ 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci edac_dbg(0, "mc#%d: channel %d, dimm %d, %llu MiB (%u pages)\n", 3838c2ecf20Sopenharmony_ci imc->mc, chan, dimmno, size >> 20, dimm->nr_pages); 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci snprintf(dimm->label, sizeof(dimm->label), "CPU_SrcID#%u_MC#%u_Chan#%u_DIMM#%u", 3868c2ecf20Sopenharmony_ci imc->src_id, imc->lmc, chan, dimmno); 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci return (size == 0 || size == ~0ull) ? 0 : 1; 3898c2ecf20Sopenharmony_ci} 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ciint skx_register_mci(struct skx_imc *imc, struct pci_dev *pdev, 3928c2ecf20Sopenharmony_ci const char *ctl_name, const char *mod_str, 3938c2ecf20Sopenharmony_ci get_dimm_config_f get_dimm_config) 3948c2ecf20Sopenharmony_ci{ 3958c2ecf20Sopenharmony_ci struct mem_ctl_info *mci; 3968c2ecf20Sopenharmony_ci struct edac_mc_layer layers[2]; 3978c2ecf20Sopenharmony_ci struct skx_pvt *pvt; 3988c2ecf20Sopenharmony_ci int rc; 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci /* Allocate a new MC control structure */ 4018c2ecf20Sopenharmony_ci layers[0].type = EDAC_MC_LAYER_CHANNEL; 4028c2ecf20Sopenharmony_ci layers[0].size = NUM_CHANNELS; 4038c2ecf20Sopenharmony_ci layers[0].is_virt_csrow = false; 4048c2ecf20Sopenharmony_ci layers[1].type = EDAC_MC_LAYER_SLOT; 4058c2ecf20Sopenharmony_ci layers[1].size = NUM_DIMMS; 4068c2ecf20Sopenharmony_ci layers[1].is_virt_csrow = true; 4078c2ecf20Sopenharmony_ci mci = edac_mc_alloc(imc->mc, ARRAY_SIZE(layers), layers, 4088c2ecf20Sopenharmony_ci sizeof(struct skx_pvt)); 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci if (unlikely(!mci)) 4118c2ecf20Sopenharmony_ci return -ENOMEM; 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci edac_dbg(0, "MC#%d: mci = %p\n", imc->mc, mci); 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci /* Associate skx_dev and mci for future usage */ 4168c2ecf20Sopenharmony_ci imc->mci = mci; 4178c2ecf20Sopenharmony_ci pvt = mci->pvt_info; 4188c2ecf20Sopenharmony_ci pvt->imc = imc; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci mci->ctl_name = kasprintf(GFP_KERNEL, "%s#%d IMC#%d", ctl_name, 4218c2ecf20Sopenharmony_ci imc->node_id, imc->lmc); 4228c2ecf20Sopenharmony_ci if (!mci->ctl_name) { 4238c2ecf20Sopenharmony_ci rc = -ENOMEM; 4248c2ecf20Sopenharmony_ci goto fail0; 4258c2ecf20Sopenharmony_ci } 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci mci->mtype_cap = MEM_FLAG_DDR4 | MEM_FLAG_NVDIMM; 4288c2ecf20Sopenharmony_ci mci->edac_ctl_cap = EDAC_FLAG_NONE; 4298c2ecf20Sopenharmony_ci mci->edac_cap = EDAC_FLAG_NONE; 4308c2ecf20Sopenharmony_ci mci->mod_name = mod_str; 4318c2ecf20Sopenharmony_ci mci->dev_name = pci_name(pdev); 4328c2ecf20Sopenharmony_ci mci->ctl_page_to_phys = NULL; 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci rc = get_dimm_config(mci); 4358c2ecf20Sopenharmony_ci if (rc < 0) 4368c2ecf20Sopenharmony_ci goto fail; 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci /* Record ptr to the generic device */ 4398c2ecf20Sopenharmony_ci mci->pdev = &pdev->dev; 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci /* Add this new MC control structure to EDAC's list of MCs */ 4428c2ecf20Sopenharmony_ci if (unlikely(edac_mc_add_mc(mci))) { 4438c2ecf20Sopenharmony_ci edac_dbg(0, "MC: failed edac_mc_add_mc()\n"); 4448c2ecf20Sopenharmony_ci rc = -EINVAL; 4458c2ecf20Sopenharmony_ci goto fail; 4468c2ecf20Sopenharmony_ci } 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci return 0; 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_cifail: 4518c2ecf20Sopenharmony_ci kfree(mci->ctl_name); 4528c2ecf20Sopenharmony_cifail0: 4538c2ecf20Sopenharmony_ci edac_mc_free(mci); 4548c2ecf20Sopenharmony_ci imc->mci = NULL; 4558c2ecf20Sopenharmony_ci return rc; 4568c2ecf20Sopenharmony_ci} 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_cistatic void skx_unregister_mci(struct skx_imc *imc) 4598c2ecf20Sopenharmony_ci{ 4608c2ecf20Sopenharmony_ci struct mem_ctl_info *mci = imc->mci; 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci if (!mci) 4638c2ecf20Sopenharmony_ci return; 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci edac_dbg(0, "MC%d: mci = %p\n", imc->mc, mci); 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci /* Remove MC sysfs nodes */ 4688c2ecf20Sopenharmony_ci edac_mc_del_mc(mci->pdev); 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci edac_dbg(1, "%s: free mci struct\n", mci->ctl_name); 4718c2ecf20Sopenharmony_ci kfree(mci->ctl_name); 4728c2ecf20Sopenharmony_ci edac_mc_free(mci); 4738c2ecf20Sopenharmony_ci} 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_cistatic void skx_mce_output_error(struct mem_ctl_info *mci, 4768c2ecf20Sopenharmony_ci const struct mce *m, 4778c2ecf20Sopenharmony_ci struct decoded_addr *res) 4788c2ecf20Sopenharmony_ci{ 4798c2ecf20Sopenharmony_ci enum hw_event_mc_err_type tp_event; 4808c2ecf20Sopenharmony_ci char *optype; 4818c2ecf20Sopenharmony_ci bool ripv = GET_BITFIELD(m->mcgstatus, 0, 0); 4828c2ecf20Sopenharmony_ci bool overflow = GET_BITFIELD(m->status, 62, 62); 4838c2ecf20Sopenharmony_ci bool uncorrected_error = GET_BITFIELD(m->status, 61, 61); 4848c2ecf20Sopenharmony_ci bool recoverable; 4858c2ecf20Sopenharmony_ci int len; 4868c2ecf20Sopenharmony_ci u32 core_err_cnt = GET_BITFIELD(m->status, 38, 52); 4878c2ecf20Sopenharmony_ci u32 mscod = GET_BITFIELD(m->status, 16, 31); 4888c2ecf20Sopenharmony_ci u32 errcode = GET_BITFIELD(m->status, 0, 15); 4898c2ecf20Sopenharmony_ci u32 optypenum = GET_BITFIELD(m->status, 4, 6); 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci recoverable = GET_BITFIELD(m->status, 56, 56); 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci if (uncorrected_error) { 4948c2ecf20Sopenharmony_ci core_err_cnt = 1; 4958c2ecf20Sopenharmony_ci if (ripv) { 4968c2ecf20Sopenharmony_ci tp_event = HW_EVENT_ERR_UNCORRECTED; 4978c2ecf20Sopenharmony_ci } else { 4988c2ecf20Sopenharmony_ci tp_event = HW_EVENT_ERR_FATAL; 4998c2ecf20Sopenharmony_ci } 5008c2ecf20Sopenharmony_ci } else { 5018c2ecf20Sopenharmony_ci tp_event = HW_EVENT_ERR_CORRECTED; 5028c2ecf20Sopenharmony_ci } 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci /* 5058c2ecf20Sopenharmony_ci * According to Intel Architecture spec vol 3B, 5068c2ecf20Sopenharmony_ci * Table 15-10 "IA32_MCi_Status [15:0] Compound Error Code Encoding" 5078c2ecf20Sopenharmony_ci * memory errors should fit one of these masks: 5088c2ecf20Sopenharmony_ci * 000f 0000 1mmm cccc (binary) 5098c2ecf20Sopenharmony_ci * 000f 0010 1mmm cccc (binary) [RAM used as cache] 5108c2ecf20Sopenharmony_ci * where: 5118c2ecf20Sopenharmony_ci * f = Correction Report Filtering Bit. If 1, subsequent errors 5128c2ecf20Sopenharmony_ci * won't be shown 5138c2ecf20Sopenharmony_ci * mmm = error type 5148c2ecf20Sopenharmony_ci * cccc = channel 5158c2ecf20Sopenharmony_ci * If the mask doesn't match, report an error to the parsing logic 5168c2ecf20Sopenharmony_ci */ 5178c2ecf20Sopenharmony_ci if (!((errcode & 0xef80) == 0x80 || (errcode & 0xef80) == 0x280)) { 5188c2ecf20Sopenharmony_ci optype = "Can't parse: it is not a mem"; 5198c2ecf20Sopenharmony_ci } else { 5208c2ecf20Sopenharmony_ci switch (optypenum) { 5218c2ecf20Sopenharmony_ci case 0: 5228c2ecf20Sopenharmony_ci optype = "generic undef request error"; 5238c2ecf20Sopenharmony_ci break; 5248c2ecf20Sopenharmony_ci case 1: 5258c2ecf20Sopenharmony_ci optype = "memory read error"; 5268c2ecf20Sopenharmony_ci break; 5278c2ecf20Sopenharmony_ci case 2: 5288c2ecf20Sopenharmony_ci optype = "memory write error"; 5298c2ecf20Sopenharmony_ci break; 5308c2ecf20Sopenharmony_ci case 3: 5318c2ecf20Sopenharmony_ci optype = "addr/cmd error"; 5328c2ecf20Sopenharmony_ci break; 5338c2ecf20Sopenharmony_ci case 4: 5348c2ecf20Sopenharmony_ci optype = "memory scrubbing error"; 5358c2ecf20Sopenharmony_ci break; 5368c2ecf20Sopenharmony_ci default: 5378c2ecf20Sopenharmony_ci optype = "reserved"; 5388c2ecf20Sopenharmony_ci break; 5398c2ecf20Sopenharmony_ci } 5408c2ecf20Sopenharmony_ci } 5418c2ecf20Sopenharmony_ci if (adxl_component_count) { 5428c2ecf20Sopenharmony_ci len = snprintf(skx_msg, MSG_SIZE, "%s%s err_code:0x%04x:0x%04x %s", 5438c2ecf20Sopenharmony_ci overflow ? " OVERFLOW" : "", 5448c2ecf20Sopenharmony_ci (uncorrected_error && recoverable) ? " recoverable" : "", 5458c2ecf20Sopenharmony_ci mscod, errcode, adxl_msg); 5468c2ecf20Sopenharmony_ci } else { 5478c2ecf20Sopenharmony_ci len = snprintf(skx_msg, MSG_SIZE, 5488c2ecf20Sopenharmony_ci "%s%s err_code:0x%04x:0x%04x socket:%d imc:%d rank:%d bg:%d ba:%d row:0x%x col:0x%x", 5498c2ecf20Sopenharmony_ci overflow ? " OVERFLOW" : "", 5508c2ecf20Sopenharmony_ci (uncorrected_error && recoverable) ? " recoverable" : "", 5518c2ecf20Sopenharmony_ci mscod, errcode, 5528c2ecf20Sopenharmony_ci res->socket, res->imc, res->rank, 5538c2ecf20Sopenharmony_ci res->bank_group, res->bank_address, res->row, res->column); 5548c2ecf20Sopenharmony_ci } 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci if (skx_show_retry_rd_err_log) 5578c2ecf20Sopenharmony_ci skx_show_retry_rd_err_log(res, skx_msg + len, MSG_SIZE - len); 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci edac_dbg(0, "%s\n", skx_msg); 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci /* Call the helper to output message */ 5628c2ecf20Sopenharmony_ci edac_mc_handle_error(tp_event, mci, core_err_cnt, 5638c2ecf20Sopenharmony_ci m->addr >> PAGE_SHIFT, m->addr & ~PAGE_MASK, 0, 5648c2ecf20Sopenharmony_ci res->channel, res->dimm, -1, 5658c2ecf20Sopenharmony_ci optype, skx_msg); 5668c2ecf20Sopenharmony_ci} 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ciint skx_mce_check_error(struct notifier_block *nb, unsigned long val, 5698c2ecf20Sopenharmony_ci void *data) 5708c2ecf20Sopenharmony_ci{ 5718c2ecf20Sopenharmony_ci struct mce *mce = (struct mce *)data; 5728c2ecf20Sopenharmony_ci struct decoded_addr res; 5738c2ecf20Sopenharmony_ci struct mem_ctl_info *mci; 5748c2ecf20Sopenharmony_ci char *type; 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci if (mce->kflags & MCE_HANDLED_CEC) 5778c2ecf20Sopenharmony_ci return NOTIFY_DONE; 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci /* ignore unless this is memory related with an address */ 5808c2ecf20Sopenharmony_ci if ((mce->status & 0xefff) >> 7 != 1 || !(mce->status & MCI_STATUS_ADDRV)) 5818c2ecf20Sopenharmony_ci return NOTIFY_DONE; 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci memset(&res, 0, sizeof(res)); 5848c2ecf20Sopenharmony_ci res.addr = mce->addr; 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci if (adxl_component_count) { 5878c2ecf20Sopenharmony_ci if (!skx_adxl_decode(&res)) 5888c2ecf20Sopenharmony_ci return NOTIFY_DONE; 5898c2ecf20Sopenharmony_ci } else if (!skx_decode || !skx_decode(&res)) { 5908c2ecf20Sopenharmony_ci return NOTIFY_DONE; 5918c2ecf20Sopenharmony_ci } 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci mci = res.dev->imc[res.imc].mci; 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci if (!mci) 5968c2ecf20Sopenharmony_ci return NOTIFY_DONE; 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci if (mce->mcgstatus & MCG_STATUS_MCIP) 5998c2ecf20Sopenharmony_ci type = "Exception"; 6008c2ecf20Sopenharmony_ci else 6018c2ecf20Sopenharmony_ci type = "Event"; 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci skx_mc_printk(mci, KERN_DEBUG, "HANDLING MCE MEMORY ERROR\n"); 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci skx_mc_printk(mci, KERN_DEBUG, "CPU %d: Machine Check %s: 0x%llx " 6068c2ecf20Sopenharmony_ci "Bank %d: 0x%llx\n", mce->extcpu, type, 6078c2ecf20Sopenharmony_ci mce->mcgstatus, mce->bank, mce->status); 6088c2ecf20Sopenharmony_ci skx_mc_printk(mci, KERN_DEBUG, "TSC 0x%llx ", mce->tsc); 6098c2ecf20Sopenharmony_ci skx_mc_printk(mci, KERN_DEBUG, "ADDR 0x%llx ", mce->addr); 6108c2ecf20Sopenharmony_ci skx_mc_printk(mci, KERN_DEBUG, "MISC 0x%llx ", mce->misc); 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci skx_mc_printk(mci, KERN_DEBUG, "PROCESSOR %u:0x%x TIME %llu SOCKET " 6138c2ecf20Sopenharmony_ci "%u APIC 0x%x\n", mce->cpuvendor, mce->cpuid, 6148c2ecf20Sopenharmony_ci mce->time, mce->socketid, mce->apicid); 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci skx_mce_output_error(mci, mce, &res); 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci mce->kflags |= MCE_HANDLED_EDAC; 6198c2ecf20Sopenharmony_ci return NOTIFY_DONE; 6208c2ecf20Sopenharmony_ci} 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_civoid skx_remove(void) 6238c2ecf20Sopenharmony_ci{ 6248c2ecf20Sopenharmony_ci int i, j; 6258c2ecf20Sopenharmony_ci struct skx_dev *d, *tmp; 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci edac_dbg(0, "\n"); 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci list_for_each_entry_safe(d, tmp, &dev_edac_list, list) { 6308c2ecf20Sopenharmony_ci list_del(&d->list); 6318c2ecf20Sopenharmony_ci for (i = 0; i < NUM_IMC; i++) { 6328c2ecf20Sopenharmony_ci if (d->imc[i].mci) 6338c2ecf20Sopenharmony_ci skx_unregister_mci(&d->imc[i]); 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci if (d->imc[i].mdev) 6368c2ecf20Sopenharmony_ci pci_dev_put(d->imc[i].mdev); 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci if (d->imc[i].mbase) 6398c2ecf20Sopenharmony_ci iounmap(d->imc[i].mbase); 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci for (j = 0; j < NUM_CHANNELS; j++) { 6428c2ecf20Sopenharmony_ci if (d->imc[i].chan[j].cdev) 6438c2ecf20Sopenharmony_ci pci_dev_put(d->imc[i].chan[j].cdev); 6448c2ecf20Sopenharmony_ci } 6458c2ecf20Sopenharmony_ci } 6468c2ecf20Sopenharmony_ci if (d->util_all) 6478c2ecf20Sopenharmony_ci pci_dev_put(d->util_all); 6488c2ecf20Sopenharmony_ci if (d->sad_all) 6498c2ecf20Sopenharmony_ci pci_dev_put(d->sad_all); 6508c2ecf20Sopenharmony_ci if (d->uracu) 6518c2ecf20Sopenharmony_ci pci_dev_put(d->uracu); 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci kfree(d); 6548c2ecf20Sopenharmony_ci } 6558c2ecf20Sopenharmony_ci} 656