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