xref: /linux/drivers/edac/thunderx_edac.c (revision a1ff5a7d78a036d6c2178ee5acd6ba4946243800)
141003396SSergey Temerkhanov /*
241003396SSergey Temerkhanov  * Cavium ThunderX memory controller kernel module
341003396SSergey Temerkhanov  *
441003396SSergey Temerkhanov  * This file is subject to the terms and conditions of the GNU General Public
541003396SSergey Temerkhanov  * License.  See the file "COPYING" in the main directory of this archive
641003396SSergey Temerkhanov  * for more details.
741003396SSergey Temerkhanov  *
841003396SSergey Temerkhanov  * Copyright Cavium, Inc. (C) 2015-2017. All rights reserved.
941003396SSergey Temerkhanov  *
1041003396SSergey Temerkhanov  */
1141003396SSergey Temerkhanov 
1241003396SSergey Temerkhanov #include <linux/module.h>
1341003396SSergey Temerkhanov #include <linux/pci.h>
1441003396SSergey Temerkhanov #include <linux/edac.h>
1541003396SSergey Temerkhanov #include <linux/interrupt.h>
1641003396SSergey Temerkhanov #include <linux/string.h>
1741003396SSergey Temerkhanov #include <linux/stop_machine.h>
1841003396SSergey Temerkhanov #include <linux/delay.h>
1941003396SSergey Temerkhanov #include <linux/sizes.h>
2041003396SSergey Temerkhanov #include <linux/atomic.h>
2141003396SSergey Temerkhanov #include <linux/bitfield.h>
2241003396SSergey Temerkhanov #include <linux/circ_buf.h>
2341003396SSergey Temerkhanov 
2441003396SSergey Temerkhanov #include <asm/page.h>
2541003396SSergey Temerkhanov 
2641003396SSergey Temerkhanov #include "edac_module.h"
2741003396SSergey Temerkhanov 
2841003396SSergey Temerkhanov #define phys_to_pfn(phys)	(PFN_DOWN(phys))
2941003396SSergey Temerkhanov 
3041003396SSergey Temerkhanov #define THUNDERX_NODE		GENMASK(45, 44)
3141003396SSergey Temerkhanov 
3241003396SSergey Temerkhanov enum {
3341003396SSergey Temerkhanov 	ERR_CORRECTED	= 1,
3441003396SSergey Temerkhanov 	ERR_UNCORRECTED	= 2,
3541003396SSergey Temerkhanov 	ERR_UNKNOWN	= 3,
3641003396SSergey Temerkhanov };
3741003396SSergey Temerkhanov 
3841003396SSergey Temerkhanov struct error_descr {
3941003396SSergey Temerkhanov 	int	type;
4041003396SSergey Temerkhanov 	u64	mask;
4141003396SSergey Temerkhanov 	char	*descr;
4241003396SSergey Temerkhanov };
4341003396SSergey Temerkhanov 
decode_register(char * str,size_t size,const struct error_descr * descr,const uint64_t reg)4441003396SSergey Temerkhanov static void decode_register(char *str, size_t size,
4541003396SSergey Temerkhanov 			   const struct error_descr *descr,
4641003396SSergey Temerkhanov 			   const uint64_t reg)
4741003396SSergey Temerkhanov {
4841003396SSergey Temerkhanov 	int ret = 0;
4941003396SSergey Temerkhanov 
5041003396SSergey Temerkhanov 	while (descr->type && descr->mask && descr->descr) {
5141003396SSergey Temerkhanov 		if (reg & descr->mask) {
5241003396SSergey Temerkhanov 			ret = snprintf(str, size, "\n\t%s, %s",
5341003396SSergey Temerkhanov 				       descr->type == ERR_CORRECTED ?
5441003396SSergey Temerkhanov 					 "Corrected" : "Uncorrected",
5541003396SSergey Temerkhanov 				       descr->descr);
5641003396SSergey Temerkhanov 			str += ret;
5741003396SSergey Temerkhanov 			size -= ret;
5841003396SSergey Temerkhanov 		}
5941003396SSergey Temerkhanov 		descr++;
6041003396SSergey Temerkhanov 	}
6141003396SSergey Temerkhanov }
6241003396SSergey Temerkhanov 
get_bits(unsigned long data,int pos,int width)6341003396SSergey Temerkhanov static unsigned long get_bits(unsigned long data, int pos, int width)
6441003396SSergey Temerkhanov {
6541003396SSergey Temerkhanov 	return (data >> pos) & ((1 << width) - 1);
6641003396SSergey Temerkhanov }
6741003396SSergey Temerkhanov 
6841003396SSergey Temerkhanov #define L2C_CTL			0x87E080800000
6941003396SSergey Temerkhanov #define L2C_CTL_DISIDXALIAS	BIT(0)
7041003396SSergey Temerkhanov 
7141003396SSergey Temerkhanov #define PCI_DEVICE_ID_THUNDER_LMC 0xa022
7241003396SSergey Temerkhanov 
7341003396SSergey Temerkhanov #define LMC_FADR		0x20
7441003396SSergey Temerkhanov #define LMC_FADR_FDIMM(x)	((x >> 37) & 0x1)
7541003396SSergey Temerkhanov #define LMC_FADR_FBUNK(x)	((x >> 36) & 0x1)
7641003396SSergey Temerkhanov #define LMC_FADR_FBANK(x)	((x >> 32) & 0xf)
7741003396SSergey Temerkhanov #define LMC_FADR_FROW(x)	((x >> 14) & 0xffff)
7841003396SSergey Temerkhanov #define LMC_FADR_FCOL(x)	((x >> 0) & 0x1fff)
7941003396SSergey Temerkhanov 
8041003396SSergey Temerkhanov #define LMC_NXM_FADR		0x28
8141003396SSergey Temerkhanov #define LMC_ECC_SYND		0x38
8241003396SSergey Temerkhanov 
8341003396SSergey Temerkhanov #define LMC_ECC_PARITY_TEST	0x108
8441003396SSergey Temerkhanov 
8541003396SSergey Temerkhanov #define LMC_INT_W1S		0x150
8641003396SSergey Temerkhanov 
8741003396SSergey Temerkhanov #define LMC_INT_ENA_W1C		0x158
8841003396SSergey Temerkhanov #define LMC_INT_ENA_W1S		0x160
8941003396SSergey Temerkhanov 
9041003396SSergey Temerkhanov #define LMC_CONFIG		0x188
9141003396SSergey Temerkhanov 
9241003396SSergey Temerkhanov #define LMC_CONFIG_BG2		BIT(62)
9341003396SSergey Temerkhanov #define LMC_CONFIG_RANK_ENA	BIT(42)
9441003396SSergey Temerkhanov #define LMC_CONFIG_PBANK_LSB(x)	(((x) >> 5) & 0xF)
9541003396SSergey Temerkhanov #define LMC_CONFIG_ROW_LSB(x)	(((x) >> 2) & 0x7)
9641003396SSergey Temerkhanov 
9741003396SSergey Temerkhanov #define LMC_CONTROL		0x190
9841003396SSergey Temerkhanov #define LMC_CONTROL_XOR_BANK	BIT(16)
9941003396SSergey Temerkhanov 
10041003396SSergey Temerkhanov #define LMC_INT			0x1F0
10141003396SSergey Temerkhanov 
10241003396SSergey Temerkhanov #define LMC_INT_DDR_ERR		BIT(11)
10341003396SSergey Temerkhanov #define LMC_INT_DED_ERR		(0xFUL << 5)
10441003396SSergey Temerkhanov #define LMC_INT_SEC_ERR         (0xFUL << 1)
10541003396SSergey Temerkhanov #define LMC_INT_NXM_WR_MASK	BIT(0)
10641003396SSergey Temerkhanov 
10741003396SSergey Temerkhanov #define LMC_DDR_PLL_CTL		0x258
10841003396SSergey Temerkhanov #define LMC_DDR_PLL_CTL_DDR4	BIT(29)
10941003396SSergey Temerkhanov 
11041003396SSergey Temerkhanov #define LMC_FADR_SCRAMBLED	0x330
11141003396SSergey Temerkhanov 
11241003396SSergey Temerkhanov #define LMC_INT_UE              (LMC_INT_DDR_ERR | LMC_INT_DED_ERR | \
11341003396SSergey Temerkhanov 				 LMC_INT_NXM_WR_MASK)
11441003396SSergey Temerkhanov 
11541003396SSergey Temerkhanov #define LMC_INT_CE		(LMC_INT_SEC_ERR)
11641003396SSergey Temerkhanov 
11741003396SSergey Temerkhanov static const struct error_descr lmc_errors[] = {
11841003396SSergey Temerkhanov 	{
11941003396SSergey Temerkhanov 		.type  = ERR_CORRECTED,
12041003396SSergey Temerkhanov 		.mask  = LMC_INT_SEC_ERR,
12141003396SSergey Temerkhanov 		.descr = "Single-bit ECC error",
12241003396SSergey Temerkhanov 	},
12341003396SSergey Temerkhanov 	{
12441003396SSergey Temerkhanov 		.type  = ERR_UNCORRECTED,
12541003396SSergey Temerkhanov 		.mask  = LMC_INT_DDR_ERR,
12641003396SSergey Temerkhanov 		.descr = "DDR chip error",
12741003396SSergey Temerkhanov 	},
12841003396SSergey Temerkhanov 	{
12941003396SSergey Temerkhanov 		.type  = ERR_UNCORRECTED,
13041003396SSergey Temerkhanov 		.mask  = LMC_INT_DED_ERR,
13141003396SSergey Temerkhanov 		.descr = "Double-bit ECC error",
13241003396SSergey Temerkhanov 	},
13341003396SSergey Temerkhanov 	{
13441003396SSergey Temerkhanov 		.type = ERR_UNCORRECTED,
13541003396SSergey Temerkhanov 		.mask = LMC_INT_NXM_WR_MASK,
13641003396SSergey Temerkhanov 		.descr = "Non-existent memory write",
13741003396SSergey Temerkhanov 	},
13841003396SSergey Temerkhanov 	{0, 0, NULL},
13941003396SSergey Temerkhanov };
14041003396SSergey Temerkhanov 
14141003396SSergey Temerkhanov #define LMC_INT_EN_DDR_ERROR_ALERT_ENA	BIT(5)
14241003396SSergey Temerkhanov #define LMC_INT_EN_DLCRAM_DED_ERR	BIT(4)
14341003396SSergey Temerkhanov #define LMC_INT_EN_DLCRAM_SEC_ERR	BIT(3)
14441003396SSergey Temerkhanov #define LMC_INT_INTR_DED_ENA		BIT(2)
14541003396SSergey Temerkhanov #define LMC_INT_INTR_SEC_ENA		BIT(1)
14641003396SSergey Temerkhanov #define LMC_INT_INTR_NXM_WR_ENA		BIT(0)
14741003396SSergey Temerkhanov 
14841003396SSergey Temerkhanov #define LMC_INT_ENA_ALL			GENMASK(5, 0)
14941003396SSergey Temerkhanov 
15041003396SSergey Temerkhanov #define LMC_DDR_PLL_CTL		0x258
15141003396SSergey Temerkhanov #define LMC_DDR_PLL_CTL_DDR4	BIT(29)
15241003396SSergey Temerkhanov 
15341003396SSergey Temerkhanov #define LMC_CONTROL		0x190
15441003396SSergey Temerkhanov #define LMC_CONTROL_RDIMM	BIT(0)
15541003396SSergey Temerkhanov 
15641003396SSergey Temerkhanov #define LMC_SCRAM_FADR		0x330
15741003396SSergey Temerkhanov 
15841003396SSergey Temerkhanov #define LMC_CHAR_MASK0		0x228
15941003396SSergey Temerkhanov #define LMC_CHAR_MASK2		0x238
16041003396SSergey Temerkhanov 
16141003396SSergey Temerkhanov #define RING_ENTRIES	8
16241003396SSergey Temerkhanov 
16341003396SSergey Temerkhanov struct debugfs_entry {
16441003396SSergey Temerkhanov 	const char *name;
16541003396SSergey Temerkhanov 	umode_t mode;
16641003396SSergey Temerkhanov 	const struct file_operations fops;
16741003396SSergey Temerkhanov };
16841003396SSergey Temerkhanov 
16941003396SSergey Temerkhanov struct lmc_err_ctx {
17041003396SSergey Temerkhanov 	u64 reg_int;
17141003396SSergey Temerkhanov 	u64 reg_fadr;
17241003396SSergey Temerkhanov 	u64 reg_nxm_fadr;
17341003396SSergey Temerkhanov 	u64 reg_scram_fadr;
17441003396SSergey Temerkhanov 	u64 reg_ecc_synd;
17541003396SSergey Temerkhanov };
17641003396SSergey Temerkhanov 
17741003396SSergey Temerkhanov struct thunderx_lmc {
17841003396SSergey Temerkhanov 	void __iomem *regs;
17941003396SSergey Temerkhanov 	struct pci_dev *pdev;
18041003396SSergey Temerkhanov 	struct msix_entry msix_ent;
18141003396SSergey Temerkhanov 
18241003396SSergey Temerkhanov 	atomic_t ecc_int;
18341003396SSergey Temerkhanov 
18441003396SSergey Temerkhanov 	u64 mask0;
18541003396SSergey Temerkhanov 	u64 mask2;
18641003396SSergey Temerkhanov 	u64 parity_test;
18741003396SSergey Temerkhanov 	u64 node;
18841003396SSergey Temerkhanov 
18941003396SSergey Temerkhanov 	int xbits;
19041003396SSergey Temerkhanov 	int bank_width;
19141003396SSergey Temerkhanov 	int pbank_lsb;
19241003396SSergey Temerkhanov 	int dimm_lsb;
19341003396SSergey Temerkhanov 	int rank_lsb;
19441003396SSergey Temerkhanov 	int bank_lsb;
19541003396SSergey Temerkhanov 	int row_lsb;
19641003396SSergey Temerkhanov 	int col_hi_lsb;
19741003396SSergey Temerkhanov 
19841003396SSergey Temerkhanov 	int xor_bank;
19941003396SSergey Temerkhanov 	int l2c_alias;
20041003396SSergey Temerkhanov 
20141003396SSergey Temerkhanov 	struct page *mem;
20241003396SSergey Temerkhanov 
20341003396SSergey Temerkhanov 	struct lmc_err_ctx err_ctx[RING_ENTRIES];
20441003396SSergey Temerkhanov 	unsigned long ring_head;
20541003396SSergey Temerkhanov 	unsigned long ring_tail;
20641003396SSergey Temerkhanov };
20741003396SSergey Temerkhanov 
20841003396SSergey Temerkhanov #define ring_pos(pos, size) ((pos) & (size - 1))
20941003396SSergey Temerkhanov 
21041003396SSergey Temerkhanov #define DEBUGFS_STRUCT(_name, _mode, _write, _read)			    \
21141003396SSergey Temerkhanov static struct debugfs_entry debugfs_##_name = {				    \
21241003396SSergey Temerkhanov 	.name = __stringify(_name),					    \
21341003396SSergey Temerkhanov 	.mode = VERIFY_OCTAL_PERMISSIONS(_mode),			    \
21441003396SSergey Temerkhanov 	.fops = {							    \
21541003396SSergey Temerkhanov 		.open = simple_open,					    \
21641003396SSergey Temerkhanov 		.write = _write,					    \
21741003396SSergey Temerkhanov 		.read  = _read,						    \
21841003396SSergey Temerkhanov 		.llseek = generic_file_llseek,				    \
21941003396SSergey Temerkhanov 	},								    \
22041003396SSergey Temerkhanov }
22141003396SSergey Temerkhanov 
22241003396SSergey Temerkhanov #define DEBUGFS_FIELD_ATTR(_type, _field)				    \
22341003396SSergey Temerkhanov static ssize_t thunderx_##_type##_##_field##_read(struct file *file,	    \
22441003396SSergey Temerkhanov 					    char __user *data,		    \
22541003396SSergey Temerkhanov 					    size_t count, loff_t *ppos)	    \
22641003396SSergey Temerkhanov {									    \
22741003396SSergey Temerkhanov 	struct thunderx_##_type *pdata = file->private_data;		    \
22841003396SSergey Temerkhanov 	char buf[20];							    \
22941003396SSergey Temerkhanov 									    \
23041003396SSergey Temerkhanov 	snprintf(buf, count, "0x%016llx", pdata->_field);		    \
23141003396SSergey Temerkhanov 	return simple_read_from_buffer(data, count, ppos,		    \
23241003396SSergey Temerkhanov 				       buf, sizeof(buf));		    \
23341003396SSergey Temerkhanov }									    \
23441003396SSergey Temerkhanov 									    \
23541003396SSergey Temerkhanov static ssize_t thunderx_##_type##_##_field##_write(struct file *file,	    \
23641003396SSergey Temerkhanov 					     const char __user *data,	    \
23741003396SSergey Temerkhanov 					     size_t count, loff_t *ppos)    \
23841003396SSergey Temerkhanov {									    \
23941003396SSergey Temerkhanov 	struct thunderx_##_type *pdata = file->private_data;		    \
24041003396SSergey Temerkhanov 	int res;							    \
24141003396SSergey Temerkhanov 									    \
24241003396SSergey Temerkhanov 	res = kstrtoull_from_user(data, count, 0, &pdata->_field);	    \
24341003396SSergey Temerkhanov 									    \
24441003396SSergey Temerkhanov 	return res ? res : count;					    \
24541003396SSergey Temerkhanov }									    \
24641003396SSergey Temerkhanov 									    \
24741003396SSergey Temerkhanov DEBUGFS_STRUCT(_field, 0600,						    \
24841003396SSergey Temerkhanov 		   thunderx_##_type##_##_field##_write,			    \
24941003396SSergey Temerkhanov 		   thunderx_##_type##_##_field##_read)			    \
25041003396SSergey Temerkhanov 
25141003396SSergey Temerkhanov #define DEBUGFS_REG_ATTR(_type, _name, _reg)				    \
25241003396SSergey Temerkhanov static ssize_t thunderx_##_type##_##_name##_read(struct file *file,	    \
25341003396SSergey Temerkhanov 					   char __user *data,		    \
25441003396SSergey Temerkhanov 					   size_t count, loff_t *ppos)      \
25541003396SSergey Temerkhanov {									    \
25641003396SSergey Temerkhanov 	struct thunderx_##_type *pdata = file->private_data;		    \
25741003396SSergey Temerkhanov 	char buf[20];							    \
25841003396SSergey Temerkhanov 									    \
25941003396SSergey Temerkhanov 	sprintf(buf, "0x%016llx", readq(pdata->regs + _reg));		    \
26041003396SSergey Temerkhanov 	return simple_read_from_buffer(data, count, ppos,		    \
26141003396SSergey Temerkhanov 				       buf, sizeof(buf));		    \
26241003396SSergey Temerkhanov }									    \
26341003396SSergey Temerkhanov 									    \
26441003396SSergey Temerkhanov static ssize_t thunderx_##_type##_##_name##_write(struct file *file,	    \
26541003396SSergey Temerkhanov 					    const char __user *data,	    \
26641003396SSergey Temerkhanov 					    size_t count, loff_t *ppos)     \
26741003396SSergey Temerkhanov {									    \
26841003396SSergey Temerkhanov 	struct thunderx_##_type *pdata = file->private_data;		    \
26941003396SSergey Temerkhanov 	u64 val;							    \
27041003396SSergey Temerkhanov 	int res;							    \
27141003396SSergey Temerkhanov 									    \
27241003396SSergey Temerkhanov 	res = kstrtoull_from_user(data, count, 0, &val);		    \
27341003396SSergey Temerkhanov 									    \
27441003396SSergey Temerkhanov 	if (!res) {							    \
27541003396SSergey Temerkhanov 		writeq(val, pdata->regs + _reg);			    \
27641003396SSergey Temerkhanov 		res = count;						    \
27741003396SSergey Temerkhanov 	}								    \
27841003396SSergey Temerkhanov 									    \
27941003396SSergey Temerkhanov 	return res;							    \
28041003396SSergey Temerkhanov }									    \
28141003396SSergey Temerkhanov 									    \
28241003396SSergey Temerkhanov DEBUGFS_STRUCT(_name, 0600,						    \
28341003396SSergey Temerkhanov 	       thunderx_##_type##_##_name##_write,			    \
28441003396SSergey Temerkhanov 	       thunderx_##_type##_##_name##_read)
28541003396SSergey Temerkhanov 
28641003396SSergey Temerkhanov #define LMC_DEBUGFS_ENT(_field)	DEBUGFS_FIELD_ATTR(lmc, _field)
28741003396SSergey Temerkhanov 
28841003396SSergey Temerkhanov /*
28941003396SSergey Temerkhanov  * To get an ECC error injected, the following steps are needed:
29041003396SSergey Temerkhanov  * - Setup the ECC injection by writing the appropriate parameters:
29141003396SSergey Temerkhanov  *	echo <bit mask value> > /sys/kernel/debug/<device number>/ecc_mask0
29241003396SSergey Temerkhanov  *	echo <bit mask value> > /sys/kernel/debug/<device number>/ecc_mask2
29341003396SSergey Temerkhanov  *	echo 0x802 > /sys/kernel/debug/<device number>/ecc_parity_test
29441003396SSergey Temerkhanov  * - Do the actual injection:
29541003396SSergey Temerkhanov  *	echo 1 > /sys/kernel/debug/<device number>/inject_ecc
29641003396SSergey Temerkhanov  */
thunderx_lmc_inject_int_write(struct file * file,const char __user * data,size_t count,loff_t * ppos)29741003396SSergey Temerkhanov static ssize_t thunderx_lmc_inject_int_write(struct file *file,
29841003396SSergey Temerkhanov 					     const char __user *data,
29941003396SSergey Temerkhanov 					     size_t count, loff_t *ppos)
30041003396SSergey Temerkhanov {
30141003396SSergey Temerkhanov 	struct thunderx_lmc *lmc = file->private_data;
30241003396SSergey Temerkhanov 	u64 val;
30341003396SSergey Temerkhanov 	int res;
30441003396SSergey Temerkhanov 
30541003396SSergey Temerkhanov 	res = kstrtoull_from_user(data, count, 0, &val);
30641003396SSergey Temerkhanov 
30741003396SSergey Temerkhanov 	if (!res) {
30841003396SSergey Temerkhanov 		/* Trigger the interrupt */
30941003396SSergey Temerkhanov 		writeq(val, lmc->regs + LMC_INT_W1S);
31041003396SSergey Temerkhanov 		res = count;
31141003396SSergey Temerkhanov 	}
31241003396SSergey Temerkhanov 
31341003396SSergey Temerkhanov 	return res;
31441003396SSergey Temerkhanov }
31541003396SSergey Temerkhanov 
thunderx_lmc_int_read(struct file * file,char __user * data,size_t count,loff_t * ppos)31641003396SSergey Temerkhanov static ssize_t thunderx_lmc_int_read(struct file *file,
31741003396SSergey Temerkhanov 				     char __user *data,
31841003396SSergey Temerkhanov 				     size_t count, loff_t *ppos)
31941003396SSergey Temerkhanov {
32041003396SSergey Temerkhanov 	struct thunderx_lmc *lmc = file->private_data;
32141003396SSergey Temerkhanov 	char buf[20];
32241003396SSergey Temerkhanov 	u64 lmc_int = readq(lmc->regs + LMC_INT);
32341003396SSergey Temerkhanov 
32441003396SSergey Temerkhanov 	snprintf(buf, sizeof(buf), "0x%016llx", lmc_int);
32541003396SSergey Temerkhanov 	return simple_read_from_buffer(data, count, ppos, buf, sizeof(buf));
32641003396SSergey Temerkhanov }
32741003396SSergey Temerkhanov 
32841003396SSergey Temerkhanov #define TEST_PATTERN 0xa5
32941003396SSergey Temerkhanov 
inject_ecc_fn(void * arg)33041003396SSergey Temerkhanov static int inject_ecc_fn(void *arg)
33141003396SSergey Temerkhanov {
33241003396SSergey Temerkhanov 	struct thunderx_lmc *lmc = arg;
33341003396SSergey Temerkhanov 	uintptr_t addr, phys;
33441003396SSergey Temerkhanov 	unsigned int cline_size = cache_line_size();
33541003396SSergey Temerkhanov 	const unsigned int lines = PAGE_SIZE / cline_size;
33641003396SSergey Temerkhanov 	unsigned int i, cl_idx;
33741003396SSergey Temerkhanov 
33841003396SSergey Temerkhanov 	addr = (uintptr_t)page_address(lmc->mem);
33941003396SSergey Temerkhanov 	phys = (uintptr_t)page_to_phys(lmc->mem);
34041003396SSergey Temerkhanov 
34141003396SSergey Temerkhanov 	cl_idx = (phys & 0x7f) >> 4;
34241003396SSergey Temerkhanov 	lmc->parity_test &= ~(7ULL << 8);
34341003396SSergey Temerkhanov 	lmc->parity_test |= (cl_idx << 8);
34441003396SSergey Temerkhanov 
34541003396SSergey Temerkhanov 	writeq(lmc->mask0, lmc->regs + LMC_CHAR_MASK0);
34641003396SSergey Temerkhanov 	writeq(lmc->mask2, lmc->regs + LMC_CHAR_MASK2);
34741003396SSergey Temerkhanov 	writeq(lmc->parity_test, lmc->regs + LMC_ECC_PARITY_TEST);
34841003396SSergey Temerkhanov 
34941003396SSergey Temerkhanov 	readq(lmc->regs + LMC_CHAR_MASK0);
35041003396SSergey Temerkhanov 	readq(lmc->regs + LMC_CHAR_MASK2);
35141003396SSergey Temerkhanov 	readq(lmc->regs + LMC_ECC_PARITY_TEST);
35241003396SSergey Temerkhanov 
35341003396SSergey Temerkhanov 	for (i = 0; i < lines; i++) {
35441003396SSergey Temerkhanov 		memset((void *)addr, TEST_PATTERN, cline_size);
35541003396SSergey Temerkhanov 		barrier();
35641003396SSergey Temerkhanov 
35741003396SSergey Temerkhanov 		/*
35841003396SSergey Temerkhanov 		 * Flush L1 cachelines to the PoC (L2).
35941003396SSergey Temerkhanov 		 * This will cause cacheline eviction to the L2.
36041003396SSergey Temerkhanov 		 */
36141003396SSergey Temerkhanov 		asm volatile("dc civac, %0\n"
36241003396SSergey Temerkhanov 			     "dsb sy\n"
36341003396SSergey Temerkhanov 			     : : "r"(addr + i * cline_size));
36441003396SSergey Temerkhanov 	}
36541003396SSergey Temerkhanov 
36641003396SSergey Temerkhanov 	for (i = 0; i < lines; i++) {
36741003396SSergey Temerkhanov 		/*
36841003396SSergey Temerkhanov 		 * Flush L2 cachelines to the DRAM.
36941003396SSergey Temerkhanov 		 * This will cause cacheline eviction to the DRAM
37041003396SSergey Temerkhanov 		 * and ECC corruption according to the masks set.
37141003396SSergey Temerkhanov 		 */
37241003396SSergey Temerkhanov 		__asm__ volatile("sys #0,c11,C1,#2, %0\n"
37341003396SSergey Temerkhanov 				 : : "r"(phys + i * cline_size));
37441003396SSergey Temerkhanov 	}
37541003396SSergey Temerkhanov 
37641003396SSergey Temerkhanov 	for (i = 0; i < lines; i++) {
37741003396SSergey Temerkhanov 		/*
37841003396SSergey Temerkhanov 		 * Invalidate L2 cachelines.
37941003396SSergey Temerkhanov 		 * The subsequent load will cause cacheline fetch
38041003396SSergey Temerkhanov 		 * from the DRAM and an error interrupt
38141003396SSergey Temerkhanov 		 */
38241003396SSergey Temerkhanov 		__asm__ volatile("sys #0,c11,C1,#1, %0"
38341003396SSergey Temerkhanov 				 : : "r"(phys + i * cline_size));
38441003396SSergey Temerkhanov 	}
38541003396SSergey Temerkhanov 
38641003396SSergey Temerkhanov 	for (i = 0; i < lines; i++) {
38741003396SSergey Temerkhanov 		/*
38841003396SSergey Temerkhanov 		 * Invalidate L1 cachelines.
38941003396SSergey Temerkhanov 		 * The subsequent load will cause cacheline fetch
39041003396SSergey Temerkhanov 		 * from the L2 and/or DRAM
39141003396SSergey Temerkhanov 		 */
39241003396SSergey Temerkhanov 		asm volatile("dc ivac, %0\n"
39341003396SSergey Temerkhanov 			     "dsb sy\n"
39441003396SSergey Temerkhanov 			     : : "r"(addr + i * cline_size));
39541003396SSergey Temerkhanov 	}
39641003396SSergey Temerkhanov 
39741003396SSergey Temerkhanov 	return 0;
39841003396SSergey Temerkhanov }
39941003396SSergey Temerkhanov 
thunderx_lmc_inject_ecc_write(struct file * file,const char __user * data,size_t count,loff_t * ppos)40041003396SSergey Temerkhanov static ssize_t thunderx_lmc_inject_ecc_write(struct file *file,
40141003396SSergey Temerkhanov 					     const char __user *data,
40241003396SSergey Temerkhanov 					     size_t count, loff_t *ppos)
40341003396SSergey Temerkhanov {
40441003396SSergey Temerkhanov 	struct thunderx_lmc *lmc = file->private_data;
40541003396SSergey Temerkhanov 	unsigned int cline_size = cache_line_size();
4066663484bSKees Cook 	u8 *tmp;
40741003396SSergey Temerkhanov 	void __iomem *addr;
40841003396SSergey Temerkhanov 	unsigned int offs, timeout = 100000;
40941003396SSergey Temerkhanov 
41041003396SSergey Temerkhanov 	atomic_set(&lmc->ecc_int, 0);
41141003396SSergey Temerkhanov 
41241003396SSergey Temerkhanov 	lmc->mem = alloc_pages_node(lmc->node, GFP_KERNEL, 0);
41341003396SSergey Temerkhanov 	if (!lmc->mem)
41441003396SSergey Temerkhanov 		return -ENOMEM;
41541003396SSergey Temerkhanov 
4166663484bSKees Cook 	tmp = kmalloc(cline_size, GFP_KERNEL);
4176663484bSKees Cook 	if (!tmp) {
4186663484bSKees Cook 		__free_pages(lmc->mem, 0);
4196663484bSKees Cook 		return -ENOMEM;
4206663484bSKees Cook 	}
4216663484bSKees Cook 
42241003396SSergey Temerkhanov 	addr = page_address(lmc->mem);
42341003396SSergey Temerkhanov 
42441003396SSergey Temerkhanov 	while (!atomic_read(&lmc->ecc_int) && timeout--) {
42541003396SSergey Temerkhanov 		stop_machine(inject_ecc_fn, lmc, NULL);
42641003396SSergey Temerkhanov 
4276663484bSKees Cook 		for (offs = 0; offs < PAGE_SIZE; offs += cline_size) {
42841003396SSergey Temerkhanov 			/*
42941003396SSergey Temerkhanov 			 * Do a load from the previously rigged location
43041003396SSergey Temerkhanov 			 * This should generate an error interrupt.
43141003396SSergey Temerkhanov 			 */
43241003396SSergey Temerkhanov 			memcpy(tmp, addr + offs, cline_size);
43341003396SSergey Temerkhanov 			asm volatile("dsb ld\n");
43441003396SSergey Temerkhanov 		}
43541003396SSergey Temerkhanov 	}
43641003396SSergey Temerkhanov 
4376663484bSKees Cook 	kfree(tmp);
43841003396SSergey Temerkhanov 	__free_pages(lmc->mem, 0);
43941003396SSergey Temerkhanov 
44041003396SSergey Temerkhanov 	return count;
44141003396SSergey Temerkhanov }
44241003396SSergey Temerkhanov 
44341003396SSergey Temerkhanov LMC_DEBUGFS_ENT(mask0);
44441003396SSergey Temerkhanov LMC_DEBUGFS_ENT(mask2);
44541003396SSergey Temerkhanov LMC_DEBUGFS_ENT(parity_test);
44641003396SSergey Temerkhanov 
44741003396SSergey Temerkhanov DEBUGFS_STRUCT(inject_int, 0200, thunderx_lmc_inject_int_write, NULL);
44841003396SSergey Temerkhanov DEBUGFS_STRUCT(inject_ecc, 0200, thunderx_lmc_inject_ecc_write, NULL);
44941003396SSergey Temerkhanov DEBUGFS_STRUCT(int_w1c, 0400, NULL, thunderx_lmc_int_read);
45041003396SSergey Temerkhanov 
451bd17e0b7SWei Yongjun static struct debugfs_entry *lmc_dfs_ents[] = {
45241003396SSergey Temerkhanov 	&debugfs_mask0,
45341003396SSergey Temerkhanov 	&debugfs_mask2,
45441003396SSergey Temerkhanov 	&debugfs_parity_test,
45541003396SSergey Temerkhanov 	&debugfs_inject_ecc,
45641003396SSergey Temerkhanov 	&debugfs_inject_int,
45741003396SSergey Temerkhanov 	&debugfs_int_w1c,
45841003396SSergey Temerkhanov };
45941003396SSergey Temerkhanov 
thunderx_create_debugfs_nodes(struct dentry * parent,struct debugfs_entry * attrs[],void * data,size_t num)46041003396SSergey Temerkhanov static int thunderx_create_debugfs_nodes(struct dentry *parent,
46141003396SSergey Temerkhanov 					  struct debugfs_entry *attrs[],
46241003396SSergey Temerkhanov 					  void *data,
46341003396SSergey Temerkhanov 					  size_t num)
46441003396SSergey Temerkhanov {
46541003396SSergey Temerkhanov 	int i;
46641003396SSergey Temerkhanov 	struct dentry *ent;
46741003396SSergey Temerkhanov 
46841003396SSergey Temerkhanov 	if (!IS_ENABLED(CONFIG_EDAC_DEBUG))
46941003396SSergey Temerkhanov 		return 0;
47041003396SSergey Temerkhanov 
47141003396SSergey Temerkhanov 	if (!parent)
47241003396SSergey Temerkhanov 		return -ENOENT;
47341003396SSergey Temerkhanov 
47441003396SSergey Temerkhanov 	for (i = 0; i < num; i++) {
47541003396SSergey Temerkhanov 		ent = edac_debugfs_create_file(attrs[i]->name, attrs[i]->mode,
47641003396SSergey Temerkhanov 					       parent, data, &attrs[i]->fops);
47741003396SSergey Temerkhanov 
478bf5c04ddSYeqi Fu 		if (IS_ERR(ent))
47941003396SSergey Temerkhanov 			break;
48041003396SSergey Temerkhanov 	}
48141003396SSergey Temerkhanov 
48241003396SSergey Temerkhanov 	return i;
48341003396SSergey Temerkhanov }
48441003396SSergey Temerkhanov 
thunderx_faddr_to_phys(u64 faddr,struct thunderx_lmc * lmc)48541003396SSergey Temerkhanov static phys_addr_t thunderx_faddr_to_phys(u64 faddr, struct thunderx_lmc *lmc)
48641003396SSergey Temerkhanov {
48741003396SSergey Temerkhanov 	phys_addr_t addr = 0;
48841003396SSergey Temerkhanov 	int bank, xbits;
48941003396SSergey Temerkhanov 
49041003396SSergey Temerkhanov 	addr |= lmc->node << 40;
49141003396SSergey Temerkhanov 	addr |= LMC_FADR_FDIMM(faddr) << lmc->dimm_lsb;
49241003396SSergey Temerkhanov 	addr |= LMC_FADR_FBUNK(faddr) << lmc->rank_lsb;
49341003396SSergey Temerkhanov 	addr |= LMC_FADR_FROW(faddr) << lmc->row_lsb;
49441003396SSergey Temerkhanov 	addr |= (LMC_FADR_FCOL(faddr) >> 4) << lmc->col_hi_lsb;
49541003396SSergey Temerkhanov 
49641003396SSergey Temerkhanov 	bank = LMC_FADR_FBANK(faddr) << lmc->bank_lsb;
49741003396SSergey Temerkhanov 
49841003396SSergey Temerkhanov 	if (lmc->xor_bank)
49941003396SSergey Temerkhanov 		bank ^= get_bits(addr, 12 + lmc->xbits, lmc->bank_width);
50041003396SSergey Temerkhanov 
50141003396SSergey Temerkhanov 	addr |= bank << lmc->bank_lsb;
50241003396SSergey Temerkhanov 
50341003396SSergey Temerkhanov 	xbits = PCI_FUNC(lmc->pdev->devfn);
50441003396SSergey Temerkhanov 
50541003396SSergey Temerkhanov 	if (lmc->l2c_alias)
50641003396SSergey Temerkhanov 		xbits ^= get_bits(addr, 20, lmc->xbits) ^
50741003396SSergey Temerkhanov 			 get_bits(addr, 12, lmc->xbits);
50841003396SSergey Temerkhanov 
50941003396SSergey Temerkhanov 	addr |= xbits << 7;
51041003396SSergey Temerkhanov 
51141003396SSergey Temerkhanov 	return addr;
51241003396SSergey Temerkhanov }
51341003396SSergey Temerkhanov 
thunderx_get_num_lmcs(unsigned int node)51441003396SSergey Temerkhanov static unsigned int thunderx_get_num_lmcs(unsigned int node)
51541003396SSergey Temerkhanov {
51641003396SSergey Temerkhanov 	unsigned int number = 0;
51741003396SSergey Temerkhanov 	struct pci_dev *pdev = NULL;
51841003396SSergey Temerkhanov 
51941003396SSergey Temerkhanov 	do {
52041003396SSergey Temerkhanov 		pdev = pci_get_device(PCI_VENDOR_ID_CAVIUM,
52141003396SSergey Temerkhanov 				      PCI_DEVICE_ID_THUNDER_LMC,
52241003396SSergey Temerkhanov 				      pdev);
52341003396SSergey Temerkhanov 		if (pdev) {
52441003396SSergey Temerkhanov #ifdef CONFIG_NUMA
52541003396SSergey Temerkhanov 			if (pdev->dev.numa_node == node)
52641003396SSergey Temerkhanov 				number++;
52741003396SSergey Temerkhanov #else
52841003396SSergey Temerkhanov 			number++;
52941003396SSergey Temerkhanov #endif
53041003396SSergey Temerkhanov 		}
53141003396SSergey Temerkhanov 	} while (pdev);
53241003396SSergey Temerkhanov 
53341003396SSergey Temerkhanov 	return number;
53441003396SSergey Temerkhanov }
53541003396SSergey Temerkhanov 
53641003396SSergey Temerkhanov #define LMC_MESSAGE_SIZE	120
53741003396SSergey Temerkhanov #define LMC_OTHER_SIZE		(50 * ARRAY_SIZE(lmc_errors))
53841003396SSergey Temerkhanov 
thunderx_lmc_err_isr(int irq,void * dev_id)53941003396SSergey Temerkhanov static irqreturn_t thunderx_lmc_err_isr(int irq, void *dev_id)
54041003396SSergey Temerkhanov {
54141003396SSergey Temerkhanov 	struct mem_ctl_info *mci = dev_id;
54241003396SSergey Temerkhanov 	struct thunderx_lmc *lmc = mci->pvt_info;
54341003396SSergey Temerkhanov 
54441003396SSergey Temerkhanov 	unsigned long head = ring_pos(lmc->ring_head, ARRAY_SIZE(lmc->err_ctx));
54541003396SSergey Temerkhanov 	struct lmc_err_ctx *ctx = &lmc->err_ctx[head];
54641003396SSergey Temerkhanov 
54741003396SSergey Temerkhanov 	writeq(0, lmc->regs + LMC_CHAR_MASK0);
54841003396SSergey Temerkhanov 	writeq(0, lmc->regs + LMC_CHAR_MASK2);
54941003396SSergey Temerkhanov 	writeq(0x2, lmc->regs + LMC_ECC_PARITY_TEST);
55041003396SSergey Temerkhanov 
55141003396SSergey Temerkhanov 	ctx->reg_int = readq(lmc->regs + LMC_INT);
55241003396SSergey Temerkhanov 	ctx->reg_fadr = readq(lmc->regs + LMC_FADR);
55341003396SSergey Temerkhanov 	ctx->reg_nxm_fadr = readq(lmc->regs + LMC_NXM_FADR);
55441003396SSergey Temerkhanov 	ctx->reg_scram_fadr = readq(lmc->regs + LMC_SCRAM_FADR);
55541003396SSergey Temerkhanov 	ctx->reg_ecc_synd = readq(lmc->regs + LMC_ECC_SYND);
55641003396SSergey Temerkhanov 
55741003396SSergey Temerkhanov 	lmc->ring_head++;
55841003396SSergey Temerkhanov 
55941003396SSergey Temerkhanov 	atomic_set(&lmc->ecc_int, 1);
56041003396SSergey Temerkhanov 
56141003396SSergey Temerkhanov 	/* Clear the interrupt */
56241003396SSergey Temerkhanov 	writeq(ctx->reg_int, lmc->regs + LMC_INT);
56341003396SSergey Temerkhanov 
56441003396SSergey Temerkhanov 	return IRQ_WAKE_THREAD;
56541003396SSergey Temerkhanov }
56641003396SSergey Temerkhanov 
thunderx_lmc_threaded_isr(int irq,void * dev_id)56741003396SSergey Temerkhanov static irqreturn_t thunderx_lmc_threaded_isr(int irq, void *dev_id)
56841003396SSergey Temerkhanov {
56941003396SSergey Temerkhanov 	struct mem_ctl_info *mci = dev_id;
57041003396SSergey Temerkhanov 	struct thunderx_lmc *lmc = mci->pvt_info;
57141003396SSergey Temerkhanov 	phys_addr_t phys_addr;
57241003396SSergey Temerkhanov 
57341003396SSergey Temerkhanov 	unsigned long tail;
57441003396SSergey Temerkhanov 	struct lmc_err_ctx *ctx;
57541003396SSergey Temerkhanov 
57641003396SSergey Temerkhanov 	irqreturn_t ret = IRQ_NONE;
57741003396SSergey Temerkhanov 
57841003396SSergey Temerkhanov 	char *msg;
57941003396SSergey Temerkhanov 	char *other;
58041003396SSergey Temerkhanov 
58141003396SSergey Temerkhanov 	msg = kmalloc(LMC_MESSAGE_SIZE, GFP_KERNEL);
58241003396SSergey Temerkhanov 	other =  kmalloc(LMC_OTHER_SIZE, GFP_KERNEL);
58341003396SSergey Temerkhanov 
58441003396SSergey Temerkhanov 	if (!msg || !other)
58541003396SSergey Temerkhanov 		goto err_free;
58641003396SSergey Temerkhanov 
58741003396SSergey Temerkhanov 	while (CIRC_CNT(lmc->ring_head, lmc->ring_tail,
58841003396SSergey Temerkhanov 		ARRAY_SIZE(lmc->err_ctx))) {
58941003396SSergey Temerkhanov 		tail = ring_pos(lmc->ring_tail, ARRAY_SIZE(lmc->err_ctx));
59041003396SSergey Temerkhanov 
59141003396SSergey Temerkhanov 		ctx = &lmc->err_ctx[tail];
59241003396SSergey Temerkhanov 
59341003396SSergey Temerkhanov 		dev_dbg(&lmc->pdev->dev, "LMC_INT: %016llx\n",
59441003396SSergey Temerkhanov 			ctx->reg_int);
59541003396SSergey Temerkhanov 		dev_dbg(&lmc->pdev->dev, "LMC_FADR: %016llx\n",
59641003396SSergey Temerkhanov 			ctx->reg_fadr);
59741003396SSergey Temerkhanov 		dev_dbg(&lmc->pdev->dev, "LMC_NXM_FADR: %016llx\n",
59841003396SSergey Temerkhanov 			ctx->reg_nxm_fadr);
59941003396SSergey Temerkhanov 		dev_dbg(&lmc->pdev->dev, "LMC_SCRAM_FADR: %016llx\n",
60041003396SSergey Temerkhanov 			ctx->reg_scram_fadr);
60141003396SSergey Temerkhanov 		dev_dbg(&lmc->pdev->dev, "LMC_ECC_SYND: %016llx\n",
60241003396SSergey Temerkhanov 			ctx->reg_ecc_synd);
60341003396SSergey Temerkhanov 
60441003396SSergey Temerkhanov 		snprintf(msg, LMC_MESSAGE_SIZE,
60541003396SSergey Temerkhanov 			 "DIMM %lld rank %lld bank %lld row %lld col %lld",
60641003396SSergey Temerkhanov 			 LMC_FADR_FDIMM(ctx->reg_scram_fadr),
60741003396SSergey Temerkhanov 			 LMC_FADR_FBUNK(ctx->reg_scram_fadr),
60841003396SSergey Temerkhanov 			 LMC_FADR_FBANK(ctx->reg_scram_fadr),
60941003396SSergey Temerkhanov 			 LMC_FADR_FROW(ctx->reg_scram_fadr),
61041003396SSergey Temerkhanov 			 LMC_FADR_FCOL(ctx->reg_scram_fadr));
61141003396SSergey Temerkhanov 
61241003396SSergey Temerkhanov 		decode_register(other, LMC_OTHER_SIZE, lmc_errors,
61341003396SSergey Temerkhanov 				ctx->reg_int);
61441003396SSergey Temerkhanov 
61541003396SSergey Temerkhanov 		phys_addr = thunderx_faddr_to_phys(ctx->reg_fadr, lmc);
61641003396SSergey Temerkhanov 
61741003396SSergey Temerkhanov 		if (ctx->reg_int & LMC_INT_UE)
61841003396SSergey Temerkhanov 			edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1,
61941003396SSergey Temerkhanov 					     phys_to_pfn(phys_addr),
62041003396SSergey Temerkhanov 					     offset_in_page(phys_addr),
62141003396SSergey Temerkhanov 					     0, -1, -1, -1, msg, other);
62241003396SSergey Temerkhanov 		else if (ctx->reg_int & LMC_INT_CE)
62341003396SSergey Temerkhanov 			edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1,
62441003396SSergey Temerkhanov 					     phys_to_pfn(phys_addr),
62541003396SSergey Temerkhanov 					     offset_in_page(phys_addr),
62641003396SSergey Temerkhanov 					     0, -1, -1, -1, msg, other);
62741003396SSergey Temerkhanov 
62841003396SSergey Temerkhanov 		lmc->ring_tail++;
62941003396SSergey Temerkhanov 	}
63041003396SSergey Temerkhanov 
63141003396SSergey Temerkhanov 	ret = IRQ_HANDLED;
63241003396SSergey Temerkhanov 
63341003396SSergey Temerkhanov err_free:
63441003396SSergey Temerkhanov 	kfree(msg);
63541003396SSergey Temerkhanov 	kfree(other);
63641003396SSergey Temerkhanov 
63741003396SSergey Temerkhanov 	return ret;
63841003396SSergey Temerkhanov }
63941003396SSergey Temerkhanov 
64041003396SSergey Temerkhanov static const struct pci_device_id thunderx_lmc_pci_tbl[] = {
64141003396SSergey Temerkhanov 	{ PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, PCI_DEVICE_ID_THUNDER_LMC) },
64241003396SSergey Temerkhanov 	{ 0, },
64341003396SSergey Temerkhanov };
64441003396SSergey Temerkhanov 
pci_dev_to_mc_idx(struct pci_dev * pdev)64541003396SSergey Temerkhanov static inline int pci_dev_to_mc_idx(struct pci_dev *pdev)
64641003396SSergey Temerkhanov {
64741003396SSergey Temerkhanov 	int node = dev_to_node(&pdev->dev);
64841003396SSergey Temerkhanov 	int ret = PCI_FUNC(pdev->devfn);
64941003396SSergey Temerkhanov 
6503d2d8c0fSSergey Temerkhanov 	ret += max(node, 0) << 3;
65141003396SSergey Temerkhanov 
65241003396SSergey Temerkhanov 	return ret;
65341003396SSergey Temerkhanov }
65441003396SSergey Temerkhanov 
thunderx_lmc_probe(struct pci_dev * pdev,const struct pci_device_id * id)65541003396SSergey Temerkhanov static int thunderx_lmc_probe(struct pci_dev *pdev,
65641003396SSergey Temerkhanov 				const struct pci_device_id *id)
65741003396SSergey Temerkhanov {
65841003396SSergey Temerkhanov 	struct thunderx_lmc *lmc;
65941003396SSergey Temerkhanov 	struct edac_mc_layer layer;
66041003396SSergey Temerkhanov 	struct mem_ctl_info *mci;
66141003396SSergey Temerkhanov 	u64 lmc_control, lmc_ddr_pll_ctl, lmc_config;
66241003396SSergey Temerkhanov 	int ret;
66341003396SSergey Temerkhanov 	u64 lmc_int;
66441003396SSergey Temerkhanov 	void *l2c_ioaddr;
66541003396SSergey Temerkhanov 
66641003396SSergey Temerkhanov 	layer.type = EDAC_MC_LAYER_SLOT;
66741003396SSergey Temerkhanov 	layer.size = 2;
66841003396SSergey Temerkhanov 	layer.is_virt_csrow = false;
66941003396SSergey Temerkhanov 
67041003396SSergey Temerkhanov 	ret = pcim_enable_device(pdev);
67141003396SSergey Temerkhanov 	if (ret) {
67241003396SSergey Temerkhanov 		dev_err(&pdev->dev, "Cannot enable PCI device: %d\n", ret);
67341003396SSergey Temerkhanov 		return ret;
67441003396SSergey Temerkhanov 	}
67541003396SSergey Temerkhanov 
67641003396SSergey Temerkhanov 	ret = pcim_iomap_regions(pdev, BIT(0), "thunderx_lmc");
67741003396SSergey Temerkhanov 	if (ret) {
67841003396SSergey Temerkhanov 		dev_err(&pdev->dev, "Cannot map PCI resources: %d\n", ret);
67941003396SSergey Temerkhanov 		return ret;
68041003396SSergey Temerkhanov 	}
68141003396SSergey Temerkhanov 
68241003396SSergey Temerkhanov 	mci = edac_mc_alloc(pci_dev_to_mc_idx(pdev), 1, &layer,
68341003396SSergey Temerkhanov 			    sizeof(struct thunderx_lmc));
68441003396SSergey Temerkhanov 	if (!mci)
68541003396SSergey Temerkhanov 		return -ENOMEM;
68641003396SSergey Temerkhanov 
68741003396SSergey Temerkhanov 	mci->pdev = &pdev->dev;
68841003396SSergey Temerkhanov 	lmc = mci->pvt_info;
68941003396SSergey Temerkhanov 
69041003396SSergey Temerkhanov 	pci_set_drvdata(pdev, mci);
69141003396SSergey Temerkhanov 
69241003396SSergey Temerkhanov 	lmc->regs = pcim_iomap_table(pdev)[0];
69341003396SSergey Temerkhanov 
69441003396SSergey Temerkhanov 	lmc_control = readq(lmc->regs + LMC_CONTROL);
69541003396SSergey Temerkhanov 	lmc_ddr_pll_ctl = readq(lmc->regs + LMC_DDR_PLL_CTL);
69641003396SSergey Temerkhanov 	lmc_config = readq(lmc->regs + LMC_CONFIG);
69741003396SSergey Temerkhanov 
69841003396SSergey Temerkhanov 	if (lmc_control & LMC_CONTROL_RDIMM) {
69941003396SSergey Temerkhanov 		mci->mtype_cap = FIELD_GET(LMC_DDR_PLL_CTL_DDR4,
70041003396SSergey Temerkhanov 					   lmc_ddr_pll_ctl) ?
70141003396SSergey Temerkhanov 				MEM_RDDR4 : MEM_RDDR3;
70241003396SSergey Temerkhanov 	} else {
70341003396SSergey Temerkhanov 		mci->mtype_cap = FIELD_GET(LMC_DDR_PLL_CTL_DDR4,
70441003396SSergey Temerkhanov 					   lmc_ddr_pll_ctl) ?
70541003396SSergey Temerkhanov 				MEM_DDR4 : MEM_DDR3;
70641003396SSergey Temerkhanov 	}
70741003396SSergey Temerkhanov 
70841003396SSergey Temerkhanov 	mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED;
70941003396SSergey Temerkhanov 	mci->edac_cap = EDAC_FLAG_SECDED;
71041003396SSergey Temerkhanov 
71141003396SSergey Temerkhanov 	mci->mod_name = "thunderx-lmc";
71241003396SSergey Temerkhanov 	mci->ctl_name = "thunderx-lmc";
71341003396SSergey Temerkhanov 	mci->dev_name = dev_name(&pdev->dev);
71441003396SSergey Temerkhanov 	mci->scrub_mode = SCRUB_NONE;
71541003396SSergey Temerkhanov 
71641003396SSergey Temerkhanov 	lmc->pdev = pdev;
71741003396SSergey Temerkhanov 	lmc->msix_ent.entry = 0;
71841003396SSergey Temerkhanov 
71941003396SSergey Temerkhanov 	lmc->ring_head = 0;
72041003396SSergey Temerkhanov 	lmc->ring_tail = 0;
72141003396SSergey Temerkhanov 
72241003396SSergey Temerkhanov 	ret = pci_enable_msix_exact(pdev, &lmc->msix_ent, 1);
72341003396SSergey Temerkhanov 	if (ret) {
72441003396SSergey Temerkhanov 		dev_err(&pdev->dev, "Cannot enable interrupt: %d\n", ret);
72541003396SSergey Temerkhanov 		goto err_free;
72641003396SSergey Temerkhanov 	}
72741003396SSergey Temerkhanov 
72841003396SSergey Temerkhanov 	ret = devm_request_threaded_irq(&pdev->dev, lmc->msix_ent.vector,
72941003396SSergey Temerkhanov 					thunderx_lmc_err_isr,
73041003396SSergey Temerkhanov 					thunderx_lmc_threaded_isr, 0,
73141003396SSergey Temerkhanov 					"[EDAC] ThunderX LMC", mci);
73241003396SSergey Temerkhanov 	if (ret) {
73341003396SSergey Temerkhanov 		dev_err(&pdev->dev, "Cannot set ISR: %d\n", ret);
73441003396SSergey Temerkhanov 		goto err_free;
73541003396SSergey Temerkhanov 	}
73641003396SSergey Temerkhanov 
73741003396SSergey Temerkhanov 	lmc->node = FIELD_GET(THUNDERX_NODE, pci_resource_start(pdev, 0));
73841003396SSergey Temerkhanov 
73941003396SSergey Temerkhanov 	lmc->xbits = thunderx_get_num_lmcs(lmc->node) >> 1;
74041003396SSergey Temerkhanov 	lmc->bank_width = (FIELD_GET(LMC_DDR_PLL_CTL_DDR4, lmc_ddr_pll_ctl) &&
74141003396SSergey Temerkhanov 			   FIELD_GET(LMC_CONFIG_BG2, lmc_config)) ? 4 : 3;
74241003396SSergey Temerkhanov 
74341003396SSergey Temerkhanov 	lmc->pbank_lsb = (lmc_config >> 5) & 0xf;
74441003396SSergey Temerkhanov 	lmc->dimm_lsb  = 28 + lmc->pbank_lsb + lmc->xbits;
74541003396SSergey Temerkhanov 	lmc->rank_lsb = lmc->dimm_lsb;
74641003396SSergey Temerkhanov 	lmc->rank_lsb -= FIELD_GET(LMC_CONFIG_RANK_ENA, lmc_config) ? 1 : 0;
74741003396SSergey Temerkhanov 	lmc->bank_lsb = 7 + lmc->xbits;
74841003396SSergey Temerkhanov 	lmc->row_lsb = 14 + LMC_CONFIG_ROW_LSB(lmc_config) + lmc->xbits;
74941003396SSergey Temerkhanov 
75041003396SSergey Temerkhanov 	lmc->col_hi_lsb = lmc->bank_lsb + lmc->bank_width;
75141003396SSergey Temerkhanov 
75241003396SSergey Temerkhanov 	lmc->xor_bank = lmc_control & LMC_CONTROL_XOR_BANK;
75341003396SSergey Temerkhanov 
7543eaef0faSChristophe JAILLET 	l2c_ioaddr = ioremap(L2C_CTL | FIELD_PREP(THUNDERX_NODE, lmc->node), PAGE_SIZE);
75541003396SSergey Temerkhanov 	if (!l2c_ioaddr) {
75641003396SSergey Temerkhanov 		dev_err(&pdev->dev, "Cannot map L2C_CTL\n");
7573eaef0faSChristophe JAILLET 		ret = -ENOMEM;
75841003396SSergey Temerkhanov 		goto err_free;
75941003396SSergey Temerkhanov 	}
76041003396SSergey Temerkhanov 
76141003396SSergey Temerkhanov 	lmc->l2c_alias = !(readq(l2c_ioaddr) & L2C_CTL_DISIDXALIAS);
76241003396SSergey Temerkhanov 
76341003396SSergey Temerkhanov 	iounmap(l2c_ioaddr);
76441003396SSergey Temerkhanov 
76541003396SSergey Temerkhanov 	ret = edac_mc_add_mc(mci);
76641003396SSergey Temerkhanov 	if (ret) {
76741003396SSergey Temerkhanov 		dev_err(&pdev->dev, "Cannot add the MC: %d\n", ret);
76841003396SSergey Temerkhanov 		goto err_free;
76941003396SSergey Temerkhanov 	}
77041003396SSergey Temerkhanov 
77141003396SSergey Temerkhanov 	lmc_int = readq(lmc->regs + LMC_INT);
77241003396SSergey Temerkhanov 	writeq(lmc_int, lmc->regs + LMC_INT);
77341003396SSergey Temerkhanov 
77441003396SSergey Temerkhanov 	writeq(LMC_INT_ENA_ALL, lmc->regs + LMC_INT_ENA_W1S);
77541003396SSergey Temerkhanov 
77641003396SSergey Temerkhanov 	if (IS_ENABLED(CONFIG_EDAC_DEBUG)) {
77741003396SSergey Temerkhanov 		ret = thunderx_create_debugfs_nodes(mci->debugfs,
77841003396SSergey Temerkhanov 						    lmc_dfs_ents,
77941003396SSergey Temerkhanov 						    lmc,
78041003396SSergey Temerkhanov 						    ARRAY_SIZE(lmc_dfs_ents));
78141003396SSergey Temerkhanov 
78241003396SSergey Temerkhanov 		if (ret != ARRAY_SIZE(lmc_dfs_ents)) {
78341003396SSergey Temerkhanov 			dev_warn(&pdev->dev, "Error creating debugfs entries: %d%s\n",
78441003396SSergey Temerkhanov 				 ret, ret >= 0 ? " created" : "");
78541003396SSergey Temerkhanov 		}
78641003396SSergey Temerkhanov 	}
78741003396SSergey Temerkhanov 
78841003396SSergey Temerkhanov 	return 0;
78941003396SSergey Temerkhanov 
79041003396SSergey Temerkhanov err_free:
79141003396SSergey Temerkhanov 	pci_set_drvdata(pdev, NULL);
79241003396SSergey Temerkhanov 	edac_mc_free(mci);
79341003396SSergey Temerkhanov 
79441003396SSergey Temerkhanov 	return ret;
79541003396SSergey Temerkhanov }
79641003396SSergey Temerkhanov 
thunderx_lmc_remove(struct pci_dev * pdev)79741003396SSergey Temerkhanov static void thunderx_lmc_remove(struct pci_dev *pdev)
79841003396SSergey Temerkhanov {
79941003396SSergey Temerkhanov 	struct mem_ctl_info *mci = pci_get_drvdata(pdev);
80041003396SSergey Temerkhanov 	struct thunderx_lmc *lmc = mci->pvt_info;
80141003396SSergey Temerkhanov 
80241003396SSergey Temerkhanov 	writeq(LMC_INT_ENA_ALL, lmc->regs + LMC_INT_ENA_W1C);
80341003396SSergey Temerkhanov 
80441003396SSergey Temerkhanov 	edac_mc_del_mc(&pdev->dev);
80541003396SSergey Temerkhanov 	edac_mc_free(mci);
80641003396SSergey Temerkhanov }
80741003396SSergey Temerkhanov 
80841003396SSergey Temerkhanov MODULE_DEVICE_TABLE(pci, thunderx_lmc_pci_tbl);
80941003396SSergey Temerkhanov 
81041003396SSergey Temerkhanov static struct pci_driver thunderx_lmc_driver = {
81141003396SSergey Temerkhanov 	.name     = "thunderx_lmc_edac",
81241003396SSergey Temerkhanov 	.probe    = thunderx_lmc_probe,
81341003396SSergey Temerkhanov 	.remove   = thunderx_lmc_remove,
81441003396SSergey Temerkhanov 	.id_table = thunderx_lmc_pci_tbl,
81541003396SSergey Temerkhanov };
81641003396SSergey Temerkhanov 
81741003396SSergey Temerkhanov /*---------------------- OCX driver ---------------------------------*/
81841003396SSergey Temerkhanov 
81941003396SSergey Temerkhanov #define PCI_DEVICE_ID_THUNDER_OCX 0xa013
82041003396SSergey Temerkhanov 
82141003396SSergey Temerkhanov #define OCX_LINK_INTS		3
82241003396SSergey Temerkhanov #define OCX_INTS		(OCX_LINK_INTS + 1)
82341003396SSergey Temerkhanov #define OCX_RX_LANES		24
82441003396SSergey Temerkhanov #define OCX_RX_LANE_STATS	15
82541003396SSergey Temerkhanov 
82641003396SSergey Temerkhanov #define OCX_COM_INT		0x100
82741003396SSergey Temerkhanov #define OCX_COM_INT_W1S		0x108
82841003396SSergey Temerkhanov #define OCX_COM_INT_ENA_W1S	0x110
82941003396SSergey Temerkhanov #define OCX_COM_INT_ENA_W1C	0x118
83041003396SSergey Temerkhanov 
83141003396SSergey Temerkhanov #define OCX_COM_IO_BADID		BIT(54)
83241003396SSergey Temerkhanov #define OCX_COM_MEM_BADID		BIT(53)
83341003396SSergey Temerkhanov #define OCX_COM_COPR_BADID		BIT(52)
83441003396SSergey Temerkhanov #define OCX_COM_WIN_REQ_BADID		BIT(51)
83541003396SSergey Temerkhanov #define OCX_COM_WIN_REQ_TOUT		BIT(50)
83641003396SSergey Temerkhanov #define OCX_COM_RX_LANE			GENMASK(23, 0)
83741003396SSergey Temerkhanov 
83841003396SSergey Temerkhanov #define OCX_COM_INT_CE			(OCX_COM_IO_BADID      | \
83941003396SSergey Temerkhanov 					 OCX_COM_MEM_BADID     | \
84041003396SSergey Temerkhanov 					 OCX_COM_COPR_BADID    | \
84141003396SSergey Temerkhanov 					 OCX_COM_WIN_REQ_BADID | \
84241003396SSergey Temerkhanov 					 OCX_COM_WIN_REQ_TOUT)
84341003396SSergey Temerkhanov 
84441003396SSergey Temerkhanov static const struct error_descr ocx_com_errors[] = {
84541003396SSergey Temerkhanov 	{
84641003396SSergey Temerkhanov 		.type  = ERR_CORRECTED,
84741003396SSergey Temerkhanov 		.mask  = OCX_COM_IO_BADID,
84841003396SSergey Temerkhanov 		.descr = "Invalid IO transaction node ID",
84941003396SSergey Temerkhanov 	},
85041003396SSergey Temerkhanov 	{
85141003396SSergey Temerkhanov 		.type  = ERR_CORRECTED,
85241003396SSergey Temerkhanov 		.mask  = OCX_COM_MEM_BADID,
85341003396SSergey Temerkhanov 		.descr = "Invalid memory transaction node ID",
85441003396SSergey Temerkhanov 	},
85541003396SSergey Temerkhanov 	{
85641003396SSergey Temerkhanov 		.type  = ERR_CORRECTED,
85741003396SSergey Temerkhanov 		.mask  = OCX_COM_COPR_BADID,
85841003396SSergey Temerkhanov 		.descr = "Invalid coprocessor transaction node ID",
85941003396SSergey Temerkhanov 	},
86041003396SSergey Temerkhanov 	{
86141003396SSergey Temerkhanov 		.type  = ERR_CORRECTED,
86241003396SSergey Temerkhanov 		.mask  = OCX_COM_WIN_REQ_BADID,
86341003396SSergey Temerkhanov 		.descr = "Invalid SLI transaction node ID",
86441003396SSergey Temerkhanov 	},
86541003396SSergey Temerkhanov 	{
86641003396SSergey Temerkhanov 		.type  = ERR_CORRECTED,
86741003396SSergey Temerkhanov 		.mask  = OCX_COM_WIN_REQ_TOUT,
86841003396SSergey Temerkhanov 		.descr = "Window/core request timeout",
86941003396SSergey Temerkhanov 	},
87041003396SSergey Temerkhanov 	{0, 0, NULL},
87141003396SSergey Temerkhanov };
87241003396SSergey Temerkhanov 
87341003396SSergey Temerkhanov #define OCX_COM_LINKX_INT(x)		(0x120 + (x) * 8)
87441003396SSergey Temerkhanov #define OCX_COM_LINKX_INT_W1S(x)	(0x140 + (x) * 8)
87541003396SSergey Temerkhanov #define OCX_COM_LINKX_INT_ENA_W1S(x)	(0x160 + (x) * 8)
87641003396SSergey Temerkhanov #define OCX_COM_LINKX_INT_ENA_W1C(x)	(0x180 + (x) * 8)
87741003396SSergey Temerkhanov 
87841003396SSergey Temerkhanov #define OCX_COM_LINK_BAD_WORD			BIT(13)
87941003396SSergey Temerkhanov #define OCX_COM_LINK_ALIGN_FAIL			BIT(12)
88041003396SSergey Temerkhanov #define OCX_COM_LINK_ALIGN_DONE			BIT(11)
88141003396SSergey Temerkhanov #define OCX_COM_LINK_UP				BIT(10)
88241003396SSergey Temerkhanov #define OCX_COM_LINK_STOP			BIT(9)
88341003396SSergey Temerkhanov #define OCX_COM_LINK_BLK_ERR			BIT(8)
88441003396SSergey Temerkhanov #define OCX_COM_LINK_REINIT			BIT(7)
88541003396SSergey Temerkhanov #define OCX_COM_LINK_LNK_DATA			BIT(6)
88641003396SSergey Temerkhanov #define OCX_COM_LINK_RXFIFO_DBE			BIT(5)
88741003396SSergey Temerkhanov #define OCX_COM_LINK_RXFIFO_SBE			BIT(4)
88841003396SSergey Temerkhanov #define OCX_COM_LINK_TXFIFO_DBE			BIT(3)
88941003396SSergey Temerkhanov #define OCX_COM_LINK_TXFIFO_SBE			BIT(2)
89041003396SSergey Temerkhanov #define OCX_COM_LINK_REPLAY_DBE			BIT(1)
89141003396SSergey Temerkhanov #define OCX_COM_LINK_REPLAY_SBE			BIT(0)
89241003396SSergey Temerkhanov 
89341003396SSergey Temerkhanov static const struct error_descr ocx_com_link_errors[] = {
89441003396SSergey Temerkhanov 	{
89541003396SSergey Temerkhanov 		.type  = ERR_CORRECTED,
89641003396SSergey Temerkhanov 		.mask  = OCX_COM_LINK_REPLAY_SBE,
89741003396SSergey Temerkhanov 		.descr = "Replay buffer single-bit error",
89841003396SSergey Temerkhanov 	},
89941003396SSergey Temerkhanov 	{
90041003396SSergey Temerkhanov 		.type  = ERR_CORRECTED,
90141003396SSergey Temerkhanov 		.mask  = OCX_COM_LINK_TXFIFO_SBE,
90241003396SSergey Temerkhanov 		.descr = "TX FIFO single-bit error",
90341003396SSergey Temerkhanov 	},
90441003396SSergey Temerkhanov 	{
90541003396SSergey Temerkhanov 		.type  = ERR_CORRECTED,
90641003396SSergey Temerkhanov 		.mask  = OCX_COM_LINK_RXFIFO_SBE,
90741003396SSergey Temerkhanov 		.descr = "RX FIFO single-bit error",
90841003396SSergey Temerkhanov 	},
90941003396SSergey Temerkhanov 	{
91041003396SSergey Temerkhanov 		.type  = ERR_CORRECTED,
91141003396SSergey Temerkhanov 		.mask  = OCX_COM_LINK_BLK_ERR,
91241003396SSergey Temerkhanov 		.descr = "Block code error",
91341003396SSergey Temerkhanov 	},
91441003396SSergey Temerkhanov 	{
91541003396SSergey Temerkhanov 		.type  = ERR_CORRECTED,
91641003396SSergey Temerkhanov 		.mask  = OCX_COM_LINK_ALIGN_FAIL,
91741003396SSergey Temerkhanov 		.descr = "Link alignment failure",
91841003396SSergey Temerkhanov 	},
91941003396SSergey Temerkhanov 	{
92041003396SSergey Temerkhanov 		.type  = ERR_CORRECTED,
92141003396SSergey Temerkhanov 		.mask  = OCX_COM_LINK_BAD_WORD,
92241003396SSergey Temerkhanov 		.descr = "Bad code word",
92341003396SSergey Temerkhanov 	},
92441003396SSergey Temerkhanov 	{
92541003396SSergey Temerkhanov 		.type  = ERR_UNCORRECTED,
92641003396SSergey Temerkhanov 		.mask  = OCX_COM_LINK_REPLAY_DBE,
92741003396SSergey Temerkhanov 		.descr = "Replay buffer double-bit error",
92841003396SSergey Temerkhanov 	},
92941003396SSergey Temerkhanov 	{
93041003396SSergey Temerkhanov 		.type  = ERR_UNCORRECTED,
93141003396SSergey Temerkhanov 		.mask  = OCX_COM_LINK_TXFIFO_DBE,
93241003396SSergey Temerkhanov 		.descr = "TX FIFO double-bit error",
93341003396SSergey Temerkhanov 	},
93441003396SSergey Temerkhanov 	{
93541003396SSergey Temerkhanov 		.type  = ERR_UNCORRECTED,
93641003396SSergey Temerkhanov 		.mask  = OCX_COM_LINK_RXFIFO_DBE,
93741003396SSergey Temerkhanov 		.descr = "RX FIFO double-bit error",
93841003396SSergey Temerkhanov 	},
93941003396SSergey Temerkhanov 	{
94041003396SSergey Temerkhanov 		.type  = ERR_UNCORRECTED,
94141003396SSergey Temerkhanov 		.mask  = OCX_COM_LINK_STOP,
94241003396SSergey Temerkhanov 		.descr = "Link stopped",
94341003396SSergey Temerkhanov 	},
94441003396SSergey Temerkhanov 	{0, 0, NULL},
94541003396SSergey Temerkhanov };
94641003396SSergey Temerkhanov 
94741003396SSergey Temerkhanov #define OCX_COM_LINK_INT_UE       (OCX_COM_LINK_REPLAY_DBE | \
94841003396SSergey Temerkhanov 				   OCX_COM_LINK_TXFIFO_DBE | \
94941003396SSergey Temerkhanov 				   OCX_COM_LINK_RXFIFO_DBE | \
95041003396SSergey Temerkhanov 				   OCX_COM_LINK_STOP)
95141003396SSergey Temerkhanov 
95241003396SSergey Temerkhanov #define OCX_COM_LINK_INT_CE       (OCX_COM_LINK_REPLAY_SBE | \
95341003396SSergey Temerkhanov 				   OCX_COM_LINK_TXFIFO_SBE | \
95441003396SSergey Temerkhanov 				   OCX_COM_LINK_RXFIFO_SBE | \
95541003396SSergey Temerkhanov 				   OCX_COM_LINK_BLK_ERR    | \
95641003396SSergey Temerkhanov 				   OCX_COM_LINK_ALIGN_FAIL | \
95741003396SSergey Temerkhanov 				   OCX_COM_LINK_BAD_WORD)
95841003396SSergey Temerkhanov 
95941003396SSergey Temerkhanov #define OCX_LNE_INT(x)			(0x8018 + (x) * 0x100)
96041003396SSergey Temerkhanov #define OCX_LNE_INT_EN(x)		(0x8020 + (x) * 0x100)
96141003396SSergey Temerkhanov #define OCX_LNE_BAD_CNT(x)		(0x8028 + (x) * 0x100)
96241003396SSergey Temerkhanov #define OCX_LNE_CFG(x)			(0x8000 + (x) * 0x100)
96341003396SSergey Temerkhanov #define OCX_LNE_STAT(x, y)		(0x8040 + (x) * 0x100 + (y) * 8)
96441003396SSergey Temerkhanov 
96541003396SSergey Temerkhanov #define OCX_LNE_CFG_RX_BDRY_LOCK_DIS		BIT(8)
96641003396SSergey Temerkhanov #define OCX_LNE_CFG_RX_STAT_WRAP_DIS		BIT(2)
96741003396SSergey Temerkhanov #define OCX_LNE_CFG_RX_STAT_RDCLR		BIT(1)
96841003396SSergey Temerkhanov #define OCX_LNE_CFG_RX_STAT_ENA			BIT(0)
96941003396SSergey Temerkhanov 
97041003396SSergey Temerkhanov 
97141003396SSergey Temerkhanov #define OCX_LANE_BAD_64B67B			BIT(8)
97241003396SSergey Temerkhanov #define OCX_LANE_DSKEW_FIFO_OVFL		BIT(5)
97341003396SSergey Temerkhanov #define OCX_LANE_SCRM_SYNC_LOSS			BIT(4)
97441003396SSergey Temerkhanov #define OCX_LANE_UKWN_CNTL_WORD			BIT(3)
97541003396SSergey Temerkhanov #define OCX_LANE_CRC32_ERR			BIT(2)
97641003396SSergey Temerkhanov #define OCX_LANE_BDRY_SYNC_LOSS			BIT(1)
97741003396SSergey Temerkhanov #define OCX_LANE_SERDES_LOCK_LOSS		BIT(0)
97841003396SSergey Temerkhanov 
97941003396SSergey Temerkhanov #define OCX_COM_LANE_INT_UE       (0)
98041003396SSergey Temerkhanov #define OCX_COM_LANE_INT_CE       (OCX_LANE_SERDES_LOCK_LOSS | \
98141003396SSergey Temerkhanov 				   OCX_LANE_BDRY_SYNC_LOSS   | \
98241003396SSergey Temerkhanov 				   OCX_LANE_CRC32_ERR        | \
98341003396SSergey Temerkhanov 				   OCX_LANE_UKWN_CNTL_WORD   | \
98441003396SSergey Temerkhanov 				   OCX_LANE_SCRM_SYNC_LOSS   | \
98541003396SSergey Temerkhanov 				   OCX_LANE_DSKEW_FIFO_OVFL  | \
98641003396SSergey Temerkhanov 				   OCX_LANE_BAD_64B67B)
98741003396SSergey Temerkhanov 
98841003396SSergey Temerkhanov static const struct error_descr ocx_lane_errors[] = {
98941003396SSergey Temerkhanov 	{
99041003396SSergey Temerkhanov 		.type  = ERR_CORRECTED,
99141003396SSergey Temerkhanov 		.mask  = OCX_LANE_SERDES_LOCK_LOSS,
99241003396SSergey Temerkhanov 		.descr = "RX SerDes lock lost",
99341003396SSergey Temerkhanov 	},
99441003396SSergey Temerkhanov 	{
99541003396SSergey Temerkhanov 		.type  = ERR_CORRECTED,
99641003396SSergey Temerkhanov 		.mask  = OCX_LANE_BDRY_SYNC_LOSS,
99741003396SSergey Temerkhanov 		.descr = "RX word boundary lost",
99841003396SSergey Temerkhanov 	},
99941003396SSergey Temerkhanov 	{
100041003396SSergey Temerkhanov 		.type  = ERR_CORRECTED,
100141003396SSergey Temerkhanov 		.mask  = OCX_LANE_CRC32_ERR,
100241003396SSergey Temerkhanov 		.descr = "CRC32 error",
100341003396SSergey Temerkhanov 	},
100441003396SSergey Temerkhanov 	{
100541003396SSergey Temerkhanov 		.type  = ERR_CORRECTED,
100641003396SSergey Temerkhanov 		.mask  = OCX_LANE_UKWN_CNTL_WORD,
100741003396SSergey Temerkhanov 		.descr = "Unknown control word",
100841003396SSergey Temerkhanov 	},
100941003396SSergey Temerkhanov 	{
101041003396SSergey Temerkhanov 		.type  = ERR_CORRECTED,
101141003396SSergey Temerkhanov 		.mask  = OCX_LANE_SCRM_SYNC_LOSS,
101241003396SSergey Temerkhanov 		.descr = "Scrambler synchronization lost",
101341003396SSergey Temerkhanov 	},
101441003396SSergey Temerkhanov 	{
101541003396SSergey Temerkhanov 		.type  = ERR_CORRECTED,
101641003396SSergey Temerkhanov 		.mask  = OCX_LANE_DSKEW_FIFO_OVFL,
101741003396SSergey Temerkhanov 		.descr = "RX deskew FIFO overflow",
101841003396SSergey Temerkhanov 	},
101941003396SSergey Temerkhanov 	{
102041003396SSergey Temerkhanov 		.type  = ERR_CORRECTED,
102141003396SSergey Temerkhanov 		.mask  = OCX_LANE_BAD_64B67B,
102241003396SSergey Temerkhanov 		.descr = "Bad 64B/67B codeword",
102341003396SSergey Temerkhanov 	},
102441003396SSergey Temerkhanov 	{0, 0, NULL},
102541003396SSergey Temerkhanov };
102641003396SSergey Temerkhanov 
102741003396SSergey Temerkhanov #define OCX_LNE_INT_ENA_ALL		(GENMASK(9, 8) | GENMASK(6, 0))
102841003396SSergey Temerkhanov #define OCX_COM_INT_ENA_ALL		(GENMASK(54, 50) | GENMASK(23, 0))
102941003396SSergey Temerkhanov #define OCX_COM_LINKX_INT_ENA_ALL	(GENMASK(13, 12) | \
103041003396SSergey Temerkhanov 					 GENMASK(9, 7) | GENMASK(5, 0))
103141003396SSergey Temerkhanov 
103241003396SSergey Temerkhanov #define OCX_TLKX_ECC_CTL(x)		(0x10018 + (x) * 0x2000)
103341003396SSergey Temerkhanov #define OCX_RLKX_ECC_CTL(x)		(0x18018 + (x) * 0x2000)
103441003396SSergey Temerkhanov 
103541003396SSergey Temerkhanov struct ocx_com_err_ctx {
103641003396SSergey Temerkhanov 	u64 reg_com_int;
103741003396SSergey Temerkhanov 	u64 reg_lane_int[OCX_RX_LANES];
103841003396SSergey Temerkhanov 	u64 reg_lane_stat11[OCX_RX_LANES];
103941003396SSergey Temerkhanov };
104041003396SSergey Temerkhanov 
104141003396SSergey Temerkhanov struct ocx_link_err_ctx {
104241003396SSergey Temerkhanov 	u64 reg_com_link_int;
104341003396SSergey Temerkhanov 	int link;
104441003396SSergey Temerkhanov };
104541003396SSergey Temerkhanov 
104641003396SSergey Temerkhanov struct thunderx_ocx {
104741003396SSergey Temerkhanov 	void __iomem *regs;
104841003396SSergey Temerkhanov 	int com_link;
104941003396SSergey Temerkhanov 	struct pci_dev *pdev;
105041003396SSergey Temerkhanov 	struct edac_device_ctl_info *edac_dev;
105141003396SSergey Temerkhanov 
105241003396SSergey Temerkhanov 	struct dentry *debugfs;
105341003396SSergey Temerkhanov 	struct msix_entry msix_ent[OCX_INTS];
105441003396SSergey Temerkhanov 
105541003396SSergey Temerkhanov 	struct ocx_com_err_ctx com_err_ctx[RING_ENTRIES];
105641003396SSergey Temerkhanov 	struct ocx_link_err_ctx link_err_ctx[RING_ENTRIES];
105741003396SSergey Temerkhanov 
105841003396SSergey Temerkhanov 	unsigned long com_ring_head;
105941003396SSergey Temerkhanov 	unsigned long com_ring_tail;
106041003396SSergey Temerkhanov 
106141003396SSergey Temerkhanov 	unsigned long link_ring_head;
106241003396SSergey Temerkhanov 	unsigned long link_ring_tail;
106341003396SSergey Temerkhanov };
106441003396SSergey Temerkhanov 
106541003396SSergey Temerkhanov #define OCX_MESSAGE_SIZE	SZ_1K
106641003396SSergey Temerkhanov #define OCX_OTHER_SIZE		(50 * ARRAY_SIZE(ocx_com_link_errors))
106741003396SSergey Temerkhanov 
106841003396SSergey Temerkhanov /* This handler is threaded */
thunderx_ocx_com_isr(int irq,void * irq_id)106941003396SSergey Temerkhanov static irqreturn_t thunderx_ocx_com_isr(int irq, void *irq_id)
107041003396SSergey Temerkhanov {
107141003396SSergey Temerkhanov 	struct msix_entry *msix = irq_id;
107241003396SSergey Temerkhanov 	struct thunderx_ocx *ocx = container_of(msix, struct thunderx_ocx,
107341003396SSergey Temerkhanov 						msix_ent[msix->entry]);
107441003396SSergey Temerkhanov 
107541003396SSergey Temerkhanov 	int lane;
107641003396SSergey Temerkhanov 	unsigned long head = ring_pos(ocx->com_ring_head,
107741003396SSergey Temerkhanov 				      ARRAY_SIZE(ocx->com_err_ctx));
107841003396SSergey Temerkhanov 	struct ocx_com_err_ctx *ctx = &ocx->com_err_ctx[head];
107941003396SSergey Temerkhanov 
108041003396SSergey Temerkhanov 	ctx->reg_com_int = readq(ocx->regs + OCX_COM_INT);
108141003396SSergey Temerkhanov 
108241003396SSergey Temerkhanov 	for (lane = 0; lane < OCX_RX_LANES; lane++) {
108341003396SSergey Temerkhanov 		ctx->reg_lane_int[lane] =
108441003396SSergey Temerkhanov 			readq(ocx->regs + OCX_LNE_INT(lane));
108541003396SSergey Temerkhanov 		ctx->reg_lane_stat11[lane] =
108641003396SSergey Temerkhanov 			readq(ocx->regs + OCX_LNE_STAT(lane, 11));
108741003396SSergey Temerkhanov 
108841003396SSergey Temerkhanov 		writeq(ctx->reg_lane_int[lane], ocx->regs + OCX_LNE_INT(lane));
108941003396SSergey Temerkhanov 	}
109041003396SSergey Temerkhanov 
109141003396SSergey Temerkhanov 	writeq(ctx->reg_com_int, ocx->regs + OCX_COM_INT);
109241003396SSergey Temerkhanov 
109341003396SSergey Temerkhanov 	ocx->com_ring_head++;
109441003396SSergey Temerkhanov 
109541003396SSergey Temerkhanov 	return IRQ_WAKE_THREAD;
109641003396SSergey Temerkhanov }
109741003396SSergey Temerkhanov 
thunderx_ocx_com_threaded_isr(int irq,void * irq_id)109841003396SSergey Temerkhanov static irqreturn_t thunderx_ocx_com_threaded_isr(int irq, void *irq_id)
109941003396SSergey Temerkhanov {
110041003396SSergey Temerkhanov 	struct msix_entry *msix = irq_id;
110141003396SSergey Temerkhanov 	struct thunderx_ocx *ocx = container_of(msix, struct thunderx_ocx,
110241003396SSergey Temerkhanov 						msix_ent[msix->entry]);
110341003396SSergey Temerkhanov 
110441003396SSergey Temerkhanov 	irqreturn_t ret = IRQ_NONE;
110541003396SSergey Temerkhanov 
110641003396SSergey Temerkhanov 	unsigned long tail;
110741003396SSergey Temerkhanov 	struct ocx_com_err_ctx *ctx;
110841003396SSergey Temerkhanov 	int lane;
110941003396SSergey Temerkhanov 	char *msg;
111041003396SSergey Temerkhanov 	char *other;
111141003396SSergey Temerkhanov 
111241003396SSergey Temerkhanov 	msg = kmalloc(OCX_MESSAGE_SIZE, GFP_KERNEL);
111341003396SSergey Temerkhanov 	other = kmalloc(OCX_OTHER_SIZE, GFP_KERNEL);
111441003396SSergey Temerkhanov 
111541003396SSergey Temerkhanov 	if (!msg || !other)
111641003396SSergey Temerkhanov 		goto err_free;
111741003396SSergey Temerkhanov 
111841003396SSergey Temerkhanov 	while (CIRC_CNT(ocx->com_ring_head, ocx->com_ring_tail,
111941003396SSergey Temerkhanov 			ARRAY_SIZE(ocx->com_err_ctx))) {
112041003396SSergey Temerkhanov 		tail = ring_pos(ocx->com_ring_tail,
112141003396SSergey Temerkhanov 				ARRAY_SIZE(ocx->com_err_ctx));
112241003396SSergey Temerkhanov 		ctx = &ocx->com_err_ctx[tail];
112341003396SSergey Temerkhanov 
112441003396SSergey Temerkhanov 		snprintf(msg, OCX_MESSAGE_SIZE, "%s: OCX_COM_INT: %016llx",
112541003396SSergey Temerkhanov 			ocx->edac_dev->ctl_name, ctx->reg_com_int);
112641003396SSergey Temerkhanov 
112741003396SSergey Temerkhanov 		decode_register(other, OCX_OTHER_SIZE,
112841003396SSergey Temerkhanov 				ocx_com_errors, ctx->reg_com_int);
112941003396SSergey Temerkhanov 
1130475c58e1SArnd Bergmann 		strlcat(msg, other, OCX_MESSAGE_SIZE);
113141003396SSergey Temerkhanov 
113241003396SSergey Temerkhanov 		for (lane = 0; lane < OCX_RX_LANES; lane++)
113341003396SSergey Temerkhanov 			if (ctx->reg_com_int & BIT(lane)) {
113441003396SSergey Temerkhanov 				snprintf(other, OCX_OTHER_SIZE,
113541003396SSergey Temerkhanov 					 "\n\tOCX_LNE_INT[%02d]: %016llx OCX_LNE_STAT11[%02d]: %016llx",
113641003396SSergey Temerkhanov 					 lane, ctx->reg_lane_int[lane],
113741003396SSergey Temerkhanov 					 lane, ctx->reg_lane_stat11[lane]);
113841003396SSergey Temerkhanov 
1139475c58e1SArnd Bergmann 				strlcat(msg, other, OCX_MESSAGE_SIZE);
114041003396SSergey Temerkhanov 
114141003396SSergey Temerkhanov 				decode_register(other, OCX_OTHER_SIZE,
114241003396SSergey Temerkhanov 						ocx_lane_errors,
114341003396SSergey Temerkhanov 						ctx->reg_lane_int[lane]);
1144475c58e1SArnd Bergmann 				strlcat(msg, other, OCX_MESSAGE_SIZE);
114541003396SSergey Temerkhanov 			}
114641003396SSergey Temerkhanov 
11475195c206SSergey Temerkhanov 		if (ctx->reg_com_int & OCX_COM_INT_CE)
114841003396SSergey Temerkhanov 			edac_device_handle_ce(ocx->edac_dev, 0, 0, msg);
114941003396SSergey Temerkhanov 
115041003396SSergey Temerkhanov 		ocx->com_ring_tail++;
115141003396SSergey Temerkhanov 	}
115241003396SSergey Temerkhanov 
115341003396SSergey Temerkhanov 	ret = IRQ_HANDLED;
115441003396SSergey Temerkhanov 
115541003396SSergey Temerkhanov err_free:
115641003396SSergey Temerkhanov 	kfree(other);
115741003396SSergey Temerkhanov 	kfree(msg);
115841003396SSergey Temerkhanov 
115941003396SSergey Temerkhanov 	return ret;
116041003396SSergey Temerkhanov }
116141003396SSergey Temerkhanov 
thunderx_ocx_lnk_isr(int irq,void * irq_id)116241003396SSergey Temerkhanov static irqreturn_t thunderx_ocx_lnk_isr(int irq, void *irq_id)
116341003396SSergey Temerkhanov {
116441003396SSergey Temerkhanov 	struct msix_entry *msix = irq_id;
116541003396SSergey Temerkhanov 	struct thunderx_ocx *ocx = container_of(msix, struct thunderx_ocx,
116641003396SSergey Temerkhanov 						msix_ent[msix->entry]);
116741003396SSergey Temerkhanov 	unsigned long head = ring_pos(ocx->link_ring_head,
116841003396SSergey Temerkhanov 				      ARRAY_SIZE(ocx->link_err_ctx));
116941003396SSergey Temerkhanov 	struct ocx_link_err_ctx *ctx = &ocx->link_err_ctx[head];
117041003396SSergey Temerkhanov 
117141003396SSergey Temerkhanov 	ctx->link = msix->entry;
117241003396SSergey Temerkhanov 	ctx->reg_com_link_int = readq(ocx->regs + OCX_COM_LINKX_INT(ctx->link));
117341003396SSergey Temerkhanov 
117441003396SSergey Temerkhanov 	writeq(ctx->reg_com_link_int, ocx->regs + OCX_COM_LINKX_INT(ctx->link));
117541003396SSergey Temerkhanov 
117641003396SSergey Temerkhanov 	ocx->link_ring_head++;
117741003396SSergey Temerkhanov 
117841003396SSergey Temerkhanov 	return IRQ_WAKE_THREAD;
117941003396SSergey Temerkhanov }
118041003396SSergey Temerkhanov 
thunderx_ocx_lnk_threaded_isr(int irq,void * irq_id)118141003396SSergey Temerkhanov static irqreturn_t thunderx_ocx_lnk_threaded_isr(int irq, void *irq_id)
118241003396SSergey Temerkhanov {
118341003396SSergey Temerkhanov 	struct msix_entry *msix = irq_id;
118441003396SSergey Temerkhanov 	struct thunderx_ocx *ocx = container_of(msix, struct thunderx_ocx,
118541003396SSergey Temerkhanov 						msix_ent[msix->entry]);
118641003396SSergey Temerkhanov 	irqreturn_t ret = IRQ_NONE;
118741003396SSergey Temerkhanov 	unsigned long tail;
118841003396SSergey Temerkhanov 	struct ocx_link_err_ctx *ctx;
118941003396SSergey Temerkhanov 
119041003396SSergey Temerkhanov 	char *msg;
119141003396SSergey Temerkhanov 	char *other;
119241003396SSergey Temerkhanov 
119341003396SSergey Temerkhanov 	msg = kmalloc(OCX_MESSAGE_SIZE, GFP_KERNEL);
119441003396SSergey Temerkhanov 	other = kmalloc(OCX_OTHER_SIZE, GFP_KERNEL);
119541003396SSergey Temerkhanov 
119641003396SSergey Temerkhanov 	if (!msg || !other)
119741003396SSergey Temerkhanov 		goto err_free;
119841003396SSergey Temerkhanov 
119941003396SSergey Temerkhanov 	while (CIRC_CNT(ocx->link_ring_head, ocx->link_ring_tail,
120041003396SSergey Temerkhanov 			ARRAY_SIZE(ocx->link_err_ctx))) {
120141003396SSergey Temerkhanov 		tail = ring_pos(ocx->link_ring_head,
120241003396SSergey Temerkhanov 				ARRAY_SIZE(ocx->link_err_ctx));
120341003396SSergey Temerkhanov 
120441003396SSergey Temerkhanov 		ctx = &ocx->link_err_ctx[tail];
120541003396SSergey Temerkhanov 
120641003396SSergey Temerkhanov 		snprintf(msg, OCX_MESSAGE_SIZE,
120741003396SSergey Temerkhanov 			 "%s: OCX_COM_LINK_INT[%d]: %016llx",
120841003396SSergey Temerkhanov 			 ocx->edac_dev->ctl_name,
120941003396SSergey Temerkhanov 			 ctx->link, ctx->reg_com_link_int);
121041003396SSergey Temerkhanov 
121141003396SSergey Temerkhanov 		decode_register(other, OCX_OTHER_SIZE,
121241003396SSergey Temerkhanov 				ocx_com_link_errors, ctx->reg_com_link_int);
121341003396SSergey Temerkhanov 
1214475c58e1SArnd Bergmann 		strlcat(msg, other, OCX_MESSAGE_SIZE);
121541003396SSergey Temerkhanov 
121641003396SSergey Temerkhanov 		if (ctx->reg_com_link_int & OCX_COM_LINK_INT_UE)
121741003396SSergey Temerkhanov 			edac_device_handle_ue(ocx->edac_dev, 0, 0, msg);
121841003396SSergey Temerkhanov 		else if (ctx->reg_com_link_int & OCX_COM_LINK_INT_CE)
121941003396SSergey Temerkhanov 			edac_device_handle_ce(ocx->edac_dev, 0, 0, msg);
122041003396SSergey Temerkhanov 
122141003396SSergey Temerkhanov 		ocx->link_ring_tail++;
122241003396SSergey Temerkhanov 	}
122341003396SSergey Temerkhanov 
122441003396SSergey Temerkhanov 	ret = IRQ_HANDLED;
122541003396SSergey Temerkhanov err_free:
122641003396SSergey Temerkhanov 	kfree(other);
122741003396SSergey Temerkhanov 	kfree(msg);
122841003396SSergey Temerkhanov 
122941003396SSergey Temerkhanov 	return ret;
123041003396SSergey Temerkhanov }
123141003396SSergey Temerkhanov 
123241003396SSergey Temerkhanov #define OCX_DEBUGFS_ATTR(_name, _reg)	DEBUGFS_REG_ATTR(ocx, _name, _reg)
123341003396SSergey Temerkhanov 
123441003396SSergey Temerkhanov OCX_DEBUGFS_ATTR(tlk0_ecc_ctl, OCX_TLKX_ECC_CTL(0));
123541003396SSergey Temerkhanov OCX_DEBUGFS_ATTR(tlk1_ecc_ctl, OCX_TLKX_ECC_CTL(1));
123641003396SSergey Temerkhanov OCX_DEBUGFS_ATTR(tlk2_ecc_ctl, OCX_TLKX_ECC_CTL(2));
123741003396SSergey Temerkhanov 
123841003396SSergey Temerkhanov OCX_DEBUGFS_ATTR(rlk0_ecc_ctl, OCX_RLKX_ECC_CTL(0));
123941003396SSergey Temerkhanov OCX_DEBUGFS_ATTR(rlk1_ecc_ctl, OCX_RLKX_ECC_CTL(1));
124041003396SSergey Temerkhanov OCX_DEBUGFS_ATTR(rlk2_ecc_ctl, OCX_RLKX_ECC_CTL(2));
124141003396SSergey Temerkhanov 
124241003396SSergey Temerkhanov OCX_DEBUGFS_ATTR(com_link0_int, OCX_COM_LINKX_INT_W1S(0));
124341003396SSergey Temerkhanov OCX_DEBUGFS_ATTR(com_link1_int, OCX_COM_LINKX_INT_W1S(1));
124441003396SSergey Temerkhanov OCX_DEBUGFS_ATTR(com_link2_int, OCX_COM_LINKX_INT_W1S(2));
124541003396SSergey Temerkhanov 
124641003396SSergey Temerkhanov OCX_DEBUGFS_ATTR(lne00_badcnt, OCX_LNE_BAD_CNT(0));
124741003396SSergey Temerkhanov OCX_DEBUGFS_ATTR(lne01_badcnt, OCX_LNE_BAD_CNT(1));
124841003396SSergey Temerkhanov OCX_DEBUGFS_ATTR(lne02_badcnt, OCX_LNE_BAD_CNT(2));
124941003396SSergey Temerkhanov OCX_DEBUGFS_ATTR(lne03_badcnt, OCX_LNE_BAD_CNT(3));
125041003396SSergey Temerkhanov OCX_DEBUGFS_ATTR(lne04_badcnt, OCX_LNE_BAD_CNT(4));
125141003396SSergey Temerkhanov OCX_DEBUGFS_ATTR(lne05_badcnt, OCX_LNE_BAD_CNT(5));
125241003396SSergey Temerkhanov OCX_DEBUGFS_ATTR(lne06_badcnt, OCX_LNE_BAD_CNT(6));
125341003396SSergey Temerkhanov OCX_DEBUGFS_ATTR(lne07_badcnt, OCX_LNE_BAD_CNT(7));
125441003396SSergey Temerkhanov 
125541003396SSergey Temerkhanov OCX_DEBUGFS_ATTR(lne08_badcnt, OCX_LNE_BAD_CNT(8));
125641003396SSergey Temerkhanov OCX_DEBUGFS_ATTR(lne09_badcnt, OCX_LNE_BAD_CNT(9));
125741003396SSergey Temerkhanov OCX_DEBUGFS_ATTR(lne10_badcnt, OCX_LNE_BAD_CNT(10));
125841003396SSergey Temerkhanov OCX_DEBUGFS_ATTR(lne11_badcnt, OCX_LNE_BAD_CNT(11));
125941003396SSergey Temerkhanov OCX_DEBUGFS_ATTR(lne12_badcnt, OCX_LNE_BAD_CNT(12));
126041003396SSergey Temerkhanov OCX_DEBUGFS_ATTR(lne13_badcnt, OCX_LNE_BAD_CNT(13));
126141003396SSergey Temerkhanov OCX_DEBUGFS_ATTR(lne14_badcnt, OCX_LNE_BAD_CNT(14));
126241003396SSergey Temerkhanov OCX_DEBUGFS_ATTR(lne15_badcnt, OCX_LNE_BAD_CNT(15));
126341003396SSergey Temerkhanov 
126441003396SSergey Temerkhanov OCX_DEBUGFS_ATTR(lne16_badcnt, OCX_LNE_BAD_CNT(16));
126541003396SSergey Temerkhanov OCX_DEBUGFS_ATTR(lne17_badcnt, OCX_LNE_BAD_CNT(17));
126641003396SSergey Temerkhanov OCX_DEBUGFS_ATTR(lne18_badcnt, OCX_LNE_BAD_CNT(18));
126741003396SSergey Temerkhanov OCX_DEBUGFS_ATTR(lne19_badcnt, OCX_LNE_BAD_CNT(19));
126841003396SSergey Temerkhanov OCX_DEBUGFS_ATTR(lne20_badcnt, OCX_LNE_BAD_CNT(20));
126941003396SSergey Temerkhanov OCX_DEBUGFS_ATTR(lne21_badcnt, OCX_LNE_BAD_CNT(21));
127041003396SSergey Temerkhanov OCX_DEBUGFS_ATTR(lne22_badcnt, OCX_LNE_BAD_CNT(22));
127141003396SSergey Temerkhanov OCX_DEBUGFS_ATTR(lne23_badcnt, OCX_LNE_BAD_CNT(23));
127241003396SSergey Temerkhanov 
127341003396SSergey Temerkhanov OCX_DEBUGFS_ATTR(com_int, OCX_COM_INT_W1S);
127441003396SSergey Temerkhanov 
127558d66175SZou Wei static struct debugfs_entry *ocx_dfs_ents[] = {
127641003396SSergey Temerkhanov 	&debugfs_tlk0_ecc_ctl,
127741003396SSergey Temerkhanov 	&debugfs_tlk1_ecc_ctl,
127841003396SSergey Temerkhanov 	&debugfs_tlk2_ecc_ctl,
127941003396SSergey Temerkhanov 
128041003396SSergey Temerkhanov 	&debugfs_rlk0_ecc_ctl,
128141003396SSergey Temerkhanov 	&debugfs_rlk1_ecc_ctl,
128241003396SSergey Temerkhanov 	&debugfs_rlk2_ecc_ctl,
128341003396SSergey Temerkhanov 
128441003396SSergey Temerkhanov 	&debugfs_com_link0_int,
128541003396SSergey Temerkhanov 	&debugfs_com_link1_int,
128641003396SSergey Temerkhanov 	&debugfs_com_link2_int,
128741003396SSergey Temerkhanov 
128841003396SSergey Temerkhanov 	&debugfs_lne00_badcnt,
128941003396SSergey Temerkhanov 	&debugfs_lne01_badcnt,
129041003396SSergey Temerkhanov 	&debugfs_lne02_badcnt,
129141003396SSergey Temerkhanov 	&debugfs_lne03_badcnt,
129241003396SSergey Temerkhanov 	&debugfs_lne04_badcnt,
129341003396SSergey Temerkhanov 	&debugfs_lne05_badcnt,
129441003396SSergey Temerkhanov 	&debugfs_lne06_badcnt,
129541003396SSergey Temerkhanov 	&debugfs_lne07_badcnt,
129641003396SSergey Temerkhanov 	&debugfs_lne08_badcnt,
129741003396SSergey Temerkhanov 	&debugfs_lne09_badcnt,
129841003396SSergey Temerkhanov 	&debugfs_lne10_badcnt,
129941003396SSergey Temerkhanov 	&debugfs_lne11_badcnt,
130041003396SSergey Temerkhanov 	&debugfs_lne12_badcnt,
130141003396SSergey Temerkhanov 	&debugfs_lne13_badcnt,
130241003396SSergey Temerkhanov 	&debugfs_lne14_badcnt,
130341003396SSergey Temerkhanov 	&debugfs_lne15_badcnt,
130441003396SSergey Temerkhanov 	&debugfs_lne16_badcnt,
130541003396SSergey Temerkhanov 	&debugfs_lne17_badcnt,
130641003396SSergey Temerkhanov 	&debugfs_lne18_badcnt,
130741003396SSergey Temerkhanov 	&debugfs_lne19_badcnt,
130841003396SSergey Temerkhanov 	&debugfs_lne20_badcnt,
130941003396SSergey Temerkhanov 	&debugfs_lne21_badcnt,
131041003396SSergey Temerkhanov 	&debugfs_lne22_badcnt,
131141003396SSergey Temerkhanov 	&debugfs_lne23_badcnt,
131241003396SSergey Temerkhanov 
131341003396SSergey Temerkhanov 	&debugfs_com_int,
131441003396SSergey Temerkhanov };
131541003396SSergey Temerkhanov 
131641003396SSergey Temerkhanov static const struct pci_device_id thunderx_ocx_pci_tbl[] = {
131741003396SSergey Temerkhanov 	{ PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, PCI_DEVICE_ID_THUNDER_OCX) },
131841003396SSergey Temerkhanov 	{ 0, },
131941003396SSergey Temerkhanov };
132041003396SSergey Temerkhanov 
thunderx_ocx_clearstats(struct thunderx_ocx * ocx)132141003396SSergey Temerkhanov static void thunderx_ocx_clearstats(struct thunderx_ocx *ocx)
132241003396SSergey Temerkhanov {
132341003396SSergey Temerkhanov 	int lane, stat, cfg;
132441003396SSergey Temerkhanov 
132541003396SSergey Temerkhanov 	for (lane = 0; lane < OCX_RX_LANES; lane++) {
132641003396SSergey Temerkhanov 		cfg = readq(ocx->regs + OCX_LNE_CFG(lane));
132741003396SSergey Temerkhanov 		cfg |= OCX_LNE_CFG_RX_STAT_RDCLR;
132841003396SSergey Temerkhanov 		cfg &= ~OCX_LNE_CFG_RX_STAT_ENA;
132941003396SSergey Temerkhanov 		writeq(cfg, ocx->regs + OCX_LNE_CFG(lane));
133041003396SSergey Temerkhanov 
133141003396SSergey Temerkhanov 		for (stat = 0; stat < OCX_RX_LANE_STATS; stat++)
133241003396SSergey Temerkhanov 			readq(ocx->regs + OCX_LNE_STAT(lane, stat));
133341003396SSergey Temerkhanov 	}
133441003396SSergey Temerkhanov }
133541003396SSergey Temerkhanov 
thunderx_ocx_probe(struct pci_dev * pdev,const struct pci_device_id * id)133641003396SSergey Temerkhanov static int thunderx_ocx_probe(struct pci_dev *pdev,
133741003396SSergey Temerkhanov 			      const struct pci_device_id *id)
133841003396SSergey Temerkhanov {
133941003396SSergey Temerkhanov 	struct thunderx_ocx *ocx;
134041003396SSergey Temerkhanov 	struct edac_device_ctl_info *edac_dev;
134141003396SSergey Temerkhanov 	char name[32];
134241003396SSergey Temerkhanov 	int idx;
134341003396SSergey Temerkhanov 	int i;
134441003396SSergey Temerkhanov 	int ret;
134541003396SSergey Temerkhanov 	u64 reg;
134641003396SSergey Temerkhanov 
134741003396SSergey Temerkhanov 	ret = pcim_enable_device(pdev);
134841003396SSergey Temerkhanov 	if (ret) {
134941003396SSergey Temerkhanov 		dev_err(&pdev->dev, "Cannot enable PCI device: %d\n", ret);
135041003396SSergey Temerkhanov 		return ret;
135141003396SSergey Temerkhanov 	}
135241003396SSergey Temerkhanov 
135341003396SSergey Temerkhanov 	ret = pcim_iomap_regions(pdev, BIT(0), "thunderx_ocx");
135441003396SSergey Temerkhanov 	if (ret) {
135541003396SSergey Temerkhanov 		dev_err(&pdev->dev, "Cannot map PCI resources: %d\n", ret);
135641003396SSergey Temerkhanov 		return ret;
135741003396SSergey Temerkhanov 	}
135841003396SSergey Temerkhanov 
135941003396SSergey Temerkhanov 	idx = edac_device_alloc_index();
136041003396SSergey Temerkhanov 	snprintf(name, sizeof(name), "OCX%d", idx);
136141003396SSergey Temerkhanov 	edac_dev = edac_device_alloc_ctl_info(sizeof(struct thunderx_ocx),
1362*48bc8869SJiri Slaby (SUSE) 					      name, 1, "CCPI", 1, 0, idx);
136341003396SSergey Temerkhanov 	if (!edac_dev) {
136489f5f8fbSChristophe JAILLET 		dev_err(&pdev->dev, "Cannot allocate EDAC device\n");
136541003396SSergey Temerkhanov 		return -ENOMEM;
136641003396SSergey Temerkhanov 	}
136741003396SSergey Temerkhanov 	ocx = edac_dev->pvt_info;
136841003396SSergey Temerkhanov 	ocx->edac_dev = edac_dev;
136941003396SSergey Temerkhanov 	ocx->com_ring_head = 0;
137041003396SSergey Temerkhanov 	ocx->com_ring_tail = 0;
137141003396SSergey Temerkhanov 	ocx->link_ring_head = 0;
137241003396SSergey Temerkhanov 	ocx->link_ring_tail = 0;
137341003396SSergey Temerkhanov 
137441003396SSergey Temerkhanov 	ocx->regs = pcim_iomap_table(pdev)[0];
137541003396SSergey Temerkhanov 	if (!ocx->regs) {
137689f5f8fbSChristophe JAILLET 		dev_err(&pdev->dev, "Cannot map PCI resources\n");
137741003396SSergey Temerkhanov 		ret = -ENODEV;
137841003396SSergey Temerkhanov 		goto err_free;
137941003396SSergey Temerkhanov 	}
138041003396SSergey Temerkhanov 
138141003396SSergey Temerkhanov 	ocx->pdev = pdev;
138241003396SSergey Temerkhanov 
138341003396SSergey Temerkhanov 	for (i = 0; i < OCX_INTS; i++) {
138441003396SSergey Temerkhanov 		ocx->msix_ent[i].entry = i;
138541003396SSergey Temerkhanov 		ocx->msix_ent[i].vector = 0;
138641003396SSergey Temerkhanov 	}
138741003396SSergey Temerkhanov 
138841003396SSergey Temerkhanov 	ret = pci_enable_msix_exact(pdev, ocx->msix_ent, OCX_INTS);
138941003396SSergey Temerkhanov 	if (ret) {
139041003396SSergey Temerkhanov 		dev_err(&pdev->dev, "Cannot enable interrupt: %d\n", ret);
139141003396SSergey Temerkhanov 		goto err_free;
139241003396SSergey Temerkhanov 	}
139341003396SSergey Temerkhanov 
139441003396SSergey Temerkhanov 	for (i = 0; i < OCX_INTS; i++) {
139541003396SSergey Temerkhanov 		ret = devm_request_threaded_irq(&pdev->dev,
139641003396SSergey Temerkhanov 						ocx->msix_ent[i].vector,
139741003396SSergey Temerkhanov 						(i == 3) ?
139841003396SSergey Temerkhanov 						 thunderx_ocx_com_isr :
139941003396SSergey Temerkhanov 						 thunderx_ocx_lnk_isr,
140041003396SSergey Temerkhanov 						(i == 3) ?
140141003396SSergey Temerkhanov 						 thunderx_ocx_com_threaded_isr :
140241003396SSergey Temerkhanov 						 thunderx_ocx_lnk_threaded_isr,
140341003396SSergey Temerkhanov 						0, "[EDAC] ThunderX OCX",
140441003396SSergey Temerkhanov 						&ocx->msix_ent[i]);
140541003396SSergey Temerkhanov 		if (ret)
140641003396SSergey Temerkhanov 			goto err_free;
140741003396SSergey Temerkhanov 	}
140841003396SSergey Temerkhanov 
140941003396SSergey Temerkhanov 	edac_dev->dev = &pdev->dev;
141041003396SSergey Temerkhanov 	edac_dev->dev_name = dev_name(&pdev->dev);
141141003396SSergey Temerkhanov 	edac_dev->mod_name = "thunderx-ocx";
141241003396SSergey Temerkhanov 	edac_dev->ctl_name = "thunderx-ocx";
141341003396SSergey Temerkhanov 
141441003396SSergey Temerkhanov 	ret = edac_device_add_device(edac_dev);
141541003396SSergey Temerkhanov 	if (ret) {
141641003396SSergey Temerkhanov 		dev_err(&pdev->dev, "Cannot add EDAC device: %d\n", ret);
141741003396SSergey Temerkhanov 		goto err_free;
141841003396SSergey Temerkhanov 	}
141941003396SSergey Temerkhanov 
142041003396SSergey Temerkhanov 	if (IS_ENABLED(CONFIG_EDAC_DEBUG)) {
142141003396SSergey Temerkhanov 		ocx->debugfs = edac_debugfs_create_dir(pdev->dev.kobj.name);
142241003396SSergey Temerkhanov 
142341003396SSergey Temerkhanov 		ret = thunderx_create_debugfs_nodes(ocx->debugfs,
142441003396SSergey Temerkhanov 						    ocx_dfs_ents,
142541003396SSergey Temerkhanov 						    ocx,
142641003396SSergey Temerkhanov 						    ARRAY_SIZE(ocx_dfs_ents));
142741003396SSergey Temerkhanov 		if (ret != ARRAY_SIZE(ocx_dfs_ents)) {
142841003396SSergey Temerkhanov 			dev_warn(&pdev->dev, "Error creating debugfs entries: %d%s\n",
142941003396SSergey Temerkhanov 				 ret, ret >= 0 ? " created" : "");
143041003396SSergey Temerkhanov 		}
143141003396SSergey Temerkhanov 	}
143241003396SSergey Temerkhanov 
143341003396SSergey Temerkhanov 	pci_set_drvdata(pdev, edac_dev);
143441003396SSergey Temerkhanov 
143541003396SSergey Temerkhanov 	thunderx_ocx_clearstats(ocx);
143641003396SSergey Temerkhanov 
143741003396SSergey Temerkhanov 	for (i = 0; i < OCX_RX_LANES; i++) {
143841003396SSergey Temerkhanov 		writeq(OCX_LNE_INT_ENA_ALL,
143941003396SSergey Temerkhanov 		       ocx->regs + OCX_LNE_INT_EN(i));
144041003396SSergey Temerkhanov 
144141003396SSergey Temerkhanov 		reg = readq(ocx->regs + OCX_LNE_INT(i));
144241003396SSergey Temerkhanov 		writeq(reg, ocx->regs + OCX_LNE_INT(i));
144341003396SSergey Temerkhanov 
144441003396SSergey Temerkhanov 	}
144541003396SSergey Temerkhanov 
144641003396SSergey Temerkhanov 	for (i = 0; i < OCX_LINK_INTS; i++) {
144741003396SSergey Temerkhanov 		reg = readq(ocx->regs + OCX_COM_LINKX_INT(i));
144841003396SSergey Temerkhanov 		writeq(reg, ocx->regs + OCX_COM_LINKX_INT(i));
144941003396SSergey Temerkhanov 
145041003396SSergey Temerkhanov 		writeq(OCX_COM_LINKX_INT_ENA_ALL,
145141003396SSergey Temerkhanov 		       ocx->regs + OCX_COM_LINKX_INT_ENA_W1S(i));
145241003396SSergey Temerkhanov 	}
145341003396SSergey Temerkhanov 
145441003396SSergey Temerkhanov 	reg = readq(ocx->regs + OCX_COM_INT);
145541003396SSergey Temerkhanov 	writeq(reg, ocx->regs + OCX_COM_INT);
145641003396SSergey Temerkhanov 
145741003396SSergey Temerkhanov 	writeq(OCX_COM_INT_ENA_ALL, ocx->regs + OCX_COM_INT_ENA_W1S);
145841003396SSergey Temerkhanov 
145941003396SSergey Temerkhanov 	return 0;
146041003396SSergey Temerkhanov err_free:
146141003396SSergey Temerkhanov 	edac_device_free_ctl_info(edac_dev);
146241003396SSergey Temerkhanov 
146341003396SSergey Temerkhanov 	return ret;
146441003396SSergey Temerkhanov }
146541003396SSergey Temerkhanov 
thunderx_ocx_remove(struct pci_dev * pdev)146641003396SSergey Temerkhanov static void thunderx_ocx_remove(struct pci_dev *pdev)
146741003396SSergey Temerkhanov {
146841003396SSergey Temerkhanov 	struct edac_device_ctl_info *edac_dev = pci_get_drvdata(pdev);
146941003396SSergey Temerkhanov 	struct thunderx_ocx *ocx = edac_dev->pvt_info;
147041003396SSergey Temerkhanov 	int i;
147141003396SSergey Temerkhanov 
147241003396SSergey Temerkhanov 	writeq(OCX_COM_INT_ENA_ALL, ocx->regs + OCX_COM_INT_ENA_W1C);
147341003396SSergey Temerkhanov 
147441003396SSergey Temerkhanov 	for (i = 0; i < OCX_INTS; i++) {
147541003396SSergey Temerkhanov 		writeq(OCX_COM_LINKX_INT_ENA_ALL,
147641003396SSergey Temerkhanov 		       ocx->regs + OCX_COM_LINKX_INT_ENA_W1C(i));
147741003396SSergey Temerkhanov 	}
147841003396SSergey Temerkhanov 
147941003396SSergey Temerkhanov 	edac_debugfs_remove_recursive(ocx->debugfs);
148041003396SSergey Temerkhanov 
148141003396SSergey Temerkhanov 	edac_device_del_device(&pdev->dev);
148241003396SSergey Temerkhanov 	edac_device_free_ctl_info(edac_dev);
148341003396SSergey Temerkhanov }
148441003396SSergey Temerkhanov 
148541003396SSergey Temerkhanov MODULE_DEVICE_TABLE(pci, thunderx_ocx_pci_tbl);
148641003396SSergey Temerkhanov 
148741003396SSergey Temerkhanov static struct pci_driver thunderx_ocx_driver = {
148841003396SSergey Temerkhanov 	.name     = "thunderx_ocx_edac",
148941003396SSergey Temerkhanov 	.probe    = thunderx_ocx_probe,
149041003396SSergey Temerkhanov 	.remove   = thunderx_ocx_remove,
149141003396SSergey Temerkhanov 	.id_table = thunderx_ocx_pci_tbl,
149241003396SSergey Temerkhanov };
149341003396SSergey Temerkhanov 
149441003396SSergey Temerkhanov /*---------------------- L2C driver ---------------------------------*/
149541003396SSergey Temerkhanov 
149641003396SSergey Temerkhanov #define PCI_DEVICE_ID_THUNDER_L2C_TAD 0xa02e
149741003396SSergey Temerkhanov #define PCI_DEVICE_ID_THUNDER_L2C_CBC 0xa02f
149841003396SSergey Temerkhanov #define PCI_DEVICE_ID_THUNDER_L2C_MCI 0xa030
149941003396SSergey Temerkhanov 
150041003396SSergey Temerkhanov #define L2C_TAD_INT_W1C		0x40000
150141003396SSergey Temerkhanov #define L2C_TAD_INT_W1S		0x40008
150241003396SSergey Temerkhanov 
150341003396SSergey Temerkhanov #define L2C_TAD_INT_ENA_W1C	0x40020
150441003396SSergey Temerkhanov #define L2C_TAD_INT_ENA_W1S	0x40028
150541003396SSergey Temerkhanov 
150641003396SSergey Temerkhanov 
150741003396SSergey Temerkhanov #define L2C_TAD_INT_L2DDBE	 BIT(1)
150841003396SSergey Temerkhanov #define L2C_TAD_INT_SBFSBE	 BIT(2)
150941003396SSergey Temerkhanov #define L2C_TAD_INT_SBFDBE	 BIT(3)
151041003396SSergey Temerkhanov #define L2C_TAD_INT_FBFSBE	 BIT(4)
151141003396SSergey Temerkhanov #define L2C_TAD_INT_FBFDBE	 BIT(5)
151241003396SSergey Temerkhanov #define L2C_TAD_INT_TAGDBE	 BIT(9)
151341003396SSergey Temerkhanov #define L2C_TAD_INT_RDDISLMC	 BIT(15)
151441003396SSergey Temerkhanov #define L2C_TAD_INT_WRDISLMC	 BIT(16)
151541003396SSergey Temerkhanov #define L2C_TAD_INT_LFBTO	 BIT(17)
151641003396SSergey Temerkhanov #define L2C_TAD_INT_GSYNCTO	 BIT(18)
151741003396SSergey Temerkhanov #define L2C_TAD_INT_RTGSBE	 BIT(32)
151841003396SSergey Temerkhanov #define L2C_TAD_INT_RTGDBE	 BIT(33)
151941003396SSergey Temerkhanov #define L2C_TAD_INT_RDDISOCI	 BIT(34)
152041003396SSergey Temerkhanov #define L2C_TAD_INT_WRDISOCI	 BIT(35)
152141003396SSergey Temerkhanov 
152241003396SSergey Temerkhanov #define L2C_TAD_INT_ECC		(L2C_TAD_INT_L2DDBE | \
152341003396SSergey Temerkhanov 				 L2C_TAD_INT_SBFSBE | L2C_TAD_INT_SBFDBE | \
152441003396SSergey Temerkhanov 				 L2C_TAD_INT_FBFSBE | L2C_TAD_INT_FBFDBE)
152541003396SSergey Temerkhanov 
152641003396SSergey Temerkhanov #define L2C_TAD_INT_CE          (L2C_TAD_INT_SBFSBE | \
152741003396SSergey Temerkhanov 				 L2C_TAD_INT_FBFSBE)
152841003396SSergey Temerkhanov 
152941003396SSergey Temerkhanov #define L2C_TAD_INT_UE          (L2C_TAD_INT_L2DDBE | \
153041003396SSergey Temerkhanov 				 L2C_TAD_INT_SBFDBE | \
153141003396SSergey Temerkhanov 				 L2C_TAD_INT_FBFDBE | \
153241003396SSergey Temerkhanov 				 L2C_TAD_INT_TAGDBE | \
153341003396SSergey Temerkhanov 				 L2C_TAD_INT_RTGDBE | \
153441003396SSergey Temerkhanov 				 L2C_TAD_INT_WRDISOCI | \
153541003396SSergey Temerkhanov 				 L2C_TAD_INT_RDDISOCI | \
153641003396SSergey Temerkhanov 				 L2C_TAD_INT_WRDISLMC | \
153741003396SSergey Temerkhanov 				 L2C_TAD_INT_RDDISLMC | \
153841003396SSergey Temerkhanov 				 L2C_TAD_INT_LFBTO    | \
153941003396SSergey Temerkhanov 				 L2C_TAD_INT_GSYNCTO)
154041003396SSergey Temerkhanov 
154141003396SSergey Temerkhanov static const struct error_descr l2_tad_errors[] = {
154241003396SSergey Temerkhanov 	{
154341003396SSergey Temerkhanov 		.type  = ERR_CORRECTED,
154441003396SSergey Temerkhanov 		.mask  = L2C_TAD_INT_SBFSBE,
154541003396SSergey Temerkhanov 		.descr = "SBF single-bit error",
154641003396SSergey Temerkhanov 	},
154741003396SSergey Temerkhanov 	{
154841003396SSergey Temerkhanov 		.type  = ERR_CORRECTED,
154941003396SSergey Temerkhanov 		.mask  = L2C_TAD_INT_FBFSBE,
155041003396SSergey Temerkhanov 		.descr = "FBF single-bit error",
155141003396SSergey Temerkhanov 	},
155241003396SSergey Temerkhanov 	{
155341003396SSergey Temerkhanov 		.type  = ERR_UNCORRECTED,
155441003396SSergey Temerkhanov 		.mask  = L2C_TAD_INT_L2DDBE,
155541003396SSergey Temerkhanov 		.descr = "L2D double-bit error",
155641003396SSergey Temerkhanov 	},
155741003396SSergey Temerkhanov 	{
155841003396SSergey Temerkhanov 		.type  = ERR_UNCORRECTED,
155941003396SSergey Temerkhanov 		.mask  = L2C_TAD_INT_SBFDBE,
156041003396SSergey Temerkhanov 		.descr = "SBF double-bit error",
156141003396SSergey Temerkhanov 	},
156241003396SSergey Temerkhanov 	{
156341003396SSergey Temerkhanov 		.type  = ERR_UNCORRECTED,
156441003396SSergey Temerkhanov 		.mask  = L2C_TAD_INT_FBFDBE,
156541003396SSergey Temerkhanov 		.descr = "FBF double-bit error",
156641003396SSergey Temerkhanov 	},
156741003396SSergey Temerkhanov 	{
156841003396SSergey Temerkhanov 		.type  = ERR_UNCORRECTED,
156941003396SSergey Temerkhanov 		.mask  = L2C_TAD_INT_TAGDBE,
157041003396SSergey Temerkhanov 		.descr = "TAG double-bit error",
157141003396SSergey Temerkhanov 	},
157241003396SSergey Temerkhanov 	{
157341003396SSergey Temerkhanov 		.type  = ERR_UNCORRECTED,
157441003396SSergey Temerkhanov 		.mask  = L2C_TAD_INT_RTGDBE,
157541003396SSergey Temerkhanov 		.descr = "RTG double-bit error",
157641003396SSergey Temerkhanov 	},
157741003396SSergey Temerkhanov 	{
157841003396SSergey Temerkhanov 		.type  = ERR_UNCORRECTED,
157941003396SSergey Temerkhanov 		.mask  = L2C_TAD_INT_WRDISOCI,
158041003396SSergey Temerkhanov 		.descr = "Write to a disabled CCPI",
158141003396SSergey Temerkhanov 	},
158241003396SSergey Temerkhanov 	{
158341003396SSergey Temerkhanov 		.type  = ERR_UNCORRECTED,
158441003396SSergey Temerkhanov 		.mask  = L2C_TAD_INT_RDDISOCI,
158541003396SSergey Temerkhanov 		.descr = "Read from a disabled CCPI",
158641003396SSergey Temerkhanov 	},
158741003396SSergey Temerkhanov 	{
158841003396SSergey Temerkhanov 		.type  = ERR_UNCORRECTED,
158941003396SSergey Temerkhanov 		.mask  = L2C_TAD_INT_WRDISLMC,
159041003396SSergey Temerkhanov 		.descr = "Write to a disabled LMC",
159141003396SSergey Temerkhanov 	},
159241003396SSergey Temerkhanov 	{
159341003396SSergey Temerkhanov 		.type  = ERR_UNCORRECTED,
159441003396SSergey Temerkhanov 		.mask  = L2C_TAD_INT_RDDISLMC,
159541003396SSergey Temerkhanov 		.descr = "Read from a disabled LMC",
159641003396SSergey Temerkhanov 	},
159741003396SSergey Temerkhanov 	{
159841003396SSergey Temerkhanov 		.type  = ERR_UNCORRECTED,
159941003396SSergey Temerkhanov 		.mask  = L2C_TAD_INT_LFBTO,
160041003396SSergey Temerkhanov 		.descr = "LFB entry timeout",
160141003396SSergey Temerkhanov 	},
160241003396SSergey Temerkhanov 	{
160341003396SSergey Temerkhanov 		.type  = ERR_UNCORRECTED,
160441003396SSergey Temerkhanov 		.mask  = L2C_TAD_INT_GSYNCTO,
160541003396SSergey Temerkhanov 		.descr = "Global sync CCPI timeout",
160641003396SSergey Temerkhanov 	},
160741003396SSergey Temerkhanov 	{0, 0, NULL},
160841003396SSergey Temerkhanov };
160941003396SSergey Temerkhanov 
161041003396SSergey Temerkhanov #define L2C_TAD_INT_TAG		(L2C_TAD_INT_TAGDBE)
161141003396SSergey Temerkhanov 
161241003396SSergey Temerkhanov #define L2C_TAD_INT_RTG		(L2C_TAD_INT_RTGDBE)
161341003396SSergey Temerkhanov 
161441003396SSergey Temerkhanov #define L2C_TAD_INT_DISLMC	(L2C_TAD_INT_WRDISLMC | L2C_TAD_INT_RDDISLMC)
161541003396SSergey Temerkhanov 
161641003396SSergey Temerkhanov #define L2C_TAD_INT_DISOCI	(L2C_TAD_INT_WRDISOCI | L2C_TAD_INT_RDDISOCI)
161741003396SSergey Temerkhanov 
161841003396SSergey Temerkhanov #define L2C_TAD_INT_ENA_ALL	(L2C_TAD_INT_ECC | L2C_TAD_INT_TAG | \
16195195c206SSergey Temerkhanov 				 L2C_TAD_INT_RTG | \
162041003396SSergey Temerkhanov 				 L2C_TAD_INT_DISLMC | L2C_TAD_INT_DISOCI | \
162141003396SSergey Temerkhanov 				 L2C_TAD_INT_LFBTO)
162241003396SSergey Temerkhanov 
162341003396SSergey Temerkhanov #define L2C_TAD_TIMETWO		0x50000
162441003396SSergey Temerkhanov #define L2C_TAD_TIMEOUT		0x50100
162541003396SSergey Temerkhanov #define L2C_TAD_ERR		0x60000
162641003396SSergey Temerkhanov #define L2C_TAD_TQD_ERR		0x60100
162741003396SSergey Temerkhanov #define L2C_TAD_TTG_ERR		0x60200
162841003396SSergey Temerkhanov 
162941003396SSergey Temerkhanov 
163041003396SSergey Temerkhanov #define L2C_CBC_INT_W1C		0x60000
163141003396SSergey Temerkhanov 
163241003396SSergey Temerkhanov #define L2C_CBC_INT_RSDSBE	 BIT(0)
163341003396SSergey Temerkhanov #define L2C_CBC_INT_RSDDBE	 BIT(1)
163441003396SSergey Temerkhanov 
163541003396SSergey Temerkhanov #define L2C_CBC_INT_RSD		 (L2C_CBC_INT_RSDSBE | L2C_CBC_INT_RSDDBE)
163641003396SSergey Temerkhanov 
163741003396SSergey Temerkhanov #define L2C_CBC_INT_MIBSBE	 BIT(4)
163841003396SSergey Temerkhanov #define L2C_CBC_INT_MIBDBE	 BIT(5)
163941003396SSergey Temerkhanov 
164041003396SSergey Temerkhanov #define L2C_CBC_INT_MIB		 (L2C_CBC_INT_MIBSBE | L2C_CBC_INT_MIBDBE)
164141003396SSergey Temerkhanov 
164241003396SSergey Temerkhanov #define L2C_CBC_INT_IORDDISOCI	 BIT(6)
164341003396SSergey Temerkhanov #define L2C_CBC_INT_IOWRDISOCI	 BIT(7)
164441003396SSergey Temerkhanov 
164541003396SSergey Temerkhanov #define L2C_CBC_INT_IODISOCI	 (L2C_CBC_INT_IORDDISOCI | \
164641003396SSergey Temerkhanov 				  L2C_CBC_INT_IOWRDISOCI)
164741003396SSergey Temerkhanov 
164841003396SSergey Temerkhanov #define L2C_CBC_INT_CE		 (L2C_CBC_INT_RSDSBE | L2C_CBC_INT_MIBSBE)
164941003396SSergey Temerkhanov #define L2C_CBC_INT_UE		 (L2C_CBC_INT_RSDDBE | L2C_CBC_INT_MIBDBE)
165041003396SSergey Temerkhanov 
165141003396SSergey Temerkhanov 
165241003396SSergey Temerkhanov static const struct error_descr l2_cbc_errors[] = {
165341003396SSergey Temerkhanov 	{
165441003396SSergey Temerkhanov 		.type  = ERR_CORRECTED,
165541003396SSergey Temerkhanov 		.mask  = L2C_CBC_INT_RSDSBE,
165641003396SSergey Temerkhanov 		.descr = "RSD single-bit error",
165741003396SSergey Temerkhanov 	},
165841003396SSergey Temerkhanov 	{
165941003396SSergey Temerkhanov 		.type  = ERR_CORRECTED,
166041003396SSergey Temerkhanov 		.mask  = L2C_CBC_INT_MIBSBE,
166141003396SSergey Temerkhanov 		.descr = "MIB single-bit error",
166241003396SSergey Temerkhanov 	},
166341003396SSergey Temerkhanov 	{
166441003396SSergey Temerkhanov 		.type  = ERR_UNCORRECTED,
166541003396SSergey Temerkhanov 		.mask  = L2C_CBC_INT_RSDDBE,
166641003396SSergey Temerkhanov 		.descr = "RSD double-bit error",
166741003396SSergey Temerkhanov 	},
166841003396SSergey Temerkhanov 	{
166941003396SSergey Temerkhanov 		.type  = ERR_UNCORRECTED,
167041003396SSergey Temerkhanov 		.mask  = L2C_CBC_INT_MIBDBE,
167141003396SSergey Temerkhanov 		.descr = "MIB double-bit error",
167241003396SSergey Temerkhanov 	},
167341003396SSergey Temerkhanov 	{
167441003396SSergey Temerkhanov 		.type  = ERR_UNCORRECTED,
167541003396SSergey Temerkhanov 		.mask  = L2C_CBC_INT_IORDDISOCI,
167641003396SSergey Temerkhanov 		.descr = "Read from a disabled CCPI",
167741003396SSergey Temerkhanov 	},
167841003396SSergey Temerkhanov 	{
167941003396SSergey Temerkhanov 		.type  = ERR_UNCORRECTED,
168041003396SSergey Temerkhanov 		.mask  = L2C_CBC_INT_IOWRDISOCI,
168141003396SSergey Temerkhanov 		.descr = "Write to a disabled CCPI",
168241003396SSergey Temerkhanov 	},
168341003396SSergey Temerkhanov 	{0, 0, NULL},
168441003396SSergey Temerkhanov };
168541003396SSergey Temerkhanov 
168641003396SSergey Temerkhanov #define L2C_CBC_INT_W1S		0x60008
168741003396SSergey Temerkhanov #define L2C_CBC_INT_ENA_W1C	0x60020
168841003396SSergey Temerkhanov 
168941003396SSergey Temerkhanov #define L2C_CBC_INT_ENA_ALL	 (L2C_CBC_INT_RSD | L2C_CBC_INT_MIB | \
169041003396SSergey Temerkhanov 				  L2C_CBC_INT_IODISOCI)
169141003396SSergey Temerkhanov 
169241003396SSergey Temerkhanov #define L2C_CBC_INT_ENA_W1S	0x60028
169341003396SSergey Temerkhanov 
169441003396SSergey Temerkhanov #define L2C_CBC_IODISOCIERR	0x80008
169541003396SSergey Temerkhanov #define L2C_CBC_IOCERR		0x80010
169641003396SSergey Temerkhanov #define L2C_CBC_RSDERR		0x80018
169741003396SSergey Temerkhanov #define L2C_CBC_MIBERR		0x80020
169841003396SSergey Temerkhanov 
169941003396SSergey Temerkhanov 
170041003396SSergey Temerkhanov #define L2C_MCI_INT_W1C		0x0
170141003396SSergey Temerkhanov 
170241003396SSergey Temerkhanov #define L2C_MCI_INT_VBFSBE	 BIT(0)
170341003396SSergey Temerkhanov #define L2C_MCI_INT_VBFDBE	 BIT(1)
170441003396SSergey Temerkhanov 
170541003396SSergey Temerkhanov static const struct error_descr l2_mci_errors[] = {
170641003396SSergey Temerkhanov 	{
170741003396SSergey Temerkhanov 		.type  = ERR_CORRECTED,
170841003396SSergey Temerkhanov 		.mask  = L2C_MCI_INT_VBFSBE,
170941003396SSergey Temerkhanov 		.descr = "VBF single-bit error",
171041003396SSergey Temerkhanov 	},
171141003396SSergey Temerkhanov 	{
171241003396SSergey Temerkhanov 		.type  = ERR_UNCORRECTED,
171341003396SSergey Temerkhanov 		.mask  = L2C_MCI_INT_VBFDBE,
171441003396SSergey Temerkhanov 		.descr = "VBF double-bit error",
171541003396SSergey Temerkhanov 	},
171641003396SSergey Temerkhanov 	{0, 0, NULL},
171741003396SSergey Temerkhanov };
171841003396SSergey Temerkhanov 
171941003396SSergey Temerkhanov #define L2C_MCI_INT_W1S		0x8
172041003396SSergey Temerkhanov #define L2C_MCI_INT_ENA_W1C	0x20
172141003396SSergey Temerkhanov 
172241003396SSergey Temerkhanov #define L2C_MCI_INT_ENA_ALL	 (L2C_MCI_INT_VBFSBE | L2C_MCI_INT_VBFDBE)
172341003396SSergey Temerkhanov 
172441003396SSergey Temerkhanov #define L2C_MCI_INT_ENA_W1S	0x28
172541003396SSergey Temerkhanov 
172641003396SSergey Temerkhanov #define L2C_MCI_ERR		0x10000
172741003396SSergey Temerkhanov 
172841003396SSergey Temerkhanov #define L2C_MESSAGE_SIZE	SZ_1K
172941003396SSergey Temerkhanov #define L2C_OTHER_SIZE		(50 * ARRAY_SIZE(l2_tad_errors))
173041003396SSergey Temerkhanov 
173141003396SSergey Temerkhanov struct l2c_err_ctx {
173241003396SSergey Temerkhanov 	char *reg_ext_name;
173341003396SSergey Temerkhanov 	u64  reg_int;
173441003396SSergey Temerkhanov 	u64  reg_ext;
173541003396SSergey Temerkhanov };
173641003396SSergey Temerkhanov 
173741003396SSergey Temerkhanov struct thunderx_l2c {
173841003396SSergey Temerkhanov 	void __iomem *regs;
173941003396SSergey Temerkhanov 	struct pci_dev *pdev;
174041003396SSergey Temerkhanov 	struct edac_device_ctl_info *edac_dev;
174141003396SSergey Temerkhanov 
174241003396SSergey Temerkhanov 	struct dentry *debugfs;
174341003396SSergey Temerkhanov 
174441003396SSergey Temerkhanov 	int index;
174541003396SSergey Temerkhanov 
174641003396SSergey Temerkhanov 	struct msix_entry msix_ent;
174741003396SSergey Temerkhanov 
174841003396SSergey Temerkhanov 	struct l2c_err_ctx err_ctx[RING_ENTRIES];
174941003396SSergey Temerkhanov 	unsigned long ring_head;
175041003396SSergey Temerkhanov 	unsigned long ring_tail;
175141003396SSergey Temerkhanov };
175241003396SSergey Temerkhanov 
thunderx_l2c_tad_isr(int irq,void * irq_id)175341003396SSergey Temerkhanov static irqreturn_t thunderx_l2c_tad_isr(int irq, void *irq_id)
175441003396SSergey Temerkhanov {
175541003396SSergey Temerkhanov 	struct msix_entry *msix = irq_id;
175641003396SSergey Temerkhanov 	struct thunderx_l2c *tad = container_of(msix, struct thunderx_l2c,
175741003396SSergey Temerkhanov 						msix_ent);
175841003396SSergey Temerkhanov 
175941003396SSergey Temerkhanov 	unsigned long head = ring_pos(tad->ring_head, ARRAY_SIZE(tad->err_ctx));
176041003396SSergey Temerkhanov 	struct l2c_err_ctx *ctx = &tad->err_ctx[head];
176141003396SSergey Temerkhanov 
176241003396SSergey Temerkhanov 	ctx->reg_int = readq(tad->regs + L2C_TAD_INT_W1C);
176341003396SSergey Temerkhanov 
176441003396SSergey Temerkhanov 	if (ctx->reg_int & L2C_TAD_INT_ECC) {
176541003396SSergey Temerkhanov 		ctx->reg_ext_name = "TQD_ERR";
176641003396SSergey Temerkhanov 		ctx->reg_ext = readq(tad->regs + L2C_TAD_TQD_ERR);
176741003396SSergey Temerkhanov 	} else if (ctx->reg_int & L2C_TAD_INT_TAG) {
176841003396SSergey Temerkhanov 		ctx->reg_ext_name = "TTG_ERR";
176941003396SSergey Temerkhanov 		ctx->reg_ext = readq(tad->regs + L2C_TAD_TTG_ERR);
177041003396SSergey Temerkhanov 	} else if (ctx->reg_int & L2C_TAD_INT_LFBTO) {
177141003396SSergey Temerkhanov 		ctx->reg_ext_name = "TIMEOUT";
177241003396SSergey Temerkhanov 		ctx->reg_ext = readq(tad->regs + L2C_TAD_TIMEOUT);
177341003396SSergey Temerkhanov 	} else if (ctx->reg_int & L2C_TAD_INT_DISOCI) {
177441003396SSergey Temerkhanov 		ctx->reg_ext_name = "ERR";
177541003396SSergey Temerkhanov 		ctx->reg_ext = readq(tad->regs + L2C_TAD_ERR);
177641003396SSergey Temerkhanov 	}
177741003396SSergey Temerkhanov 
177841003396SSergey Temerkhanov 	writeq(ctx->reg_int, tad->regs + L2C_TAD_INT_W1C);
177941003396SSergey Temerkhanov 
178041003396SSergey Temerkhanov 	tad->ring_head++;
178141003396SSergey Temerkhanov 
178241003396SSergey Temerkhanov 	return IRQ_WAKE_THREAD;
178341003396SSergey Temerkhanov }
178441003396SSergey Temerkhanov 
thunderx_l2c_cbc_isr(int irq,void * irq_id)178541003396SSergey Temerkhanov static irqreturn_t thunderx_l2c_cbc_isr(int irq, void *irq_id)
178641003396SSergey Temerkhanov {
178741003396SSergey Temerkhanov 	struct msix_entry *msix = irq_id;
178841003396SSergey Temerkhanov 	struct thunderx_l2c *cbc = container_of(msix, struct thunderx_l2c,
178941003396SSergey Temerkhanov 						msix_ent);
179041003396SSergey Temerkhanov 
179141003396SSergey Temerkhanov 	unsigned long head = ring_pos(cbc->ring_head, ARRAY_SIZE(cbc->err_ctx));
179241003396SSergey Temerkhanov 	struct l2c_err_ctx *ctx = &cbc->err_ctx[head];
179341003396SSergey Temerkhanov 
179441003396SSergey Temerkhanov 	ctx->reg_int = readq(cbc->regs + L2C_CBC_INT_W1C);
179541003396SSergey Temerkhanov 
179641003396SSergey Temerkhanov 	if (ctx->reg_int & L2C_CBC_INT_RSD) {
179741003396SSergey Temerkhanov 		ctx->reg_ext_name = "RSDERR";
179841003396SSergey Temerkhanov 		ctx->reg_ext = readq(cbc->regs + L2C_CBC_RSDERR);
179941003396SSergey Temerkhanov 	} else if (ctx->reg_int & L2C_CBC_INT_MIB) {
180041003396SSergey Temerkhanov 		ctx->reg_ext_name = "MIBERR";
180141003396SSergey Temerkhanov 		ctx->reg_ext = readq(cbc->regs + L2C_CBC_MIBERR);
180241003396SSergey Temerkhanov 	} else if (ctx->reg_int & L2C_CBC_INT_IODISOCI) {
180341003396SSergey Temerkhanov 		ctx->reg_ext_name = "IODISOCIERR";
180441003396SSergey Temerkhanov 		ctx->reg_ext = readq(cbc->regs + L2C_CBC_IODISOCIERR);
180541003396SSergey Temerkhanov 	}
180641003396SSergey Temerkhanov 
180741003396SSergey Temerkhanov 	writeq(ctx->reg_int, cbc->regs + L2C_CBC_INT_W1C);
180841003396SSergey Temerkhanov 
180941003396SSergey Temerkhanov 	cbc->ring_head++;
181041003396SSergey Temerkhanov 
181141003396SSergey Temerkhanov 	return IRQ_WAKE_THREAD;
181241003396SSergey Temerkhanov }
181341003396SSergey Temerkhanov 
thunderx_l2c_mci_isr(int irq,void * irq_id)181441003396SSergey Temerkhanov static irqreturn_t thunderx_l2c_mci_isr(int irq, void *irq_id)
181541003396SSergey Temerkhanov {
181641003396SSergey Temerkhanov 	struct msix_entry *msix = irq_id;
181741003396SSergey Temerkhanov 	struct thunderx_l2c *mci = container_of(msix, struct thunderx_l2c,
181841003396SSergey Temerkhanov 						msix_ent);
181941003396SSergey Temerkhanov 
182041003396SSergey Temerkhanov 	unsigned long head = ring_pos(mci->ring_head, ARRAY_SIZE(mci->err_ctx));
182141003396SSergey Temerkhanov 	struct l2c_err_ctx *ctx = &mci->err_ctx[head];
182241003396SSergey Temerkhanov 
182341003396SSergey Temerkhanov 	ctx->reg_int = readq(mci->regs + L2C_MCI_INT_W1C);
182441003396SSergey Temerkhanov 	ctx->reg_ext = readq(mci->regs + L2C_MCI_ERR);
182541003396SSergey Temerkhanov 
182641003396SSergey Temerkhanov 	writeq(ctx->reg_int, mci->regs + L2C_MCI_INT_W1C);
182741003396SSergey Temerkhanov 
182841003396SSergey Temerkhanov 	ctx->reg_ext_name = "ERR";
182941003396SSergey Temerkhanov 
183041003396SSergey Temerkhanov 	mci->ring_head++;
183141003396SSergey Temerkhanov 
183241003396SSergey Temerkhanov 	return IRQ_WAKE_THREAD;
183341003396SSergey Temerkhanov }
183441003396SSergey Temerkhanov 
thunderx_l2c_threaded_isr(int irq,void * irq_id)183541003396SSergey Temerkhanov static irqreturn_t thunderx_l2c_threaded_isr(int irq, void *irq_id)
183641003396SSergey Temerkhanov {
183741003396SSergey Temerkhanov 	struct msix_entry *msix = irq_id;
183841003396SSergey Temerkhanov 	struct thunderx_l2c *l2c = container_of(msix, struct thunderx_l2c,
183941003396SSergey Temerkhanov 						msix_ent);
184041003396SSergey Temerkhanov 
184141003396SSergey Temerkhanov 	unsigned long tail = ring_pos(l2c->ring_tail, ARRAY_SIZE(l2c->err_ctx));
184241003396SSergey Temerkhanov 	struct l2c_err_ctx *ctx = &l2c->err_ctx[tail];
184341003396SSergey Temerkhanov 	irqreturn_t ret = IRQ_NONE;
184441003396SSergey Temerkhanov 
184541003396SSergey Temerkhanov 	u64 mask_ue, mask_ce;
184641003396SSergey Temerkhanov 	const struct error_descr *l2_errors;
184741003396SSergey Temerkhanov 	char *reg_int_name;
184841003396SSergey Temerkhanov 
184941003396SSergey Temerkhanov 	char *msg;
185041003396SSergey Temerkhanov 	char *other;
185141003396SSergey Temerkhanov 
185241003396SSergey Temerkhanov 	msg = kmalloc(OCX_MESSAGE_SIZE, GFP_KERNEL);
185341003396SSergey Temerkhanov 	other = kmalloc(OCX_OTHER_SIZE, GFP_KERNEL);
185441003396SSergey Temerkhanov 
185541003396SSergey Temerkhanov 	if (!msg || !other)
185641003396SSergey Temerkhanov 		goto err_free;
185741003396SSergey Temerkhanov 
185841003396SSergey Temerkhanov 	switch (l2c->pdev->device) {
185941003396SSergey Temerkhanov 	case PCI_DEVICE_ID_THUNDER_L2C_TAD:
186041003396SSergey Temerkhanov 		reg_int_name = "L2C_TAD_INT";
186141003396SSergey Temerkhanov 		mask_ue = L2C_TAD_INT_UE;
186241003396SSergey Temerkhanov 		mask_ce = L2C_TAD_INT_CE;
186341003396SSergey Temerkhanov 		l2_errors = l2_tad_errors;
186441003396SSergey Temerkhanov 		break;
186541003396SSergey Temerkhanov 	case PCI_DEVICE_ID_THUNDER_L2C_CBC:
186641003396SSergey Temerkhanov 		reg_int_name = "L2C_CBC_INT";
186741003396SSergey Temerkhanov 		mask_ue = L2C_CBC_INT_UE;
186841003396SSergey Temerkhanov 		mask_ce = L2C_CBC_INT_CE;
186941003396SSergey Temerkhanov 		l2_errors = l2_cbc_errors;
187041003396SSergey Temerkhanov 		break;
187141003396SSergey Temerkhanov 	case PCI_DEVICE_ID_THUNDER_L2C_MCI:
187241003396SSergey Temerkhanov 		reg_int_name = "L2C_MCI_INT";
187341003396SSergey Temerkhanov 		mask_ue = L2C_MCI_INT_VBFDBE;
187441003396SSergey Temerkhanov 		mask_ce = L2C_MCI_INT_VBFSBE;
187541003396SSergey Temerkhanov 		l2_errors = l2_mci_errors;
187641003396SSergey Temerkhanov 		break;
187741003396SSergey Temerkhanov 	default:
187841003396SSergey Temerkhanov 		dev_err(&l2c->pdev->dev, "Unsupported device: %04x\n",
187941003396SSergey Temerkhanov 			l2c->pdev->device);
1880d8c27ba8SDan Carpenter 		goto err_free;
188141003396SSergey Temerkhanov 	}
188241003396SSergey Temerkhanov 
188341003396SSergey Temerkhanov 	while (CIRC_CNT(l2c->ring_head, l2c->ring_tail,
188441003396SSergey Temerkhanov 			ARRAY_SIZE(l2c->err_ctx))) {
188541003396SSergey Temerkhanov 		snprintf(msg, L2C_MESSAGE_SIZE,
188641003396SSergey Temerkhanov 			 "%s: %s: %016llx, %s: %016llx",
188741003396SSergey Temerkhanov 			 l2c->edac_dev->ctl_name, reg_int_name, ctx->reg_int,
188841003396SSergey Temerkhanov 			 ctx->reg_ext_name, ctx->reg_ext);
188941003396SSergey Temerkhanov 
189041003396SSergey Temerkhanov 		decode_register(other, L2C_OTHER_SIZE, l2_errors, ctx->reg_int);
189141003396SSergey Temerkhanov 
1892475c58e1SArnd Bergmann 		strlcat(msg, other, L2C_MESSAGE_SIZE);
189341003396SSergey Temerkhanov 
189441003396SSergey Temerkhanov 		if (ctx->reg_int & mask_ue)
189541003396SSergey Temerkhanov 			edac_device_handle_ue(l2c->edac_dev, 0, 0, msg);
189641003396SSergey Temerkhanov 		else if (ctx->reg_int & mask_ce)
189741003396SSergey Temerkhanov 			edac_device_handle_ce(l2c->edac_dev, 0, 0, msg);
189841003396SSergey Temerkhanov 
189941003396SSergey Temerkhanov 		l2c->ring_tail++;
190041003396SSergey Temerkhanov 	}
190141003396SSergey Temerkhanov 
1902d8c27ba8SDan Carpenter 	ret = IRQ_HANDLED;
190341003396SSergey Temerkhanov 
190441003396SSergey Temerkhanov err_free:
190541003396SSergey Temerkhanov 	kfree(other);
190641003396SSergey Temerkhanov 	kfree(msg);
190741003396SSergey Temerkhanov 
190841003396SSergey Temerkhanov 	return ret;
190941003396SSergey Temerkhanov }
191041003396SSergey Temerkhanov 
191141003396SSergey Temerkhanov #define L2C_DEBUGFS_ATTR(_name, _reg)	DEBUGFS_REG_ATTR(l2c, _name, _reg)
191241003396SSergey Temerkhanov 
191341003396SSergey Temerkhanov L2C_DEBUGFS_ATTR(tad_int, L2C_TAD_INT_W1S);
191441003396SSergey Temerkhanov 
191558d66175SZou Wei static struct debugfs_entry *l2c_tad_dfs_ents[] = {
191641003396SSergey Temerkhanov 	&debugfs_tad_int,
191741003396SSergey Temerkhanov };
191841003396SSergey Temerkhanov 
191941003396SSergey Temerkhanov L2C_DEBUGFS_ATTR(cbc_int, L2C_CBC_INT_W1S);
192041003396SSergey Temerkhanov 
192158d66175SZou Wei static struct debugfs_entry *l2c_cbc_dfs_ents[] = {
192241003396SSergey Temerkhanov 	&debugfs_cbc_int,
192341003396SSergey Temerkhanov };
192441003396SSergey Temerkhanov 
192541003396SSergey Temerkhanov L2C_DEBUGFS_ATTR(mci_int, L2C_MCI_INT_W1S);
192641003396SSergey Temerkhanov 
192758d66175SZou Wei static struct debugfs_entry *l2c_mci_dfs_ents[] = {
192841003396SSergey Temerkhanov 	&debugfs_mci_int,
192941003396SSergey Temerkhanov };
193041003396SSergey Temerkhanov 
193141003396SSergey Temerkhanov static const struct pci_device_id thunderx_l2c_pci_tbl[] = {
193241003396SSergey Temerkhanov 	{ PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, PCI_DEVICE_ID_THUNDER_L2C_TAD), },
193341003396SSergey Temerkhanov 	{ PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, PCI_DEVICE_ID_THUNDER_L2C_CBC), },
193441003396SSergey Temerkhanov 	{ PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, PCI_DEVICE_ID_THUNDER_L2C_MCI), },
193541003396SSergey Temerkhanov 	{ 0, },
193641003396SSergey Temerkhanov };
193741003396SSergey Temerkhanov 
thunderx_l2c_probe(struct pci_dev * pdev,const struct pci_device_id * id)193841003396SSergey Temerkhanov static int thunderx_l2c_probe(struct pci_dev *pdev,
193941003396SSergey Temerkhanov 			      const struct pci_device_id *id)
194041003396SSergey Temerkhanov {
194141003396SSergey Temerkhanov 	struct thunderx_l2c *l2c;
194241003396SSergey Temerkhanov 	struct edac_device_ctl_info *edac_dev;
194341003396SSergey Temerkhanov 	struct debugfs_entry **l2c_devattr;
194441003396SSergey Temerkhanov 	size_t dfs_entries;
194541003396SSergey Temerkhanov 	irqreturn_t (*thunderx_l2c_isr)(int, void *) = NULL;
194641003396SSergey Temerkhanov 	char name[32];
194741003396SSergey Temerkhanov 	const char *fmt;
194841003396SSergey Temerkhanov 	u64 reg_en_offs, reg_en_mask;
194941003396SSergey Temerkhanov 	int idx;
195041003396SSergey Temerkhanov 	int ret;
195141003396SSergey Temerkhanov 
195241003396SSergey Temerkhanov 	ret = pcim_enable_device(pdev);
195341003396SSergey Temerkhanov 	if (ret) {
195441003396SSergey Temerkhanov 		dev_err(&pdev->dev, "Cannot enable PCI device: %d\n", ret);
195541003396SSergey Temerkhanov 		return ret;
195641003396SSergey Temerkhanov 	}
195741003396SSergey Temerkhanov 
195841003396SSergey Temerkhanov 	ret = pcim_iomap_regions(pdev, BIT(0), "thunderx_l2c");
195941003396SSergey Temerkhanov 	if (ret) {
196041003396SSergey Temerkhanov 		dev_err(&pdev->dev, "Cannot map PCI resources: %d\n", ret);
196141003396SSergey Temerkhanov 		return ret;
196241003396SSergey Temerkhanov 	}
196341003396SSergey Temerkhanov 
196441003396SSergey Temerkhanov 	switch (pdev->device) {
196541003396SSergey Temerkhanov 	case PCI_DEVICE_ID_THUNDER_L2C_TAD:
196641003396SSergey Temerkhanov 		thunderx_l2c_isr = thunderx_l2c_tad_isr;
196741003396SSergey Temerkhanov 		l2c_devattr = l2c_tad_dfs_ents;
196841003396SSergey Temerkhanov 		dfs_entries = ARRAY_SIZE(l2c_tad_dfs_ents);
196941003396SSergey Temerkhanov 		fmt = "L2C-TAD%d";
197041003396SSergey Temerkhanov 		reg_en_offs = L2C_TAD_INT_ENA_W1S;
197141003396SSergey Temerkhanov 		reg_en_mask = L2C_TAD_INT_ENA_ALL;
197241003396SSergey Temerkhanov 		break;
197341003396SSergey Temerkhanov 	case PCI_DEVICE_ID_THUNDER_L2C_CBC:
197441003396SSergey Temerkhanov 		thunderx_l2c_isr = thunderx_l2c_cbc_isr;
197541003396SSergey Temerkhanov 		l2c_devattr = l2c_cbc_dfs_ents;
197641003396SSergey Temerkhanov 		dfs_entries = ARRAY_SIZE(l2c_cbc_dfs_ents);
197741003396SSergey Temerkhanov 		fmt = "L2C-CBC%d";
197841003396SSergey Temerkhanov 		reg_en_offs = L2C_CBC_INT_ENA_W1S;
197941003396SSergey Temerkhanov 		reg_en_mask = L2C_CBC_INT_ENA_ALL;
198041003396SSergey Temerkhanov 		break;
198141003396SSergey Temerkhanov 	case PCI_DEVICE_ID_THUNDER_L2C_MCI:
198241003396SSergey Temerkhanov 		thunderx_l2c_isr = thunderx_l2c_mci_isr;
198341003396SSergey Temerkhanov 		l2c_devattr = l2c_mci_dfs_ents;
198441003396SSergey Temerkhanov 		dfs_entries = ARRAY_SIZE(l2c_mci_dfs_ents);
198541003396SSergey Temerkhanov 		fmt = "L2C-MCI%d";
198641003396SSergey Temerkhanov 		reg_en_offs = L2C_MCI_INT_ENA_W1S;
198741003396SSergey Temerkhanov 		reg_en_mask = L2C_MCI_INT_ENA_ALL;
198841003396SSergey Temerkhanov 		break;
198941003396SSergey Temerkhanov 	default:
199041003396SSergey Temerkhanov 		//Should never ever get here
199141003396SSergey Temerkhanov 		dev_err(&pdev->dev, "Unsupported PCI device: %04x\n",
199241003396SSergey Temerkhanov 			pdev->device);
199341003396SSergey Temerkhanov 		return -EINVAL;
199441003396SSergey Temerkhanov 	}
199541003396SSergey Temerkhanov 
199641003396SSergey Temerkhanov 	idx = edac_device_alloc_index();
199741003396SSergey Temerkhanov 	snprintf(name, sizeof(name), fmt, idx);
199841003396SSergey Temerkhanov 
199941003396SSergey Temerkhanov 	edac_dev = edac_device_alloc_ctl_info(sizeof(struct thunderx_l2c),
2000*48bc8869SJiri Slaby (SUSE) 					      name, 1, "L2C", 1, 0, idx);
200141003396SSergey Temerkhanov 	if (!edac_dev) {
200241003396SSergey Temerkhanov 		dev_err(&pdev->dev, "Cannot allocate EDAC device\n");
200341003396SSergey Temerkhanov 		return -ENOMEM;
200441003396SSergey Temerkhanov 	}
200541003396SSergey Temerkhanov 
200641003396SSergey Temerkhanov 	l2c = edac_dev->pvt_info;
200741003396SSergey Temerkhanov 	l2c->edac_dev = edac_dev;
200841003396SSergey Temerkhanov 
200941003396SSergey Temerkhanov 	l2c->regs = pcim_iomap_table(pdev)[0];
201041003396SSergey Temerkhanov 	if (!l2c->regs) {
201141003396SSergey Temerkhanov 		dev_err(&pdev->dev, "Cannot map PCI resources\n");
201241003396SSergey Temerkhanov 		ret = -ENODEV;
201341003396SSergey Temerkhanov 		goto err_free;
201441003396SSergey Temerkhanov 	}
201541003396SSergey Temerkhanov 
201641003396SSergey Temerkhanov 	l2c->pdev = pdev;
201741003396SSergey Temerkhanov 
201841003396SSergey Temerkhanov 	l2c->ring_head = 0;
201941003396SSergey Temerkhanov 	l2c->ring_tail = 0;
202041003396SSergey Temerkhanov 
202141003396SSergey Temerkhanov 	l2c->msix_ent.entry = 0;
202241003396SSergey Temerkhanov 	l2c->msix_ent.vector = 0;
202341003396SSergey Temerkhanov 
202441003396SSergey Temerkhanov 	ret = pci_enable_msix_exact(pdev, &l2c->msix_ent, 1);
202541003396SSergey Temerkhanov 	if (ret) {
202641003396SSergey Temerkhanov 		dev_err(&pdev->dev, "Cannot enable interrupt: %d\n", ret);
202741003396SSergey Temerkhanov 		goto err_free;
202841003396SSergey Temerkhanov 	}
202941003396SSergey Temerkhanov 
203041003396SSergey Temerkhanov 	ret = devm_request_threaded_irq(&pdev->dev, l2c->msix_ent.vector,
203141003396SSergey Temerkhanov 					thunderx_l2c_isr,
203241003396SSergey Temerkhanov 					thunderx_l2c_threaded_isr,
203341003396SSergey Temerkhanov 					0, "[EDAC] ThunderX L2C",
203441003396SSergey Temerkhanov 					&l2c->msix_ent);
203541003396SSergey Temerkhanov 	if (ret)
203641003396SSergey Temerkhanov 		goto err_free;
203741003396SSergey Temerkhanov 
203841003396SSergey Temerkhanov 	edac_dev->dev = &pdev->dev;
203941003396SSergey Temerkhanov 	edac_dev->dev_name = dev_name(&pdev->dev);
204041003396SSergey Temerkhanov 	edac_dev->mod_name = "thunderx-l2c";
204141003396SSergey Temerkhanov 	edac_dev->ctl_name = "thunderx-l2c";
204241003396SSergey Temerkhanov 
204341003396SSergey Temerkhanov 	ret = edac_device_add_device(edac_dev);
204441003396SSergey Temerkhanov 	if (ret) {
204541003396SSergey Temerkhanov 		dev_err(&pdev->dev, "Cannot add EDAC device: %d\n", ret);
204641003396SSergey Temerkhanov 		goto err_free;
204741003396SSergey Temerkhanov 	}
204841003396SSergey Temerkhanov 
204941003396SSergey Temerkhanov 	if (IS_ENABLED(CONFIG_EDAC_DEBUG)) {
205041003396SSergey Temerkhanov 		l2c->debugfs = edac_debugfs_create_dir(pdev->dev.kobj.name);
205141003396SSergey Temerkhanov 
2052cf978258SVadim Lomovtsev 		ret = thunderx_create_debugfs_nodes(l2c->debugfs, l2c_devattr,
205341003396SSergey Temerkhanov 					      l2c, dfs_entries);
205441003396SSergey Temerkhanov 
205541003396SSergey Temerkhanov 		if (ret != dfs_entries) {
205641003396SSergey Temerkhanov 			dev_warn(&pdev->dev, "Error creating debugfs entries: %d%s\n",
205741003396SSergey Temerkhanov 				 ret, ret >= 0 ? " created" : "");
205841003396SSergey Temerkhanov 		}
205941003396SSergey Temerkhanov 	}
206041003396SSergey Temerkhanov 
206141003396SSergey Temerkhanov 	pci_set_drvdata(pdev, edac_dev);
206241003396SSergey Temerkhanov 
206341003396SSergey Temerkhanov 	writeq(reg_en_mask, l2c->regs + reg_en_offs);
206441003396SSergey Temerkhanov 
206541003396SSergey Temerkhanov 	return 0;
206641003396SSergey Temerkhanov 
206741003396SSergey Temerkhanov err_free:
206841003396SSergey Temerkhanov 	edac_device_free_ctl_info(edac_dev);
206941003396SSergey Temerkhanov 
207041003396SSergey Temerkhanov 	return ret;
207141003396SSergey Temerkhanov }
207241003396SSergey Temerkhanov 
thunderx_l2c_remove(struct pci_dev * pdev)207341003396SSergey Temerkhanov static void thunderx_l2c_remove(struct pci_dev *pdev)
207441003396SSergey Temerkhanov {
207541003396SSergey Temerkhanov 	struct edac_device_ctl_info *edac_dev = pci_get_drvdata(pdev);
207641003396SSergey Temerkhanov 	struct thunderx_l2c *l2c = edac_dev->pvt_info;
207741003396SSergey Temerkhanov 
207841003396SSergey Temerkhanov 	switch (pdev->device) {
207941003396SSergey Temerkhanov 	case PCI_DEVICE_ID_THUNDER_L2C_TAD:
208041003396SSergey Temerkhanov 		writeq(L2C_TAD_INT_ENA_ALL, l2c->regs + L2C_TAD_INT_ENA_W1C);
208141003396SSergey Temerkhanov 		break;
208241003396SSergey Temerkhanov 	case PCI_DEVICE_ID_THUNDER_L2C_CBC:
208341003396SSergey Temerkhanov 		writeq(L2C_CBC_INT_ENA_ALL, l2c->regs + L2C_CBC_INT_ENA_W1C);
208441003396SSergey Temerkhanov 		break;
208541003396SSergey Temerkhanov 	case PCI_DEVICE_ID_THUNDER_L2C_MCI:
2086621c4fe3SJan Glauber 		writeq(L2C_MCI_INT_ENA_ALL, l2c->regs + L2C_MCI_INT_ENA_W1C);
208741003396SSergey Temerkhanov 		break;
208841003396SSergey Temerkhanov 	}
208941003396SSergey Temerkhanov 
209041003396SSergey Temerkhanov 	edac_debugfs_remove_recursive(l2c->debugfs);
209141003396SSergey Temerkhanov 
209241003396SSergey Temerkhanov 	edac_device_del_device(&pdev->dev);
209341003396SSergey Temerkhanov 	edac_device_free_ctl_info(edac_dev);
209441003396SSergey Temerkhanov }
209541003396SSergey Temerkhanov 
209641003396SSergey Temerkhanov MODULE_DEVICE_TABLE(pci, thunderx_l2c_pci_tbl);
209741003396SSergey Temerkhanov 
209841003396SSergey Temerkhanov static struct pci_driver thunderx_l2c_driver = {
209941003396SSergey Temerkhanov 	.name     = "thunderx_l2c_edac",
210041003396SSergey Temerkhanov 	.probe    = thunderx_l2c_probe,
210141003396SSergey Temerkhanov 	.remove   = thunderx_l2c_remove,
210241003396SSergey Temerkhanov 	.id_table = thunderx_l2c_pci_tbl,
210341003396SSergey Temerkhanov };
210441003396SSergey Temerkhanov 
thunderx_edac_init(void)210541003396SSergey Temerkhanov static int __init thunderx_edac_init(void)
210641003396SSergey Temerkhanov {
210741003396SSergey Temerkhanov 	int rc = 0;
210841003396SSergey Temerkhanov 
2109315bada6SJia He 	if (ghes_get_devices())
2110315bada6SJia He 		return -EBUSY;
2111315bada6SJia He 
211241003396SSergey Temerkhanov 	rc = pci_register_driver(&thunderx_lmc_driver);
211341003396SSergey Temerkhanov 	if (rc)
211441003396SSergey Temerkhanov 		return rc;
211541003396SSergey Temerkhanov 
211641003396SSergey Temerkhanov 	rc = pci_register_driver(&thunderx_ocx_driver);
211741003396SSergey Temerkhanov 	if (rc)
211841003396SSergey Temerkhanov 		goto err_lmc;
211941003396SSergey Temerkhanov 
212041003396SSergey Temerkhanov 	rc = pci_register_driver(&thunderx_l2c_driver);
212141003396SSergey Temerkhanov 	if (rc)
212241003396SSergey Temerkhanov 		goto err_ocx;
212341003396SSergey Temerkhanov 
212441003396SSergey Temerkhanov 	return rc;
212541003396SSergey Temerkhanov err_ocx:
212641003396SSergey Temerkhanov 	pci_unregister_driver(&thunderx_ocx_driver);
212741003396SSergey Temerkhanov err_lmc:
212841003396SSergey Temerkhanov 	pci_unregister_driver(&thunderx_lmc_driver);
212941003396SSergey Temerkhanov 
213041003396SSergey Temerkhanov 	return rc;
213141003396SSergey Temerkhanov }
213241003396SSergey Temerkhanov 
thunderx_edac_exit(void)213341003396SSergey Temerkhanov static void __exit thunderx_edac_exit(void)
213441003396SSergey Temerkhanov {
213541003396SSergey Temerkhanov 	pci_unregister_driver(&thunderx_l2c_driver);
213641003396SSergey Temerkhanov 	pci_unregister_driver(&thunderx_ocx_driver);
213741003396SSergey Temerkhanov 	pci_unregister_driver(&thunderx_lmc_driver);
213841003396SSergey Temerkhanov 
213941003396SSergey Temerkhanov }
214041003396SSergey Temerkhanov 
214141003396SSergey Temerkhanov module_init(thunderx_edac_init);
214241003396SSergey Temerkhanov module_exit(thunderx_edac_exit);
214341003396SSergey Temerkhanov 
214441003396SSergey Temerkhanov MODULE_LICENSE("GPL v2");
214541003396SSergey Temerkhanov MODULE_AUTHOR("Cavium, Inc.");
214641003396SSergey Temerkhanov MODULE_DESCRIPTION("EDAC Driver for Cavium ThunderX");
2147