162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Ampere Computing SoC's SMpro Error Monitoring Driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) 2022, Ampere Computing LLC
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/mod_devicetable.h>
1062306a36Sopenharmony_ci#include <linux/module.h>
1162306a36Sopenharmony_ci#include <linux/platform_device.h>
1262306a36Sopenharmony_ci#include <linux/regmap.h>
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci/* GPI RAS Error Registers */
1562306a36Sopenharmony_ci#define GPI_RAS_ERR		0x7E
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci/* Core and L2C Error Registers */
1862306a36Sopenharmony_ci#define CORE_CE_ERR_CNT		0x80
1962306a36Sopenharmony_ci#define CORE_CE_ERR_LEN		0x81
2062306a36Sopenharmony_ci#define CORE_CE_ERR_DATA	0x82
2162306a36Sopenharmony_ci#define CORE_UE_ERR_CNT		0x83
2262306a36Sopenharmony_ci#define CORE_UE_ERR_LEN		0x84
2362306a36Sopenharmony_ci#define CORE_UE_ERR_DATA	0x85
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci/* Memory Error Registers */
2662306a36Sopenharmony_ci#define MEM_CE_ERR_CNT		0x90
2762306a36Sopenharmony_ci#define MEM_CE_ERR_LEN		0x91
2862306a36Sopenharmony_ci#define MEM_CE_ERR_DATA		0x92
2962306a36Sopenharmony_ci#define MEM_UE_ERR_CNT		0x93
3062306a36Sopenharmony_ci#define MEM_UE_ERR_LEN		0x94
3162306a36Sopenharmony_ci#define MEM_UE_ERR_DATA		0x95
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci/* RAS Error/Warning Registers */
3462306a36Sopenharmony_ci#define ERR_SMPRO_TYPE		0xA0
3562306a36Sopenharmony_ci#define ERR_PMPRO_TYPE		0xA1
3662306a36Sopenharmony_ci#define ERR_SMPRO_INFO_LO	0xA2
3762306a36Sopenharmony_ci#define ERR_SMPRO_INFO_HI	0xA3
3862306a36Sopenharmony_ci#define ERR_SMPRO_DATA_LO	0xA4
3962306a36Sopenharmony_ci#define ERR_SMPRO_DATA_HI	0xA5
4062306a36Sopenharmony_ci#define WARN_SMPRO_INFO_LO	0xAA
4162306a36Sopenharmony_ci#define WARN_SMPRO_INFO_HI	0xAB
4262306a36Sopenharmony_ci#define ERR_PMPRO_INFO_LO	0xA6
4362306a36Sopenharmony_ci#define ERR_PMPRO_INFO_HI	0xA7
4462306a36Sopenharmony_ci#define ERR_PMPRO_DATA_LO	0xA8
4562306a36Sopenharmony_ci#define ERR_PMPRO_DATA_HI	0xA9
4662306a36Sopenharmony_ci#define WARN_PMPRO_INFO_LO	0xAC
4762306a36Sopenharmony_ci#define WARN_PMPRO_INFO_HI	0xAD
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci/* Boot Stage Register */
5062306a36Sopenharmony_ci#define BOOTSTAGE		0xB0
5162306a36Sopenharmony_ci#define DIMM_SYNDROME_SEL	0xB4
5262306a36Sopenharmony_ci#define DIMM_SYNDROME_ERR	0xB5
5362306a36Sopenharmony_ci#define DIMM_SYNDROME_STAGE	4
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci/* PCIE Error Registers */
5662306a36Sopenharmony_ci#define PCIE_CE_ERR_CNT		0xC0
5762306a36Sopenharmony_ci#define PCIE_CE_ERR_LEN		0xC1
5862306a36Sopenharmony_ci#define PCIE_CE_ERR_DATA	0xC2
5962306a36Sopenharmony_ci#define PCIE_UE_ERR_CNT		0xC3
6062306a36Sopenharmony_ci#define PCIE_UE_ERR_LEN		0xC4
6162306a36Sopenharmony_ci#define PCIE_UE_ERR_DATA	0xC5
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci/* Other Error Registers */
6462306a36Sopenharmony_ci#define OTHER_CE_ERR_CNT	0xD0
6562306a36Sopenharmony_ci#define OTHER_CE_ERR_LEN	0xD1
6662306a36Sopenharmony_ci#define OTHER_CE_ERR_DATA	0xD2
6762306a36Sopenharmony_ci#define OTHER_UE_ERR_CNT	0xD8
6862306a36Sopenharmony_ci#define OTHER_UE_ERR_LEN	0xD9
6962306a36Sopenharmony_ci#define OTHER_UE_ERR_DATA	0xDA
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci/* Event Data Registers */
7262306a36Sopenharmony_ci#define VRD_WARN_FAULT_EVENT_DATA	0x78
7362306a36Sopenharmony_ci#define VRD_HOT_EVENT_DATA		0x79
7462306a36Sopenharmony_ci#define DIMM_HOT_EVENT_DATA		0x7A
7562306a36Sopenharmony_ci#define DIMM_2X_REFRESH_EVENT_DATA	0x96
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci#define MAX_READ_BLOCK_LENGTH	48
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci#define RAS_SMPRO_ERR		0
8062306a36Sopenharmony_ci#define RAS_PMPRO_ERR		1
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_cienum RAS_48BYTES_ERR_TYPES {
8362306a36Sopenharmony_ci	CORE_CE_ERR,
8462306a36Sopenharmony_ci	CORE_UE_ERR,
8562306a36Sopenharmony_ci	MEM_CE_ERR,
8662306a36Sopenharmony_ci	MEM_UE_ERR,
8762306a36Sopenharmony_ci	PCIE_CE_ERR,
8862306a36Sopenharmony_ci	PCIE_UE_ERR,
8962306a36Sopenharmony_ci	OTHER_CE_ERR,
9062306a36Sopenharmony_ci	OTHER_UE_ERR,
9162306a36Sopenharmony_ci	NUM_48BYTES_ERR_TYPE,
9262306a36Sopenharmony_ci};
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_cistruct smpro_error_hdr {
9562306a36Sopenharmony_ci	u8 count;	/* Number of the RAS errors */
9662306a36Sopenharmony_ci	u8 len;		/* Number of data bytes */
9762306a36Sopenharmony_ci	u8 data;	/* Start of 48-byte data */
9862306a36Sopenharmony_ci	u8 max_cnt;	/* Max num of errors */
9962306a36Sopenharmony_ci};
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci/*
10262306a36Sopenharmony_ci * Included Address of registers to get Count, Length of data and Data
10362306a36Sopenharmony_ci * of the 48 bytes error data
10462306a36Sopenharmony_ci */
10562306a36Sopenharmony_cistatic struct smpro_error_hdr smpro_error_table[] = {
10662306a36Sopenharmony_ci	[CORE_CE_ERR] = {
10762306a36Sopenharmony_ci		.count = CORE_CE_ERR_CNT,
10862306a36Sopenharmony_ci		.len = CORE_CE_ERR_LEN,
10962306a36Sopenharmony_ci		.data = CORE_CE_ERR_DATA,
11062306a36Sopenharmony_ci		.max_cnt = 32
11162306a36Sopenharmony_ci	},
11262306a36Sopenharmony_ci	[CORE_UE_ERR] = {
11362306a36Sopenharmony_ci		.count = CORE_UE_ERR_CNT,
11462306a36Sopenharmony_ci		.len = CORE_UE_ERR_LEN,
11562306a36Sopenharmony_ci		.data = CORE_UE_ERR_DATA,
11662306a36Sopenharmony_ci		.max_cnt = 32
11762306a36Sopenharmony_ci	},
11862306a36Sopenharmony_ci	[MEM_CE_ERR] = {
11962306a36Sopenharmony_ci		.count = MEM_CE_ERR_CNT,
12062306a36Sopenharmony_ci		.len = MEM_CE_ERR_LEN,
12162306a36Sopenharmony_ci		.data = MEM_CE_ERR_DATA,
12262306a36Sopenharmony_ci		.max_cnt = 16
12362306a36Sopenharmony_ci	},
12462306a36Sopenharmony_ci	[MEM_UE_ERR] = {
12562306a36Sopenharmony_ci		.count = MEM_UE_ERR_CNT,
12662306a36Sopenharmony_ci		.len = MEM_UE_ERR_LEN,
12762306a36Sopenharmony_ci		.data = MEM_UE_ERR_DATA,
12862306a36Sopenharmony_ci		.max_cnt = 16
12962306a36Sopenharmony_ci	},
13062306a36Sopenharmony_ci	[PCIE_CE_ERR] = {
13162306a36Sopenharmony_ci		.count = PCIE_CE_ERR_CNT,
13262306a36Sopenharmony_ci		.len = PCIE_CE_ERR_LEN,
13362306a36Sopenharmony_ci		.data = PCIE_CE_ERR_DATA,
13462306a36Sopenharmony_ci		.max_cnt = 96
13562306a36Sopenharmony_ci	},
13662306a36Sopenharmony_ci	[PCIE_UE_ERR] = {
13762306a36Sopenharmony_ci		.count = PCIE_UE_ERR_CNT,
13862306a36Sopenharmony_ci		.len = PCIE_UE_ERR_LEN,
13962306a36Sopenharmony_ci		.data = PCIE_UE_ERR_DATA,
14062306a36Sopenharmony_ci		.max_cnt = 96
14162306a36Sopenharmony_ci	},
14262306a36Sopenharmony_ci	[OTHER_CE_ERR] = {
14362306a36Sopenharmony_ci		.count = OTHER_CE_ERR_CNT,
14462306a36Sopenharmony_ci		.len = OTHER_CE_ERR_LEN,
14562306a36Sopenharmony_ci		.data = OTHER_CE_ERR_DATA,
14662306a36Sopenharmony_ci		.max_cnt = 8
14762306a36Sopenharmony_ci	},
14862306a36Sopenharmony_ci	[OTHER_UE_ERR] = {
14962306a36Sopenharmony_ci		.count = OTHER_UE_ERR_CNT,
15062306a36Sopenharmony_ci		.len = OTHER_UE_ERR_LEN,
15162306a36Sopenharmony_ci		.data = OTHER_UE_ERR_DATA,
15262306a36Sopenharmony_ci		.max_cnt = 8
15362306a36Sopenharmony_ci	},
15462306a36Sopenharmony_ci};
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci/*
15762306a36Sopenharmony_ci * List of SCP registers which are used to get
15862306a36Sopenharmony_ci * one type of RAS Internal errors.
15962306a36Sopenharmony_ci */
16062306a36Sopenharmony_cistruct smpro_int_error_hdr {
16162306a36Sopenharmony_ci	u8 type;
16262306a36Sopenharmony_ci	u8 info_l;
16362306a36Sopenharmony_ci	u8 info_h;
16462306a36Sopenharmony_ci	u8 data_l;
16562306a36Sopenharmony_ci	u8 data_h;
16662306a36Sopenharmony_ci	u8 warn_l;
16762306a36Sopenharmony_ci	u8 warn_h;
16862306a36Sopenharmony_ci};
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_cistatic struct smpro_int_error_hdr list_smpro_int_error_hdr[] = {
17162306a36Sopenharmony_ci	[RAS_SMPRO_ERR] = {
17262306a36Sopenharmony_ci		.type = ERR_SMPRO_TYPE,
17362306a36Sopenharmony_ci		.info_l = ERR_SMPRO_INFO_LO,
17462306a36Sopenharmony_ci		.info_h = ERR_SMPRO_INFO_HI,
17562306a36Sopenharmony_ci		.data_l = ERR_SMPRO_DATA_LO,
17662306a36Sopenharmony_ci		.data_h = ERR_SMPRO_DATA_HI,
17762306a36Sopenharmony_ci		.warn_l = WARN_SMPRO_INFO_LO,
17862306a36Sopenharmony_ci		.warn_h = WARN_SMPRO_INFO_HI,
17962306a36Sopenharmony_ci	},
18062306a36Sopenharmony_ci	[RAS_PMPRO_ERR] = {
18162306a36Sopenharmony_ci		.type = ERR_PMPRO_TYPE,
18262306a36Sopenharmony_ci		.info_l = ERR_PMPRO_INFO_LO,
18362306a36Sopenharmony_ci		.info_h = ERR_PMPRO_INFO_HI,
18462306a36Sopenharmony_ci		.data_l = ERR_PMPRO_DATA_LO,
18562306a36Sopenharmony_ci		.data_h = ERR_PMPRO_DATA_HI,
18662306a36Sopenharmony_ci		.warn_l = WARN_PMPRO_INFO_LO,
18762306a36Sopenharmony_ci		.warn_h = WARN_PMPRO_INFO_HI,
18862306a36Sopenharmony_ci	},
18962306a36Sopenharmony_ci};
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_cistruct smpro_errmon {
19262306a36Sopenharmony_ci	struct regmap *regmap;
19362306a36Sopenharmony_ci};
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_cienum EVENT_TYPES {
19662306a36Sopenharmony_ci	VRD_WARN_FAULT_EVENT,
19762306a36Sopenharmony_ci	VRD_HOT_EVENT,
19862306a36Sopenharmony_ci	DIMM_HOT_EVENT,
19962306a36Sopenharmony_ci	DIMM_2X_REFRESH_EVENT,
20062306a36Sopenharmony_ci	NUM_EVENTS_TYPE,
20162306a36Sopenharmony_ci};
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci/* Included Address of event source and data registers */
20462306a36Sopenharmony_cistatic u8 smpro_event_table[NUM_EVENTS_TYPE] = {
20562306a36Sopenharmony_ci	VRD_WARN_FAULT_EVENT_DATA,
20662306a36Sopenharmony_ci	VRD_HOT_EVENT_DATA,
20762306a36Sopenharmony_ci	DIMM_HOT_EVENT_DATA,
20862306a36Sopenharmony_ci	DIMM_2X_REFRESH_EVENT_DATA,
20962306a36Sopenharmony_ci};
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_cistatic ssize_t smpro_event_data_read(struct device *dev,
21262306a36Sopenharmony_ci				     struct device_attribute *da, char *buf,
21362306a36Sopenharmony_ci				     int channel)
21462306a36Sopenharmony_ci{
21562306a36Sopenharmony_ci	struct smpro_errmon *errmon = dev_get_drvdata(dev);
21662306a36Sopenharmony_ci	s32 event_data;
21762306a36Sopenharmony_ci	int ret;
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	ret = regmap_read(errmon->regmap, smpro_event_table[channel], &event_data);
22062306a36Sopenharmony_ci	if (ret)
22162306a36Sopenharmony_ci		return ret;
22262306a36Sopenharmony_ci	/* Clear event after read */
22362306a36Sopenharmony_ci	if (event_data != 0)
22462306a36Sopenharmony_ci		regmap_write(errmon->regmap, smpro_event_table[channel], event_data);
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	return sysfs_emit(buf, "%04x\n", event_data);
22762306a36Sopenharmony_ci}
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_cistatic ssize_t smpro_overflow_data_read(struct device *dev, struct device_attribute *da,
23062306a36Sopenharmony_ci					char *buf, int channel)
23162306a36Sopenharmony_ci{
23262306a36Sopenharmony_ci	struct smpro_errmon *errmon = dev_get_drvdata(dev);
23362306a36Sopenharmony_ci	struct smpro_error_hdr *err_info;
23462306a36Sopenharmony_ci	s32 err_count;
23562306a36Sopenharmony_ci	int ret;
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	err_info = &smpro_error_table[channel];
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	ret = regmap_read(errmon->regmap, err_info->count, &err_count);
24062306a36Sopenharmony_ci	if (ret)
24162306a36Sopenharmony_ci		return ret;
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	/* Bit 8 indicates the overflow status */
24462306a36Sopenharmony_ci	return sysfs_emit(buf, "%d\n", (err_count & BIT(8)) ? 1 : 0);
24562306a36Sopenharmony_ci}
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_cistatic ssize_t smpro_error_data_read(struct device *dev, struct device_attribute *da,
24862306a36Sopenharmony_ci				     char *buf, int channel)
24962306a36Sopenharmony_ci{
25062306a36Sopenharmony_ci	struct smpro_errmon *errmon = dev_get_drvdata(dev);
25162306a36Sopenharmony_ci	unsigned char err_data[MAX_READ_BLOCK_LENGTH];
25262306a36Sopenharmony_ci	struct smpro_error_hdr *err_info;
25362306a36Sopenharmony_ci	s32 err_count, err_length;
25462306a36Sopenharmony_ci	int ret;
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	err_info = &smpro_error_table[channel];
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	ret = regmap_read(errmon->regmap, err_info->count, &err_count);
25962306a36Sopenharmony_ci	/* Error count is the low byte */
26062306a36Sopenharmony_ci	err_count &= 0xff;
26162306a36Sopenharmony_ci	if (ret || !err_count || err_count > err_info->max_cnt)
26262306a36Sopenharmony_ci		return ret;
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	ret = regmap_read(errmon->regmap, err_info->len, &err_length);
26562306a36Sopenharmony_ci	if (ret || err_length <= 0)
26662306a36Sopenharmony_ci		return ret;
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	if (err_length > MAX_READ_BLOCK_LENGTH)
26962306a36Sopenharmony_ci		err_length = MAX_READ_BLOCK_LENGTH;
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	memset(err_data, 0x00, MAX_READ_BLOCK_LENGTH);
27262306a36Sopenharmony_ci	ret = regmap_noinc_read(errmon->regmap, err_info->data, err_data, err_length);
27362306a36Sopenharmony_ci	if (ret < 0)
27462306a36Sopenharmony_ci		return ret;
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	/* clear the error */
27762306a36Sopenharmony_ci	ret = regmap_write(errmon->regmap, err_info->count, 0x100);
27862306a36Sopenharmony_ci	if (ret)
27962306a36Sopenharmony_ci		return ret;
28062306a36Sopenharmony_ci	/*
28162306a36Sopenharmony_ci	 * The output of Core/Memory/PCIe/Others UE/CE errors follows the format
28262306a36Sopenharmony_ci	 * specified in section 5.8.1 CE/UE Error Data record in
28362306a36Sopenharmony_ci	 * Altra SOC BMC Interface specification.
28462306a36Sopenharmony_ci	 */
28562306a36Sopenharmony_ci	return sysfs_emit(buf, "%*phN\n", MAX_READ_BLOCK_LENGTH, err_data);
28662306a36Sopenharmony_ci}
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci/*
28962306a36Sopenharmony_ci * Output format:
29062306a36Sopenharmony_ci * <4-byte hex value of error info><4-byte hex value of error extensive data>
29162306a36Sopenharmony_ci * Where:
29262306a36Sopenharmony_ci *   + error info : The error information
29362306a36Sopenharmony_ci *   + error data : Extensive data (32 bits)
29462306a36Sopenharmony_ci * Reference to section 5.10 RAS Internal Error Register Definition in
29562306a36Sopenharmony_ci * Altra SOC BMC Interface specification
29662306a36Sopenharmony_ci */
29762306a36Sopenharmony_cistatic ssize_t smpro_internal_err_read(struct device *dev, struct device_attribute *da,
29862306a36Sopenharmony_ci				       char *buf, int channel)
29962306a36Sopenharmony_ci{
30062306a36Sopenharmony_ci	struct smpro_errmon *errmon = dev_get_drvdata(dev);
30162306a36Sopenharmony_ci	struct smpro_int_error_hdr *err_info;
30262306a36Sopenharmony_ci	unsigned int err[4] = { 0 };
30362306a36Sopenharmony_ci	unsigned int err_type;
30462306a36Sopenharmony_ci	unsigned int val;
30562306a36Sopenharmony_ci	int ret;
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	/* read error status */
30862306a36Sopenharmony_ci	ret = regmap_read(errmon->regmap, GPI_RAS_ERR, &val);
30962306a36Sopenharmony_ci	if (ret)
31062306a36Sopenharmony_ci		return ret;
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	if ((channel == RAS_SMPRO_ERR && !(val & BIT(0))) ||
31362306a36Sopenharmony_ci	    (channel == RAS_PMPRO_ERR && !(val & BIT(1))))
31462306a36Sopenharmony_ci		return 0;
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	err_info = &list_smpro_int_error_hdr[channel];
31762306a36Sopenharmony_ci	ret = regmap_read(errmon->regmap, err_info->type, &val);
31862306a36Sopenharmony_ci	if (ret)
31962306a36Sopenharmony_ci		return ret;
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	err_type = (val & BIT(1)) ? BIT(1) :
32262306a36Sopenharmony_ci		   (val & BIT(2)) ? BIT(2) : 0;
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	if (!err_type)
32562306a36Sopenharmony_ci		return 0;
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	ret = regmap_read(errmon->regmap, err_info->info_l, err + 1);
32862306a36Sopenharmony_ci	if (ret)
32962306a36Sopenharmony_ci		return ret;
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	ret = regmap_read(errmon->regmap, err_info->info_h, err);
33262306a36Sopenharmony_ci	if (ret)
33362306a36Sopenharmony_ci		return ret;
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	if (err_type & BIT(2)) {
33662306a36Sopenharmony_ci		/* Error with data type */
33762306a36Sopenharmony_ci		ret = regmap_read(errmon->regmap, err_info->data_l, err + 3);
33862306a36Sopenharmony_ci		if (ret)
33962306a36Sopenharmony_ci			return ret;
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci		ret = regmap_read(errmon->regmap, err_info->data_h, err + 2);
34262306a36Sopenharmony_ci		if (ret)
34362306a36Sopenharmony_ci			return ret;
34462306a36Sopenharmony_ci	}
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	/* clear the read errors */
34762306a36Sopenharmony_ci	ret = regmap_write(errmon->regmap, err_info->type, err_type);
34862306a36Sopenharmony_ci	if (ret)
34962306a36Sopenharmony_ci		return ret;
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	return sysfs_emit(buf, "%*phN\n", (int)sizeof(err), err);
35262306a36Sopenharmony_ci}
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci/*
35562306a36Sopenharmony_ci * Output format:
35662306a36Sopenharmony_ci * <4-byte hex value of warining info>
35762306a36Sopenharmony_ci * Reference to section 5.10 RAS Internal Error Register Definition in
35862306a36Sopenharmony_ci * Altra SOC BMC Interface specification
35962306a36Sopenharmony_ci */
36062306a36Sopenharmony_cistatic ssize_t smpro_internal_warn_read(struct device *dev, struct device_attribute *da,
36162306a36Sopenharmony_ci					char *buf, int channel)
36262306a36Sopenharmony_ci{
36362306a36Sopenharmony_ci	struct smpro_errmon *errmon = dev_get_drvdata(dev);
36462306a36Sopenharmony_ci	struct smpro_int_error_hdr *err_info;
36562306a36Sopenharmony_ci	unsigned int warn[2] = { 0 };
36662306a36Sopenharmony_ci	unsigned int val;
36762306a36Sopenharmony_ci	int ret;
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	/* read error status */
37062306a36Sopenharmony_ci	ret = regmap_read(errmon->regmap, GPI_RAS_ERR, &val);
37162306a36Sopenharmony_ci	if (ret)
37262306a36Sopenharmony_ci		return ret;
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	if ((channel == RAS_SMPRO_ERR && !(val & BIT(0))) ||
37562306a36Sopenharmony_ci	    (channel == RAS_PMPRO_ERR && !(val & BIT(1))))
37662306a36Sopenharmony_ci		return 0;
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	err_info = &list_smpro_int_error_hdr[channel];
37962306a36Sopenharmony_ci	ret = regmap_read(errmon->regmap, err_info->type, &val);
38062306a36Sopenharmony_ci	if (ret)
38162306a36Sopenharmony_ci		return ret;
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	if (!(val & BIT(0)))
38462306a36Sopenharmony_ci		return 0;
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	ret = regmap_read(errmon->regmap, err_info->warn_l, warn + 1);
38762306a36Sopenharmony_ci	if (ret)
38862306a36Sopenharmony_ci		return ret;
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	ret = regmap_read(errmon->regmap, err_info->warn_h, warn);
39162306a36Sopenharmony_ci	if (ret)
39262306a36Sopenharmony_ci		return ret;
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	/* clear the warning */
39562306a36Sopenharmony_ci	ret = regmap_write(errmon->regmap, err_info->type, BIT(0));
39662306a36Sopenharmony_ci	if (ret)
39762306a36Sopenharmony_ci		return ret;
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	return sysfs_emit(buf, "%*phN\n", (int)sizeof(warn), warn);
40062306a36Sopenharmony_ci}
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci#define ERROR_OVERFLOW_RO(_error, _index) \
40362306a36Sopenharmony_ci	static ssize_t overflow_##_error##_show(struct device *dev,            \
40462306a36Sopenharmony_ci						struct device_attribute *da,   \
40562306a36Sopenharmony_ci						char *buf)                     \
40662306a36Sopenharmony_ci	{                                                                      \
40762306a36Sopenharmony_ci		return smpro_overflow_data_read(dev, da, buf, _index);         \
40862306a36Sopenharmony_ci	}                                                                      \
40962306a36Sopenharmony_ci	static DEVICE_ATTR_RO(overflow_##_error)
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ciERROR_OVERFLOW_RO(core_ce, CORE_CE_ERR);
41262306a36Sopenharmony_ciERROR_OVERFLOW_RO(core_ue, CORE_UE_ERR);
41362306a36Sopenharmony_ciERROR_OVERFLOW_RO(mem_ce, MEM_CE_ERR);
41462306a36Sopenharmony_ciERROR_OVERFLOW_RO(mem_ue, MEM_UE_ERR);
41562306a36Sopenharmony_ciERROR_OVERFLOW_RO(pcie_ce, PCIE_CE_ERR);
41662306a36Sopenharmony_ciERROR_OVERFLOW_RO(pcie_ue, PCIE_UE_ERR);
41762306a36Sopenharmony_ciERROR_OVERFLOW_RO(other_ce, OTHER_CE_ERR);
41862306a36Sopenharmony_ciERROR_OVERFLOW_RO(other_ue, OTHER_UE_ERR);
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci#define ERROR_RO(_error, _index) \
42162306a36Sopenharmony_ci	static ssize_t error_##_error##_show(struct device *dev,            \
42262306a36Sopenharmony_ci					     struct device_attribute *da,   \
42362306a36Sopenharmony_ci					     char *buf)                     \
42462306a36Sopenharmony_ci	{                                                                   \
42562306a36Sopenharmony_ci		return smpro_error_data_read(dev, da, buf, _index);         \
42662306a36Sopenharmony_ci	}                                                                   \
42762306a36Sopenharmony_ci	static DEVICE_ATTR_RO(error_##_error)
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ciERROR_RO(core_ce, CORE_CE_ERR);
43062306a36Sopenharmony_ciERROR_RO(core_ue, CORE_UE_ERR);
43162306a36Sopenharmony_ciERROR_RO(mem_ce, MEM_CE_ERR);
43262306a36Sopenharmony_ciERROR_RO(mem_ue, MEM_UE_ERR);
43362306a36Sopenharmony_ciERROR_RO(pcie_ce, PCIE_CE_ERR);
43462306a36Sopenharmony_ciERROR_RO(pcie_ue, PCIE_UE_ERR);
43562306a36Sopenharmony_ciERROR_RO(other_ce, OTHER_CE_ERR);
43662306a36Sopenharmony_ciERROR_RO(other_ue, OTHER_UE_ERR);
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_cistatic ssize_t error_smpro_show(struct device *dev, struct device_attribute *da, char *buf)
43962306a36Sopenharmony_ci{
44062306a36Sopenharmony_ci	return smpro_internal_err_read(dev, da, buf, RAS_SMPRO_ERR);
44162306a36Sopenharmony_ci}
44262306a36Sopenharmony_cistatic DEVICE_ATTR_RO(error_smpro);
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_cistatic ssize_t error_pmpro_show(struct device *dev, struct device_attribute *da, char *buf)
44562306a36Sopenharmony_ci{
44662306a36Sopenharmony_ci	return smpro_internal_err_read(dev, da, buf, RAS_PMPRO_ERR);
44762306a36Sopenharmony_ci}
44862306a36Sopenharmony_cistatic DEVICE_ATTR_RO(error_pmpro);
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_cistatic ssize_t warn_smpro_show(struct device *dev, struct device_attribute *da, char *buf)
45162306a36Sopenharmony_ci{
45262306a36Sopenharmony_ci	return smpro_internal_warn_read(dev, da, buf, RAS_SMPRO_ERR);
45362306a36Sopenharmony_ci}
45462306a36Sopenharmony_cistatic DEVICE_ATTR_RO(warn_smpro);
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_cistatic ssize_t warn_pmpro_show(struct device *dev, struct device_attribute *da, char *buf)
45762306a36Sopenharmony_ci{
45862306a36Sopenharmony_ci	return smpro_internal_warn_read(dev, da, buf, RAS_PMPRO_ERR);
45962306a36Sopenharmony_ci}
46062306a36Sopenharmony_cistatic DEVICE_ATTR_RO(warn_pmpro);
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci#define EVENT_RO(_event, _index) \
46362306a36Sopenharmony_ci	static ssize_t event_##_event##_show(struct device *dev,            \
46462306a36Sopenharmony_ci					     struct device_attribute *da,   \
46562306a36Sopenharmony_ci					     char *buf)                     \
46662306a36Sopenharmony_ci	{                                                                   \
46762306a36Sopenharmony_ci		return smpro_event_data_read(dev, da, buf, _index);         \
46862306a36Sopenharmony_ci	}                                                                   \
46962306a36Sopenharmony_ci	static DEVICE_ATTR_RO(event_##_event)
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ciEVENT_RO(vrd_warn_fault, VRD_WARN_FAULT_EVENT);
47262306a36Sopenharmony_ciEVENT_RO(vrd_hot, VRD_HOT_EVENT);
47362306a36Sopenharmony_ciEVENT_RO(dimm_hot, DIMM_HOT_EVENT);
47462306a36Sopenharmony_ciEVENT_RO(dimm_2x_refresh, DIMM_2X_REFRESH_EVENT);
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_cistatic ssize_t smpro_dimm_syndrome_read(struct device *dev, struct device_attribute *da,
47762306a36Sopenharmony_ci					char *buf, unsigned int slot)
47862306a36Sopenharmony_ci{
47962306a36Sopenharmony_ci	struct smpro_errmon *errmon = dev_get_drvdata(dev);
48062306a36Sopenharmony_ci	unsigned int data;
48162306a36Sopenharmony_ci	int ret;
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci	ret = regmap_read(errmon->regmap, BOOTSTAGE, &data);
48462306a36Sopenharmony_ci	if (ret)
48562306a36Sopenharmony_ci		return ret;
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	/* check for valid stage */
48862306a36Sopenharmony_ci	data = (data >> 8) & 0xff;
48962306a36Sopenharmony_ci	if (data != DIMM_SYNDROME_STAGE)
49062306a36Sopenharmony_ci		return ret;
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	/* Write the slot ID to retrieve Error Syndrome */
49362306a36Sopenharmony_ci	ret = regmap_write(errmon->regmap, DIMM_SYNDROME_SEL, slot);
49462306a36Sopenharmony_ci	if (ret)
49562306a36Sopenharmony_ci		return ret;
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	/* Read the Syndrome error */
49862306a36Sopenharmony_ci	ret = regmap_read(errmon->regmap, DIMM_SYNDROME_ERR, &data);
49962306a36Sopenharmony_ci	if (ret || !data)
50062306a36Sopenharmony_ci		return ret;
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	return sysfs_emit(buf, "%04x\n", data);
50362306a36Sopenharmony_ci}
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci#define EVENT_DIMM_SYNDROME(_slot) \
50662306a36Sopenharmony_ci	static ssize_t event_dimm##_slot##_syndrome_show(struct device *dev,          \
50762306a36Sopenharmony_ci							 struct device_attribute *da, \
50862306a36Sopenharmony_ci							 char *buf)                   \
50962306a36Sopenharmony_ci	{                                                                             \
51062306a36Sopenharmony_ci		return smpro_dimm_syndrome_read(dev, da, buf, _slot);                 \
51162306a36Sopenharmony_ci	}                                                                             \
51262306a36Sopenharmony_ci	static DEVICE_ATTR_RO(event_dimm##_slot##_syndrome)
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ciEVENT_DIMM_SYNDROME(0);
51562306a36Sopenharmony_ciEVENT_DIMM_SYNDROME(1);
51662306a36Sopenharmony_ciEVENT_DIMM_SYNDROME(2);
51762306a36Sopenharmony_ciEVENT_DIMM_SYNDROME(3);
51862306a36Sopenharmony_ciEVENT_DIMM_SYNDROME(4);
51962306a36Sopenharmony_ciEVENT_DIMM_SYNDROME(5);
52062306a36Sopenharmony_ciEVENT_DIMM_SYNDROME(6);
52162306a36Sopenharmony_ciEVENT_DIMM_SYNDROME(7);
52262306a36Sopenharmony_ciEVENT_DIMM_SYNDROME(8);
52362306a36Sopenharmony_ciEVENT_DIMM_SYNDROME(9);
52462306a36Sopenharmony_ciEVENT_DIMM_SYNDROME(10);
52562306a36Sopenharmony_ciEVENT_DIMM_SYNDROME(11);
52662306a36Sopenharmony_ciEVENT_DIMM_SYNDROME(12);
52762306a36Sopenharmony_ciEVENT_DIMM_SYNDROME(13);
52862306a36Sopenharmony_ciEVENT_DIMM_SYNDROME(14);
52962306a36Sopenharmony_ciEVENT_DIMM_SYNDROME(15);
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_cistatic struct attribute *smpro_errmon_attrs[] = {
53262306a36Sopenharmony_ci	&dev_attr_overflow_core_ce.attr,
53362306a36Sopenharmony_ci	&dev_attr_overflow_core_ue.attr,
53462306a36Sopenharmony_ci	&dev_attr_overflow_mem_ce.attr,
53562306a36Sopenharmony_ci	&dev_attr_overflow_mem_ue.attr,
53662306a36Sopenharmony_ci	&dev_attr_overflow_pcie_ce.attr,
53762306a36Sopenharmony_ci	&dev_attr_overflow_pcie_ue.attr,
53862306a36Sopenharmony_ci	&dev_attr_overflow_other_ce.attr,
53962306a36Sopenharmony_ci	&dev_attr_overflow_other_ue.attr,
54062306a36Sopenharmony_ci	&dev_attr_error_core_ce.attr,
54162306a36Sopenharmony_ci	&dev_attr_error_core_ue.attr,
54262306a36Sopenharmony_ci	&dev_attr_error_mem_ce.attr,
54362306a36Sopenharmony_ci	&dev_attr_error_mem_ue.attr,
54462306a36Sopenharmony_ci	&dev_attr_error_pcie_ce.attr,
54562306a36Sopenharmony_ci	&dev_attr_error_pcie_ue.attr,
54662306a36Sopenharmony_ci	&dev_attr_error_other_ce.attr,
54762306a36Sopenharmony_ci	&dev_attr_error_other_ue.attr,
54862306a36Sopenharmony_ci	&dev_attr_error_smpro.attr,
54962306a36Sopenharmony_ci	&dev_attr_error_pmpro.attr,
55062306a36Sopenharmony_ci	&dev_attr_warn_smpro.attr,
55162306a36Sopenharmony_ci	&dev_attr_warn_pmpro.attr,
55262306a36Sopenharmony_ci	&dev_attr_event_vrd_warn_fault.attr,
55362306a36Sopenharmony_ci	&dev_attr_event_vrd_hot.attr,
55462306a36Sopenharmony_ci	&dev_attr_event_dimm_hot.attr,
55562306a36Sopenharmony_ci	&dev_attr_event_dimm_2x_refresh.attr,
55662306a36Sopenharmony_ci	&dev_attr_event_dimm0_syndrome.attr,
55762306a36Sopenharmony_ci	&dev_attr_event_dimm1_syndrome.attr,
55862306a36Sopenharmony_ci	&dev_attr_event_dimm2_syndrome.attr,
55962306a36Sopenharmony_ci	&dev_attr_event_dimm3_syndrome.attr,
56062306a36Sopenharmony_ci	&dev_attr_event_dimm4_syndrome.attr,
56162306a36Sopenharmony_ci	&dev_attr_event_dimm5_syndrome.attr,
56262306a36Sopenharmony_ci	&dev_attr_event_dimm6_syndrome.attr,
56362306a36Sopenharmony_ci	&dev_attr_event_dimm7_syndrome.attr,
56462306a36Sopenharmony_ci	&dev_attr_event_dimm8_syndrome.attr,
56562306a36Sopenharmony_ci	&dev_attr_event_dimm9_syndrome.attr,
56662306a36Sopenharmony_ci	&dev_attr_event_dimm10_syndrome.attr,
56762306a36Sopenharmony_ci	&dev_attr_event_dimm11_syndrome.attr,
56862306a36Sopenharmony_ci	&dev_attr_event_dimm12_syndrome.attr,
56962306a36Sopenharmony_ci	&dev_attr_event_dimm13_syndrome.attr,
57062306a36Sopenharmony_ci	&dev_attr_event_dimm14_syndrome.attr,
57162306a36Sopenharmony_ci	&dev_attr_event_dimm15_syndrome.attr,
57262306a36Sopenharmony_ci	NULL
57362306a36Sopenharmony_ci};
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ciATTRIBUTE_GROUPS(smpro_errmon);
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_cistatic int smpro_errmon_probe(struct platform_device *pdev)
57862306a36Sopenharmony_ci{
57962306a36Sopenharmony_ci	struct smpro_errmon *errmon;
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci	errmon = devm_kzalloc(&pdev->dev, sizeof(struct smpro_errmon), GFP_KERNEL);
58262306a36Sopenharmony_ci	if (!errmon)
58362306a36Sopenharmony_ci		return -ENOMEM;
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci	platform_set_drvdata(pdev, errmon);
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci	errmon->regmap = dev_get_regmap(pdev->dev.parent, NULL);
58862306a36Sopenharmony_ci	if (!errmon->regmap)
58962306a36Sopenharmony_ci		return -ENODEV;
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci	return 0;
59262306a36Sopenharmony_ci}
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_cistatic struct platform_driver smpro_errmon_driver = {
59562306a36Sopenharmony_ci	.probe          = smpro_errmon_probe,
59662306a36Sopenharmony_ci	.driver = {
59762306a36Sopenharmony_ci		.name   = "smpro-errmon",
59862306a36Sopenharmony_ci		.dev_groups = smpro_errmon_groups,
59962306a36Sopenharmony_ci	},
60062306a36Sopenharmony_ci};
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_cimodule_platform_driver(smpro_errmon_driver);
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ciMODULE_AUTHOR("Tung Nguyen <tung.nguyen@amperecomputing.com>");
60562306a36Sopenharmony_ciMODULE_AUTHOR("Thinh Pham <thinh.pham@amperecomputing.com>");
60662306a36Sopenharmony_ciMODULE_AUTHOR("Hoang Nguyen <hnguyen@amperecomputing.com>");
60762306a36Sopenharmony_ciMODULE_AUTHOR("Thu Nguyen <thu@os.amperecomputing.com>");
60862306a36Sopenharmony_ciMODULE_AUTHOR("Quan Nguyen <quan@os.amperecomputing.com>");
60962306a36Sopenharmony_ciMODULE_DESCRIPTION("Ampere Altra SMpro driver");
61062306a36Sopenharmony_ciMODULE_LICENSE("GPL");
611