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, &reg)) {
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, &reg)) {
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, &reg)) {
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], &reg)) {
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], &reg)) {
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], &reg)) {
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