xref: /linux/drivers/edac/imh_base.c (revision 323bbfcf1ef8836d0d2ad9e2c1f1c684f0e3b5b3)
19fc67b11SQiuxu Zhuo // SPDX-License-Identifier: GPL-2.0
29fc67b11SQiuxu Zhuo /*
39fc67b11SQiuxu Zhuo  * Driver for Intel(R) servers with Integrated Memory/IO Hub-based memory controller.
49fc67b11SQiuxu Zhuo  * Copyright (c) 2025, Intel Corporation.
59fc67b11SQiuxu Zhuo  */
69fc67b11SQiuxu Zhuo 
79fc67b11SQiuxu Zhuo #include <linux/kernel.h>
89fc67b11SQiuxu Zhuo #include <linux/io.h>
99fc67b11SQiuxu Zhuo #include <asm/cpu_device_id.h>
109fc67b11SQiuxu Zhuo #include <asm/intel-family.h>
119fc67b11SQiuxu Zhuo #include <asm/mce.h>
129fc67b11SQiuxu Zhuo #include <asm/cpu.h>
139fc67b11SQiuxu Zhuo #include "edac_module.h"
149fc67b11SQiuxu Zhuo #include "skx_common.h"
159fc67b11SQiuxu Zhuo 
169fc67b11SQiuxu Zhuo #define IMH_REVISION	"v0.0.1"
179fc67b11SQiuxu Zhuo #define EDAC_MOD_STR	"imh_edac"
189fc67b11SQiuxu Zhuo 
199fc67b11SQiuxu Zhuo /* Debug macros */
209fc67b11SQiuxu Zhuo #define imh_printk(level, fmt, arg...)	\
219fc67b11SQiuxu Zhuo 	edac_printk(level, "imh", fmt, ##arg)
229fc67b11SQiuxu Zhuo 
239fc67b11SQiuxu Zhuo /* Configuration Agent(Ubox) */
249fc67b11SQiuxu Zhuo #define MMIO_BASE_H(reg)		(((u64)GET_BITFIELD(reg, 0, 29)) << 23)
259fc67b11SQiuxu Zhuo #define SOCKET_ID(reg)			GET_BITFIELD(reg, 0, 3)
269fc67b11SQiuxu Zhuo 
279fc67b11SQiuxu Zhuo /* PUNIT */
289fc67b11SQiuxu Zhuo #define DDR_IMC_BITMAP(reg)		GET_BITFIELD(reg, 23, 30)
299fc67b11SQiuxu Zhuo 
309fc67b11SQiuxu Zhuo /* Memory Controller */
319fc67b11SQiuxu Zhuo #define ECC_ENABLED(reg)		GET_BITFIELD(reg, 2, 2)
329fc67b11SQiuxu Zhuo #define DIMM_POPULATED(reg)		GET_BITFIELD(reg, 15, 15)
339fc67b11SQiuxu Zhuo 
349fc67b11SQiuxu Zhuo /* System Cache Agent(SCA) */
359fc67b11SQiuxu Zhuo #define TOLM(reg)			(((u64)GET_BITFIELD(reg, 16, 31)) << 16)
369fc67b11SQiuxu Zhuo #define TOHM(reg)			(((u64)GET_BITFIELD(reg, 16, 51)) << 16)
379fc67b11SQiuxu Zhuo 
38f619613fSQiuxu Zhuo /* Home Agent (HA) */
39f619613fSQiuxu Zhuo #define NMCACHING(reg)			GET_BITFIELD(reg, 8, 8)
40f619613fSQiuxu Zhuo 
419fc67b11SQiuxu Zhuo /**
429fc67b11SQiuxu Zhuo  * struct local_reg - A register as described in the local package view.
439fc67b11SQiuxu Zhuo  *
449fc67b11SQiuxu Zhuo  * @pkg: (input)	The package where the register is located.
459fc67b11SQiuxu Zhuo  * @pbase: (input)	The IP MMIO base physical address in the local package view.
469fc67b11SQiuxu Zhuo  * @size: (input)	The IP MMIO size.
479fc67b11SQiuxu Zhuo  * @offset: (input)	The register offset from the IP MMIO base @pbase.
489fc67b11SQiuxu Zhuo  * @width: (input)	The register width in byte.
499fc67b11SQiuxu Zhuo  * @vbase: (internal)	The IP MMIO base virtual address.
509fc67b11SQiuxu Zhuo  * @val: (output)	The register value.
519fc67b11SQiuxu Zhuo  */
529fc67b11SQiuxu Zhuo struct local_reg {
539fc67b11SQiuxu Zhuo 	int pkg;
549fc67b11SQiuxu Zhuo 	u64 pbase;
559fc67b11SQiuxu Zhuo 	u32 size;
569fc67b11SQiuxu Zhuo 	u32 offset;
579fc67b11SQiuxu Zhuo 	u8  width;
589fc67b11SQiuxu Zhuo 	void __iomem *vbase;
599fc67b11SQiuxu Zhuo 	u64 val;
609fc67b11SQiuxu Zhuo };
619fc67b11SQiuxu Zhuo 
629fc67b11SQiuxu Zhuo #define DEFINE_LOCAL_REG(name, cfg, package, north, ip_name, ip_idx, reg_name)	\
639fc67b11SQiuxu Zhuo 	struct local_reg name = {						\
649fc67b11SQiuxu Zhuo 		.pkg	= package,						\
659fc67b11SQiuxu Zhuo 		.pbase	= (north ? (cfg)->mmio_base_l_north :			\
669fc67b11SQiuxu Zhuo 			  (cfg)->mmio_base_l_south) +				\
679fc67b11SQiuxu Zhuo 			  (cfg)->ip_name##_base +				\
689fc67b11SQiuxu Zhuo 			  (cfg)->ip_name##_size * (ip_idx),			\
699fc67b11SQiuxu Zhuo 		.size	= (cfg)->ip_name##_size,				\
709fc67b11SQiuxu Zhuo 		.offset	= (cfg)->ip_name##_reg_##reg_name##_offset,		\
719fc67b11SQiuxu Zhuo 		.width	= (cfg)->ip_name##_reg_##reg_name##_width,		\
729fc67b11SQiuxu Zhuo 	}
739fc67b11SQiuxu Zhuo 
readx(void __iomem * addr,u8 width)749fc67b11SQiuxu Zhuo static u64 readx(void __iomem *addr, u8 width)
759fc67b11SQiuxu Zhuo {
769fc67b11SQiuxu Zhuo 	switch (width) {
779fc67b11SQiuxu Zhuo 	case 1:
789fc67b11SQiuxu Zhuo 		return readb(addr);
799fc67b11SQiuxu Zhuo 	case 2:
809fc67b11SQiuxu Zhuo 		return readw(addr);
819fc67b11SQiuxu Zhuo 	case 4:
829fc67b11SQiuxu Zhuo 		return readl(addr);
839fc67b11SQiuxu Zhuo 	case 8:
849fc67b11SQiuxu Zhuo 		return readq(addr);
859fc67b11SQiuxu Zhuo 	default:
869fc67b11SQiuxu Zhuo 		imh_printk(KERN_ERR, "Invalid reg 0x%p width %d\n", addr, width);
879fc67b11SQiuxu Zhuo 		return 0;
889fc67b11SQiuxu Zhuo 	}
899fc67b11SQiuxu Zhuo }
909fc67b11SQiuxu Zhuo 
__read_local_reg(void * reg)919fc67b11SQiuxu Zhuo static void __read_local_reg(void *reg)
929fc67b11SQiuxu Zhuo {
939fc67b11SQiuxu Zhuo 	struct local_reg *r = (struct local_reg *)reg;
949fc67b11SQiuxu Zhuo 
959fc67b11SQiuxu Zhuo 	r->val = readx(r->vbase + r->offset, r->width);
969fc67b11SQiuxu Zhuo }
979fc67b11SQiuxu Zhuo 
989fc67b11SQiuxu Zhuo /* Read a local-view register. */
read_local_reg(struct local_reg * reg)999fc67b11SQiuxu Zhuo static bool read_local_reg(struct local_reg *reg)
1009fc67b11SQiuxu Zhuo {
1019fc67b11SQiuxu Zhuo 	int cpu;
1029fc67b11SQiuxu Zhuo 
1039fc67b11SQiuxu Zhuo 	/* Get the target CPU in the package @reg->pkg. */
1049fc67b11SQiuxu Zhuo 	for_each_online_cpu(cpu) {
1059fc67b11SQiuxu Zhuo 		if (reg->pkg == topology_physical_package_id(cpu))
1069fc67b11SQiuxu Zhuo 			break;
1079fc67b11SQiuxu Zhuo 	}
1089fc67b11SQiuxu Zhuo 
1099fc67b11SQiuxu Zhuo 	if (cpu >= nr_cpu_ids)
1109fc67b11SQiuxu Zhuo 		return false;
1119fc67b11SQiuxu Zhuo 
1129fc67b11SQiuxu Zhuo 	reg->vbase = ioremap(reg->pbase, reg->size);
1139fc67b11SQiuxu Zhuo 	if (!reg->vbase) {
1149fc67b11SQiuxu Zhuo 		imh_printk(KERN_ERR, "Failed to ioremap 0x%llx\n", reg->pbase);
1159fc67b11SQiuxu Zhuo 		return false;
1169fc67b11SQiuxu Zhuo 	}
1179fc67b11SQiuxu Zhuo 
1189fc67b11SQiuxu Zhuo 	/* Get the target CPU to read the register. */
1199fc67b11SQiuxu Zhuo 	smp_call_function_single(cpu, __read_local_reg, reg, 1);
1209fc67b11SQiuxu Zhuo 	iounmap(reg->vbase);
1219fc67b11SQiuxu Zhuo 
1229fc67b11SQiuxu Zhuo 	return true;
1239fc67b11SQiuxu Zhuo }
1249fc67b11SQiuxu Zhuo 
1259fc67b11SQiuxu Zhuo /* Get the bitmap of memory controller instances in package @pkg. */
get_imc_bitmap(struct res_config * cfg,int pkg,bool north)1269fc67b11SQiuxu Zhuo static u32 get_imc_bitmap(struct res_config *cfg, int pkg, bool north)
1279fc67b11SQiuxu Zhuo {
1289fc67b11SQiuxu Zhuo 	DEFINE_LOCAL_REG(reg, cfg, pkg, north, pcu, 0, capid3);
1299fc67b11SQiuxu Zhuo 
1309fc67b11SQiuxu Zhuo 	if (!read_local_reg(&reg))
1319fc67b11SQiuxu Zhuo 		return 0;
1329fc67b11SQiuxu Zhuo 
1339fc67b11SQiuxu Zhuo 	edac_dbg(2, "Pkg%d %s mc instances bitmap 0x%llx (reg 0x%llx)\n",
1349fc67b11SQiuxu Zhuo 		 pkg, north ? "north" : "south",
1359fc67b11SQiuxu Zhuo 		 DDR_IMC_BITMAP(reg.val), reg.val);
1369fc67b11SQiuxu Zhuo 
1379fc67b11SQiuxu Zhuo 	return DDR_IMC_BITMAP(reg.val);
1389fc67b11SQiuxu Zhuo }
1399fc67b11SQiuxu Zhuo 
imc_release(struct device * dev)1409fc67b11SQiuxu Zhuo static void imc_release(struct device *dev)
1419fc67b11SQiuxu Zhuo {
1429fc67b11SQiuxu Zhuo 	edac_dbg(2, "imc device %s released\n", dev_name(dev));
1439fc67b11SQiuxu Zhuo 	kfree(dev);
1449fc67b11SQiuxu Zhuo }
1459fc67b11SQiuxu Zhuo 
__get_ddr_munits(struct res_config * cfg,struct skx_dev * d,bool north,int lmc)1469fc67b11SQiuxu Zhuo static int __get_ddr_munits(struct res_config *cfg, struct skx_dev *d,
1479fc67b11SQiuxu Zhuo 			    bool north, int lmc)
1489fc67b11SQiuxu Zhuo {
1499fc67b11SQiuxu Zhuo 	unsigned long size = cfg->ddr_chan_mmio_sz * cfg->ddr_chan_num;
1509fc67b11SQiuxu Zhuo 	unsigned long bitmap = get_imc_bitmap(cfg, d->pkg, north);
1519fc67b11SQiuxu Zhuo 	void __iomem *mbase;
1529fc67b11SQiuxu Zhuo 	struct device *dev;
1539fc67b11SQiuxu Zhuo 	int i, rc, pmc;
1549fc67b11SQiuxu Zhuo 	u64 base;
1559fc67b11SQiuxu Zhuo 
1569fc67b11SQiuxu Zhuo 	for_each_set_bit(i, &bitmap, sizeof(bitmap) * 8) {
1579fc67b11SQiuxu Zhuo 		base  = north ? d->mmio_base_h_north : d->mmio_base_h_south;
1589fc67b11SQiuxu Zhuo 		base += cfg->ddr_imc_base + size * i;
1599fc67b11SQiuxu Zhuo 
1609fc67b11SQiuxu Zhuo 		edac_dbg(2, "Pkg%d mc%d mmio base 0x%llx size 0x%lx\n",
1619fc67b11SQiuxu Zhuo 			 d->pkg, lmc, base, size);
1629fc67b11SQiuxu Zhuo 
1639fc67b11SQiuxu Zhuo 		/* Set up the imc MMIO. */
1649fc67b11SQiuxu Zhuo 		mbase = ioremap(base, size);
1659fc67b11SQiuxu Zhuo 		if (!mbase) {
1669fc67b11SQiuxu Zhuo 			imh_printk(KERN_ERR, "Failed to ioremap 0x%llx\n", base);
1679fc67b11SQiuxu Zhuo 			return -ENOMEM;
1689fc67b11SQiuxu Zhuo 		}
1699fc67b11SQiuxu Zhuo 
1709fc67b11SQiuxu Zhuo 		d->imc[lmc].mbase = mbase;
1719fc67b11SQiuxu Zhuo 		d->imc[lmc].lmc = lmc;
1729fc67b11SQiuxu Zhuo 
1739fc67b11SQiuxu Zhuo 		/* Create the imc device instance. */
174bf4afc53SLinus Torvalds 		dev = kzalloc_obj(*dev);
1759fc67b11SQiuxu Zhuo 		if (!dev)
1769fc67b11SQiuxu Zhuo 			return -ENOMEM;
1779fc67b11SQiuxu Zhuo 
1789fc67b11SQiuxu Zhuo 		dev->release = imc_release;
1799fc67b11SQiuxu Zhuo 		device_initialize(dev);
1809fc67b11SQiuxu Zhuo 		rc = dev_set_name(dev, "0x%llx", base);
1819fc67b11SQiuxu Zhuo 		if (rc) {
1829fc67b11SQiuxu Zhuo 			imh_printk(KERN_ERR, "Failed to set dev name\n");
1839fc67b11SQiuxu Zhuo 			put_device(dev);
1849fc67b11SQiuxu Zhuo 			return rc;
1859fc67b11SQiuxu Zhuo 		}
1869fc67b11SQiuxu Zhuo 
1879fc67b11SQiuxu Zhuo 		d->imc[lmc].dev = dev;
1889fc67b11SQiuxu Zhuo 
1899fc67b11SQiuxu Zhuo 		/* Set up the imc index mapping. */
1909fc67b11SQiuxu Zhuo 		pmc = north ? i : 8 + i;
1919fc67b11SQiuxu Zhuo 		skx_set_mc_mapping(d, pmc, lmc);
1929fc67b11SQiuxu Zhuo 
1939fc67b11SQiuxu Zhuo 		lmc++;
1949fc67b11SQiuxu Zhuo 	}
1959fc67b11SQiuxu Zhuo 
1969fc67b11SQiuxu Zhuo 	return lmc;
1979fc67b11SQiuxu Zhuo }
1989fc67b11SQiuxu Zhuo 
get_ddr_munits(struct res_config * cfg,struct skx_dev * d)1999fc67b11SQiuxu Zhuo static bool get_ddr_munits(struct res_config *cfg, struct skx_dev *d)
2009fc67b11SQiuxu Zhuo {
2019fc67b11SQiuxu Zhuo 	int lmc = __get_ddr_munits(cfg, d, true, 0);
2029fc67b11SQiuxu Zhuo 
2039fc67b11SQiuxu Zhuo 	if (lmc < 0)
2049fc67b11SQiuxu Zhuo 		return false;
2059fc67b11SQiuxu Zhuo 
2069fc67b11SQiuxu Zhuo 	lmc = __get_ddr_munits(cfg, d, false, lmc);
2079fc67b11SQiuxu Zhuo 	if (lmc <= 0)
2089fc67b11SQiuxu Zhuo 		return false;
2099fc67b11SQiuxu Zhuo 
2109fc67b11SQiuxu Zhuo 	return true;
2119fc67b11SQiuxu Zhuo }
2129fc67b11SQiuxu Zhuo 
get_socket_id(struct res_config * cfg,struct skx_dev * d)2139fc67b11SQiuxu Zhuo static bool get_socket_id(struct res_config *cfg, struct skx_dev *d)
2149fc67b11SQiuxu Zhuo {
2159fc67b11SQiuxu Zhuo 	DEFINE_LOCAL_REG(reg, cfg, d->pkg, true, ubox, 0, socket_id);
2169fc67b11SQiuxu Zhuo 	u8 src_id;
2179fc67b11SQiuxu Zhuo 	int i;
2189fc67b11SQiuxu Zhuo 
2199fc67b11SQiuxu Zhuo 	if (!read_local_reg(&reg))
2209fc67b11SQiuxu Zhuo 		return false;
2219fc67b11SQiuxu Zhuo 
2229fc67b11SQiuxu Zhuo 	src_id = SOCKET_ID(reg.val);
2239fc67b11SQiuxu Zhuo 	edac_dbg(2, "socket id 0x%x (reg 0x%llx)\n", src_id, reg.val);
2249fc67b11SQiuxu Zhuo 
2259fc67b11SQiuxu Zhuo 	for (i = 0; i < cfg->ddr_imc_num; i++)
2269fc67b11SQiuxu Zhuo 		d->imc[i].src_id   = src_id;
2279fc67b11SQiuxu Zhuo 
2289fc67b11SQiuxu Zhuo 	return true;
2299fc67b11SQiuxu Zhuo }
2309fc67b11SQiuxu Zhuo 
2319fc67b11SQiuxu Zhuo /* Get TOLM (Top Of Low Memory) and TOHM (Top Of High Memory) parameters. */
imh_get_tolm_tohm(struct res_config * cfg,u64 * tolm,u64 * tohm)2329fc67b11SQiuxu Zhuo static bool imh_get_tolm_tohm(struct res_config *cfg, u64 *tolm, u64 *tohm)
2339fc67b11SQiuxu Zhuo {
2349fc67b11SQiuxu Zhuo 	DEFINE_LOCAL_REG(reg, cfg, 0, true, sca, 0, tolm);
2359fc67b11SQiuxu Zhuo 
2369fc67b11SQiuxu Zhuo 	if (!read_local_reg(&reg))
2379fc67b11SQiuxu Zhuo 		return false;
2389fc67b11SQiuxu Zhuo 
2399fc67b11SQiuxu Zhuo 	*tolm = TOLM(reg.val);
2409fc67b11SQiuxu Zhuo 	edac_dbg(2, "tolm 0x%llx (reg 0x%llx)\n", *tolm, reg.val);
2419fc67b11SQiuxu Zhuo 
2429fc67b11SQiuxu Zhuo 	DEFINE_LOCAL_REG(reg2, cfg, 0, true, sca, 0, tohm);
2439fc67b11SQiuxu Zhuo 
2449fc67b11SQiuxu Zhuo 	if (!read_local_reg(&reg2))
2459fc67b11SQiuxu Zhuo 		return false;
2469fc67b11SQiuxu Zhuo 
2479fc67b11SQiuxu Zhuo 	*tohm = TOHM(reg2.val);
2489fc67b11SQiuxu Zhuo 	edac_dbg(2, "tohm 0x%llx (reg 0x%llx)\n", *tohm, reg2.val);
2499fc67b11SQiuxu Zhuo 
2509fc67b11SQiuxu Zhuo 	return true;
2519fc67b11SQiuxu Zhuo }
2529fc67b11SQiuxu Zhuo 
2539fc67b11SQiuxu Zhuo /* Get the system-view MMIO_BASE_H for {north,south}-IMH. */
imh_get_all_mmio_base_h(struct res_config * cfg,struct list_head * edac_list)2549fc67b11SQiuxu Zhuo static int imh_get_all_mmio_base_h(struct res_config *cfg, struct list_head *edac_list)
2559fc67b11SQiuxu Zhuo {
2569fc67b11SQiuxu Zhuo 	int i, n = topology_max_packages(), imc_num = cfg->ddr_imc_num + cfg->hbm_imc_num;
2579fc67b11SQiuxu Zhuo 	struct skx_dev *d;
2589fc67b11SQiuxu Zhuo 
2599fc67b11SQiuxu Zhuo 	for (i = 0; i < n; i++) {
260*323bbfcfSLinus Torvalds 		d = kzalloc_flex(*d, imc, imc_num);
2619fc67b11SQiuxu Zhuo 		if (!d)
2629fc67b11SQiuxu Zhuo 			return -ENOMEM;
2639fc67b11SQiuxu Zhuo 
2649fc67b11SQiuxu Zhuo 		DEFINE_LOCAL_REG(reg, cfg, i, true, ubox, 0, mmio_base);
2659fc67b11SQiuxu Zhuo 
2669fc67b11SQiuxu Zhuo 		/* Get MMIO_BASE_H for the north-IMH. */
2679fc67b11SQiuxu Zhuo 		if (!read_local_reg(&reg) || !reg.val) {
2689fc67b11SQiuxu Zhuo 			kfree(d);
2699fc67b11SQiuxu Zhuo 			imh_printk(KERN_ERR, "Pkg%d has no north mmio_base_h\n", i);
2709fc67b11SQiuxu Zhuo 			return -ENODEV;
2719fc67b11SQiuxu Zhuo 		}
2729fc67b11SQiuxu Zhuo 
2739fc67b11SQiuxu Zhuo 		d->mmio_base_h_north = MMIO_BASE_H(reg.val);
2749fc67b11SQiuxu Zhuo 		edac_dbg(2, "Pkg%d north mmio_base_h 0x%llx (reg 0x%llx)\n",
2759fc67b11SQiuxu Zhuo 			 i, d->mmio_base_h_north, reg.val);
2769fc67b11SQiuxu Zhuo 
2779fc67b11SQiuxu Zhuo 		/* Get MMIO_BASE_H for the south-IMH (optional). */
2789fc67b11SQiuxu Zhuo 		DEFINE_LOCAL_REG(reg2, cfg, i, false, ubox, 0, mmio_base);
2799fc67b11SQiuxu Zhuo 
2809fc67b11SQiuxu Zhuo 		if (read_local_reg(&reg2)) {
2819fc67b11SQiuxu Zhuo 			d->mmio_base_h_south = MMIO_BASE_H(reg2.val);
2829fc67b11SQiuxu Zhuo 			edac_dbg(2, "Pkg%d south mmio_base_h 0x%llx (reg 0x%llx)\n",
2839fc67b11SQiuxu Zhuo 				 i, d->mmio_base_h_south, reg2.val);
2849fc67b11SQiuxu Zhuo 		}
2859fc67b11SQiuxu Zhuo 
2869fc67b11SQiuxu Zhuo 		d->pkg = i;
2879fc67b11SQiuxu Zhuo 		d->num_imc = imc_num;
2889fc67b11SQiuxu Zhuo 		skx_init_mc_mapping(d);
2899fc67b11SQiuxu Zhuo 		list_add_tail(&d->list, edac_list);
2909fc67b11SQiuxu Zhuo 	}
2919fc67b11SQiuxu Zhuo 
2929fc67b11SQiuxu Zhuo 	return 0;
2939fc67b11SQiuxu Zhuo }
2949fc67b11SQiuxu Zhuo 
2959fc67b11SQiuxu Zhuo /* Get the number of per-package memory controllers. */
imh_get_imc_num(struct res_config * cfg)2969fc67b11SQiuxu Zhuo static int imh_get_imc_num(struct res_config *cfg)
2979fc67b11SQiuxu Zhuo {
2989fc67b11SQiuxu Zhuo 	int imc_num = hweight32(get_imc_bitmap(cfg, 0, true)) +
2999fc67b11SQiuxu Zhuo 		      hweight32(get_imc_bitmap(cfg, 0, false));
3009fc67b11SQiuxu Zhuo 
3019fc67b11SQiuxu Zhuo 	if (!imc_num) {
3029fc67b11SQiuxu Zhuo 		imh_printk(KERN_ERR, "Invalid mc number\n");
3039fc67b11SQiuxu Zhuo 		return -ENODEV;
3049fc67b11SQiuxu Zhuo 	}
3059fc67b11SQiuxu Zhuo 
3069fc67b11SQiuxu Zhuo 	if (cfg->ddr_imc_num != imc_num) {
3079fc67b11SQiuxu Zhuo 		/*
3089fc67b11SQiuxu Zhuo 		 * Update the configuration data to reflect the number of
3099fc67b11SQiuxu Zhuo 		 * present DDR memory controllers.
3109fc67b11SQiuxu Zhuo 		 */
3119fc67b11SQiuxu Zhuo 		cfg->ddr_imc_num = imc_num;
3129fc67b11SQiuxu Zhuo 		edac_dbg(2, "Set ddr mc number %d\n", imc_num);
3139fc67b11SQiuxu Zhuo 	}
3149fc67b11SQiuxu Zhuo 
3159fc67b11SQiuxu Zhuo 	return 0;
3169fc67b11SQiuxu Zhuo }
3179fc67b11SQiuxu Zhuo 
3189fc67b11SQiuxu Zhuo /* Get all memory controllers' parameters. */
imh_get_munits(struct res_config * cfg,struct list_head * edac_list)3199fc67b11SQiuxu Zhuo static int imh_get_munits(struct res_config *cfg, struct list_head *edac_list)
3209fc67b11SQiuxu Zhuo {
3219fc67b11SQiuxu Zhuo 	struct skx_imc *imc;
3229fc67b11SQiuxu Zhuo 	struct skx_dev *d;
3239fc67b11SQiuxu Zhuo 	u8 mc = 0;
3249fc67b11SQiuxu Zhuo 	int i;
3259fc67b11SQiuxu Zhuo 
3269fc67b11SQiuxu Zhuo 	list_for_each_entry(d, edac_list, list) {
3279fc67b11SQiuxu Zhuo 		if (!get_ddr_munits(cfg, d)) {
3289fc67b11SQiuxu Zhuo 			imh_printk(KERN_ERR, "No mc found\n");
3299fc67b11SQiuxu Zhuo 			return -ENODEV;
3309fc67b11SQiuxu Zhuo 		}
3319fc67b11SQiuxu Zhuo 
3329fc67b11SQiuxu Zhuo 		if (!get_socket_id(cfg, d)) {
3339fc67b11SQiuxu Zhuo 			imh_printk(KERN_ERR, "Failed to get socket id\n");
3349fc67b11SQiuxu Zhuo 			return -ENODEV;
3359fc67b11SQiuxu Zhuo 		}
3369fc67b11SQiuxu Zhuo 
3379fc67b11SQiuxu Zhuo 		for (i = 0; i < cfg->ddr_imc_num; i++) {
3389fc67b11SQiuxu Zhuo 			imc = &d->imc[i];
3399fc67b11SQiuxu Zhuo 			if (!imc->mbase)
3409fc67b11SQiuxu Zhuo 				continue;
3419fc67b11SQiuxu Zhuo 
3429fc67b11SQiuxu Zhuo 			imc->chan_mmio_sz = cfg->ddr_chan_mmio_sz;
3439fc67b11SQiuxu Zhuo 			imc->num_channels = cfg->ddr_chan_num;
3449fc67b11SQiuxu Zhuo 			imc->num_dimms    = cfg->ddr_dimm_num;
3459fc67b11SQiuxu Zhuo 			imc->mc		  = mc++;
3469fc67b11SQiuxu Zhuo 		}
3479fc67b11SQiuxu Zhuo 	}
3489fc67b11SQiuxu Zhuo 
3499fc67b11SQiuxu Zhuo 	return 0;
3509fc67b11SQiuxu Zhuo }
3519fc67b11SQiuxu Zhuo 
check_2lm_enabled(struct res_config * cfg,struct skx_dev * d,int ha_idx)352f619613fSQiuxu Zhuo static bool check_2lm_enabled(struct res_config *cfg, struct skx_dev *d, int ha_idx)
353f619613fSQiuxu Zhuo {
354f619613fSQiuxu Zhuo 	DEFINE_LOCAL_REG(reg, cfg, d->pkg, true, ha, ha_idx, mode);
355f619613fSQiuxu Zhuo 
356f619613fSQiuxu Zhuo 	if (!read_local_reg(&reg))
357f619613fSQiuxu Zhuo 		return false;
358f619613fSQiuxu Zhuo 
359f619613fSQiuxu Zhuo 	if (!NMCACHING(reg.val))
360f619613fSQiuxu Zhuo 		return false;
361f619613fSQiuxu Zhuo 
362f619613fSQiuxu Zhuo 	edac_dbg(2, "2-level memory configuration (reg 0x%llx, ha idx %d)\n", reg.val, ha_idx);
363f619613fSQiuxu Zhuo 	return true;
364f619613fSQiuxu Zhuo }
365f619613fSQiuxu Zhuo 
366f619613fSQiuxu Zhuo /* Check whether the system has a 2-level memory configuration. */
imh_2lm_enabled(struct res_config * cfg,struct list_head * head)367f619613fSQiuxu Zhuo static bool imh_2lm_enabled(struct res_config *cfg, struct list_head *head)
368f619613fSQiuxu Zhuo {
369f619613fSQiuxu Zhuo 	struct skx_dev *d;
370f619613fSQiuxu Zhuo 	int i;
371f619613fSQiuxu Zhuo 
372f619613fSQiuxu Zhuo 	list_for_each_entry(d, head, list) {
373f619613fSQiuxu Zhuo 		for (i = 0; i < cfg->ddr_imc_num; i++)
374f619613fSQiuxu Zhuo 			if (check_2lm_enabled(cfg, d, i))
375f619613fSQiuxu Zhuo 				return true;
376f619613fSQiuxu Zhuo 	}
377f619613fSQiuxu Zhuo 
378f619613fSQiuxu Zhuo 	return false;
379f619613fSQiuxu Zhuo }
380f619613fSQiuxu Zhuo 
3819fc67b11SQiuxu Zhuo /* Helpers to read memory controller registers */
read_imc_reg(struct skx_imc * imc,int chan,u32 offset,u8 width)3829fc67b11SQiuxu Zhuo static u64 read_imc_reg(struct skx_imc *imc, int chan, u32 offset, u8 width)
3839fc67b11SQiuxu Zhuo {
3849fc67b11SQiuxu Zhuo 	return readx(imc->mbase + imc->chan_mmio_sz * chan + offset, width);
3859fc67b11SQiuxu Zhuo }
3869fc67b11SQiuxu Zhuo 
read_imc_mcmtr(struct res_config * cfg,struct skx_imc * imc,int chan)3879fc67b11SQiuxu Zhuo static u32 read_imc_mcmtr(struct res_config *cfg, struct skx_imc *imc, int chan)
3889fc67b11SQiuxu Zhuo {
3899fc67b11SQiuxu Zhuo 	return (u32)read_imc_reg(imc, chan, cfg->ddr_reg_mcmtr_offset, cfg->ddr_reg_mcmtr_width);
3909fc67b11SQiuxu Zhuo }
3919fc67b11SQiuxu Zhuo 
read_imc_dimmmtr(struct res_config * cfg,struct skx_imc * imc,int chan,int dimm)3929fc67b11SQiuxu Zhuo static u32 read_imc_dimmmtr(struct res_config *cfg, struct skx_imc *imc, int chan, int dimm)
3939fc67b11SQiuxu Zhuo {
3949fc67b11SQiuxu Zhuo 	return (u32)read_imc_reg(imc, chan, cfg->ddr_reg_dimmmtr_offset +
3959fc67b11SQiuxu Zhuo 				 cfg->ddr_reg_dimmmtr_width * dimm,
3969fc67b11SQiuxu Zhuo 				 cfg->ddr_reg_dimmmtr_width);
3979fc67b11SQiuxu Zhuo }
3989fc67b11SQiuxu Zhuo 
ecc_enabled(u32 mcmtr)3999fc67b11SQiuxu Zhuo static bool ecc_enabled(u32 mcmtr)
4009fc67b11SQiuxu Zhuo {
4019fc67b11SQiuxu Zhuo 	return (bool)ECC_ENABLED(mcmtr);
4029fc67b11SQiuxu Zhuo }
4039fc67b11SQiuxu Zhuo 
dimm_populated(u32 dimmmtr)4049fc67b11SQiuxu Zhuo static bool dimm_populated(u32 dimmmtr)
4059fc67b11SQiuxu Zhuo {
4069fc67b11SQiuxu Zhuo 	return (bool)DIMM_POPULATED(dimmmtr);
4079fc67b11SQiuxu Zhuo }
4089fc67b11SQiuxu Zhuo 
4099fc67b11SQiuxu Zhuo /* Get each DIMM's configurations of the memory controller @mci. */
imh_get_dimm_config(struct mem_ctl_info * mci,struct res_config * cfg)4109fc67b11SQiuxu Zhuo static int imh_get_dimm_config(struct mem_ctl_info *mci, struct res_config *cfg)
4119fc67b11SQiuxu Zhuo {
4129fc67b11SQiuxu Zhuo 	struct skx_pvt *pvt = mci->pvt_info;
4139fc67b11SQiuxu Zhuo 	struct skx_imc *imc = pvt->imc;
4149fc67b11SQiuxu Zhuo 	struct dimm_info *dimm;
4159fc67b11SQiuxu Zhuo 	u32 mcmtr, dimmmtr;
4169fc67b11SQiuxu Zhuo 	int i, j, ndimms;
4179fc67b11SQiuxu Zhuo 
4189fc67b11SQiuxu Zhuo 	for (i = 0; i < imc->num_channels; i++) {
4199fc67b11SQiuxu Zhuo 		if (!imc->mbase)
4209fc67b11SQiuxu Zhuo 			continue;
4219fc67b11SQiuxu Zhuo 
4229fc67b11SQiuxu Zhuo 		mcmtr = read_imc_mcmtr(cfg, imc, i);
4239fc67b11SQiuxu Zhuo 
4249fc67b11SQiuxu Zhuo 		for (ndimms = 0, j = 0; j < imc->num_dimms; j++) {
4259fc67b11SQiuxu Zhuo 			dimmmtr = read_imc_dimmmtr(cfg, imc, i, j);
4269fc67b11SQiuxu Zhuo 			edac_dbg(1, "mcmtr 0x%x dimmmtr 0x%x (mc%d ch%d dimm%d)\n",
4279fc67b11SQiuxu Zhuo 				 mcmtr, dimmmtr, imc->mc, i, j);
4289fc67b11SQiuxu Zhuo 
4299fc67b11SQiuxu Zhuo 			if (!dimm_populated(dimmmtr))
4309fc67b11SQiuxu Zhuo 				continue;
4319fc67b11SQiuxu Zhuo 
4329fc67b11SQiuxu Zhuo 			dimm = edac_get_dimm(mci, i, j, 0);
4339fc67b11SQiuxu Zhuo 			ndimms += skx_get_dimm_info(dimmmtr, 0, 0, dimm,
4349fc67b11SQiuxu Zhuo 						    imc, i, j, cfg);
4359fc67b11SQiuxu Zhuo 		}
4369fc67b11SQiuxu Zhuo 
4379fc67b11SQiuxu Zhuo 		if (ndimms && !ecc_enabled(mcmtr)) {
4389fc67b11SQiuxu Zhuo 			imh_printk(KERN_ERR, "ECC is disabled on mc%d ch%d\n",
4399fc67b11SQiuxu Zhuo 				   imc->mc, i);
4409fc67b11SQiuxu Zhuo 			return -ENODEV;
4419fc67b11SQiuxu Zhuo 		}
4429fc67b11SQiuxu Zhuo 	}
4439fc67b11SQiuxu Zhuo 
4449fc67b11SQiuxu Zhuo 	return 0;
4459fc67b11SQiuxu Zhuo }
4469fc67b11SQiuxu Zhuo 
4479fc67b11SQiuxu Zhuo /* Register all memory controllers to the EDAC core. */
imh_register_mci(struct res_config * cfg,struct list_head * edac_list)4489fc67b11SQiuxu Zhuo static int imh_register_mci(struct res_config *cfg, struct list_head *edac_list)
4499fc67b11SQiuxu Zhuo {
4509fc67b11SQiuxu Zhuo 	struct skx_imc *imc;
4519fc67b11SQiuxu Zhuo 	struct skx_dev *d;
4529fc67b11SQiuxu Zhuo 	int i, rc;
4539fc67b11SQiuxu Zhuo 
4549fc67b11SQiuxu Zhuo 	list_for_each_entry(d, edac_list, list) {
4559fc67b11SQiuxu Zhuo 		for (i = 0; i < cfg->ddr_imc_num; i++) {
4569fc67b11SQiuxu Zhuo 			imc = &d->imc[i];
4579fc67b11SQiuxu Zhuo 			if (!imc->mbase)
4589fc67b11SQiuxu Zhuo 				continue;
4599fc67b11SQiuxu Zhuo 
4609fc67b11SQiuxu Zhuo 			rc = skx_register_mci(imc, imc->dev,
4619fc67b11SQiuxu Zhuo 					      dev_name(imc->dev),
4629fc67b11SQiuxu Zhuo 					      "Intel IMH-based Socket",
4639fc67b11SQiuxu Zhuo 					      EDAC_MOD_STR,
4649fc67b11SQiuxu Zhuo 					      imh_get_dimm_config, cfg);
4659fc67b11SQiuxu Zhuo 			if (rc)
4669fc67b11SQiuxu Zhuo 				return rc;
4679fc67b11SQiuxu Zhuo 		}
4689fc67b11SQiuxu Zhuo 	}
4699fc67b11SQiuxu Zhuo 
4709fc67b11SQiuxu Zhuo 	return 0;
4719fc67b11SQiuxu Zhuo }
4729fc67b11SQiuxu Zhuo 
4739fc67b11SQiuxu Zhuo static struct res_config dmr_cfg = {
4749fc67b11SQiuxu Zhuo 	.type				= DMR,
4759fc67b11SQiuxu Zhuo 	.support_ddr5			= true,
4769fc67b11SQiuxu Zhuo 	.mmio_base_l_north		= 0xf6800000,
4779fc67b11SQiuxu Zhuo 	.mmio_base_l_south		= 0xf6000000,
4789fc67b11SQiuxu Zhuo 	.ddr_chan_num			= 1,
4799fc67b11SQiuxu Zhuo 	.ddr_dimm_num			= 2,
4809fc67b11SQiuxu Zhuo 	.ddr_imc_base			= 0x39b000,
4819fc67b11SQiuxu Zhuo 	.ddr_chan_mmio_sz		= 0x8000,
4829fc67b11SQiuxu Zhuo 	.ddr_reg_mcmtr_offset		= 0x360,
4839fc67b11SQiuxu Zhuo 	.ddr_reg_mcmtr_width		= 4,
4849fc67b11SQiuxu Zhuo 	.ddr_reg_dimmmtr_offset		= 0x370,
4859fc67b11SQiuxu Zhuo 	.ddr_reg_dimmmtr_width		= 4,
4869fc67b11SQiuxu Zhuo 	.ubox_base			= 0x0,
4879fc67b11SQiuxu Zhuo 	.ubox_size			= 0x2000,
4889fc67b11SQiuxu Zhuo 	.ubox_reg_mmio_base_offset	= 0x580,
4899fc67b11SQiuxu Zhuo 	.ubox_reg_mmio_base_width	= 4,
4909fc67b11SQiuxu Zhuo 	.ubox_reg_socket_id_offset	= 0x1080,
4919fc67b11SQiuxu Zhuo 	.ubox_reg_socket_id_width	= 4,
4929fc67b11SQiuxu Zhuo 	.pcu_base			= 0x3000,
4939fc67b11SQiuxu Zhuo 	.pcu_size			= 0x10000,
4949fc67b11SQiuxu Zhuo 	.pcu_reg_capid3_offset		= 0x290,
4959fc67b11SQiuxu Zhuo 	.pcu_reg_capid3_width		= 4,
4969fc67b11SQiuxu Zhuo 	.sca_base			= 0x24c000,
4979fc67b11SQiuxu Zhuo 	.sca_size			= 0x2500,
4989fc67b11SQiuxu Zhuo 	.sca_reg_tolm_offset		= 0x2100,
4999fc67b11SQiuxu Zhuo 	.sca_reg_tolm_width		= 8,
5009fc67b11SQiuxu Zhuo 	.sca_reg_tohm_offset		= 0x2108,
5019fc67b11SQiuxu Zhuo 	.sca_reg_tohm_width		= 8,
502f619613fSQiuxu Zhuo 	.ha_base			= 0x3eb000,
503f619613fSQiuxu Zhuo 	.ha_size			= 0x1000,
504f619613fSQiuxu Zhuo 	.ha_reg_mode_offset		= 0x4a0,
505f619613fSQiuxu Zhuo 	.ha_reg_mode_width		= 4,
5069fc67b11SQiuxu Zhuo };
5079fc67b11SQiuxu Zhuo 
5089fc67b11SQiuxu Zhuo static const struct x86_cpu_id imh_cpuids[] = {
5099fc67b11SQiuxu Zhuo 	X86_MATCH_VFM(INTEL_DIAMONDRAPIDS_X, &dmr_cfg),
5109fc67b11SQiuxu Zhuo 	{}
5119fc67b11SQiuxu Zhuo };
5129fc67b11SQiuxu Zhuo MODULE_DEVICE_TABLE(x86cpu, imh_cpuids);
5139fc67b11SQiuxu Zhuo 
5149fc67b11SQiuxu Zhuo static struct notifier_block imh_mce_dec = {
5159fc67b11SQiuxu Zhuo 	.notifier_call	= skx_mce_check_error,
5169fc67b11SQiuxu Zhuo 	.priority	= MCE_PRIO_EDAC,
5179fc67b11SQiuxu Zhuo };
5189fc67b11SQiuxu Zhuo 
imh_init(void)5199fc67b11SQiuxu Zhuo static int __init imh_init(void)
5209fc67b11SQiuxu Zhuo {
5219fc67b11SQiuxu Zhuo 	const struct x86_cpu_id *id;
5229fc67b11SQiuxu Zhuo 	struct list_head *edac_list;
5239fc67b11SQiuxu Zhuo 	struct res_config *cfg;
5249fc67b11SQiuxu Zhuo 	const char *owner;
5259fc67b11SQiuxu Zhuo 	u64 tolm, tohm;
5269fc67b11SQiuxu Zhuo 	int rc;
5279fc67b11SQiuxu Zhuo 
5289fc67b11SQiuxu Zhuo 	edac_dbg(2, "\n");
5299fc67b11SQiuxu Zhuo 
5309fc67b11SQiuxu Zhuo 	if (ghes_get_devices())
5319fc67b11SQiuxu Zhuo 		return -EBUSY;
5329fc67b11SQiuxu Zhuo 
5339fc67b11SQiuxu Zhuo 	owner = edac_get_owner();
5349fc67b11SQiuxu Zhuo 	if (owner && strncmp(owner, EDAC_MOD_STR, sizeof(EDAC_MOD_STR)))
5359fc67b11SQiuxu Zhuo 		return -EBUSY;
5369fc67b11SQiuxu Zhuo 
5379fc67b11SQiuxu Zhuo 	if (cpu_feature_enabled(X86_FEATURE_HYPERVISOR))
5389fc67b11SQiuxu Zhuo 		return -ENODEV;
5399fc67b11SQiuxu Zhuo 
5409fc67b11SQiuxu Zhuo 	id = x86_match_cpu(imh_cpuids);
5419fc67b11SQiuxu Zhuo 	if (!id)
5429fc67b11SQiuxu Zhuo 		return -ENODEV;
5439fc67b11SQiuxu Zhuo 	cfg = (struct res_config *)id->driver_data;
5449fc67b11SQiuxu Zhuo 	skx_set_res_cfg(cfg);
5459fc67b11SQiuxu Zhuo 
5469fc67b11SQiuxu Zhuo 	if (!imh_get_tolm_tohm(cfg, &tolm, &tohm))
5479fc67b11SQiuxu Zhuo 		return -ENODEV;
5489fc67b11SQiuxu Zhuo 
5499fc67b11SQiuxu Zhuo 	skx_set_hi_lo(tolm, tohm);
5509fc67b11SQiuxu Zhuo 
5519fc67b11SQiuxu Zhuo 	rc = imh_get_imc_num(cfg);
5529fc67b11SQiuxu Zhuo 	if (rc < 0)
5539fc67b11SQiuxu Zhuo 		goto fail;
5549fc67b11SQiuxu Zhuo 
5559fc67b11SQiuxu Zhuo 	edac_list = skx_get_edac_list();
5569fc67b11SQiuxu Zhuo 
5579fc67b11SQiuxu Zhuo 	rc = imh_get_all_mmio_base_h(cfg, edac_list);
5589fc67b11SQiuxu Zhuo 	if (rc)
5599fc67b11SQiuxu Zhuo 		goto fail;
5609fc67b11SQiuxu Zhuo 
5619fc67b11SQiuxu Zhuo 	rc = imh_get_munits(cfg, edac_list);
5629fc67b11SQiuxu Zhuo 	if (rc)
5639fc67b11SQiuxu Zhuo 		goto fail;
5649fc67b11SQiuxu Zhuo 
565f619613fSQiuxu Zhuo 	skx_set_mem_cfg(imh_2lm_enabled(cfg, edac_list));
566f619613fSQiuxu Zhuo 
5679fc67b11SQiuxu Zhuo 	rc = imh_register_mci(cfg, edac_list);
5689fc67b11SQiuxu Zhuo 	if (rc)
5699fc67b11SQiuxu Zhuo 		goto fail;
5709fc67b11SQiuxu Zhuo 
5719fc67b11SQiuxu Zhuo 	rc = skx_adxl_get();
5729fc67b11SQiuxu Zhuo 	if (rc)
5739fc67b11SQiuxu Zhuo 		goto fail;
5749fc67b11SQiuxu Zhuo 
5759fc67b11SQiuxu Zhuo 	opstate_init();
5769fc67b11SQiuxu Zhuo 	mce_register_decode_chain(&imh_mce_dec);
5775f40ea7fSQiuxu Zhuo 	skx_setup_debug("imh_test");
5789fc67b11SQiuxu Zhuo 
5799fc67b11SQiuxu Zhuo 	imh_printk(KERN_INFO, "%s\n", IMH_REVISION);
5809fc67b11SQiuxu Zhuo 
5819fc67b11SQiuxu Zhuo 	return 0;
5829fc67b11SQiuxu Zhuo fail:
5839fc67b11SQiuxu Zhuo 	skx_remove();
5849fc67b11SQiuxu Zhuo 	return rc;
5859fc67b11SQiuxu Zhuo }
5869fc67b11SQiuxu Zhuo 
imh_exit(void)5879fc67b11SQiuxu Zhuo static void __exit imh_exit(void)
5889fc67b11SQiuxu Zhuo {
5899fc67b11SQiuxu Zhuo 	edac_dbg(2, "\n");
5909fc67b11SQiuxu Zhuo 
5915f40ea7fSQiuxu Zhuo 	skx_teardown_debug();
5929fc67b11SQiuxu Zhuo 	mce_unregister_decode_chain(&imh_mce_dec);
5939fc67b11SQiuxu Zhuo 	skx_adxl_put();
5949fc67b11SQiuxu Zhuo 	skx_remove();
5959fc67b11SQiuxu Zhuo }
5969fc67b11SQiuxu Zhuo 
5979fc67b11SQiuxu Zhuo module_init(imh_init);
5989fc67b11SQiuxu Zhuo module_exit(imh_exit);
5999fc67b11SQiuxu Zhuo 
6009fc67b11SQiuxu Zhuo MODULE_LICENSE("GPL");
6019fc67b11SQiuxu Zhuo MODULE_AUTHOR("Qiuxu Zhuo");
6029fc67b11SQiuxu Zhuo MODULE_DESCRIPTION("MC Driver for Intel servers using IMH-based memory controller");
603