xref: /openbmc/linux/drivers/edac/pnd2_edac.c (revision 315bada6)
12025cf9eSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
25c71ad17STony Luck /*
35c71ad17STony Luck  * Driver for Pondicherry2 memory controller.
45c71ad17STony Luck  *
55c71ad17STony Luck  * Copyright (c) 2016, Intel Corporation.
65c71ad17STony Luck  *
75c71ad17STony Luck  * [Derived from sb_edac.c]
85c71ad17STony Luck  *
95c71ad17STony Luck  * Translation of system physical addresses to DIMM addresses
105c71ad17STony Luck  * is a two stage process:
115c71ad17STony Luck  *
125c71ad17STony Luck  * First the Pondicherry 2 memory controller handles slice and channel interleaving
135c71ad17STony Luck  * in "sys2pmi()". This is (almost) completley common between platforms.
145c71ad17STony Luck  *
155c71ad17STony Luck  * Then a platform specific dunit (DIMM unit) completes the process to provide DIMM,
165c71ad17STony Luck  * rank, bank, row and column using the appropriate "dunit_ops" functions/parameters.
175c71ad17STony Luck  */
185c71ad17STony Luck 
195c71ad17STony Luck #include <linux/module.h>
205c71ad17STony Luck #include <linux/init.h>
215c71ad17STony Luck #include <linux/pci.h>
225c71ad17STony Luck #include <linux/pci_ids.h>
235c71ad17STony Luck #include <linux/slab.h>
245c71ad17STony Luck #include <linux/delay.h>
255c71ad17STony Luck #include <linux/edac.h>
265c71ad17STony Luck #include <linux/mmzone.h>
275c71ad17STony Luck #include <linux/smp.h>
285c71ad17STony Luck #include <linux/bitmap.h>
295c71ad17STony Luck #include <linux/math64.h>
305c71ad17STony Luck #include <linux/mod_devicetable.h>
317b2db704SAndy Shevchenko #include <linux/platform_data/x86/p2sb.h>
327b2db704SAndy Shevchenko 
335c71ad17STony Luck #include <asm/cpu_device_id.h>
345c71ad17STony Luck #include <asm/intel-family.h>
355c71ad17STony Luck #include <asm/processor.h>
365c71ad17STony Luck #include <asm/mce.h>
375c71ad17STony Luck 
385c71ad17STony Luck #include "edac_mc.h"
395c71ad17STony Luck #include "edac_module.h"
405c71ad17STony Luck #include "pnd2_edac.h"
415c71ad17STony Luck 
42301375e7SToshi Kani #define EDAC_MOD_STR		"pnd2_edac"
43301375e7SToshi Kani 
445c71ad17STony Luck #define APL_NUM_CHANNELS	4
455c71ad17STony Luck #define DNV_NUM_CHANNELS	2
465c71ad17STony Luck #define DNV_MAX_DIMMS		2 /* Max DIMMs per channel */
475c71ad17STony Luck 
485c71ad17STony Luck enum type {
495c71ad17STony Luck 	APL,
505c71ad17STony Luck 	DNV, /* All requests go to PMI CH0 on each slice (CH1 disabled) */
515c71ad17STony Luck };
525c71ad17STony Luck 
535c71ad17STony Luck struct dram_addr {
545c71ad17STony Luck 	int chan;
555c71ad17STony Luck 	int dimm;
565c71ad17STony Luck 	int rank;
575c71ad17STony Luck 	int bank;
585c71ad17STony Luck 	int row;
595c71ad17STony Luck 	int col;
605c71ad17STony Luck };
615c71ad17STony Luck 
625c71ad17STony Luck struct pnd2_pvt {
635c71ad17STony Luck 	int dimm_geom[APL_NUM_CHANNELS];
645c71ad17STony Luck 	u64 tolm, tohm;
655c71ad17STony Luck };
665c71ad17STony Luck 
675c71ad17STony Luck /*
685c71ad17STony Luck  * System address space is divided into multiple regions with
695c71ad17STony Luck  * different interleave rules in each. The as0/as1 regions
705c71ad17STony Luck  * have no interleaving at all. The as2 region is interleaved
715c71ad17STony Luck  * between two channels. The mot region is magic and may overlap
725c71ad17STony Luck  * other regions, with its interleave rules taking precedence.
735c71ad17STony Luck  * Addresses not in any of these regions are interleaved across
745c71ad17STony Luck  * all four channels.
755c71ad17STony Luck  */
765c71ad17STony Luck static struct region {
775c71ad17STony Luck 	u64	base;
785c71ad17STony Luck 	u64	limit;
795c71ad17STony Luck 	u8	enabled;
805c71ad17STony Luck } mot, as0, as1, as2;
815c71ad17STony Luck 
825c71ad17STony Luck static struct dunit_ops {
835c71ad17STony Luck 	char *name;
845c71ad17STony Luck 	enum type type;
855c71ad17STony Luck 	int pmiaddr_shift;
865c71ad17STony Luck 	int pmiidx_shift;
875c71ad17STony Luck 	int channels;
885c71ad17STony Luck 	int dimms_per_channel;
895c71ad17STony Luck 	int (*rd_reg)(int port, int off, int op, void *data, size_t sz, char *name);
905c71ad17STony Luck 	int (*get_registers)(void);
915c71ad17STony Luck 	int (*check_ecc)(void);
925c71ad17STony Luck 	void (*mk_region)(char *name, struct region *rp, void *asym);
935c71ad17STony Luck 	void (*get_dimm_config)(struct mem_ctl_info *mci);
945c71ad17STony Luck 	int (*pmi2mem)(struct mem_ctl_info *mci, u64 pmiaddr, u32 pmiidx,
955c71ad17STony Luck 				   struct dram_addr *daddr, char *msg);
965c71ad17STony Luck } *ops;
975c71ad17STony Luck 
985c71ad17STony Luck static struct mem_ctl_info *pnd2_mci;
995c71ad17STony Luck 
1005c71ad17STony Luck #define PND2_MSG_SIZE	256
1015c71ad17STony Luck 
1025c71ad17STony Luck /* Debug macros */
1035c71ad17STony Luck #define pnd2_printk(level, fmt, arg...)			\
1045c71ad17STony Luck 	edac_printk(level, "pnd2", fmt, ##arg)
1055c71ad17STony Luck 
1065c71ad17STony Luck #define pnd2_mc_printk(mci, level, fmt, arg...)	\
1075c71ad17STony Luck 	edac_mc_chipset_printk(mci, level, "pnd2", fmt, ##arg)
1085c71ad17STony Luck 
1095c71ad17STony Luck #define MOT_CHAN_INTLV_BIT_1SLC_2CH 12
1105c71ad17STony Luck #define MOT_CHAN_INTLV_BIT_2SLC_2CH 13
1115c71ad17STony Luck #define SELECTOR_DISABLED (-1)
1125c71ad17STony Luck #define _4GB (1ul << 32)
1135c71ad17STony Luck 
1145c71ad17STony Luck #define PMI_ADDRESS_WIDTH	31
1155c71ad17STony Luck #define PND_MAX_PHYS_BIT	39
1165c71ad17STony Luck 
1175c71ad17STony Luck #define APL_ASYMSHIFT		28
1185c71ad17STony Luck #define DNV_ASYMSHIFT		31
1195c71ad17STony Luck #define CH_HASH_MASK_LSB	6
1205c71ad17STony Luck #define SLICE_HASH_MASK_LSB	6
1215c71ad17STony Luck #define MOT_SLC_INTLV_BIT	12
1225c71ad17STony Luck #define LOG2_PMI_ADDR_GRANULARITY	5
1235c71ad17STony Luck #define MOT_SHIFT	24
1245c71ad17STony Luck 
1255c71ad17STony Luck #define GET_BITFIELD(v, lo, hi)	(((v) & GENMASK_ULL(hi, lo)) >> (lo))
1265c71ad17STony Luck #define U64_LSHIFT(val, s)	((u64)(val) << (s))
1275c71ad17STony Luck 
1283e5d2bd1STony Luck /*
1293e5d2bd1STony Luck  * On Apollo Lake we access memory controller registers via a
1303e5d2bd1STony Luck  * side-band mailbox style interface in a hidden PCI device
1313e5d2bd1STony Luck  * configuration space.
1323e5d2bd1STony Luck  */
1333e5d2bd1STony Luck static struct pci_bus	*p2sb_bus;
1343e5d2bd1STony Luck #define P2SB_DEVFN	PCI_DEVFN(0xd, 0)
1353e5d2bd1STony Luck #define P2SB_ADDR_OFF	0xd0
1363e5d2bd1STony Luck #define P2SB_DATA_OFF	0xd4
1373e5d2bd1STony Luck #define P2SB_STAT_OFF	0xd8
1383e5d2bd1STony Luck #define P2SB_ROUT_OFF	0xda
1393e5d2bd1STony Luck #define P2SB_EADD_OFF	0xdc
1403e5d2bd1STony Luck #define P2SB_HIDE_OFF	0xe1
1413e5d2bd1STony Luck 
1423e5d2bd1STony Luck #define P2SB_BUSY	1
1433e5d2bd1STony Luck 
1443e5d2bd1STony Luck #define P2SB_READ(size, off, ptr) \
1453e5d2bd1STony Luck 	pci_bus_read_config_##size(p2sb_bus, P2SB_DEVFN, off, ptr)
1463e5d2bd1STony Luck #define P2SB_WRITE(size, off, val) \
1473e5d2bd1STony Luck 	pci_bus_write_config_##size(p2sb_bus, P2SB_DEVFN, off, val)
1483e5d2bd1STony Luck 
p2sb_is_busy(u16 * status)1493e5d2bd1STony Luck static bool p2sb_is_busy(u16 *status)
1505c71ad17STony Luck {
1513e5d2bd1STony Luck 	P2SB_READ(word, P2SB_STAT_OFF, status);
1525c71ad17STony Luck 
1533e5d2bd1STony Luck 	return !!(*status & P2SB_BUSY);
1543e5d2bd1STony Luck }
1555c71ad17STony Luck 
_apl_rd_reg(int port,int off,int op,u32 * data)1563e5d2bd1STony Luck static int _apl_rd_reg(int port, int off, int op, u32 *data)
1573e5d2bd1STony Luck {
1583e5d2bd1STony Luck 	int retries = 0xff, ret;
1593e5d2bd1STony Luck 	u16 status;
160bc8f10baSQiuxu Zhuo 	u8 hidden;
1615c71ad17STony Luck 
162bc8f10baSQiuxu Zhuo 	/* Unhide the P2SB device, if it's hidden */
163bc8f10baSQiuxu Zhuo 	P2SB_READ(byte, P2SB_HIDE_OFF, &hidden);
164bc8f10baSQiuxu Zhuo 	if (hidden)
1653e5d2bd1STony Luck 		P2SB_WRITE(byte, P2SB_HIDE_OFF, 0);
1665c71ad17STony Luck 
1673e5d2bd1STony Luck 	if (p2sb_is_busy(&status)) {
1683e5d2bd1STony Luck 		ret = -EAGAIN;
1693e5d2bd1STony Luck 		goto out;
1703e5d2bd1STony Luck 	}
1715c71ad17STony Luck 
1723e5d2bd1STony Luck 	P2SB_WRITE(dword, P2SB_ADDR_OFF, (port << 24) | off);
1733e5d2bd1STony Luck 	P2SB_WRITE(dword, P2SB_DATA_OFF, 0);
1743e5d2bd1STony Luck 	P2SB_WRITE(dword, P2SB_EADD_OFF, 0);
1753e5d2bd1STony Luck 	P2SB_WRITE(word, P2SB_ROUT_OFF, 0);
1763e5d2bd1STony Luck 	P2SB_WRITE(word, P2SB_STAT_OFF, (op << 8) | P2SB_BUSY);
1773e5d2bd1STony Luck 
1783e5d2bd1STony Luck 	while (p2sb_is_busy(&status)) {
1793e5d2bd1STony Luck 		if (retries-- == 0) {
1803e5d2bd1STony Luck 			ret = -EBUSY;
1813e5d2bd1STony Luck 			goto out;
1823e5d2bd1STony Luck 		}
1833e5d2bd1STony Luck 	}
1843e5d2bd1STony Luck 
1853e5d2bd1STony Luck 	P2SB_READ(dword, P2SB_DATA_OFF, data);
1863e5d2bd1STony Luck 	ret = (status >> 1) & 0x3;
1873e5d2bd1STony Luck out:
188bc8f10baSQiuxu Zhuo 	/* Hide the P2SB device, if it was hidden before */
189bc8f10baSQiuxu Zhuo 	if (hidden)
190bc8f10baSQiuxu Zhuo 		P2SB_WRITE(byte, P2SB_HIDE_OFF, hidden);
1915c71ad17STony Luck 
1925c71ad17STony Luck 	return ret;
1935c71ad17STony Luck }
1945c71ad17STony Luck 
apl_rd_reg(int port,int off,int op,void * data,size_t sz,char * name)1955c71ad17STony Luck static int apl_rd_reg(int port, int off, int op, void *data, size_t sz, char *name)
1965c71ad17STony Luck {
1975c71ad17STony Luck 	int ret = 0;
1985c71ad17STony Luck 
1995c71ad17STony Luck 	edac_dbg(2, "Read %s port=%x off=%x op=%x\n", name, port, off, op);
2005c71ad17STony Luck 	switch (sz) {
2015c71ad17STony Luck 	case 8:
2023e5d2bd1STony Luck 		ret = _apl_rd_reg(port, off + 4, op, (u32 *)(data + 4));
203df561f66SGustavo A. R. Silva 		fallthrough;
2045c71ad17STony Luck 	case 4:
2053e5d2bd1STony Luck 		ret |= _apl_rd_reg(port, off, op, (u32 *)data);
2065c71ad17STony Luck 		pnd2_printk(KERN_DEBUG, "%s=%x%08x ret=%d\n", name,
2075c71ad17STony Luck 					sz == 8 ? *((u32 *)(data + 4)) : 0, *((u32 *)data), ret);
2085c71ad17STony Luck 		break;
2095c71ad17STony Luck 	}
2105c71ad17STony Luck 
2115c71ad17STony Luck 	return ret;
2125c71ad17STony Luck }
2135c71ad17STony Luck 
get_mem_ctrl_hub_base_addr(void)2145c71ad17STony Luck static u64 get_mem_ctrl_hub_base_addr(void)
2155c71ad17STony Luck {
2165c71ad17STony Luck 	struct b_cr_mchbar_lo_pci lo;
2175c71ad17STony Luck 	struct b_cr_mchbar_hi_pci hi;
2185c71ad17STony Luck 	struct pci_dev *pdev;
2195c71ad17STony Luck 
2205c71ad17STony Luck 	pdev = pci_get_device(PCI_VENDOR_ID_INTEL, 0x1980, NULL);
2215c71ad17STony Luck 	if (pdev) {
2225c71ad17STony Luck 		pci_read_config_dword(pdev, 0x48, (u32 *)&lo);
2235c71ad17STony Luck 		pci_read_config_dword(pdev, 0x4c, (u32 *)&hi);
2245c71ad17STony Luck 		pci_dev_put(pdev);
2255c71ad17STony Luck 	} else {
2265c71ad17STony Luck 		return 0;
2275c71ad17STony Luck 	}
2285c71ad17STony Luck 
2295c71ad17STony Luck 	if (!lo.enable) {
2305c71ad17STony Luck 		edac_dbg(2, "MMIO via memory controller hub base address is disabled!\n");
2315c71ad17STony Luck 		return 0;
2325c71ad17STony Luck 	}
2335c71ad17STony Luck 
2345c71ad17STony Luck 	return U64_LSHIFT(hi.base, 32) | U64_LSHIFT(lo.base, 15);
2355c71ad17STony Luck }
2365c71ad17STony Luck 
23729a3388bSStephen Douthit #define DNV_MCHBAR_SIZE  0x8000
23829a3388bSStephen Douthit #define DNV_SB_PORT_SIZE 0x10000
dnv_rd_reg(int port,int off,int op,void * data,size_t sz,char * name)2395c71ad17STony Luck static int dnv_rd_reg(int port, int off, int op, void *data, size_t sz, char *name)
2405c71ad17STony Luck {
2415c71ad17STony Luck 	struct pci_dev *pdev;
2426adc32f5SAndy Shevchenko 	void __iomem *base;
2437b2db704SAndy Shevchenko 	struct resource r;
2447b2db704SAndy Shevchenko 	int ret;
2455c71ad17STony Luck 
2465c71ad17STony Luck 	if (op == 4) {
2475c71ad17STony Luck 		pdev = pci_get_device(PCI_VENDOR_ID_INTEL, 0x1980, NULL);
2485c71ad17STony Luck 		if (!pdev)
2495c71ad17STony Luck 			return -ENODEV;
2505c71ad17STony Luck 
2515c71ad17STony Luck 		pci_read_config_dword(pdev, off, data);
2525c71ad17STony Luck 		pci_dev_put(pdev);
2535c71ad17STony Luck 	} else {
2545c71ad17STony Luck 		/* MMIO via memory controller hub base address */
2555c71ad17STony Luck 		if (op == 0 && port == 0x4c) {
2567b2db704SAndy Shevchenko 			memset(&r, 0, sizeof(r));
2577b2db704SAndy Shevchenko 
2587b2db704SAndy Shevchenko 			r.start = get_mem_ctrl_hub_base_addr();
2597b2db704SAndy Shevchenko 			if (!r.start)
2605c71ad17STony Luck 				return -ENODEV;
2617b2db704SAndy Shevchenko 			r.end = r.start + DNV_MCHBAR_SIZE - 1;
2625c71ad17STony Luck 		} else {
2635c71ad17STony Luck 			/* MMIO via sideband register base address */
2647b2db704SAndy Shevchenko 			ret = p2sb_bar(NULL, 0, &r);
2657b2db704SAndy Shevchenko 			if (ret)
2667b2db704SAndy Shevchenko 				return ret;
2677b2db704SAndy Shevchenko 
2687b2db704SAndy Shevchenko 			r.start += (port << 16);
2697b2db704SAndy Shevchenko 			r.end = r.start + DNV_SB_PORT_SIZE - 1;
2705c71ad17STony Luck 		}
2715c71ad17STony Luck 
2727b2db704SAndy Shevchenko 		base = ioremap(r.start, resource_size(&r));
2735c71ad17STony Luck 		if (!base)
2745c71ad17STony Luck 			return -ENODEV;
2755c71ad17STony Luck 
2765c71ad17STony Luck 		if (sz == 8)
2776adc32f5SAndy Shevchenko 			*(u64 *)data = readq(base + off);
2786adc32f5SAndy Shevchenko 		else
2796adc32f5SAndy Shevchenko 			*(u32 *)data = readl(base + off);
2805c71ad17STony Luck 
2815c71ad17STony Luck 		iounmap(base);
2825c71ad17STony Luck 	}
2835c71ad17STony Luck 
2845c71ad17STony Luck 	edac_dbg(2, "Read %s=%.8x_%.8x\n", name,
2855c71ad17STony Luck 			(sz == 8) ? *(u32 *)(data + 4) : 0, *(u32 *)data);
2865c71ad17STony Luck 
2875c71ad17STony Luck 	return 0;
2885c71ad17STony Luck }
2895c71ad17STony Luck 
2905c71ad17STony Luck #define RD_REGP(regp, regname, port)	\
2915c71ad17STony Luck 	ops->rd_reg(port,					\
2925c71ad17STony Luck 		regname##_offset,				\
2935c71ad17STony Luck 		regname##_r_opcode,				\
2945c71ad17STony Luck 		regp, sizeof(struct regname),	\
2955c71ad17STony Luck 		#regname)
2965c71ad17STony Luck 
2975c71ad17STony Luck #define RD_REG(regp, regname)			\
2985c71ad17STony Luck 	ops->rd_reg(regname ## _port,		\
2995c71ad17STony Luck 		regname##_offset,				\
3005c71ad17STony Luck 		regname##_r_opcode,				\
3015c71ad17STony Luck 		regp, sizeof(struct regname),	\
3025c71ad17STony Luck 		#regname)
3035c71ad17STony Luck 
3045c71ad17STony Luck static u64 top_lm, top_hm;
3055c71ad17STony Luck static bool two_slices;
3065c71ad17STony Luck static bool two_channels; /* Both PMI channels in one slice enabled */
3075c71ad17STony Luck 
3085c71ad17STony Luck static u8 sym_chan_mask;
3095c71ad17STony Luck static u8 asym_chan_mask;
3105c71ad17STony Luck static u8 chan_mask;
3115c71ad17STony Luck 
3125c71ad17STony Luck static int slice_selector = -1;
3135c71ad17STony Luck static int chan_selector = -1;
3145c71ad17STony Luck static u64 slice_hash_mask;
3155c71ad17STony Luck static u64 chan_hash_mask;
3165c71ad17STony Luck 
mk_region(char * name,struct region * rp,u64 base,u64 limit)3175c71ad17STony Luck static void mk_region(char *name, struct region *rp, u64 base, u64 limit)
3185c71ad17STony Luck {
3195c71ad17STony Luck 	rp->enabled = 1;
3205c71ad17STony Luck 	rp->base = base;
3215c71ad17STony Luck 	rp->limit = limit;
3225c71ad17STony Luck 	edac_dbg(2, "Region:%s [%llx, %llx]\n", name, base, limit);
3235c71ad17STony Luck }
3245c71ad17STony Luck 
mk_region_mask(char * name,struct region * rp,u64 base,u64 mask)3255c71ad17STony Luck static void mk_region_mask(char *name, struct region *rp, u64 base, u64 mask)
3265c71ad17STony Luck {
3275c71ad17STony Luck 	if (mask == 0) {
3285c71ad17STony Luck 		pr_info(FW_BUG "MOT mask cannot be zero\n");
3295c71ad17STony Luck 		return;
3305c71ad17STony Luck 	}
3315c71ad17STony Luck 	if (mask != GENMASK_ULL(PND_MAX_PHYS_BIT, __ffs(mask))) {
3325c71ad17STony Luck 		pr_info(FW_BUG "MOT mask not power of two\n");
3335c71ad17STony Luck 		return;
3345c71ad17STony Luck 	}
3355c71ad17STony Luck 	if (base & ~mask) {
3365c71ad17STony Luck 		pr_info(FW_BUG "MOT region base/mask alignment error\n");
3375c71ad17STony Luck 		return;
3385c71ad17STony Luck 	}
3395c71ad17STony Luck 	rp->base = base;
3405c71ad17STony Luck 	rp->limit = (base | ~mask) & GENMASK_ULL(PND_MAX_PHYS_BIT, 0);
3415c71ad17STony Luck 	rp->enabled = 1;
3425c71ad17STony Luck 	edac_dbg(2, "Region:%s [%llx, %llx]\n", name, base, rp->limit);
3435c71ad17STony Luck }
3445c71ad17STony Luck 
in_region(struct region * rp,u64 addr)3455c71ad17STony Luck static bool in_region(struct region *rp, u64 addr)
3465c71ad17STony Luck {
3475c71ad17STony Luck 	if (!rp->enabled)
3485c71ad17STony Luck 		return false;
3495c71ad17STony Luck 
3505c71ad17STony Luck 	return rp->base <= addr && addr <= rp->limit;
3515c71ad17STony Luck }
3525c71ad17STony Luck 
gen_sym_mask(struct b_cr_slice_channel_hash * p)3535c71ad17STony Luck static int gen_sym_mask(struct b_cr_slice_channel_hash *p)
3545c71ad17STony Luck {
3555c71ad17STony Luck 	int mask = 0;
3565c71ad17STony Luck 
3575c71ad17STony Luck 	if (!p->slice_0_mem_disabled)
3585c71ad17STony Luck 		mask |= p->sym_slice0_channel_enabled;
3595c71ad17STony Luck 
3605c71ad17STony Luck 	if (!p->slice_1_disabled)
3615c71ad17STony Luck 		mask |= p->sym_slice1_channel_enabled << 2;
3625c71ad17STony Luck 
3635c71ad17STony Luck 	if (p->ch_1_disabled || p->enable_pmi_dual_data_mode)
3645c71ad17STony Luck 		mask &= 0x5;
3655c71ad17STony Luck 
3665c71ad17STony Luck 	return mask;
3675c71ad17STony Luck }
3685c71ad17STony Luck 
gen_asym_mask(struct b_cr_slice_channel_hash * p,struct b_cr_asym_mem_region0_mchbar * as0,struct b_cr_asym_mem_region1_mchbar * as1,struct b_cr_asym_2way_mem_region_mchbar * as2way)3695c71ad17STony Luck static int gen_asym_mask(struct b_cr_slice_channel_hash *p,
3705c71ad17STony Luck 			 struct b_cr_asym_mem_region0_mchbar *as0,
3715c71ad17STony Luck 			 struct b_cr_asym_mem_region1_mchbar *as1,
3725c71ad17STony Luck 			 struct b_cr_asym_2way_mem_region_mchbar *as2way)
3735c71ad17STony Luck {
3745c71ad17STony Luck 	const int intlv[] = { 0x5, 0xA, 0x3, 0xC };
3755c71ad17STony Luck 	int mask = 0;
3765c71ad17STony Luck 
3775c71ad17STony Luck 	if (as2way->asym_2way_interleave_enable)
3785c71ad17STony Luck 		mask = intlv[as2way->asym_2way_intlv_mode];
3795c71ad17STony Luck 	if (as0->slice0_asym_enable)
3805c71ad17STony Luck 		mask |= (1 << as0->slice0_asym_channel_select);
3815c71ad17STony Luck 	if (as1->slice1_asym_enable)
3825c71ad17STony Luck 		mask |= (4 << as1->slice1_asym_channel_select);
3835c71ad17STony Luck 	if (p->slice_0_mem_disabled)
3845c71ad17STony Luck 		mask &= 0xc;
3855c71ad17STony Luck 	if (p->slice_1_disabled)
3865c71ad17STony Luck 		mask &= 0x3;
3875c71ad17STony Luck 	if (p->ch_1_disabled || p->enable_pmi_dual_data_mode)
3885c71ad17STony Luck 		mask &= 0x5;
3895c71ad17STony Luck 
3905c71ad17STony Luck 	return mask;
3915c71ad17STony Luck }
3925c71ad17STony Luck 
3935c71ad17STony Luck static struct b_cr_tolud_pci tolud;
3945c71ad17STony Luck static struct b_cr_touud_lo_pci touud_lo;
3955c71ad17STony Luck static struct b_cr_touud_hi_pci touud_hi;
3965c71ad17STony Luck static struct b_cr_asym_mem_region0_mchbar asym0;
3975c71ad17STony Luck static struct b_cr_asym_mem_region1_mchbar asym1;
3985c71ad17STony Luck static struct b_cr_asym_2way_mem_region_mchbar asym_2way;
3995c71ad17STony Luck static struct b_cr_mot_out_base_mchbar mot_base;
4005c71ad17STony Luck static struct b_cr_mot_out_mask_mchbar mot_mask;
4015c71ad17STony Luck static struct b_cr_slice_channel_hash chash;
4025c71ad17STony Luck 
4035c71ad17STony Luck /* Apollo Lake dunit */
4045c71ad17STony Luck /*
4055c71ad17STony Luck  * Validated on board with just two DIMMs in the [0] and [2] positions
4065c71ad17STony Luck  * in this array. Other port number matches documentation, but caution
4075c71ad17STony Luck  * advised.
4085c71ad17STony Luck  */
4095c71ad17STony Luck static const int apl_dports[APL_NUM_CHANNELS] = { 0x18, 0x10, 0x11, 0x19 };
4105c71ad17STony Luck static struct d_cr_drp0 drp0[APL_NUM_CHANNELS];
4115c71ad17STony Luck 
4125c71ad17STony Luck /* Denverton dunit */
4135c71ad17STony Luck static const int dnv_dports[DNV_NUM_CHANNELS] = { 0x10, 0x12 };
4145c71ad17STony Luck static struct d_cr_dsch dsch;
4155c71ad17STony Luck static struct d_cr_ecc_ctrl ecc_ctrl[DNV_NUM_CHANNELS];
4165c71ad17STony Luck static struct d_cr_drp drp[DNV_NUM_CHANNELS];
4175c71ad17STony Luck static struct d_cr_dmap dmap[DNV_NUM_CHANNELS];
4185c71ad17STony Luck static struct d_cr_dmap1 dmap1[DNV_NUM_CHANNELS];
4195c71ad17STony Luck static struct d_cr_dmap2 dmap2[DNV_NUM_CHANNELS];
4205c71ad17STony Luck static struct d_cr_dmap3 dmap3[DNV_NUM_CHANNELS];
4215c71ad17STony Luck static struct d_cr_dmap4 dmap4[DNV_NUM_CHANNELS];
4225c71ad17STony Luck static struct d_cr_dmap5 dmap5[DNV_NUM_CHANNELS];
4235c71ad17STony Luck 
apl_mk_region(char * name,struct region * rp,void * asym)4245c71ad17STony Luck static void apl_mk_region(char *name, struct region *rp, void *asym)
4255c71ad17STony Luck {
4265c71ad17STony Luck 	struct b_cr_asym_mem_region0_mchbar *a = asym;
4275c71ad17STony Luck 
4285c71ad17STony Luck 	mk_region(name, rp,
4295c71ad17STony Luck 			  U64_LSHIFT(a->slice0_asym_base, APL_ASYMSHIFT),
4305c71ad17STony Luck 			  U64_LSHIFT(a->slice0_asym_limit, APL_ASYMSHIFT) +
4315c71ad17STony Luck 			  GENMASK_ULL(APL_ASYMSHIFT - 1, 0));
4325c71ad17STony Luck }
4335c71ad17STony Luck 
dnv_mk_region(char * name,struct region * rp,void * asym)4345c71ad17STony Luck static void dnv_mk_region(char *name, struct region *rp, void *asym)
4355c71ad17STony Luck {
4365c71ad17STony Luck 	struct b_cr_asym_mem_region_denverton *a = asym;
4375c71ad17STony Luck 
4385c71ad17STony Luck 	mk_region(name, rp,
4395c71ad17STony Luck 			  U64_LSHIFT(a->slice_asym_base, DNV_ASYMSHIFT),
4405c71ad17STony Luck 			  U64_LSHIFT(a->slice_asym_limit, DNV_ASYMSHIFT) +
4415c71ad17STony Luck 			  GENMASK_ULL(DNV_ASYMSHIFT - 1, 0));
4425c71ad17STony Luck }
4435c71ad17STony Luck 
apl_get_registers(void)4445c71ad17STony Luck static int apl_get_registers(void)
4455c71ad17STony Luck {
446164c2924STony Luck 	int ret = -ENODEV;
4475c71ad17STony Luck 	int i;
4485c71ad17STony Luck 
4495c71ad17STony Luck 	if (RD_REG(&asym_2way, b_cr_asym_2way_mem_region_mchbar))
4505c71ad17STony Luck 		return -ENODEV;
4515c71ad17STony Luck 
452164c2924STony Luck 	/*
453164c2924STony Luck 	 * RD_REGP() will fail for unpopulated or non-existent
454164c2924STony Luck 	 * DIMM slots. Return success if we find at least one DIMM.
455164c2924STony Luck 	 */
4565c71ad17STony Luck 	for (i = 0; i < APL_NUM_CHANNELS; i++)
457164c2924STony Luck 		if (!RD_REGP(&drp0[i], d_cr_drp0, apl_dports[i]))
458164c2924STony Luck 			ret = 0;
4595c71ad17STony Luck 
460164c2924STony Luck 	return ret;
4615c71ad17STony Luck }
4625c71ad17STony Luck 
dnv_get_registers(void)4635c71ad17STony Luck static int dnv_get_registers(void)
4645c71ad17STony Luck {
4655c71ad17STony Luck 	int i;
4665c71ad17STony Luck 
4675c71ad17STony Luck 	if (RD_REG(&dsch, d_cr_dsch))
4685c71ad17STony Luck 		return -ENODEV;
4695c71ad17STony Luck 
4705c71ad17STony Luck 	for (i = 0; i < DNV_NUM_CHANNELS; i++)
4715c71ad17STony Luck 		if (RD_REGP(&ecc_ctrl[i], d_cr_ecc_ctrl, dnv_dports[i]) ||
4725c71ad17STony Luck 			RD_REGP(&drp[i], d_cr_drp, dnv_dports[i]) ||
4735c71ad17STony Luck 			RD_REGP(&dmap[i], d_cr_dmap, dnv_dports[i]) ||
4745c71ad17STony Luck 			RD_REGP(&dmap1[i], d_cr_dmap1, dnv_dports[i]) ||
4755c71ad17STony Luck 			RD_REGP(&dmap2[i], d_cr_dmap2, dnv_dports[i]) ||
4765c71ad17STony Luck 			RD_REGP(&dmap3[i], d_cr_dmap3, dnv_dports[i]) ||
4775c71ad17STony Luck 			RD_REGP(&dmap4[i], d_cr_dmap4, dnv_dports[i]) ||
4785c71ad17STony Luck 			RD_REGP(&dmap5[i], d_cr_dmap5, dnv_dports[i]))
4795c71ad17STony Luck 			return -ENODEV;
4805c71ad17STony Luck 
4815c71ad17STony Luck 	return 0;
4825c71ad17STony Luck }
4835c71ad17STony Luck 
4845c71ad17STony Luck /*
4855c71ad17STony Luck  * Read all the h/w config registers once here (they don't
4865c71ad17STony Luck  * change at run time. Figure out which address ranges have
4875c71ad17STony Luck  * which interleave characteristics.
4885c71ad17STony Luck  */
get_registers(void)4895c71ad17STony Luck static int get_registers(void)
4905c71ad17STony Luck {
4915c71ad17STony Luck 	const int intlv[] = { 10, 11, 12, 12 };
4925c71ad17STony Luck 
4935c71ad17STony Luck 	if (RD_REG(&tolud, b_cr_tolud_pci) ||
4945c71ad17STony Luck 		RD_REG(&touud_lo, b_cr_touud_lo_pci) ||
4955c71ad17STony Luck 		RD_REG(&touud_hi, b_cr_touud_hi_pci) ||
4965c71ad17STony Luck 		RD_REG(&asym0, b_cr_asym_mem_region0_mchbar) ||
4975c71ad17STony Luck 		RD_REG(&asym1, b_cr_asym_mem_region1_mchbar) ||
4985c71ad17STony Luck 		RD_REG(&mot_base, b_cr_mot_out_base_mchbar) ||
4995c71ad17STony Luck 		RD_REG(&mot_mask, b_cr_mot_out_mask_mchbar) ||
5005c71ad17STony Luck 		RD_REG(&chash, b_cr_slice_channel_hash))
5015c71ad17STony Luck 		return -ENODEV;
5025c71ad17STony Luck 
5035c71ad17STony Luck 	if (ops->get_registers())
5045c71ad17STony Luck 		return -ENODEV;
5055c71ad17STony Luck 
5065c71ad17STony Luck 	if (ops->type == DNV) {
5075c71ad17STony Luck 		/* PMI channel idx (always 0) for asymmetric region */
5085c71ad17STony Luck 		asym0.slice0_asym_channel_select = 0;
5095c71ad17STony Luck 		asym1.slice1_asym_channel_select = 0;
5105c71ad17STony Luck 		/* PMI channel bitmap (always 1) for symmetric region */
5115c71ad17STony Luck 		chash.sym_slice0_channel_enabled = 0x1;
5125c71ad17STony Luck 		chash.sym_slice1_channel_enabled = 0x1;
5135c71ad17STony Luck 	}
5145c71ad17STony Luck 
5155c71ad17STony Luck 	if (asym0.slice0_asym_enable)
5165c71ad17STony Luck 		ops->mk_region("as0", &as0, &asym0);
5175c71ad17STony Luck 
5185c71ad17STony Luck 	if (asym1.slice1_asym_enable)
5195c71ad17STony Luck 		ops->mk_region("as1", &as1, &asym1);
5205c71ad17STony Luck 
5215c71ad17STony Luck 	if (asym_2way.asym_2way_interleave_enable) {
5225c71ad17STony Luck 		mk_region("as2way", &as2,
5235c71ad17STony Luck 				  U64_LSHIFT(asym_2way.asym_2way_base, APL_ASYMSHIFT),
5245c71ad17STony Luck 				  U64_LSHIFT(asym_2way.asym_2way_limit, APL_ASYMSHIFT) +
5255c71ad17STony Luck 				  GENMASK_ULL(APL_ASYMSHIFT - 1, 0));
5265c71ad17STony Luck 	}
5275c71ad17STony Luck 
5285c71ad17STony Luck 	if (mot_base.imr_en) {
5295c71ad17STony Luck 		mk_region_mask("mot", &mot,
5305c71ad17STony Luck 					   U64_LSHIFT(mot_base.mot_out_base, MOT_SHIFT),
5315c71ad17STony Luck 					   U64_LSHIFT(mot_mask.mot_out_mask, MOT_SHIFT));
5325c71ad17STony Luck 	}
5335c71ad17STony Luck 
5345c71ad17STony Luck 	top_lm = U64_LSHIFT(tolud.tolud, 20);
5355c71ad17STony Luck 	top_hm = U64_LSHIFT(touud_hi.touud, 32) | U64_LSHIFT(touud_lo.touud, 20);
5365c71ad17STony Luck 
5375c71ad17STony Luck 	two_slices = !chash.slice_1_disabled &&
5385c71ad17STony Luck 				 !chash.slice_0_mem_disabled &&
5395c71ad17STony Luck 				 (chash.sym_slice0_channel_enabled != 0) &&
5405c71ad17STony Luck 				 (chash.sym_slice1_channel_enabled != 0);
5415c71ad17STony Luck 	two_channels = !chash.ch_1_disabled &&
5425c71ad17STony Luck 				 !chash.enable_pmi_dual_data_mode &&
5435c71ad17STony Luck 				 ((chash.sym_slice0_channel_enabled == 3) ||
5445c71ad17STony Luck 				 (chash.sym_slice1_channel_enabled == 3));
5455c71ad17STony Luck 
5465c71ad17STony Luck 	sym_chan_mask = gen_sym_mask(&chash);
5475c71ad17STony Luck 	asym_chan_mask = gen_asym_mask(&chash, &asym0, &asym1, &asym_2way);
5485c71ad17STony Luck 	chan_mask = sym_chan_mask | asym_chan_mask;
5495c71ad17STony Luck 
5505c71ad17STony Luck 	if (two_slices && !two_channels) {
5515c71ad17STony Luck 		if (chash.hvm_mode)
5525c71ad17STony Luck 			slice_selector = 29;
5535c71ad17STony Luck 		else
5545c71ad17STony Luck 			slice_selector = intlv[chash.interleave_mode];
5555c71ad17STony Luck 	} else if (!two_slices && two_channels) {
5565c71ad17STony Luck 		if (chash.hvm_mode)
5575c71ad17STony Luck 			chan_selector = 29;
5585c71ad17STony Luck 		else
5595c71ad17STony Luck 			chan_selector = intlv[chash.interleave_mode];
5605c71ad17STony Luck 	} else if (two_slices && two_channels) {
5615c71ad17STony Luck 		if (chash.hvm_mode) {
5625c71ad17STony Luck 			slice_selector = 29;
5635c71ad17STony Luck 			chan_selector = 30;
5645c71ad17STony Luck 		} else {
5655c71ad17STony Luck 			slice_selector = intlv[chash.interleave_mode];
5665c71ad17STony Luck 			chan_selector = intlv[chash.interleave_mode] + 1;
5675c71ad17STony Luck 		}
5685c71ad17STony Luck 	}
5695c71ad17STony Luck 
5705c71ad17STony Luck 	if (two_slices) {
5715c71ad17STony Luck 		if (!chash.hvm_mode)
5725c71ad17STony Luck 			slice_hash_mask = chash.slice_hash_mask << SLICE_HASH_MASK_LSB;
5735c71ad17STony Luck 		if (!two_channels)
5745c71ad17STony Luck 			slice_hash_mask |= BIT_ULL(slice_selector);
5755c71ad17STony Luck 	}
5765c71ad17STony Luck 
5775c71ad17STony Luck 	if (two_channels) {
5785c71ad17STony Luck 		if (!chash.hvm_mode)
5795c71ad17STony Luck 			chan_hash_mask = chash.ch_hash_mask << CH_HASH_MASK_LSB;
5805c71ad17STony Luck 		if (!two_slices)
5815c71ad17STony Luck 			chan_hash_mask |= BIT_ULL(chan_selector);
5825c71ad17STony Luck 	}
5835c71ad17STony Luck 
5845c71ad17STony Luck 	return 0;
5855c71ad17STony Luck }
5865c71ad17STony Luck 
5875c71ad17STony Luck /* Get a contiguous memory address (remove the MMIO gap) */
remove_mmio_gap(u64 sys)5885c71ad17STony Luck static u64 remove_mmio_gap(u64 sys)
5895c71ad17STony Luck {
5905c71ad17STony Luck 	return (sys < _4GB) ? sys : sys - (_4GB - top_lm);
5915c71ad17STony Luck }
5925c71ad17STony Luck 
5935c71ad17STony Luck /* Squeeze out one address bit, shift upper part down to fill gap */
remove_addr_bit(u64 * addr,int bitidx)5945c71ad17STony Luck static void remove_addr_bit(u64 *addr, int bitidx)
5955c71ad17STony Luck {
5965c71ad17STony Luck 	u64	mask;
5975c71ad17STony Luck 
5985c71ad17STony Luck 	if (bitidx == -1)
5995c71ad17STony Luck 		return;
6005c71ad17STony Luck 
6015c71ad17STony Luck 	mask = (1ull << bitidx) - 1;
6025c71ad17STony Luck 	*addr = ((*addr >> 1) & ~mask) | (*addr & mask);
6035c71ad17STony Luck }
6045c71ad17STony Luck 
6055c71ad17STony Luck /* XOR all the bits from addr specified in mask */
hash_by_mask(u64 addr,u64 mask)6065c71ad17STony Luck static int hash_by_mask(u64 addr, u64 mask)
6075c71ad17STony Luck {
6085c71ad17STony Luck 	u64 result = addr & mask;
6095c71ad17STony Luck 
6105c71ad17STony Luck 	result = (result >> 32) ^ result;
6115c71ad17STony Luck 	result = (result >> 16) ^ result;
6125c71ad17STony Luck 	result = (result >> 8) ^ result;
6135c71ad17STony Luck 	result = (result >> 4) ^ result;
6145c71ad17STony Luck 	result = (result >> 2) ^ result;
6155c71ad17STony Luck 	result = (result >> 1) ^ result;
6165c71ad17STony Luck 
6175c71ad17STony Luck 	return (int)result & 1;
6185c71ad17STony Luck }
6195c71ad17STony Luck 
6205c71ad17STony Luck /*
6215c71ad17STony Luck  * First stage decode. Take the system address and figure out which
6225c71ad17STony Luck  * second stage will deal with it based on interleave modes.
6235c71ad17STony Luck  */
sys2pmi(const u64 addr,u32 * pmiidx,u64 * pmiaddr,char * msg)6245c71ad17STony Luck static int sys2pmi(const u64 addr, u32 *pmiidx, u64 *pmiaddr, char *msg)
6255c71ad17STony Luck {
6265c71ad17STony Luck 	u64 contig_addr, contig_base, contig_offset, contig_base_adj;
6275c71ad17STony Luck 	int mot_intlv_bit = two_slices ? MOT_CHAN_INTLV_BIT_2SLC_2CH :
6285c71ad17STony Luck 						MOT_CHAN_INTLV_BIT_1SLC_2CH;
6295c71ad17STony Luck 	int slice_intlv_bit_rm = SELECTOR_DISABLED;
6305c71ad17STony Luck 	int chan_intlv_bit_rm = SELECTOR_DISABLED;
6315c71ad17STony Luck 	/* Determine if address is in the MOT region. */
6325c71ad17STony Luck 	bool mot_hit = in_region(&mot, addr);
6335c71ad17STony Luck 	/* Calculate the number of symmetric regions enabled. */
6345c71ad17STony Luck 	int sym_channels = hweight8(sym_chan_mask);
6355c71ad17STony Luck 
6365c71ad17STony Luck 	/*
6375c71ad17STony Luck 	 * The amount we need to shift the asym base can be determined by the
6385c71ad17STony Luck 	 * number of enabled symmetric channels.
6395c71ad17STony Luck 	 * NOTE: This can only work because symmetric memory is not supposed
6405c71ad17STony Luck 	 * to do a 3-way interleave.
6415c71ad17STony Luck 	 */
6425c71ad17STony Luck 	int sym_chan_shift = sym_channels >> 1;
6435c71ad17STony Luck 
6445c71ad17STony Luck 	/* Give up if address is out of range, or in MMIO gap */
6455c71ad17STony Luck 	if (addr >= (1ul << PND_MAX_PHYS_BIT) ||
6465c71ad17STony Luck 	   (addr >= top_lm && addr < _4GB) || addr >= top_hm) {
6475c71ad17STony Luck 		snprintf(msg, PND2_MSG_SIZE, "Error address 0x%llx is not DRAM", addr);
6485c71ad17STony Luck 		return -EINVAL;
6495c71ad17STony Luck 	}
6505c71ad17STony Luck 
6515c71ad17STony Luck 	/* Get a contiguous memory address (remove the MMIO gap) */
6525c71ad17STony Luck 	contig_addr = remove_mmio_gap(addr);
6535c71ad17STony Luck 
6545c71ad17STony Luck 	if (in_region(&as0, addr)) {
6555c71ad17STony Luck 		*pmiidx = asym0.slice0_asym_channel_select;
6565c71ad17STony Luck 
6575c71ad17STony Luck 		contig_base = remove_mmio_gap(as0.base);
6585c71ad17STony Luck 		contig_offset = contig_addr - contig_base;
6595c71ad17STony Luck 		contig_base_adj = (contig_base >> sym_chan_shift) *
6605c71ad17STony Luck 						  ((chash.sym_slice0_channel_enabled >> (*pmiidx & 1)) & 1);
6615c71ad17STony Luck 		contig_addr = contig_offset + ((sym_channels > 0) ? contig_base_adj : 0ull);
6625c71ad17STony Luck 	} else if (in_region(&as1, addr)) {
6635c71ad17STony Luck 		*pmiidx = 2u + asym1.slice1_asym_channel_select;
6645c71ad17STony Luck 
6655c71ad17STony Luck 		contig_base = remove_mmio_gap(as1.base);
6665c71ad17STony Luck 		contig_offset = contig_addr - contig_base;
6675c71ad17STony Luck 		contig_base_adj = (contig_base >> sym_chan_shift) *
6685c71ad17STony Luck 						  ((chash.sym_slice1_channel_enabled >> (*pmiidx & 1)) & 1);
6695c71ad17STony Luck 		contig_addr = contig_offset + ((sym_channels > 0) ? contig_base_adj : 0ull);
6705c71ad17STony Luck 	} else if (in_region(&as2, addr) && (asym_2way.asym_2way_intlv_mode == 0x3ul)) {
6715c71ad17STony Luck 		bool channel1;
6725c71ad17STony Luck 
6735c71ad17STony Luck 		mot_intlv_bit = MOT_CHAN_INTLV_BIT_1SLC_2CH;
6745c71ad17STony Luck 		*pmiidx = (asym_2way.asym_2way_intlv_mode & 1) << 1;
6755c71ad17STony Luck 		channel1 = mot_hit ? ((bool)((addr >> mot_intlv_bit) & 1)) :
6765c71ad17STony Luck 			hash_by_mask(contig_addr, chan_hash_mask);
6775c71ad17STony Luck 		*pmiidx |= (u32)channel1;
6785c71ad17STony Luck 
6795c71ad17STony Luck 		contig_base = remove_mmio_gap(as2.base);
6805c71ad17STony Luck 		chan_intlv_bit_rm = mot_hit ? mot_intlv_bit : chan_selector;
6815c71ad17STony Luck 		contig_offset = contig_addr - contig_base;
6825c71ad17STony Luck 		remove_addr_bit(&contig_offset, chan_intlv_bit_rm);
6835c71ad17STony Luck 		contig_addr = (contig_base >> sym_chan_shift) + contig_offset;
6845c71ad17STony Luck 	} else {
6855c71ad17STony Luck 		/* Otherwise we're in normal, boring symmetric mode. */
6865c71ad17STony Luck 		*pmiidx = 0u;
6875c71ad17STony Luck 
6885c71ad17STony Luck 		if (two_slices) {
6895c71ad17STony Luck 			bool slice1;
6905c71ad17STony Luck 
6915c71ad17STony Luck 			if (mot_hit) {
6925c71ad17STony Luck 				slice_intlv_bit_rm = MOT_SLC_INTLV_BIT;
6935c71ad17STony Luck 				slice1 = (addr >> MOT_SLC_INTLV_BIT) & 1;
6945c71ad17STony Luck 			} else {
6955c71ad17STony Luck 				slice_intlv_bit_rm = slice_selector;
6965c71ad17STony Luck 				slice1 = hash_by_mask(addr, slice_hash_mask);
6975c71ad17STony Luck 			}
6985c71ad17STony Luck 
6995c71ad17STony Luck 			*pmiidx = (u32)slice1 << 1;
7005c71ad17STony Luck 		}
7015c71ad17STony Luck 
7025c71ad17STony Luck 		if (two_channels) {
7035c71ad17STony Luck 			bool channel1;
7045c71ad17STony Luck 
7055c71ad17STony Luck 			mot_intlv_bit = two_slices ? MOT_CHAN_INTLV_BIT_2SLC_2CH :
7065c71ad17STony Luck 							MOT_CHAN_INTLV_BIT_1SLC_2CH;
7075c71ad17STony Luck 
7085c71ad17STony Luck 			if (mot_hit) {
7095c71ad17STony Luck 				chan_intlv_bit_rm = mot_intlv_bit;
7105c71ad17STony Luck 				channel1 = (addr >> mot_intlv_bit) & 1;
7115c71ad17STony Luck 			} else {
7125c71ad17STony Luck 				chan_intlv_bit_rm = chan_selector;
7135c71ad17STony Luck 				channel1 = hash_by_mask(contig_addr, chan_hash_mask);
7145c71ad17STony Luck 			}
7155c71ad17STony Luck 
7165c71ad17STony Luck 			*pmiidx |= (u32)channel1;
7175c71ad17STony Luck 		}
7185c71ad17STony Luck 	}
7195c71ad17STony Luck 
7205c71ad17STony Luck 	/* Remove the chan_selector bit first */
7215c71ad17STony Luck 	remove_addr_bit(&contig_addr, chan_intlv_bit_rm);
7225c71ad17STony Luck 	/* Remove the slice bit (we remove it second because it must be lower */
7235c71ad17STony Luck 	remove_addr_bit(&contig_addr, slice_intlv_bit_rm);
7245c71ad17STony Luck 	*pmiaddr = contig_addr;
7255c71ad17STony Luck 
7265c71ad17STony Luck 	return 0;
7275c71ad17STony Luck }
7285c71ad17STony Luck 
7295c71ad17STony Luck /* Translate PMI address to memory (rank, row, bank, column) */
7305c71ad17STony Luck #define C(n) (0x10 | (n))	/* column */
7315c71ad17STony Luck #define B(n) (0x20 | (n))	/* bank */
7325c71ad17STony Luck #define R(n) (0x40 | (n))	/* row */
7335c71ad17STony Luck #define RS   (0x80)			/* rank */
7345c71ad17STony Luck 
7355c71ad17STony Luck /* addrdec values */
7365c71ad17STony Luck #define AMAP_1KB	0
7375c71ad17STony Luck #define AMAP_2KB	1
7385c71ad17STony Luck #define AMAP_4KB	2
7395c71ad17STony Luck #define AMAP_RSVD	3
7405c71ad17STony Luck 
7415c71ad17STony Luck /* dden values */
7425c71ad17STony Luck #define DEN_4Gb		0
7435c71ad17STony Luck #define DEN_8Gb		2
7445c71ad17STony Luck 
7455c71ad17STony Luck /* dwid values */
7465c71ad17STony Luck #define X8		0
7475c71ad17STony Luck #define X16		1
7485c71ad17STony Luck 
7495c71ad17STony Luck static struct dimm_geometry {
7505c71ad17STony Luck 	u8	addrdec;
7515c71ad17STony Luck 	u8	dden;
7525c71ad17STony Luck 	u8	dwid;
7535c71ad17STony Luck 	u8	rowbits, colbits;
7545c71ad17STony Luck 	u16	bits[PMI_ADDRESS_WIDTH];
7555c71ad17STony Luck } dimms[] = {
7565c71ad17STony Luck 	{
7575c71ad17STony Luck 		.addrdec = AMAP_1KB, .dden = DEN_4Gb, .dwid = X16,
7585c71ad17STony Luck 		.rowbits = 15, .colbits = 10,
7595c71ad17STony Luck 		.bits = {
7605c71ad17STony Luck 			C(2),  C(3),  C(4),  C(5),  C(6),  B(0),  B(1),  B(2),  R(0),
7615c71ad17STony Luck 			R(1),  R(2),  R(3),  R(4),  R(5),  R(6),  R(7),  R(8),  R(9),
7625c71ad17STony Luck 			R(10), C(7),  C(8),  C(9),  R(11), RS,    R(12), R(13), R(14),
7635c71ad17STony Luck 			0,     0,     0,     0
7645c71ad17STony Luck 		}
7655c71ad17STony Luck 	},
7665c71ad17STony Luck 	{
7675c71ad17STony Luck 		.addrdec = AMAP_1KB, .dden = DEN_4Gb, .dwid = X8,
7685c71ad17STony Luck 		.rowbits = 16, .colbits = 10,
7695c71ad17STony Luck 		.bits = {
7705c71ad17STony Luck 			C(2),  C(3),  C(4),  C(5),  C(6),  B(0),  B(1),  B(2),  R(0),
7715c71ad17STony Luck 			R(1),  R(2),  R(3),  R(4),  R(5),  R(6),  R(7),  R(8),  R(9),
7725c71ad17STony Luck 			R(10), C(7),  C(8),  C(9),  R(11), RS,    R(12), R(13), R(14),
7735c71ad17STony Luck 			R(15), 0,     0,     0
7745c71ad17STony Luck 		}
7755c71ad17STony Luck 	},
7765c71ad17STony Luck 	{
7775c71ad17STony Luck 		.addrdec = AMAP_1KB, .dden = DEN_8Gb, .dwid = X16,
7785c71ad17STony Luck 		.rowbits = 16, .colbits = 10,
7795c71ad17STony Luck 		.bits = {
7805c71ad17STony Luck 			C(2),  C(3),  C(4),  C(5),  C(6),  B(0),  B(1),  B(2),  R(0),
7815c71ad17STony Luck 			R(1),  R(2),  R(3),  R(4),  R(5),  R(6),  R(7),  R(8),  R(9),
7825c71ad17STony Luck 			R(10), C(7),  C(8),  C(9),  R(11), RS,    R(12), R(13), R(14),
7835c71ad17STony Luck 			R(15), 0,     0,     0
7845c71ad17STony Luck 		}
7855c71ad17STony Luck 	},
7865c71ad17STony Luck 	{
7875c71ad17STony Luck 		.addrdec = AMAP_1KB, .dden = DEN_8Gb, .dwid = X8,
7885c71ad17STony Luck 		.rowbits = 16, .colbits = 11,
7895c71ad17STony Luck 		.bits = {
7905c71ad17STony Luck 			C(2),  C(3),  C(4),  C(5),  C(6),  B(0),  B(1),  B(2),  R(0),
7915c71ad17STony Luck 			R(1),  R(2),  R(3),  R(4),  R(5),  R(6),  R(7),  R(8),  R(9),
7925c71ad17STony Luck 			R(10), C(7),  C(8),  C(9),  R(11), RS,    C(11), R(12), R(13),
7935c71ad17STony Luck 			R(14), R(15), 0,     0
7945c71ad17STony Luck 		}
7955c71ad17STony Luck 	},
7965c71ad17STony Luck 	{
7975c71ad17STony Luck 		.addrdec = AMAP_2KB, .dden = DEN_4Gb, .dwid = X16,
7985c71ad17STony Luck 		.rowbits = 15, .colbits = 10,
7995c71ad17STony Luck 		.bits = {
8005c71ad17STony Luck 			C(2),  C(3),  C(4),  C(5),  C(6),  C(7),  B(0),  B(1),  B(2),
8015c71ad17STony Luck 			R(0),  R(1),  R(2),  R(3),  R(4),  R(5),  R(6),  R(7),  R(8),
8025c71ad17STony Luck 			R(9),  R(10), C(8),  C(9),  R(11), RS,    R(12), R(13), R(14),
8035c71ad17STony Luck 			0,     0,     0,     0
8045c71ad17STony Luck 		}
8055c71ad17STony Luck 	},
8065c71ad17STony Luck 	{
8075c71ad17STony Luck 		.addrdec = AMAP_2KB, .dden = DEN_4Gb, .dwid = X8,
8085c71ad17STony Luck 		.rowbits = 16, .colbits = 10,
8095c71ad17STony Luck 		.bits = {
8105c71ad17STony Luck 			C(2),  C(3),  C(4),  C(5),  C(6),  C(7),  B(0),  B(1),  B(2),
8115c71ad17STony Luck 			R(0),  R(1),  R(2),  R(3),  R(4),  R(5),  R(6),  R(7),  R(8),
8125c71ad17STony Luck 			R(9),  R(10), C(8),  C(9),  R(11), RS,    R(12), R(13), R(14),
8135c71ad17STony Luck 			R(15), 0,     0,     0
8145c71ad17STony Luck 		}
8155c71ad17STony Luck 	},
8165c71ad17STony Luck 	{
8175c71ad17STony Luck 		.addrdec = AMAP_2KB, .dden = DEN_8Gb, .dwid = X16,
8185c71ad17STony Luck 		.rowbits = 16, .colbits = 10,
8195c71ad17STony Luck 		.bits = {
8205c71ad17STony Luck 			C(2),  C(3),  C(4),  C(5),  C(6),  C(7),  B(0),  B(1),  B(2),
8215c71ad17STony Luck 			R(0),  R(1),  R(2),  R(3),  R(4),  R(5),  R(6),  R(7),  R(8),
8225c71ad17STony Luck 			R(9),  R(10), C(8),  C(9),  R(11), RS,    R(12), R(13), R(14),
8235c71ad17STony Luck 			R(15), 0,     0,     0
8245c71ad17STony Luck 		}
8255c71ad17STony Luck 	},
8265c71ad17STony Luck 	{
8275c71ad17STony Luck 		.addrdec = AMAP_2KB, .dden = DEN_8Gb, .dwid = X8,
8285c71ad17STony Luck 		.rowbits = 16, .colbits = 11,
8295c71ad17STony Luck 		.bits = {
8305c71ad17STony Luck 			C(2),  C(3),  C(4),  C(5),  C(6),  C(7),  B(0),  B(1),  B(2),
8315c71ad17STony Luck 			R(0),  R(1),  R(2),  R(3),  R(4),  R(5),  R(6),  R(7),  R(8),
8325c71ad17STony Luck 			R(9),  R(10), C(8),  C(9),  R(11), RS,    C(11), R(12), R(13),
8335c71ad17STony Luck 			R(14), R(15), 0,     0
8345c71ad17STony Luck 		}
8355c71ad17STony Luck 	},
8365c71ad17STony Luck 	{
8375c71ad17STony Luck 		.addrdec = AMAP_4KB, .dden = DEN_4Gb, .dwid = X16,
8385c71ad17STony Luck 		.rowbits = 15, .colbits = 10,
8395c71ad17STony Luck 		.bits = {
8405c71ad17STony Luck 			C(2),  C(3),  C(4),  C(5),  C(6),  C(7),  C(8),  B(0),  B(1),
8415c71ad17STony Luck 			B(2),  R(0),  R(1),  R(2),  R(3),  R(4),  R(5),  R(6),  R(7),
8425c71ad17STony Luck 			R(8),  R(9),  R(10), C(9),  R(11), RS,    R(12), R(13), R(14),
8435c71ad17STony Luck 			0,     0,     0,     0
8445c71ad17STony Luck 		}
8455c71ad17STony Luck 	},
8465c71ad17STony Luck 	{
8475c71ad17STony Luck 		.addrdec = AMAP_4KB, .dden = DEN_4Gb, .dwid = X8,
8485c71ad17STony Luck 		.rowbits = 16, .colbits = 10,
8495c71ad17STony Luck 		.bits = {
8505c71ad17STony Luck 			C(2),  C(3),  C(4),  C(5),  C(6),  C(7),  C(8),  B(0),  B(1),
8515c71ad17STony Luck 			B(2),  R(0),  R(1),  R(2),  R(3),  R(4),  R(5),  R(6),  R(7),
8525c71ad17STony Luck 			R(8),  R(9),  R(10), C(9),  R(11), RS,    R(12), R(13), R(14),
8535c71ad17STony Luck 			R(15), 0,     0,     0
8545c71ad17STony Luck 		}
8555c71ad17STony Luck 	},
8565c71ad17STony Luck 	{
8575c71ad17STony Luck 		.addrdec = AMAP_4KB, .dden = DEN_8Gb, .dwid = X16,
8585c71ad17STony Luck 		.rowbits = 16, .colbits = 10,
8595c71ad17STony Luck 		.bits = {
8605c71ad17STony Luck 			C(2),  C(3),  C(4),  C(5),  C(6),  C(7),  C(8),  B(0),  B(1),
8615c71ad17STony Luck 			B(2),  R(0),  R(1),  R(2),  R(3),  R(4),  R(5),  R(6),  R(7),
8625c71ad17STony Luck 			R(8),  R(9),  R(10), C(9),  R(11), RS,    R(12), R(13), R(14),
8635c71ad17STony Luck 			R(15), 0,     0,     0
8645c71ad17STony Luck 		}
8655c71ad17STony Luck 	},
8665c71ad17STony Luck 	{
8675c71ad17STony Luck 		.addrdec = AMAP_4KB, .dden = DEN_8Gb, .dwid = X8,
8685c71ad17STony Luck 		.rowbits = 16, .colbits = 11,
8695c71ad17STony Luck 		.bits = {
8705c71ad17STony Luck 			C(2),  C(3),  C(4),  C(5),  C(6),  C(7),  C(8),  B(0),  B(1),
8715c71ad17STony Luck 			B(2),  R(0),  R(1),  R(2),  R(3),  R(4),  R(5),  R(6),  R(7),
8725c71ad17STony Luck 			R(8),  R(9),  R(10), C(9),  R(11), RS,    C(11), R(12), R(13),
8735c71ad17STony Luck 			R(14), R(15), 0,     0
8745c71ad17STony Luck 		}
8755c71ad17STony Luck 	}
8765c71ad17STony Luck };
8775c71ad17STony Luck 
bank_hash(u64 pmiaddr,int idx,int shft)8785c71ad17STony Luck static int bank_hash(u64 pmiaddr, int idx, int shft)
8795c71ad17STony Luck {
8805c71ad17STony Luck 	int bhash = 0;
8815c71ad17STony Luck 
8825c71ad17STony Luck 	switch (idx) {
8835c71ad17STony Luck 	case 0:
8845c71ad17STony Luck 		bhash ^= ((pmiaddr >> (12 + shft)) ^ (pmiaddr >> (9 + shft))) & 1;
8855c71ad17STony Luck 		break;
8865c71ad17STony Luck 	case 1:
8875c71ad17STony Luck 		bhash ^= (((pmiaddr >> (10 + shft)) ^ (pmiaddr >> (8 + shft))) & 1) << 1;
8885c71ad17STony Luck 		bhash ^= ((pmiaddr >> 22) & 1) << 1;
8895c71ad17STony Luck 		break;
8905c71ad17STony Luck 	case 2:
8915c71ad17STony Luck 		bhash ^= (((pmiaddr >> (13 + shft)) ^ (pmiaddr >> (11 + shft))) & 1) << 2;
8925c71ad17STony Luck 		break;
8935c71ad17STony Luck 	}
8945c71ad17STony Luck 
8955c71ad17STony Luck 	return bhash;
8965c71ad17STony Luck }
8975c71ad17STony Luck 
rank_hash(u64 pmiaddr)8985c71ad17STony Luck static int rank_hash(u64 pmiaddr)
8995c71ad17STony Luck {
9005c71ad17STony Luck 	return ((pmiaddr >> 16) ^ (pmiaddr >> 10)) & 1;
9015c71ad17STony Luck }
9025c71ad17STony Luck 
9035c71ad17STony Luck /* Second stage decode. Compute rank, bank, row & column. */
apl_pmi2mem(struct mem_ctl_info * mci,u64 pmiaddr,u32 pmiidx,struct dram_addr * daddr,char * msg)9045c71ad17STony Luck static int apl_pmi2mem(struct mem_ctl_info *mci, u64 pmiaddr, u32 pmiidx,
9055c71ad17STony Luck 		       struct dram_addr *daddr, char *msg)
9065c71ad17STony Luck {
9075c71ad17STony Luck 	struct d_cr_drp0 *cr_drp0 = &drp0[pmiidx];
9085c71ad17STony Luck 	struct pnd2_pvt *pvt = mci->pvt_info;
9095c71ad17STony Luck 	int g = pvt->dimm_geom[pmiidx];
9105c71ad17STony Luck 	struct dimm_geometry *d = &dimms[g];
9115c71ad17STony Luck 	int column = 0, bank = 0, row = 0, rank = 0;
9125c71ad17STony Luck 	int i, idx, type, skiprs = 0;
9135c71ad17STony Luck 
9145c71ad17STony Luck 	for (i = 0; i < PMI_ADDRESS_WIDTH; i++) {
9155c71ad17STony Luck 		int	bit = (pmiaddr >> i) & 1;
9165c71ad17STony Luck 
9175c71ad17STony Luck 		if (i + skiprs >= PMI_ADDRESS_WIDTH) {
9185c71ad17STony Luck 			snprintf(msg, PND2_MSG_SIZE, "Bad dimm_geometry[] table\n");
9195c71ad17STony Luck 			return -EINVAL;
9205c71ad17STony Luck 		}
9215c71ad17STony Luck 
9225c71ad17STony Luck 		type = d->bits[i + skiprs] & ~0xf;
9235c71ad17STony Luck 		idx = d->bits[i + skiprs] & 0xf;
9245c71ad17STony Luck 
9255c71ad17STony Luck 		/*
9265c71ad17STony Luck 		 * On single rank DIMMs ignore the rank select bit
9275c71ad17STony Luck 		 * and shift remainder of "bits[]" down one place.
9285c71ad17STony Luck 		 */
9295c71ad17STony Luck 		if (type == RS && (cr_drp0->rken0 + cr_drp0->rken1) == 1) {
9305c71ad17STony Luck 			skiprs = 1;
9315c71ad17STony Luck 			type = d->bits[i + skiprs] & ~0xf;
9325c71ad17STony Luck 			idx = d->bits[i + skiprs] & 0xf;
9335c71ad17STony Luck 		}
9345c71ad17STony Luck 
9355c71ad17STony Luck 		switch (type) {
9365c71ad17STony Luck 		case C(0):
9375c71ad17STony Luck 			column |= (bit << idx);
9385c71ad17STony Luck 			break;
9395c71ad17STony Luck 		case B(0):
9405c71ad17STony Luck 			bank |= (bit << idx);
9415c71ad17STony Luck 			if (cr_drp0->bahen)
9425c71ad17STony Luck 				bank ^= bank_hash(pmiaddr, idx, d->addrdec);
9435c71ad17STony Luck 			break;
9445c71ad17STony Luck 		case R(0):
9455c71ad17STony Luck 			row |= (bit << idx);
9465c71ad17STony Luck 			break;
9475c71ad17STony Luck 		case RS:
9485c71ad17STony Luck 			rank = bit;
9495c71ad17STony Luck 			if (cr_drp0->rsien)
9505c71ad17STony Luck 				rank ^= rank_hash(pmiaddr);
9515c71ad17STony Luck 			break;
9525c71ad17STony Luck 		default:
9535c71ad17STony Luck 			if (bit) {
9545c71ad17STony Luck 				snprintf(msg, PND2_MSG_SIZE, "Bad translation\n");
9555c71ad17STony Luck 				return -EINVAL;
9565c71ad17STony Luck 			}
9575c71ad17STony Luck 			goto done;
9585c71ad17STony Luck 		}
9595c71ad17STony Luck 	}
9605c71ad17STony Luck 
9615c71ad17STony Luck done:
9625c71ad17STony Luck 	daddr->col = column;
9635c71ad17STony Luck 	daddr->bank = bank;
9645c71ad17STony Luck 	daddr->row = row;
9655c71ad17STony Luck 	daddr->rank = rank;
9665c71ad17STony Luck 	daddr->dimm = 0;
9675c71ad17STony Luck 
9685c71ad17STony Luck 	return 0;
9695c71ad17STony Luck }
9705c71ad17STony Luck 
9715c71ad17STony Luck /* Pluck bit "in" from pmiaddr and return value shifted to bit "out" */
9725c71ad17STony Luck #define dnv_get_bit(pmi, in, out) ((int)(((pmi) >> (in)) & 1u) << (out))
9735c71ad17STony Luck 
dnv_pmi2mem(struct mem_ctl_info * mci,u64 pmiaddr,u32 pmiidx,struct dram_addr * daddr,char * msg)9745c71ad17STony Luck static int dnv_pmi2mem(struct mem_ctl_info *mci, u64 pmiaddr, u32 pmiidx,
9755c71ad17STony Luck 					   struct dram_addr *daddr, char *msg)
9765c71ad17STony Luck {
9775c71ad17STony Luck 	/* Rank 0 or 1 */
9785c71ad17STony Luck 	daddr->rank = dnv_get_bit(pmiaddr, dmap[pmiidx].rs0 + 13, 0);
9795c71ad17STony Luck 	/* Rank 2 or 3 */
9805c71ad17STony Luck 	daddr->rank |= dnv_get_bit(pmiaddr, dmap[pmiidx].rs1 + 13, 1);
9815c71ad17STony Luck 
9825c71ad17STony Luck 	/*
9835c71ad17STony Luck 	 * Normally ranks 0,1 are DIMM0, and 2,3 are DIMM1, but we
9845c71ad17STony Luck 	 * flip them if DIMM1 is larger than DIMM0.
9855c71ad17STony Luck 	 */
9865c71ad17STony Luck 	daddr->dimm = (daddr->rank >= 2) ^ drp[pmiidx].dimmflip;
9875c71ad17STony Luck 
9885c71ad17STony Luck 	daddr->bank = dnv_get_bit(pmiaddr, dmap[pmiidx].ba0 + 6, 0);
9895c71ad17STony Luck 	daddr->bank |= dnv_get_bit(pmiaddr, dmap[pmiidx].ba1 + 6, 1);
9905c71ad17STony Luck 	daddr->bank |= dnv_get_bit(pmiaddr, dmap[pmiidx].bg0 + 6, 2);
9915c71ad17STony Luck 	if (dsch.ddr4en)
9925c71ad17STony Luck 		daddr->bank |= dnv_get_bit(pmiaddr, dmap[pmiidx].bg1 + 6, 3);
9935c71ad17STony Luck 	if (dmap1[pmiidx].bxor) {
9945c71ad17STony Luck 		if (dsch.ddr4en) {
9955c71ad17STony Luck 			daddr->bank ^= dnv_get_bit(pmiaddr, dmap3[pmiidx].row6 + 6, 0);
9965c71ad17STony Luck 			daddr->bank ^= dnv_get_bit(pmiaddr, dmap3[pmiidx].row7 + 6, 1);
9975c71ad17STony Luck 			if (dsch.chan_width == 0)
9985c71ad17STony Luck 				/* 64/72 bit dram channel width */
9995c71ad17STony Luck 				daddr->bank ^= dnv_get_bit(pmiaddr, dmap5[pmiidx].ca3 + 6, 2);
10005c71ad17STony Luck 			else
10015c71ad17STony Luck 				/* 32/40 bit dram channel width */
10025c71ad17STony Luck 				daddr->bank ^= dnv_get_bit(pmiaddr, dmap5[pmiidx].ca4 + 6, 2);
10035c71ad17STony Luck 			daddr->bank ^= dnv_get_bit(pmiaddr, dmap2[pmiidx].row2 + 6, 3);
10045c71ad17STony Luck 		} else {
10055c71ad17STony Luck 			daddr->bank ^= dnv_get_bit(pmiaddr, dmap2[pmiidx].row2 + 6, 0);
10065c71ad17STony Luck 			daddr->bank ^= dnv_get_bit(pmiaddr, dmap3[pmiidx].row6 + 6, 1);
10075c71ad17STony Luck 			if (dsch.chan_width == 0)
10085c71ad17STony Luck 				daddr->bank ^= dnv_get_bit(pmiaddr, dmap5[pmiidx].ca3 + 6, 2);
10095c71ad17STony Luck 			else
10105c71ad17STony Luck 				daddr->bank ^= dnv_get_bit(pmiaddr, dmap5[pmiidx].ca4 + 6, 2);
10115c71ad17STony Luck 		}
10125c71ad17STony Luck 	}
10135c71ad17STony Luck 
10145c71ad17STony Luck 	daddr->row = dnv_get_bit(pmiaddr, dmap2[pmiidx].row0 + 6, 0);
10155c71ad17STony Luck 	daddr->row |= dnv_get_bit(pmiaddr, dmap2[pmiidx].row1 + 6, 1);
10165c71ad17STony Luck 	daddr->row |= dnv_get_bit(pmiaddr, dmap2[pmiidx].row2 + 6, 2);
10175c71ad17STony Luck 	daddr->row |= dnv_get_bit(pmiaddr, dmap2[pmiidx].row3 + 6, 3);
10185c71ad17STony Luck 	daddr->row |= dnv_get_bit(pmiaddr, dmap2[pmiidx].row4 + 6, 4);
10195c71ad17STony Luck 	daddr->row |= dnv_get_bit(pmiaddr, dmap2[pmiidx].row5 + 6, 5);
10205c71ad17STony Luck 	daddr->row |= dnv_get_bit(pmiaddr, dmap3[pmiidx].row6 + 6, 6);
10215c71ad17STony Luck 	daddr->row |= dnv_get_bit(pmiaddr, dmap3[pmiidx].row7 + 6, 7);
10225c71ad17STony Luck 	daddr->row |= dnv_get_bit(pmiaddr, dmap3[pmiidx].row8 + 6, 8);
10235c71ad17STony Luck 	daddr->row |= dnv_get_bit(pmiaddr, dmap3[pmiidx].row9 + 6, 9);
10245c71ad17STony Luck 	daddr->row |= dnv_get_bit(pmiaddr, dmap3[pmiidx].row10 + 6, 10);
10255c71ad17STony Luck 	daddr->row |= dnv_get_bit(pmiaddr, dmap3[pmiidx].row11 + 6, 11);
10265c71ad17STony Luck 	daddr->row |= dnv_get_bit(pmiaddr, dmap4[pmiidx].row12 + 6, 12);
10275c71ad17STony Luck 	daddr->row |= dnv_get_bit(pmiaddr, dmap4[pmiidx].row13 + 6, 13);
10285c71ad17STony Luck 	if (dmap4[pmiidx].row14 != 31)
10295c71ad17STony Luck 		daddr->row |= dnv_get_bit(pmiaddr, dmap4[pmiidx].row14 + 6, 14);
10305c71ad17STony Luck 	if (dmap4[pmiidx].row15 != 31)
10315c71ad17STony Luck 		daddr->row |= dnv_get_bit(pmiaddr, dmap4[pmiidx].row15 + 6, 15);
10325c71ad17STony Luck 	if (dmap4[pmiidx].row16 != 31)
10335c71ad17STony Luck 		daddr->row |= dnv_get_bit(pmiaddr, dmap4[pmiidx].row16 + 6, 16);
10345c71ad17STony Luck 	if (dmap4[pmiidx].row17 != 31)
10355c71ad17STony Luck 		daddr->row |= dnv_get_bit(pmiaddr, dmap4[pmiidx].row17 + 6, 17);
10365c71ad17STony Luck 
10375c71ad17STony Luck 	daddr->col = dnv_get_bit(pmiaddr, dmap5[pmiidx].ca3 + 6, 3);
10385c71ad17STony Luck 	daddr->col |= dnv_get_bit(pmiaddr, dmap5[pmiidx].ca4 + 6, 4);
10395c71ad17STony Luck 	daddr->col |= dnv_get_bit(pmiaddr, dmap5[pmiidx].ca5 + 6, 5);
10405c71ad17STony Luck 	daddr->col |= dnv_get_bit(pmiaddr, dmap5[pmiidx].ca6 + 6, 6);
10415c71ad17STony Luck 	daddr->col |= dnv_get_bit(pmiaddr, dmap5[pmiidx].ca7 + 6, 7);
10425c71ad17STony Luck 	daddr->col |= dnv_get_bit(pmiaddr, dmap5[pmiidx].ca8 + 6, 8);
10435c71ad17STony Luck 	daddr->col |= dnv_get_bit(pmiaddr, dmap5[pmiidx].ca9 + 6, 9);
10445c71ad17STony Luck 	if (!dsch.ddr4en && dmap1[pmiidx].ca11 != 0x3f)
10455c71ad17STony Luck 		daddr->col |= dnv_get_bit(pmiaddr, dmap1[pmiidx].ca11 + 13, 11);
10465c71ad17STony Luck 
10475c71ad17STony Luck 	return 0;
10485c71ad17STony Luck }
10495c71ad17STony Luck 
check_channel(int ch)10505c71ad17STony Luck static int check_channel(int ch)
10515c71ad17STony Luck {
10525c71ad17STony Luck 	if (drp0[ch].dramtype != 0) {
10535c71ad17STony Luck 		pnd2_printk(KERN_INFO, "Unsupported DIMM in channel %d\n", ch);
10545c71ad17STony Luck 		return 1;
10555c71ad17STony Luck 	} else if (drp0[ch].eccen == 0) {
10565c71ad17STony Luck 		pnd2_printk(KERN_INFO, "ECC disabled on channel %d\n", ch);
10575c71ad17STony Luck 		return 1;
10585c71ad17STony Luck 	}
10595c71ad17STony Luck 	return 0;
10605c71ad17STony Luck }
10615c71ad17STony Luck 
apl_check_ecc_active(void)10625c71ad17STony Luck static int apl_check_ecc_active(void)
10635c71ad17STony Luck {
10645c71ad17STony Luck 	int	i, ret = 0;
10655c71ad17STony Luck 
10665c71ad17STony Luck 	/* Check dramtype and ECC mode for each present DIMM */
10675c71ad17STony Luck 	for (i = 0; i < APL_NUM_CHANNELS; i++)
10685c71ad17STony Luck 		if (chan_mask & BIT(i))
10695c71ad17STony Luck 			ret += check_channel(i);
10705c71ad17STony Luck 	return ret ? -EINVAL : 0;
10715c71ad17STony Luck }
10725c71ad17STony Luck 
10735c71ad17STony Luck #define DIMMS_PRESENT(d) ((d)->rken0 + (d)->rken1 + (d)->rken2 + (d)->rken3)
10745c71ad17STony Luck 
check_unit(int ch)10755c71ad17STony Luck static int check_unit(int ch)
10765c71ad17STony Luck {
10775c71ad17STony Luck 	struct d_cr_drp *d = &drp[ch];
10785c71ad17STony Luck 
10795c71ad17STony Luck 	if (DIMMS_PRESENT(d) && !ecc_ctrl[ch].eccen) {
10805c71ad17STony Luck 		pnd2_printk(KERN_INFO, "ECC disabled on channel %d\n", ch);
10815c71ad17STony Luck 		return 1;
10825c71ad17STony Luck 	}
10835c71ad17STony Luck 	return 0;
10845c71ad17STony Luck }
10855c71ad17STony Luck 
dnv_check_ecc_active(void)10865c71ad17STony Luck static int dnv_check_ecc_active(void)
10875c71ad17STony Luck {
10885c71ad17STony Luck 	int	i, ret = 0;
10895c71ad17STony Luck 
10905c71ad17STony Luck 	for (i = 0; i < DNV_NUM_CHANNELS; i++)
10915c71ad17STony Luck 		ret += check_unit(i);
10925c71ad17STony Luck 	return ret ? -EINVAL : 0;
10935c71ad17STony Luck }
10945c71ad17STony Luck 
get_memory_error_data(struct mem_ctl_info * mci,u64 addr,struct dram_addr * daddr,char * msg)10955c71ad17STony Luck static int get_memory_error_data(struct mem_ctl_info *mci, u64 addr,
10965c71ad17STony Luck 								 struct dram_addr *daddr, char *msg)
10975c71ad17STony Luck {
10985c71ad17STony Luck 	u64	pmiaddr;
10995c71ad17STony Luck 	u32	pmiidx;
11005c71ad17STony Luck 	int	ret;
11015c71ad17STony Luck 
11025c71ad17STony Luck 	ret = sys2pmi(addr, &pmiidx, &pmiaddr, msg);
11035c71ad17STony Luck 	if (ret)
11045c71ad17STony Luck 		return ret;
11055c71ad17STony Luck 
11065c71ad17STony Luck 	pmiaddr >>= ops->pmiaddr_shift;
11075c71ad17STony Luck 	/* pmi channel idx to dimm channel idx */
11085c71ad17STony Luck 	pmiidx >>= ops->pmiidx_shift;
11095c71ad17STony Luck 	daddr->chan = pmiidx;
11105c71ad17STony Luck 
11115c71ad17STony Luck 	ret = ops->pmi2mem(mci, pmiaddr, pmiidx, daddr, msg);
11125c71ad17STony Luck 	if (ret)
11135c71ad17STony Luck 		return ret;
11145c71ad17STony Luck 
11155c71ad17STony Luck 	edac_dbg(0, "SysAddr=%llx PmiAddr=%llx Channel=%d DIMM=%d Rank=%d Bank=%d Row=%d Column=%d\n",
11165c71ad17STony Luck 			 addr, pmiaddr, daddr->chan, daddr->dimm, daddr->rank, daddr->bank, daddr->row, daddr->col);
11175c71ad17STony Luck 
11185c71ad17STony Luck 	return 0;
11195c71ad17STony Luck }
11205c71ad17STony Luck 
pnd2_mce_output_error(struct mem_ctl_info * mci,const struct mce * m,struct dram_addr * daddr)11215c71ad17STony Luck static void pnd2_mce_output_error(struct mem_ctl_info *mci, const struct mce *m,
11225c71ad17STony Luck 				  struct dram_addr *daddr)
11235c71ad17STony Luck {
11245c71ad17STony Luck 	enum hw_event_mc_err_type tp_event;
11255c71ad17STony Luck 	char *optype, msg[PND2_MSG_SIZE];
11265c71ad17STony Luck 	bool ripv = m->mcgstatus & MCG_STATUS_RIPV;
11275c71ad17STony Luck 	bool overflow = m->status & MCI_STATUS_OVER;
11285c71ad17STony Luck 	bool uc_err = m->status & MCI_STATUS_UC;
11295c71ad17STony Luck 	bool recov = m->status & MCI_STATUS_S;
11305c71ad17STony Luck 	u32 core_err_cnt = GET_BITFIELD(m->status, 38, 52);
11315c71ad17STony Luck 	u32 mscod = GET_BITFIELD(m->status, 16, 31);
11325c71ad17STony Luck 	u32 errcode = GET_BITFIELD(m->status, 0, 15);
11335c71ad17STony Luck 	u32 optypenum = GET_BITFIELD(m->status, 4, 6);
11345c71ad17STony Luck 	int rc;
11355c71ad17STony Luck 
113645bc6098STony Luck 	tp_event = uc_err ? (ripv ? HW_EVENT_ERR_UNCORRECTED : HW_EVENT_ERR_FATAL) :
11375c71ad17STony Luck 						 HW_EVENT_ERR_CORRECTED;
11385c71ad17STony Luck 
11395c71ad17STony Luck 	/*
11405c71ad17STony Luck 	 * According with Table 15-9 of the Intel Architecture spec vol 3A,
11415c71ad17STony Luck 	 * memory errors should fit in this mask:
11425c71ad17STony Luck 	 *	000f 0000 1mmm cccc (binary)
11435c71ad17STony Luck 	 * where:
11445c71ad17STony Luck 	 *	f = Correction Report Filtering Bit. If 1, subsequent errors
11455c71ad17STony Luck 	 *	    won't be shown
11465c71ad17STony Luck 	 *	mmm = error type
11475c71ad17STony Luck 	 *	cccc = channel
11485c71ad17STony Luck 	 * If the mask doesn't match, report an error to the parsing logic
11495c71ad17STony Luck 	 */
11505c71ad17STony Luck 	if (!((errcode & 0xef80) == 0x80)) {
11515c71ad17STony Luck 		optype = "Can't parse: it is not a mem";
11525c71ad17STony Luck 	} else {
11535c71ad17STony Luck 		switch (optypenum) {
11545c71ad17STony Luck 		case 0:
11555c71ad17STony Luck 			optype = "generic undef request error";
11565c71ad17STony Luck 			break;
11575c71ad17STony Luck 		case 1:
11585c71ad17STony Luck 			optype = "memory read error";
11595c71ad17STony Luck 			break;
11605c71ad17STony Luck 		case 2:
11615c71ad17STony Luck 			optype = "memory write error";
11625c71ad17STony Luck 			break;
11635c71ad17STony Luck 		case 3:
11645c71ad17STony Luck 			optype = "addr/cmd error";
11655c71ad17STony Luck 			break;
11665c71ad17STony Luck 		case 4:
11675c71ad17STony Luck 			optype = "memory scrubbing error";
11685c71ad17STony Luck 			break;
11695c71ad17STony Luck 		default:
11705c71ad17STony Luck 			optype = "reserved";
11715c71ad17STony Luck 			break;
11725c71ad17STony Luck 		}
11735c71ad17STony Luck 	}
11745c71ad17STony Luck 
11755c71ad17STony Luck 	/* Only decode errors with an valid address (ADDRV) */
11765c71ad17STony Luck 	if (!(m->status & MCI_STATUS_ADDRV))
11775c71ad17STony Luck 		return;
11785c71ad17STony Luck 
11795c71ad17STony Luck 	rc = get_memory_error_data(mci, m->addr, daddr, msg);
11805c71ad17STony Luck 	if (rc)
11815c71ad17STony Luck 		goto address_error;
11825c71ad17STony Luck 
11835c71ad17STony Luck 	snprintf(msg, sizeof(msg),
11845c71ad17STony Luck 		 "%s%s err_code:%04x:%04x channel:%d DIMM:%d rank:%d row:%d bank:%d col:%d",
11855c71ad17STony Luck 		 overflow ? " OVERFLOW" : "", (uc_err && recov) ? " recoverable" : "", mscod,
11865c71ad17STony Luck 		 errcode, daddr->chan, daddr->dimm, daddr->rank, daddr->row, daddr->bank, daddr->col);
11875c71ad17STony Luck 
11885c71ad17STony Luck 	edac_dbg(0, "%s\n", msg);
11895c71ad17STony Luck 
11905c71ad17STony Luck 	/* Call the helper to output message */
11915c71ad17STony Luck 	edac_mc_handle_error(tp_event, mci, core_err_cnt, m->addr >> PAGE_SHIFT,
1192819f60fbSQiuxu Zhuo 						 m->addr & ~PAGE_MASK, 0, daddr->chan, daddr->dimm, -1, optype, msg);
11935c71ad17STony Luck 
11945c71ad17STony Luck 	return;
11955c71ad17STony Luck 
11965c71ad17STony Luck address_error:
11975c71ad17STony Luck 	edac_mc_handle_error(tp_event, mci, core_err_cnt, 0, 0, 0, -1, -1, -1, msg, "");
11985c71ad17STony Luck }
11995c71ad17STony Luck 
apl_get_dimm_config(struct mem_ctl_info * mci)12005c71ad17STony Luck static void apl_get_dimm_config(struct mem_ctl_info *mci)
12015c71ad17STony Luck {
12025c71ad17STony Luck 	struct pnd2_pvt	*pvt = mci->pvt_info;
12035c71ad17STony Luck 	struct dimm_info *dimm;
12045c71ad17STony Luck 	struct d_cr_drp0 *d;
12055c71ad17STony Luck 	u64	capacity;
12065c71ad17STony Luck 	int	i, g;
12075c71ad17STony Luck 
12085c71ad17STony Luck 	for (i = 0; i < APL_NUM_CHANNELS; i++) {
12095c71ad17STony Luck 		if (!(chan_mask & BIT(i)))
12105c71ad17STony Luck 			continue;
12115c71ad17STony Luck 
1212bc9ad9e4SRobert Richter 		dimm = edac_get_dimm(mci, i, 0, 0);
12135c71ad17STony Luck 		if (!dimm) {
12145c71ad17STony Luck 			edac_dbg(0, "No allocated DIMM for channel %d\n", i);
12155c71ad17STony Luck 			continue;
12165c71ad17STony Luck 		}
12175c71ad17STony Luck 
12185c71ad17STony Luck 		d = &drp0[i];
12195c71ad17STony Luck 		for (g = 0; g < ARRAY_SIZE(dimms); g++)
12205c71ad17STony Luck 			if (dimms[g].addrdec == d->addrdec &&
12215c71ad17STony Luck 			    dimms[g].dden == d->dden &&
12225c71ad17STony Luck 			    dimms[g].dwid == d->dwid)
12235c71ad17STony Luck 				break;
12245c71ad17STony Luck 
12255c71ad17STony Luck 		if (g == ARRAY_SIZE(dimms)) {
12265c71ad17STony Luck 			edac_dbg(0, "Channel %d: unrecognized DIMM\n", i);
12275c71ad17STony Luck 			continue;
12285c71ad17STony Luck 		}
12295c71ad17STony Luck 
12305c71ad17STony Luck 		pvt->dimm_geom[i] = g;
12315c71ad17STony Luck 		capacity = (d->rken0 + d->rken1) * 8 * (1ul << dimms[g].rowbits) *
12325c71ad17STony Luck 				   (1ul << dimms[g].colbits);
12335c71ad17STony Luck 		edac_dbg(0, "Channel %d: %lld MByte DIMM\n", i, capacity >> (20 - 3));
12345c71ad17STony Luck 		dimm->nr_pages = MiB_TO_PAGES(capacity >> (20 - 3));
12355c71ad17STony Luck 		dimm->grain = 32;
12365c71ad17STony Luck 		dimm->dtype = (d->dwid == 0) ? DEV_X8 : DEV_X16;
12375c71ad17STony Luck 		dimm->mtype = MEM_DDR3;
12385c71ad17STony Luck 		dimm->edac_mode = EDAC_SECDED;
12395c71ad17STony Luck 		snprintf(dimm->label, sizeof(dimm->label), "Slice#%d_Chan#%d", i / 2, i % 2);
12405c71ad17STony Luck 	}
12415c71ad17STony Luck }
12425c71ad17STony Luck 
12435c71ad17STony Luck static const int dnv_dtypes[] = {
12445c71ad17STony Luck 	DEV_X8, DEV_X4, DEV_X16, DEV_UNKNOWN
12455c71ad17STony Luck };
12465c71ad17STony Luck 
dnv_get_dimm_config(struct mem_ctl_info * mci)12475c71ad17STony Luck static void dnv_get_dimm_config(struct mem_ctl_info *mci)
12485c71ad17STony Luck {
12495c71ad17STony Luck 	int	i, j, ranks_of_dimm[DNV_MAX_DIMMS], banks, rowbits, colbits, memtype;
12505c71ad17STony Luck 	struct dimm_info *dimm;
12515c71ad17STony Luck 	struct d_cr_drp *d;
12525c71ad17STony Luck 	u64	capacity;
12535c71ad17STony Luck 
12545c71ad17STony Luck 	if (dsch.ddr4en) {
12555c71ad17STony Luck 		memtype = MEM_DDR4;
12565c71ad17STony Luck 		banks = 16;
12575c71ad17STony Luck 		colbits = 10;
12585c71ad17STony Luck 	} else {
12595c71ad17STony Luck 		memtype = MEM_DDR3;
12605c71ad17STony Luck 		banks = 8;
12615c71ad17STony Luck 	}
12625c71ad17STony Luck 
12635c71ad17STony Luck 	for (i = 0; i < DNV_NUM_CHANNELS; i++) {
12645c71ad17STony Luck 		if (dmap4[i].row14 == 31)
12655c71ad17STony Luck 			rowbits = 14;
12665c71ad17STony Luck 		else if (dmap4[i].row15 == 31)
12675c71ad17STony Luck 			rowbits = 15;
12685c71ad17STony Luck 		else if (dmap4[i].row16 == 31)
12695c71ad17STony Luck 			rowbits = 16;
12705c71ad17STony Luck 		else if (dmap4[i].row17 == 31)
12715c71ad17STony Luck 			rowbits = 17;
12725c71ad17STony Luck 		else
12735c71ad17STony Luck 			rowbits = 18;
12745c71ad17STony Luck 
12755c71ad17STony Luck 		if (memtype == MEM_DDR3) {
12765c71ad17STony Luck 			if (dmap1[i].ca11 != 0x3f)
12775c71ad17STony Luck 				colbits = 12;
12785c71ad17STony Luck 			else
12795c71ad17STony Luck 				colbits = 10;
12805c71ad17STony Luck 		}
12815c71ad17STony Luck 
12825c71ad17STony Luck 		d = &drp[i];
12835c71ad17STony Luck 		/* DIMM0 is present if rank0 and/or rank1 is enabled */
12845c71ad17STony Luck 		ranks_of_dimm[0] = d->rken0 + d->rken1;
12855c71ad17STony Luck 		/* DIMM1 is present if rank2 and/or rank3 is enabled */
12865c71ad17STony Luck 		ranks_of_dimm[1] = d->rken2 + d->rken3;
12875c71ad17STony Luck 
12885c71ad17STony Luck 		for (j = 0; j < DNV_MAX_DIMMS; j++) {
12895c71ad17STony Luck 			if (!ranks_of_dimm[j])
12905c71ad17STony Luck 				continue;
12915c71ad17STony Luck 
1292bc9ad9e4SRobert Richter 			dimm = edac_get_dimm(mci, i, j, 0);
12935c71ad17STony Luck 			if (!dimm) {
12945c71ad17STony Luck 				edac_dbg(0, "No allocated DIMM for channel %d DIMM %d\n", i, j);
12955c71ad17STony Luck 				continue;
12965c71ad17STony Luck 			}
12975c71ad17STony Luck 
12985c71ad17STony Luck 			capacity = ranks_of_dimm[j] * banks * (1ul << rowbits) * (1ul << colbits);
12995c71ad17STony Luck 			edac_dbg(0, "Channel %d DIMM %d: %lld MByte DIMM\n", i, j, capacity >> (20 - 3));
13005c71ad17STony Luck 			dimm->nr_pages = MiB_TO_PAGES(capacity >> (20 - 3));
13015c71ad17STony Luck 			dimm->grain = 32;
13025c71ad17STony Luck 			dimm->dtype = dnv_dtypes[j ? d->dimmdwid0 : d->dimmdwid1];
13035c71ad17STony Luck 			dimm->mtype = memtype;
13045c71ad17STony Luck 			dimm->edac_mode = EDAC_SECDED;
13055c71ad17STony Luck 			snprintf(dimm->label, sizeof(dimm->label), "Chan#%d_DIMM#%d", i, j);
13065c71ad17STony Luck 		}
13075c71ad17STony Luck 	}
13085c71ad17STony Luck }
13095c71ad17STony Luck 
pnd2_register_mci(struct mem_ctl_info ** ppmci)13105c71ad17STony Luck static int pnd2_register_mci(struct mem_ctl_info **ppmci)
13115c71ad17STony Luck {
13125c71ad17STony Luck 	struct edac_mc_layer layers[2];
13135c71ad17STony Luck 	struct mem_ctl_info *mci;
13145c71ad17STony Luck 	struct pnd2_pvt *pvt;
13155c71ad17STony Luck 	int rc;
13165c71ad17STony Luck 
13175c71ad17STony Luck 	rc = ops->check_ecc();
13185c71ad17STony Luck 	if (rc < 0)
13195c71ad17STony Luck 		return rc;
13205c71ad17STony Luck 
13215c71ad17STony Luck 	/* Allocate a new MC control structure */
13225c71ad17STony Luck 	layers[0].type = EDAC_MC_LAYER_CHANNEL;
13235c71ad17STony Luck 	layers[0].size = ops->channels;
13245c71ad17STony Luck 	layers[0].is_virt_csrow = false;
13255c71ad17STony Luck 	layers[1].type = EDAC_MC_LAYER_SLOT;
13265c71ad17STony Luck 	layers[1].size = ops->dimms_per_channel;
13275c71ad17STony Luck 	layers[1].is_virt_csrow = true;
13285c71ad17STony Luck 	mci = edac_mc_alloc(0, ARRAY_SIZE(layers), layers, sizeof(*pvt));
13295c71ad17STony Luck 	if (!mci)
13305c71ad17STony Luck 		return -ENOMEM;
13315c71ad17STony Luck 
13325c71ad17STony Luck 	pvt = mci->pvt_info;
13335c71ad17STony Luck 	memset(pvt, 0, sizeof(*pvt));
13345c71ad17STony Luck 
1335301375e7SToshi Kani 	mci->mod_name = EDAC_MOD_STR;
13365c71ad17STony Luck 	mci->dev_name = ops->name;
13375c71ad17STony Luck 	mci->ctl_name = "Pondicherry2";
13385c71ad17STony Luck 
13395c71ad17STony Luck 	/* Get dimm basic config and the memory layout */
13405c71ad17STony Luck 	ops->get_dimm_config(mci);
13415c71ad17STony Luck 
13425c71ad17STony Luck 	if (edac_mc_add_mc(mci)) {
13435c71ad17STony Luck 		edac_dbg(0, "MC: failed edac_mc_add_mc()\n");
13445c71ad17STony Luck 		edac_mc_free(mci);
13455c71ad17STony Luck 		return -EINVAL;
13465c71ad17STony Luck 	}
13475c71ad17STony Luck 
13485c71ad17STony Luck 	*ppmci = mci;
13495c71ad17STony Luck 
13505c71ad17STony Luck 	return 0;
13515c71ad17STony Luck }
13525c71ad17STony Luck 
pnd2_unregister_mci(struct mem_ctl_info * mci)13535c71ad17STony Luck static void pnd2_unregister_mci(struct mem_ctl_info *mci)
13545c71ad17STony Luck {
13555c71ad17STony Luck 	if (unlikely(!mci || !mci->pvt_info)) {
13565c71ad17STony Luck 		pnd2_printk(KERN_ERR, "Couldn't find mci handler\n");
13575c71ad17STony Luck 		return;
13585c71ad17STony Luck 	}
13595c71ad17STony Luck 
13605c71ad17STony Luck 	/* Remove MC sysfs nodes */
13615c71ad17STony Luck 	edac_mc_del_mc(NULL);
13625c71ad17STony Luck 	edac_dbg(1, "%s: free mci struct\n", mci->ctl_name);
13635c71ad17STony Luck 	edac_mc_free(mci);
13645c71ad17STony Luck }
13655c71ad17STony Luck 
13665c71ad17STony Luck /*
13675c71ad17STony Luck  * Callback function registered with core kernel mce code.
13685c71ad17STony Luck  * Called once for each logged error.
13695c71ad17STony Luck  */
pnd2_mce_check_error(struct notifier_block * nb,unsigned long val,void * data)13705c71ad17STony Luck static int pnd2_mce_check_error(struct notifier_block *nb, unsigned long val, void *data)
13715c71ad17STony Luck {
13725c71ad17STony Luck 	struct mce *mce = (struct mce *)data;
13735c71ad17STony Luck 	struct mem_ctl_info *mci;
13745c71ad17STony Luck 	struct dram_addr daddr;
13755c71ad17STony Luck 	char *type;
13765c71ad17STony Luck 
13775c71ad17STony Luck 	mci = pnd2_mci;
137823ba710aSTony Luck 	if (!mci || (mce->kflags & MCE_HANDLED_CEC))
13795c71ad17STony Luck 		return NOTIFY_DONE;
13805c71ad17STony Luck 
13815c71ad17STony Luck 	/*
13825c71ad17STony Luck 	 * Just let mcelog handle it if the error is
13835c71ad17STony Luck 	 * outside the memory controller. A memory error
13845c71ad17STony Luck 	 * is indicated by bit 7 = 1 and bits = 8-11,13-15 = 0.
13855c71ad17STony Luck 	 * bit 12 has an special meaning.
13865c71ad17STony Luck 	 */
13875c71ad17STony Luck 	if ((mce->status & 0xefff) >> 7 != 1)
13885c71ad17STony Luck 		return NOTIFY_DONE;
13895c71ad17STony Luck 
13905c71ad17STony Luck 	if (mce->mcgstatus & MCG_STATUS_MCIP)
13915c71ad17STony Luck 		type = "Exception";
13925c71ad17STony Luck 	else
13935c71ad17STony Luck 		type = "Event";
13945c71ad17STony Luck 
13955c71ad17STony Luck 	pnd2_mc_printk(mci, KERN_INFO, "HANDLING MCE MEMORY ERROR\n");
13965c71ad17STony Luck 	pnd2_mc_printk(mci, KERN_INFO, "CPU %u: Machine Check %s: %llx Bank %u: %llx\n",
13975c71ad17STony Luck 				   mce->extcpu, type, mce->mcgstatus, mce->bank, mce->status);
13985c71ad17STony Luck 	pnd2_mc_printk(mci, KERN_INFO, "TSC %llx ", mce->tsc);
13995c71ad17STony Luck 	pnd2_mc_printk(mci, KERN_INFO, "ADDR %llx ", mce->addr);
14005c71ad17STony Luck 	pnd2_mc_printk(mci, KERN_INFO, "MISC %llx ", mce->misc);
14015c71ad17STony Luck 	pnd2_mc_printk(mci, KERN_INFO, "PROCESSOR %u:%x TIME %llu SOCKET %u APIC %x\n",
14025c71ad17STony Luck 				   mce->cpuvendor, mce->cpuid, mce->time, mce->socketid, mce->apicid);
14035c71ad17STony Luck 
14045c71ad17STony Luck 	pnd2_mce_output_error(mci, mce, &daddr);
14055c71ad17STony Luck 
14065c71ad17STony Luck 	/* Advice mcelog that the error were handled */
140723ba710aSTony Luck 	mce->kflags |= MCE_HANDLED_EDAC;
140823ba710aSTony Luck 	return NOTIFY_OK;
14095c71ad17STony Luck }
14105c71ad17STony Luck 
14115c71ad17STony Luck static struct notifier_block pnd2_mce_dec = {
14125c71ad17STony Luck 	.notifier_call	= pnd2_mce_check_error,
141330bf38e4SZhenzhong Duan 	.priority	= MCE_PRIO_EDAC,
14145c71ad17STony Luck };
14155c71ad17STony Luck 
14165c71ad17STony Luck #ifdef CONFIG_EDAC_DEBUG
14175c71ad17STony Luck /*
14185c71ad17STony Luck  * Write an address to this file to exercise the address decode
14195c71ad17STony Luck  * logic in this driver.
14205c71ad17STony Luck  */
14215c71ad17STony Luck static u64 pnd2_fake_addr;
14225c71ad17STony Luck #define PND2_BLOB_SIZE 1024
14235c71ad17STony Luck static char pnd2_result[PND2_BLOB_SIZE];
14245c71ad17STony Luck static struct dentry *pnd2_test;
14255c71ad17STony Luck static struct debugfs_blob_wrapper pnd2_blob = {
14265c71ad17STony Luck 	.data = pnd2_result,
14275c71ad17STony Luck 	.size = 0
14285c71ad17STony Luck };
14295c71ad17STony Luck 
debugfs_u64_set(void * data,u64 val)14305c71ad17STony Luck static int debugfs_u64_set(void *data, u64 val)
14315c71ad17STony Luck {
14325c71ad17STony Luck 	struct dram_addr daddr;
14335c71ad17STony Luck 	struct mce m;
14345c71ad17STony Luck 
14355c71ad17STony Luck 	*(u64 *)data = val;
14365c71ad17STony Luck 	m.mcgstatus = 0;
14375c71ad17STony Luck 	/* ADDRV + MemRd + Unknown channel */
14385c71ad17STony Luck 	m.status = MCI_STATUS_ADDRV + 0x9f;
14395c71ad17STony Luck 	m.addr = val;
14405c71ad17STony Luck 	pnd2_mce_output_error(pnd2_mci, &m, &daddr);
14415c71ad17STony Luck 	snprintf(pnd2_blob.data, PND2_BLOB_SIZE,
14425c71ad17STony Luck 			 "SysAddr=%llx Channel=%d DIMM=%d Rank=%d Bank=%d Row=%d Column=%d\n",
14435c71ad17STony Luck 			 m.addr, daddr.chan, daddr.dimm, daddr.rank, daddr.bank, daddr.row, daddr.col);
14445c71ad17STony Luck 	pnd2_blob.size = strlen(pnd2_blob.data);
14455c71ad17STony Luck 
14465c71ad17STony Luck 	return 0;
14475c71ad17STony Luck }
14485c71ad17STony Luck DEFINE_DEBUGFS_ATTRIBUTE(fops_u64_wo, NULL, debugfs_u64_set, "%llu\n");
14495c71ad17STony Luck 
setup_pnd2_debug(void)14505c71ad17STony Luck static void setup_pnd2_debug(void)
14515c71ad17STony Luck {
14525c71ad17STony Luck 	pnd2_test = edac_debugfs_create_dir("pnd2_test");
14535c71ad17STony Luck 	edac_debugfs_create_file("pnd2_debug_addr", 0200, pnd2_test,
14545c71ad17STony Luck 							 &pnd2_fake_addr, &fops_u64_wo);
14555c71ad17STony Luck 	debugfs_create_blob("pnd2_debug_results", 0400, pnd2_test, &pnd2_blob);
14565c71ad17STony Luck }
14575c71ad17STony Luck 
teardown_pnd2_debug(void)14585c71ad17STony Luck static void teardown_pnd2_debug(void)
14595c71ad17STony Luck {
14605c71ad17STony Luck 	debugfs_remove_recursive(pnd2_test);
14615c71ad17STony Luck }
1462cd1be315SBorislav Petkov #else
setup_pnd2_debug(void)1463cd1be315SBorislav Petkov static void setup_pnd2_debug(void)	{}
teardown_pnd2_debug(void)1464cd1be315SBorislav Petkov static void teardown_pnd2_debug(void)	{}
1465cd1be315SBorislav Petkov #endif /* CONFIG_EDAC_DEBUG */
1466cd1be315SBorislav Petkov 
14675c71ad17STony Luck 
pnd2_probe(void)14685c71ad17STony Luck static int pnd2_probe(void)
14695c71ad17STony Luck {
14705c71ad17STony Luck 	int rc;
14715c71ad17STony Luck 
14725c71ad17STony Luck 	edac_dbg(2, "\n");
14735c71ad17STony Luck 	rc = get_registers();
14745c71ad17STony Luck 	if (rc)
14755c71ad17STony Luck 		return rc;
14765c71ad17STony Luck 
14775c71ad17STony Luck 	return pnd2_register_mci(&pnd2_mci);
14785c71ad17STony Luck }
14795c71ad17STony Luck 
pnd2_remove(void)14805c71ad17STony Luck static void pnd2_remove(void)
14815c71ad17STony Luck {
14825c71ad17STony Luck 	edac_dbg(0, "\n");
14835c71ad17STony Luck 	pnd2_unregister_mci(pnd2_mci);
14845c71ad17STony Luck }
14855c71ad17STony Luck 
14865c71ad17STony Luck static struct dunit_ops apl_ops = {
14875c71ad17STony Luck 		.name			= "pnd2/apl",
14885c71ad17STony Luck 		.type			= APL,
14895c71ad17STony Luck 		.pmiaddr_shift		= LOG2_PMI_ADDR_GRANULARITY,
14905c71ad17STony Luck 		.pmiidx_shift		= 0,
14915c71ad17STony Luck 		.channels		= APL_NUM_CHANNELS,
14925c71ad17STony Luck 		.dimms_per_channel	= 1,
14935c71ad17STony Luck 		.rd_reg			= apl_rd_reg,
14945c71ad17STony Luck 		.get_registers		= apl_get_registers,
14955c71ad17STony Luck 		.check_ecc		= apl_check_ecc_active,
14965c71ad17STony Luck 		.mk_region		= apl_mk_region,
14975c71ad17STony Luck 		.get_dimm_config	= apl_get_dimm_config,
14985c71ad17STony Luck 		.pmi2mem		= apl_pmi2mem,
14995c71ad17STony Luck };
15005c71ad17STony Luck 
15015c71ad17STony Luck static struct dunit_ops dnv_ops = {
15025c71ad17STony Luck 		.name			= "pnd2/dnv",
15035c71ad17STony Luck 		.type			= DNV,
15045c71ad17STony Luck 		.pmiaddr_shift		= 0,
15055c71ad17STony Luck 		.pmiidx_shift		= 1,
15065c71ad17STony Luck 		.channels		= DNV_NUM_CHANNELS,
15075c71ad17STony Luck 		.dimms_per_channel	= 2,
15085c71ad17STony Luck 		.rd_reg			= dnv_rd_reg,
15095c71ad17STony Luck 		.get_registers		= dnv_get_registers,
15105c71ad17STony Luck 		.check_ecc		= dnv_check_ecc_active,
15115c71ad17STony Luck 		.mk_region		= dnv_mk_region,
15125c71ad17STony Luck 		.get_dimm_config	= dnv_get_dimm_config,
15135c71ad17STony Luck 		.pmi2mem		= dnv_pmi2mem,
15145c71ad17STony Luck };
15155c71ad17STony Luck 
15165c71ad17STony Luck static const struct x86_cpu_id pnd2_cpuids[] = {
151729842621SThomas Gleixner 	X86_MATCH_INTEL_FAM6_MODEL(ATOM_GOLDMONT,	&apl_ops),
151829842621SThomas Gleixner 	X86_MATCH_INTEL_FAM6_MODEL(ATOM_GOLDMONT_D,	&dnv_ops),
15195c71ad17STony Luck 	{ }
15205c71ad17STony Luck };
15215c71ad17STony Luck MODULE_DEVICE_TABLE(x86cpu, pnd2_cpuids);
15225c71ad17STony Luck 
pnd2_init(void)15235c71ad17STony Luck static int __init pnd2_init(void)
15245c71ad17STony Luck {
15255c71ad17STony Luck 	const struct x86_cpu_id *id;
1526301375e7SToshi Kani 	const char *owner;
15275c71ad17STony Luck 	int rc;
15285c71ad17STony Luck 
15295c71ad17STony Luck 	edac_dbg(2, "\n");
15305c71ad17STony Luck 
1531*315bada6SJia He 	if (ghes_get_devices())
1532*315bada6SJia He 		return -EBUSY;
1533*315bada6SJia He 
1534301375e7SToshi Kani 	owner = edac_get_owner();
1535301375e7SToshi Kani 	if (owner && strncmp(owner, EDAC_MOD_STR, sizeof(EDAC_MOD_STR)))
1536301375e7SToshi Kani 		return -EBUSY;
1537301375e7SToshi Kani 
1538f0a029ffSLuck, Tony 	if (cpu_feature_enabled(X86_FEATURE_HYPERVISOR))
1539f0a029ffSLuck, Tony 		return -ENODEV;
1540f0a029ffSLuck, Tony 
15415c71ad17STony Luck 	id = x86_match_cpu(pnd2_cpuids);
15425c71ad17STony Luck 	if (!id)
15435c71ad17STony Luck 		return -ENODEV;
15445c71ad17STony Luck 
15455c71ad17STony Luck 	ops = (struct dunit_ops *)id->driver_data;
15465c71ad17STony Luck 
15473e5d2bd1STony Luck 	if (ops->type == APL) {
15483e5d2bd1STony Luck 		p2sb_bus = pci_find_bus(0, 0);
15493e5d2bd1STony Luck 		if (!p2sb_bus)
15503e5d2bd1STony Luck 			return -ENODEV;
15513e5d2bd1STony Luck 	}
15523e5d2bd1STony Luck 
15535c71ad17STony Luck 	/* Ensure that the OPSTATE is set correctly for POLL or NMI */
15545c71ad17STony Luck 	opstate_init();
15555c71ad17STony Luck 
15565c71ad17STony Luck 	rc = pnd2_probe();
15575c71ad17STony Luck 	if (rc < 0) {
15585c71ad17STony Luck 		pnd2_printk(KERN_ERR, "Failed to register device with error %d.\n", rc);
15595c71ad17STony Luck 		return rc;
15605c71ad17STony Luck 	}
15615c71ad17STony Luck 
15625c71ad17STony Luck 	if (!pnd2_mci)
15635c71ad17STony Luck 		return -ENODEV;
15645c71ad17STony Luck 
15655c71ad17STony Luck 	mce_register_decode_chain(&pnd2_mce_dec);
15665c71ad17STony Luck 	setup_pnd2_debug();
15675c71ad17STony Luck 
15685c71ad17STony Luck 	return 0;
15695c71ad17STony Luck }
15705c71ad17STony Luck 
pnd2_exit(void)15715c71ad17STony Luck static void __exit pnd2_exit(void)
15725c71ad17STony Luck {
15735c71ad17STony Luck 	edac_dbg(2, "\n");
15745c71ad17STony Luck 	teardown_pnd2_debug();
15755c71ad17STony Luck 	mce_unregister_decode_chain(&pnd2_mce_dec);
15765c71ad17STony Luck 	pnd2_remove();
15775c71ad17STony Luck }
15785c71ad17STony Luck 
15795c71ad17STony Luck module_init(pnd2_init);
15805c71ad17STony Luck module_exit(pnd2_exit);
15815c71ad17STony Luck 
15825c71ad17STony Luck module_param(edac_op_state, int, 0444);
15835c71ad17STony Luck MODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI");
15845c71ad17STony Luck 
15855c71ad17STony Luck MODULE_LICENSE("GPL v2");
15865c71ad17STony Luck MODULE_AUTHOR("Tony Luck");
15875c71ad17STony Luck MODULE_DESCRIPTION("MC Driver for Intel SoC using Pondicherry memory controller");
1588