xref: /openbmc/linux/drivers/misc/smpro-errmon.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
14a4a4e9eSQuan Nguyen // SPDX-License-Identifier: GPL-2.0-only
24a4a4e9eSQuan Nguyen /*
34a4a4e9eSQuan Nguyen  * Ampere Computing SoC's SMpro Error Monitoring Driver
44a4a4e9eSQuan Nguyen  *
54a4a4e9eSQuan Nguyen  * Copyright (c) 2022, Ampere Computing LLC
64a4a4e9eSQuan Nguyen  *
74a4a4e9eSQuan Nguyen  */
84a4a4e9eSQuan Nguyen 
94a4a4e9eSQuan Nguyen #include <linux/mod_devicetable.h>
104a4a4e9eSQuan Nguyen #include <linux/module.h>
114a4a4e9eSQuan Nguyen #include <linux/platform_device.h>
124a4a4e9eSQuan Nguyen #include <linux/regmap.h>
134a4a4e9eSQuan Nguyen 
144a4a4e9eSQuan Nguyen /* GPI RAS Error Registers */
154a4a4e9eSQuan Nguyen #define GPI_RAS_ERR		0x7E
164a4a4e9eSQuan Nguyen 
174a4a4e9eSQuan Nguyen /* Core and L2C Error Registers */
184a4a4e9eSQuan Nguyen #define CORE_CE_ERR_CNT		0x80
194a4a4e9eSQuan Nguyen #define CORE_CE_ERR_LEN		0x81
204a4a4e9eSQuan Nguyen #define CORE_CE_ERR_DATA	0x82
214a4a4e9eSQuan Nguyen #define CORE_UE_ERR_CNT		0x83
224a4a4e9eSQuan Nguyen #define CORE_UE_ERR_LEN		0x84
234a4a4e9eSQuan Nguyen #define CORE_UE_ERR_DATA	0x85
244a4a4e9eSQuan Nguyen 
254a4a4e9eSQuan Nguyen /* Memory Error Registers */
264a4a4e9eSQuan Nguyen #define MEM_CE_ERR_CNT		0x90
274a4a4e9eSQuan Nguyen #define MEM_CE_ERR_LEN		0x91
284a4a4e9eSQuan Nguyen #define MEM_CE_ERR_DATA		0x92
294a4a4e9eSQuan Nguyen #define MEM_UE_ERR_CNT		0x93
304a4a4e9eSQuan Nguyen #define MEM_UE_ERR_LEN		0x94
314a4a4e9eSQuan Nguyen #define MEM_UE_ERR_DATA		0x95
324a4a4e9eSQuan Nguyen 
334a4a4e9eSQuan Nguyen /* RAS Error/Warning Registers */
344a4a4e9eSQuan Nguyen #define ERR_SMPRO_TYPE		0xA0
354a4a4e9eSQuan Nguyen #define ERR_PMPRO_TYPE		0xA1
364a4a4e9eSQuan Nguyen #define ERR_SMPRO_INFO_LO	0xA2
374a4a4e9eSQuan Nguyen #define ERR_SMPRO_INFO_HI	0xA3
384a4a4e9eSQuan Nguyen #define ERR_SMPRO_DATA_LO	0xA4
394a4a4e9eSQuan Nguyen #define ERR_SMPRO_DATA_HI	0xA5
404a4a4e9eSQuan Nguyen #define WARN_SMPRO_INFO_LO	0xAA
414a4a4e9eSQuan Nguyen #define WARN_SMPRO_INFO_HI	0xAB
424a4a4e9eSQuan Nguyen #define ERR_PMPRO_INFO_LO	0xA6
434a4a4e9eSQuan Nguyen #define ERR_PMPRO_INFO_HI	0xA7
444a4a4e9eSQuan Nguyen #define ERR_PMPRO_DATA_LO	0xA8
454a4a4e9eSQuan Nguyen #define ERR_PMPRO_DATA_HI	0xA9
464a4a4e9eSQuan Nguyen #define WARN_PMPRO_INFO_LO	0xAC
474a4a4e9eSQuan Nguyen #define WARN_PMPRO_INFO_HI	0xAD
484a4a4e9eSQuan Nguyen 
494a4a4e9eSQuan Nguyen /* Boot Stage Register */
50*c2c99326SQuan Nguyen #define BOOTSTAGE		0xB0
51*c2c99326SQuan Nguyen #define DIMM_SYNDROME_SEL	0xB4
52*c2c99326SQuan Nguyen #define DIMM_SYNDROME_ERR	0xB5
53*c2c99326SQuan Nguyen #define DIMM_SYNDROME_STAGE	4
54*c2c99326SQuan Nguyen 
55*c2c99326SQuan Nguyen /* PCIE Error Registers */
564a4a4e9eSQuan Nguyen #define PCIE_CE_ERR_CNT		0xC0
574a4a4e9eSQuan Nguyen #define PCIE_CE_ERR_LEN		0xC1
584a4a4e9eSQuan Nguyen #define PCIE_CE_ERR_DATA	0xC2
594a4a4e9eSQuan Nguyen #define PCIE_UE_ERR_CNT		0xC3
604a4a4e9eSQuan Nguyen #define PCIE_UE_ERR_LEN		0xC4
614a4a4e9eSQuan Nguyen #define PCIE_UE_ERR_DATA	0xC5
624a4a4e9eSQuan Nguyen 
634a4a4e9eSQuan Nguyen /* Other Error Registers */
644a4a4e9eSQuan Nguyen #define OTHER_CE_ERR_CNT	0xD0
654a4a4e9eSQuan Nguyen #define OTHER_CE_ERR_LEN	0xD1
664a4a4e9eSQuan Nguyen #define OTHER_CE_ERR_DATA	0xD2
674a4a4e9eSQuan Nguyen #define OTHER_UE_ERR_CNT	0xD8
684a4a4e9eSQuan Nguyen #define OTHER_UE_ERR_LEN	0xD9
694a4a4e9eSQuan Nguyen #define OTHER_UE_ERR_DATA	0xDA
704a4a4e9eSQuan Nguyen 
714a4a4e9eSQuan Nguyen /* Event Data Registers */
724a4a4e9eSQuan Nguyen #define VRD_WARN_FAULT_EVENT_DATA	0x78
734a4a4e9eSQuan Nguyen #define VRD_HOT_EVENT_DATA		0x79
744a4a4e9eSQuan Nguyen #define DIMM_HOT_EVENT_DATA		0x7A
754a4a4e9eSQuan Nguyen #define DIMM_2X_REFRESH_EVENT_DATA	0x96
76b0f64c80SQuan Nguyen 
774a4a4e9eSQuan Nguyen #define MAX_READ_BLOCK_LENGTH	48
784a4a4e9eSQuan Nguyen 
794a4a4e9eSQuan Nguyen #define RAS_SMPRO_ERR		0
804a4a4e9eSQuan Nguyen #define RAS_PMPRO_ERR		1
814a4a4e9eSQuan Nguyen 
824a4a4e9eSQuan Nguyen enum RAS_48BYTES_ERR_TYPES {
834a4a4e9eSQuan Nguyen 	CORE_CE_ERR,
844a4a4e9eSQuan Nguyen 	CORE_UE_ERR,
854a4a4e9eSQuan Nguyen 	MEM_CE_ERR,
864a4a4e9eSQuan Nguyen 	MEM_UE_ERR,
874a4a4e9eSQuan Nguyen 	PCIE_CE_ERR,
884a4a4e9eSQuan Nguyen 	PCIE_UE_ERR,
894a4a4e9eSQuan Nguyen 	OTHER_CE_ERR,
904a4a4e9eSQuan Nguyen 	OTHER_UE_ERR,
914a4a4e9eSQuan Nguyen 	NUM_48BYTES_ERR_TYPE,
924a4a4e9eSQuan Nguyen };
934a4a4e9eSQuan Nguyen 
944a4a4e9eSQuan Nguyen struct smpro_error_hdr {
954a4a4e9eSQuan Nguyen 	u8 count;	/* Number of the RAS errors */
964a4a4e9eSQuan Nguyen 	u8 len;		/* Number of data bytes */
974a4a4e9eSQuan Nguyen 	u8 data;	/* Start of 48-byte data */
984a4a4e9eSQuan Nguyen 	u8 max_cnt;	/* Max num of errors */
994a4a4e9eSQuan Nguyen };
1004a4a4e9eSQuan Nguyen 
1014a4a4e9eSQuan Nguyen /*
1024a4a4e9eSQuan Nguyen  * Included Address of registers to get Count, Length of data and Data
1034a4a4e9eSQuan Nguyen  * of the 48 bytes error data
1044a4a4e9eSQuan Nguyen  */
1054a4a4e9eSQuan Nguyen static struct smpro_error_hdr smpro_error_table[] = {
1064a4a4e9eSQuan Nguyen 	[CORE_CE_ERR] = {
1074a4a4e9eSQuan Nguyen 		.count = CORE_CE_ERR_CNT,
1084a4a4e9eSQuan Nguyen 		.len = CORE_CE_ERR_LEN,
1094a4a4e9eSQuan Nguyen 		.data = CORE_CE_ERR_DATA,
1104a4a4e9eSQuan Nguyen 		.max_cnt = 32
1114a4a4e9eSQuan Nguyen 	},
1124a4a4e9eSQuan Nguyen 	[CORE_UE_ERR] = {
1134a4a4e9eSQuan Nguyen 		.count = CORE_UE_ERR_CNT,
1144a4a4e9eSQuan Nguyen 		.len = CORE_UE_ERR_LEN,
1154a4a4e9eSQuan Nguyen 		.data = CORE_UE_ERR_DATA,
1164a4a4e9eSQuan Nguyen 		.max_cnt = 32
1174a4a4e9eSQuan Nguyen 	},
1184a4a4e9eSQuan Nguyen 	[MEM_CE_ERR] = {
1194a4a4e9eSQuan Nguyen 		.count = MEM_CE_ERR_CNT,
1204a4a4e9eSQuan Nguyen 		.len = MEM_CE_ERR_LEN,
1214a4a4e9eSQuan Nguyen 		.data = MEM_CE_ERR_DATA,
1224a4a4e9eSQuan Nguyen 		.max_cnt = 16
1234a4a4e9eSQuan Nguyen 	},
1244a4a4e9eSQuan Nguyen 	[MEM_UE_ERR] = {
1254a4a4e9eSQuan Nguyen 		.count = MEM_UE_ERR_CNT,
1264a4a4e9eSQuan Nguyen 		.len = MEM_UE_ERR_LEN,
1274a4a4e9eSQuan Nguyen 		.data = MEM_UE_ERR_DATA,
1284a4a4e9eSQuan Nguyen 		.max_cnt = 16
1294a4a4e9eSQuan Nguyen 	},
1304a4a4e9eSQuan Nguyen 	[PCIE_CE_ERR] = {
1314a4a4e9eSQuan Nguyen 		.count = PCIE_CE_ERR_CNT,
1324a4a4e9eSQuan Nguyen 		.len = PCIE_CE_ERR_LEN,
1334a4a4e9eSQuan Nguyen 		.data = PCIE_CE_ERR_DATA,
1344a4a4e9eSQuan Nguyen 		.max_cnt = 96
1354a4a4e9eSQuan Nguyen 	},
1364a4a4e9eSQuan Nguyen 	[PCIE_UE_ERR] = {
1374a4a4e9eSQuan Nguyen 		.count = PCIE_UE_ERR_CNT,
1384a4a4e9eSQuan Nguyen 		.len = PCIE_UE_ERR_LEN,
1394a4a4e9eSQuan Nguyen 		.data = PCIE_UE_ERR_DATA,
1404a4a4e9eSQuan Nguyen 		.max_cnt = 96
1414a4a4e9eSQuan Nguyen 	},
1424a4a4e9eSQuan Nguyen 	[OTHER_CE_ERR] = {
1434a4a4e9eSQuan Nguyen 		.count = OTHER_CE_ERR_CNT,
1444a4a4e9eSQuan Nguyen 		.len = OTHER_CE_ERR_LEN,
1454a4a4e9eSQuan Nguyen 		.data = OTHER_CE_ERR_DATA,
1464a4a4e9eSQuan Nguyen 		.max_cnt = 8
1474a4a4e9eSQuan Nguyen 	},
1484a4a4e9eSQuan Nguyen 	[OTHER_UE_ERR] = {
1494a4a4e9eSQuan Nguyen 		.count = OTHER_UE_ERR_CNT,
1504a4a4e9eSQuan Nguyen 		.len = OTHER_UE_ERR_LEN,
1514a4a4e9eSQuan Nguyen 		.data = OTHER_UE_ERR_DATA,
1524a4a4e9eSQuan Nguyen 		.max_cnt = 8
1534a4a4e9eSQuan Nguyen 	},
1544a4a4e9eSQuan Nguyen };
1554a4a4e9eSQuan Nguyen 
1564a4a4e9eSQuan Nguyen /*
1574a4a4e9eSQuan Nguyen  * List of SCP registers which are used to get
1584a4a4e9eSQuan Nguyen  * one type of RAS Internal errors.
1594a4a4e9eSQuan Nguyen  */
1604a4a4e9eSQuan Nguyen struct smpro_int_error_hdr {
1614a4a4e9eSQuan Nguyen 	u8 type;
1624a4a4e9eSQuan Nguyen 	u8 info_l;
1634a4a4e9eSQuan Nguyen 	u8 info_h;
1644a4a4e9eSQuan Nguyen 	u8 data_l;
1654a4a4e9eSQuan Nguyen 	u8 data_h;
1664a4a4e9eSQuan Nguyen 	u8 warn_l;
1674a4a4e9eSQuan Nguyen 	u8 warn_h;
1684a4a4e9eSQuan Nguyen };
1694a4a4e9eSQuan Nguyen 
1704a4a4e9eSQuan Nguyen static struct smpro_int_error_hdr list_smpro_int_error_hdr[] = {
1714a4a4e9eSQuan Nguyen 	[RAS_SMPRO_ERR] = {
1724a4a4e9eSQuan Nguyen 		.type = ERR_SMPRO_TYPE,
1734a4a4e9eSQuan Nguyen 		.info_l = ERR_SMPRO_INFO_LO,
1744a4a4e9eSQuan Nguyen 		.info_h = ERR_SMPRO_INFO_HI,
1754a4a4e9eSQuan Nguyen 		.data_l = ERR_SMPRO_DATA_LO,
1764a4a4e9eSQuan Nguyen 		.data_h = ERR_SMPRO_DATA_HI,
1774a4a4e9eSQuan Nguyen 		.warn_l = WARN_SMPRO_INFO_LO,
1784a4a4e9eSQuan Nguyen 		.warn_h = WARN_SMPRO_INFO_HI,
1794a4a4e9eSQuan Nguyen 	},
1804a4a4e9eSQuan Nguyen 	[RAS_PMPRO_ERR] = {
1814a4a4e9eSQuan Nguyen 		.type = ERR_PMPRO_TYPE,
1824a4a4e9eSQuan Nguyen 		.info_l = ERR_PMPRO_INFO_LO,
1834a4a4e9eSQuan Nguyen 		.info_h = ERR_PMPRO_INFO_HI,
1844a4a4e9eSQuan Nguyen 		.data_l = ERR_PMPRO_DATA_LO,
1854a4a4e9eSQuan Nguyen 		.data_h = ERR_PMPRO_DATA_HI,
1864a4a4e9eSQuan Nguyen 		.warn_l = WARN_PMPRO_INFO_LO,
1874a4a4e9eSQuan Nguyen 		.warn_h = WARN_PMPRO_INFO_HI,
1884a4a4e9eSQuan Nguyen 	},
1894a4a4e9eSQuan Nguyen };
1904a4a4e9eSQuan Nguyen 
1914a4a4e9eSQuan Nguyen struct smpro_errmon {
1924a4a4e9eSQuan Nguyen 	struct regmap *regmap;
1934a4a4e9eSQuan Nguyen };
1944a4a4e9eSQuan Nguyen 
1954a4a4e9eSQuan Nguyen enum EVENT_TYPES {
1964a4a4e9eSQuan Nguyen 	VRD_WARN_FAULT_EVENT,
1974a4a4e9eSQuan Nguyen 	VRD_HOT_EVENT,
1984a4a4e9eSQuan Nguyen 	DIMM_HOT_EVENT,
1994a4a4e9eSQuan Nguyen 	DIMM_2X_REFRESH_EVENT,
200b0f64c80SQuan Nguyen 	NUM_EVENTS_TYPE,
2014a4a4e9eSQuan Nguyen };
2024a4a4e9eSQuan Nguyen 
2034a4a4e9eSQuan Nguyen /* Included Address of event source and data registers */
2044a4a4e9eSQuan Nguyen static u8 smpro_event_table[NUM_EVENTS_TYPE] = {
2054a4a4e9eSQuan Nguyen 	VRD_WARN_FAULT_EVENT_DATA,
2064a4a4e9eSQuan Nguyen 	VRD_HOT_EVENT_DATA,
2074a4a4e9eSQuan Nguyen 	DIMM_HOT_EVENT_DATA,
2084a4a4e9eSQuan Nguyen 	DIMM_2X_REFRESH_EVENT_DATA,
209b0f64c80SQuan Nguyen };
2104a4a4e9eSQuan Nguyen 
smpro_event_data_read(struct device * dev,struct device_attribute * da,char * buf,int channel)2114a4a4e9eSQuan Nguyen static ssize_t smpro_event_data_read(struct device *dev,
2124a4a4e9eSQuan Nguyen 				     struct device_attribute *da, char *buf,
2134a4a4e9eSQuan Nguyen 				     int channel)
2144a4a4e9eSQuan Nguyen {
2154a4a4e9eSQuan Nguyen 	struct smpro_errmon *errmon = dev_get_drvdata(dev);
2164a4a4e9eSQuan Nguyen 	s32 event_data;
2174a4a4e9eSQuan Nguyen 	int ret;
2184a4a4e9eSQuan Nguyen 
2194a4a4e9eSQuan Nguyen 	ret = regmap_read(errmon->regmap, smpro_event_table[channel], &event_data);
2204a4a4e9eSQuan Nguyen 	if (ret)
2214a4a4e9eSQuan Nguyen 		return ret;
2224a4a4e9eSQuan Nguyen 	/* Clear event after read */
2234a4a4e9eSQuan Nguyen 	if (event_data != 0)
2244a4a4e9eSQuan Nguyen 		regmap_write(errmon->regmap, smpro_event_table[channel], event_data);
2254a4a4e9eSQuan Nguyen 
2264a4a4e9eSQuan Nguyen 	return sysfs_emit(buf, "%04x\n", event_data);
2274a4a4e9eSQuan Nguyen }
2284a4a4e9eSQuan Nguyen 
smpro_overflow_data_read(struct device * dev,struct device_attribute * da,char * buf,int channel)2294a4a4e9eSQuan Nguyen static ssize_t smpro_overflow_data_read(struct device *dev, struct device_attribute *da,
2304a4a4e9eSQuan Nguyen 					char *buf, int channel)
2314a4a4e9eSQuan Nguyen {
2324a4a4e9eSQuan Nguyen 	struct smpro_errmon *errmon = dev_get_drvdata(dev);
2334a4a4e9eSQuan Nguyen 	struct smpro_error_hdr *err_info;
2344a4a4e9eSQuan Nguyen 	s32 err_count;
2354a4a4e9eSQuan Nguyen 	int ret;
2364a4a4e9eSQuan Nguyen 
2374a4a4e9eSQuan Nguyen 	err_info = &smpro_error_table[channel];
2384a4a4e9eSQuan Nguyen 
2394a4a4e9eSQuan Nguyen 	ret = regmap_read(errmon->regmap, err_info->count, &err_count);
2404a4a4e9eSQuan Nguyen 	if (ret)
2414a4a4e9eSQuan Nguyen 		return ret;
2424a4a4e9eSQuan Nguyen 
2434a4a4e9eSQuan Nguyen 	/* Bit 8 indicates the overflow status */
2444a4a4e9eSQuan Nguyen 	return sysfs_emit(buf, "%d\n", (err_count & BIT(8)) ? 1 : 0);
2454a4a4e9eSQuan Nguyen }
2464a4a4e9eSQuan Nguyen 
smpro_error_data_read(struct device * dev,struct device_attribute * da,char * buf,int channel)2474a4a4e9eSQuan Nguyen static ssize_t smpro_error_data_read(struct device *dev, struct device_attribute *da,
2484a4a4e9eSQuan Nguyen 				     char *buf, int channel)
2494a4a4e9eSQuan Nguyen {
2504a4a4e9eSQuan Nguyen 	struct smpro_errmon *errmon = dev_get_drvdata(dev);
2514a4a4e9eSQuan Nguyen 	unsigned char err_data[MAX_READ_BLOCK_LENGTH];
2524a4a4e9eSQuan Nguyen 	struct smpro_error_hdr *err_info;
2534a4a4e9eSQuan Nguyen 	s32 err_count, err_length;
2544a4a4e9eSQuan Nguyen 	int ret;
2554a4a4e9eSQuan Nguyen 
2564a4a4e9eSQuan Nguyen 	err_info = &smpro_error_table[channel];
2574a4a4e9eSQuan Nguyen 
2584a4a4e9eSQuan Nguyen 	ret = regmap_read(errmon->regmap, err_info->count, &err_count);
2594a4a4e9eSQuan Nguyen 	/* Error count is the low byte */
2604a4a4e9eSQuan Nguyen 	err_count &= 0xff;
2614a4a4e9eSQuan Nguyen 	if (ret || !err_count || err_count > err_info->max_cnt)
2624a4a4e9eSQuan Nguyen 		return ret;
2634a4a4e9eSQuan Nguyen 
2644a4a4e9eSQuan Nguyen 	ret = regmap_read(errmon->regmap, err_info->len, &err_length);
2654a4a4e9eSQuan Nguyen 	if (ret || err_length <= 0)
2664a4a4e9eSQuan Nguyen 		return ret;
2674a4a4e9eSQuan Nguyen 
2684a4a4e9eSQuan Nguyen 	if (err_length > MAX_READ_BLOCK_LENGTH)
2694a4a4e9eSQuan Nguyen 		err_length = MAX_READ_BLOCK_LENGTH;
2704a4a4e9eSQuan Nguyen 
2714a4a4e9eSQuan Nguyen 	memset(err_data, 0x00, MAX_READ_BLOCK_LENGTH);
2724a4a4e9eSQuan Nguyen 	ret = regmap_noinc_read(errmon->regmap, err_info->data, err_data, err_length);
2734a4a4e9eSQuan Nguyen 	if (ret < 0)
2744a4a4e9eSQuan Nguyen 		return ret;
2754a4a4e9eSQuan Nguyen 
2764a4a4e9eSQuan Nguyen 	/* clear the error */
2774a4a4e9eSQuan Nguyen 	ret = regmap_write(errmon->regmap, err_info->count, 0x100);
2784a4a4e9eSQuan Nguyen 	if (ret)
2794a4a4e9eSQuan Nguyen 		return ret;
2804a4a4e9eSQuan Nguyen 	/*
2814a4a4e9eSQuan Nguyen 	 * The output of Core/Memory/PCIe/Others UE/CE errors follows the format
2824a4a4e9eSQuan Nguyen 	 * specified in section 5.8.1 CE/UE Error Data record in
2834a4a4e9eSQuan Nguyen 	 * Altra SOC BMC Interface specification.
2844a4a4e9eSQuan Nguyen 	 */
2854a4a4e9eSQuan Nguyen 	return sysfs_emit(buf, "%*phN\n", MAX_READ_BLOCK_LENGTH, err_data);
2864a4a4e9eSQuan Nguyen }
2874a4a4e9eSQuan Nguyen 
2884a4a4e9eSQuan Nguyen /*
2894a4a4e9eSQuan Nguyen  * Output format:
2904a4a4e9eSQuan Nguyen  * <4-byte hex value of error info><4-byte hex value of error extensive data>
2914a4a4e9eSQuan Nguyen  * Where:
2924a4a4e9eSQuan Nguyen  *   + error info : The error information
2934a4a4e9eSQuan Nguyen  *   + error data : Extensive data (32 bits)
2944a4a4e9eSQuan Nguyen  * Reference to section 5.10 RAS Internal Error Register Definition in
2954a4a4e9eSQuan Nguyen  * Altra SOC BMC Interface specification
2964a4a4e9eSQuan Nguyen  */
smpro_internal_err_read(struct device * dev,struct device_attribute * da,char * buf,int channel)2974a4a4e9eSQuan Nguyen static ssize_t smpro_internal_err_read(struct device *dev, struct device_attribute *da,
2984a4a4e9eSQuan Nguyen 				       char *buf, int channel)
2994a4a4e9eSQuan Nguyen {
3004a4a4e9eSQuan Nguyen 	struct smpro_errmon *errmon = dev_get_drvdata(dev);
3014a4a4e9eSQuan Nguyen 	struct smpro_int_error_hdr *err_info;
3024a4a4e9eSQuan Nguyen 	unsigned int err[4] = { 0 };
3034a4a4e9eSQuan Nguyen 	unsigned int err_type;
3044a4a4e9eSQuan Nguyen 	unsigned int val;
3054a4a4e9eSQuan Nguyen 	int ret;
3064a4a4e9eSQuan Nguyen 
3074a4a4e9eSQuan Nguyen 	/* read error status */
3084a4a4e9eSQuan Nguyen 	ret = regmap_read(errmon->regmap, GPI_RAS_ERR, &val);
3094a4a4e9eSQuan Nguyen 	if (ret)
3104a4a4e9eSQuan Nguyen 		return ret;
3114a4a4e9eSQuan Nguyen 
3124a4a4e9eSQuan Nguyen 	if ((channel == RAS_SMPRO_ERR && !(val & BIT(0))) ||
3134a4a4e9eSQuan Nguyen 	    (channel == RAS_PMPRO_ERR && !(val & BIT(1))))
3144a4a4e9eSQuan Nguyen 		return 0;
3154a4a4e9eSQuan Nguyen 
3164a4a4e9eSQuan Nguyen 	err_info = &list_smpro_int_error_hdr[channel];
3174a4a4e9eSQuan Nguyen 	ret = regmap_read(errmon->regmap, err_info->type, &val);
3184a4a4e9eSQuan Nguyen 	if (ret)
3194a4a4e9eSQuan Nguyen 		return ret;
3204a4a4e9eSQuan Nguyen 
3214a4a4e9eSQuan Nguyen 	err_type = (val & BIT(1)) ? BIT(1) :
3224a4a4e9eSQuan Nguyen 		   (val & BIT(2)) ? BIT(2) : 0;
3234a4a4e9eSQuan Nguyen 
3244a4a4e9eSQuan Nguyen 	if (!err_type)
3254a4a4e9eSQuan Nguyen 		return 0;
3264a4a4e9eSQuan Nguyen 
3274a4a4e9eSQuan Nguyen 	ret = regmap_read(errmon->regmap, err_info->info_l, err + 1);
3284a4a4e9eSQuan Nguyen 	if (ret)
3294a4a4e9eSQuan Nguyen 		return ret;
3304a4a4e9eSQuan Nguyen 
3314a4a4e9eSQuan Nguyen 	ret = regmap_read(errmon->regmap, err_info->info_h, err);
3324a4a4e9eSQuan Nguyen 	if (ret)
3334a4a4e9eSQuan Nguyen 		return ret;
3344a4a4e9eSQuan Nguyen 
3354a4a4e9eSQuan Nguyen 	if (err_type & BIT(2)) {
3364a4a4e9eSQuan Nguyen 		/* Error with data type */
3374a4a4e9eSQuan Nguyen 		ret = regmap_read(errmon->regmap, err_info->data_l, err + 3);
3384a4a4e9eSQuan Nguyen 		if (ret)
3394a4a4e9eSQuan Nguyen 			return ret;
3404a4a4e9eSQuan Nguyen 
3414a4a4e9eSQuan Nguyen 		ret = regmap_read(errmon->regmap, err_info->data_h, err + 2);
3424a4a4e9eSQuan Nguyen 		if (ret)
3434a4a4e9eSQuan Nguyen 			return ret;
3444a4a4e9eSQuan Nguyen 	}
3454a4a4e9eSQuan Nguyen 
3464a4a4e9eSQuan Nguyen 	/* clear the read errors */
3474a4a4e9eSQuan Nguyen 	ret = regmap_write(errmon->regmap, err_info->type, err_type);
3484a4a4e9eSQuan Nguyen 	if (ret)
3494a4a4e9eSQuan Nguyen 		return ret;
3504a4a4e9eSQuan Nguyen 
3514a4a4e9eSQuan Nguyen 	return sysfs_emit(buf, "%*phN\n", (int)sizeof(err), err);
3524a4a4e9eSQuan Nguyen }
3534a4a4e9eSQuan Nguyen 
3544a4a4e9eSQuan Nguyen /*
3554a4a4e9eSQuan Nguyen  * Output format:
3564a4a4e9eSQuan Nguyen  * <4-byte hex value of warining info>
3574a4a4e9eSQuan Nguyen  * Reference to section 5.10 RAS Internal Error Register Definition in
3584a4a4e9eSQuan Nguyen  * Altra SOC BMC Interface specification
3594a4a4e9eSQuan Nguyen  */
smpro_internal_warn_read(struct device * dev,struct device_attribute * da,char * buf,int channel)3604a4a4e9eSQuan Nguyen static ssize_t smpro_internal_warn_read(struct device *dev, struct device_attribute *da,
3614a4a4e9eSQuan Nguyen 					char *buf, int channel)
3624a4a4e9eSQuan Nguyen {
3634a4a4e9eSQuan Nguyen 	struct smpro_errmon *errmon = dev_get_drvdata(dev);
3644a4a4e9eSQuan Nguyen 	struct smpro_int_error_hdr *err_info;
3654a4a4e9eSQuan Nguyen 	unsigned int warn[2] = { 0 };
3664a4a4e9eSQuan Nguyen 	unsigned int val;
3674a4a4e9eSQuan Nguyen 	int ret;
3684a4a4e9eSQuan Nguyen 
3694a4a4e9eSQuan Nguyen 	/* read error status */
3704a4a4e9eSQuan Nguyen 	ret = regmap_read(errmon->regmap, GPI_RAS_ERR, &val);
3714a4a4e9eSQuan Nguyen 	if (ret)
3724a4a4e9eSQuan Nguyen 		return ret;
3734a4a4e9eSQuan Nguyen 
3744a4a4e9eSQuan Nguyen 	if ((channel == RAS_SMPRO_ERR && !(val & BIT(0))) ||
3754a4a4e9eSQuan Nguyen 	    (channel == RAS_PMPRO_ERR && !(val & BIT(1))))
3764a4a4e9eSQuan Nguyen 		return 0;
3774a4a4e9eSQuan Nguyen 
3784a4a4e9eSQuan Nguyen 	err_info = &list_smpro_int_error_hdr[channel];
3794a4a4e9eSQuan Nguyen 	ret = regmap_read(errmon->regmap, err_info->type, &val);
3804a4a4e9eSQuan Nguyen 	if (ret)
3814a4a4e9eSQuan Nguyen 		return ret;
3824a4a4e9eSQuan Nguyen 
3834a4a4e9eSQuan Nguyen 	if (!(val & BIT(0)))
3844a4a4e9eSQuan Nguyen 		return 0;
3854a4a4e9eSQuan Nguyen 
3864a4a4e9eSQuan Nguyen 	ret = regmap_read(errmon->regmap, err_info->warn_l, warn + 1);
3874a4a4e9eSQuan Nguyen 	if (ret)
3884a4a4e9eSQuan Nguyen 		return ret;
3894a4a4e9eSQuan Nguyen 
3904a4a4e9eSQuan Nguyen 	ret = regmap_read(errmon->regmap, err_info->warn_h, warn);
3914a4a4e9eSQuan Nguyen 	if (ret)
3924a4a4e9eSQuan Nguyen 		return ret;
3934a4a4e9eSQuan Nguyen 
3944a4a4e9eSQuan Nguyen 	/* clear the warning */
3954a4a4e9eSQuan Nguyen 	ret = regmap_write(errmon->regmap, err_info->type, BIT(0));
3964a4a4e9eSQuan Nguyen 	if (ret)
3974a4a4e9eSQuan Nguyen 		return ret;
3984a4a4e9eSQuan Nguyen 
3994a4a4e9eSQuan Nguyen 	return sysfs_emit(buf, "%*phN\n", (int)sizeof(warn), warn);
4004a4a4e9eSQuan Nguyen }
4014a4a4e9eSQuan Nguyen 
4024a4a4e9eSQuan Nguyen #define ERROR_OVERFLOW_RO(_error, _index) \
4034a4a4e9eSQuan Nguyen 	static ssize_t overflow_##_error##_show(struct device *dev,            \
4044a4a4e9eSQuan Nguyen 						struct device_attribute *da,   \
4054a4a4e9eSQuan Nguyen 						char *buf)                     \
4064a4a4e9eSQuan Nguyen 	{                                                                      \
4074a4a4e9eSQuan Nguyen 		return smpro_overflow_data_read(dev, da, buf, _index);         \
4084a4a4e9eSQuan Nguyen 	}                                                                      \
4094a4a4e9eSQuan Nguyen 	static DEVICE_ATTR_RO(overflow_##_error)
4104a4a4e9eSQuan Nguyen 
4114a4a4e9eSQuan Nguyen ERROR_OVERFLOW_RO(core_ce, CORE_CE_ERR);
4124a4a4e9eSQuan Nguyen ERROR_OVERFLOW_RO(core_ue, CORE_UE_ERR);
4134a4a4e9eSQuan Nguyen ERROR_OVERFLOW_RO(mem_ce, MEM_CE_ERR);
4144a4a4e9eSQuan Nguyen ERROR_OVERFLOW_RO(mem_ue, MEM_UE_ERR);
4154a4a4e9eSQuan Nguyen ERROR_OVERFLOW_RO(pcie_ce, PCIE_CE_ERR);
4164a4a4e9eSQuan Nguyen ERROR_OVERFLOW_RO(pcie_ue, PCIE_UE_ERR);
4174a4a4e9eSQuan Nguyen ERROR_OVERFLOW_RO(other_ce, OTHER_CE_ERR);
4184a4a4e9eSQuan Nguyen ERROR_OVERFLOW_RO(other_ue, OTHER_UE_ERR);
4194a4a4e9eSQuan Nguyen 
4204a4a4e9eSQuan Nguyen #define ERROR_RO(_error, _index) \
4214a4a4e9eSQuan Nguyen 	static ssize_t error_##_error##_show(struct device *dev,            \
4224a4a4e9eSQuan Nguyen 					     struct device_attribute *da,   \
4234a4a4e9eSQuan Nguyen 					     char *buf)                     \
4244a4a4e9eSQuan Nguyen 	{                                                                   \
4254a4a4e9eSQuan Nguyen 		return smpro_error_data_read(dev, da, buf, _index);         \
4264a4a4e9eSQuan Nguyen 	}                                                                   \
4274a4a4e9eSQuan Nguyen 	static DEVICE_ATTR_RO(error_##_error)
4284a4a4e9eSQuan Nguyen 
4294a4a4e9eSQuan Nguyen ERROR_RO(core_ce, CORE_CE_ERR);
4304a4a4e9eSQuan Nguyen ERROR_RO(core_ue, CORE_UE_ERR);
4314a4a4e9eSQuan Nguyen ERROR_RO(mem_ce, MEM_CE_ERR);
4324a4a4e9eSQuan Nguyen ERROR_RO(mem_ue, MEM_UE_ERR);
4334a4a4e9eSQuan Nguyen ERROR_RO(pcie_ce, PCIE_CE_ERR);
4344a4a4e9eSQuan Nguyen ERROR_RO(pcie_ue, PCIE_UE_ERR);
4354a4a4e9eSQuan Nguyen ERROR_RO(other_ce, OTHER_CE_ERR);
4364a4a4e9eSQuan Nguyen ERROR_RO(other_ue, OTHER_UE_ERR);
4374a4a4e9eSQuan Nguyen 
error_smpro_show(struct device * dev,struct device_attribute * da,char * buf)4384a4a4e9eSQuan Nguyen static ssize_t error_smpro_show(struct device *dev, struct device_attribute *da, char *buf)
4394a4a4e9eSQuan Nguyen {
4404a4a4e9eSQuan Nguyen 	return smpro_internal_err_read(dev, da, buf, RAS_SMPRO_ERR);
4414a4a4e9eSQuan Nguyen }
4424a4a4e9eSQuan Nguyen static DEVICE_ATTR_RO(error_smpro);
4434a4a4e9eSQuan Nguyen 
error_pmpro_show(struct device * dev,struct device_attribute * da,char * buf)4444a4a4e9eSQuan Nguyen static ssize_t error_pmpro_show(struct device *dev, struct device_attribute *da, char *buf)
4454a4a4e9eSQuan Nguyen {
4464a4a4e9eSQuan Nguyen 	return smpro_internal_err_read(dev, da, buf, RAS_PMPRO_ERR);
4474a4a4e9eSQuan Nguyen }
4484a4a4e9eSQuan Nguyen static DEVICE_ATTR_RO(error_pmpro);
4494a4a4e9eSQuan Nguyen 
warn_smpro_show(struct device * dev,struct device_attribute * da,char * buf)4504a4a4e9eSQuan Nguyen static ssize_t warn_smpro_show(struct device *dev, struct device_attribute *da, char *buf)
4514a4a4e9eSQuan Nguyen {
4524a4a4e9eSQuan Nguyen 	return smpro_internal_warn_read(dev, da, buf, RAS_SMPRO_ERR);
4534a4a4e9eSQuan Nguyen }
4544a4a4e9eSQuan Nguyen static DEVICE_ATTR_RO(warn_smpro);
4554a4a4e9eSQuan Nguyen 
warn_pmpro_show(struct device * dev,struct device_attribute * da,char * buf)4564a4a4e9eSQuan Nguyen static ssize_t warn_pmpro_show(struct device *dev, struct device_attribute *da, char *buf)
4574a4a4e9eSQuan Nguyen {
4584a4a4e9eSQuan Nguyen 	return smpro_internal_warn_read(dev, da, buf, RAS_PMPRO_ERR);
4594a4a4e9eSQuan Nguyen }
4604a4a4e9eSQuan Nguyen static DEVICE_ATTR_RO(warn_pmpro);
4614a4a4e9eSQuan Nguyen 
4624a4a4e9eSQuan Nguyen #define EVENT_RO(_event, _index) \
4634a4a4e9eSQuan Nguyen 	static ssize_t event_##_event##_show(struct device *dev,            \
4644a4a4e9eSQuan Nguyen 					     struct device_attribute *da,   \
4654a4a4e9eSQuan Nguyen 					     char *buf)                     \
4664a4a4e9eSQuan Nguyen 	{                                                                   \
4674a4a4e9eSQuan Nguyen 		return smpro_event_data_read(dev, da, buf, _index);         \
4684a4a4e9eSQuan Nguyen 	}                                                                   \
4694a4a4e9eSQuan Nguyen 	static DEVICE_ATTR_RO(event_##_event)
4704a4a4e9eSQuan Nguyen 
4714a4a4e9eSQuan Nguyen EVENT_RO(vrd_warn_fault, VRD_WARN_FAULT_EVENT);
4724a4a4e9eSQuan Nguyen EVENT_RO(vrd_hot, VRD_HOT_EVENT);
4734a4a4e9eSQuan Nguyen EVENT_RO(dimm_hot, DIMM_HOT_EVENT);
4744a4a4e9eSQuan Nguyen EVENT_RO(dimm_2x_refresh, DIMM_2X_REFRESH_EVENT);
475b0f64c80SQuan Nguyen 
smpro_dimm_syndrome_read(struct device * dev,struct device_attribute * da,char * buf,unsigned int slot)4764a4a4e9eSQuan Nguyen static ssize_t smpro_dimm_syndrome_read(struct device *dev, struct device_attribute *da,
477*c2c99326SQuan Nguyen 					char *buf, unsigned int slot)
478*c2c99326SQuan Nguyen {
479*c2c99326SQuan Nguyen 	struct smpro_errmon *errmon = dev_get_drvdata(dev);
480*c2c99326SQuan Nguyen 	unsigned int data;
481*c2c99326SQuan Nguyen 	int ret;
482*c2c99326SQuan Nguyen 
483*c2c99326SQuan Nguyen 	ret = regmap_read(errmon->regmap, BOOTSTAGE, &data);
484*c2c99326SQuan Nguyen 	if (ret)
485*c2c99326SQuan Nguyen 		return ret;
486*c2c99326SQuan Nguyen 
487*c2c99326SQuan Nguyen 	/* check for valid stage */
488*c2c99326SQuan Nguyen 	data = (data >> 8) & 0xff;
489*c2c99326SQuan Nguyen 	if (data != DIMM_SYNDROME_STAGE)
490*c2c99326SQuan Nguyen 		return ret;
491*c2c99326SQuan Nguyen 
492*c2c99326SQuan Nguyen 	/* Write the slot ID to retrieve Error Syndrome */
493*c2c99326SQuan Nguyen 	ret = regmap_write(errmon->regmap, DIMM_SYNDROME_SEL, slot);
494*c2c99326SQuan Nguyen 	if (ret)
495*c2c99326SQuan Nguyen 		return ret;
496*c2c99326SQuan Nguyen 
497*c2c99326SQuan Nguyen 	/* Read the Syndrome error */
498*c2c99326SQuan Nguyen 	ret = regmap_read(errmon->regmap, DIMM_SYNDROME_ERR, &data);
499*c2c99326SQuan Nguyen 	if (ret || !data)
500*c2c99326SQuan Nguyen 		return ret;
501*c2c99326SQuan Nguyen 
502*c2c99326SQuan Nguyen 	return sysfs_emit(buf, "%04x\n", data);
503*c2c99326SQuan Nguyen }
504*c2c99326SQuan Nguyen 
505*c2c99326SQuan Nguyen #define EVENT_DIMM_SYNDROME(_slot) \
506*c2c99326SQuan Nguyen 	static ssize_t event_dimm##_slot##_syndrome_show(struct device *dev,          \
507*c2c99326SQuan Nguyen 							 struct device_attribute *da, \
508*c2c99326SQuan Nguyen 							 char *buf)                   \
509*c2c99326SQuan Nguyen 	{                                                                             \
510*c2c99326SQuan Nguyen 		return smpro_dimm_syndrome_read(dev, da, buf, _slot);                 \
511*c2c99326SQuan Nguyen 	}                                                                             \
512*c2c99326SQuan Nguyen 	static DEVICE_ATTR_RO(event_dimm##_slot##_syndrome)
513*c2c99326SQuan Nguyen 
514*c2c99326SQuan Nguyen EVENT_DIMM_SYNDROME(0);
515*c2c99326SQuan Nguyen EVENT_DIMM_SYNDROME(1);
516*c2c99326SQuan Nguyen EVENT_DIMM_SYNDROME(2);
517*c2c99326SQuan Nguyen EVENT_DIMM_SYNDROME(3);
518*c2c99326SQuan Nguyen EVENT_DIMM_SYNDROME(4);
519*c2c99326SQuan Nguyen EVENT_DIMM_SYNDROME(5);
520*c2c99326SQuan Nguyen EVENT_DIMM_SYNDROME(6);
521*c2c99326SQuan Nguyen EVENT_DIMM_SYNDROME(7);
522*c2c99326SQuan Nguyen EVENT_DIMM_SYNDROME(8);
523*c2c99326SQuan Nguyen EVENT_DIMM_SYNDROME(9);
524*c2c99326SQuan Nguyen EVENT_DIMM_SYNDROME(10);
525*c2c99326SQuan Nguyen EVENT_DIMM_SYNDROME(11);
526*c2c99326SQuan Nguyen EVENT_DIMM_SYNDROME(12);
527*c2c99326SQuan Nguyen EVENT_DIMM_SYNDROME(13);
528*c2c99326SQuan Nguyen EVENT_DIMM_SYNDROME(14);
529*c2c99326SQuan Nguyen EVENT_DIMM_SYNDROME(15);
530*c2c99326SQuan Nguyen 
531*c2c99326SQuan Nguyen static struct attribute *smpro_errmon_attrs[] = {
5324a4a4e9eSQuan Nguyen 	&dev_attr_overflow_core_ce.attr,
5334a4a4e9eSQuan Nguyen 	&dev_attr_overflow_core_ue.attr,
5344a4a4e9eSQuan Nguyen 	&dev_attr_overflow_mem_ce.attr,
5354a4a4e9eSQuan Nguyen 	&dev_attr_overflow_mem_ue.attr,
5364a4a4e9eSQuan Nguyen 	&dev_attr_overflow_pcie_ce.attr,
5374a4a4e9eSQuan Nguyen 	&dev_attr_overflow_pcie_ue.attr,
5384a4a4e9eSQuan Nguyen 	&dev_attr_overflow_other_ce.attr,
5394a4a4e9eSQuan Nguyen 	&dev_attr_overflow_other_ue.attr,
5404a4a4e9eSQuan Nguyen 	&dev_attr_error_core_ce.attr,
5414a4a4e9eSQuan Nguyen 	&dev_attr_error_core_ue.attr,
5424a4a4e9eSQuan Nguyen 	&dev_attr_error_mem_ce.attr,
5434a4a4e9eSQuan Nguyen 	&dev_attr_error_mem_ue.attr,
5444a4a4e9eSQuan Nguyen 	&dev_attr_error_pcie_ce.attr,
5454a4a4e9eSQuan Nguyen 	&dev_attr_error_pcie_ue.attr,
5464a4a4e9eSQuan Nguyen 	&dev_attr_error_other_ce.attr,
5474a4a4e9eSQuan Nguyen 	&dev_attr_error_other_ue.attr,
5484a4a4e9eSQuan Nguyen 	&dev_attr_error_smpro.attr,
5494a4a4e9eSQuan Nguyen 	&dev_attr_error_pmpro.attr,
5504a4a4e9eSQuan Nguyen 	&dev_attr_warn_smpro.attr,
5514a4a4e9eSQuan Nguyen 	&dev_attr_warn_pmpro.attr,
5524a4a4e9eSQuan Nguyen 	&dev_attr_event_vrd_warn_fault.attr,
5534a4a4e9eSQuan Nguyen 	&dev_attr_event_vrd_hot.attr,
5544a4a4e9eSQuan Nguyen 	&dev_attr_event_dimm_hot.attr,
5554a4a4e9eSQuan Nguyen 	&dev_attr_event_dimm_2x_refresh.attr,
556b0f64c80SQuan Nguyen 	&dev_attr_event_dimm0_syndrome.attr,
557*c2c99326SQuan Nguyen 	&dev_attr_event_dimm1_syndrome.attr,
558*c2c99326SQuan Nguyen 	&dev_attr_event_dimm2_syndrome.attr,
559*c2c99326SQuan Nguyen 	&dev_attr_event_dimm3_syndrome.attr,
560*c2c99326SQuan Nguyen 	&dev_attr_event_dimm4_syndrome.attr,
561*c2c99326SQuan Nguyen 	&dev_attr_event_dimm5_syndrome.attr,
562*c2c99326SQuan Nguyen 	&dev_attr_event_dimm6_syndrome.attr,
563*c2c99326SQuan Nguyen 	&dev_attr_event_dimm7_syndrome.attr,
564*c2c99326SQuan Nguyen 	&dev_attr_event_dimm8_syndrome.attr,
565*c2c99326SQuan Nguyen 	&dev_attr_event_dimm9_syndrome.attr,
566*c2c99326SQuan Nguyen 	&dev_attr_event_dimm10_syndrome.attr,
567*c2c99326SQuan Nguyen 	&dev_attr_event_dimm11_syndrome.attr,
568*c2c99326SQuan Nguyen 	&dev_attr_event_dimm12_syndrome.attr,
569*c2c99326SQuan Nguyen 	&dev_attr_event_dimm13_syndrome.attr,
570*c2c99326SQuan Nguyen 	&dev_attr_event_dimm14_syndrome.attr,
571*c2c99326SQuan Nguyen 	&dev_attr_event_dimm15_syndrome.attr,
572*c2c99326SQuan Nguyen 	NULL
5734a4a4e9eSQuan Nguyen };
5744a4a4e9eSQuan Nguyen 
5754a4a4e9eSQuan Nguyen ATTRIBUTE_GROUPS(smpro_errmon);
5764a4a4e9eSQuan Nguyen 
smpro_errmon_probe(struct platform_device * pdev)5774a4a4e9eSQuan Nguyen static int smpro_errmon_probe(struct platform_device *pdev)
5784a4a4e9eSQuan Nguyen {
5794a4a4e9eSQuan Nguyen 	struct smpro_errmon *errmon;
5804a4a4e9eSQuan Nguyen 
5814a4a4e9eSQuan Nguyen 	errmon = devm_kzalloc(&pdev->dev, sizeof(struct smpro_errmon), GFP_KERNEL);
5824a4a4e9eSQuan Nguyen 	if (!errmon)
5834a4a4e9eSQuan Nguyen 		return -ENOMEM;
5844a4a4e9eSQuan Nguyen 
5854a4a4e9eSQuan Nguyen 	platform_set_drvdata(pdev, errmon);
5864a4a4e9eSQuan Nguyen 
5874a4a4e9eSQuan Nguyen 	errmon->regmap = dev_get_regmap(pdev->dev.parent, NULL);
5884a4a4e9eSQuan Nguyen 	if (!errmon->regmap)
5894a4a4e9eSQuan Nguyen 		return -ENODEV;
5904a4a4e9eSQuan Nguyen 
5914a4a4e9eSQuan Nguyen 	return 0;
5924a4a4e9eSQuan Nguyen }
5934a4a4e9eSQuan Nguyen 
5944a4a4e9eSQuan Nguyen static struct platform_driver smpro_errmon_driver = {
5954a4a4e9eSQuan Nguyen 	.probe          = smpro_errmon_probe,
5964a4a4e9eSQuan Nguyen 	.driver = {
5974a4a4e9eSQuan Nguyen 		.name   = "smpro-errmon",
5984a4a4e9eSQuan Nguyen 		.dev_groups = smpro_errmon_groups,
5994a4a4e9eSQuan Nguyen 	},
6004a4a4e9eSQuan Nguyen };
6014a4a4e9eSQuan Nguyen 
6024a4a4e9eSQuan Nguyen module_platform_driver(smpro_errmon_driver);
6034a4a4e9eSQuan Nguyen 
6044a4a4e9eSQuan Nguyen MODULE_AUTHOR("Tung Nguyen <tung.nguyen@amperecomputing.com>");
6054a4a4e9eSQuan Nguyen MODULE_AUTHOR("Thinh Pham <thinh.pham@amperecomputing.com>");
6064a4a4e9eSQuan Nguyen MODULE_AUTHOR("Hoang Nguyen <hnguyen@amperecomputing.com>");
6074a4a4e9eSQuan Nguyen MODULE_AUTHOR("Thu Nguyen <thu@os.amperecomputing.com>");
6084a4a4e9eSQuan Nguyen MODULE_AUTHOR("Quan Nguyen <quan@os.amperecomputing.com>");
6094a4a4e9eSQuan Nguyen MODULE_DESCRIPTION("Ampere Altra SMpro driver");
6104a4a4e9eSQuan Nguyen MODULE_LICENSE("GPL");
6114a4a4e9eSQuan Nguyen