xref: /openbmc/linux/drivers/misc/smpro-errmon.c (revision 4a4a4e9ebaa3ce903a3cdf8bb173eeaf87828cea)
1*4a4a4e9eSQuan Nguyen // SPDX-License-Identifier: GPL-2.0-only
2*4a4a4e9eSQuan Nguyen /*
3*4a4a4e9eSQuan Nguyen  * Ampere Computing SoC's SMpro Error Monitoring Driver
4*4a4a4e9eSQuan Nguyen  *
5*4a4a4e9eSQuan Nguyen  * Copyright (c) 2022, Ampere Computing LLC
6*4a4a4e9eSQuan Nguyen  *
7*4a4a4e9eSQuan Nguyen  */
8*4a4a4e9eSQuan Nguyen 
9*4a4a4e9eSQuan Nguyen #include <linux/i2c.h>
10*4a4a4e9eSQuan Nguyen #include <linux/mod_devicetable.h>
11*4a4a4e9eSQuan Nguyen #include <linux/module.h>
12*4a4a4e9eSQuan Nguyen #include <linux/platform_device.h>
13*4a4a4e9eSQuan Nguyen #include <linux/regmap.h>
14*4a4a4e9eSQuan Nguyen 
15*4a4a4e9eSQuan Nguyen /* GPI RAS Error Registers */
16*4a4a4e9eSQuan Nguyen #define GPI_RAS_ERR		0x7E
17*4a4a4e9eSQuan Nguyen 
18*4a4a4e9eSQuan Nguyen /* Core and L2C Error Registers */
19*4a4a4e9eSQuan Nguyen #define CORE_CE_ERR_CNT		0x80
20*4a4a4e9eSQuan Nguyen #define CORE_CE_ERR_LEN		0x81
21*4a4a4e9eSQuan Nguyen #define CORE_CE_ERR_DATA	0x82
22*4a4a4e9eSQuan Nguyen #define CORE_UE_ERR_CNT		0x83
23*4a4a4e9eSQuan Nguyen #define CORE_UE_ERR_LEN		0x84
24*4a4a4e9eSQuan Nguyen #define CORE_UE_ERR_DATA	0x85
25*4a4a4e9eSQuan Nguyen 
26*4a4a4e9eSQuan Nguyen /* Memory Error Registers */
27*4a4a4e9eSQuan Nguyen #define MEM_CE_ERR_CNT		0x90
28*4a4a4e9eSQuan Nguyen #define MEM_CE_ERR_LEN		0x91
29*4a4a4e9eSQuan Nguyen #define MEM_CE_ERR_DATA		0x92
30*4a4a4e9eSQuan Nguyen #define MEM_UE_ERR_CNT		0x93
31*4a4a4e9eSQuan Nguyen #define MEM_UE_ERR_LEN		0x94
32*4a4a4e9eSQuan Nguyen #define MEM_UE_ERR_DATA		0x95
33*4a4a4e9eSQuan Nguyen 
34*4a4a4e9eSQuan Nguyen /* RAS Error/Warning Registers */
35*4a4a4e9eSQuan Nguyen #define ERR_SMPRO_TYPE		0xA0
36*4a4a4e9eSQuan Nguyen #define ERR_PMPRO_TYPE		0xA1
37*4a4a4e9eSQuan Nguyen #define ERR_SMPRO_INFO_LO	0xA2
38*4a4a4e9eSQuan Nguyen #define ERR_SMPRO_INFO_HI	0xA3
39*4a4a4e9eSQuan Nguyen #define ERR_SMPRO_DATA_LO	0xA4
40*4a4a4e9eSQuan Nguyen #define ERR_SMPRO_DATA_HI	0xA5
41*4a4a4e9eSQuan Nguyen #define WARN_SMPRO_INFO_LO	0xAA
42*4a4a4e9eSQuan Nguyen #define WARN_SMPRO_INFO_HI	0xAB
43*4a4a4e9eSQuan Nguyen #define ERR_PMPRO_INFO_LO	0xA6
44*4a4a4e9eSQuan Nguyen #define ERR_PMPRO_INFO_HI	0xA7
45*4a4a4e9eSQuan Nguyen #define ERR_PMPRO_DATA_LO	0xA8
46*4a4a4e9eSQuan Nguyen #define ERR_PMPRO_DATA_HI	0xA9
47*4a4a4e9eSQuan Nguyen #define WARN_PMPRO_INFO_LO	0xAC
48*4a4a4e9eSQuan Nguyen #define WARN_PMPRO_INFO_HI	0xAD
49*4a4a4e9eSQuan Nguyen 
50*4a4a4e9eSQuan Nguyen /* PCIE Error Registers */
51*4a4a4e9eSQuan Nguyen #define PCIE_CE_ERR_CNT		0xC0
52*4a4a4e9eSQuan Nguyen #define PCIE_CE_ERR_LEN		0xC1
53*4a4a4e9eSQuan Nguyen #define PCIE_CE_ERR_DATA	0xC2
54*4a4a4e9eSQuan Nguyen #define PCIE_UE_ERR_CNT		0xC3
55*4a4a4e9eSQuan Nguyen #define PCIE_UE_ERR_LEN		0xC4
56*4a4a4e9eSQuan Nguyen #define PCIE_UE_ERR_DATA	0xC5
57*4a4a4e9eSQuan Nguyen 
58*4a4a4e9eSQuan Nguyen /* Other Error Registers */
59*4a4a4e9eSQuan Nguyen #define OTHER_CE_ERR_CNT	0xD0
60*4a4a4e9eSQuan Nguyen #define OTHER_CE_ERR_LEN	0xD1
61*4a4a4e9eSQuan Nguyen #define OTHER_CE_ERR_DATA	0xD2
62*4a4a4e9eSQuan Nguyen #define OTHER_UE_ERR_CNT	0xD8
63*4a4a4e9eSQuan Nguyen #define OTHER_UE_ERR_LEN	0xD9
64*4a4a4e9eSQuan Nguyen #define OTHER_UE_ERR_DATA	0xDA
65*4a4a4e9eSQuan Nguyen 
66*4a4a4e9eSQuan Nguyen /* Event Data Registers */
67*4a4a4e9eSQuan Nguyen #define VRD_WARN_FAULT_EVENT_DATA	0x78
68*4a4a4e9eSQuan Nguyen #define VRD_HOT_EVENT_DATA		0x79
69*4a4a4e9eSQuan Nguyen #define DIMM_HOT_EVENT_DATA		0x7A
70*4a4a4e9eSQuan Nguyen 
71*4a4a4e9eSQuan Nguyen #define MAX_READ_BLOCK_LENGTH	48
72*4a4a4e9eSQuan Nguyen 
73*4a4a4e9eSQuan Nguyen #define RAS_SMPRO_ERR		0
74*4a4a4e9eSQuan Nguyen #define RAS_PMPRO_ERR		1
75*4a4a4e9eSQuan Nguyen 
76*4a4a4e9eSQuan Nguyen enum RAS_48BYTES_ERR_TYPES {
77*4a4a4e9eSQuan Nguyen 	CORE_CE_ERR,
78*4a4a4e9eSQuan Nguyen 	CORE_UE_ERR,
79*4a4a4e9eSQuan Nguyen 	MEM_CE_ERR,
80*4a4a4e9eSQuan Nguyen 	MEM_UE_ERR,
81*4a4a4e9eSQuan Nguyen 	PCIE_CE_ERR,
82*4a4a4e9eSQuan Nguyen 	PCIE_UE_ERR,
83*4a4a4e9eSQuan Nguyen 	OTHER_CE_ERR,
84*4a4a4e9eSQuan Nguyen 	OTHER_UE_ERR,
85*4a4a4e9eSQuan Nguyen 	NUM_48BYTES_ERR_TYPE,
86*4a4a4e9eSQuan Nguyen };
87*4a4a4e9eSQuan Nguyen 
88*4a4a4e9eSQuan Nguyen struct smpro_error_hdr {
89*4a4a4e9eSQuan Nguyen 	u8 count;	/* Number of the RAS errors */
90*4a4a4e9eSQuan Nguyen 	u8 len;		/* Number of data bytes */
91*4a4a4e9eSQuan Nguyen 	u8 data;	/* Start of 48-byte data */
92*4a4a4e9eSQuan Nguyen 	u8 max_cnt;	/* Max num of errors */
93*4a4a4e9eSQuan Nguyen };
94*4a4a4e9eSQuan Nguyen 
95*4a4a4e9eSQuan Nguyen /*
96*4a4a4e9eSQuan Nguyen  * Included Address of registers to get Count, Length of data and Data
97*4a4a4e9eSQuan Nguyen  * of the 48 bytes error data
98*4a4a4e9eSQuan Nguyen  */
99*4a4a4e9eSQuan Nguyen static struct smpro_error_hdr smpro_error_table[] = {
100*4a4a4e9eSQuan Nguyen 	[CORE_CE_ERR] = {
101*4a4a4e9eSQuan Nguyen 		.count = CORE_CE_ERR_CNT,
102*4a4a4e9eSQuan Nguyen 		.len = CORE_CE_ERR_LEN,
103*4a4a4e9eSQuan Nguyen 		.data = CORE_CE_ERR_DATA,
104*4a4a4e9eSQuan Nguyen 		.max_cnt = 32
105*4a4a4e9eSQuan Nguyen 	},
106*4a4a4e9eSQuan Nguyen 	[CORE_UE_ERR] = {
107*4a4a4e9eSQuan Nguyen 		.count = CORE_UE_ERR_CNT,
108*4a4a4e9eSQuan Nguyen 		.len = CORE_UE_ERR_LEN,
109*4a4a4e9eSQuan Nguyen 		.data = CORE_UE_ERR_DATA,
110*4a4a4e9eSQuan Nguyen 		.max_cnt = 32
111*4a4a4e9eSQuan Nguyen 	},
112*4a4a4e9eSQuan Nguyen 	[MEM_CE_ERR] = {
113*4a4a4e9eSQuan Nguyen 		.count = MEM_CE_ERR_CNT,
114*4a4a4e9eSQuan Nguyen 		.len = MEM_CE_ERR_LEN,
115*4a4a4e9eSQuan Nguyen 		.data = MEM_CE_ERR_DATA,
116*4a4a4e9eSQuan Nguyen 		.max_cnt = 16
117*4a4a4e9eSQuan Nguyen 	},
118*4a4a4e9eSQuan Nguyen 	[MEM_UE_ERR] = {
119*4a4a4e9eSQuan Nguyen 		.count = MEM_UE_ERR_CNT,
120*4a4a4e9eSQuan Nguyen 		.len = MEM_UE_ERR_LEN,
121*4a4a4e9eSQuan Nguyen 		.data = MEM_UE_ERR_DATA,
122*4a4a4e9eSQuan Nguyen 		.max_cnt = 16
123*4a4a4e9eSQuan Nguyen 	},
124*4a4a4e9eSQuan Nguyen 	[PCIE_CE_ERR] = {
125*4a4a4e9eSQuan Nguyen 		.count = PCIE_CE_ERR_CNT,
126*4a4a4e9eSQuan Nguyen 		.len = PCIE_CE_ERR_LEN,
127*4a4a4e9eSQuan Nguyen 		.data = PCIE_CE_ERR_DATA,
128*4a4a4e9eSQuan Nguyen 		.max_cnt = 96
129*4a4a4e9eSQuan Nguyen 	},
130*4a4a4e9eSQuan Nguyen 	[PCIE_UE_ERR] = {
131*4a4a4e9eSQuan Nguyen 		.count = PCIE_UE_ERR_CNT,
132*4a4a4e9eSQuan Nguyen 		.len = PCIE_UE_ERR_LEN,
133*4a4a4e9eSQuan Nguyen 		.data = PCIE_UE_ERR_DATA,
134*4a4a4e9eSQuan Nguyen 		.max_cnt = 96
135*4a4a4e9eSQuan Nguyen 	},
136*4a4a4e9eSQuan Nguyen 	[OTHER_CE_ERR] = {
137*4a4a4e9eSQuan Nguyen 		.count = OTHER_CE_ERR_CNT,
138*4a4a4e9eSQuan Nguyen 		.len = OTHER_CE_ERR_LEN,
139*4a4a4e9eSQuan Nguyen 		.data = OTHER_CE_ERR_DATA,
140*4a4a4e9eSQuan Nguyen 		.max_cnt = 8
141*4a4a4e9eSQuan Nguyen 	},
142*4a4a4e9eSQuan Nguyen 	[OTHER_UE_ERR] = {
143*4a4a4e9eSQuan Nguyen 		.count = OTHER_UE_ERR_CNT,
144*4a4a4e9eSQuan Nguyen 		.len = OTHER_UE_ERR_LEN,
145*4a4a4e9eSQuan Nguyen 		.data = OTHER_UE_ERR_DATA,
146*4a4a4e9eSQuan Nguyen 		.max_cnt = 8
147*4a4a4e9eSQuan Nguyen 	},
148*4a4a4e9eSQuan Nguyen };
149*4a4a4e9eSQuan Nguyen 
150*4a4a4e9eSQuan Nguyen /*
151*4a4a4e9eSQuan Nguyen  * List of SCP registers which are used to get
152*4a4a4e9eSQuan Nguyen  * one type of RAS Internal errors.
153*4a4a4e9eSQuan Nguyen  */
154*4a4a4e9eSQuan Nguyen struct smpro_int_error_hdr {
155*4a4a4e9eSQuan Nguyen 	u8 type;
156*4a4a4e9eSQuan Nguyen 	u8 info_l;
157*4a4a4e9eSQuan Nguyen 	u8 info_h;
158*4a4a4e9eSQuan Nguyen 	u8 data_l;
159*4a4a4e9eSQuan Nguyen 	u8 data_h;
160*4a4a4e9eSQuan Nguyen 	u8 warn_l;
161*4a4a4e9eSQuan Nguyen 	u8 warn_h;
162*4a4a4e9eSQuan Nguyen };
163*4a4a4e9eSQuan Nguyen 
164*4a4a4e9eSQuan Nguyen static struct smpro_int_error_hdr list_smpro_int_error_hdr[] = {
165*4a4a4e9eSQuan Nguyen 	[RAS_SMPRO_ERR] = {
166*4a4a4e9eSQuan Nguyen 		.type = ERR_SMPRO_TYPE,
167*4a4a4e9eSQuan Nguyen 		.info_l = ERR_SMPRO_INFO_LO,
168*4a4a4e9eSQuan Nguyen 		.info_h = ERR_SMPRO_INFO_HI,
169*4a4a4e9eSQuan Nguyen 		.data_l = ERR_SMPRO_DATA_LO,
170*4a4a4e9eSQuan Nguyen 		.data_h = ERR_SMPRO_DATA_HI,
171*4a4a4e9eSQuan Nguyen 		.warn_l = WARN_SMPRO_INFO_LO,
172*4a4a4e9eSQuan Nguyen 		.warn_h = WARN_SMPRO_INFO_HI,
173*4a4a4e9eSQuan Nguyen 	},
174*4a4a4e9eSQuan Nguyen 	[RAS_PMPRO_ERR] = {
175*4a4a4e9eSQuan Nguyen 		.type = ERR_PMPRO_TYPE,
176*4a4a4e9eSQuan Nguyen 		.info_l = ERR_PMPRO_INFO_LO,
177*4a4a4e9eSQuan Nguyen 		.info_h = ERR_PMPRO_INFO_HI,
178*4a4a4e9eSQuan Nguyen 		.data_l = ERR_PMPRO_DATA_LO,
179*4a4a4e9eSQuan Nguyen 		.data_h = ERR_PMPRO_DATA_HI,
180*4a4a4e9eSQuan Nguyen 		.warn_l = WARN_PMPRO_INFO_LO,
181*4a4a4e9eSQuan Nguyen 		.warn_h = WARN_PMPRO_INFO_HI,
182*4a4a4e9eSQuan Nguyen 	},
183*4a4a4e9eSQuan Nguyen };
184*4a4a4e9eSQuan Nguyen 
185*4a4a4e9eSQuan Nguyen struct smpro_errmon {
186*4a4a4e9eSQuan Nguyen 	struct regmap *regmap;
187*4a4a4e9eSQuan Nguyen };
188*4a4a4e9eSQuan Nguyen 
189*4a4a4e9eSQuan Nguyen enum EVENT_TYPES {
190*4a4a4e9eSQuan Nguyen 	VRD_WARN_FAULT_EVENT,
191*4a4a4e9eSQuan Nguyen 	VRD_HOT_EVENT,
192*4a4a4e9eSQuan Nguyen 	DIMM_HOT_EVENT,
193*4a4a4e9eSQuan Nguyen 	NUM_EVENTS_TYPE,
194*4a4a4e9eSQuan Nguyen };
195*4a4a4e9eSQuan Nguyen 
196*4a4a4e9eSQuan Nguyen /* Included Address of event source and data registers */
197*4a4a4e9eSQuan Nguyen static u8 smpro_event_table[NUM_EVENTS_TYPE] = {
198*4a4a4e9eSQuan Nguyen 	VRD_WARN_FAULT_EVENT_DATA,
199*4a4a4e9eSQuan Nguyen 	VRD_HOT_EVENT_DATA,
200*4a4a4e9eSQuan Nguyen 	DIMM_HOT_EVENT_DATA,
201*4a4a4e9eSQuan Nguyen };
202*4a4a4e9eSQuan Nguyen 
203*4a4a4e9eSQuan Nguyen static ssize_t smpro_event_data_read(struct device *dev,
204*4a4a4e9eSQuan Nguyen 				     struct device_attribute *da, char *buf,
205*4a4a4e9eSQuan Nguyen 				     int channel)
206*4a4a4e9eSQuan Nguyen {
207*4a4a4e9eSQuan Nguyen 	struct smpro_errmon *errmon = dev_get_drvdata(dev);
208*4a4a4e9eSQuan Nguyen 	s32 event_data;
209*4a4a4e9eSQuan Nguyen 	int ret;
210*4a4a4e9eSQuan Nguyen 
211*4a4a4e9eSQuan Nguyen 	ret = regmap_read(errmon->regmap, smpro_event_table[channel], &event_data);
212*4a4a4e9eSQuan Nguyen 	if (ret)
213*4a4a4e9eSQuan Nguyen 		return ret;
214*4a4a4e9eSQuan Nguyen 	/* Clear event after read */
215*4a4a4e9eSQuan Nguyen 	if (event_data != 0)
216*4a4a4e9eSQuan Nguyen 		regmap_write(errmon->regmap, smpro_event_table[channel], event_data);
217*4a4a4e9eSQuan Nguyen 
218*4a4a4e9eSQuan Nguyen 	return sysfs_emit(buf, "%04x\n", event_data);
219*4a4a4e9eSQuan Nguyen }
220*4a4a4e9eSQuan Nguyen 
221*4a4a4e9eSQuan Nguyen static ssize_t smpro_overflow_data_read(struct device *dev, struct device_attribute *da,
222*4a4a4e9eSQuan Nguyen 					char *buf, int channel)
223*4a4a4e9eSQuan Nguyen {
224*4a4a4e9eSQuan Nguyen 	struct smpro_errmon *errmon = dev_get_drvdata(dev);
225*4a4a4e9eSQuan Nguyen 	struct smpro_error_hdr *err_info;
226*4a4a4e9eSQuan Nguyen 	s32 err_count;
227*4a4a4e9eSQuan Nguyen 	int ret;
228*4a4a4e9eSQuan Nguyen 
229*4a4a4e9eSQuan Nguyen 	err_info = &smpro_error_table[channel];
230*4a4a4e9eSQuan Nguyen 
231*4a4a4e9eSQuan Nguyen 	ret = regmap_read(errmon->regmap, err_info->count, &err_count);
232*4a4a4e9eSQuan Nguyen 	if (ret)
233*4a4a4e9eSQuan Nguyen 		return ret;
234*4a4a4e9eSQuan Nguyen 
235*4a4a4e9eSQuan Nguyen 	/* Bit 8 indicates the overflow status */
236*4a4a4e9eSQuan Nguyen 	return sysfs_emit(buf, "%d\n", (err_count & BIT(8)) ? 1 : 0);
237*4a4a4e9eSQuan Nguyen }
238*4a4a4e9eSQuan Nguyen 
239*4a4a4e9eSQuan Nguyen static ssize_t smpro_error_data_read(struct device *dev, struct device_attribute *da,
240*4a4a4e9eSQuan Nguyen 				     char *buf, int channel)
241*4a4a4e9eSQuan Nguyen {
242*4a4a4e9eSQuan Nguyen 	struct smpro_errmon *errmon = dev_get_drvdata(dev);
243*4a4a4e9eSQuan Nguyen 	unsigned char err_data[MAX_READ_BLOCK_LENGTH];
244*4a4a4e9eSQuan Nguyen 	struct smpro_error_hdr *err_info;
245*4a4a4e9eSQuan Nguyen 	s32 err_count, err_length;
246*4a4a4e9eSQuan Nguyen 	int ret;
247*4a4a4e9eSQuan Nguyen 
248*4a4a4e9eSQuan Nguyen 	err_info = &smpro_error_table[channel];
249*4a4a4e9eSQuan Nguyen 
250*4a4a4e9eSQuan Nguyen 	ret = regmap_read(errmon->regmap, err_info->count, &err_count);
251*4a4a4e9eSQuan Nguyen 	/* Error count is the low byte */
252*4a4a4e9eSQuan Nguyen 	err_count &= 0xff;
253*4a4a4e9eSQuan Nguyen 	if (ret || !err_count || err_count > err_info->max_cnt)
254*4a4a4e9eSQuan Nguyen 		return ret;
255*4a4a4e9eSQuan Nguyen 
256*4a4a4e9eSQuan Nguyen 	ret = regmap_read(errmon->regmap, err_info->len, &err_length);
257*4a4a4e9eSQuan Nguyen 	if (ret || err_length <= 0)
258*4a4a4e9eSQuan Nguyen 		return ret;
259*4a4a4e9eSQuan Nguyen 
260*4a4a4e9eSQuan Nguyen 	if (err_length > MAX_READ_BLOCK_LENGTH)
261*4a4a4e9eSQuan Nguyen 		err_length = MAX_READ_BLOCK_LENGTH;
262*4a4a4e9eSQuan Nguyen 
263*4a4a4e9eSQuan Nguyen 	memset(err_data, 0x00, MAX_READ_BLOCK_LENGTH);
264*4a4a4e9eSQuan Nguyen 	ret = regmap_noinc_read(errmon->regmap, err_info->data, err_data, err_length);
265*4a4a4e9eSQuan Nguyen 	if (ret < 0)
266*4a4a4e9eSQuan Nguyen 		return ret;
267*4a4a4e9eSQuan Nguyen 
268*4a4a4e9eSQuan Nguyen 	/* clear the error */
269*4a4a4e9eSQuan Nguyen 	ret = regmap_write(errmon->regmap, err_info->count, 0x100);
270*4a4a4e9eSQuan Nguyen 	if (ret)
271*4a4a4e9eSQuan Nguyen 		return ret;
272*4a4a4e9eSQuan Nguyen 	/*
273*4a4a4e9eSQuan Nguyen 	 * The output of Core/Memory/PCIe/Others UE/CE errors follows the format
274*4a4a4e9eSQuan Nguyen 	 * specified in section 5.8.1 CE/UE Error Data record in
275*4a4a4e9eSQuan Nguyen 	 * Altra SOC BMC Interface specification.
276*4a4a4e9eSQuan Nguyen 	 */
277*4a4a4e9eSQuan Nguyen 	return sysfs_emit(buf, "%*phN\n", MAX_READ_BLOCK_LENGTH, err_data);
278*4a4a4e9eSQuan Nguyen }
279*4a4a4e9eSQuan Nguyen 
280*4a4a4e9eSQuan Nguyen /*
281*4a4a4e9eSQuan Nguyen  * Output format:
282*4a4a4e9eSQuan Nguyen  * <4-byte hex value of error info><4-byte hex value of error extensive data>
283*4a4a4e9eSQuan Nguyen  * Where:
284*4a4a4e9eSQuan Nguyen  *   + error info : The error information
285*4a4a4e9eSQuan Nguyen  *   + error data : Extensive data (32 bits)
286*4a4a4e9eSQuan Nguyen  * Reference to section 5.10 RAS Internal Error Register Definition in
287*4a4a4e9eSQuan Nguyen  * Altra SOC BMC Interface specification
288*4a4a4e9eSQuan Nguyen  */
289*4a4a4e9eSQuan Nguyen static ssize_t smpro_internal_err_read(struct device *dev, struct device_attribute *da,
290*4a4a4e9eSQuan Nguyen 				       char *buf, int channel)
291*4a4a4e9eSQuan Nguyen {
292*4a4a4e9eSQuan Nguyen 	struct smpro_errmon *errmon = dev_get_drvdata(dev);
293*4a4a4e9eSQuan Nguyen 	struct smpro_int_error_hdr *err_info;
294*4a4a4e9eSQuan Nguyen 	unsigned int err[4] = { 0 };
295*4a4a4e9eSQuan Nguyen 	unsigned int err_type;
296*4a4a4e9eSQuan Nguyen 	unsigned int val;
297*4a4a4e9eSQuan Nguyen 	int ret;
298*4a4a4e9eSQuan Nguyen 
299*4a4a4e9eSQuan Nguyen 	/* read error status */
300*4a4a4e9eSQuan Nguyen 	ret = regmap_read(errmon->regmap, GPI_RAS_ERR, &val);
301*4a4a4e9eSQuan Nguyen 	if (ret)
302*4a4a4e9eSQuan Nguyen 		return ret;
303*4a4a4e9eSQuan Nguyen 
304*4a4a4e9eSQuan Nguyen 	if ((channel == RAS_SMPRO_ERR && !(val & BIT(0))) ||
305*4a4a4e9eSQuan Nguyen 	    (channel == RAS_PMPRO_ERR && !(val & BIT(1))))
306*4a4a4e9eSQuan Nguyen 		return 0;
307*4a4a4e9eSQuan Nguyen 
308*4a4a4e9eSQuan Nguyen 	err_info = &list_smpro_int_error_hdr[channel];
309*4a4a4e9eSQuan Nguyen 	ret = regmap_read(errmon->regmap, err_info->type, &val);
310*4a4a4e9eSQuan Nguyen 	if (ret)
311*4a4a4e9eSQuan Nguyen 		return ret;
312*4a4a4e9eSQuan Nguyen 
313*4a4a4e9eSQuan Nguyen 	err_type = (val & BIT(1)) ? BIT(1) :
314*4a4a4e9eSQuan Nguyen 		   (val & BIT(2)) ? BIT(2) : 0;
315*4a4a4e9eSQuan Nguyen 
316*4a4a4e9eSQuan Nguyen 	if (!err_type)
317*4a4a4e9eSQuan Nguyen 		return 0;
318*4a4a4e9eSQuan Nguyen 
319*4a4a4e9eSQuan Nguyen 	ret = regmap_read(errmon->regmap, err_info->info_l, err + 1);
320*4a4a4e9eSQuan Nguyen 	if (ret)
321*4a4a4e9eSQuan Nguyen 		return ret;
322*4a4a4e9eSQuan Nguyen 
323*4a4a4e9eSQuan Nguyen 	ret = regmap_read(errmon->regmap, err_info->info_h, err);
324*4a4a4e9eSQuan Nguyen 	if (ret)
325*4a4a4e9eSQuan Nguyen 		return ret;
326*4a4a4e9eSQuan Nguyen 
327*4a4a4e9eSQuan Nguyen 	if (err_type & BIT(2)) {
328*4a4a4e9eSQuan Nguyen 		/* Error with data type */
329*4a4a4e9eSQuan Nguyen 		ret = regmap_read(errmon->regmap, err_info->data_l, err + 3);
330*4a4a4e9eSQuan Nguyen 		if (ret)
331*4a4a4e9eSQuan Nguyen 			return ret;
332*4a4a4e9eSQuan Nguyen 
333*4a4a4e9eSQuan Nguyen 		ret = regmap_read(errmon->regmap, err_info->data_h, err + 2);
334*4a4a4e9eSQuan Nguyen 		if (ret)
335*4a4a4e9eSQuan Nguyen 			return ret;
336*4a4a4e9eSQuan Nguyen 	}
337*4a4a4e9eSQuan Nguyen 
338*4a4a4e9eSQuan Nguyen 	/* clear the read errors */
339*4a4a4e9eSQuan Nguyen 	ret = regmap_write(errmon->regmap, err_info->type, err_type);
340*4a4a4e9eSQuan Nguyen 	if (ret)
341*4a4a4e9eSQuan Nguyen 		return ret;
342*4a4a4e9eSQuan Nguyen 
343*4a4a4e9eSQuan Nguyen 	return sysfs_emit(buf, "%*phN\n", (int)sizeof(err), err);
344*4a4a4e9eSQuan Nguyen }
345*4a4a4e9eSQuan Nguyen 
346*4a4a4e9eSQuan Nguyen /*
347*4a4a4e9eSQuan Nguyen  * Output format:
348*4a4a4e9eSQuan Nguyen  * <4-byte hex value of warining info>
349*4a4a4e9eSQuan Nguyen  * Reference to section 5.10 RAS Internal Error Register Definition in
350*4a4a4e9eSQuan Nguyen  * Altra SOC BMC Interface specification
351*4a4a4e9eSQuan Nguyen  */
352*4a4a4e9eSQuan Nguyen static ssize_t smpro_internal_warn_read(struct device *dev, struct device_attribute *da,
353*4a4a4e9eSQuan Nguyen 					char *buf, int channel)
354*4a4a4e9eSQuan Nguyen {
355*4a4a4e9eSQuan Nguyen 	struct smpro_errmon *errmon = dev_get_drvdata(dev);
356*4a4a4e9eSQuan Nguyen 	struct smpro_int_error_hdr *err_info;
357*4a4a4e9eSQuan Nguyen 	unsigned int warn[2] = { 0 };
358*4a4a4e9eSQuan Nguyen 	unsigned int val;
359*4a4a4e9eSQuan Nguyen 	int ret;
360*4a4a4e9eSQuan Nguyen 
361*4a4a4e9eSQuan Nguyen 	/* read error status */
362*4a4a4e9eSQuan Nguyen 	ret = regmap_read(errmon->regmap, GPI_RAS_ERR, &val);
363*4a4a4e9eSQuan Nguyen 	if (ret)
364*4a4a4e9eSQuan Nguyen 		return ret;
365*4a4a4e9eSQuan Nguyen 
366*4a4a4e9eSQuan Nguyen 	if ((channel == RAS_SMPRO_ERR && !(val & BIT(0))) ||
367*4a4a4e9eSQuan Nguyen 	    (channel == RAS_PMPRO_ERR && !(val & BIT(1))))
368*4a4a4e9eSQuan Nguyen 		return 0;
369*4a4a4e9eSQuan Nguyen 
370*4a4a4e9eSQuan Nguyen 	err_info = &list_smpro_int_error_hdr[channel];
371*4a4a4e9eSQuan Nguyen 	ret = regmap_read(errmon->regmap, err_info->type, &val);
372*4a4a4e9eSQuan Nguyen 	if (ret)
373*4a4a4e9eSQuan Nguyen 		return ret;
374*4a4a4e9eSQuan Nguyen 
375*4a4a4e9eSQuan Nguyen 	if (!(val & BIT(0)))
376*4a4a4e9eSQuan Nguyen 		return 0;
377*4a4a4e9eSQuan Nguyen 
378*4a4a4e9eSQuan Nguyen 	ret = regmap_read(errmon->regmap, err_info->warn_l, warn + 1);
379*4a4a4e9eSQuan Nguyen 	if (ret)
380*4a4a4e9eSQuan Nguyen 		return ret;
381*4a4a4e9eSQuan Nguyen 
382*4a4a4e9eSQuan Nguyen 	ret = regmap_read(errmon->regmap, err_info->warn_h, warn);
383*4a4a4e9eSQuan Nguyen 	if (ret)
384*4a4a4e9eSQuan Nguyen 		return ret;
385*4a4a4e9eSQuan Nguyen 
386*4a4a4e9eSQuan Nguyen 	/* clear the warning */
387*4a4a4e9eSQuan Nguyen 	ret = regmap_write(errmon->regmap, err_info->type, BIT(0));
388*4a4a4e9eSQuan Nguyen 	if (ret)
389*4a4a4e9eSQuan Nguyen 		return ret;
390*4a4a4e9eSQuan Nguyen 
391*4a4a4e9eSQuan Nguyen 	return sysfs_emit(buf, "%*phN\n", (int)sizeof(warn), warn);
392*4a4a4e9eSQuan Nguyen }
393*4a4a4e9eSQuan Nguyen 
394*4a4a4e9eSQuan Nguyen #define ERROR_OVERFLOW_RO(_error, _index) \
395*4a4a4e9eSQuan Nguyen 	static ssize_t overflow_##_error##_show(struct device *dev,            \
396*4a4a4e9eSQuan Nguyen 						struct device_attribute *da,   \
397*4a4a4e9eSQuan Nguyen 						char *buf)                     \
398*4a4a4e9eSQuan Nguyen 	{                                                                      \
399*4a4a4e9eSQuan Nguyen 		return smpro_overflow_data_read(dev, da, buf, _index);         \
400*4a4a4e9eSQuan Nguyen 	}                                                                      \
401*4a4a4e9eSQuan Nguyen 	static DEVICE_ATTR_RO(overflow_##_error)
402*4a4a4e9eSQuan Nguyen 
403*4a4a4e9eSQuan Nguyen ERROR_OVERFLOW_RO(core_ce, CORE_CE_ERR);
404*4a4a4e9eSQuan Nguyen ERROR_OVERFLOW_RO(core_ue, CORE_UE_ERR);
405*4a4a4e9eSQuan Nguyen ERROR_OVERFLOW_RO(mem_ce, MEM_CE_ERR);
406*4a4a4e9eSQuan Nguyen ERROR_OVERFLOW_RO(mem_ue, MEM_UE_ERR);
407*4a4a4e9eSQuan Nguyen ERROR_OVERFLOW_RO(pcie_ce, PCIE_CE_ERR);
408*4a4a4e9eSQuan Nguyen ERROR_OVERFLOW_RO(pcie_ue, PCIE_UE_ERR);
409*4a4a4e9eSQuan Nguyen ERROR_OVERFLOW_RO(other_ce, OTHER_CE_ERR);
410*4a4a4e9eSQuan Nguyen ERROR_OVERFLOW_RO(other_ue, OTHER_UE_ERR);
411*4a4a4e9eSQuan Nguyen 
412*4a4a4e9eSQuan Nguyen #define ERROR_RO(_error, _index) \
413*4a4a4e9eSQuan Nguyen 	static ssize_t error_##_error##_show(struct device *dev,            \
414*4a4a4e9eSQuan Nguyen 					     struct device_attribute *da,   \
415*4a4a4e9eSQuan Nguyen 					     char *buf)                     \
416*4a4a4e9eSQuan Nguyen 	{                                                                   \
417*4a4a4e9eSQuan Nguyen 		return smpro_error_data_read(dev, da, buf, _index);         \
418*4a4a4e9eSQuan Nguyen 	}                                                                   \
419*4a4a4e9eSQuan Nguyen 	static DEVICE_ATTR_RO(error_##_error)
420*4a4a4e9eSQuan Nguyen 
421*4a4a4e9eSQuan Nguyen ERROR_RO(core_ce, CORE_CE_ERR);
422*4a4a4e9eSQuan Nguyen ERROR_RO(core_ue, CORE_UE_ERR);
423*4a4a4e9eSQuan Nguyen ERROR_RO(mem_ce, MEM_CE_ERR);
424*4a4a4e9eSQuan Nguyen ERROR_RO(mem_ue, MEM_UE_ERR);
425*4a4a4e9eSQuan Nguyen ERROR_RO(pcie_ce, PCIE_CE_ERR);
426*4a4a4e9eSQuan Nguyen ERROR_RO(pcie_ue, PCIE_UE_ERR);
427*4a4a4e9eSQuan Nguyen ERROR_RO(other_ce, OTHER_CE_ERR);
428*4a4a4e9eSQuan Nguyen ERROR_RO(other_ue, OTHER_UE_ERR);
429*4a4a4e9eSQuan Nguyen 
430*4a4a4e9eSQuan Nguyen static ssize_t error_smpro_show(struct device *dev, struct device_attribute *da, char *buf)
431*4a4a4e9eSQuan Nguyen {
432*4a4a4e9eSQuan Nguyen 	return smpro_internal_err_read(dev, da, buf, RAS_SMPRO_ERR);
433*4a4a4e9eSQuan Nguyen }
434*4a4a4e9eSQuan Nguyen static DEVICE_ATTR_RO(error_smpro);
435*4a4a4e9eSQuan Nguyen 
436*4a4a4e9eSQuan Nguyen static ssize_t error_pmpro_show(struct device *dev, struct device_attribute *da, char *buf)
437*4a4a4e9eSQuan Nguyen {
438*4a4a4e9eSQuan Nguyen 	return smpro_internal_err_read(dev, da, buf, RAS_PMPRO_ERR);
439*4a4a4e9eSQuan Nguyen }
440*4a4a4e9eSQuan Nguyen static DEVICE_ATTR_RO(error_pmpro);
441*4a4a4e9eSQuan Nguyen 
442*4a4a4e9eSQuan Nguyen static ssize_t warn_smpro_show(struct device *dev, struct device_attribute *da, char *buf)
443*4a4a4e9eSQuan Nguyen {
444*4a4a4e9eSQuan Nguyen 	return smpro_internal_warn_read(dev, da, buf, RAS_SMPRO_ERR);
445*4a4a4e9eSQuan Nguyen }
446*4a4a4e9eSQuan Nguyen static DEVICE_ATTR_RO(warn_smpro);
447*4a4a4e9eSQuan Nguyen 
448*4a4a4e9eSQuan Nguyen static ssize_t warn_pmpro_show(struct device *dev, struct device_attribute *da, char *buf)
449*4a4a4e9eSQuan Nguyen {
450*4a4a4e9eSQuan Nguyen 	return smpro_internal_warn_read(dev, da, buf, RAS_PMPRO_ERR);
451*4a4a4e9eSQuan Nguyen }
452*4a4a4e9eSQuan Nguyen static DEVICE_ATTR_RO(warn_pmpro);
453*4a4a4e9eSQuan Nguyen 
454*4a4a4e9eSQuan Nguyen #define EVENT_RO(_event, _index) \
455*4a4a4e9eSQuan Nguyen 	static ssize_t event_##_event##_show(struct device *dev,            \
456*4a4a4e9eSQuan Nguyen 					     struct device_attribute *da,   \
457*4a4a4e9eSQuan Nguyen 					     char *buf)                     \
458*4a4a4e9eSQuan Nguyen 	{                                                                   \
459*4a4a4e9eSQuan Nguyen 		return smpro_event_data_read(dev, da, buf, _index);         \
460*4a4a4e9eSQuan Nguyen 	}                                                                   \
461*4a4a4e9eSQuan Nguyen 	static DEVICE_ATTR_RO(event_##_event)
462*4a4a4e9eSQuan Nguyen 
463*4a4a4e9eSQuan Nguyen EVENT_RO(vrd_warn_fault, VRD_WARN_FAULT_EVENT);
464*4a4a4e9eSQuan Nguyen EVENT_RO(vrd_hot, VRD_HOT_EVENT);
465*4a4a4e9eSQuan Nguyen EVENT_RO(dimm_hot, DIMM_HOT_EVENT);
466*4a4a4e9eSQuan Nguyen 
467*4a4a4e9eSQuan Nguyen static struct attribute *smpro_errmon_attrs[] = {
468*4a4a4e9eSQuan Nguyen 	&dev_attr_overflow_core_ce.attr,
469*4a4a4e9eSQuan Nguyen 	&dev_attr_overflow_core_ue.attr,
470*4a4a4e9eSQuan Nguyen 	&dev_attr_overflow_mem_ce.attr,
471*4a4a4e9eSQuan Nguyen 	&dev_attr_overflow_mem_ue.attr,
472*4a4a4e9eSQuan Nguyen 	&dev_attr_overflow_pcie_ce.attr,
473*4a4a4e9eSQuan Nguyen 	&dev_attr_overflow_pcie_ue.attr,
474*4a4a4e9eSQuan Nguyen 	&dev_attr_overflow_other_ce.attr,
475*4a4a4e9eSQuan Nguyen 	&dev_attr_overflow_other_ue.attr,
476*4a4a4e9eSQuan Nguyen 	&dev_attr_error_core_ce.attr,
477*4a4a4e9eSQuan Nguyen 	&dev_attr_error_core_ue.attr,
478*4a4a4e9eSQuan Nguyen 	&dev_attr_error_mem_ce.attr,
479*4a4a4e9eSQuan Nguyen 	&dev_attr_error_mem_ue.attr,
480*4a4a4e9eSQuan Nguyen 	&dev_attr_error_pcie_ce.attr,
481*4a4a4e9eSQuan Nguyen 	&dev_attr_error_pcie_ue.attr,
482*4a4a4e9eSQuan Nguyen 	&dev_attr_error_other_ce.attr,
483*4a4a4e9eSQuan Nguyen 	&dev_attr_error_other_ue.attr,
484*4a4a4e9eSQuan Nguyen 	&dev_attr_error_smpro.attr,
485*4a4a4e9eSQuan Nguyen 	&dev_attr_error_pmpro.attr,
486*4a4a4e9eSQuan Nguyen 	&dev_attr_warn_smpro.attr,
487*4a4a4e9eSQuan Nguyen 	&dev_attr_warn_pmpro.attr,
488*4a4a4e9eSQuan Nguyen 	&dev_attr_event_vrd_warn_fault.attr,
489*4a4a4e9eSQuan Nguyen 	&dev_attr_event_vrd_hot.attr,
490*4a4a4e9eSQuan Nguyen 	&dev_attr_event_dimm_hot.attr,
491*4a4a4e9eSQuan Nguyen 	NULL
492*4a4a4e9eSQuan Nguyen };
493*4a4a4e9eSQuan Nguyen 
494*4a4a4e9eSQuan Nguyen ATTRIBUTE_GROUPS(smpro_errmon);
495*4a4a4e9eSQuan Nguyen 
496*4a4a4e9eSQuan Nguyen static int smpro_errmon_probe(struct platform_device *pdev)
497*4a4a4e9eSQuan Nguyen {
498*4a4a4e9eSQuan Nguyen 	struct smpro_errmon *errmon;
499*4a4a4e9eSQuan Nguyen 
500*4a4a4e9eSQuan Nguyen 	errmon = devm_kzalloc(&pdev->dev, sizeof(struct smpro_errmon), GFP_KERNEL);
501*4a4a4e9eSQuan Nguyen 	if (!errmon)
502*4a4a4e9eSQuan Nguyen 		return -ENOMEM;
503*4a4a4e9eSQuan Nguyen 
504*4a4a4e9eSQuan Nguyen 	platform_set_drvdata(pdev, errmon);
505*4a4a4e9eSQuan Nguyen 
506*4a4a4e9eSQuan Nguyen 	errmon->regmap = dev_get_regmap(pdev->dev.parent, NULL);
507*4a4a4e9eSQuan Nguyen 	if (!errmon->regmap)
508*4a4a4e9eSQuan Nguyen 		return -ENODEV;
509*4a4a4e9eSQuan Nguyen 
510*4a4a4e9eSQuan Nguyen 	return 0;
511*4a4a4e9eSQuan Nguyen }
512*4a4a4e9eSQuan Nguyen 
513*4a4a4e9eSQuan Nguyen static struct platform_driver smpro_errmon_driver = {
514*4a4a4e9eSQuan Nguyen 	.probe          = smpro_errmon_probe,
515*4a4a4e9eSQuan Nguyen 	.driver = {
516*4a4a4e9eSQuan Nguyen 		.name   = "smpro-errmon",
517*4a4a4e9eSQuan Nguyen 		.dev_groups = smpro_errmon_groups,
518*4a4a4e9eSQuan Nguyen 	},
519*4a4a4e9eSQuan Nguyen };
520*4a4a4e9eSQuan Nguyen 
521*4a4a4e9eSQuan Nguyen module_platform_driver(smpro_errmon_driver);
522*4a4a4e9eSQuan Nguyen 
523*4a4a4e9eSQuan Nguyen MODULE_AUTHOR("Tung Nguyen <tung.nguyen@amperecomputing.com>");
524*4a4a4e9eSQuan Nguyen MODULE_AUTHOR("Thinh Pham <thinh.pham@amperecomputing.com>");
525*4a4a4e9eSQuan Nguyen MODULE_AUTHOR("Hoang Nguyen <hnguyen@amperecomputing.com>");
526*4a4a4e9eSQuan Nguyen MODULE_AUTHOR("Thu Nguyen <thu@os.amperecomputing.com>");
527*4a4a4e9eSQuan Nguyen MODULE_AUTHOR("Quan Nguyen <quan@os.amperecomputing.com>");
528*4a4a4e9eSQuan Nguyen MODULE_DESCRIPTION("Ampere Altra SMpro driver");
529*4a4a4e9eSQuan Nguyen MODULE_LICENSE("GPL");
530