On Fri, Apr 22, 2022 at 09:46:47AM +0700, Quan Nguyen wrote:
This commit adds Ampere's SMpro error monitor driver for monitoring
and reporting RAS-related errors as reported by SMpro co-processor
found on Ampere's Altra processor family.
Signed-off-by: Quan Nguyen <quan@xxxxxxxxxxxxxxxxxxxxxx>
---
Changes in v8:
+ Update wording for SMPRO_ERRMON on Kconfig file [Quan]
+ Avoid uninitialized variable use [kernel test robot]
+ Switch to use sysfs_emit() [Greg]
+ Make sysfs to return single value [Greg]
+ Change errors_* sysfs to error_* [Quan]
+ Add overflow_[core|mem|pcie|other]_[ce|ue] sysfs to report
overflow status of each type of HW errors [Quan]
+ Add some minor refactor [Quan]
Changes in v7:
+ Remove regmap_acquire/release_lock(), read_i2c_block_data() [Quan]
+ Use regmap_noinc_read() instead of errmon_read_block() [Quan]
+ Validate number of errors before read [Quan]
+ Fix wrong return type of *_show() function [kernel test robot]
+ Adjust patch order to avoid dependence with smpro-mfd [Lee Jones]
+ Use pointer instead of stack memory [Quan]
Changes in v6:
+ First introduced in v6 [Quan]
drivers/misc/Kconfig | 12 +
drivers/misc/Makefile | 1 +
drivers/misc/smpro-errmon.c | 477 ++++++++++++++++++++++++++++++++++++
3 files changed, 490 insertions(+)
create mode 100644 drivers/misc/smpro-errmon.c
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 41d2bb0ae23a..9fbe6797c440 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -176,6 +176,18 @@ config SGI_XP
this feature will allow for direct communication between SSIs
based on a network adapter and DMA messaging.
+config SMPRO_ERRMON
+ tristate "Ampere Computing SMPro error monitor driver"
+ depends on MFD_SMPRO || COMPILE_TEST
+ help
+ Say Y here to get support for the SMpro error monitor function
+ provided by Ampere Computing's Altra and Altra Max SoCs. Upon
+ loading, the driver creates sysfs files which can be use to gather
+ multiple HW error data reported via read and write system calls.
+
+ To compile this driver as a module, say M here. The driver will be
+ called smpro-errmon.
+
config CS5535_MFGPT
tristate "CS5535/CS5536 Geode Multi-Function General Purpose Timer (MFGPT) support"
depends on MFD_CS5535
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 70e800e9127f..483308a6e113 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -23,6 +23,7 @@ obj-$(CONFIG_ENCLOSURE_SERVICES) += enclosure.o
obj-$(CONFIG_KGDB_TESTS) += kgdbts.o
obj-$(CONFIG_SGI_XP) += sgi-xp/
obj-$(CONFIG_SGI_GRU) += sgi-gru/
+obj-$(CONFIG_SMPRO_ERRMON) += smpro-errmon.o
obj-$(CONFIG_CS5535_MFGPT) += cs5535-mfgpt.o
obj-$(CONFIG_GEHC_ACHC) += gehc-achc.o
obj-$(CONFIG_HP_ILO) += hpilo.o
diff --git a/drivers/misc/smpro-errmon.c b/drivers/misc/smpro-errmon.c
new file mode 100644
index 000000000000..df7d8fc4ff3f
--- /dev/null
+++ b/drivers/misc/smpro-errmon.c
@@ -0,0 +1,477 @@
+// SPDX-License-Identifier: GPL-2.0+
Are you sure you mean "or any later version"? I have to ask.
+static struct smpro_int_error_hdr list_smpro_int_error_hdr[2] = {
+ {
+ ERR_SMPRO_TYPE,
+ ERR_SMPRO_INFO_LO, ERR_SMPRO_INFO_HI,
+ ERR_SMPRO_DATA_LO, ERR_SMPRO_DATA_HI,
+ WARN_SMPRO_INFO_LO, WARN_SMPRO_INFO_HI
+ },
+ {
+ ERR_PMPRO_TYPE,
+ ERR_PMPRO_INFO_LO, ERR_PMPRO_INFO_HI,
+ ERR_PMPRO_DATA_LO, ERR_PMPRO_DATA_HI,
+ WARN_PMPRO_INFO_LO, WARN_PMPRO_INFO_HI
+ },
Odd indentation, checkpatch did not complain?
+static ssize_t smpro_event_data_read(struct device *dev,
+ struct device_attribute *da, char *buf,
+ int channel)
+{
+ struct smpro_errmon *errmon = dev_get_drvdata(dev);
+ s32 event_data;
+ int ret;
+
+ ret = regmap_read(errmon->regmap, smpro_event_table[channel], &event_data);
+ if (ret)
+ goto done;
Just return the error here.
+
+ ret = sysfs_emit(buf, "%02x%04x\n", channel, event_data);
sysfs rules are one value per file. You know this.
+ /* Clear event after read */
+ if (event_data != 0)
Why would it be 0? Isn't 0 a valid value?
+ regmap_write(errmon->regmap, smpro_event_table[channel], event_data);
+done:
+ return ret;
+}
+
+static ssize_t smpro_overflow_data_read(struct device *dev, struct device_attribute *da,
+ char *buf, int channel)
+{
+ struct smpro_errmon *errmon = dev_get_drvdata(dev);
+ struct smpro_error_hdr *err_info;
+ s32 err_count;
+ int ret;
+
+ err_info = &smpro_error_table[channel];
+
+ ret = regmap_read(errmon->regmap, err_info->err_count, &err_count);
+ if (ret)
+ return ret;
+
+ /* Bit 8 indicates the overflow status */
+ return sysfs_emit(buf, "%d\n", (err_count & BIT(8)) ? 1 : 0);
+}
+
+static ssize_t smpro_error_data_read(struct device *dev, struct device_attribute *da,
+ char *buf, int channel)
+{
+ struct smpro_errmon *errmon = dev_get_drvdata(dev);
+ unsigned char err_data[MAX_READ_BLOCK_LENGTH];
+ struct smpro_error_hdr *err_info;
+ s32 err_count, err_length;
+ int count = 0;
+ int ret;
+
+ err_info = &smpro_error_table[channel];
+
+ ret = regmap_read(errmon->regmap, err_info->err_count, &err_count);
+ /* Error count is the low byte */
+ err_count &= 0xff;
+ if (ret || !err_count || err_count > err_info->max_err_cnt)
+ goto done;
Just return the error.
+
+ ret = regmap_read(errmon->regmap, err_info->err_len, &err_length);
+ if (ret || err_length <= 0)
+ goto done;
return the error.
Wait, you could return 0 here, are you sure about that?
+
+ if (err_length > MAX_READ_BLOCK_LENGTH)
+ err_length = MAX_READ_BLOCK_LENGTH;
+
+ memset(err_data, 0x00, MAX_READ_BLOCK_LENGTH);
+ ret = regmap_noinc_read(errmon->regmap, err_info->err_data, err_data, err_length);
+ if (ret < 0)
+ goto done;
return the error, no need for a goto in this function.
+
+ /*
+ * The output of Core/Memory/PCIe/Others UE/CE errors follows below format:
+ * <Error Type><Error SubType><Instance><Error Status>\
+ * <Error Address><Error Misc 0><Error Misc 1><Error Misc2><Error Misc 3>
+ * Where:
+ * + Error Type: The hardwares cause the errors. (1 byte)
+ * + SubType: Sub type of error in the specified hardware error. (1 byte)
+ * + Instance: Combination of the socket, channel,
+ * slot cause the error. (2 bytes)
+ * + Error Status: Encode of error status. (4 bytes)
+ * + Error Address: The address in device causes the errors. (8 bytes)
+ * + Error Misc 0/1/2/3: Addition info about the errors. (8 bytes for each)
+ * Reference Altra SOC BMC Interface specification.
+ */
+ count = sysfs_emit(buf, "%02x%02x%04x%08x%016llx%016llx%016llx%016llx%016llx\n",
+ err_data[0], err_data[1], *(u16 *)&err_data[2],
+ *(u32 *)&err_data[4], *(u64 *)&err_data[8],
+ *(u64 *)&err_data[16], *(u64 *)&err_data[24],
+ *(u64 *)&err_data[32], *(u64 *)&err_data[40]);
+
+ /* go to next error */
+ ret = regmap_write(errmon->regmap, err_info->err_count, 0x100);
+done:
+ return ret ? ret : count;
+}
+
+/*
+ * Output format:
+ * <errType><image><dir><Location><errorCode><data>
+ * Where:
+ * + errType: SCP Error Type (3 bits)
+ * 1: Warning
+ * 2: Error
+ * 4: Error with data
+ * + image: SCP Image Code (8 bits)
+ * + dir: Direction (1 bit)
+ * 0: Enter
+ * 1: Exit
+ * + location: SCP Module Location Code (8 bits)
+ * + errorCode: SCP Error Code (16 bits)
+ * + data : Extensive data (32 bits)
+ * All bits are 0 when errType is warning or error.
+ */
+static ssize_t smpro_internal_err_read(struct device *dev, struct device_attribute *da,
+ char *buf, int channel)
+{
+ struct smpro_errmon *errmon = dev_get_drvdata(dev);
+ struct smpro_int_error_hdr *err_info;
+ unsigned int data_lo = 0, data_hi = 0;
+ unsigned int ret_hi, ret_lo;
+ unsigned int err_type;
+ unsigned int value;
+ int count = 0;
+ int ret;
+
+ /* read error status */
+ ret = regmap_read(errmon->regmap, GPI_RAS_ERR, &value);
+ if (ret)
+ goto done;
Same mess here, just return the error.
+
+ if (!((channel == RAS_SMPRO_ERRS && (value & BIT(0))) ||
+ (channel == RAS_PMPRO_ERRS && (value & BIT(1)))))
+ goto done;
No error? Are you sure?
+
+ err_info = &list_smpro_int_error_hdr[channel];
+ ret = regmap_read(errmon->regmap, err_info->err_type, &err_type);
+ if (ret)
+ goto done;
+
+ ret = regmap_read(errmon->regmap, err_info->err_info_low, &ret_lo);
+ if (ret)
+ goto done;
+
+ ret = regmap_read(errmon->regmap, err_info->err_info_high, &ret_hi);
+ if (ret)
+ goto done;
+
+ if (err_type & BIT(2)) {
+ /* Error with data type */
+ ret = regmap_read(errmon->regmap, err_info->err_data_low, &data_lo);
+ if (ret)
+ goto done;
+
+ ret = regmap_read(errmon->regmap, err_info->err_data_high, &data_hi);
+ if (ret)
+ goto done;
+
+ count = sysfs_emit(buf, "%01x%02x%01x%02x%04x%04x%04x\n",
+ 4, (ret_hi & 0xf000) >> 12, (ret_hi & 0x0800) >> 11,
+ ret_hi & 0xff, ret_lo, data_hi, data_lo);
+ /* clear the read errors */
+ ret = regmap_write(errmon->regmap, err_info->err_type, BIT(2));
+
+ } else if (err_type & BIT(1)) {
+ /* Error type */
+ count = sysfs_emit(buf, "%01x%02x%01x%02x%04x%04x%04x\n",
+ 2, (ret_hi & 0xf000) >> 12, (ret_hi & 0x0800) >> 11,
+ ret_hi & 0xff, ret_lo, data_hi, data_lo);
+ /* clear the read errors */
+ ret = regmap_write(errmon->regmap, err_info->err_type, BIT(1));
+
+ } else if (err_type & BIT(0)) {
+ /* Warning type */
+ count = sysfs_emit(buf, "%01x%02x%01x%02x%04x%04x%04x\n",
+ 1, (ret_hi & 0xf000) >> 12, (ret_hi & 0x0800) >> 11,
+ ret_hi & 0xff, ret_lo, data_hi, data_lo);