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 504a4a4e9eSQuan Nguyen /* PCIE Error Registers */ 514a4a4e9eSQuan Nguyen #define PCIE_CE_ERR_CNT 0xC0 524a4a4e9eSQuan Nguyen #define PCIE_CE_ERR_LEN 0xC1 534a4a4e9eSQuan Nguyen #define PCIE_CE_ERR_DATA 0xC2 544a4a4e9eSQuan Nguyen #define PCIE_UE_ERR_CNT 0xC3 554a4a4e9eSQuan Nguyen #define PCIE_UE_ERR_LEN 0xC4 564a4a4e9eSQuan Nguyen #define PCIE_UE_ERR_DATA 0xC5 574a4a4e9eSQuan Nguyen 584a4a4e9eSQuan Nguyen /* Other Error Registers */ 594a4a4e9eSQuan Nguyen #define OTHER_CE_ERR_CNT 0xD0 604a4a4e9eSQuan Nguyen #define OTHER_CE_ERR_LEN 0xD1 614a4a4e9eSQuan Nguyen #define OTHER_CE_ERR_DATA 0xD2 624a4a4e9eSQuan Nguyen #define OTHER_UE_ERR_CNT 0xD8 634a4a4e9eSQuan Nguyen #define OTHER_UE_ERR_LEN 0xD9 644a4a4e9eSQuan Nguyen #define OTHER_UE_ERR_DATA 0xDA 654a4a4e9eSQuan Nguyen 664a4a4e9eSQuan Nguyen /* Event Data Registers */ 674a4a4e9eSQuan Nguyen #define VRD_WARN_FAULT_EVENT_DATA 0x78 684a4a4e9eSQuan Nguyen #define VRD_HOT_EVENT_DATA 0x79 694a4a4e9eSQuan Nguyen #define DIMM_HOT_EVENT_DATA 0x7A 70*b0f64c80SQuan Nguyen #define DIMM_2X_REFRESH_EVENT_DATA 0x96 714a4a4e9eSQuan Nguyen 724a4a4e9eSQuan Nguyen #define MAX_READ_BLOCK_LENGTH 48 734a4a4e9eSQuan Nguyen 744a4a4e9eSQuan Nguyen #define RAS_SMPRO_ERR 0 754a4a4e9eSQuan Nguyen #define RAS_PMPRO_ERR 1 764a4a4e9eSQuan Nguyen 774a4a4e9eSQuan Nguyen enum RAS_48BYTES_ERR_TYPES { 784a4a4e9eSQuan Nguyen CORE_CE_ERR, 794a4a4e9eSQuan Nguyen CORE_UE_ERR, 804a4a4e9eSQuan Nguyen MEM_CE_ERR, 814a4a4e9eSQuan Nguyen MEM_UE_ERR, 824a4a4e9eSQuan Nguyen PCIE_CE_ERR, 834a4a4e9eSQuan Nguyen PCIE_UE_ERR, 844a4a4e9eSQuan Nguyen OTHER_CE_ERR, 854a4a4e9eSQuan Nguyen OTHER_UE_ERR, 864a4a4e9eSQuan Nguyen NUM_48BYTES_ERR_TYPE, 874a4a4e9eSQuan Nguyen }; 884a4a4e9eSQuan Nguyen 894a4a4e9eSQuan Nguyen struct smpro_error_hdr { 904a4a4e9eSQuan Nguyen u8 count; /* Number of the RAS errors */ 914a4a4e9eSQuan Nguyen u8 len; /* Number of data bytes */ 924a4a4e9eSQuan Nguyen u8 data; /* Start of 48-byte data */ 934a4a4e9eSQuan Nguyen u8 max_cnt; /* Max num of errors */ 944a4a4e9eSQuan Nguyen }; 954a4a4e9eSQuan Nguyen 964a4a4e9eSQuan Nguyen /* 974a4a4e9eSQuan Nguyen * Included Address of registers to get Count, Length of data and Data 984a4a4e9eSQuan Nguyen * of the 48 bytes error data 994a4a4e9eSQuan Nguyen */ 1004a4a4e9eSQuan Nguyen static struct smpro_error_hdr smpro_error_table[] = { 1014a4a4e9eSQuan Nguyen [CORE_CE_ERR] = { 1024a4a4e9eSQuan Nguyen .count = CORE_CE_ERR_CNT, 1034a4a4e9eSQuan Nguyen .len = CORE_CE_ERR_LEN, 1044a4a4e9eSQuan Nguyen .data = CORE_CE_ERR_DATA, 1054a4a4e9eSQuan Nguyen .max_cnt = 32 1064a4a4e9eSQuan Nguyen }, 1074a4a4e9eSQuan Nguyen [CORE_UE_ERR] = { 1084a4a4e9eSQuan Nguyen .count = CORE_UE_ERR_CNT, 1094a4a4e9eSQuan Nguyen .len = CORE_UE_ERR_LEN, 1104a4a4e9eSQuan Nguyen .data = CORE_UE_ERR_DATA, 1114a4a4e9eSQuan Nguyen .max_cnt = 32 1124a4a4e9eSQuan Nguyen }, 1134a4a4e9eSQuan Nguyen [MEM_CE_ERR] = { 1144a4a4e9eSQuan Nguyen .count = MEM_CE_ERR_CNT, 1154a4a4e9eSQuan Nguyen .len = MEM_CE_ERR_LEN, 1164a4a4e9eSQuan Nguyen .data = MEM_CE_ERR_DATA, 1174a4a4e9eSQuan Nguyen .max_cnt = 16 1184a4a4e9eSQuan Nguyen }, 1194a4a4e9eSQuan Nguyen [MEM_UE_ERR] = { 1204a4a4e9eSQuan Nguyen .count = MEM_UE_ERR_CNT, 1214a4a4e9eSQuan Nguyen .len = MEM_UE_ERR_LEN, 1224a4a4e9eSQuan Nguyen .data = MEM_UE_ERR_DATA, 1234a4a4e9eSQuan Nguyen .max_cnt = 16 1244a4a4e9eSQuan Nguyen }, 1254a4a4e9eSQuan Nguyen [PCIE_CE_ERR] = { 1264a4a4e9eSQuan Nguyen .count = PCIE_CE_ERR_CNT, 1274a4a4e9eSQuan Nguyen .len = PCIE_CE_ERR_LEN, 1284a4a4e9eSQuan Nguyen .data = PCIE_CE_ERR_DATA, 1294a4a4e9eSQuan Nguyen .max_cnt = 96 1304a4a4e9eSQuan Nguyen }, 1314a4a4e9eSQuan Nguyen [PCIE_UE_ERR] = { 1324a4a4e9eSQuan Nguyen .count = PCIE_UE_ERR_CNT, 1334a4a4e9eSQuan Nguyen .len = PCIE_UE_ERR_LEN, 1344a4a4e9eSQuan Nguyen .data = PCIE_UE_ERR_DATA, 1354a4a4e9eSQuan Nguyen .max_cnt = 96 1364a4a4e9eSQuan Nguyen }, 1374a4a4e9eSQuan Nguyen [OTHER_CE_ERR] = { 1384a4a4e9eSQuan Nguyen .count = OTHER_CE_ERR_CNT, 1394a4a4e9eSQuan Nguyen .len = OTHER_CE_ERR_LEN, 1404a4a4e9eSQuan Nguyen .data = OTHER_CE_ERR_DATA, 1414a4a4e9eSQuan Nguyen .max_cnt = 8 1424a4a4e9eSQuan Nguyen }, 1434a4a4e9eSQuan Nguyen [OTHER_UE_ERR] = { 1444a4a4e9eSQuan Nguyen .count = OTHER_UE_ERR_CNT, 1454a4a4e9eSQuan Nguyen .len = OTHER_UE_ERR_LEN, 1464a4a4e9eSQuan Nguyen .data = OTHER_UE_ERR_DATA, 1474a4a4e9eSQuan Nguyen .max_cnt = 8 1484a4a4e9eSQuan Nguyen }, 1494a4a4e9eSQuan Nguyen }; 1504a4a4e9eSQuan Nguyen 1514a4a4e9eSQuan Nguyen /* 1524a4a4e9eSQuan Nguyen * List of SCP registers which are used to get 1534a4a4e9eSQuan Nguyen * one type of RAS Internal errors. 1544a4a4e9eSQuan Nguyen */ 1554a4a4e9eSQuan Nguyen struct smpro_int_error_hdr { 1564a4a4e9eSQuan Nguyen u8 type; 1574a4a4e9eSQuan Nguyen u8 info_l; 1584a4a4e9eSQuan Nguyen u8 info_h; 1594a4a4e9eSQuan Nguyen u8 data_l; 1604a4a4e9eSQuan Nguyen u8 data_h; 1614a4a4e9eSQuan Nguyen u8 warn_l; 1624a4a4e9eSQuan Nguyen u8 warn_h; 1634a4a4e9eSQuan Nguyen }; 1644a4a4e9eSQuan Nguyen 1654a4a4e9eSQuan Nguyen static struct smpro_int_error_hdr list_smpro_int_error_hdr[] = { 1664a4a4e9eSQuan Nguyen [RAS_SMPRO_ERR] = { 1674a4a4e9eSQuan Nguyen .type = ERR_SMPRO_TYPE, 1684a4a4e9eSQuan Nguyen .info_l = ERR_SMPRO_INFO_LO, 1694a4a4e9eSQuan Nguyen .info_h = ERR_SMPRO_INFO_HI, 1704a4a4e9eSQuan Nguyen .data_l = ERR_SMPRO_DATA_LO, 1714a4a4e9eSQuan Nguyen .data_h = ERR_SMPRO_DATA_HI, 1724a4a4e9eSQuan Nguyen .warn_l = WARN_SMPRO_INFO_LO, 1734a4a4e9eSQuan Nguyen .warn_h = WARN_SMPRO_INFO_HI, 1744a4a4e9eSQuan Nguyen }, 1754a4a4e9eSQuan Nguyen [RAS_PMPRO_ERR] = { 1764a4a4e9eSQuan Nguyen .type = ERR_PMPRO_TYPE, 1774a4a4e9eSQuan Nguyen .info_l = ERR_PMPRO_INFO_LO, 1784a4a4e9eSQuan Nguyen .info_h = ERR_PMPRO_INFO_HI, 1794a4a4e9eSQuan Nguyen .data_l = ERR_PMPRO_DATA_LO, 1804a4a4e9eSQuan Nguyen .data_h = ERR_PMPRO_DATA_HI, 1814a4a4e9eSQuan Nguyen .warn_l = WARN_PMPRO_INFO_LO, 1824a4a4e9eSQuan Nguyen .warn_h = WARN_PMPRO_INFO_HI, 1834a4a4e9eSQuan Nguyen }, 1844a4a4e9eSQuan Nguyen }; 1854a4a4e9eSQuan Nguyen 1864a4a4e9eSQuan Nguyen struct smpro_errmon { 1874a4a4e9eSQuan Nguyen struct regmap *regmap; 1884a4a4e9eSQuan Nguyen }; 1894a4a4e9eSQuan Nguyen 1904a4a4e9eSQuan Nguyen enum EVENT_TYPES { 1914a4a4e9eSQuan Nguyen VRD_WARN_FAULT_EVENT, 1924a4a4e9eSQuan Nguyen VRD_HOT_EVENT, 1934a4a4e9eSQuan Nguyen DIMM_HOT_EVENT, 194*b0f64c80SQuan Nguyen DIMM_2X_REFRESH_EVENT, 1954a4a4e9eSQuan Nguyen NUM_EVENTS_TYPE, 1964a4a4e9eSQuan Nguyen }; 1974a4a4e9eSQuan Nguyen 1984a4a4e9eSQuan Nguyen /* Included Address of event source and data registers */ 1994a4a4e9eSQuan Nguyen static u8 smpro_event_table[NUM_EVENTS_TYPE] = { 2004a4a4e9eSQuan Nguyen VRD_WARN_FAULT_EVENT_DATA, 2014a4a4e9eSQuan Nguyen VRD_HOT_EVENT_DATA, 2024a4a4e9eSQuan Nguyen DIMM_HOT_EVENT_DATA, 203*b0f64c80SQuan Nguyen DIMM_2X_REFRESH_EVENT_DATA, 2044a4a4e9eSQuan Nguyen }; 2054a4a4e9eSQuan Nguyen 2064a4a4e9eSQuan Nguyen static ssize_t smpro_event_data_read(struct device *dev, 2074a4a4e9eSQuan Nguyen struct device_attribute *da, char *buf, 2084a4a4e9eSQuan Nguyen int channel) 2094a4a4e9eSQuan Nguyen { 2104a4a4e9eSQuan Nguyen struct smpro_errmon *errmon = dev_get_drvdata(dev); 2114a4a4e9eSQuan Nguyen s32 event_data; 2124a4a4e9eSQuan Nguyen int ret; 2134a4a4e9eSQuan Nguyen 2144a4a4e9eSQuan Nguyen ret = regmap_read(errmon->regmap, smpro_event_table[channel], &event_data); 2154a4a4e9eSQuan Nguyen if (ret) 2164a4a4e9eSQuan Nguyen return ret; 2174a4a4e9eSQuan Nguyen /* Clear event after read */ 2184a4a4e9eSQuan Nguyen if (event_data != 0) 2194a4a4e9eSQuan Nguyen regmap_write(errmon->regmap, smpro_event_table[channel], event_data); 2204a4a4e9eSQuan Nguyen 2214a4a4e9eSQuan Nguyen return sysfs_emit(buf, "%04x\n", event_data); 2224a4a4e9eSQuan Nguyen } 2234a4a4e9eSQuan Nguyen 2244a4a4e9eSQuan Nguyen static ssize_t smpro_overflow_data_read(struct device *dev, struct device_attribute *da, 2254a4a4e9eSQuan Nguyen char *buf, int channel) 2264a4a4e9eSQuan Nguyen { 2274a4a4e9eSQuan Nguyen struct smpro_errmon *errmon = dev_get_drvdata(dev); 2284a4a4e9eSQuan Nguyen struct smpro_error_hdr *err_info; 2294a4a4e9eSQuan Nguyen s32 err_count; 2304a4a4e9eSQuan Nguyen int ret; 2314a4a4e9eSQuan Nguyen 2324a4a4e9eSQuan Nguyen err_info = &smpro_error_table[channel]; 2334a4a4e9eSQuan Nguyen 2344a4a4e9eSQuan Nguyen ret = regmap_read(errmon->regmap, err_info->count, &err_count); 2354a4a4e9eSQuan Nguyen if (ret) 2364a4a4e9eSQuan Nguyen return ret; 2374a4a4e9eSQuan Nguyen 2384a4a4e9eSQuan Nguyen /* Bit 8 indicates the overflow status */ 2394a4a4e9eSQuan Nguyen return sysfs_emit(buf, "%d\n", (err_count & BIT(8)) ? 1 : 0); 2404a4a4e9eSQuan Nguyen } 2414a4a4e9eSQuan Nguyen 2424a4a4e9eSQuan Nguyen static ssize_t smpro_error_data_read(struct device *dev, struct device_attribute *da, 2434a4a4e9eSQuan Nguyen char *buf, int channel) 2444a4a4e9eSQuan Nguyen { 2454a4a4e9eSQuan Nguyen struct smpro_errmon *errmon = dev_get_drvdata(dev); 2464a4a4e9eSQuan Nguyen unsigned char err_data[MAX_READ_BLOCK_LENGTH]; 2474a4a4e9eSQuan Nguyen struct smpro_error_hdr *err_info; 2484a4a4e9eSQuan Nguyen s32 err_count, err_length; 2494a4a4e9eSQuan Nguyen int ret; 2504a4a4e9eSQuan Nguyen 2514a4a4e9eSQuan Nguyen err_info = &smpro_error_table[channel]; 2524a4a4e9eSQuan Nguyen 2534a4a4e9eSQuan Nguyen ret = regmap_read(errmon->regmap, err_info->count, &err_count); 2544a4a4e9eSQuan Nguyen /* Error count is the low byte */ 2554a4a4e9eSQuan Nguyen err_count &= 0xff; 2564a4a4e9eSQuan Nguyen if (ret || !err_count || err_count > err_info->max_cnt) 2574a4a4e9eSQuan Nguyen return ret; 2584a4a4e9eSQuan Nguyen 2594a4a4e9eSQuan Nguyen ret = regmap_read(errmon->regmap, err_info->len, &err_length); 2604a4a4e9eSQuan Nguyen if (ret || err_length <= 0) 2614a4a4e9eSQuan Nguyen return ret; 2624a4a4e9eSQuan Nguyen 2634a4a4e9eSQuan Nguyen if (err_length > MAX_READ_BLOCK_LENGTH) 2644a4a4e9eSQuan Nguyen err_length = MAX_READ_BLOCK_LENGTH; 2654a4a4e9eSQuan Nguyen 2664a4a4e9eSQuan Nguyen memset(err_data, 0x00, MAX_READ_BLOCK_LENGTH); 2674a4a4e9eSQuan Nguyen ret = regmap_noinc_read(errmon->regmap, err_info->data, err_data, err_length); 2684a4a4e9eSQuan Nguyen if (ret < 0) 2694a4a4e9eSQuan Nguyen return ret; 2704a4a4e9eSQuan Nguyen 2714a4a4e9eSQuan Nguyen /* clear the error */ 2724a4a4e9eSQuan Nguyen ret = regmap_write(errmon->regmap, err_info->count, 0x100); 2734a4a4e9eSQuan Nguyen if (ret) 2744a4a4e9eSQuan Nguyen return ret; 2754a4a4e9eSQuan Nguyen /* 2764a4a4e9eSQuan Nguyen * The output of Core/Memory/PCIe/Others UE/CE errors follows the format 2774a4a4e9eSQuan Nguyen * specified in section 5.8.1 CE/UE Error Data record in 2784a4a4e9eSQuan Nguyen * Altra SOC BMC Interface specification. 2794a4a4e9eSQuan Nguyen */ 2804a4a4e9eSQuan Nguyen return sysfs_emit(buf, "%*phN\n", MAX_READ_BLOCK_LENGTH, err_data); 2814a4a4e9eSQuan Nguyen } 2824a4a4e9eSQuan Nguyen 2834a4a4e9eSQuan Nguyen /* 2844a4a4e9eSQuan Nguyen * Output format: 2854a4a4e9eSQuan Nguyen * <4-byte hex value of error info><4-byte hex value of error extensive data> 2864a4a4e9eSQuan Nguyen * Where: 2874a4a4e9eSQuan Nguyen * + error info : The error information 2884a4a4e9eSQuan Nguyen * + error data : Extensive data (32 bits) 2894a4a4e9eSQuan Nguyen * Reference to section 5.10 RAS Internal Error Register Definition in 2904a4a4e9eSQuan Nguyen * Altra SOC BMC Interface specification 2914a4a4e9eSQuan Nguyen */ 2924a4a4e9eSQuan Nguyen static ssize_t smpro_internal_err_read(struct device *dev, struct device_attribute *da, 2934a4a4e9eSQuan Nguyen char *buf, int channel) 2944a4a4e9eSQuan Nguyen { 2954a4a4e9eSQuan Nguyen struct smpro_errmon *errmon = dev_get_drvdata(dev); 2964a4a4e9eSQuan Nguyen struct smpro_int_error_hdr *err_info; 2974a4a4e9eSQuan Nguyen unsigned int err[4] = { 0 }; 2984a4a4e9eSQuan Nguyen unsigned int err_type; 2994a4a4e9eSQuan Nguyen unsigned int val; 3004a4a4e9eSQuan Nguyen int ret; 3014a4a4e9eSQuan Nguyen 3024a4a4e9eSQuan Nguyen /* read error status */ 3034a4a4e9eSQuan Nguyen ret = regmap_read(errmon->regmap, GPI_RAS_ERR, &val); 3044a4a4e9eSQuan Nguyen if (ret) 3054a4a4e9eSQuan Nguyen return ret; 3064a4a4e9eSQuan Nguyen 3074a4a4e9eSQuan Nguyen if ((channel == RAS_SMPRO_ERR && !(val & BIT(0))) || 3084a4a4e9eSQuan Nguyen (channel == RAS_PMPRO_ERR && !(val & BIT(1)))) 3094a4a4e9eSQuan Nguyen return 0; 3104a4a4e9eSQuan Nguyen 3114a4a4e9eSQuan Nguyen err_info = &list_smpro_int_error_hdr[channel]; 3124a4a4e9eSQuan Nguyen ret = regmap_read(errmon->regmap, err_info->type, &val); 3134a4a4e9eSQuan Nguyen if (ret) 3144a4a4e9eSQuan Nguyen return ret; 3154a4a4e9eSQuan Nguyen 3164a4a4e9eSQuan Nguyen err_type = (val & BIT(1)) ? BIT(1) : 3174a4a4e9eSQuan Nguyen (val & BIT(2)) ? BIT(2) : 0; 3184a4a4e9eSQuan Nguyen 3194a4a4e9eSQuan Nguyen if (!err_type) 3204a4a4e9eSQuan Nguyen return 0; 3214a4a4e9eSQuan Nguyen 3224a4a4e9eSQuan Nguyen ret = regmap_read(errmon->regmap, err_info->info_l, err + 1); 3234a4a4e9eSQuan Nguyen if (ret) 3244a4a4e9eSQuan Nguyen return ret; 3254a4a4e9eSQuan Nguyen 3264a4a4e9eSQuan Nguyen ret = regmap_read(errmon->regmap, err_info->info_h, err); 3274a4a4e9eSQuan Nguyen if (ret) 3284a4a4e9eSQuan Nguyen return ret; 3294a4a4e9eSQuan Nguyen 3304a4a4e9eSQuan Nguyen if (err_type & BIT(2)) { 3314a4a4e9eSQuan Nguyen /* Error with data type */ 3324a4a4e9eSQuan Nguyen ret = regmap_read(errmon->regmap, err_info->data_l, err + 3); 3334a4a4e9eSQuan Nguyen if (ret) 3344a4a4e9eSQuan Nguyen return ret; 3354a4a4e9eSQuan Nguyen 3364a4a4e9eSQuan Nguyen ret = regmap_read(errmon->regmap, err_info->data_h, err + 2); 3374a4a4e9eSQuan Nguyen if (ret) 3384a4a4e9eSQuan Nguyen return ret; 3394a4a4e9eSQuan Nguyen } 3404a4a4e9eSQuan Nguyen 3414a4a4e9eSQuan Nguyen /* clear the read errors */ 3424a4a4e9eSQuan Nguyen ret = regmap_write(errmon->regmap, err_info->type, err_type); 3434a4a4e9eSQuan Nguyen if (ret) 3444a4a4e9eSQuan Nguyen return ret; 3454a4a4e9eSQuan Nguyen 3464a4a4e9eSQuan Nguyen return sysfs_emit(buf, "%*phN\n", (int)sizeof(err), err); 3474a4a4e9eSQuan Nguyen } 3484a4a4e9eSQuan Nguyen 3494a4a4e9eSQuan Nguyen /* 3504a4a4e9eSQuan Nguyen * Output format: 3514a4a4e9eSQuan Nguyen * <4-byte hex value of warining info> 3524a4a4e9eSQuan Nguyen * Reference to section 5.10 RAS Internal Error Register Definition in 3534a4a4e9eSQuan Nguyen * Altra SOC BMC Interface specification 3544a4a4e9eSQuan Nguyen */ 3554a4a4e9eSQuan Nguyen static ssize_t smpro_internal_warn_read(struct device *dev, struct device_attribute *da, 3564a4a4e9eSQuan Nguyen char *buf, int channel) 3574a4a4e9eSQuan Nguyen { 3584a4a4e9eSQuan Nguyen struct smpro_errmon *errmon = dev_get_drvdata(dev); 3594a4a4e9eSQuan Nguyen struct smpro_int_error_hdr *err_info; 3604a4a4e9eSQuan Nguyen unsigned int warn[2] = { 0 }; 3614a4a4e9eSQuan Nguyen unsigned int val; 3624a4a4e9eSQuan Nguyen int ret; 3634a4a4e9eSQuan Nguyen 3644a4a4e9eSQuan Nguyen /* read error status */ 3654a4a4e9eSQuan Nguyen ret = regmap_read(errmon->regmap, GPI_RAS_ERR, &val); 3664a4a4e9eSQuan Nguyen if (ret) 3674a4a4e9eSQuan Nguyen return ret; 3684a4a4e9eSQuan Nguyen 3694a4a4e9eSQuan Nguyen if ((channel == RAS_SMPRO_ERR && !(val & BIT(0))) || 3704a4a4e9eSQuan Nguyen (channel == RAS_PMPRO_ERR && !(val & BIT(1)))) 3714a4a4e9eSQuan Nguyen return 0; 3724a4a4e9eSQuan Nguyen 3734a4a4e9eSQuan Nguyen err_info = &list_smpro_int_error_hdr[channel]; 3744a4a4e9eSQuan Nguyen ret = regmap_read(errmon->regmap, err_info->type, &val); 3754a4a4e9eSQuan Nguyen if (ret) 3764a4a4e9eSQuan Nguyen return ret; 3774a4a4e9eSQuan Nguyen 3784a4a4e9eSQuan Nguyen if (!(val & BIT(0))) 3794a4a4e9eSQuan Nguyen return 0; 3804a4a4e9eSQuan Nguyen 3814a4a4e9eSQuan Nguyen ret = regmap_read(errmon->regmap, err_info->warn_l, warn + 1); 3824a4a4e9eSQuan Nguyen if (ret) 3834a4a4e9eSQuan Nguyen return ret; 3844a4a4e9eSQuan Nguyen 3854a4a4e9eSQuan Nguyen ret = regmap_read(errmon->regmap, err_info->warn_h, warn); 3864a4a4e9eSQuan Nguyen if (ret) 3874a4a4e9eSQuan Nguyen return ret; 3884a4a4e9eSQuan Nguyen 3894a4a4e9eSQuan Nguyen /* clear the warning */ 3904a4a4e9eSQuan Nguyen ret = regmap_write(errmon->regmap, err_info->type, BIT(0)); 3914a4a4e9eSQuan Nguyen if (ret) 3924a4a4e9eSQuan Nguyen return ret; 3934a4a4e9eSQuan Nguyen 3944a4a4e9eSQuan Nguyen return sysfs_emit(buf, "%*phN\n", (int)sizeof(warn), warn); 3954a4a4e9eSQuan Nguyen } 3964a4a4e9eSQuan Nguyen 3974a4a4e9eSQuan Nguyen #define ERROR_OVERFLOW_RO(_error, _index) \ 3984a4a4e9eSQuan Nguyen static ssize_t overflow_##_error##_show(struct device *dev, \ 3994a4a4e9eSQuan Nguyen struct device_attribute *da, \ 4004a4a4e9eSQuan Nguyen char *buf) \ 4014a4a4e9eSQuan Nguyen { \ 4024a4a4e9eSQuan Nguyen return smpro_overflow_data_read(dev, da, buf, _index); \ 4034a4a4e9eSQuan Nguyen } \ 4044a4a4e9eSQuan Nguyen static DEVICE_ATTR_RO(overflow_##_error) 4054a4a4e9eSQuan Nguyen 4064a4a4e9eSQuan Nguyen ERROR_OVERFLOW_RO(core_ce, CORE_CE_ERR); 4074a4a4e9eSQuan Nguyen ERROR_OVERFLOW_RO(core_ue, CORE_UE_ERR); 4084a4a4e9eSQuan Nguyen ERROR_OVERFLOW_RO(mem_ce, MEM_CE_ERR); 4094a4a4e9eSQuan Nguyen ERROR_OVERFLOW_RO(mem_ue, MEM_UE_ERR); 4104a4a4e9eSQuan Nguyen ERROR_OVERFLOW_RO(pcie_ce, PCIE_CE_ERR); 4114a4a4e9eSQuan Nguyen ERROR_OVERFLOW_RO(pcie_ue, PCIE_UE_ERR); 4124a4a4e9eSQuan Nguyen ERROR_OVERFLOW_RO(other_ce, OTHER_CE_ERR); 4134a4a4e9eSQuan Nguyen ERROR_OVERFLOW_RO(other_ue, OTHER_UE_ERR); 4144a4a4e9eSQuan Nguyen 4154a4a4e9eSQuan Nguyen #define ERROR_RO(_error, _index) \ 4164a4a4e9eSQuan Nguyen static ssize_t error_##_error##_show(struct device *dev, \ 4174a4a4e9eSQuan Nguyen struct device_attribute *da, \ 4184a4a4e9eSQuan Nguyen char *buf) \ 4194a4a4e9eSQuan Nguyen { \ 4204a4a4e9eSQuan Nguyen return smpro_error_data_read(dev, da, buf, _index); \ 4214a4a4e9eSQuan Nguyen } \ 4224a4a4e9eSQuan Nguyen static DEVICE_ATTR_RO(error_##_error) 4234a4a4e9eSQuan Nguyen 4244a4a4e9eSQuan Nguyen ERROR_RO(core_ce, CORE_CE_ERR); 4254a4a4e9eSQuan Nguyen ERROR_RO(core_ue, CORE_UE_ERR); 4264a4a4e9eSQuan Nguyen ERROR_RO(mem_ce, MEM_CE_ERR); 4274a4a4e9eSQuan Nguyen ERROR_RO(mem_ue, MEM_UE_ERR); 4284a4a4e9eSQuan Nguyen ERROR_RO(pcie_ce, PCIE_CE_ERR); 4294a4a4e9eSQuan Nguyen ERROR_RO(pcie_ue, PCIE_UE_ERR); 4304a4a4e9eSQuan Nguyen ERROR_RO(other_ce, OTHER_CE_ERR); 4314a4a4e9eSQuan Nguyen ERROR_RO(other_ue, OTHER_UE_ERR); 4324a4a4e9eSQuan Nguyen 4334a4a4e9eSQuan Nguyen static ssize_t error_smpro_show(struct device *dev, struct device_attribute *da, char *buf) 4344a4a4e9eSQuan Nguyen { 4354a4a4e9eSQuan Nguyen return smpro_internal_err_read(dev, da, buf, RAS_SMPRO_ERR); 4364a4a4e9eSQuan Nguyen } 4374a4a4e9eSQuan Nguyen static DEVICE_ATTR_RO(error_smpro); 4384a4a4e9eSQuan Nguyen 4394a4a4e9eSQuan Nguyen static ssize_t error_pmpro_show(struct device *dev, struct device_attribute *da, char *buf) 4404a4a4e9eSQuan Nguyen { 4414a4a4e9eSQuan Nguyen return smpro_internal_err_read(dev, da, buf, RAS_PMPRO_ERR); 4424a4a4e9eSQuan Nguyen } 4434a4a4e9eSQuan Nguyen static DEVICE_ATTR_RO(error_pmpro); 4444a4a4e9eSQuan Nguyen 4454a4a4e9eSQuan Nguyen static ssize_t warn_smpro_show(struct device *dev, struct device_attribute *da, char *buf) 4464a4a4e9eSQuan Nguyen { 4474a4a4e9eSQuan Nguyen return smpro_internal_warn_read(dev, da, buf, RAS_SMPRO_ERR); 4484a4a4e9eSQuan Nguyen } 4494a4a4e9eSQuan Nguyen static DEVICE_ATTR_RO(warn_smpro); 4504a4a4e9eSQuan Nguyen 4514a4a4e9eSQuan Nguyen static ssize_t warn_pmpro_show(struct device *dev, struct device_attribute *da, char *buf) 4524a4a4e9eSQuan Nguyen { 4534a4a4e9eSQuan Nguyen return smpro_internal_warn_read(dev, da, buf, RAS_PMPRO_ERR); 4544a4a4e9eSQuan Nguyen } 4554a4a4e9eSQuan Nguyen static DEVICE_ATTR_RO(warn_pmpro); 4564a4a4e9eSQuan Nguyen 4574a4a4e9eSQuan Nguyen #define EVENT_RO(_event, _index) \ 4584a4a4e9eSQuan Nguyen static ssize_t event_##_event##_show(struct device *dev, \ 4594a4a4e9eSQuan Nguyen struct device_attribute *da, \ 4604a4a4e9eSQuan Nguyen char *buf) \ 4614a4a4e9eSQuan Nguyen { \ 4624a4a4e9eSQuan Nguyen return smpro_event_data_read(dev, da, buf, _index); \ 4634a4a4e9eSQuan Nguyen } \ 4644a4a4e9eSQuan Nguyen static DEVICE_ATTR_RO(event_##_event) 4654a4a4e9eSQuan Nguyen 4664a4a4e9eSQuan Nguyen EVENT_RO(vrd_warn_fault, VRD_WARN_FAULT_EVENT); 4674a4a4e9eSQuan Nguyen EVENT_RO(vrd_hot, VRD_HOT_EVENT); 4684a4a4e9eSQuan Nguyen EVENT_RO(dimm_hot, DIMM_HOT_EVENT); 469*b0f64c80SQuan Nguyen EVENT_RO(dimm_2x_refresh, DIMM_2X_REFRESH_EVENT); 4704a4a4e9eSQuan Nguyen 4714a4a4e9eSQuan Nguyen static struct attribute *smpro_errmon_attrs[] = { 4724a4a4e9eSQuan Nguyen &dev_attr_overflow_core_ce.attr, 4734a4a4e9eSQuan Nguyen &dev_attr_overflow_core_ue.attr, 4744a4a4e9eSQuan Nguyen &dev_attr_overflow_mem_ce.attr, 4754a4a4e9eSQuan Nguyen &dev_attr_overflow_mem_ue.attr, 4764a4a4e9eSQuan Nguyen &dev_attr_overflow_pcie_ce.attr, 4774a4a4e9eSQuan Nguyen &dev_attr_overflow_pcie_ue.attr, 4784a4a4e9eSQuan Nguyen &dev_attr_overflow_other_ce.attr, 4794a4a4e9eSQuan Nguyen &dev_attr_overflow_other_ue.attr, 4804a4a4e9eSQuan Nguyen &dev_attr_error_core_ce.attr, 4814a4a4e9eSQuan Nguyen &dev_attr_error_core_ue.attr, 4824a4a4e9eSQuan Nguyen &dev_attr_error_mem_ce.attr, 4834a4a4e9eSQuan Nguyen &dev_attr_error_mem_ue.attr, 4844a4a4e9eSQuan Nguyen &dev_attr_error_pcie_ce.attr, 4854a4a4e9eSQuan Nguyen &dev_attr_error_pcie_ue.attr, 4864a4a4e9eSQuan Nguyen &dev_attr_error_other_ce.attr, 4874a4a4e9eSQuan Nguyen &dev_attr_error_other_ue.attr, 4884a4a4e9eSQuan Nguyen &dev_attr_error_smpro.attr, 4894a4a4e9eSQuan Nguyen &dev_attr_error_pmpro.attr, 4904a4a4e9eSQuan Nguyen &dev_attr_warn_smpro.attr, 4914a4a4e9eSQuan Nguyen &dev_attr_warn_pmpro.attr, 4924a4a4e9eSQuan Nguyen &dev_attr_event_vrd_warn_fault.attr, 4934a4a4e9eSQuan Nguyen &dev_attr_event_vrd_hot.attr, 4944a4a4e9eSQuan Nguyen &dev_attr_event_dimm_hot.attr, 495*b0f64c80SQuan Nguyen &dev_attr_event_dimm_2x_refresh.attr, 4964a4a4e9eSQuan Nguyen NULL 4974a4a4e9eSQuan Nguyen }; 4984a4a4e9eSQuan Nguyen 4994a4a4e9eSQuan Nguyen ATTRIBUTE_GROUPS(smpro_errmon); 5004a4a4e9eSQuan Nguyen 5014a4a4e9eSQuan Nguyen static int smpro_errmon_probe(struct platform_device *pdev) 5024a4a4e9eSQuan Nguyen { 5034a4a4e9eSQuan Nguyen struct smpro_errmon *errmon; 5044a4a4e9eSQuan Nguyen 5054a4a4e9eSQuan Nguyen errmon = devm_kzalloc(&pdev->dev, sizeof(struct smpro_errmon), GFP_KERNEL); 5064a4a4e9eSQuan Nguyen if (!errmon) 5074a4a4e9eSQuan Nguyen return -ENOMEM; 5084a4a4e9eSQuan Nguyen 5094a4a4e9eSQuan Nguyen platform_set_drvdata(pdev, errmon); 5104a4a4e9eSQuan Nguyen 5114a4a4e9eSQuan Nguyen errmon->regmap = dev_get_regmap(pdev->dev.parent, NULL); 5124a4a4e9eSQuan Nguyen if (!errmon->regmap) 5134a4a4e9eSQuan Nguyen return -ENODEV; 5144a4a4e9eSQuan Nguyen 5154a4a4e9eSQuan Nguyen return 0; 5164a4a4e9eSQuan Nguyen } 5174a4a4e9eSQuan Nguyen 5184a4a4e9eSQuan Nguyen static struct platform_driver smpro_errmon_driver = { 5194a4a4e9eSQuan Nguyen .probe = smpro_errmon_probe, 5204a4a4e9eSQuan Nguyen .driver = { 5214a4a4e9eSQuan Nguyen .name = "smpro-errmon", 5224a4a4e9eSQuan Nguyen .dev_groups = smpro_errmon_groups, 5234a4a4e9eSQuan Nguyen }, 5244a4a4e9eSQuan Nguyen }; 5254a4a4e9eSQuan Nguyen 5264a4a4e9eSQuan Nguyen module_platform_driver(smpro_errmon_driver); 5274a4a4e9eSQuan Nguyen 5284a4a4e9eSQuan Nguyen MODULE_AUTHOR("Tung Nguyen <tung.nguyen@amperecomputing.com>"); 5294a4a4e9eSQuan Nguyen MODULE_AUTHOR("Thinh Pham <thinh.pham@amperecomputing.com>"); 5304a4a4e9eSQuan Nguyen MODULE_AUTHOR("Hoang Nguyen <hnguyen@amperecomputing.com>"); 5314a4a4e9eSQuan Nguyen MODULE_AUTHOR("Thu Nguyen <thu@os.amperecomputing.com>"); 5324a4a4e9eSQuan Nguyen MODULE_AUTHOR("Quan Nguyen <quan@os.amperecomputing.com>"); 5334a4a4e9eSQuan Nguyen MODULE_DESCRIPTION("Ampere Altra SMpro driver"); 5344a4a4e9eSQuan Nguyen MODULE_LICENSE("GPL"); 535