162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * Cavium ThunderX memory controller kernel module
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public
562306a36Sopenharmony_ci * License.  See the file "COPYING" in the main directory of this archive
662306a36Sopenharmony_ci * for more details.
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * Copyright Cavium, Inc. (C) 2015-2017. All rights reserved.
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <linux/module.h>
1362306a36Sopenharmony_ci#include <linux/pci.h>
1462306a36Sopenharmony_ci#include <linux/edac.h>
1562306a36Sopenharmony_ci#include <linux/interrupt.h>
1662306a36Sopenharmony_ci#include <linux/string.h>
1762306a36Sopenharmony_ci#include <linux/stop_machine.h>
1862306a36Sopenharmony_ci#include <linux/delay.h>
1962306a36Sopenharmony_ci#include <linux/sizes.h>
2062306a36Sopenharmony_ci#include <linux/atomic.h>
2162306a36Sopenharmony_ci#include <linux/bitfield.h>
2262306a36Sopenharmony_ci#include <linux/circ_buf.h>
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#include <asm/page.h>
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#include "edac_module.h"
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#define phys_to_pfn(phys)	(PFN_DOWN(phys))
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci#define THUNDERX_NODE		GENMASK(45, 44)
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_cienum {
3362306a36Sopenharmony_ci	ERR_CORRECTED	= 1,
3462306a36Sopenharmony_ci	ERR_UNCORRECTED	= 2,
3562306a36Sopenharmony_ci	ERR_UNKNOWN	= 3,
3662306a36Sopenharmony_ci};
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci#define MAX_SYNDROME_REGS 4
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_cistruct error_syndrome {
4162306a36Sopenharmony_ci	u64 reg[MAX_SYNDROME_REGS];
4262306a36Sopenharmony_ci};
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cistruct error_descr {
4562306a36Sopenharmony_ci	int	type;
4662306a36Sopenharmony_ci	u64	mask;
4762306a36Sopenharmony_ci	char	*descr;
4862306a36Sopenharmony_ci};
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_cistatic void decode_register(char *str, size_t size,
5162306a36Sopenharmony_ci			   const struct error_descr *descr,
5262306a36Sopenharmony_ci			   const uint64_t reg)
5362306a36Sopenharmony_ci{
5462306a36Sopenharmony_ci	int ret = 0;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	while (descr->type && descr->mask && descr->descr) {
5762306a36Sopenharmony_ci		if (reg & descr->mask) {
5862306a36Sopenharmony_ci			ret = snprintf(str, size, "\n\t%s, %s",
5962306a36Sopenharmony_ci				       descr->type == ERR_CORRECTED ?
6062306a36Sopenharmony_ci					 "Corrected" : "Uncorrected",
6162306a36Sopenharmony_ci				       descr->descr);
6262306a36Sopenharmony_ci			str += ret;
6362306a36Sopenharmony_ci			size -= ret;
6462306a36Sopenharmony_ci		}
6562306a36Sopenharmony_ci		descr++;
6662306a36Sopenharmony_ci	}
6762306a36Sopenharmony_ci}
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_cistatic unsigned long get_bits(unsigned long data, int pos, int width)
7062306a36Sopenharmony_ci{
7162306a36Sopenharmony_ci	return (data >> pos) & ((1 << width) - 1);
7262306a36Sopenharmony_ci}
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci#define L2C_CTL			0x87E080800000
7562306a36Sopenharmony_ci#define L2C_CTL_DISIDXALIAS	BIT(0)
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci#define PCI_DEVICE_ID_THUNDER_LMC 0xa022
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci#define LMC_FADR		0x20
8062306a36Sopenharmony_ci#define LMC_FADR_FDIMM(x)	((x >> 37) & 0x1)
8162306a36Sopenharmony_ci#define LMC_FADR_FBUNK(x)	((x >> 36) & 0x1)
8262306a36Sopenharmony_ci#define LMC_FADR_FBANK(x)	((x >> 32) & 0xf)
8362306a36Sopenharmony_ci#define LMC_FADR_FROW(x)	((x >> 14) & 0xffff)
8462306a36Sopenharmony_ci#define LMC_FADR_FCOL(x)	((x >> 0) & 0x1fff)
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci#define LMC_NXM_FADR		0x28
8762306a36Sopenharmony_ci#define LMC_ECC_SYND		0x38
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci#define LMC_ECC_PARITY_TEST	0x108
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci#define LMC_INT_W1S		0x150
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci#define LMC_INT_ENA_W1C		0x158
9462306a36Sopenharmony_ci#define LMC_INT_ENA_W1S		0x160
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci#define LMC_CONFIG		0x188
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci#define LMC_CONFIG_BG2		BIT(62)
9962306a36Sopenharmony_ci#define LMC_CONFIG_RANK_ENA	BIT(42)
10062306a36Sopenharmony_ci#define LMC_CONFIG_PBANK_LSB(x)	(((x) >> 5) & 0xF)
10162306a36Sopenharmony_ci#define LMC_CONFIG_ROW_LSB(x)	(((x) >> 2) & 0x7)
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci#define LMC_CONTROL		0x190
10462306a36Sopenharmony_ci#define LMC_CONTROL_XOR_BANK	BIT(16)
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci#define LMC_INT			0x1F0
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci#define LMC_INT_DDR_ERR		BIT(11)
10962306a36Sopenharmony_ci#define LMC_INT_DED_ERR		(0xFUL << 5)
11062306a36Sopenharmony_ci#define LMC_INT_SEC_ERR         (0xFUL << 1)
11162306a36Sopenharmony_ci#define LMC_INT_NXM_WR_MASK	BIT(0)
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci#define LMC_DDR_PLL_CTL		0x258
11462306a36Sopenharmony_ci#define LMC_DDR_PLL_CTL_DDR4	BIT(29)
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci#define LMC_FADR_SCRAMBLED	0x330
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci#define LMC_INT_UE              (LMC_INT_DDR_ERR | LMC_INT_DED_ERR | \
11962306a36Sopenharmony_ci				 LMC_INT_NXM_WR_MASK)
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci#define LMC_INT_CE		(LMC_INT_SEC_ERR)
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_cistatic const struct error_descr lmc_errors[] = {
12462306a36Sopenharmony_ci	{
12562306a36Sopenharmony_ci		.type  = ERR_CORRECTED,
12662306a36Sopenharmony_ci		.mask  = LMC_INT_SEC_ERR,
12762306a36Sopenharmony_ci		.descr = "Single-bit ECC error",
12862306a36Sopenharmony_ci	},
12962306a36Sopenharmony_ci	{
13062306a36Sopenharmony_ci		.type  = ERR_UNCORRECTED,
13162306a36Sopenharmony_ci		.mask  = LMC_INT_DDR_ERR,
13262306a36Sopenharmony_ci		.descr = "DDR chip error",
13362306a36Sopenharmony_ci	},
13462306a36Sopenharmony_ci	{
13562306a36Sopenharmony_ci		.type  = ERR_UNCORRECTED,
13662306a36Sopenharmony_ci		.mask  = LMC_INT_DED_ERR,
13762306a36Sopenharmony_ci		.descr = "Double-bit ECC error",
13862306a36Sopenharmony_ci	},
13962306a36Sopenharmony_ci	{
14062306a36Sopenharmony_ci		.type = ERR_UNCORRECTED,
14162306a36Sopenharmony_ci		.mask = LMC_INT_NXM_WR_MASK,
14262306a36Sopenharmony_ci		.descr = "Non-existent memory write",
14362306a36Sopenharmony_ci	},
14462306a36Sopenharmony_ci	{0, 0, NULL},
14562306a36Sopenharmony_ci};
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci#define LMC_INT_EN_DDR_ERROR_ALERT_ENA	BIT(5)
14862306a36Sopenharmony_ci#define LMC_INT_EN_DLCRAM_DED_ERR	BIT(4)
14962306a36Sopenharmony_ci#define LMC_INT_EN_DLCRAM_SEC_ERR	BIT(3)
15062306a36Sopenharmony_ci#define LMC_INT_INTR_DED_ENA		BIT(2)
15162306a36Sopenharmony_ci#define LMC_INT_INTR_SEC_ENA		BIT(1)
15262306a36Sopenharmony_ci#define LMC_INT_INTR_NXM_WR_ENA		BIT(0)
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci#define LMC_INT_ENA_ALL			GENMASK(5, 0)
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci#define LMC_DDR_PLL_CTL		0x258
15762306a36Sopenharmony_ci#define LMC_DDR_PLL_CTL_DDR4	BIT(29)
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci#define LMC_CONTROL		0x190
16062306a36Sopenharmony_ci#define LMC_CONTROL_RDIMM	BIT(0)
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci#define LMC_SCRAM_FADR		0x330
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci#define LMC_CHAR_MASK0		0x228
16562306a36Sopenharmony_ci#define LMC_CHAR_MASK2		0x238
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci#define RING_ENTRIES	8
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_cistruct debugfs_entry {
17062306a36Sopenharmony_ci	const char *name;
17162306a36Sopenharmony_ci	umode_t mode;
17262306a36Sopenharmony_ci	const struct file_operations fops;
17362306a36Sopenharmony_ci};
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_cistruct lmc_err_ctx {
17662306a36Sopenharmony_ci	u64 reg_int;
17762306a36Sopenharmony_ci	u64 reg_fadr;
17862306a36Sopenharmony_ci	u64 reg_nxm_fadr;
17962306a36Sopenharmony_ci	u64 reg_scram_fadr;
18062306a36Sopenharmony_ci	u64 reg_ecc_synd;
18162306a36Sopenharmony_ci};
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_cistruct thunderx_lmc {
18462306a36Sopenharmony_ci	void __iomem *regs;
18562306a36Sopenharmony_ci	struct pci_dev *pdev;
18662306a36Sopenharmony_ci	struct msix_entry msix_ent;
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	atomic_t ecc_int;
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	u64 mask0;
19162306a36Sopenharmony_ci	u64 mask2;
19262306a36Sopenharmony_ci	u64 parity_test;
19362306a36Sopenharmony_ci	u64 node;
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	int xbits;
19662306a36Sopenharmony_ci	int bank_width;
19762306a36Sopenharmony_ci	int pbank_lsb;
19862306a36Sopenharmony_ci	int dimm_lsb;
19962306a36Sopenharmony_ci	int rank_lsb;
20062306a36Sopenharmony_ci	int bank_lsb;
20162306a36Sopenharmony_ci	int row_lsb;
20262306a36Sopenharmony_ci	int col_hi_lsb;
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	int xor_bank;
20562306a36Sopenharmony_ci	int l2c_alias;
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	struct page *mem;
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	struct lmc_err_ctx err_ctx[RING_ENTRIES];
21062306a36Sopenharmony_ci	unsigned long ring_head;
21162306a36Sopenharmony_ci	unsigned long ring_tail;
21262306a36Sopenharmony_ci};
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci#define ring_pos(pos, size) ((pos) & (size - 1))
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci#define DEBUGFS_STRUCT(_name, _mode, _write, _read)			    \
21762306a36Sopenharmony_cistatic struct debugfs_entry debugfs_##_name = {				    \
21862306a36Sopenharmony_ci	.name = __stringify(_name),					    \
21962306a36Sopenharmony_ci	.mode = VERIFY_OCTAL_PERMISSIONS(_mode),			    \
22062306a36Sopenharmony_ci	.fops = {							    \
22162306a36Sopenharmony_ci		.open = simple_open,					    \
22262306a36Sopenharmony_ci		.write = _write,					    \
22362306a36Sopenharmony_ci		.read  = _read,						    \
22462306a36Sopenharmony_ci		.llseek = generic_file_llseek,				    \
22562306a36Sopenharmony_ci	},								    \
22662306a36Sopenharmony_ci}
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci#define DEBUGFS_FIELD_ATTR(_type, _field)				    \
22962306a36Sopenharmony_cistatic ssize_t thunderx_##_type##_##_field##_read(struct file *file,	    \
23062306a36Sopenharmony_ci					    char __user *data,		    \
23162306a36Sopenharmony_ci					    size_t count, loff_t *ppos)	    \
23262306a36Sopenharmony_ci{									    \
23362306a36Sopenharmony_ci	struct thunderx_##_type *pdata = file->private_data;		    \
23462306a36Sopenharmony_ci	char buf[20];							    \
23562306a36Sopenharmony_ci									    \
23662306a36Sopenharmony_ci	snprintf(buf, count, "0x%016llx", pdata->_field);		    \
23762306a36Sopenharmony_ci	return simple_read_from_buffer(data, count, ppos,		    \
23862306a36Sopenharmony_ci				       buf, sizeof(buf));		    \
23962306a36Sopenharmony_ci}									    \
24062306a36Sopenharmony_ci									    \
24162306a36Sopenharmony_cistatic ssize_t thunderx_##_type##_##_field##_write(struct file *file,	    \
24262306a36Sopenharmony_ci					     const char __user *data,	    \
24362306a36Sopenharmony_ci					     size_t count, loff_t *ppos)    \
24462306a36Sopenharmony_ci{									    \
24562306a36Sopenharmony_ci	struct thunderx_##_type *pdata = file->private_data;		    \
24662306a36Sopenharmony_ci	int res;							    \
24762306a36Sopenharmony_ci									    \
24862306a36Sopenharmony_ci	res = kstrtoull_from_user(data, count, 0, &pdata->_field);	    \
24962306a36Sopenharmony_ci									    \
25062306a36Sopenharmony_ci	return res ? res : count;					    \
25162306a36Sopenharmony_ci}									    \
25262306a36Sopenharmony_ci									    \
25362306a36Sopenharmony_ciDEBUGFS_STRUCT(_field, 0600,						    \
25462306a36Sopenharmony_ci		   thunderx_##_type##_##_field##_write,			    \
25562306a36Sopenharmony_ci		   thunderx_##_type##_##_field##_read)			    \
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci#define DEBUGFS_REG_ATTR(_type, _name, _reg)				    \
25862306a36Sopenharmony_cistatic ssize_t thunderx_##_type##_##_name##_read(struct file *file,	    \
25962306a36Sopenharmony_ci					   char __user *data,		    \
26062306a36Sopenharmony_ci					   size_t count, loff_t *ppos)      \
26162306a36Sopenharmony_ci{									    \
26262306a36Sopenharmony_ci	struct thunderx_##_type *pdata = file->private_data;		    \
26362306a36Sopenharmony_ci	char buf[20];							    \
26462306a36Sopenharmony_ci									    \
26562306a36Sopenharmony_ci	sprintf(buf, "0x%016llx", readq(pdata->regs + _reg));		    \
26662306a36Sopenharmony_ci	return simple_read_from_buffer(data, count, ppos,		    \
26762306a36Sopenharmony_ci				       buf, sizeof(buf));		    \
26862306a36Sopenharmony_ci}									    \
26962306a36Sopenharmony_ci									    \
27062306a36Sopenharmony_cistatic ssize_t thunderx_##_type##_##_name##_write(struct file *file,	    \
27162306a36Sopenharmony_ci					    const char __user *data,	    \
27262306a36Sopenharmony_ci					    size_t count, loff_t *ppos)     \
27362306a36Sopenharmony_ci{									    \
27462306a36Sopenharmony_ci	struct thunderx_##_type *pdata = file->private_data;		    \
27562306a36Sopenharmony_ci	u64 val;							    \
27662306a36Sopenharmony_ci	int res;							    \
27762306a36Sopenharmony_ci									    \
27862306a36Sopenharmony_ci	res = kstrtoull_from_user(data, count, 0, &val);		    \
27962306a36Sopenharmony_ci									    \
28062306a36Sopenharmony_ci	if (!res) {							    \
28162306a36Sopenharmony_ci		writeq(val, pdata->regs + _reg);			    \
28262306a36Sopenharmony_ci		res = count;						    \
28362306a36Sopenharmony_ci	}								    \
28462306a36Sopenharmony_ci									    \
28562306a36Sopenharmony_ci	return res;							    \
28662306a36Sopenharmony_ci}									    \
28762306a36Sopenharmony_ci									    \
28862306a36Sopenharmony_ciDEBUGFS_STRUCT(_name, 0600,						    \
28962306a36Sopenharmony_ci	       thunderx_##_type##_##_name##_write,			    \
29062306a36Sopenharmony_ci	       thunderx_##_type##_##_name##_read)
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci#define LMC_DEBUGFS_ENT(_field)	DEBUGFS_FIELD_ATTR(lmc, _field)
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci/*
29562306a36Sopenharmony_ci * To get an ECC error injected, the following steps are needed:
29662306a36Sopenharmony_ci * - Setup the ECC injection by writing the appropriate parameters:
29762306a36Sopenharmony_ci *	echo <bit mask value> > /sys/kernel/debug/<device number>/ecc_mask0
29862306a36Sopenharmony_ci *	echo <bit mask value> > /sys/kernel/debug/<device number>/ecc_mask2
29962306a36Sopenharmony_ci *	echo 0x802 > /sys/kernel/debug/<device number>/ecc_parity_test
30062306a36Sopenharmony_ci * - Do the actual injection:
30162306a36Sopenharmony_ci *	echo 1 > /sys/kernel/debug/<device number>/inject_ecc
30262306a36Sopenharmony_ci */
30362306a36Sopenharmony_cistatic ssize_t thunderx_lmc_inject_int_write(struct file *file,
30462306a36Sopenharmony_ci					     const char __user *data,
30562306a36Sopenharmony_ci					     size_t count, loff_t *ppos)
30662306a36Sopenharmony_ci{
30762306a36Sopenharmony_ci	struct thunderx_lmc *lmc = file->private_data;
30862306a36Sopenharmony_ci	u64 val;
30962306a36Sopenharmony_ci	int res;
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	res = kstrtoull_from_user(data, count, 0, &val);
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	if (!res) {
31462306a36Sopenharmony_ci		/* Trigger the interrupt */
31562306a36Sopenharmony_ci		writeq(val, lmc->regs + LMC_INT_W1S);
31662306a36Sopenharmony_ci		res = count;
31762306a36Sopenharmony_ci	}
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	return res;
32062306a36Sopenharmony_ci}
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_cistatic ssize_t thunderx_lmc_int_read(struct file *file,
32362306a36Sopenharmony_ci				     char __user *data,
32462306a36Sopenharmony_ci				     size_t count, loff_t *ppos)
32562306a36Sopenharmony_ci{
32662306a36Sopenharmony_ci	struct thunderx_lmc *lmc = file->private_data;
32762306a36Sopenharmony_ci	char buf[20];
32862306a36Sopenharmony_ci	u64 lmc_int = readq(lmc->regs + LMC_INT);
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	snprintf(buf, sizeof(buf), "0x%016llx", lmc_int);
33162306a36Sopenharmony_ci	return simple_read_from_buffer(data, count, ppos, buf, sizeof(buf));
33262306a36Sopenharmony_ci}
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci#define TEST_PATTERN 0xa5
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_cistatic int inject_ecc_fn(void *arg)
33762306a36Sopenharmony_ci{
33862306a36Sopenharmony_ci	struct thunderx_lmc *lmc = arg;
33962306a36Sopenharmony_ci	uintptr_t addr, phys;
34062306a36Sopenharmony_ci	unsigned int cline_size = cache_line_size();
34162306a36Sopenharmony_ci	const unsigned int lines = PAGE_SIZE / cline_size;
34262306a36Sopenharmony_ci	unsigned int i, cl_idx;
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	addr = (uintptr_t)page_address(lmc->mem);
34562306a36Sopenharmony_ci	phys = (uintptr_t)page_to_phys(lmc->mem);
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	cl_idx = (phys & 0x7f) >> 4;
34862306a36Sopenharmony_ci	lmc->parity_test &= ~(7ULL << 8);
34962306a36Sopenharmony_ci	lmc->parity_test |= (cl_idx << 8);
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	writeq(lmc->mask0, lmc->regs + LMC_CHAR_MASK0);
35262306a36Sopenharmony_ci	writeq(lmc->mask2, lmc->regs + LMC_CHAR_MASK2);
35362306a36Sopenharmony_ci	writeq(lmc->parity_test, lmc->regs + LMC_ECC_PARITY_TEST);
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	readq(lmc->regs + LMC_CHAR_MASK0);
35662306a36Sopenharmony_ci	readq(lmc->regs + LMC_CHAR_MASK2);
35762306a36Sopenharmony_ci	readq(lmc->regs + LMC_ECC_PARITY_TEST);
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	for (i = 0; i < lines; i++) {
36062306a36Sopenharmony_ci		memset((void *)addr, TEST_PATTERN, cline_size);
36162306a36Sopenharmony_ci		barrier();
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci		/*
36462306a36Sopenharmony_ci		 * Flush L1 cachelines to the PoC (L2).
36562306a36Sopenharmony_ci		 * This will cause cacheline eviction to the L2.
36662306a36Sopenharmony_ci		 */
36762306a36Sopenharmony_ci		asm volatile("dc civac, %0\n"
36862306a36Sopenharmony_ci			     "dsb sy\n"
36962306a36Sopenharmony_ci			     : : "r"(addr + i * cline_size));
37062306a36Sopenharmony_ci	}
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	for (i = 0; i < lines; i++) {
37362306a36Sopenharmony_ci		/*
37462306a36Sopenharmony_ci		 * Flush L2 cachelines to the DRAM.
37562306a36Sopenharmony_ci		 * This will cause cacheline eviction to the DRAM
37662306a36Sopenharmony_ci		 * and ECC corruption according to the masks set.
37762306a36Sopenharmony_ci		 */
37862306a36Sopenharmony_ci		__asm__ volatile("sys #0,c11,C1,#2, %0\n"
37962306a36Sopenharmony_ci				 : : "r"(phys + i * cline_size));
38062306a36Sopenharmony_ci	}
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	for (i = 0; i < lines; i++) {
38362306a36Sopenharmony_ci		/*
38462306a36Sopenharmony_ci		 * Invalidate L2 cachelines.
38562306a36Sopenharmony_ci		 * The subsequent load will cause cacheline fetch
38662306a36Sopenharmony_ci		 * from the DRAM and an error interrupt
38762306a36Sopenharmony_ci		 */
38862306a36Sopenharmony_ci		__asm__ volatile("sys #0,c11,C1,#1, %0"
38962306a36Sopenharmony_ci				 : : "r"(phys + i * cline_size));
39062306a36Sopenharmony_ci	}
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	for (i = 0; i < lines; i++) {
39362306a36Sopenharmony_ci		/*
39462306a36Sopenharmony_ci		 * Invalidate L1 cachelines.
39562306a36Sopenharmony_ci		 * The subsequent load will cause cacheline fetch
39662306a36Sopenharmony_ci		 * from the L2 and/or DRAM
39762306a36Sopenharmony_ci		 */
39862306a36Sopenharmony_ci		asm volatile("dc ivac, %0\n"
39962306a36Sopenharmony_ci			     "dsb sy\n"
40062306a36Sopenharmony_ci			     : : "r"(addr + i * cline_size));
40162306a36Sopenharmony_ci	}
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	return 0;
40462306a36Sopenharmony_ci}
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_cistatic ssize_t thunderx_lmc_inject_ecc_write(struct file *file,
40762306a36Sopenharmony_ci					     const char __user *data,
40862306a36Sopenharmony_ci					     size_t count, loff_t *ppos)
40962306a36Sopenharmony_ci{
41062306a36Sopenharmony_ci	struct thunderx_lmc *lmc = file->private_data;
41162306a36Sopenharmony_ci	unsigned int cline_size = cache_line_size();
41262306a36Sopenharmony_ci	u8 *tmp;
41362306a36Sopenharmony_ci	void __iomem *addr;
41462306a36Sopenharmony_ci	unsigned int offs, timeout = 100000;
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci	atomic_set(&lmc->ecc_int, 0);
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	lmc->mem = alloc_pages_node(lmc->node, GFP_KERNEL, 0);
41962306a36Sopenharmony_ci	if (!lmc->mem)
42062306a36Sopenharmony_ci		return -ENOMEM;
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci	tmp = kmalloc(cline_size, GFP_KERNEL);
42362306a36Sopenharmony_ci	if (!tmp) {
42462306a36Sopenharmony_ci		__free_pages(lmc->mem, 0);
42562306a36Sopenharmony_ci		return -ENOMEM;
42662306a36Sopenharmony_ci	}
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	addr = page_address(lmc->mem);
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	while (!atomic_read(&lmc->ecc_int) && timeout--) {
43162306a36Sopenharmony_ci		stop_machine(inject_ecc_fn, lmc, NULL);
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci		for (offs = 0; offs < PAGE_SIZE; offs += cline_size) {
43462306a36Sopenharmony_ci			/*
43562306a36Sopenharmony_ci			 * Do a load from the previously rigged location
43662306a36Sopenharmony_ci			 * This should generate an error interrupt.
43762306a36Sopenharmony_ci			 */
43862306a36Sopenharmony_ci			memcpy(tmp, addr + offs, cline_size);
43962306a36Sopenharmony_ci			asm volatile("dsb ld\n");
44062306a36Sopenharmony_ci		}
44162306a36Sopenharmony_ci	}
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	kfree(tmp);
44462306a36Sopenharmony_ci	__free_pages(lmc->mem, 0);
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	return count;
44762306a36Sopenharmony_ci}
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ciLMC_DEBUGFS_ENT(mask0);
45062306a36Sopenharmony_ciLMC_DEBUGFS_ENT(mask2);
45162306a36Sopenharmony_ciLMC_DEBUGFS_ENT(parity_test);
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ciDEBUGFS_STRUCT(inject_int, 0200, thunderx_lmc_inject_int_write, NULL);
45462306a36Sopenharmony_ciDEBUGFS_STRUCT(inject_ecc, 0200, thunderx_lmc_inject_ecc_write, NULL);
45562306a36Sopenharmony_ciDEBUGFS_STRUCT(int_w1c, 0400, NULL, thunderx_lmc_int_read);
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_cistatic struct debugfs_entry *lmc_dfs_ents[] = {
45862306a36Sopenharmony_ci	&debugfs_mask0,
45962306a36Sopenharmony_ci	&debugfs_mask2,
46062306a36Sopenharmony_ci	&debugfs_parity_test,
46162306a36Sopenharmony_ci	&debugfs_inject_ecc,
46262306a36Sopenharmony_ci	&debugfs_inject_int,
46362306a36Sopenharmony_ci	&debugfs_int_w1c,
46462306a36Sopenharmony_ci};
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_cistatic int thunderx_create_debugfs_nodes(struct dentry *parent,
46762306a36Sopenharmony_ci					  struct debugfs_entry *attrs[],
46862306a36Sopenharmony_ci					  void *data,
46962306a36Sopenharmony_ci					  size_t num)
47062306a36Sopenharmony_ci{
47162306a36Sopenharmony_ci	int i;
47262306a36Sopenharmony_ci	struct dentry *ent;
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci	if (!IS_ENABLED(CONFIG_EDAC_DEBUG))
47562306a36Sopenharmony_ci		return 0;
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci	if (!parent)
47862306a36Sopenharmony_ci		return -ENOENT;
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	for (i = 0; i < num; i++) {
48162306a36Sopenharmony_ci		ent = edac_debugfs_create_file(attrs[i]->name, attrs[i]->mode,
48262306a36Sopenharmony_ci					       parent, data, &attrs[i]->fops);
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci		if (IS_ERR(ent))
48562306a36Sopenharmony_ci			break;
48662306a36Sopenharmony_ci	}
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci	return i;
48962306a36Sopenharmony_ci}
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_cistatic phys_addr_t thunderx_faddr_to_phys(u64 faddr, struct thunderx_lmc *lmc)
49262306a36Sopenharmony_ci{
49362306a36Sopenharmony_ci	phys_addr_t addr = 0;
49462306a36Sopenharmony_ci	int bank, xbits;
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	addr |= lmc->node << 40;
49762306a36Sopenharmony_ci	addr |= LMC_FADR_FDIMM(faddr) << lmc->dimm_lsb;
49862306a36Sopenharmony_ci	addr |= LMC_FADR_FBUNK(faddr) << lmc->rank_lsb;
49962306a36Sopenharmony_ci	addr |= LMC_FADR_FROW(faddr) << lmc->row_lsb;
50062306a36Sopenharmony_ci	addr |= (LMC_FADR_FCOL(faddr) >> 4) << lmc->col_hi_lsb;
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	bank = LMC_FADR_FBANK(faddr) << lmc->bank_lsb;
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci	if (lmc->xor_bank)
50562306a36Sopenharmony_ci		bank ^= get_bits(addr, 12 + lmc->xbits, lmc->bank_width);
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	addr |= bank << lmc->bank_lsb;
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	xbits = PCI_FUNC(lmc->pdev->devfn);
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci	if (lmc->l2c_alias)
51262306a36Sopenharmony_ci		xbits ^= get_bits(addr, 20, lmc->xbits) ^
51362306a36Sopenharmony_ci			 get_bits(addr, 12, lmc->xbits);
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	addr |= xbits << 7;
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	return addr;
51862306a36Sopenharmony_ci}
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_cistatic unsigned int thunderx_get_num_lmcs(unsigned int node)
52162306a36Sopenharmony_ci{
52262306a36Sopenharmony_ci	unsigned int number = 0;
52362306a36Sopenharmony_ci	struct pci_dev *pdev = NULL;
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	do {
52662306a36Sopenharmony_ci		pdev = pci_get_device(PCI_VENDOR_ID_CAVIUM,
52762306a36Sopenharmony_ci				      PCI_DEVICE_ID_THUNDER_LMC,
52862306a36Sopenharmony_ci				      pdev);
52962306a36Sopenharmony_ci		if (pdev) {
53062306a36Sopenharmony_ci#ifdef CONFIG_NUMA
53162306a36Sopenharmony_ci			if (pdev->dev.numa_node == node)
53262306a36Sopenharmony_ci				number++;
53362306a36Sopenharmony_ci#else
53462306a36Sopenharmony_ci			number++;
53562306a36Sopenharmony_ci#endif
53662306a36Sopenharmony_ci		}
53762306a36Sopenharmony_ci	} while (pdev);
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci	return number;
54062306a36Sopenharmony_ci}
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci#define LMC_MESSAGE_SIZE	120
54362306a36Sopenharmony_ci#define LMC_OTHER_SIZE		(50 * ARRAY_SIZE(lmc_errors))
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_cistatic irqreturn_t thunderx_lmc_err_isr(int irq, void *dev_id)
54662306a36Sopenharmony_ci{
54762306a36Sopenharmony_ci	struct mem_ctl_info *mci = dev_id;
54862306a36Sopenharmony_ci	struct thunderx_lmc *lmc = mci->pvt_info;
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci	unsigned long head = ring_pos(lmc->ring_head, ARRAY_SIZE(lmc->err_ctx));
55162306a36Sopenharmony_ci	struct lmc_err_ctx *ctx = &lmc->err_ctx[head];
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	writeq(0, lmc->regs + LMC_CHAR_MASK0);
55462306a36Sopenharmony_ci	writeq(0, lmc->regs + LMC_CHAR_MASK2);
55562306a36Sopenharmony_ci	writeq(0x2, lmc->regs + LMC_ECC_PARITY_TEST);
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci	ctx->reg_int = readq(lmc->regs + LMC_INT);
55862306a36Sopenharmony_ci	ctx->reg_fadr = readq(lmc->regs + LMC_FADR);
55962306a36Sopenharmony_ci	ctx->reg_nxm_fadr = readq(lmc->regs + LMC_NXM_FADR);
56062306a36Sopenharmony_ci	ctx->reg_scram_fadr = readq(lmc->regs + LMC_SCRAM_FADR);
56162306a36Sopenharmony_ci	ctx->reg_ecc_synd = readq(lmc->regs + LMC_ECC_SYND);
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci	lmc->ring_head++;
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci	atomic_set(&lmc->ecc_int, 1);
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci	/* Clear the interrupt */
56862306a36Sopenharmony_ci	writeq(ctx->reg_int, lmc->regs + LMC_INT);
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	return IRQ_WAKE_THREAD;
57162306a36Sopenharmony_ci}
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_cistatic irqreturn_t thunderx_lmc_threaded_isr(int irq, void *dev_id)
57462306a36Sopenharmony_ci{
57562306a36Sopenharmony_ci	struct mem_ctl_info *mci = dev_id;
57662306a36Sopenharmony_ci	struct thunderx_lmc *lmc = mci->pvt_info;
57762306a36Sopenharmony_ci	phys_addr_t phys_addr;
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci	unsigned long tail;
58062306a36Sopenharmony_ci	struct lmc_err_ctx *ctx;
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci	irqreturn_t ret = IRQ_NONE;
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ci	char *msg;
58562306a36Sopenharmony_ci	char *other;
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci	msg = kmalloc(LMC_MESSAGE_SIZE, GFP_KERNEL);
58862306a36Sopenharmony_ci	other =  kmalloc(LMC_OTHER_SIZE, GFP_KERNEL);
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci	if (!msg || !other)
59162306a36Sopenharmony_ci		goto err_free;
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci	while (CIRC_CNT(lmc->ring_head, lmc->ring_tail,
59462306a36Sopenharmony_ci		ARRAY_SIZE(lmc->err_ctx))) {
59562306a36Sopenharmony_ci		tail = ring_pos(lmc->ring_tail, ARRAY_SIZE(lmc->err_ctx));
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci		ctx = &lmc->err_ctx[tail];
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci		dev_dbg(&lmc->pdev->dev, "LMC_INT: %016llx\n",
60062306a36Sopenharmony_ci			ctx->reg_int);
60162306a36Sopenharmony_ci		dev_dbg(&lmc->pdev->dev, "LMC_FADR: %016llx\n",
60262306a36Sopenharmony_ci			ctx->reg_fadr);
60362306a36Sopenharmony_ci		dev_dbg(&lmc->pdev->dev, "LMC_NXM_FADR: %016llx\n",
60462306a36Sopenharmony_ci			ctx->reg_nxm_fadr);
60562306a36Sopenharmony_ci		dev_dbg(&lmc->pdev->dev, "LMC_SCRAM_FADR: %016llx\n",
60662306a36Sopenharmony_ci			ctx->reg_scram_fadr);
60762306a36Sopenharmony_ci		dev_dbg(&lmc->pdev->dev, "LMC_ECC_SYND: %016llx\n",
60862306a36Sopenharmony_ci			ctx->reg_ecc_synd);
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci		snprintf(msg, LMC_MESSAGE_SIZE,
61162306a36Sopenharmony_ci			 "DIMM %lld rank %lld bank %lld row %lld col %lld",
61262306a36Sopenharmony_ci			 LMC_FADR_FDIMM(ctx->reg_scram_fadr),
61362306a36Sopenharmony_ci			 LMC_FADR_FBUNK(ctx->reg_scram_fadr),
61462306a36Sopenharmony_ci			 LMC_FADR_FBANK(ctx->reg_scram_fadr),
61562306a36Sopenharmony_ci			 LMC_FADR_FROW(ctx->reg_scram_fadr),
61662306a36Sopenharmony_ci			 LMC_FADR_FCOL(ctx->reg_scram_fadr));
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ci		decode_register(other, LMC_OTHER_SIZE, lmc_errors,
61962306a36Sopenharmony_ci				ctx->reg_int);
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci		phys_addr = thunderx_faddr_to_phys(ctx->reg_fadr, lmc);
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_ci		if (ctx->reg_int & LMC_INT_UE)
62462306a36Sopenharmony_ci			edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1,
62562306a36Sopenharmony_ci					     phys_to_pfn(phys_addr),
62662306a36Sopenharmony_ci					     offset_in_page(phys_addr),
62762306a36Sopenharmony_ci					     0, -1, -1, -1, msg, other);
62862306a36Sopenharmony_ci		else if (ctx->reg_int & LMC_INT_CE)
62962306a36Sopenharmony_ci			edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1,
63062306a36Sopenharmony_ci					     phys_to_pfn(phys_addr),
63162306a36Sopenharmony_ci					     offset_in_page(phys_addr),
63262306a36Sopenharmony_ci					     0, -1, -1, -1, msg, other);
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci		lmc->ring_tail++;
63562306a36Sopenharmony_ci	}
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_ci	ret = IRQ_HANDLED;
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_cierr_free:
64062306a36Sopenharmony_ci	kfree(msg);
64162306a36Sopenharmony_ci	kfree(other);
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_ci	return ret;
64462306a36Sopenharmony_ci}
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_cistatic const struct pci_device_id thunderx_lmc_pci_tbl[] = {
64762306a36Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, PCI_DEVICE_ID_THUNDER_LMC) },
64862306a36Sopenharmony_ci	{ 0, },
64962306a36Sopenharmony_ci};
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_cistatic inline int pci_dev_to_mc_idx(struct pci_dev *pdev)
65262306a36Sopenharmony_ci{
65362306a36Sopenharmony_ci	int node = dev_to_node(&pdev->dev);
65462306a36Sopenharmony_ci	int ret = PCI_FUNC(pdev->devfn);
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ci	ret += max(node, 0) << 3;
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci	return ret;
65962306a36Sopenharmony_ci}
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_cistatic int thunderx_lmc_probe(struct pci_dev *pdev,
66262306a36Sopenharmony_ci				const struct pci_device_id *id)
66362306a36Sopenharmony_ci{
66462306a36Sopenharmony_ci	struct thunderx_lmc *lmc;
66562306a36Sopenharmony_ci	struct edac_mc_layer layer;
66662306a36Sopenharmony_ci	struct mem_ctl_info *mci;
66762306a36Sopenharmony_ci	u64 lmc_control, lmc_ddr_pll_ctl, lmc_config;
66862306a36Sopenharmony_ci	int ret;
66962306a36Sopenharmony_ci	u64 lmc_int;
67062306a36Sopenharmony_ci	void *l2c_ioaddr;
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci	layer.type = EDAC_MC_LAYER_SLOT;
67362306a36Sopenharmony_ci	layer.size = 2;
67462306a36Sopenharmony_ci	layer.is_virt_csrow = false;
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci	ret = pcim_enable_device(pdev);
67762306a36Sopenharmony_ci	if (ret) {
67862306a36Sopenharmony_ci		dev_err(&pdev->dev, "Cannot enable PCI device: %d\n", ret);
67962306a36Sopenharmony_ci		return ret;
68062306a36Sopenharmony_ci	}
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci	ret = pcim_iomap_regions(pdev, BIT(0), "thunderx_lmc");
68362306a36Sopenharmony_ci	if (ret) {
68462306a36Sopenharmony_ci		dev_err(&pdev->dev, "Cannot map PCI resources: %d\n", ret);
68562306a36Sopenharmony_ci		return ret;
68662306a36Sopenharmony_ci	}
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_ci	mci = edac_mc_alloc(pci_dev_to_mc_idx(pdev), 1, &layer,
68962306a36Sopenharmony_ci			    sizeof(struct thunderx_lmc));
69062306a36Sopenharmony_ci	if (!mci)
69162306a36Sopenharmony_ci		return -ENOMEM;
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci	mci->pdev = &pdev->dev;
69462306a36Sopenharmony_ci	lmc = mci->pvt_info;
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci	pci_set_drvdata(pdev, mci);
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci	lmc->regs = pcim_iomap_table(pdev)[0];
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_ci	lmc_control = readq(lmc->regs + LMC_CONTROL);
70162306a36Sopenharmony_ci	lmc_ddr_pll_ctl = readq(lmc->regs + LMC_DDR_PLL_CTL);
70262306a36Sopenharmony_ci	lmc_config = readq(lmc->regs + LMC_CONFIG);
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci	if (lmc_control & LMC_CONTROL_RDIMM) {
70562306a36Sopenharmony_ci		mci->mtype_cap = FIELD_GET(LMC_DDR_PLL_CTL_DDR4,
70662306a36Sopenharmony_ci					   lmc_ddr_pll_ctl) ?
70762306a36Sopenharmony_ci				MEM_RDDR4 : MEM_RDDR3;
70862306a36Sopenharmony_ci	} else {
70962306a36Sopenharmony_ci		mci->mtype_cap = FIELD_GET(LMC_DDR_PLL_CTL_DDR4,
71062306a36Sopenharmony_ci					   lmc_ddr_pll_ctl) ?
71162306a36Sopenharmony_ci				MEM_DDR4 : MEM_DDR3;
71262306a36Sopenharmony_ci	}
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci	mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED;
71562306a36Sopenharmony_ci	mci->edac_cap = EDAC_FLAG_SECDED;
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_ci	mci->mod_name = "thunderx-lmc";
71862306a36Sopenharmony_ci	mci->ctl_name = "thunderx-lmc";
71962306a36Sopenharmony_ci	mci->dev_name = dev_name(&pdev->dev);
72062306a36Sopenharmony_ci	mci->scrub_mode = SCRUB_NONE;
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_ci	lmc->pdev = pdev;
72362306a36Sopenharmony_ci	lmc->msix_ent.entry = 0;
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_ci	lmc->ring_head = 0;
72662306a36Sopenharmony_ci	lmc->ring_tail = 0;
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci	ret = pci_enable_msix_exact(pdev, &lmc->msix_ent, 1);
72962306a36Sopenharmony_ci	if (ret) {
73062306a36Sopenharmony_ci		dev_err(&pdev->dev, "Cannot enable interrupt: %d\n", ret);
73162306a36Sopenharmony_ci		goto err_free;
73262306a36Sopenharmony_ci	}
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_ci	ret = devm_request_threaded_irq(&pdev->dev, lmc->msix_ent.vector,
73562306a36Sopenharmony_ci					thunderx_lmc_err_isr,
73662306a36Sopenharmony_ci					thunderx_lmc_threaded_isr, 0,
73762306a36Sopenharmony_ci					"[EDAC] ThunderX LMC", mci);
73862306a36Sopenharmony_ci	if (ret) {
73962306a36Sopenharmony_ci		dev_err(&pdev->dev, "Cannot set ISR: %d\n", ret);
74062306a36Sopenharmony_ci		goto err_free;
74162306a36Sopenharmony_ci	}
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_ci	lmc->node = FIELD_GET(THUNDERX_NODE, pci_resource_start(pdev, 0));
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_ci	lmc->xbits = thunderx_get_num_lmcs(lmc->node) >> 1;
74662306a36Sopenharmony_ci	lmc->bank_width = (FIELD_GET(LMC_DDR_PLL_CTL_DDR4, lmc_ddr_pll_ctl) &&
74762306a36Sopenharmony_ci			   FIELD_GET(LMC_CONFIG_BG2, lmc_config)) ? 4 : 3;
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_ci	lmc->pbank_lsb = (lmc_config >> 5) & 0xf;
75062306a36Sopenharmony_ci	lmc->dimm_lsb  = 28 + lmc->pbank_lsb + lmc->xbits;
75162306a36Sopenharmony_ci	lmc->rank_lsb = lmc->dimm_lsb;
75262306a36Sopenharmony_ci	lmc->rank_lsb -= FIELD_GET(LMC_CONFIG_RANK_ENA, lmc_config) ? 1 : 0;
75362306a36Sopenharmony_ci	lmc->bank_lsb = 7 + lmc->xbits;
75462306a36Sopenharmony_ci	lmc->row_lsb = 14 + LMC_CONFIG_ROW_LSB(lmc_config) + lmc->xbits;
75562306a36Sopenharmony_ci
75662306a36Sopenharmony_ci	lmc->col_hi_lsb = lmc->bank_lsb + lmc->bank_width;
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_ci	lmc->xor_bank = lmc_control & LMC_CONTROL_XOR_BANK;
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_ci	l2c_ioaddr = ioremap(L2C_CTL | FIELD_PREP(THUNDERX_NODE, lmc->node), PAGE_SIZE);
76162306a36Sopenharmony_ci	if (!l2c_ioaddr) {
76262306a36Sopenharmony_ci		dev_err(&pdev->dev, "Cannot map L2C_CTL\n");
76362306a36Sopenharmony_ci		ret = -ENOMEM;
76462306a36Sopenharmony_ci		goto err_free;
76562306a36Sopenharmony_ci	}
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_ci	lmc->l2c_alias = !(readq(l2c_ioaddr) & L2C_CTL_DISIDXALIAS);
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_ci	iounmap(l2c_ioaddr);
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_ci	ret = edac_mc_add_mc(mci);
77262306a36Sopenharmony_ci	if (ret) {
77362306a36Sopenharmony_ci		dev_err(&pdev->dev, "Cannot add the MC: %d\n", ret);
77462306a36Sopenharmony_ci		goto err_free;
77562306a36Sopenharmony_ci	}
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_ci	lmc_int = readq(lmc->regs + LMC_INT);
77862306a36Sopenharmony_ci	writeq(lmc_int, lmc->regs + LMC_INT);
77962306a36Sopenharmony_ci
78062306a36Sopenharmony_ci	writeq(LMC_INT_ENA_ALL, lmc->regs + LMC_INT_ENA_W1S);
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ci	if (IS_ENABLED(CONFIG_EDAC_DEBUG)) {
78362306a36Sopenharmony_ci		ret = thunderx_create_debugfs_nodes(mci->debugfs,
78462306a36Sopenharmony_ci						    lmc_dfs_ents,
78562306a36Sopenharmony_ci						    lmc,
78662306a36Sopenharmony_ci						    ARRAY_SIZE(lmc_dfs_ents));
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_ci		if (ret != ARRAY_SIZE(lmc_dfs_ents)) {
78962306a36Sopenharmony_ci			dev_warn(&pdev->dev, "Error creating debugfs entries: %d%s\n",
79062306a36Sopenharmony_ci				 ret, ret >= 0 ? " created" : "");
79162306a36Sopenharmony_ci		}
79262306a36Sopenharmony_ci	}
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci	return 0;
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_cierr_free:
79762306a36Sopenharmony_ci	pci_set_drvdata(pdev, NULL);
79862306a36Sopenharmony_ci	edac_mc_free(mci);
79962306a36Sopenharmony_ci
80062306a36Sopenharmony_ci	return ret;
80162306a36Sopenharmony_ci}
80262306a36Sopenharmony_ci
80362306a36Sopenharmony_cistatic void thunderx_lmc_remove(struct pci_dev *pdev)
80462306a36Sopenharmony_ci{
80562306a36Sopenharmony_ci	struct mem_ctl_info *mci = pci_get_drvdata(pdev);
80662306a36Sopenharmony_ci	struct thunderx_lmc *lmc = mci->pvt_info;
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_ci	writeq(LMC_INT_ENA_ALL, lmc->regs + LMC_INT_ENA_W1C);
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ci	edac_mc_del_mc(&pdev->dev);
81162306a36Sopenharmony_ci	edac_mc_free(mci);
81262306a36Sopenharmony_ci}
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, thunderx_lmc_pci_tbl);
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_cistatic struct pci_driver thunderx_lmc_driver = {
81762306a36Sopenharmony_ci	.name     = "thunderx_lmc_edac",
81862306a36Sopenharmony_ci	.probe    = thunderx_lmc_probe,
81962306a36Sopenharmony_ci	.remove   = thunderx_lmc_remove,
82062306a36Sopenharmony_ci	.id_table = thunderx_lmc_pci_tbl,
82162306a36Sopenharmony_ci};
82262306a36Sopenharmony_ci
82362306a36Sopenharmony_ci/*---------------------- OCX driver ---------------------------------*/
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_ci#define PCI_DEVICE_ID_THUNDER_OCX 0xa013
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_ci#define OCX_LINK_INTS		3
82862306a36Sopenharmony_ci#define OCX_INTS		(OCX_LINK_INTS + 1)
82962306a36Sopenharmony_ci#define OCX_RX_LANES		24
83062306a36Sopenharmony_ci#define OCX_RX_LANE_STATS	15
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_ci#define OCX_COM_INT		0x100
83362306a36Sopenharmony_ci#define OCX_COM_INT_W1S		0x108
83462306a36Sopenharmony_ci#define OCX_COM_INT_ENA_W1S	0x110
83562306a36Sopenharmony_ci#define OCX_COM_INT_ENA_W1C	0x118
83662306a36Sopenharmony_ci
83762306a36Sopenharmony_ci#define OCX_COM_IO_BADID		BIT(54)
83862306a36Sopenharmony_ci#define OCX_COM_MEM_BADID		BIT(53)
83962306a36Sopenharmony_ci#define OCX_COM_COPR_BADID		BIT(52)
84062306a36Sopenharmony_ci#define OCX_COM_WIN_REQ_BADID		BIT(51)
84162306a36Sopenharmony_ci#define OCX_COM_WIN_REQ_TOUT		BIT(50)
84262306a36Sopenharmony_ci#define OCX_COM_RX_LANE			GENMASK(23, 0)
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_ci#define OCX_COM_INT_CE			(OCX_COM_IO_BADID      | \
84562306a36Sopenharmony_ci					 OCX_COM_MEM_BADID     | \
84662306a36Sopenharmony_ci					 OCX_COM_COPR_BADID    | \
84762306a36Sopenharmony_ci					 OCX_COM_WIN_REQ_BADID | \
84862306a36Sopenharmony_ci					 OCX_COM_WIN_REQ_TOUT)
84962306a36Sopenharmony_ci
85062306a36Sopenharmony_cistatic const struct error_descr ocx_com_errors[] = {
85162306a36Sopenharmony_ci	{
85262306a36Sopenharmony_ci		.type  = ERR_CORRECTED,
85362306a36Sopenharmony_ci		.mask  = OCX_COM_IO_BADID,
85462306a36Sopenharmony_ci		.descr = "Invalid IO transaction node ID",
85562306a36Sopenharmony_ci	},
85662306a36Sopenharmony_ci	{
85762306a36Sopenharmony_ci		.type  = ERR_CORRECTED,
85862306a36Sopenharmony_ci		.mask  = OCX_COM_MEM_BADID,
85962306a36Sopenharmony_ci		.descr = "Invalid memory transaction node ID",
86062306a36Sopenharmony_ci	},
86162306a36Sopenharmony_ci	{
86262306a36Sopenharmony_ci		.type  = ERR_CORRECTED,
86362306a36Sopenharmony_ci		.mask  = OCX_COM_COPR_BADID,
86462306a36Sopenharmony_ci		.descr = "Invalid coprocessor transaction node ID",
86562306a36Sopenharmony_ci	},
86662306a36Sopenharmony_ci	{
86762306a36Sopenharmony_ci		.type  = ERR_CORRECTED,
86862306a36Sopenharmony_ci		.mask  = OCX_COM_WIN_REQ_BADID,
86962306a36Sopenharmony_ci		.descr = "Invalid SLI transaction node ID",
87062306a36Sopenharmony_ci	},
87162306a36Sopenharmony_ci	{
87262306a36Sopenharmony_ci		.type  = ERR_CORRECTED,
87362306a36Sopenharmony_ci		.mask  = OCX_COM_WIN_REQ_TOUT,
87462306a36Sopenharmony_ci		.descr = "Window/core request timeout",
87562306a36Sopenharmony_ci	},
87662306a36Sopenharmony_ci	{0, 0, NULL},
87762306a36Sopenharmony_ci};
87862306a36Sopenharmony_ci
87962306a36Sopenharmony_ci#define OCX_COM_LINKX_INT(x)		(0x120 + (x) * 8)
88062306a36Sopenharmony_ci#define OCX_COM_LINKX_INT_W1S(x)	(0x140 + (x) * 8)
88162306a36Sopenharmony_ci#define OCX_COM_LINKX_INT_ENA_W1S(x)	(0x160 + (x) * 8)
88262306a36Sopenharmony_ci#define OCX_COM_LINKX_INT_ENA_W1C(x)	(0x180 + (x) * 8)
88362306a36Sopenharmony_ci
88462306a36Sopenharmony_ci#define OCX_COM_LINK_BAD_WORD			BIT(13)
88562306a36Sopenharmony_ci#define OCX_COM_LINK_ALIGN_FAIL			BIT(12)
88662306a36Sopenharmony_ci#define OCX_COM_LINK_ALIGN_DONE			BIT(11)
88762306a36Sopenharmony_ci#define OCX_COM_LINK_UP				BIT(10)
88862306a36Sopenharmony_ci#define OCX_COM_LINK_STOP			BIT(9)
88962306a36Sopenharmony_ci#define OCX_COM_LINK_BLK_ERR			BIT(8)
89062306a36Sopenharmony_ci#define OCX_COM_LINK_REINIT			BIT(7)
89162306a36Sopenharmony_ci#define OCX_COM_LINK_LNK_DATA			BIT(6)
89262306a36Sopenharmony_ci#define OCX_COM_LINK_RXFIFO_DBE			BIT(5)
89362306a36Sopenharmony_ci#define OCX_COM_LINK_RXFIFO_SBE			BIT(4)
89462306a36Sopenharmony_ci#define OCX_COM_LINK_TXFIFO_DBE			BIT(3)
89562306a36Sopenharmony_ci#define OCX_COM_LINK_TXFIFO_SBE			BIT(2)
89662306a36Sopenharmony_ci#define OCX_COM_LINK_REPLAY_DBE			BIT(1)
89762306a36Sopenharmony_ci#define OCX_COM_LINK_REPLAY_SBE			BIT(0)
89862306a36Sopenharmony_ci
89962306a36Sopenharmony_cistatic const struct error_descr ocx_com_link_errors[] = {
90062306a36Sopenharmony_ci	{
90162306a36Sopenharmony_ci		.type  = ERR_CORRECTED,
90262306a36Sopenharmony_ci		.mask  = OCX_COM_LINK_REPLAY_SBE,
90362306a36Sopenharmony_ci		.descr = "Replay buffer single-bit error",
90462306a36Sopenharmony_ci	},
90562306a36Sopenharmony_ci	{
90662306a36Sopenharmony_ci		.type  = ERR_CORRECTED,
90762306a36Sopenharmony_ci		.mask  = OCX_COM_LINK_TXFIFO_SBE,
90862306a36Sopenharmony_ci		.descr = "TX FIFO single-bit error",
90962306a36Sopenharmony_ci	},
91062306a36Sopenharmony_ci	{
91162306a36Sopenharmony_ci		.type  = ERR_CORRECTED,
91262306a36Sopenharmony_ci		.mask  = OCX_COM_LINK_RXFIFO_SBE,
91362306a36Sopenharmony_ci		.descr = "RX FIFO single-bit error",
91462306a36Sopenharmony_ci	},
91562306a36Sopenharmony_ci	{
91662306a36Sopenharmony_ci		.type  = ERR_CORRECTED,
91762306a36Sopenharmony_ci		.mask  = OCX_COM_LINK_BLK_ERR,
91862306a36Sopenharmony_ci		.descr = "Block code error",
91962306a36Sopenharmony_ci	},
92062306a36Sopenharmony_ci	{
92162306a36Sopenharmony_ci		.type  = ERR_CORRECTED,
92262306a36Sopenharmony_ci		.mask  = OCX_COM_LINK_ALIGN_FAIL,
92362306a36Sopenharmony_ci		.descr = "Link alignment failure",
92462306a36Sopenharmony_ci	},
92562306a36Sopenharmony_ci	{
92662306a36Sopenharmony_ci		.type  = ERR_CORRECTED,
92762306a36Sopenharmony_ci		.mask  = OCX_COM_LINK_BAD_WORD,
92862306a36Sopenharmony_ci		.descr = "Bad code word",
92962306a36Sopenharmony_ci	},
93062306a36Sopenharmony_ci	{
93162306a36Sopenharmony_ci		.type  = ERR_UNCORRECTED,
93262306a36Sopenharmony_ci		.mask  = OCX_COM_LINK_REPLAY_DBE,
93362306a36Sopenharmony_ci		.descr = "Replay buffer double-bit error",
93462306a36Sopenharmony_ci	},
93562306a36Sopenharmony_ci	{
93662306a36Sopenharmony_ci		.type  = ERR_UNCORRECTED,
93762306a36Sopenharmony_ci		.mask  = OCX_COM_LINK_TXFIFO_DBE,
93862306a36Sopenharmony_ci		.descr = "TX FIFO double-bit error",
93962306a36Sopenharmony_ci	},
94062306a36Sopenharmony_ci	{
94162306a36Sopenharmony_ci		.type  = ERR_UNCORRECTED,
94262306a36Sopenharmony_ci		.mask  = OCX_COM_LINK_RXFIFO_DBE,
94362306a36Sopenharmony_ci		.descr = "RX FIFO double-bit error",
94462306a36Sopenharmony_ci	},
94562306a36Sopenharmony_ci	{
94662306a36Sopenharmony_ci		.type  = ERR_UNCORRECTED,
94762306a36Sopenharmony_ci		.mask  = OCX_COM_LINK_STOP,
94862306a36Sopenharmony_ci		.descr = "Link stopped",
94962306a36Sopenharmony_ci	},
95062306a36Sopenharmony_ci	{0, 0, NULL},
95162306a36Sopenharmony_ci};
95262306a36Sopenharmony_ci
95362306a36Sopenharmony_ci#define OCX_COM_LINK_INT_UE       (OCX_COM_LINK_REPLAY_DBE | \
95462306a36Sopenharmony_ci				   OCX_COM_LINK_TXFIFO_DBE | \
95562306a36Sopenharmony_ci				   OCX_COM_LINK_RXFIFO_DBE | \
95662306a36Sopenharmony_ci				   OCX_COM_LINK_STOP)
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_ci#define OCX_COM_LINK_INT_CE       (OCX_COM_LINK_REPLAY_SBE | \
95962306a36Sopenharmony_ci				   OCX_COM_LINK_TXFIFO_SBE | \
96062306a36Sopenharmony_ci				   OCX_COM_LINK_RXFIFO_SBE | \
96162306a36Sopenharmony_ci				   OCX_COM_LINK_BLK_ERR    | \
96262306a36Sopenharmony_ci				   OCX_COM_LINK_ALIGN_FAIL | \
96362306a36Sopenharmony_ci				   OCX_COM_LINK_BAD_WORD)
96462306a36Sopenharmony_ci
96562306a36Sopenharmony_ci#define OCX_LNE_INT(x)			(0x8018 + (x) * 0x100)
96662306a36Sopenharmony_ci#define OCX_LNE_INT_EN(x)		(0x8020 + (x) * 0x100)
96762306a36Sopenharmony_ci#define OCX_LNE_BAD_CNT(x)		(0x8028 + (x) * 0x100)
96862306a36Sopenharmony_ci#define OCX_LNE_CFG(x)			(0x8000 + (x) * 0x100)
96962306a36Sopenharmony_ci#define OCX_LNE_STAT(x, y)		(0x8040 + (x) * 0x100 + (y) * 8)
97062306a36Sopenharmony_ci
97162306a36Sopenharmony_ci#define OCX_LNE_CFG_RX_BDRY_LOCK_DIS		BIT(8)
97262306a36Sopenharmony_ci#define OCX_LNE_CFG_RX_STAT_WRAP_DIS		BIT(2)
97362306a36Sopenharmony_ci#define OCX_LNE_CFG_RX_STAT_RDCLR		BIT(1)
97462306a36Sopenharmony_ci#define OCX_LNE_CFG_RX_STAT_ENA			BIT(0)
97562306a36Sopenharmony_ci
97662306a36Sopenharmony_ci
97762306a36Sopenharmony_ci#define OCX_LANE_BAD_64B67B			BIT(8)
97862306a36Sopenharmony_ci#define OCX_LANE_DSKEW_FIFO_OVFL		BIT(5)
97962306a36Sopenharmony_ci#define OCX_LANE_SCRM_SYNC_LOSS			BIT(4)
98062306a36Sopenharmony_ci#define OCX_LANE_UKWN_CNTL_WORD			BIT(3)
98162306a36Sopenharmony_ci#define OCX_LANE_CRC32_ERR			BIT(2)
98262306a36Sopenharmony_ci#define OCX_LANE_BDRY_SYNC_LOSS			BIT(1)
98362306a36Sopenharmony_ci#define OCX_LANE_SERDES_LOCK_LOSS		BIT(0)
98462306a36Sopenharmony_ci
98562306a36Sopenharmony_ci#define OCX_COM_LANE_INT_UE       (0)
98662306a36Sopenharmony_ci#define OCX_COM_LANE_INT_CE       (OCX_LANE_SERDES_LOCK_LOSS | \
98762306a36Sopenharmony_ci				   OCX_LANE_BDRY_SYNC_LOSS   | \
98862306a36Sopenharmony_ci				   OCX_LANE_CRC32_ERR        | \
98962306a36Sopenharmony_ci				   OCX_LANE_UKWN_CNTL_WORD   | \
99062306a36Sopenharmony_ci				   OCX_LANE_SCRM_SYNC_LOSS   | \
99162306a36Sopenharmony_ci				   OCX_LANE_DSKEW_FIFO_OVFL  | \
99262306a36Sopenharmony_ci				   OCX_LANE_BAD_64B67B)
99362306a36Sopenharmony_ci
99462306a36Sopenharmony_cistatic const struct error_descr ocx_lane_errors[] = {
99562306a36Sopenharmony_ci	{
99662306a36Sopenharmony_ci		.type  = ERR_CORRECTED,
99762306a36Sopenharmony_ci		.mask  = OCX_LANE_SERDES_LOCK_LOSS,
99862306a36Sopenharmony_ci		.descr = "RX SerDes lock lost",
99962306a36Sopenharmony_ci	},
100062306a36Sopenharmony_ci	{
100162306a36Sopenharmony_ci		.type  = ERR_CORRECTED,
100262306a36Sopenharmony_ci		.mask  = OCX_LANE_BDRY_SYNC_LOSS,
100362306a36Sopenharmony_ci		.descr = "RX word boundary lost",
100462306a36Sopenharmony_ci	},
100562306a36Sopenharmony_ci	{
100662306a36Sopenharmony_ci		.type  = ERR_CORRECTED,
100762306a36Sopenharmony_ci		.mask  = OCX_LANE_CRC32_ERR,
100862306a36Sopenharmony_ci		.descr = "CRC32 error",
100962306a36Sopenharmony_ci	},
101062306a36Sopenharmony_ci	{
101162306a36Sopenharmony_ci		.type  = ERR_CORRECTED,
101262306a36Sopenharmony_ci		.mask  = OCX_LANE_UKWN_CNTL_WORD,
101362306a36Sopenharmony_ci		.descr = "Unknown control word",
101462306a36Sopenharmony_ci	},
101562306a36Sopenharmony_ci	{
101662306a36Sopenharmony_ci		.type  = ERR_CORRECTED,
101762306a36Sopenharmony_ci		.mask  = OCX_LANE_SCRM_SYNC_LOSS,
101862306a36Sopenharmony_ci		.descr = "Scrambler synchronization lost",
101962306a36Sopenharmony_ci	},
102062306a36Sopenharmony_ci	{
102162306a36Sopenharmony_ci		.type  = ERR_CORRECTED,
102262306a36Sopenharmony_ci		.mask  = OCX_LANE_DSKEW_FIFO_OVFL,
102362306a36Sopenharmony_ci		.descr = "RX deskew FIFO overflow",
102462306a36Sopenharmony_ci	},
102562306a36Sopenharmony_ci	{
102662306a36Sopenharmony_ci		.type  = ERR_CORRECTED,
102762306a36Sopenharmony_ci		.mask  = OCX_LANE_BAD_64B67B,
102862306a36Sopenharmony_ci		.descr = "Bad 64B/67B codeword",
102962306a36Sopenharmony_ci	},
103062306a36Sopenharmony_ci	{0, 0, NULL},
103162306a36Sopenharmony_ci};
103262306a36Sopenharmony_ci
103362306a36Sopenharmony_ci#define OCX_LNE_INT_ENA_ALL		(GENMASK(9, 8) | GENMASK(6, 0))
103462306a36Sopenharmony_ci#define OCX_COM_INT_ENA_ALL		(GENMASK(54, 50) | GENMASK(23, 0))
103562306a36Sopenharmony_ci#define OCX_COM_LINKX_INT_ENA_ALL	(GENMASK(13, 12) | \
103662306a36Sopenharmony_ci					 GENMASK(9, 7) | GENMASK(5, 0))
103762306a36Sopenharmony_ci
103862306a36Sopenharmony_ci#define OCX_TLKX_ECC_CTL(x)		(0x10018 + (x) * 0x2000)
103962306a36Sopenharmony_ci#define OCX_RLKX_ECC_CTL(x)		(0x18018 + (x) * 0x2000)
104062306a36Sopenharmony_ci
104162306a36Sopenharmony_cistruct ocx_com_err_ctx {
104262306a36Sopenharmony_ci	u64 reg_com_int;
104362306a36Sopenharmony_ci	u64 reg_lane_int[OCX_RX_LANES];
104462306a36Sopenharmony_ci	u64 reg_lane_stat11[OCX_RX_LANES];
104562306a36Sopenharmony_ci};
104662306a36Sopenharmony_ci
104762306a36Sopenharmony_cistruct ocx_link_err_ctx {
104862306a36Sopenharmony_ci	u64 reg_com_link_int;
104962306a36Sopenharmony_ci	int link;
105062306a36Sopenharmony_ci};
105162306a36Sopenharmony_ci
105262306a36Sopenharmony_cistruct thunderx_ocx {
105362306a36Sopenharmony_ci	void __iomem *regs;
105462306a36Sopenharmony_ci	int com_link;
105562306a36Sopenharmony_ci	struct pci_dev *pdev;
105662306a36Sopenharmony_ci	struct edac_device_ctl_info *edac_dev;
105762306a36Sopenharmony_ci
105862306a36Sopenharmony_ci	struct dentry *debugfs;
105962306a36Sopenharmony_ci	struct msix_entry msix_ent[OCX_INTS];
106062306a36Sopenharmony_ci
106162306a36Sopenharmony_ci	struct ocx_com_err_ctx com_err_ctx[RING_ENTRIES];
106262306a36Sopenharmony_ci	struct ocx_link_err_ctx link_err_ctx[RING_ENTRIES];
106362306a36Sopenharmony_ci
106462306a36Sopenharmony_ci	unsigned long com_ring_head;
106562306a36Sopenharmony_ci	unsigned long com_ring_tail;
106662306a36Sopenharmony_ci
106762306a36Sopenharmony_ci	unsigned long link_ring_head;
106862306a36Sopenharmony_ci	unsigned long link_ring_tail;
106962306a36Sopenharmony_ci};
107062306a36Sopenharmony_ci
107162306a36Sopenharmony_ci#define OCX_MESSAGE_SIZE	SZ_1K
107262306a36Sopenharmony_ci#define OCX_OTHER_SIZE		(50 * ARRAY_SIZE(ocx_com_link_errors))
107362306a36Sopenharmony_ci
107462306a36Sopenharmony_ci/* This handler is threaded */
107562306a36Sopenharmony_cistatic irqreturn_t thunderx_ocx_com_isr(int irq, void *irq_id)
107662306a36Sopenharmony_ci{
107762306a36Sopenharmony_ci	struct msix_entry *msix = irq_id;
107862306a36Sopenharmony_ci	struct thunderx_ocx *ocx = container_of(msix, struct thunderx_ocx,
107962306a36Sopenharmony_ci						msix_ent[msix->entry]);
108062306a36Sopenharmony_ci
108162306a36Sopenharmony_ci	int lane;
108262306a36Sopenharmony_ci	unsigned long head = ring_pos(ocx->com_ring_head,
108362306a36Sopenharmony_ci				      ARRAY_SIZE(ocx->com_err_ctx));
108462306a36Sopenharmony_ci	struct ocx_com_err_ctx *ctx = &ocx->com_err_ctx[head];
108562306a36Sopenharmony_ci
108662306a36Sopenharmony_ci	ctx->reg_com_int = readq(ocx->regs + OCX_COM_INT);
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_ci	for (lane = 0; lane < OCX_RX_LANES; lane++) {
108962306a36Sopenharmony_ci		ctx->reg_lane_int[lane] =
109062306a36Sopenharmony_ci			readq(ocx->regs + OCX_LNE_INT(lane));
109162306a36Sopenharmony_ci		ctx->reg_lane_stat11[lane] =
109262306a36Sopenharmony_ci			readq(ocx->regs + OCX_LNE_STAT(lane, 11));
109362306a36Sopenharmony_ci
109462306a36Sopenharmony_ci		writeq(ctx->reg_lane_int[lane], ocx->regs + OCX_LNE_INT(lane));
109562306a36Sopenharmony_ci	}
109662306a36Sopenharmony_ci
109762306a36Sopenharmony_ci	writeq(ctx->reg_com_int, ocx->regs + OCX_COM_INT);
109862306a36Sopenharmony_ci
109962306a36Sopenharmony_ci	ocx->com_ring_head++;
110062306a36Sopenharmony_ci
110162306a36Sopenharmony_ci	return IRQ_WAKE_THREAD;
110262306a36Sopenharmony_ci}
110362306a36Sopenharmony_ci
110462306a36Sopenharmony_cistatic irqreturn_t thunderx_ocx_com_threaded_isr(int irq, void *irq_id)
110562306a36Sopenharmony_ci{
110662306a36Sopenharmony_ci	struct msix_entry *msix = irq_id;
110762306a36Sopenharmony_ci	struct thunderx_ocx *ocx = container_of(msix, struct thunderx_ocx,
110862306a36Sopenharmony_ci						msix_ent[msix->entry]);
110962306a36Sopenharmony_ci
111062306a36Sopenharmony_ci	irqreturn_t ret = IRQ_NONE;
111162306a36Sopenharmony_ci
111262306a36Sopenharmony_ci	unsigned long tail;
111362306a36Sopenharmony_ci	struct ocx_com_err_ctx *ctx;
111462306a36Sopenharmony_ci	int lane;
111562306a36Sopenharmony_ci	char *msg;
111662306a36Sopenharmony_ci	char *other;
111762306a36Sopenharmony_ci
111862306a36Sopenharmony_ci	msg = kmalloc(OCX_MESSAGE_SIZE, GFP_KERNEL);
111962306a36Sopenharmony_ci	other = kmalloc(OCX_OTHER_SIZE, GFP_KERNEL);
112062306a36Sopenharmony_ci
112162306a36Sopenharmony_ci	if (!msg || !other)
112262306a36Sopenharmony_ci		goto err_free;
112362306a36Sopenharmony_ci
112462306a36Sopenharmony_ci	while (CIRC_CNT(ocx->com_ring_head, ocx->com_ring_tail,
112562306a36Sopenharmony_ci			ARRAY_SIZE(ocx->com_err_ctx))) {
112662306a36Sopenharmony_ci		tail = ring_pos(ocx->com_ring_tail,
112762306a36Sopenharmony_ci				ARRAY_SIZE(ocx->com_err_ctx));
112862306a36Sopenharmony_ci		ctx = &ocx->com_err_ctx[tail];
112962306a36Sopenharmony_ci
113062306a36Sopenharmony_ci		snprintf(msg, OCX_MESSAGE_SIZE, "%s: OCX_COM_INT: %016llx",
113162306a36Sopenharmony_ci			ocx->edac_dev->ctl_name, ctx->reg_com_int);
113262306a36Sopenharmony_ci
113362306a36Sopenharmony_ci		decode_register(other, OCX_OTHER_SIZE,
113462306a36Sopenharmony_ci				ocx_com_errors, ctx->reg_com_int);
113562306a36Sopenharmony_ci
113662306a36Sopenharmony_ci		strlcat(msg, other, OCX_MESSAGE_SIZE);
113762306a36Sopenharmony_ci
113862306a36Sopenharmony_ci		for (lane = 0; lane < OCX_RX_LANES; lane++)
113962306a36Sopenharmony_ci			if (ctx->reg_com_int & BIT(lane)) {
114062306a36Sopenharmony_ci				snprintf(other, OCX_OTHER_SIZE,
114162306a36Sopenharmony_ci					 "\n\tOCX_LNE_INT[%02d]: %016llx OCX_LNE_STAT11[%02d]: %016llx",
114262306a36Sopenharmony_ci					 lane, ctx->reg_lane_int[lane],
114362306a36Sopenharmony_ci					 lane, ctx->reg_lane_stat11[lane]);
114462306a36Sopenharmony_ci
114562306a36Sopenharmony_ci				strlcat(msg, other, OCX_MESSAGE_SIZE);
114662306a36Sopenharmony_ci
114762306a36Sopenharmony_ci				decode_register(other, OCX_OTHER_SIZE,
114862306a36Sopenharmony_ci						ocx_lane_errors,
114962306a36Sopenharmony_ci						ctx->reg_lane_int[lane]);
115062306a36Sopenharmony_ci				strlcat(msg, other, OCX_MESSAGE_SIZE);
115162306a36Sopenharmony_ci			}
115262306a36Sopenharmony_ci
115362306a36Sopenharmony_ci		if (ctx->reg_com_int & OCX_COM_INT_CE)
115462306a36Sopenharmony_ci			edac_device_handle_ce(ocx->edac_dev, 0, 0, msg);
115562306a36Sopenharmony_ci
115662306a36Sopenharmony_ci		ocx->com_ring_tail++;
115762306a36Sopenharmony_ci	}
115862306a36Sopenharmony_ci
115962306a36Sopenharmony_ci	ret = IRQ_HANDLED;
116062306a36Sopenharmony_ci
116162306a36Sopenharmony_cierr_free:
116262306a36Sopenharmony_ci	kfree(other);
116362306a36Sopenharmony_ci	kfree(msg);
116462306a36Sopenharmony_ci
116562306a36Sopenharmony_ci	return ret;
116662306a36Sopenharmony_ci}
116762306a36Sopenharmony_ci
116862306a36Sopenharmony_cistatic irqreturn_t thunderx_ocx_lnk_isr(int irq, void *irq_id)
116962306a36Sopenharmony_ci{
117062306a36Sopenharmony_ci	struct msix_entry *msix = irq_id;
117162306a36Sopenharmony_ci	struct thunderx_ocx *ocx = container_of(msix, struct thunderx_ocx,
117262306a36Sopenharmony_ci						msix_ent[msix->entry]);
117362306a36Sopenharmony_ci	unsigned long head = ring_pos(ocx->link_ring_head,
117462306a36Sopenharmony_ci				      ARRAY_SIZE(ocx->link_err_ctx));
117562306a36Sopenharmony_ci	struct ocx_link_err_ctx *ctx = &ocx->link_err_ctx[head];
117662306a36Sopenharmony_ci
117762306a36Sopenharmony_ci	ctx->link = msix->entry;
117862306a36Sopenharmony_ci	ctx->reg_com_link_int = readq(ocx->regs + OCX_COM_LINKX_INT(ctx->link));
117962306a36Sopenharmony_ci
118062306a36Sopenharmony_ci	writeq(ctx->reg_com_link_int, ocx->regs + OCX_COM_LINKX_INT(ctx->link));
118162306a36Sopenharmony_ci
118262306a36Sopenharmony_ci	ocx->link_ring_head++;
118362306a36Sopenharmony_ci
118462306a36Sopenharmony_ci	return IRQ_WAKE_THREAD;
118562306a36Sopenharmony_ci}
118662306a36Sopenharmony_ci
118762306a36Sopenharmony_cistatic irqreturn_t thunderx_ocx_lnk_threaded_isr(int irq, void *irq_id)
118862306a36Sopenharmony_ci{
118962306a36Sopenharmony_ci	struct msix_entry *msix = irq_id;
119062306a36Sopenharmony_ci	struct thunderx_ocx *ocx = container_of(msix, struct thunderx_ocx,
119162306a36Sopenharmony_ci						msix_ent[msix->entry]);
119262306a36Sopenharmony_ci	irqreturn_t ret = IRQ_NONE;
119362306a36Sopenharmony_ci	unsigned long tail;
119462306a36Sopenharmony_ci	struct ocx_link_err_ctx *ctx;
119562306a36Sopenharmony_ci
119662306a36Sopenharmony_ci	char *msg;
119762306a36Sopenharmony_ci	char *other;
119862306a36Sopenharmony_ci
119962306a36Sopenharmony_ci	msg = kmalloc(OCX_MESSAGE_SIZE, GFP_KERNEL);
120062306a36Sopenharmony_ci	other = kmalloc(OCX_OTHER_SIZE, GFP_KERNEL);
120162306a36Sopenharmony_ci
120262306a36Sopenharmony_ci	if (!msg || !other)
120362306a36Sopenharmony_ci		goto err_free;
120462306a36Sopenharmony_ci
120562306a36Sopenharmony_ci	while (CIRC_CNT(ocx->link_ring_head, ocx->link_ring_tail,
120662306a36Sopenharmony_ci			ARRAY_SIZE(ocx->link_err_ctx))) {
120762306a36Sopenharmony_ci		tail = ring_pos(ocx->link_ring_head,
120862306a36Sopenharmony_ci				ARRAY_SIZE(ocx->link_err_ctx));
120962306a36Sopenharmony_ci
121062306a36Sopenharmony_ci		ctx = &ocx->link_err_ctx[tail];
121162306a36Sopenharmony_ci
121262306a36Sopenharmony_ci		snprintf(msg, OCX_MESSAGE_SIZE,
121362306a36Sopenharmony_ci			 "%s: OCX_COM_LINK_INT[%d]: %016llx",
121462306a36Sopenharmony_ci			 ocx->edac_dev->ctl_name,
121562306a36Sopenharmony_ci			 ctx->link, ctx->reg_com_link_int);
121662306a36Sopenharmony_ci
121762306a36Sopenharmony_ci		decode_register(other, OCX_OTHER_SIZE,
121862306a36Sopenharmony_ci				ocx_com_link_errors, ctx->reg_com_link_int);
121962306a36Sopenharmony_ci
122062306a36Sopenharmony_ci		strlcat(msg, other, OCX_MESSAGE_SIZE);
122162306a36Sopenharmony_ci
122262306a36Sopenharmony_ci		if (ctx->reg_com_link_int & OCX_COM_LINK_INT_UE)
122362306a36Sopenharmony_ci			edac_device_handle_ue(ocx->edac_dev, 0, 0, msg);
122462306a36Sopenharmony_ci		else if (ctx->reg_com_link_int & OCX_COM_LINK_INT_CE)
122562306a36Sopenharmony_ci			edac_device_handle_ce(ocx->edac_dev, 0, 0, msg);
122662306a36Sopenharmony_ci
122762306a36Sopenharmony_ci		ocx->link_ring_tail++;
122862306a36Sopenharmony_ci	}
122962306a36Sopenharmony_ci
123062306a36Sopenharmony_ci	ret = IRQ_HANDLED;
123162306a36Sopenharmony_cierr_free:
123262306a36Sopenharmony_ci	kfree(other);
123362306a36Sopenharmony_ci	kfree(msg);
123462306a36Sopenharmony_ci
123562306a36Sopenharmony_ci	return ret;
123662306a36Sopenharmony_ci}
123762306a36Sopenharmony_ci
123862306a36Sopenharmony_ci#define OCX_DEBUGFS_ATTR(_name, _reg)	DEBUGFS_REG_ATTR(ocx, _name, _reg)
123962306a36Sopenharmony_ci
124062306a36Sopenharmony_ciOCX_DEBUGFS_ATTR(tlk0_ecc_ctl, OCX_TLKX_ECC_CTL(0));
124162306a36Sopenharmony_ciOCX_DEBUGFS_ATTR(tlk1_ecc_ctl, OCX_TLKX_ECC_CTL(1));
124262306a36Sopenharmony_ciOCX_DEBUGFS_ATTR(tlk2_ecc_ctl, OCX_TLKX_ECC_CTL(2));
124362306a36Sopenharmony_ci
124462306a36Sopenharmony_ciOCX_DEBUGFS_ATTR(rlk0_ecc_ctl, OCX_RLKX_ECC_CTL(0));
124562306a36Sopenharmony_ciOCX_DEBUGFS_ATTR(rlk1_ecc_ctl, OCX_RLKX_ECC_CTL(1));
124662306a36Sopenharmony_ciOCX_DEBUGFS_ATTR(rlk2_ecc_ctl, OCX_RLKX_ECC_CTL(2));
124762306a36Sopenharmony_ci
124862306a36Sopenharmony_ciOCX_DEBUGFS_ATTR(com_link0_int, OCX_COM_LINKX_INT_W1S(0));
124962306a36Sopenharmony_ciOCX_DEBUGFS_ATTR(com_link1_int, OCX_COM_LINKX_INT_W1S(1));
125062306a36Sopenharmony_ciOCX_DEBUGFS_ATTR(com_link2_int, OCX_COM_LINKX_INT_W1S(2));
125162306a36Sopenharmony_ci
125262306a36Sopenharmony_ciOCX_DEBUGFS_ATTR(lne00_badcnt, OCX_LNE_BAD_CNT(0));
125362306a36Sopenharmony_ciOCX_DEBUGFS_ATTR(lne01_badcnt, OCX_LNE_BAD_CNT(1));
125462306a36Sopenharmony_ciOCX_DEBUGFS_ATTR(lne02_badcnt, OCX_LNE_BAD_CNT(2));
125562306a36Sopenharmony_ciOCX_DEBUGFS_ATTR(lne03_badcnt, OCX_LNE_BAD_CNT(3));
125662306a36Sopenharmony_ciOCX_DEBUGFS_ATTR(lne04_badcnt, OCX_LNE_BAD_CNT(4));
125762306a36Sopenharmony_ciOCX_DEBUGFS_ATTR(lne05_badcnt, OCX_LNE_BAD_CNT(5));
125862306a36Sopenharmony_ciOCX_DEBUGFS_ATTR(lne06_badcnt, OCX_LNE_BAD_CNT(6));
125962306a36Sopenharmony_ciOCX_DEBUGFS_ATTR(lne07_badcnt, OCX_LNE_BAD_CNT(7));
126062306a36Sopenharmony_ci
126162306a36Sopenharmony_ciOCX_DEBUGFS_ATTR(lne08_badcnt, OCX_LNE_BAD_CNT(8));
126262306a36Sopenharmony_ciOCX_DEBUGFS_ATTR(lne09_badcnt, OCX_LNE_BAD_CNT(9));
126362306a36Sopenharmony_ciOCX_DEBUGFS_ATTR(lne10_badcnt, OCX_LNE_BAD_CNT(10));
126462306a36Sopenharmony_ciOCX_DEBUGFS_ATTR(lne11_badcnt, OCX_LNE_BAD_CNT(11));
126562306a36Sopenharmony_ciOCX_DEBUGFS_ATTR(lne12_badcnt, OCX_LNE_BAD_CNT(12));
126662306a36Sopenharmony_ciOCX_DEBUGFS_ATTR(lne13_badcnt, OCX_LNE_BAD_CNT(13));
126762306a36Sopenharmony_ciOCX_DEBUGFS_ATTR(lne14_badcnt, OCX_LNE_BAD_CNT(14));
126862306a36Sopenharmony_ciOCX_DEBUGFS_ATTR(lne15_badcnt, OCX_LNE_BAD_CNT(15));
126962306a36Sopenharmony_ci
127062306a36Sopenharmony_ciOCX_DEBUGFS_ATTR(lne16_badcnt, OCX_LNE_BAD_CNT(16));
127162306a36Sopenharmony_ciOCX_DEBUGFS_ATTR(lne17_badcnt, OCX_LNE_BAD_CNT(17));
127262306a36Sopenharmony_ciOCX_DEBUGFS_ATTR(lne18_badcnt, OCX_LNE_BAD_CNT(18));
127362306a36Sopenharmony_ciOCX_DEBUGFS_ATTR(lne19_badcnt, OCX_LNE_BAD_CNT(19));
127462306a36Sopenharmony_ciOCX_DEBUGFS_ATTR(lne20_badcnt, OCX_LNE_BAD_CNT(20));
127562306a36Sopenharmony_ciOCX_DEBUGFS_ATTR(lne21_badcnt, OCX_LNE_BAD_CNT(21));
127662306a36Sopenharmony_ciOCX_DEBUGFS_ATTR(lne22_badcnt, OCX_LNE_BAD_CNT(22));
127762306a36Sopenharmony_ciOCX_DEBUGFS_ATTR(lne23_badcnt, OCX_LNE_BAD_CNT(23));
127862306a36Sopenharmony_ci
127962306a36Sopenharmony_ciOCX_DEBUGFS_ATTR(com_int, OCX_COM_INT_W1S);
128062306a36Sopenharmony_ci
128162306a36Sopenharmony_cistatic struct debugfs_entry *ocx_dfs_ents[] = {
128262306a36Sopenharmony_ci	&debugfs_tlk0_ecc_ctl,
128362306a36Sopenharmony_ci	&debugfs_tlk1_ecc_ctl,
128462306a36Sopenharmony_ci	&debugfs_tlk2_ecc_ctl,
128562306a36Sopenharmony_ci
128662306a36Sopenharmony_ci	&debugfs_rlk0_ecc_ctl,
128762306a36Sopenharmony_ci	&debugfs_rlk1_ecc_ctl,
128862306a36Sopenharmony_ci	&debugfs_rlk2_ecc_ctl,
128962306a36Sopenharmony_ci
129062306a36Sopenharmony_ci	&debugfs_com_link0_int,
129162306a36Sopenharmony_ci	&debugfs_com_link1_int,
129262306a36Sopenharmony_ci	&debugfs_com_link2_int,
129362306a36Sopenharmony_ci
129462306a36Sopenharmony_ci	&debugfs_lne00_badcnt,
129562306a36Sopenharmony_ci	&debugfs_lne01_badcnt,
129662306a36Sopenharmony_ci	&debugfs_lne02_badcnt,
129762306a36Sopenharmony_ci	&debugfs_lne03_badcnt,
129862306a36Sopenharmony_ci	&debugfs_lne04_badcnt,
129962306a36Sopenharmony_ci	&debugfs_lne05_badcnt,
130062306a36Sopenharmony_ci	&debugfs_lne06_badcnt,
130162306a36Sopenharmony_ci	&debugfs_lne07_badcnt,
130262306a36Sopenharmony_ci	&debugfs_lne08_badcnt,
130362306a36Sopenharmony_ci	&debugfs_lne09_badcnt,
130462306a36Sopenharmony_ci	&debugfs_lne10_badcnt,
130562306a36Sopenharmony_ci	&debugfs_lne11_badcnt,
130662306a36Sopenharmony_ci	&debugfs_lne12_badcnt,
130762306a36Sopenharmony_ci	&debugfs_lne13_badcnt,
130862306a36Sopenharmony_ci	&debugfs_lne14_badcnt,
130962306a36Sopenharmony_ci	&debugfs_lne15_badcnt,
131062306a36Sopenharmony_ci	&debugfs_lne16_badcnt,
131162306a36Sopenharmony_ci	&debugfs_lne17_badcnt,
131262306a36Sopenharmony_ci	&debugfs_lne18_badcnt,
131362306a36Sopenharmony_ci	&debugfs_lne19_badcnt,
131462306a36Sopenharmony_ci	&debugfs_lne20_badcnt,
131562306a36Sopenharmony_ci	&debugfs_lne21_badcnt,
131662306a36Sopenharmony_ci	&debugfs_lne22_badcnt,
131762306a36Sopenharmony_ci	&debugfs_lne23_badcnt,
131862306a36Sopenharmony_ci
131962306a36Sopenharmony_ci	&debugfs_com_int,
132062306a36Sopenharmony_ci};
132162306a36Sopenharmony_ci
132262306a36Sopenharmony_cistatic const struct pci_device_id thunderx_ocx_pci_tbl[] = {
132362306a36Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, PCI_DEVICE_ID_THUNDER_OCX) },
132462306a36Sopenharmony_ci	{ 0, },
132562306a36Sopenharmony_ci};
132662306a36Sopenharmony_ci
132762306a36Sopenharmony_cistatic void thunderx_ocx_clearstats(struct thunderx_ocx *ocx)
132862306a36Sopenharmony_ci{
132962306a36Sopenharmony_ci	int lane, stat, cfg;
133062306a36Sopenharmony_ci
133162306a36Sopenharmony_ci	for (lane = 0; lane < OCX_RX_LANES; lane++) {
133262306a36Sopenharmony_ci		cfg = readq(ocx->regs + OCX_LNE_CFG(lane));
133362306a36Sopenharmony_ci		cfg |= OCX_LNE_CFG_RX_STAT_RDCLR;
133462306a36Sopenharmony_ci		cfg &= ~OCX_LNE_CFG_RX_STAT_ENA;
133562306a36Sopenharmony_ci		writeq(cfg, ocx->regs + OCX_LNE_CFG(lane));
133662306a36Sopenharmony_ci
133762306a36Sopenharmony_ci		for (stat = 0; stat < OCX_RX_LANE_STATS; stat++)
133862306a36Sopenharmony_ci			readq(ocx->regs + OCX_LNE_STAT(lane, stat));
133962306a36Sopenharmony_ci	}
134062306a36Sopenharmony_ci}
134162306a36Sopenharmony_ci
134262306a36Sopenharmony_cistatic int thunderx_ocx_probe(struct pci_dev *pdev,
134362306a36Sopenharmony_ci			      const struct pci_device_id *id)
134462306a36Sopenharmony_ci{
134562306a36Sopenharmony_ci	struct thunderx_ocx *ocx;
134662306a36Sopenharmony_ci	struct edac_device_ctl_info *edac_dev;
134762306a36Sopenharmony_ci	char name[32];
134862306a36Sopenharmony_ci	int idx;
134962306a36Sopenharmony_ci	int i;
135062306a36Sopenharmony_ci	int ret;
135162306a36Sopenharmony_ci	u64 reg;
135262306a36Sopenharmony_ci
135362306a36Sopenharmony_ci	ret = pcim_enable_device(pdev);
135462306a36Sopenharmony_ci	if (ret) {
135562306a36Sopenharmony_ci		dev_err(&pdev->dev, "Cannot enable PCI device: %d\n", ret);
135662306a36Sopenharmony_ci		return ret;
135762306a36Sopenharmony_ci	}
135862306a36Sopenharmony_ci
135962306a36Sopenharmony_ci	ret = pcim_iomap_regions(pdev, BIT(0), "thunderx_ocx");
136062306a36Sopenharmony_ci	if (ret) {
136162306a36Sopenharmony_ci		dev_err(&pdev->dev, "Cannot map PCI resources: %d\n", ret);
136262306a36Sopenharmony_ci		return ret;
136362306a36Sopenharmony_ci	}
136462306a36Sopenharmony_ci
136562306a36Sopenharmony_ci	idx = edac_device_alloc_index();
136662306a36Sopenharmony_ci	snprintf(name, sizeof(name), "OCX%d", idx);
136762306a36Sopenharmony_ci	edac_dev = edac_device_alloc_ctl_info(sizeof(struct thunderx_ocx),
136862306a36Sopenharmony_ci					      name, 1, "CCPI", 1,
136962306a36Sopenharmony_ci					      0, NULL, 0, idx);
137062306a36Sopenharmony_ci	if (!edac_dev) {
137162306a36Sopenharmony_ci		dev_err(&pdev->dev, "Cannot allocate EDAC device\n");
137262306a36Sopenharmony_ci		return -ENOMEM;
137362306a36Sopenharmony_ci	}
137462306a36Sopenharmony_ci	ocx = edac_dev->pvt_info;
137562306a36Sopenharmony_ci	ocx->edac_dev = edac_dev;
137662306a36Sopenharmony_ci	ocx->com_ring_head = 0;
137762306a36Sopenharmony_ci	ocx->com_ring_tail = 0;
137862306a36Sopenharmony_ci	ocx->link_ring_head = 0;
137962306a36Sopenharmony_ci	ocx->link_ring_tail = 0;
138062306a36Sopenharmony_ci
138162306a36Sopenharmony_ci	ocx->regs = pcim_iomap_table(pdev)[0];
138262306a36Sopenharmony_ci	if (!ocx->regs) {
138362306a36Sopenharmony_ci		dev_err(&pdev->dev, "Cannot map PCI resources\n");
138462306a36Sopenharmony_ci		ret = -ENODEV;
138562306a36Sopenharmony_ci		goto err_free;
138662306a36Sopenharmony_ci	}
138762306a36Sopenharmony_ci
138862306a36Sopenharmony_ci	ocx->pdev = pdev;
138962306a36Sopenharmony_ci
139062306a36Sopenharmony_ci	for (i = 0; i < OCX_INTS; i++) {
139162306a36Sopenharmony_ci		ocx->msix_ent[i].entry = i;
139262306a36Sopenharmony_ci		ocx->msix_ent[i].vector = 0;
139362306a36Sopenharmony_ci	}
139462306a36Sopenharmony_ci
139562306a36Sopenharmony_ci	ret = pci_enable_msix_exact(pdev, ocx->msix_ent, OCX_INTS);
139662306a36Sopenharmony_ci	if (ret) {
139762306a36Sopenharmony_ci		dev_err(&pdev->dev, "Cannot enable interrupt: %d\n", ret);
139862306a36Sopenharmony_ci		goto err_free;
139962306a36Sopenharmony_ci	}
140062306a36Sopenharmony_ci
140162306a36Sopenharmony_ci	for (i = 0; i < OCX_INTS; i++) {
140262306a36Sopenharmony_ci		ret = devm_request_threaded_irq(&pdev->dev,
140362306a36Sopenharmony_ci						ocx->msix_ent[i].vector,
140462306a36Sopenharmony_ci						(i == 3) ?
140562306a36Sopenharmony_ci						 thunderx_ocx_com_isr :
140662306a36Sopenharmony_ci						 thunderx_ocx_lnk_isr,
140762306a36Sopenharmony_ci						(i == 3) ?
140862306a36Sopenharmony_ci						 thunderx_ocx_com_threaded_isr :
140962306a36Sopenharmony_ci						 thunderx_ocx_lnk_threaded_isr,
141062306a36Sopenharmony_ci						0, "[EDAC] ThunderX OCX",
141162306a36Sopenharmony_ci						&ocx->msix_ent[i]);
141262306a36Sopenharmony_ci		if (ret)
141362306a36Sopenharmony_ci			goto err_free;
141462306a36Sopenharmony_ci	}
141562306a36Sopenharmony_ci
141662306a36Sopenharmony_ci	edac_dev->dev = &pdev->dev;
141762306a36Sopenharmony_ci	edac_dev->dev_name = dev_name(&pdev->dev);
141862306a36Sopenharmony_ci	edac_dev->mod_name = "thunderx-ocx";
141962306a36Sopenharmony_ci	edac_dev->ctl_name = "thunderx-ocx";
142062306a36Sopenharmony_ci
142162306a36Sopenharmony_ci	ret = edac_device_add_device(edac_dev);
142262306a36Sopenharmony_ci	if (ret) {
142362306a36Sopenharmony_ci		dev_err(&pdev->dev, "Cannot add EDAC device: %d\n", ret);
142462306a36Sopenharmony_ci		goto err_free;
142562306a36Sopenharmony_ci	}
142662306a36Sopenharmony_ci
142762306a36Sopenharmony_ci	if (IS_ENABLED(CONFIG_EDAC_DEBUG)) {
142862306a36Sopenharmony_ci		ocx->debugfs = edac_debugfs_create_dir(pdev->dev.kobj.name);
142962306a36Sopenharmony_ci
143062306a36Sopenharmony_ci		ret = thunderx_create_debugfs_nodes(ocx->debugfs,
143162306a36Sopenharmony_ci						    ocx_dfs_ents,
143262306a36Sopenharmony_ci						    ocx,
143362306a36Sopenharmony_ci						    ARRAY_SIZE(ocx_dfs_ents));
143462306a36Sopenharmony_ci		if (ret != ARRAY_SIZE(ocx_dfs_ents)) {
143562306a36Sopenharmony_ci			dev_warn(&pdev->dev, "Error creating debugfs entries: %d%s\n",
143662306a36Sopenharmony_ci				 ret, ret >= 0 ? " created" : "");
143762306a36Sopenharmony_ci		}
143862306a36Sopenharmony_ci	}
143962306a36Sopenharmony_ci
144062306a36Sopenharmony_ci	pci_set_drvdata(pdev, edac_dev);
144162306a36Sopenharmony_ci
144262306a36Sopenharmony_ci	thunderx_ocx_clearstats(ocx);
144362306a36Sopenharmony_ci
144462306a36Sopenharmony_ci	for (i = 0; i < OCX_RX_LANES; i++) {
144562306a36Sopenharmony_ci		writeq(OCX_LNE_INT_ENA_ALL,
144662306a36Sopenharmony_ci		       ocx->regs + OCX_LNE_INT_EN(i));
144762306a36Sopenharmony_ci
144862306a36Sopenharmony_ci		reg = readq(ocx->regs + OCX_LNE_INT(i));
144962306a36Sopenharmony_ci		writeq(reg, ocx->regs + OCX_LNE_INT(i));
145062306a36Sopenharmony_ci
145162306a36Sopenharmony_ci	}
145262306a36Sopenharmony_ci
145362306a36Sopenharmony_ci	for (i = 0; i < OCX_LINK_INTS; i++) {
145462306a36Sopenharmony_ci		reg = readq(ocx->regs + OCX_COM_LINKX_INT(i));
145562306a36Sopenharmony_ci		writeq(reg, ocx->regs + OCX_COM_LINKX_INT(i));
145662306a36Sopenharmony_ci
145762306a36Sopenharmony_ci		writeq(OCX_COM_LINKX_INT_ENA_ALL,
145862306a36Sopenharmony_ci		       ocx->regs + OCX_COM_LINKX_INT_ENA_W1S(i));
145962306a36Sopenharmony_ci	}
146062306a36Sopenharmony_ci
146162306a36Sopenharmony_ci	reg = readq(ocx->regs + OCX_COM_INT);
146262306a36Sopenharmony_ci	writeq(reg, ocx->regs + OCX_COM_INT);
146362306a36Sopenharmony_ci
146462306a36Sopenharmony_ci	writeq(OCX_COM_INT_ENA_ALL, ocx->regs + OCX_COM_INT_ENA_W1S);
146562306a36Sopenharmony_ci
146662306a36Sopenharmony_ci	return 0;
146762306a36Sopenharmony_cierr_free:
146862306a36Sopenharmony_ci	edac_device_free_ctl_info(edac_dev);
146962306a36Sopenharmony_ci
147062306a36Sopenharmony_ci	return ret;
147162306a36Sopenharmony_ci}
147262306a36Sopenharmony_ci
147362306a36Sopenharmony_cistatic void thunderx_ocx_remove(struct pci_dev *pdev)
147462306a36Sopenharmony_ci{
147562306a36Sopenharmony_ci	struct edac_device_ctl_info *edac_dev = pci_get_drvdata(pdev);
147662306a36Sopenharmony_ci	struct thunderx_ocx *ocx = edac_dev->pvt_info;
147762306a36Sopenharmony_ci	int i;
147862306a36Sopenharmony_ci
147962306a36Sopenharmony_ci	writeq(OCX_COM_INT_ENA_ALL, ocx->regs + OCX_COM_INT_ENA_W1C);
148062306a36Sopenharmony_ci
148162306a36Sopenharmony_ci	for (i = 0; i < OCX_INTS; i++) {
148262306a36Sopenharmony_ci		writeq(OCX_COM_LINKX_INT_ENA_ALL,
148362306a36Sopenharmony_ci		       ocx->regs + OCX_COM_LINKX_INT_ENA_W1C(i));
148462306a36Sopenharmony_ci	}
148562306a36Sopenharmony_ci
148662306a36Sopenharmony_ci	edac_debugfs_remove_recursive(ocx->debugfs);
148762306a36Sopenharmony_ci
148862306a36Sopenharmony_ci	edac_device_del_device(&pdev->dev);
148962306a36Sopenharmony_ci	edac_device_free_ctl_info(edac_dev);
149062306a36Sopenharmony_ci}
149162306a36Sopenharmony_ci
149262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, thunderx_ocx_pci_tbl);
149362306a36Sopenharmony_ci
149462306a36Sopenharmony_cistatic struct pci_driver thunderx_ocx_driver = {
149562306a36Sopenharmony_ci	.name     = "thunderx_ocx_edac",
149662306a36Sopenharmony_ci	.probe    = thunderx_ocx_probe,
149762306a36Sopenharmony_ci	.remove   = thunderx_ocx_remove,
149862306a36Sopenharmony_ci	.id_table = thunderx_ocx_pci_tbl,
149962306a36Sopenharmony_ci};
150062306a36Sopenharmony_ci
150162306a36Sopenharmony_ci/*---------------------- L2C driver ---------------------------------*/
150262306a36Sopenharmony_ci
150362306a36Sopenharmony_ci#define PCI_DEVICE_ID_THUNDER_L2C_TAD 0xa02e
150462306a36Sopenharmony_ci#define PCI_DEVICE_ID_THUNDER_L2C_CBC 0xa02f
150562306a36Sopenharmony_ci#define PCI_DEVICE_ID_THUNDER_L2C_MCI 0xa030
150662306a36Sopenharmony_ci
150762306a36Sopenharmony_ci#define L2C_TAD_INT_W1C		0x40000
150862306a36Sopenharmony_ci#define L2C_TAD_INT_W1S		0x40008
150962306a36Sopenharmony_ci
151062306a36Sopenharmony_ci#define L2C_TAD_INT_ENA_W1C	0x40020
151162306a36Sopenharmony_ci#define L2C_TAD_INT_ENA_W1S	0x40028
151262306a36Sopenharmony_ci
151362306a36Sopenharmony_ci
151462306a36Sopenharmony_ci#define L2C_TAD_INT_L2DDBE	 BIT(1)
151562306a36Sopenharmony_ci#define L2C_TAD_INT_SBFSBE	 BIT(2)
151662306a36Sopenharmony_ci#define L2C_TAD_INT_SBFDBE	 BIT(3)
151762306a36Sopenharmony_ci#define L2C_TAD_INT_FBFSBE	 BIT(4)
151862306a36Sopenharmony_ci#define L2C_TAD_INT_FBFDBE	 BIT(5)
151962306a36Sopenharmony_ci#define L2C_TAD_INT_TAGDBE	 BIT(9)
152062306a36Sopenharmony_ci#define L2C_TAD_INT_RDDISLMC	 BIT(15)
152162306a36Sopenharmony_ci#define L2C_TAD_INT_WRDISLMC	 BIT(16)
152262306a36Sopenharmony_ci#define L2C_TAD_INT_LFBTO	 BIT(17)
152362306a36Sopenharmony_ci#define L2C_TAD_INT_GSYNCTO	 BIT(18)
152462306a36Sopenharmony_ci#define L2C_TAD_INT_RTGSBE	 BIT(32)
152562306a36Sopenharmony_ci#define L2C_TAD_INT_RTGDBE	 BIT(33)
152662306a36Sopenharmony_ci#define L2C_TAD_INT_RDDISOCI	 BIT(34)
152762306a36Sopenharmony_ci#define L2C_TAD_INT_WRDISOCI	 BIT(35)
152862306a36Sopenharmony_ci
152962306a36Sopenharmony_ci#define L2C_TAD_INT_ECC		(L2C_TAD_INT_L2DDBE | \
153062306a36Sopenharmony_ci				 L2C_TAD_INT_SBFSBE | L2C_TAD_INT_SBFDBE | \
153162306a36Sopenharmony_ci				 L2C_TAD_INT_FBFSBE | L2C_TAD_INT_FBFDBE)
153262306a36Sopenharmony_ci
153362306a36Sopenharmony_ci#define L2C_TAD_INT_CE          (L2C_TAD_INT_SBFSBE | \
153462306a36Sopenharmony_ci				 L2C_TAD_INT_FBFSBE)
153562306a36Sopenharmony_ci
153662306a36Sopenharmony_ci#define L2C_TAD_INT_UE          (L2C_TAD_INT_L2DDBE | \
153762306a36Sopenharmony_ci				 L2C_TAD_INT_SBFDBE | \
153862306a36Sopenharmony_ci				 L2C_TAD_INT_FBFDBE | \
153962306a36Sopenharmony_ci				 L2C_TAD_INT_TAGDBE | \
154062306a36Sopenharmony_ci				 L2C_TAD_INT_RTGDBE | \
154162306a36Sopenharmony_ci				 L2C_TAD_INT_WRDISOCI | \
154262306a36Sopenharmony_ci				 L2C_TAD_INT_RDDISOCI | \
154362306a36Sopenharmony_ci				 L2C_TAD_INT_WRDISLMC | \
154462306a36Sopenharmony_ci				 L2C_TAD_INT_RDDISLMC | \
154562306a36Sopenharmony_ci				 L2C_TAD_INT_LFBTO    | \
154662306a36Sopenharmony_ci				 L2C_TAD_INT_GSYNCTO)
154762306a36Sopenharmony_ci
154862306a36Sopenharmony_cistatic const struct error_descr l2_tad_errors[] = {
154962306a36Sopenharmony_ci	{
155062306a36Sopenharmony_ci		.type  = ERR_CORRECTED,
155162306a36Sopenharmony_ci		.mask  = L2C_TAD_INT_SBFSBE,
155262306a36Sopenharmony_ci		.descr = "SBF single-bit error",
155362306a36Sopenharmony_ci	},
155462306a36Sopenharmony_ci	{
155562306a36Sopenharmony_ci		.type  = ERR_CORRECTED,
155662306a36Sopenharmony_ci		.mask  = L2C_TAD_INT_FBFSBE,
155762306a36Sopenharmony_ci		.descr = "FBF single-bit error",
155862306a36Sopenharmony_ci	},
155962306a36Sopenharmony_ci	{
156062306a36Sopenharmony_ci		.type  = ERR_UNCORRECTED,
156162306a36Sopenharmony_ci		.mask  = L2C_TAD_INT_L2DDBE,
156262306a36Sopenharmony_ci		.descr = "L2D double-bit error",
156362306a36Sopenharmony_ci	},
156462306a36Sopenharmony_ci	{
156562306a36Sopenharmony_ci		.type  = ERR_UNCORRECTED,
156662306a36Sopenharmony_ci		.mask  = L2C_TAD_INT_SBFDBE,
156762306a36Sopenharmony_ci		.descr = "SBF double-bit error",
156862306a36Sopenharmony_ci	},
156962306a36Sopenharmony_ci	{
157062306a36Sopenharmony_ci		.type  = ERR_UNCORRECTED,
157162306a36Sopenharmony_ci		.mask  = L2C_TAD_INT_FBFDBE,
157262306a36Sopenharmony_ci		.descr = "FBF double-bit error",
157362306a36Sopenharmony_ci	},
157462306a36Sopenharmony_ci	{
157562306a36Sopenharmony_ci		.type  = ERR_UNCORRECTED,
157662306a36Sopenharmony_ci		.mask  = L2C_TAD_INT_TAGDBE,
157762306a36Sopenharmony_ci		.descr = "TAG double-bit error",
157862306a36Sopenharmony_ci	},
157962306a36Sopenharmony_ci	{
158062306a36Sopenharmony_ci		.type  = ERR_UNCORRECTED,
158162306a36Sopenharmony_ci		.mask  = L2C_TAD_INT_RTGDBE,
158262306a36Sopenharmony_ci		.descr = "RTG double-bit error",
158362306a36Sopenharmony_ci	},
158462306a36Sopenharmony_ci	{
158562306a36Sopenharmony_ci		.type  = ERR_UNCORRECTED,
158662306a36Sopenharmony_ci		.mask  = L2C_TAD_INT_WRDISOCI,
158762306a36Sopenharmony_ci		.descr = "Write to a disabled CCPI",
158862306a36Sopenharmony_ci	},
158962306a36Sopenharmony_ci	{
159062306a36Sopenharmony_ci		.type  = ERR_UNCORRECTED,
159162306a36Sopenharmony_ci		.mask  = L2C_TAD_INT_RDDISOCI,
159262306a36Sopenharmony_ci		.descr = "Read from a disabled CCPI",
159362306a36Sopenharmony_ci	},
159462306a36Sopenharmony_ci	{
159562306a36Sopenharmony_ci		.type  = ERR_UNCORRECTED,
159662306a36Sopenharmony_ci		.mask  = L2C_TAD_INT_WRDISLMC,
159762306a36Sopenharmony_ci		.descr = "Write to a disabled LMC",
159862306a36Sopenharmony_ci	},
159962306a36Sopenharmony_ci	{
160062306a36Sopenharmony_ci		.type  = ERR_UNCORRECTED,
160162306a36Sopenharmony_ci		.mask  = L2C_TAD_INT_RDDISLMC,
160262306a36Sopenharmony_ci		.descr = "Read from a disabled LMC",
160362306a36Sopenharmony_ci	},
160462306a36Sopenharmony_ci	{
160562306a36Sopenharmony_ci		.type  = ERR_UNCORRECTED,
160662306a36Sopenharmony_ci		.mask  = L2C_TAD_INT_LFBTO,
160762306a36Sopenharmony_ci		.descr = "LFB entry timeout",
160862306a36Sopenharmony_ci	},
160962306a36Sopenharmony_ci	{
161062306a36Sopenharmony_ci		.type  = ERR_UNCORRECTED,
161162306a36Sopenharmony_ci		.mask  = L2C_TAD_INT_GSYNCTO,
161262306a36Sopenharmony_ci		.descr = "Global sync CCPI timeout",
161362306a36Sopenharmony_ci	},
161462306a36Sopenharmony_ci	{0, 0, NULL},
161562306a36Sopenharmony_ci};
161662306a36Sopenharmony_ci
161762306a36Sopenharmony_ci#define L2C_TAD_INT_TAG		(L2C_TAD_INT_TAGDBE)
161862306a36Sopenharmony_ci
161962306a36Sopenharmony_ci#define L2C_TAD_INT_RTG		(L2C_TAD_INT_RTGDBE)
162062306a36Sopenharmony_ci
162162306a36Sopenharmony_ci#define L2C_TAD_INT_DISLMC	(L2C_TAD_INT_WRDISLMC | L2C_TAD_INT_RDDISLMC)
162262306a36Sopenharmony_ci
162362306a36Sopenharmony_ci#define L2C_TAD_INT_DISOCI	(L2C_TAD_INT_WRDISOCI | L2C_TAD_INT_RDDISOCI)
162462306a36Sopenharmony_ci
162562306a36Sopenharmony_ci#define L2C_TAD_INT_ENA_ALL	(L2C_TAD_INT_ECC | L2C_TAD_INT_TAG | \
162662306a36Sopenharmony_ci				 L2C_TAD_INT_RTG | \
162762306a36Sopenharmony_ci				 L2C_TAD_INT_DISLMC | L2C_TAD_INT_DISOCI | \
162862306a36Sopenharmony_ci				 L2C_TAD_INT_LFBTO)
162962306a36Sopenharmony_ci
163062306a36Sopenharmony_ci#define L2C_TAD_TIMETWO		0x50000
163162306a36Sopenharmony_ci#define L2C_TAD_TIMEOUT		0x50100
163262306a36Sopenharmony_ci#define L2C_TAD_ERR		0x60000
163362306a36Sopenharmony_ci#define L2C_TAD_TQD_ERR		0x60100
163462306a36Sopenharmony_ci#define L2C_TAD_TTG_ERR		0x60200
163562306a36Sopenharmony_ci
163662306a36Sopenharmony_ci
163762306a36Sopenharmony_ci#define L2C_CBC_INT_W1C		0x60000
163862306a36Sopenharmony_ci
163962306a36Sopenharmony_ci#define L2C_CBC_INT_RSDSBE	 BIT(0)
164062306a36Sopenharmony_ci#define L2C_CBC_INT_RSDDBE	 BIT(1)
164162306a36Sopenharmony_ci
164262306a36Sopenharmony_ci#define L2C_CBC_INT_RSD		 (L2C_CBC_INT_RSDSBE | L2C_CBC_INT_RSDDBE)
164362306a36Sopenharmony_ci
164462306a36Sopenharmony_ci#define L2C_CBC_INT_MIBSBE	 BIT(4)
164562306a36Sopenharmony_ci#define L2C_CBC_INT_MIBDBE	 BIT(5)
164662306a36Sopenharmony_ci
164762306a36Sopenharmony_ci#define L2C_CBC_INT_MIB		 (L2C_CBC_INT_MIBSBE | L2C_CBC_INT_MIBDBE)
164862306a36Sopenharmony_ci
164962306a36Sopenharmony_ci#define L2C_CBC_INT_IORDDISOCI	 BIT(6)
165062306a36Sopenharmony_ci#define L2C_CBC_INT_IOWRDISOCI	 BIT(7)
165162306a36Sopenharmony_ci
165262306a36Sopenharmony_ci#define L2C_CBC_INT_IODISOCI	 (L2C_CBC_INT_IORDDISOCI | \
165362306a36Sopenharmony_ci				  L2C_CBC_INT_IOWRDISOCI)
165462306a36Sopenharmony_ci
165562306a36Sopenharmony_ci#define L2C_CBC_INT_CE		 (L2C_CBC_INT_RSDSBE | L2C_CBC_INT_MIBSBE)
165662306a36Sopenharmony_ci#define L2C_CBC_INT_UE		 (L2C_CBC_INT_RSDDBE | L2C_CBC_INT_MIBDBE)
165762306a36Sopenharmony_ci
165862306a36Sopenharmony_ci
165962306a36Sopenharmony_cistatic const struct error_descr l2_cbc_errors[] = {
166062306a36Sopenharmony_ci	{
166162306a36Sopenharmony_ci		.type  = ERR_CORRECTED,
166262306a36Sopenharmony_ci		.mask  = L2C_CBC_INT_RSDSBE,
166362306a36Sopenharmony_ci		.descr = "RSD single-bit error",
166462306a36Sopenharmony_ci	},
166562306a36Sopenharmony_ci	{
166662306a36Sopenharmony_ci		.type  = ERR_CORRECTED,
166762306a36Sopenharmony_ci		.mask  = L2C_CBC_INT_MIBSBE,
166862306a36Sopenharmony_ci		.descr = "MIB single-bit error",
166962306a36Sopenharmony_ci	},
167062306a36Sopenharmony_ci	{
167162306a36Sopenharmony_ci		.type  = ERR_UNCORRECTED,
167262306a36Sopenharmony_ci		.mask  = L2C_CBC_INT_RSDDBE,
167362306a36Sopenharmony_ci		.descr = "RSD double-bit error",
167462306a36Sopenharmony_ci	},
167562306a36Sopenharmony_ci	{
167662306a36Sopenharmony_ci		.type  = ERR_UNCORRECTED,
167762306a36Sopenharmony_ci		.mask  = L2C_CBC_INT_MIBDBE,
167862306a36Sopenharmony_ci		.descr = "MIB double-bit error",
167962306a36Sopenharmony_ci	},
168062306a36Sopenharmony_ci	{
168162306a36Sopenharmony_ci		.type  = ERR_UNCORRECTED,
168262306a36Sopenharmony_ci		.mask  = L2C_CBC_INT_IORDDISOCI,
168362306a36Sopenharmony_ci		.descr = "Read from a disabled CCPI",
168462306a36Sopenharmony_ci	},
168562306a36Sopenharmony_ci	{
168662306a36Sopenharmony_ci		.type  = ERR_UNCORRECTED,
168762306a36Sopenharmony_ci		.mask  = L2C_CBC_INT_IOWRDISOCI,
168862306a36Sopenharmony_ci		.descr = "Write to a disabled CCPI",
168962306a36Sopenharmony_ci	},
169062306a36Sopenharmony_ci	{0, 0, NULL},
169162306a36Sopenharmony_ci};
169262306a36Sopenharmony_ci
169362306a36Sopenharmony_ci#define L2C_CBC_INT_W1S		0x60008
169462306a36Sopenharmony_ci#define L2C_CBC_INT_ENA_W1C	0x60020
169562306a36Sopenharmony_ci
169662306a36Sopenharmony_ci#define L2C_CBC_INT_ENA_ALL	 (L2C_CBC_INT_RSD | L2C_CBC_INT_MIB | \
169762306a36Sopenharmony_ci				  L2C_CBC_INT_IODISOCI)
169862306a36Sopenharmony_ci
169962306a36Sopenharmony_ci#define L2C_CBC_INT_ENA_W1S	0x60028
170062306a36Sopenharmony_ci
170162306a36Sopenharmony_ci#define L2C_CBC_IODISOCIERR	0x80008
170262306a36Sopenharmony_ci#define L2C_CBC_IOCERR		0x80010
170362306a36Sopenharmony_ci#define L2C_CBC_RSDERR		0x80018
170462306a36Sopenharmony_ci#define L2C_CBC_MIBERR		0x80020
170562306a36Sopenharmony_ci
170662306a36Sopenharmony_ci
170762306a36Sopenharmony_ci#define L2C_MCI_INT_W1C		0x0
170862306a36Sopenharmony_ci
170962306a36Sopenharmony_ci#define L2C_MCI_INT_VBFSBE	 BIT(0)
171062306a36Sopenharmony_ci#define L2C_MCI_INT_VBFDBE	 BIT(1)
171162306a36Sopenharmony_ci
171262306a36Sopenharmony_cistatic const struct error_descr l2_mci_errors[] = {
171362306a36Sopenharmony_ci	{
171462306a36Sopenharmony_ci		.type  = ERR_CORRECTED,
171562306a36Sopenharmony_ci		.mask  = L2C_MCI_INT_VBFSBE,
171662306a36Sopenharmony_ci		.descr = "VBF single-bit error",
171762306a36Sopenharmony_ci	},
171862306a36Sopenharmony_ci	{
171962306a36Sopenharmony_ci		.type  = ERR_UNCORRECTED,
172062306a36Sopenharmony_ci		.mask  = L2C_MCI_INT_VBFDBE,
172162306a36Sopenharmony_ci		.descr = "VBF double-bit error",
172262306a36Sopenharmony_ci	},
172362306a36Sopenharmony_ci	{0, 0, NULL},
172462306a36Sopenharmony_ci};
172562306a36Sopenharmony_ci
172662306a36Sopenharmony_ci#define L2C_MCI_INT_W1S		0x8
172762306a36Sopenharmony_ci#define L2C_MCI_INT_ENA_W1C	0x20
172862306a36Sopenharmony_ci
172962306a36Sopenharmony_ci#define L2C_MCI_INT_ENA_ALL	 (L2C_MCI_INT_VBFSBE | L2C_MCI_INT_VBFDBE)
173062306a36Sopenharmony_ci
173162306a36Sopenharmony_ci#define L2C_MCI_INT_ENA_W1S	0x28
173262306a36Sopenharmony_ci
173362306a36Sopenharmony_ci#define L2C_MCI_ERR		0x10000
173462306a36Sopenharmony_ci
173562306a36Sopenharmony_ci#define L2C_MESSAGE_SIZE	SZ_1K
173662306a36Sopenharmony_ci#define L2C_OTHER_SIZE		(50 * ARRAY_SIZE(l2_tad_errors))
173762306a36Sopenharmony_ci
173862306a36Sopenharmony_cistruct l2c_err_ctx {
173962306a36Sopenharmony_ci	char *reg_ext_name;
174062306a36Sopenharmony_ci	u64  reg_int;
174162306a36Sopenharmony_ci	u64  reg_ext;
174262306a36Sopenharmony_ci};
174362306a36Sopenharmony_ci
174462306a36Sopenharmony_cistruct thunderx_l2c {
174562306a36Sopenharmony_ci	void __iomem *regs;
174662306a36Sopenharmony_ci	struct pci_dev *pdev;
174762306a36Sopenharmony_ci	struct edac_device_ctl_info *edac_dev;
174862306a36Sopenharmony_ci
174962306a36Sopenharmony_ci	struct dentry *debugfs;
175062306a36Sopenharmony_ci
175162306a36Sopenharmony_ci	int index;
175262306a36Sopenharmony_ci
175362306a36Sopenharmony_ci	struct msix_entry msix_ent;
175462306a36Sopenharmony_ci
175562306a36Sopenharmony_ci	struct l2c_err_ctx err_ctx[RING_ENTRIES];
175662306a36Sopenharmony_ci	unsigned long ring_head;
175762306a36Sopenharmony_ci	unsigned long ring_tail;
175862306a36Sopenharmony_ci};
175962306a36Sopenharmony_ci
176062306a36Sopenharmony_cistatic irqreturn_t thunderx_l2c_tad_isr(int irq, void *irq_id)
176162306a36Sopenharmony_ci{
176262306a36Sopenharmony_ci	struct msix_entry *msix = irq_id;
176362306a36Sopenharmony_ci	struct thunderx_l2c *tad = container_of(msix, struct thunderx_l2c,
176462306a36Sopenharmony_ci						msix_ent);
176562306a36Sopenharmony_ci
176662306a36Sopenharmony_ci	unsigned long head = ring_pos(tad->ring_head, ARRAY_SIZE(tad->err_ctx));
176762306a36Sopenharmony_ci	struct l2c_err_ctx *ctx = &tad->err_ctx[head];
176862306a36Sopenharmony_ci
176962306a36Sopenharmony_ci	ctx->reg_int = readq(tad->regs + L2C_TAD_INT_W1C);
177062306a36Sopenharmony_ci
177162306a36Sopenharmony_ci	if (ctx->reg_int & L2C_TAD_INT_ECC) {
177262306a36Sopenharmony_ci		ctx->reg_ext_name = "TQD_ERR";
177362306a36Sopenharmony_ci		ctx->reg_ext = readq(tad->regs + L2C_TAD_TQD_ERR);
177462306a36Sopenharmony_ci	} else if (ctx->reg_int & L2C_TAD_INT_TAG) {
177562306a36Sopenharmony_ci		ctx->reg_ext_name = "TTG_ERR";
177662306a36Sopenharmony_ci		ctx->reg_ext = readq(tad->regs + L2C_TAD_TTG_ERR);
177762306a36Sopenharmony_ci	} else if (ctx->reg_int & L2C_TAD_INT_LFBTO) {
177862306a36Sopenharmony_ci		ctx->reg_ext_name = "TIMEOUT";
177962306a36Sopenharmony_ci		ctx->reg_ext = readq(tad->regs + L2C_TAD_TIMEOUT);
178062306a36Sopenharmony_ci	} else if (ctx->reg_int & L2C_TAD_INT_DISOCI) {
178162306a36Sopenharmony_ci		ctx->reg_ext_name = "ERR";
178262306a36Sopenharmony_ci		ctx->reg_ext = readq(tad->regs + L2C_TAD_ERR);
178362306a36Sopenharmony_ci	}
178462306a36Sopenharmony_ci
178562306a36Sopenharmony_ci	writeq(ctx->reg_int, tad->regs + L2C_TAD_INT_W1C);
178662306a36Sopenharmony_ci
178762306a36Sopenharmony_ci	tad->ring_head++;
178862306a36Sopenharmony_ci
178962306a36Sopenharmony_ci	return IRQ_WAKE_THREAD;
179062306a36Sopenharmony_ci}
179162306a36Sopenharmony_ci
179262306a36Sopenharmony_cistatic irqreturn_t thunderx_l2c_cbc_isr(int irq, void *irq_id)
179362306a36Sopenharmony_ci{
179462306a36Sopenharmony_ci	struct msix_entry *msix = irq_id;
179562306a36Sopenharmony_ci	struct thunderx_l2c *cbc = container_of(msix, struct thunderx_l2c,
179662306a36Sopenharmony_ci						msix_ent);
179762306a36Sopenharmony_ci
179862306a36Sopenharmony_ci	unsigned long head = ring_pos(cbc->ring_head, ARRAY_SIZE(cbc->err_ctx));
179962306a36Sopenharmony_ci	struct l2c_err_ctx *ctx = &cbc->err_ctx[head];
180062306a36Sopenharmony_ci
180162306a36Sopenharmony_ci	ctx->reg_int = readq(cbc->regs + L2C_CBC_INT_W1C);
180262306a36Sopenharmony_ci
180362306a36Sopenharmony_ci	if (ctx->reg_int & L2C_CBC_INT_RSD) {
180462306a36Sopenharmony_ci		ctx->reg_ext_name = "RSDERR";
180562306a36Sopenharmony_ci		ctx->reg_ext = readq(cbc->regs + L2C_CBC_RSDERR);
180662306a36Sopenharmony_ci	} else if (ctx->reg_int & L2C_CBC_INT_MIB) {
180762306a36Sopenharmony_ci		ctx->reg_ext_name = "MIBERR";
180862306a36Sopenharmony_ci		ctx->reg_ext = readq(cbc->regs + L2C_CBC_MIBERR);
180962306a36Sopenharmony_ci	} else if (ctx->reg_int & L2C_CBC_INT_IODISOCI) {
181062306a36Sopenharmony_ci		ctx->reg_ext_name = "IODISOCIERR";
181162306a36Sopenharmony_ci		ctx->reg_ext = readq(cbc->regs + L2C_CBC_IODISOCIERR);
181262306a36Sopenharmony_ci	}
181362306a36Sopenharmony_ci
181462306a36Sopenharmony_ci	writeq(ctx->reg_int, cbc->regs + L2C_CBC_INT_W1C);
181562306a36Sopenharmony_ci
181662306a36Sopenharmony_ci	cbc->ring_head++;
181762306a36Sopenharmony_ci
181862306a36Sopenharmony_ci	return IRQ_WAKE_THREAD;
181962306a36Sopenharmony_ci}
182062306a36Sopenharmony_ci
182162306a36Sopenharmony_cistatic irqreturn_t thunderx_l2c_mci_isr(int irq, void *irq_id)
182262306a36Sopenharmony_ci{
182362306a36Sopenharmony_ci	struct msix_entry *msix = irq_id;
182462306a36Sopenharmony_ci	struct thunderx_l2c *mci = container_of(msix, struct thunderx_l2c,
182562306a36Sopenharmony_ci						msix_ent);
182662306a36Sopenharmony_ci
182762306a36Sopenharmony_ci	unsigned long head = ring_pos(mci->ring_head, ARRAY_SIZE(mci->err_ctx));
182862306a36Sopenharmony_ci	struct l2c_err_ctx *ctx = &mci->err_ctx[head];
182962306a36Sopenharmony_ci
183062306a36Sopenharmony_ci	ctx->reg_int = readq(mci->regs + L2C_MCI_INT_W1C);
183162306a36Sopenharmony_ci	ctx->reg_ext = readq(mci->regs + L2C_MCI_ERR);
183262306a36Sopenharmony_ci
183362306a36Sopenharmony_ci	writeq(ctx->reg_int, mci->regs + L2C_MCI_INT_W1C);
183462306a36Sopenharmony_ci
183562306a36Sopenharmony_ci	ctx->reg_ext_name = "ERR";
183662306a36Sopenharmony_ci
183762306a36Sopenharmony_ci	mci->ring_head++;
183862306a36Sopenharmony_ci
183962306a36Sopenharmony_ci	return IRQ_WAKE_THREAD;
184062306a36Sopenharmony_ci}
184162306a36Sopenharmony_ci
184262306a36Sopenharmony_cistatic irqreturn_t thunderx_l2c_threaded_isr(int irq, void *irq_id)
184362306a36Sopenharmony_ci{
184462306a36Sopenharmony_ci	struct msix_entry *msix = irq_id;
184562306a36Sopenharmony_ci	struct thunderx_l2c *l2c = container_of(msix, struct thunderx_l2c,
184662306a36Sopenharmony_ci						msix_ent);
184762306a36Sopenharmony_ci
184862306a36Sopenharmony_ci	unsigned long tail = ring_pos(l2c->ring_tail, ARRAY_SIZE(l2c->err_ctx));
184962306a36Sopenharmony_ci	struct l2c_err_ctx *ctx = &l2c->err_ctx[tail];
185062306a36Sopenharmony_ci	irqreturn_t ret = IRQ_NONE;
185162306a36Sopenharmony_ci
185262306a36Sopenharmony_ci	u64 mask_ue, mask_ce;
185362306a36Sopenharmony_ci	const struct error_descr *l2_errors;
185462306a36Sopenharmony_ci	char *reg_int_name;
185562306a36Sopenharmony_ci
185662306a36Sopenharmony_ci	char *msg;
185762306a36Sopenharmony_ci	char *other;
185862306a36Sopenharmony_ci
185962306a36Sopenharmony_ci	msg = kmalloc(OCX_MESSAGE_SIZE, GFP_KERNEL);
186062306a36Sopenharmony_ci	other = kmalloc(OCX_OTHER_SIZE, GFP_KERNEL);
186162306a36Sopenharmony_ci
186262306a36Sopenharmony_ci	if (!msg || !other)
186362306a36Sopenharmony_ci		goto err_free;
186462306a36Sopenharmony_ci
186562306a36Sopenharmony_ci	switch (l2c->pdev->device) {
186662306a36Sopenharmony_ci	case PCI_DEVICE_ID_THUNDER_L2C_TAD:
186762306a36Sopenharmony_ci		reg_int_name = "L2C_TAD_INT";
186862306a36Sopenharmony_ci		mask_ue = L2C_TAD_INT_UE;
186962306a36Sopenharmony_ci		mask_ce = L2C_TAD_INT_CE;
187062306a36Sopenharmony_ci		l2_errors = l2_tad_errors;
187162306a36Sopenharmony_ci		break;
187262306a36Sopenharmony_ci	case PCI_DEVICE_ID_THUNDER_L2C_CBC:
187362306a36Sopenharmony_ci		reg_int_name = "L2C_CBC_INT";
187462306a36Sopenharmony_ci		mask_ue = L2C_CBC_INT_UE;
187562306a36Sopenharmony_ci		mask_ce = L2C_CBC_INT_CE;
187662306a36Sopenharmony_ci		l2_errors = l2_cbc_errors;
187762306a36Sopenharmony_ci		break;
187862306a36Sopenharmony_ci	case PCI_DEVICE_ID_THUNDER_L2C_MCI:
187962306a36Sopenharmony_ci		reg_int_name = "L2C_MCI_INT";
188062306a36Sopenharmony_ci		mask_ue = L2C_MCI_INT_VBFDBE;
188162306a36Sopenharmony_ci		mask_ce = L2C_MCI_INT_VBFSBE;
188262306a36Sopenharmony_ci		l2_errors = l2_mci_errors;
188362306a36Sopenharmony_ci		break;
188462306a36Sopenharmony_ci	default:
188562306a36Sopenharmony_ci		dev_err(&l2c->pdev->dev, "Unsupported device: %04x\n",
188662306a36Sopenharmony_ci			l2c->pdev->device);
188762306a36Sopenharmony_ci		goto err_free;
188862306a36Sopenharmony_ci	}
188962306a36Sopenharmony_ci
189062306a36Sopenharmony_ci	while (CIRC_CNT(l2c->ring_head, l2c->ring_tail,
189162306a36Sopenharmony_ci			ARRAY_SIZE(l2c->err_ctx))) {
189262306a36Sopenharmony_ci		snprintf(msg, L2C_MESSAGE_SIZE,
189362306a36Sopenharmony_ci			 "%s: %s: %016llx, %s: %016llx",
189462306a36Sopenharmony_ci			 l2c->edac_dev->ctl_name, reg_int_name, ctx->reg_int,
189562306a36Sopenharmony_ci			 ctx->reg_ext_name, ctx->reg_ext);
189662306a36Sopenharmony_ci
189762306a36Sopenharmony_ci		decode_register(other, L2C_OTHER_SIZE, l2_errors, ctx->reg_int);
189862306a36Sopenharmony_ci
189962306a36Sopenharmony_ci		strlcat(msg, other, L2C_MESSAGE_SIZE);
190062306a36Sopenharmony_ci
190162306a36Sopenharmony_ci		if (ctx->reg_int & mask_ue)
190262306a36Sopenharmony_ci			edac_device_handle_ue(l2c->edac_dev, 0, 0, msg);
190362306a36Sopenharmony_ci		else if (ctx->reg_int & mask_ce)
190462306a36Sopenharmony_ci			edac_device_handle_ce(l2c->edac_dev, 0, 0, msg);
190562306a36Sopenharmony_ci
190662306a36Sopenharmony_ci		l2c->ring_tail++;
190762306a36Sopenharmony_ci	}
190862306a36Sopenharmony_ci
190962306a36Sopenharmony_ci	ret = IRQ_HANDLED;
191062306a36Sopenharmony_ci
191162306a36Sopenharmony_cierr_free:
191262306a36Sopenharmony_ci	kfree(other);
191362306a36Sopenharmony_ci	kfree(msg);
191462306a36Sopenharmony_ci
191562306a36Sopenharmony_ci	return ret;
191662306a36Sopenharmony_ci}
191762306a36Sopenharmony_ci
191862306a36Sopenharmony_ci#define L2C_DEBUGFS_ATTR(_name, _reg)	DEBUGFS_REG_ATTR(l2c, _name, _reg)
191962306a36Sopenharmony_ci
192062306a36Sopenharmony_ciL2C_DEBUGFS_ATTR(tad_int, L2C_TAD_INT_W1S);
192162306a36Sopenharmony_ci
192262306a36Sopenharmony_cistatic struct debugfs_entry *l2c_tad_dfs_ents[] = {
192362306a36Sopenharmony_ci	&debugfs_tad_int,
192462306a36Sopenharmony_ci};
192562306a36Sopenharmony_ci
192662306a36Sopenharmony_ciL2C_DEBUGFS_ATTR(cbc_int, L2C_CBC_INT_W1S);
192762306a36Sopenharmony_ci
192862306a36Sopenharmony_cistatic struct debugfs_entry *l2c_cbc_dfs_ents[] = {
192962306a36Sopenharmony_ci	&debugfs_cbc_int,
193062306a36Sopenharmony_ci};
193162306a36Sopenharmony_ci
193262306a36Sopenharmony_ciL2C_DEBUGFS_ATTR(mci_int, L2C_MCI_INT_W1S);
193362306a36Sopenharmony_ci
193462306a36Sopenharmony_cistatic struct debugfs_entry *l2c_mci_dfs_ents[] = {
193562306a36Sopenharmony_ci	&debugfs_mci_int,
193662306a36Sopenharmony_ci};
193762306a36Sopenharmony_ci
193862306a36Sopenharmony_cistatic const struct pci_device_id thunderx_l2c_pci_tbl[] = {
193962306a36Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, PCI_DEVICE_ID_THUNDER_L2C_TAD), },
194062306a36Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, PCI_DEVICE_ID_THUNDER_L2C_CBC), },
194162306a36Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, PCI_DEVICE_ID_THUNDER_L2C_MCI), },
194262306a36Sopenharmony_ci	{ 0, },
194362306a36Sopenharmony_ci};
194462306a36Sopenharmony_ci
194562306a36Sopenharmony_cistatic int thunderx_l2c_probe(struct pci_dev *pdev,
194662306a36Sopenharmony_ci			      const struct pci_device_id *id)
194762306a36Sopenharmony_ci{
194862306a36Sopenharmony_ci	struct thunderx_l2c *l2c;
194962306a36Sopenharmony_ci	struct edac_device_ctl_info *edac_dev;
195062306a36Sopenharmony_ci	struct debugfs_entry **l2c_devattr;
195162306a36Sopenharmony_ci	size_t dfs_entries;
195262306a36Sopenharmony_ci	irqreturn_t (*thunderx_l2c_isr)(int, void *) = NULL;
195362306a36Sopenharmony_ci	char name[32];
195462306a36Sopenharmony_ci	const char *fmt;
195562306a36Sopenharmony_ci	u64 reg_en_offs, reg_en_mask;
195662306a36Sopenharmony_ci	int idx;
195762306a36Sopenharmony_ci	int ret;
195862306a36Sopenharmony_ci
195962306a36Sopenharmony_ci	ret = pcim_enable_device(pdev);
196062306a36Sopenharmony_ci	if (ret) {
196162306a36Sopenharmony_ci		dev_err(&pdev->dev, "Cannot enable PCI device: %d\n", ret);
196262306a36Sopenharmony_ci		return ret;
196362306a36Sopenharmony_ci	}
196462306a36Sopenharmony_ci
196562306a36Sopenharmony_ci	ret = pcim_iomap_regions(pdev, BIT(0), "thunderx_l2c");
196662306a36Sopenharmony_ci	if (ret) {
196762306a36Sopenharmony_ci		dev_err(&pdev->dev, "Cannot map PCI resources: %d\n", ret);
196862306a36Sopenharmony_ci		return ret;
196962306a36Sopenharmony_ci	}
197062306a36Sopenharmony_ci
197162306a36Sopenharmony_ci	switch (pdev->device) {
197262306a36Sopenharmony_ci	case PCI_DEVICE_ID_THUNDER_L2C_TAD:
197362306a36Sopenharmony_ci		thunderx_l2c_isr = thunderx_l2c_tad_isr;
197462306a36Sopenharmony_ci		l2c_devattr = l2c_tad_dfs_ents;
197562306a36Sopenharmony_ci		dfs_entries = ARRAY_SIZE(l2c_tad_dfs_ents);
197662306a36Sopenharmony_ci		fmt = "L2C-TAD%d";
197762306a36Sopenharmony_ci		reg_en_offs = L2C_TAD_INT_ENA_W1S;
197862306a36Sopenharmony_ci		reg_en_mask = L2C_TAD_INT_ENA_ALL;
197962306a36Sopenharmony_ci		break;
198062306a36Sopenharmony_ci	case PCI_DEVICE_ID_THUNDER_L2C_CBC:
198162306a36Sopenharmony_ci		thunderx_l2c_isr = thunderx_l2c_cbc_isr;
198262306a36Sopenharmony_ci		l2c_devattr = l2c_cbc_dfs_ents;
198362306a36Sopenharmony_ci		dfs_entries = ARRAY_SIZE(l2c_cbc_dfs_ents);
198462306a36Sopenharmony_ci		fmt = "L2C-CBC%d";
198562306a36Sopenharmony_ci		reg_en_offs = L2C_CBC_INT_ENA_W1S;
198662306a36Sopenharmony_ci		reg_en_mask = L2C_CBC_INT_ENA_ALL;
198762306a36Sopenharmony_ci		break;
198862306a36Sopenharmony_ci	case PCI_DEVICE_ID_THUNDER_L2C_MCI:
198962306a36Sopenharmony_ci		thunderx_l2c_isr = thunderx_l2c_mci_isr;
199062306a36Sopenharmony_ci		l2c_devattr = l2c_mci_dfs_ents;
199162306a36Sopenharmony_ci		dfs_entries = ARRAY_SIZE(l2c_mci_dfs_ents);
199262306a36Sopenharmony_ci		fmt = "L2C-MCI%d";
199362306a36Sopenharmony_ci		reg_en_offs = L2C_MCI_INT_ENA_W1S;
199462306a36Sopenharmony_ci		reg_en_mask = L2C_MCI_INT_ENA_ALL;
199562306a36Sopenharmony_ci		break;
199662306a36Sopenharmony_ci	default:
199762306a36Sopenharmony_ci		//Should never ever get here
199862306a36Sopenharmony_ci		dev_err(&pdev->dev, "Unsupported PCI device: %04x\n",
199962306a36Sopenharmony_ci			pdev->device);
200062306a36Sopenharmony_ci		return -EINVAL;
200162306a36Sopenharmony_ci	}
200262306a36Sopenharmony_ci
200362306a36Sopenharmony_ci	idx = edac_device_alloc_index();
200462306a36Sopenharmony_ci	snprintf(name, sizeof(name), fmt, idx);
200562306a36Sopenharmony_ci
200662306a36Sopenharmony_ci	edac_dev = edac_device_alloc_ctl_info(sizeof(struct thunderx_l2c),
200762306a36Sopenharmony_ci					      name, 1, "L2C", 1, 0,
200862306a36Sopenharmony_ci					      NULL, 0, idx);
200962306a36Sopenharmony_ci	if (!edac_dev) {
201062306a36Sopenharmony_ci		dev_err(&pdev->dev, "Cannot allocate EDAC device\n");
201162306a36Sopenharmony_ci		return -ENOMEM;
201262306a36Sopenharmony_ci	}
201362306a36Sopenharmony_ci
201462306a36Sopenharmony_ci	l2c = edac_dev->pvt_info;
201562306a36Sopenharmony_ci	l2c->edac_dev = edac_dev;
201662306a36Sopenharmony_ci
201762306a36Sopenharmony_ci	l2c->regs = pcim_iomap_table(pdev)[0];
201862306a36Sopenharmony_ci	if (!l2c->regs) {
201962306a36Sopenharmony_ci		dev_err(&pdev->dev, "Cannot map PCI resources\n");
202062306a36Sopenharmony_ci		ret = -ENODEV;
202162306a36Sopenharmony_ci		goto err_free;
202262306a36Sopenharmony_ci	}
202362306a36Sopenharmony_ci
202462306a36Sopenharmony_ci	l2c->pdev = pdev;
202562306a36Sopenharmony_ci
202662306a36Sopenharmony_ci	l2c->ring_head = 0;
202762306a36Sopenharmony_ci	l2c->ring_tail = 0;
202862306a36Sopenharmony_ci
202962306a36Sopenharmony_ci	l2c->msix_ent.entry = 0;
203062306a36Sopenharmony_ci	l2c->msix_ent.vector = 0;
203162306a36Sopenharmony_ci
203262306a36Sopenharmony_ci	ret = pci_enable_msix_exact(pdev, &l2c->msix_ent, 1);
203362306a36Sopenharmony_ci	if (ret) {
203462306a36Sopenharmony_ci		dev_err(&pdev->dev, "Cannot enable interrupt: %d\n", ret);
203562306a36Sopenharmony_ci		goto err_free;
203662306a36Sopenharmony_ci	}
203762306a36Sopenharmony_ci
203862306a36Sopenharmony_ci	ret = devm_request_threaded_irq(&pdev->dev, l2c->msix_ent.vector,
203962306a36Sopenharmony_ci					thunderx_l2c_isr,
204062306a36Sopenharmony_ci					thunderx_l2c_threaded_isr,
204162306a36Sopenharmony_ci					0, "[EDAC] ThunderX L2C",
204262306a36Sopenharmony_ci					&l2c->msix_ent);
204362306a36Sopenharmony_ci	if (ret)
204462306a36Sopenharmony_ci		goto err_free;
204562306a36Sopenharmony_ci
204662306a36Sopenharmony_ci	edac_dev->dev = &pdev->dev;
204762306a36Sopenharmony_ci	edac_dev->dev_name = dev_name(&pdev->dev);
204862306a36Sopenharmony_ci	edac_dev->mod_name = "thunderx-l2c";
204962306a36Sopenharmony_ci	edac_dev->ctl_name = "thunderx-l2c";
205062306a36Sopenharmony_ci
205162306a36Sopenharmony_ci	ret = edac_device_add_device(edac_dev);
205262306a36Sopenharmony_ci	if (ret) {
205362306a36Sopenharmony_ci		dev_err(&pdev->dev, "Cannot add EDAC device: %d\n", ret);
205462306a36Sopenharmony_ci		goto err_free;
205562306a36Sopenharmony_ci	}
205662306a36Sopenharmony_ci
205762306a36Sopenharmony_ci	if (IS_ENABLED(CONFIG_EDAC_DEBUG)) {
205862306a36Sopenharmony_ci		l2c->debugfs = edac_debugfs_create_dir(pdev->dev.kobj.name);
205962306a36Sopenharmony_ci
206062306a36Sopenharmony_ci		ret = thunderx_create_debugfs_nodes(l2c->debugfs, l2c_devattr,
206162306a36Sopenharmony_ci					      l2c, dfs_entries);
206262306a36Sopenharmony_ci
206362306a36Sopenharmony_ci		if (ret != dfs_entries) {
206462306a36Sopenharmony_ci			dev_warn(&pdev->dev, "Error creating debugfs entries: %d%s\n",
206562306a36Sopenharmony_ci				 ret, ret >= 0 ? " created" : "");
206662306a36Sopenharmony_ci		}
206762306a36Sopenharmony_ci	}
206862306a36Sopenharmony_ci
206962306a36Sopenharmony_ci	pci_set_drvdata(pdev, edac_dev);
207062306a36Sopenharmony_ci
207162306a36Sopenharmony_ci	writeq(reg_en_mask, l2c->regs + reg_en_offs);
207262306a36Sopenharmony_ci
207362306a36Sopenharmony_ci	return 0;
207462306a36Sopenharmony_ci
207562306a36Sopenharmony_cierr_free:
207662306a36Sopenharmony_ci	edac_device_free_ctl_info(edac_dev);
207762306a36Sopenharmony_ci
207862306a36Sopenharmony_ci	return ret;
207962306a36Sopenharmony_ci}
208062306a36Sopenharmony_ci
208162306a36Sopenharmony_cistatic void thunderx_l2c_remove(struct pci_dev *pdev)
208262306a36Sopenharmony_ci{
208362306a36Sopenharmony_ci	struct edac_device_ctl_info *edac_dev = pci_get_drvdata(pdev);
208462306a36Sopenharmony_ci	struct thunderx_l2c *l2c = edac_dev->pvt_info;
208562306a36Sopenharmony_ci
208662306a36Sopenharmony_ci	switch (pdev->device) {
208762306a36Sopenharmony_ci	case PCI_DEVICE_ID_THUNDER_L2C_TAD:
208862306a36Sopenharmony_ci		writeq(L2C_TAD_INT_ENA_ALL, l2c->regs + L2C_TAD_INT_ENA_W1C);
208962306a36Sopenharmony_ci		break;
209062306a36Sopenharmony_ci	case PCI_DEVICE_ID_THUNDER_L2C_CBC:
209162306a36Sopenharmony_ci		writeq(L2C_CBC_INT_ENA_ALL, l2c->regs + L2C_CBC_INT_ENA_W1C);
209262306a36Sopenharmony_ci		break;
209362306a36Sopenharmony_ci	case PCI_DEVICE_ID_THUNDER_L2C_MCI:
209462306a36Sopenharmony_ci		writeq(L2C_MCI_INT_ENA_ALL, l2c->regs + L2C_MCI_INT_ENA_W1C);
209562306a36Sopenharmony_ci		break;
209662306a36Sopenharmony_ci	}
209762306a36Sopenharmony_ci
209862306a36Sopenharmony_ci	edac_debugfs_remove_recursive(l2c->debugfs);
209962306a36Sopenharmony_ci
210062306a36Sopenharmony_ci	edac_device_del_device(&pdev->dev);
210162306a36Sopenharmony_ci	edac_device_free_ctl_info(edac_dev);
210262306a36Sopenharmony_ci}
210362306a36Sopenharmony_ci
210462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, thunderx_l2c_pci_tbl);
210562306a36Sopenharmony_ci
210662306a36Sopenharmony_cistatic struct pci_driver thunderx_l2c_driver = {
210762306a36Sopenharmony_ci	.name     = "thunderx_l2c_edac",
210862306a36Sopenharmony_ci	.probe    = thunderx_l2c_probe,
210962306a36Sopenharmony_ci	.remove   = thunderx_l2c_remove,
211062306a36Sopenharmony_ci	.id_table = thunderx_l2c_pci_tbl,
211162306a36Sopenharmony_ci};
211262306a36Sopenharmony_ci
211362306a36Sopenharmony_cistatic int __init thunderx_edac_init(void)
211462306a36Sopenharmony_ci{
211562306a36Sopenharmony_ci	int rc = 0;
211662306a36Sopenharmony_ci
211762306a36Sopenharmony_ci	if (ghes_get_devices())
211862306a36Sopenharmony_ci		return -EBUSY;
211962306a36Sopenharmony_ci
212062306a36Sopenharmony_ci	rc = pci_register_driver(&thunderx_lmc_driver);
212162306a36Sopenharmony_ci	if (rc)
212262306a36Sopenharmony_ci		return rc;
212362306a36Sopenharmony_ci
212462306a36Sopenharmony_ci	rc = pci_register_driver(&thunderx_ocx_driver);
212562306a36Sopenharmony_ci	if (rc)
212662306a36Sopenharmony_ci		goto err_lmc;
212762306a36Sopenharmony_ci
212862306a36Sopenharmony_ci	rc = pci_register_driver(&thunderx_l2c_driver);
212962306a36Sopenharmony_ci	if (rc)
213062306a36Sopenharmony_ci		goto err_ocx;
213162306a36Sopenharmony_ci
213262306a36Sopenharmony_ci	return rc;
213362306a36Sopenharmony_cierr_ocx:
213462306a36Sopenharmony_ci	pci_unregister_driver(&thunderx_ocx_driver);
213562306a36Sopenharmony_cierr_lmc:
213662306a36Sopenharmony_ci	pci_unregister_driver(&thunderx_lmc_driver);
213762306a36Sopenharmony_ci
213862306a36Sopenharmony_ci	return rc;
213962306a36Sopenharmony_ci}
214062306a36Sopenharmony_ci
214162306a36Sopenharmony_cistatic void __exit thunderx_edac_exit(void)
214262306a36Sopenharmony_ci{
214362306a36Sopenharmony_ci	pci_unregister_driver(&thunderx_l2c_driver);
214462306a36Sopenharmony_ci	pci_unregister_driver(&thunderx_ocx_driver);
214562306a36Sopenharmony_ci	pci_unregister_driver(&thunderx_lmc_driver);
214662306a36Sopenharmony_ci
214762306a36Sopenharmony_ci}
214862306a36Sopenharmony_ci
214962306a36Sopenharmony_cimodule_init(thunderx_edac_init);
215062306a36Sopenharmony_cimodule_exit(thunderx_edac_exit);
215162306a36Sopenharmony_ci
215262306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
215362306a36Sopenharmony_ciMODULE_AUTHOR("Cavium, Inc.");
215462306a36Sopenharmony_ciMODULE_DESCRIPTION("EDAC Driver for Cavium ThunderX");
2155