18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Cavium ThunderX memory controller kernel module 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 58c2ecf20Sopenharmony_ci * License. See the file "COPYING" in the main directory of this archive 68c2ecf20Sopenharmony_ci * for more details. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Copyright Cavium, Inc. (C) 2015-2017. All rights reserved. 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/pci.h> 148c2ecf20Sopenharmony_ci#include <linux/edac.h> 158c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 168c2ecf20Sopenharmony_ci#include <linux/string.h> 178c2ecf20Sopenharmony_ci#include <linux/stop_machine.h> 188c2ecf20Sopenharmony_ci#include <linux/delay.h> 198c2ecf20Sopenharmony_ci#include <linux/sizes.h> 208c2ecf20Sopenharmony_ci#include <linux/atomic.h> 218c2ecf20Sopenharmony_ci#include <linux/bitfield.h> 228c2ecf20Sopenharmony_ci#include <linux/circ_buf.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#include <asm/page.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#include "edac_module.h" 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#define phys_to_pfn(phys) (PFN_DOWN(phys)) 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#define THUNDERX_NODE GENMASK(45, 44) 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cienum { 338c2ecf20Sopenharmony_ci ERR_CORRECTED = 1, 348c2ecf20Sopenharmony_ci ERR_UNCORRECTED = 2, 358c2ecf20Sopenharmony_ci ERR_UNKNOWN = 3, 368c2ecf20Sopenharmony_ci}; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci#define MAX_SYNDROME_REGS 4 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistruct error_syndrome { 418c2ecf20Sopenharmony_ci u64 reg[MAX_SYNDROME_REGS]; 428c2ecf20Sopenharmony_ci}; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistruct error_descr { 458c2ecf20Sopenharmony_ci int type; 468c2ecf20Sopenharmony_ci u64 mask; 478c2ecf20Sopenharmony_ci char *descr; 488c2ecf20Sopenharmony_ci}; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistatic void decode_register(char *str, size_t size, 518c2ecf20Sopenharmony_ci const struct error_descr *descr, 528c2ecf20Sopenharmony_ci const uint64_t reg) 538c2ecf20Sopenharmony_ci{ 548c2ecf20Sopenharmony_ci int ret = 0; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci while (descr->type && descr->mask && descr->descr) { 578c2ecf20Sopenharmony_ci if (reg & descr->mask) { 588c2ecf20Sopenharmony_ci ret = snprintf(str, size, "\n\t%s, %s", 598c2ecf20Sopenharmony_ci descr->type == ERR_CORRECTED ? 608c2ecf20Sopenharmony_ci "Corrected" : "Uncorrected", 618c2ecf20Sopenharmony_ci descr->descr); 628c2ecf20Sopenharmony_ci str += ret; 638c2ecf20Sopenharmony_ci size -= ret; 648c2ecf20Sopenharmony_ci } 658c2ecf20Sopenharmony_ci descr++; 668c2ecf20Sopenharmony_ci } 678c2ecf20Sopenharmony_ci} 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_cistatic unsigned long get_bits(unsigned long data, int pos, int width) 708c2ecf20Sopenharmony_ci{ 718c2ecf20Sopenharmony_ci return (data >> pos) & ((1 << width) - 1); 728c2ecf20Sopenharmony_ci} 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci#define L2C_CTL 0x87E080800000 758c2ecf20Sopenharmony_ci#define L2C_CTL_DISIDXALIAS BIT(0) 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci#define PCI_DEVICE_ID_THUNDER_LMC 0xa022 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci#define LMC_FADR 0x20 808c2ecf20Sopenharmony_ci#define LMC_FADR_FDIMM(x) ((x >> 37) & 0x1) 818c2ecf20Sopenharmony_ci#define LMC_FADR_FBUNK(x) ((x >> 36) & 0x1) 828c2ecf20Sopenharmony_ci#define LMC_FADR_FBANK(x) ((x >> 32) & 0xf) 838c2ecf20Sopenharmony_ci#define LMC_FADR_FROW(x) ((x >> 14) & 0xffff) 848c2ecf20Sopenharmony_ci#define LMC_FADR_FCOL(x) ((x >> 0) & 0x1fff) 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci#define LMC_NXM_FADR 0x28 878c2ecf20Sopenharmony_ci#define LMC_ECC_SYND 0x38 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci#define LMC_ECC_PARITY_TEST 0x108 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci#define LMC_INT_W1S 0x150 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci#define LMC_INT_ENA_W1C 0x158 948c2ecf20Sopenharmony_ci#define LMC_INT_ENA_W1S 0x160 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci#define LMC_CONFIG 0x188 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci#define LMC_CONFIG_BG2 BIT(62) 998c2ecf20Sopenharmony_ci#define LMC_CONFIG_RANK_ENA BIT(42) 1008c2ecf20Sopenharmony_ci#define LMC_CONFIG_PBANK_LSB(x) (((x) >> 5) & 0xF) 1018c2ecf20Sopenharmony_ci#define LMC_CONFIG_ROW_LSB(x) (((x) >> 2) & 0x7) 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci#define LMC_CONTROL 0x190 1048c2ecf20Sopenharmony_ci#define LMC_CONTROL_XOR_BANK BIT(16) 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci#define LMC_INT 0x1F0 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci#define LMC_INT_DDR_ERR BIT(11) 1098c2ecf20Sopenharmony_ci#define LMC_INT_DED_ERR (0xFUL << 5) 1108c2ecf20Sopenharmony_ci#define LMC_INT_SEC_ERR (0xFUL << 1) 1118c2ecf20Sopenharmony_ci#define LMC_INT_NXM_WR_MASK BIT(0) 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci#define LMC_DDR_PLL_CTL 0x258 1148c2ecf20Sopenharmony_ci#define LMC_DDR_PLL_CTL_DDR4 BIT(29) 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci#define LMC_FADR_SCRAMBLED 0x330 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci#define LMC_INT_UE (LMC_INT_DDR_ERR | LMC_INT_DED_ERR | \ 1198c2ecf20Sopenharmony_ci LMC_INT_NXM_WR_MASK) 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci#define LMC_INT_CE (LMC_INT_SEC_ERR) 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_cistatic const struct error_descr lmc_errors[] = { 1248c2ecf20Sopenharmony_ci { 1258c2ecf20Sopenharmony_ci .type = ERR_CORRECTED, 1268c2ecf20Sopenharmony_ci .mask = LMC_INT_SEC_ERR, 1278c2ecf20Sopenharmony_ci .descr = "Single-bit ECC error", 1288c2ecf20Sopenharmony_ci }, 1298c2ecf20Sopenharmony_ci { 1308c2ecf20Sopenharmony_ci .type = ERR_UNCORRECTED, 1318c2ecf20Sopenharmony_ci .mask = LMC_INT_DDR_ERR, 1328c2ecf20Sopenharmony_ci .descr = "DDR chip error", 1338c2ecf20Sopenharmony_ci }, 1348c2ecf20Sopenharmony_ci { 1358c2ecf20Sopenharmony_ci .type = ERR_UNCORRECTED, 1368c2ecf20Sopenharmony_ci .mask = LMC_INT_DED_ERR, 1378c2ecf20Sopenharmony_ci .descr = "Double-bit ECC error", 1388c2ecf20Sopenharmony_ci }, 1398c2ecf20Sopenharmony_ci { 1408c2ecf20Sopenharmony_ci .type = ERR_UNCORRECTED, 1418c2ecf20Sopenharmony_ci .mask = LMC_INT_NXM_WR_MASK, 1428c2ecf20Sopenharmony_ci .descr = "Non-existent memory write", 1438c2ecf20Sopenharmony_ci }, 1448c2ecf20Sopenharmony_ci {0, 0, NULL}, 1458c2ecf20Sopenharmony_ci}; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci#define LMC_INT_EN_DDR_ERROR_ALERT_ENA BIT(5) 1488c2ecf20Sopenharmony_ci#define LMC_INT_EN_DLCRAM_DED_ERR BIT(4) 1498c2ecf20Sopenharmony_ci#define LMC_INT_EN_DLCRAM_SEC_ERR BIT(3) 1508c2ecf20Sopenharmony_ci#define LMC_INT_INTR_DED_ENA BIT(2) 1518c2ecf20Sopenharmony_ci#define LMC_INT_INTR_SEC_ENA BIT(1) 1528c2ecf20Sopenharmony_ci#define LMC_INT_INTR_NXM_WR_ENA BIT(0) 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci#define LMC_INT_ENA_ALL GENMASK(5, 0) 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci#define LMC_DDR_PLL_CTL 0x258 1578c2ecf20Sopenharmony_ci#define LMC_DDR_PLL_CTL_DDR4 BIT(29) 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci#define LMC_CONTROL 0x190 1608c2ecf20Sopenharmony_ci#define LMC_CONTROL_RDIMM BIT(0) 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci#define LMC_SCRAM_FADR 0x330 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci#define LMC_CHAR_MASK0 0x228 1658c2ecf20Sopenharmony_ci#define LMC_CHAR_MASK2 0x238 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci#define RING_ENTRIES 8 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_cistruct debugfs_entry { 1708c2ecf20Sopenharmony_ci const char *name; 1718c2ecf20Sopenharmony_ci umode_t mode; 1728c2ecf20Sopenharmony_ci const struct file_operations fops; 1738c2ecf20Sopenharmony_ci}; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_cistruct lmc_err_ctx { 1768c2ecf20Sopenharmony_ci u64 reg_int; 1778c2ecf20Sopenharmony_ci u64 reg_fadr; 1788c2ecf20Sopenharmony_ci u64 reg_nxm_fadr; 1798c2ecf20Sopenharmony_ci u64 reg_scram_fadr; 1808c2ecf20Sopenharmony_ci u64 reg_ecc_synd; 1818c2ecf20Sopenharmony_ci}; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_cistruct thunderx_lmc { 1848c2ecf20Sopenharmony_ci void __iomem *regs; 1858c2ecf20Sopenharmony_ci struct pci_dev *pdev; 1868c2ecf20Sopenharmony_ci struct msix_entry msix_ent; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci atomic_t ecc_int; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci u64 mask0; 1918c2ecf20Sopenharmony_ci u64 mask2; 1928c2ecf20Sopenharmony_ci u64 parity_test; 1938c2ecf20Sopenharmony_ci u64 node; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci int xbits; 1968c2ecf20Sopenharmony_ci int bank_width; 1978c2ecf20Sopenharmony_ci int pbank_lsb; 1988c2ecf20Sopenharmony_ci int dimm_lsb; 1998c2ecf20Sopenharmony_ci int rank_lsb; 2008c2ecf20Sopenharmony_ci int bank_lsb; 2018c2ecf20Sopenharmony_ci int row_lsb; 2028c2ecf20Sopenharmony_ci int col_hi_lsb; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci int xor_bank; 2058c2ecf20Sopenharmony_ci int l2c_alias; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci struct page *mem; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci struct lmc_err_ctx err_ctx[RING_ENTRIES]; 2108c2ecf20Sopenharmony_ci unsigned long ring_head; 2118c2ecf20Sopenharmony_ci unsigned long ring_tail; 2128c2ecf20Sopenharmony_ci}; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci#define ring_pos(pos, size) ((pos) & (size - 1)) 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci#define DEBUGFS_STRUCT(_name, _mode, _write, _read) \ 2178c2ecf20Sopenharmony_cistatic struct debugfs_entry debugfs_##_name = { \ 2188c2ecf20Sopenharmony_ci .name = __stringify(_name), \ 2198c2ecf20Sopenharmony_ci .mode = VERIFY_OCTAL_PERMISSIONS(_mode), \ 2208c2ecf20Sopenharmony_ci .fops = { \ 2218c2ecf20Sopenharmony_ci .open = simple_open, \ 2228c2ecf20Sopenharmony_ci .write = _write, \ 2238c2ecf20Sopenharmony_ci .read = _read, \ 2248c2ecf20Sopenharmony_ci .llseek = generic_file_llseek, \ 2258c2ecf20Sopenharmony_ci }, \ 2268c2ecf20Sopenharmony_ci} 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci#define DEBUGFS_FIELD_ATTR(_type, _field) \ 2298c2ecf20Sopenharmony_cistatic ssize_t thunderx_##_type##_##_field##_read(struct file *file, \ 2308c2ecf20Sopenharmony_ci char __user *data, \ 2318c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) \ 2328c2ecf20Sopenharmony_ci{ \ 2338c2ecf20Sopenharmony_ci struct thunderx_##_type *pdata = file->private_data; \ 2348c2ecf20Sopenharmony_ci char buf[20]; \ 2358c2ecf20Sopenharmony_ci \ 2368c2ecf20Sopenharmony_ci snprintf(buf, count, "0x%016llx", pdata->_field); \ 2378c2ecf20Sopenharmony_ci return simple_read_from_buffer(data, count, ppos, \ 2388c2ecf20Sopenharmony_ci buf, sizeof(buf)); \ 2398c2ecf20Sopenharmony_ci} \ 2408c2ecf20Sopenharmony_ci \ 2418c2ecf20Sopenharmony_cistatic ssize_t thunderx_##_type##_##_field##_write(struct file *file, \ 2428c2ecf20Sopenharmony_ci const char __user *data, \ 2438c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) \ 2448c2ecf20Sopenharmony_ci{ \ 2458c2ecf20Sopenharmony_ci struct thunderx_##_type *pdata = file->private_data; \ 2468c2ecf20Sopenharmony_ci int res; \ 2478c2ecf20Sopenharmony_ci \ 2488c2ecf20Sopenharmony_ci res = kstrtoull_from_user(data, count, 0, &pdata->_field); \ 2498c2ecf20Sopenharmony_ci \ 2508c2ecf20Sopenharmony_ci return res ? res : count; \ 2518c2ecf20Sopenharmony_ci} \ 2528c2ecf20Sopenharmony_ci \ 2538c2ecf20Sopenharmony_ciDEBUGFS_STRUCT(_field, 0600, \ 2548c2ecf20Sopenharmony_ci thunderx_##_type##_##_field##_write, \ 2558c2ecf20Sopenharmony_ci thunderx_##_type##_##_field##_read) \ 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci#define DEBUGFS_REG_ATTR(_type, _name, _reg) \ 2588c2ecf20Sopenharmony_cistatic ssize_t thunderx_##_type##_##_name##_read(struct file *file, \ 2598c2ecf20Sopenharmony_ci char __user *data, \ 2608c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) \ 2618c2ecf20Sopenharmony_ci{ \ 2628c2ecf20Sopenharmony_ci struct thunderx_##_type *pdata = file->private_data; \ 2638c2ecf20Sopenharmony_ci char buf[20]; \ 2648c2ecf20Sopenharmony_ci \ 2658c2ecf20Sopenharmony_ci sprintf(buf, "0x%016llx", readq(pdata->regs + _reg)); \ 2668c2ecf20Sopenharmony_ci return simple_read_from_buffer(data, count, ppos, \ 2678c2ecf20Sopenharmony_ci buf, sizeof(buf)); \ 2688c2ecf20Sopenharmony_ci} \ 2698c2ecf20Sopenharmony_ci \ 2708c2ecf20Sopenharmony_cistatic ssize_t thunderx_##_type##_##_name##_write(struct file *file, \ 2718c2ecf20Sopenharmony_ci const char __user *data, \ 2728c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) \ 2738c2ecf20Sopenharmony_ci{ \ 2748c2ecf20Sopenharmony_ci struct thunderx_##_type *pdata = file->private_data; \ 2758c2ecf20Sopenharmony_ci u64 val; \ 2768c2ecf20Sopenharmony_ci int res; \ 2778c2ecf20Sopenharmony_ci \ 2788c2ecf20Sopenharmony_ci res = kstrtoull_from_user(data, count, 0, &val); \ 2798c2ecf20Sopenharmony_ci \ 2808c2ecf20Sopenharmony_ci if (!res) { \ 2818c2ecf20Sopenharmony_ci writeq(val, pdata->regs + _reg); \ 2828c2ecf20Sopenharmony_ci res = count; \ 2838c2ecf20Sopenharmony_ci } \ 2848c2ecf20Sopenharmony_ci \ 2858c2ecf20Sopenharmony_ci return res; \ 2868c2ecf20Sopenharmony_ci} \ 2878c2ecf20Sopenharmony_ci \ 2888c2ecf20Sopenharmony_ciDEBUGFS_STRUCT(_name, 0600, \ 2898c2ecf20Sopenharmony_ci thunderx_##_type##_##_name##_write, \ 2908c2ecf20Sopenharmony_ci thunderx_##_type##_##_name##_read) 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci#define LMC_DEBUGFS_ENT(_field) DEBUGFS_FIELD_ATTR(lmc, _field) 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci/* 2958c2ecf20Sopenharmony_ci * To get an ECC error injected, the following steps are needed: 2968c2ecf20Sopenharmony_ci * - Setup the ECC injection by writing the appropriate parameters: 2978c2ecf20Sopenharmony_ci * echo <bit mask value> > /sys/kernel/debug/<device number>/ecc_mask0 2988c2ecf20Sopenharmony_ci * echo <bit mask value> > /sys/kernel/debug/<device number>/ecc_mask2 2998c2ecf20Sopenharmony_ci * echo 0x802 > /sys/kernel/debug/<device number>/ecc_parity_test 3008c2ecf20Sopenharmony_ci * - Do the actual injection: 3018c2ecf20Sopenharmony_ci * echo 1 > /sys/kernel/debug/<device number>/inject_ecc 3028c2ecf20Sopenharmony_ci */ 3038c2ecf20Sopenharmony_cistatic ssize_t thunderx_lmc_inject_int_write(struct file *file, 3048c2ecf20Sopenharmony_ci const char __user *data, 3058c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 3068c2ecf20Sopenharmony_ci{ 3078c2ecf20Sopenharmony_ci struct thunderx_lmc *lmc = file->private_data; 3088c2ecf20Sopenharmony_ci u64 val; 3098c2ecf20Sopenharmony_ci int res; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci res = kstrtoull_from_user(data, count, 0, &val); 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci if (!res) { 3148c2ecf20Sopenharmony_ci /* Trigger the interrupt */ 3158c2ecf20Sopenharmony_ci writeq(val, lmc->regs + LMC_INT_W1S); 3168c2ecf20Sopenharmony_ci res = count; 3178c2ecf20Sopenharmony_ci } 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci return res; 3208c2ecf20Sopenharmony_ci} 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_cistatic ssize_t thunderx_lmc_int_read(struct file *file, 3238c2ecf20Sopenharmony_ci char __user *data, 3248c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 3258c2ecf20Sopenharmony_ci{ 3268c2ecf20Sopenharmony_ci struct thunderx_lmc *lmc = file->private_data; 3278c2ecf20Sopenharmony_ci char buf[20]; 3288c2ecf20Sopenharmony_ci u64 lmc_int = readq(lmc->regs + LMC_INT); 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci snprintf(buf, sizeof(buf), "0x%016llx", lmc_int); 3318c2ecf20Sopenharmony_ci return simple_read_from_buffer(data, count, ppos, buf, sizeof(buf)); 3328c2ecf20Sopenharmony_ci} 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci#define TEST_PATTERN 0xa5 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_cistatic int inject_ecc_fn(void *arg) 3378c2ecf20Sopenharmony_ci{ 3388c2ecf20Sopenharmony_ci struct thunderx_lmc *lmc = arg; 3398c2ecf20Sopenharmony_ci uintptr_t addr, phys; 3408c2ecf20Sopenharmony_ci unsigned int cline_size = cache_line_size(); 3418c2ecf20Sopenharmony_ci const unsigned int lines = PAGE_SIZE / cline_size; 3428c2ecf20Sopenharmony_ci unsigned int i, cl_idx; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci addr = (uintptr_t)page_address(lmc->mem); 3458c2ecf20Sopenharmony_ci phys = (uintptr_t)page_to_phys(lmc->mem); 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci cl_idx = (phys & 0x7f) >> 4; 3488c2ecf20Sopenharmony_ci lmc->parity_test &= ~(7ULL << 8); 3498c2ecf20Sopenharmony_ci lmc->parity_test |= (cl_idx << 8); 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci writeq(lmc->mask0, lmc->regs + LMC_CHAR_MASK0); 3528c2ecf20Sopenharmony_ci writeq(lmc->mask2, lmc->regs + LMC_CHAR_MASK2); 3538c2ecf20Sopenharmony_ci writeq(lmc->parity_test, lmc->regs + LMC_ECC_PARITY_TEST); 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci readq(lmc->regs + LMC_CHAR_MASK0); 3568c2ecf20Sopenharmony_ci readq(lmc->regs + LMC_CHAR_MASK2); 3578c2ecf20Sopenharmony_ci readq(lmc->regs + LMC_ECC_PARITY_TEST); 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci for (i = 0; i < lines; i++) { 3608c2ecf20Sopenharmony_ci memset((void *)addr, TEST_PATTERN, cline_size); 3618c2ecf20Sopenharmony_ci barrier(); 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci /* 3648c2ecf20Sopenharmony_ci * Flush L1 cachelines to the PoC (L2). 3658c2ecf20Sopenharmony_ci * This will cause cacheline eviction to the L2. 3668c2ecf20Sopenharmony_ci */ 3678c2ecf20Sopenharmony_ci asm volatile("dc civac, %0\n" 3688c2ecf20Sopenharmony_ci "dsb sy\n" 3698c2ecf20Sopenharmony_ci : : "r"(addr + i * cline_size)); 3708c2ecf20Sopenharmony_ci } 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci for (i = 0; i < lines; i++) { 3738c2ecf20Sopenharmony_ci /* 3748c2ecf20Sopenharmony_ci * Flush L2 cachelines to the DRAM. 3758c2ecf20Sopenharmony_ci * This will cause cacheline eviction to the DRAM 3768c2ecf20Sopenharmony_ci * and ECC corruption according to the masks set. 3778c2ecf20Sopenharmony_ci */ 3788c2ecf20Sopenharmony_ci __asm__ volatile("sys #0,c11,C1,#2, %0\n" 3798c2ecf20Sopenharmony_ci : : "r"(phys + i * cline_size)); 3808c2ecf20Sopenharmony_ci } 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci for (i = 0; i < lines; i++) { 3838c2ecf20Sopenharmony_ci /* 3848c2ecf20Sopenharmony_ci * Invalidate L2 cachelines. 3858c2ecf20Sopenharmony_ci * The subsequent load will cause cacheline fetch 3868c2ecf20Sopenharmony_ci * from the DRAM and an error interrupt 3878c2ecf20Sopenharmony_ci */ 3888c2ecf20Sopenharmony_ci __asm__ volatile("sys #0,c11,C1,#1, %0" 3898c2ecf20Sopenharmony_ci : : "r"(phys + i * cline_size)); 3908c2ecf20Sopenharmony_ci } 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci for (i = 0; i < lines; i++) { 3938c2ecf20Sopenharmony_ci /* 3948c2ecf20Sopenharmony_ci * Invalidate L1 cachelines. 3958c2ecf20Sopenharmony_ci * The subsequent load will cause cacheline fetch 3968c2ecf20Sopenharmony_ci * from the L2 and/or DRAM 3978c2ecf20Sopenharmony_ci */ 3988c2ecf20Sopenharmony_ci asm volatile("dc ivac, %0\n" 3998c2ecf20Sopenharmony_ci "dsb sy\n" 4008c2ecf20Sopenharmony_ci : : "r"(addr + i * cline_size)); 4018c2ecf20Sopenharmony_ci } 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci return 0; 4048c2ecf20Sopenharmony_ci} 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_cistatic ssize_t thunderx_lmc_inject_ecc_write(struct file *file, 4078c2ecf20Sopenharmony_ci const char __user *data, 4088c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 4098c2ecf20Sopenharmony_ci{ 4108c2ecf20Sopenharmony_ci struct thunderx_lmc *lmc = file->private_data; 4118c2ecf20Sopenharmony_ci unsigned int cline_size = cache_line_size(); 4128c2ecf20Sopenharmony_ci u8 *tmp; 4138c2ecf20Sopenharmony_ci void __iomem *addr; 4148c2ecf20Sopenharmony_ci unsigned int offs, timeout = 100000; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci atomic_set(&lmc->ecc_int, 0); 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci lmc->mem = alloc_pages_node(lmc->node, GFP_KERNEL, 0); 4198c2ecf20Sopenharmony_ci if (!lmc->mem) 4208c2ecf20Sopenharmony_ci return -ENOMEM; 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci tmp = kmalloc(cline_size, GFP_KERNEL); 4238c2ecf20Sopenharmony_ci if (!tmp) { 4248c2ecf20Sopenharmony_ci __free_pages(lmc->mem, 0); 4258c2ecf20Sopenharmony_ci return -ENOMEM; 4268c2ecf20Sopenharmony_ci } 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci addr = page_address(lmc->mem); 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci while (!atomic_read(&lmc->ecc_int) && timeout--) { 4318c2ecf20Sopenharmony_ci stop_machine(inject_ecc_fn, lmc, NULL); 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci for (offs = 0; offs < PAGE_SIZE; offs += cline_size) { 4348c2ecf20Sopenharmony_ci /* 4358c2ecf20Sopenharmony_ci * Do a load from the previously rigged location 4368c2ecf20Sopenharmony_ci * This should generate an error interrupt. 4378c2ecf20Sopenharmony_ci */ 4388c2ecf20Sopenharmony_ci memcpy(tmp, addr + offs, cline_size); 4398c2ecf20Sopenharmony_ci asm volatile("dsb ld\n"); 4408c2ecf20Sopenharmony_ci } 4418c2ecf20Sopenharmony_ci } 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci kfree(tmp); 4448c2ecf20Sopenharmony_ci __free_pages(lmc->mem, 0); 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci return count; 4478c2ecf20Sopenharmony_ci} 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ciLMC_DEBUGFS_ENT(mask0); 4508c2ecf20Sopenharmony_ciLMC_DEBUGFS_ENT(mask2); 4518c2ecf20Sopenharmony_ciLMC_DEBUGFS_ENT(parity_test); 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ciDEBUGFS_STRUCT(inject_int, 0200, thunderx_lmc_inject_int_write, NULL); 4548c2ecf20Sopenharmony_ciDEBUGFS_STRUCT(inject_ecc, 0200, thunderx_lmc_inject_ecc_write, NULL); 4558c2ecf20Sopenharmony_ciDEBUGFS_STRUCT(int_w1c, 0400, NULL, thunderx_lmc_int_read); 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_cistatic struct debugfs_entry *lmc_dfs_ents[] = { 4588c2ecf20Sopenharmony_ci &debugfs_mask0, 4598c2ecf20Sopenharmony_ci &debugfs_mask2, 4608c2ecf20Sopenharmony_ci &debugfs_parity_test, 4618c2ecf20Sopenharmony_ci &debugfs_inject_ecc, 4628c2ecf20Sopenharmony_ci &debugfs_inject_int, 4638c2ecf20Sopenharmony_ci &debugfs_int_w1c, 4648c2ecf20Sopenharmony_ci}; 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_cistatic int thunderx_create_debugfs_nodes(struct dentry *parent, 4678c2ecf20Sopenharmony_ci struct debugfs_entry *attrs[], 4688c2ecf20Sopenharmony_ci void *data, 4698c2ecf20Sopenharmony_ci size_t num) 4708c2ecf20Sopenharmony_ci{ 4718c2ecf20Sopenharmony_ci int i; 4728c2ecf20Sopenharmony_ci struct dentry *ent; 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci if (!IS_ENABLED(CONFIG_EDAC_DEBUG)) 4758c2ecf20Sopenharmony_ci return 0; 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci if (!parent) 4788c2ecf20Sopenharmony_ci return -ENOENT; 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci for (i = 0; i < num; i++) { 4818c2ecf20Sopenharmony_ci ent = edac_debugfs_create_file(attrs[i]->name, attrs[i]->mode, 4828c2ecf20Sopenharmony_ci parent, data, &attrs[i]->fops); 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci if (!ent) 4858c2ecf20Sopenharmony_ci break; 4868c2ecf20Sopenharmony_ci } 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci return i; 4898c2ecf20Sopenharmony_ci} 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_cistatic phys_addr_t thunderx_faddr_to_phys(u64 faddr, struct thunderx_lmc *lmc) 4928c2ecf20Sopenharmony_ci{ 4938c2ecf20Sopenharmony_ci phys_addr_t addr = 0; 4948c2ecf20Sopenharmony_ci int bank, xbits; 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci addr |= lmc->node << 40; 4978c2ecf20Sopenharmony_ci addr |= LMC_FADR_FDIMM(faddr) << lmc->dimm_lsb; 4988c2ecf20Sopenharmony_ci addr |= LMC_FADR_FBUNK(faddr) << lmc->rank_lsb; 4998c2ecf20Sopenharmony_ci addr |= LMC_FADR_FROW(faddr) << lmc->row_lsb; 5008c2ecf20Sopenharmony_ci addr |= (LMC_FADR_FCOL(faddr) >> 4) << lmc->col_hi_lsb; 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci bank = LMC_FADR_FBANK(faddr) << lmc->bank_lsb; 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci if (lmc->xor_bank) 5058c2ecf20Sopenharmony_ci bank ^= get_bits(addr, 12 + lmc->xbits, lmc->bank_width); 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci addr |= bank << lmc->bank_lsb; 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci xbits = PCI_FUNC(lmc->pdev->devfn); 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci if (lmc->l2c_alias) 5128c2ecf20Sopenharmony_ci xbits ^= get_bits(addr, 20, lmc->xbits) ^ 5138c2ecf20Sopenharmony_ci get_bits(addr, 12, lmc->xbits); 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci addr |= xbits << 7; 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci return addr; 5188c2ecf20Sopenharmony_ci} 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_cistatic unsigned int thunderx_get_num_lmcs(unsigned int node) 5218c2ecf20Sopenharmony_ci{ 5228c2ecf20Sopenharmony_ci unsigned int number = 0; 5238c2ecf20Sopenharmony_ci struct pci_dev *pdev = NULL; 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci do { 5268c2ecf20Sopenharmony_ci pdev = pci_get_device(PCI_VENDOR_ID_CAVIUM, 5278c2ecf20Sopenharmony_ci PCI_DEVICE_ID_THUNDER_LMC, 5288c2ecf20Sopenharmony_ci pdev); 5298c2ecf20Sopenharmony_ci if (pdev) { 5308c2ecf20Sopenharmony_ci#ifdef CONFIG_NUMA 5318c2ecf20Sopenharmony_ci if (pdev->dev.numa_node == node) 5328c2ecf20Sopenharmony_ci number++; 5338c2ecf20Sopenharmony_ci#else 5348c2ecf20Sopenharmony_ci number++; 5358c2ecf20Sopenharmony_ci#endif 5368c2ecf20Sopenharmony_ci } 5378c2ecf20Sopenharmony_ci } while (pdev); 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci return number; 5408c2ecf20Sopenharmony_ci} 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci#define LMC_MESSAGE_SIZE 120 5438c2ecf20Sopenharmony_ci#define LMC_OTHER_SIZE (50 * ARRAY_SIZE(lmc_errors)) 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_cistatic irqreturn_t thunderx_lmc_err_isr(int irq, void *dev_id) 5468c2ecf20Sopenharmony_ci{ 5478c2ecf20Sopenharmony_ci struct mem_ctl_info *mci = dev_id; 5488c2ecf20Sopenharmony_ci struct thunderx_lmc *lmc = mci->pvt_info; 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci unsigned long head = ring_pos(lmc->ring_head, ARRAY_SIZE(lmc->err_ctx)); 5518c2ecf20Sopenharmony_ci struct lmc_err_ctx *ctx = &lmc->err_ctx[head]; 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci writeq(0, lmc->regs + LMC_CHAR_MASK0); 5548c2ecf20Sopenharmony_ci writeq(0, lmc->regs + LMC_CHAR_MASK2); 5558c2ecf20Sopenharmony_ci writeq(0x2, lmc->regs + LMC_ECC_PARITY_TEST); 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci ctx->reg_int = readq(lmc->regs + LMC_INT); 5588c2ecf20Sopenharmony_ci ctx->reg_fadr = readq(lmc->regs + LMC_FADR); 5598c2ecf20Sopenharmony_ci ctx->reg_nxm_fadr = readq(lmc->regs + LMC_NXM_FADR); 5608c2ecf20Sopenharmony_ci ctx->reg_scram_fadr = readq(lmc->regs + LMC_SCRAM_FADR); 5618c2ecf20Sopenharmony_ci ctx->reg_ecc_synd = readq(lmc->regs + LMC_ECC_SYND); 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci lmc->ring_head++; 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci atomic_set(&lmc->ecc_int, 1); 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci /* Clear the interrupt */ 5688c2ecf20Sopenharmony_ci writeq(ctx->reg_int, lmc->regs + LMC_INT); 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci return IRQ_WAKE_THREAD; 5718c2ecf20Sopenharmony_ci} 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_cistatic irqreturn_t thunderx_lmc_threaded_isr(int irq, void *dev_id) 5748c2ecf20Sopenharmony_ci{ 5758c2ecf20Sopenharmony_ci struct mem_ctl_info *mci = dev_id; 5768c2ecf20Sopenharmony_ci struct thunderx_lmc *lmc = mci->pvt_info; 5778c2ecf20Sopenharmony_ci phys_addr_t phys_addr; 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci unsigned long tail; 5808c2ecf20Sopenharmony_ci struct lmc_err_ctx *ctx; 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci irqreturn_t ret = IRQ_NONE; 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci char *msg; 5858c2ecf20Sopenharmony_ci char *other; 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci msg = kmalloc(LMC_MESSAGE_SIZE, GFP_KERNEL); 5888c2ecf20Sopenharmony_ci other = kmalloc(LMC_OTHER_SIZE, GFP_KERNEL); 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci if (!msg || !other) 5918c2ecf20Sopenharmony_ci goto err_free; 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci while (CIRC_CNT(lmc->ring_head, lmc->ring_tail, 5948c2ecf20Sopenharmony_ci ARRAY_SIZE(lmc->err_ctx))) { 5958c2ecf20Sopenharmony_ci tail = ring_pos(lmc->ring_tail, ARRAY_SIZE(lmc->err_ctx)); 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci ctx = &lmc->err_ctx[tail]; 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci dev_dbg(&lmc->pdev->dev, "LMC_INT: %016llx\n", 6008c2ecf20Sopenharmony_ci ctx->reg_int); 6018c2ecf20Sopenharmony_ci dev_dbg(&lmc->pdev->dev, "LMC_FADR: %016llx\n", 6028c2ecf20Sopenharmony_ci ctx->reg_fadr); 6038c2ecf20Sopenharmony_ci dev_dbg(&lmc->pdev->dev, "LMC_NXM_FADR: %016llx\n", 6048c2ecf20Sopenharmony_ci ctx->reg_nxm_fadr); 6058c2ecf20Sopenharmony_ci dev_dbg(&lmc->pdev->dev, "LMC_SCRAM_FADR: %016llx\n", 6068c2ecf20Sopenharmony_ci ctx->reg_scram_fadr); 6078c2ecf20Sopenharmony_ci dev_dbg(&lmc->pdev->dev, "LMC_ECC_SYND: %016llx\n", 6088c2ecf20Sopenharmony_ci ctx->reg_ecc_synd); 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci snprintf(msg, LMC_MESSAGE_SIZE, 6118c2ecf20Sopenharmony_ci "DIMM %lld rank %lld bank %lld row %lld col %lld", 6128c2ecf20Sopenharmony_ci LMC_FADR_FDIMM(ctx->reg_scram_fadr), 6138c2ecf20Sopenharmony_ci LMC_FADR_FBUNK(ctx->reg_scram_fadr), 6148c2ecf20Sopenharmony_ci LMC_FADR_FBANK(ctx->reg_scram_fadr), 6158c2ecf20Sopenharmony_ci LMC_FADR_FROW(ctx->reg_scram_fadr), 6168c2ecf20Sopenharmony_ci LMC_FADR_FCOL(ctx->reg_scram_fadr)); 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci decode_register(other, LMC_OTHER_SIZE, lmc_errors, 6198c2ecf20Sopenharmony_ci ctx->reg_int); 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci phys_addr = thunderx_faddr_to_phys(ctx->reg_fadr, lmc); 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci if (ctx->reg_int & LMC_INT_UE) 6248c2ecf20Sopenharmony_ci edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, 6258c2ecf20Sopenharmony_ci phys_to_pfn(phys_addr), 6268c2ecf20Sopenharmony_ci offset_in_page(phys_addr), 6278c2ecf20Sopenharmony_ci 0, -1, -1, -1, msg, other); 6288c2ecf20Sopenharmony_ci else if (ctx->reg_int & LMC_INT_CE) 6298c2ecf20Sopenharmony_ci edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1, 6308c2ecf20Sopenharmony_ci phys_to_pfn(phys_addr), 6318c2ecf20Sopenharmony_ci offset_in_page(phys_addr), 6328c2ecf20Sopenharmony_ci 0, -1, -1, -1, msg, other); 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci lmc->ring_tail++; 6358c2ecf20Sopenharmony_ci } 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci ret = IRQ_HANDLED; 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_cierr_free: 6408c2ecf20Sopenharmony_ci kfree(msg); 6418c2ecf20Sopenharmony_ci kfree(other); 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci return ret; 6448c2ecf20Sopenharmony_ci} 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_cistatic const struct pci_device_id thunderx_lmc_pci_tbl[] = { 6478c2ecf20Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, PCI_DEVICE_ID_THUNDER_LMC) }, 6488c2ecf20Sopenharmony_ci { 0, }, 6498c2ecf20Sopenharmony_ci}; 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_cistatic inline int pci_dev_to_mc_idx(struct pci_dev *pdev) 6528c2ecf20Sopenharmony_ci{ 6538c2ecf20Sopenharmony_ci int node = dev_to_node(&pdev->dev); 6548c2ecf20Sopenharmony_ci int ret = PCI_FUNC(pdev->devfn); 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci ret += max(node, 0) << 3; 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci return ret; 6598c2ecf20Sopenharmony_ci} 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_cistatic int thunderx_lmc_probe(struct pci_dev *pdev, 6628c2ecf20Sopenharmony_ci const struct pci_device_id *id) 6638c2ecf20Sopenharmony_ci{ 6648c2ecf20Sopenharmony_ci struct thunderx_lmc *lmc; 6658c2ecf20Sopenharmony_ci struct edac_mc_layer layer; 6668c2ecf20Sopenharmony_ci struct mem_ctl_info *mci; 6678c2ecf20Sopenharmony_ci u64 lmc_control, lmc_ddr_pll_ctl, lmc_config; 6688c2ecf20Sopenharmony_ci int ret; 6698c2ecf20Sopenharmony_ci u64 lmc_int; 6708c2ecf20Sopenharmony_ci void *l2c_ioaddr; 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci layer.type = EDAC_MC_LAYER_SLOT; 6738c2ecf20Sopenharmony_ci layer.size = 2; 6748c2ecf20Sopenharmony_ci layer.is_virt_csrow = false; 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci ret = pcim_enable_device(pdev); 6778c2ecf20Sopenharmony_ci if (ret) { 6788c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Cannot enable PCI device: %d\n", ret); 6798c2ecf20Sopenharmony_ci return ret; 6808c2ecf20Sopenharmony_ci } 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci ret = pcim_iomap_regions(pdev, BIT(0), "thunderx_lmc"); 6838c2ecf20Sopenharmony_ci if (ret) { 6848c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Cannot map PCI resources: %d\n", ret); 6858c2ecf20Sopenharmony_ci return ret; 6868c2ecf20Sopenharmony_ci } 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci mci = edac_mc_alloc(pci_dev_to_mc_idx(pdev), 1, &layer, 6898c2ecf20Sopenharmony_ci sizeof(struct thunderx_lmc)); 6908c2ecf20Sopenharmony_ci if (!mci) 6918c2ecf20Sopenharmony_ci return -ENOMEM; 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ci mci->pdev = &pdev->dev; 6948c2ecf20Sopenharmony_ci lmc = mci->pvt_info; 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci pci_set_drvdata(pdev, mci); 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci lmc->regs = pcim_iomap_table(pdev)[0]; 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci lmc_control = readq(lmc->regs + LMC_CONTROL); 7018c2ecf20Sopenharmony_ci lmc_ddr_pll_ctl = readq(lmc->regs + LMC_DDR_PLL_CTL); 7028c2ecf20Sopenharmony_ci lmc_config = readq(lmc->regs + LMC_CONFIG); 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci if (lmc_control & LMC_CONTROL_RDIMM) { 7058c2ecf20Sopenharmony_ci mci->mtype_cap = FIELD_GET(LMC_DDR_PLL_CTL_DDR4, 7068c2ecf20Sopenharmony_ci lmc_ddr_pll_ctl) ? 7078c2ecf20Sopenharmony_ci MEM_RDDR4 : MEM_RDDR3; 7088c2ecf20Sopenharmony_ci } else { 7098c2ecf20Sopenharmony_ci mci->mtype_cap = FIELD_GET(LMC_DDR_PLL_CTL_DDR4, 7108c2ecf20Sopenharmony_ci lmc_ddr_pll_ctl) ? 7118c2ecf20Sopenharmony_ci MEM_DDR4 : MEM_DDR3; 7128c2ecf20Sopenharmony_ci } 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED; 7158c2ecf20Sopenharmony_ci mci->edac_cap = EDAC_FLAG_SECDED; 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci mci->mod_name = "thunderx-lmc"; 7188c2ecf20Sopenharmony_ci mci->ctl_name = "thunderx-lmc"; 7198c2ecf20Sopenharmony_ci mci->dev_name = dev_name(&pdev->dev); 7208c2ecf20Sopenharmony_ci mci->scrub_mode = SCRUB_NONE; 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci lmc->pdev = pdev; 7238c2ecf20Sopenharmony_ci lmc->msix_ent.entry = 0; 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci lmc->ring_head = 0; 7268c2ecf20Sopenharmony_ci lmc->ring_tail = 0; 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci ret = pci_enable_msix_exact(pdev, &lmc->msix_ent, 1); 7298c2ecf20Sopenharmony_ci if (ret) { 7308c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Cannot enable interrupt: %d\n", ret); 7318c2ecf20Sopenharmony_ci goto err_free; 7328c2ecf20Sopenharmony_ci } 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci ret = devm_request_threaded_irq(&pdev->dev, lmc->msix_ent.vector, 7358c2ecf20Sopenharmony_ci thunderx_lmc_err_isr, 7368c2ecf20Sopenharmony_ci thunderx_lmc_threaded_isr, 0, 7378c2ecf20Sopenharmony_ci "[EDAC] ThunderX LMC", mci); 7388c2ecf20Sopenharmony_ci if (ret) { 7398c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Cannot set ISR: %d\n", ret); 7408c2ecf20Sopenharmony_ci goto err_free; 7418c2ecf20Sopenharmony_ci } 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_ci lmc->node = FIELD_GET(THUNDERX_NODE, pci_resource_start(pdev, 0)); 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_ci lmc->xbits = thunderx_get_num_lmcs(lmc->node) >> 1; 7468c2ecf20Sopenharmony_ci lmc->bank_width = (FIELD_GET(LMC_DDR_PLL_CTL_DDR4, lmc_ddr_pll_ctl) && 7478c2ecf20Sopenharmony_ci FIELD_GET(LMC_CONFIG_BG2, lmc_config)) ? 4 : 3; 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_ci lmc->pbank_lsb = (lmc_config >> 5) & 0xf; 7508c2ecf20Sopenharmony_ci lmc->dimm_lsb = 28 + lmc->pbank_lsb + lmc->xbits; 7518c2ecf20Sopenharmony_ci lmc->rank_lsb = lmc->dimm_lsb; 7528c2ecf20Sopenharmony_ci lmc->rank_lsb -= FIELD_GET(LMC_CONFIG_RANK_ENA, lmc_config) ? 1 : 0; 7538c2ecf20Sopenharmony_ci lmc->bank_lsb = 7 + lmc->xbits; 7548c2ecf20Sopenharmony_ci lmc->row_lsb = 14 + LMC_CONFIG_ROW_LSB(lmc_config) + lmc->xbits; 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci lmc->col_hi_lsb = lmc->bank_lsb + lmc->bank_width; 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ci lmc->xor_bank = lmc_control & LMC_CONTROL_XOR_BANK; 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_ci l2c_ioaddr = ioremap(L2C_CTL | FIELD_PREP(THUNDERX_NODE, lmc->node), PAGE_SIZE); 7618c2ecf20Sopenharmony_ci if (!l2c_ioaddr) { 7628c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Cannot map L2C_CTL\n"); 7638c2ecf20Sopenharmony_ci ret = -ENOMEM; 7648c2ecf20Sopenharmony_ci goto err_free; 7658c2ecf20Sopenharmony_ci } 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci lmc->l2c_alias = !(readq(l2c_ioaddr) & L2C_CTL_DISIDXALIAS); 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_ci iounmap(l2c_ioaddr); 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci ret = edac_mc_add_mc(mci); 7728c2ecf20Sopenharmony_ci if (ret) { 7738c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Cannot add the MC: %d\n", ret); 7748c2ecf20Sopenharmony_ci goto err_free; 7758c2ecf20Sopenharmony_ci } 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ci lmc_int = readq(lmc->regs + LMC_INT); 7788c2ecf20Sopenharmony_ci writeq(lmc_int, lmc->regs + LMC_INT); 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_ci writeq(LMC_INT_ENA_ALL, lmc->regs + LMC_INT_ENA_W1S); 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_EDAC_DEBUG)) { 7838c2ecf20Sopenharmony_ci ret = thunderx_create_debugfs_nodes(mci->debugfs, 7848c2ecf20Sopenharmony_ci lmc_dfs_ents, 7858c2ecf20Sopenharmony_ci lmc, 7868c2ecf20Sopenharmony_ci ARRAY_SIZE(lmc_dfs_ents)); 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci if (ret != ARRAY_SIZE(lmc_dfs_ents)) { 7898c2ecf20Sopenharmony_ci dev_warn(&pdev->dev, "Error creating debugfs entries: %d%s\n", 7908c2ecf20Sopenharmony_ci ret, ret >= 0 ? " created" : ""); 7918c2ecf20Sopenharmony_ci } 7928c2ecf20Sopenharmony_ci } 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_ci return 0; 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_cierr_free: 7978c2ecf20Sopenharmony_ci pci_set_drvdata(pdev, NULL); 7988c2ecf20Sopenharmony_ci edac_mc_free(mci); 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ci return ret; 8018c2ecf20Sopenharmony_ci} 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_cistatic void thunderx_lmc_remove(struct pci_dev *pdev) 8048c2ecf20Sopenharmony_ci{ 8058c2ecf20Sopenharmony_ci struct mem_ctl_info *mci = pci_get_drvdata(pdev); 8068c2ecf20Sopenharmony_ci struct thunderx_lmc *lmc = mci->pvt_info; 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ci writeq(LMC_INT_ENA_ALL, lmc->regs + LMC_INT_ENA_W1C); 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_ci edac_mc_del_mc(&pdev->dev); 8118c2ecf20Sopenharmony_ci edac_mc_free(mci); 8128c2ecf20Sopenharmony_ci} 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, thunderx_lmc_pci_tbl); 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_cistatic struct pci_driver thunderx_lmc_driver = { 8178c2ecf20Sopenharmony_ci .name = "thunderx_lmc_edac", 8188c2ecf20Sopenharmony_ci .probe = thunderx_lmc_probe, 8198c2ecf20Sopenharmony_ci .remove = thunderx_lmc_remove, 8208c2ecf20Sopenharmony_ci .id_table = thunderx_lmc_pci_tbl, 8218c2ecf20Sopenharmony_ci}; 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci/*---------------------- OCX driver ---------------------------------*/ 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_ci#define PCI_DEVICE_ID_THUNDER_OCX 0xa013 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_ci#define OCX_LINK_INTS 3 8288c2ecf20Sopenharmony_ci#define OCX_INTS (OCX_LINK_INTS + 1) 8298c2ecf20Sopenharmony_ci#define OCX_RX_LANES 24 8308c2ecf20Sopenharmony_ci#define OCX_RX_LANE_STATS 15 8318c2ecf20Sopenharmony_ci 8328c2ecf20Sopenharmony_ci#define OCX_COM_INT 0x100 8338c2ecf20Sopenharmony_ci#define OCX_COM_INT_W1S 0x108 8348c2ecf20Sopenharmony_ci#define OCX_COM_INT_ENA_W1S 0x110 8358c2ecf20Sopenharmony_ci#define OCX_COM_INT_ENA_W1C 0x118 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci#define OCX_COM_IO_BADID BIT(54) 8388c2ecf20Sopenharmony_ci#define OCX_COM_MEM_BADID BIT(53) 8398c2ecf20Sopenharmony_ci#define OCX_COM_COPR_BADID BIT(52) 8408c2ecf20Sopenharmony_ci#define OCX_COM_WIN_REQ_BADID BIT(51) 8418c2ecf20Sopenharmony_ci#define OCX_COM_WIN_REQ_TOUT BIT(50) 8428c2ecf20Sopenharmony_ci#define OCX_COM_RX_LANE GENMASK(23, 0) 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci#define OCX_COM_INT_CE (OCX_COM_IO_BADID | \ 8458c2ecf20Sopenharmony_ci OCX_COM_MEM_BADID | \ 8468c2ecf20Sopenharmony_ci OCX_COM_COPR_BADID | \ 8478c2ecf20Sopenharmony_ci OCX_COM_WIN_REQ_BADID | \ 8488c2ecf20Sopenharmony_ci OCX_COM_WIN_REQ_TOUT) 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_cistatic const struct error_descr ocx_com_errors[] = { 8518c2ecf20Sopenharmony_ci { 8528c2ecf20Sopenharmony_ci .type = ERR_CORRECTED, 8538c2ecf20Sopenharmony_ci .mask = OCX_COM_IO_BADID, 8548c2ecf20Sopenharmony_ci .descr = "Invalid IO transaction node ID", 8558c2ecf20Sopenharmony_ci }, 8568c2ecf20Sopenharmony_ci { 8578c2ecf20Sopenharmony_ci .type = ERR_CORRECTED, 8588c2ecf20Sopenharmony_ci .mask = OCX_COM_MEM_BADID, 8598c2ecf20Sopenharmony_ci .descr = "Invalid memory transaction node ID", 8608c2ecf20Sopenharmony_ci }, 8618c2ecf20Sopenharmony_ci { 8628c2ecf20Sopenharmony_ci .type = ERR_CORRECTED, 8638c2ecf20Sopenharmony_ci .mask = OCX_COM_COPR_BADID, 8648c2ecf20Sopenharmony_ci .descr = "Invalid coprocessor transaction node ID", 8658c2ecf20Sopenharmony_ci }, 8668c2ecf20Sopenharmony_ci { 8678c2ecf20Sopenharmony_ci .type = ERR_CORRECTED, 8688c2ecf20Sopenharmony_ci .mask = OCX_COM_WIN_REQ_BADID, 8698c2ecf20Sopenharmony_ci .descr = "Invalid SLI transaction node ID", 8708c2ecf20Sopenharmony_ci }, 8718c2ecf20Sopenharmony_ci { 8728c2ecf20Sopenharmony_ci .type = ERR_CORRECTED, 8738c2ecf20Sopenharmony_ci .mask = OCX_COM_WIN_REQ_TOUT, 8748c2ecf20Sopenharmony_ci .descr = "Window/core request timeout", 8758c2ecf20Sopenharmony_ci }, 8768c2ecf20Sopenharmony_ci {0, 0, NULL}, 8778c2ecf20Sopenharmony_ci}; 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_ci#define OCX_COM_LINKX_INT(x) (0x120 + (x) * 8) 8808c2ecf20Sopenharmony_ci#define OCX_COM_LINKX_INT_W1S(x) (0x140 + (x) * 8) 8818c2ecf20Sopenharmony_ci#define OCX_COM_LINKX_INT_ENA_W1S(x) (0x160 + (x) * 8) 8828c2ecf20Sopenharmony_ci#define OCX_COM_LINKX_INT_ENA_W1C(x) (0x180 + (x) * 8) 8838c2ecf20Sopenharmony_ci 8848c2ecf20Sopenharmony_ci#define OCX_COM_LINK_BAD_WORD BIT(13) 8858c2ecf20Sopenharmony_ci#define OCX_COM_LINK_ALIGN_FAIL BIT(12) 8868c2ecf20Sopenharmony_ci#define OCX_COM_LINK_ALIGN_DONE BIT(11) 8878c2ecf20Sopenharmony_ci#define OCX_COM_LINK_UP BIT(10) 8888c2ecf20Sopenharmony_ci#define OCX_COM_LINK_STOP BIT(9) 8898c2ecf20Sopenharmony_ci#define OCX_COM_LINK_BLK_ERR BIT(8) 8908c2ecf20Sopenharmony_ci#define OCX_COM_LINK_REINIT BIT(7) 8918c2ecf20Sopenharmony_ci#define OCX_COM_LINK_LNK_DATA BIT(6) 8928c2ecf20Sopenharmony_ci#define OCX_COM_LINK_RXFIFO_DBE BIT(5) 8938c2ecf20Sopenharmony_ci#define OCX_COM_LINK_RXFIFO_SBE BIT(4) 8948c2ecf20Sopenharmony_ci#define OCX_COM_LINK_TXFIFO_DBE BIT(3) 8958c2ecf20Sopenharmony_ci#define OCX_COM_LINK_TXFIFO_SBE BIT(2) 8968c2ecf20Sopenharmony_ci#define OCX_COM_LINK_REPLAY_DBE BIT(1) 8978c2ecf20Sopenharmony_ci#define OCX_COM_LINK_REPLAY_SBE BIT(0) 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_cistatic const struct error_descr ocx_com_link_errors[] = { 9008c2ecf20Sopenharmony_ci { 9018c2ecf20Sopenharmony_ci .type = ERR_CORRECTED, 9028c2ecf20Sopenharmony_ci .mask = OCX_COM_LINK_REPLAY_SBE, 9038c2ecf20Sopenharmony_ci .descr = "Replay buffer single-bit error", 9048c2ecf20Sopenharmony_ci }, 9058c2ecf20Sopenharmony_ci { 9068c2ecf20Sopenharmony_ci .type = ERR_CORRECTED, 9078c2ecf20Sopenharmony_ci .mask = OCX_COM_LINK_TXFIFO_SBE, 9088c2ecf20Sopenharmony_ci .descr = "TX FIFO single-bit error", 9098c2ecf20Sopenharmony_ci }, 9108c2ecf20Sopenharmony_ci { 9118c2ecf20Sopenharmony_ci .type = ERR_CORRECTED, 9128c2ecf20Sopenharmony_ci .mask = OCX_COM_LINK_RXFIFO_SBE, 9138c2ecf20Sopenharmony_ci .descr = "RX FIFO single-bit error", 9148c2ecf20Sopenharmony_ci }, 9158c2ecf20Sopenharmony_ci { 9168c2ecf20Sopenharmony_ci .type = ERR_CORRECTED, 9178c2ecf20Sopenharmony_ci .mask = OCX_COM_LINK_BLK_ERR, 9188c2ecf20Sopenharmony_ci .descr = "Block code error", 9198c2ecf20Sopenharmony_ci }, 9208c2ecf20Sopenharmony_ci { 9218c2ecf20Sopenharmony_ci .type = ERR_CORRECTED, 9228c2ecf20Sopenharmony_ci .mask = OCX_COM_LINK_ALIGN_FAIL, 9238c2ecf20Sopenharmony_ci .descr = "Link alignment failure", 9248c2ecf20Sopenharmony_ci }, 9258c2ecf20Sopenharmony_ci { 9268c2ecf20Sopenharmony_ci .type = ERR_CORRECTED, 9278c2ecf20Sopenharmony_ci .mask = OCX_COM_LINK_BAD_WORD, 9288c2ecf20Sopenharmony_ci .descr = "Bad code word", 9298c2ecf20Sopenharmony_ci }, 9308c2ecf20Sopenharmony_ci { 9318c2ecf20Sopenharmony_ci .type = ERR_UNCORRECTED, 9328c2ecf20Sopenharmony_ci .mask = OCX_COM_LINK_REPLAY_DBE, 9338c2ecf20Sopenharmony_ci .descr = "Replay buffer double-bit error", 9348c2ecf20Sopenharmony_ci }, 9358c2ecf20Sopenharmony_ci { 9368c2ecf20Sopenharmony_ci .type = ERR_UNCORRECTED, 9378c2ecf20Sopenharmony_ci .mask = OCX_COM_LINK_TXFIFO_DBE, 9388c2ecf20Sopenharmony_ci .descr = "TX FIFO double-bit error", 9398c2ecf20Sopenharmony_ci }, 9408c2ecf20Sopenharmony_ci { 9418c2ecf20Sopenharmony_ci .type = ERR_UNCORRECTED, 9428c2ecf20Sopenharmony_ci .mask = OCX_COM_LINK_RXFIFO_DBE, 9438c2ecf20Sopenharmony_ci .descr = "RX FIFO double-bit error", 9448c2ecf20Sopenharmony_ci }, 9458c2ecf20Sopenharmony_ci { 9468c2ecf20Sopenharmony_ci .type = ERR_UNCORRECTED, 9478c2ecf20Sopenharmony_ci .mask = OCX_COM_LINK_STOP, 9488c2ecf20Sopenharmony_ci .descr = "Link stopped", 9498c2ecf20Sopenharmony_ci }, 9508c2ecf20Sopenharmony_ci {0, 0, NULL}, 9518c2ecf20Sopenharmony_ci}; 9528c2ecf20Sopenharmony_ci 9538c2ecf20Sopenharmony_ci#define OCX_COM_LINK_INT_UE (OCX_COM_LINK_REPLAY_DBE | \ 9548c2ecf20Sopenharmony_ci OCX_COM_LINK_TXFIFO_DBE | \ 9558c2ecf20Sopenharmony_ci OCX_COM_LINK_RXFIFO_DBE | \ 9568c2ecf20Sopenharmony_ci OCX_COM_LINK_STOP) 9578c2ecf20Sopenharmony_ci 9588c2ecf20Sopenharmony_ci#define OCX_COM_LINK_INT_CE (OCX_COM_LINK_REPLAY_SBE | \ 9598c2ecf20Sopenharmony_ci OCX_COM_LINK_TXFIFO_SBE | \ 9608c2ecf20Sopenharmony_ci OCX_COM_LINK_RXFIFO_SBE | \ 9618c2ecf20Sopenharmony_ci OCX_COM_LINK_BLK_ERR | \ 9628c2ecf20Sopenharmony_ci OCX_COM_LINK_ALIGN_FAIL | \ 9638c2ecf20Sopenharmony_ci OCX_COM_LINK_BAD_WORD) 9648c2ecf20Sopenharmony_ci 9658c2ecf20Sopenharmony_ci#define OCX_LNE_INT(x) (0x8018 + (x) * 0x100) 9668c2ecf20Sopenharmony_ci#define OCX_LNE_INT_EN(x) (0x8020 + (x) * 0x100) 9678c2ecf20Sopenharmony_ci#define OCX_LNE_BAD_CNT(x) (0x8028 + (x) * 0x100) 9688c2ecf20Sopenharmony_ci#define OCX_LNE_CFG(x) (0x8000 + (x) * 0x100) 9698c2ecf20Sopenharmony_ci#define OCX_LNE_STAT(x, y) (0x8040 + (x) * 0x100 + (y) * 8) 9708c2ecf20Sopenharmony_ci 9718c2ecf20Sopenharmony_ci#define OCX_LNE_CFG_RX_BDRY_LOCK_DIS BIT(8) 9728c2ecf20Sopenharmony_ci#define OCX_LNE_CFG_RX_STAT_WRAP_DIS BIT(2) 9738c2ecf20Sopenharmony_ci#define OCX_LNE_CFG_RX_STAT_RDCLR BIT(1) 9748c2ecf20Sopenharmony_ci#define OCX_LNE_CFG_RX_STAT_ENA BIT(0) 9758c2ecf20Sopenharmony_ci 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_ci#define OCX_LANE_BAD_64B67B BIT(8) 9788c2ecf20Sopenharmony_ci#define OCX_LANE_DSKEW_FIFO_OVFL BIT(5) 9798c2ecf20Sopenharmony_ci#define OCX_LANE_SCRM_SYNC_LOSS BIT(4) 9808c2ecf20Sopenharmony_ci#define OCX_LANE_UKWN_CNTL_WORD BIT(3) 9818c2ecf20Sopenharmony_ci#define OCX_LANE_CRC32_ERR BIT(2) 9828c2ecf20Sopenharmony_ci#define OCX_LANE_BDRY_SYNC_LOSS BIT(1) 9838c2ecf20Sopenharmony_ci#define OCX_LANE_SERDES_LOCK_LOSS BIT(0) 9848c2ecf20Sopenharmony_ci 9858c2ecf20Sopenharmony_ci#define OCX_COM_LANE_INT_UE (0) 9868c2ecf20Sopenharmony_ci#define OCX_COM_LANE_INT_CE (OCX_LANE_SERDES_LOCK_LOSS | \ 9878c2ecf20Sopenharmony_ci OCX_LANE_BDRY_SYNC_LOSS | \ 9888c2ecf20Sopenharmony_ci OCX_LANE_CRC32_ERR | \ 9898c2ecf20Sopenharmony_ci OCX_LANE_UKWN_CNTL_WORD | \ 9908c2ecf20Sopenharmony_ci OCX_LANE_SCRM_SYNC_LOSS | \ 9918c2ecf20Sopenharmony_ci OCX_LANE_DSKEW_FIFO_OVFL | \ 9928c2ecf20Sopenharmony_ci OCX_LANE_BAD_64B67B) 9938c2ecf20Sopenharmony_ci 9948c2ecf20Sopenharmony_cistatic const struct error_descr ocx_lane_errors[] = { 9958c2ecf20Sopenharmony_ci { 9968c2ecf20Sopenharmony_ci .type = ERR_CORRECTED, 9978c2ecf20Sopenharmony_ci .mask = OCX_LANE_SERDES_LOCK_LOSS, 9988c2ecf20Sopenharmony_ci .descr = "RX SerDes lock lost", 9998c2ecf20Sopenharmony_ci }, 10008c2ecf20Sopenharmony_ci { 10018c2ecf20Sopenharmony_ci .type = ERR_CORRECTED, 10028c2ecf20Sopenharmony_ci .mask = OCX_LANE_BDRY_SYNC_LOSS, 10038c2ecf20Sopenharmony_ci .descr = "RX word boundary lost", 10048c2ecf20Sopenharmony_ci }, 10058c2ecf20Sopenharmony_ci { 10068c2ecf20Sopenharmony_ci .type = ERR_CORRECTED, 10078c2ecf20Sopenharmony_ci .mask = OCX_LANE_CRC32_ERR, 10088c2ecf20Sopenharmony_ci .descr = "CRC32 error", 10098c2ecf20Sopenharmony_ci }, 10108c2ecf20Sopenharmony_ci { 10118c2ecf20Sopenharmony_ci .type = ERR_CORRECTED, 10128c2ecf20Sopenharmony_ci .mask = OCX_LANE_UKWN_CNTL_WORD, 10138c2ecf20Sopenharmony_ci .descr = "Unknown control word", 10148c2ecf20Sopenharmony_ci }, 10158c2ecf20Sopenharmony_ci { 10168c2ecf20Sopenharmony_ci .type = ERR_CORRECTED, 10178c2ecf20Sopenharmony_ci .mask = OCX_LANE_SCRM_SYNC_LOSS, 10188c2ecf20Sopenharmony_ci .descr = "Scrambler synchronization lost", 10198c2ecf20Sopenharmony_ci }, 10208c2ecf20Sopenharmony_ci { 10218c2ecf20Sopenharmony_ci .type = ERR_CORRECTED, 10228c2ecf20Sopenharmony_ci .mask = OCX_LANE_DSKEW_FIFO_OVFL, 10238c2ecf20Sopenharmony_ci .descr = "RX deskew FIFO overflow", 10248c2ecf20Sopenharmony_ci }, 10258c2ecf20Sopenharmony_ci { 10268c2ecf20Sopenharmony_ci .type = ERR_CORRECTED, 10278c2ecf20Sopenharmony_ci .mask = OCX_LANE_BAD_64B67B, 10288c2ecf20Sopenharmony_ci .descr = "Bad 64B/67B codeword", 10298c2ecf20Sopenharmony_ci }, 10308c2ecf20Sopenharmony_ci {0, 0, NULL}, 10318c2ecf20Sopenharmony_ci}; 10328c2ecf20Sopenharmony_ci 10338c2ecf20Sopenharmony_ci#define OCX_LNE_INT_ENA_ALL (GENMASK(9, 8) | GENMASK(6, 0)) 10348c2ecf20Sopenharmony_ci#define OCX_COM_INT_ENA_ALL (GENMASK(54, 50) | GENMASK(23, 0)) 10358c2ecf20Sopenharmony_ci#define OCX_COM_LINKX_INT_ENA_ALL (GENMASK(13, 12) | \ 10368c2ecf20Sopenharmony_ci GENMASK(9, 7) | GENMASK(5, 0)) 10378c2ecf20Sopenharmony_ci 10388c2ecf20Sopenharmony_ci#define OCX_TLKX_ECC_CTL(x) (0x10018 + (x) * 0x2000) 10398c2ecf20Sopenharmony_ci#define OCX_RLKX_ECC_CTL(x) (0x18018 + (x) * 0x2000) 10408c2ecf20Sopenharmony_ci 10418c2ecf20Sopenharmony_cistruct ocx_com_err_ctx { 10428c2ecf20Sopenharmony_ci u64 reg_com_int; 10438c2ecf20Sopenharmony_ci u64 reg_lane_int[OCX_RX_LANES]; 10448c2ecf20Sopenharmony_ci u64 reg_lane_stat11[OCX_RX_LANES]; 10458c2ecf20Sopenharmony_ci}; 10468c2ecf20Sopenharmony_ci 10478c2ecf20Sopenharmony_cistruct ocx_link_err_ctx { 10488c2ecf20Sopenharmony_ci u64 reg_com_link_int; 10498c2ecf20Sopenharmony_ci int link; 10508c2ecf20Sopenharmony_ci}; 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_cistruct thunderx_ocx { 10538c2ecf20Sopenharmony_ci void __iomem *regs; 10548c2ecf20Sopenharmony_ci int com_link; 10558c2ecf20Sopenharmony_ci struct pci_dev *pdev; 10568c2ecf20Sopenharmony_ci struct edac_device_ctl_info *edac_dev; 10578c2ecf20Sopenharmony_ci 10588c2ecf20Sopenharmony_ci struct dentry *debugfs; 10598c2ecf20Sopenharmony_ci struct msix_entry msix_ent[OCX_INTS]; 10608c2ecf20Sopenharmony_ci 10618c2ecf20Sopenharmony_ci struct ocx_com_err_ctx com_err_ctx[RING_ENTRIES]; 10628c2ecf20Sopenharmony_ci struct ocx_link_err_ctx link_err_ctx[RING_ENTRIES]; 10638c2ecf20Sopenharmony_ci 10648c2ecf20Sopenharmony_ci unsigned long com_ring_head; 10658c2ecf20Sopenharmony_ci unsigned long com_ring_tail; 10668c2ecf20Sopenharmony_ci 10678c2ecf20Sopenharmony_ci unsigned long link_ring_head; 10688c2ecf20Sopenharmony_ci unsigned long link_ring_tail; 10698c2ecf20Sopenharmony_ci}; 10708c2ecf20Sopenharmony_ci 10718c2ecf20Sopenharmony_ci#define OCX_MESSAGE_SIZE SZ_1K 10728c2ecf20Sopenharmony_ci#define OCX_OTHER_SIZE (50 * ARRAY_SIZE(ocx_com_link_errors)) 10738c2ecf20Sopenharmony_ci 10748c2ecf20Sopenharmony_ci/* This handler is threaded */ 10758c2ecf20Sopenharmony_cistatic irqreturn_t thunderx_ocx_com_isr(int irq, void *irq_id) 10768c2ecf20Sopenharmony_ci{ 10778c2ecf20Sopenharmony_ci struct msix_entry *msix = irq_id; 10788c2ecf20Sopenharmony_ci struct thunderx_ocx *ocx = container_of(msix, struct thunderx_ocx, 10798c2ecf20Sopenharmony_ci msix_ent[msix->entry]); 10808c2ecf20Sopenharmony_ci 10818c2ecf20Sopenharmony_ci int lane; 10828c2ecf20Sopenharmony_ci unsigned long head = ring_pos(ocx->com_ring_head, 10838c2ecf20Sopenharmony_ci ARRAY_SIZE(ocx->com_err_ctx)); 10848c2ecf20Sopenharmony_ci struct ocx_com_err_ctx *ctx = &ocx->com_err_ctx[head]; 10858c2ecf20Sopenharmony_ci 10868c2ecf20Sopenharmony_ci ctx->reg_com_int = readq(ocx->regs + OCX_COM_INT); 10878c2ecf20Sopenharmony_ci 10888c2ecf20Sopenharmony_ci for (lane = 0; lane < OCX_RX_LANES; lane++) { 10898c2ecf20Sopenharmony_ci ctx->reg_lane_int[lane] = 10908c2ecf20Sopenharmony_ci readq(ocx->regs + OCX_LNE_INT(lane)); 10918c2ecf20Sopenharmony_ci ctx->reg_lane_stat11[lane] = 10928c2ecf20Sopenharmony_ci readq(ocx->regs + OCX_LNE_STAT(lane, 11)); 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_ci writeq(ctx->reg_lane_int[lane], ocx->regs + OCX_LNE_INT(lane)); 10958c2ecf20Sopenharmony_ci } 10968c2ecf20Sopenharmony_ci 10978c2ecf20Sopenharmony_ci writeq(ctx->reg_com_int, ocx->regs + OCX_COM_INT); 10988c2ecf20Sopenharmony_ci 10998c2ecf20Sopenharmony_ci ocx->com_ring_head++; 11008c2ecf20Sopenharmony_ci 11018c2ecf20Sopenharmony_ci return IRQ_WAKE_THREAD; 11028c2ecf20Sopenharmony_ci} 11038c2ecf20Sopenharmony_ci 11048c2ecf20Sopenharmony_cistatic irqreturn_t thunderx_ocx_com_threaded_isr(int irq, void *irq_id) 11058c2ecf20Sopenharmony_ci{ 11068c2ecf20Sopenharmony_ci struct msix_entry *msix = irq_id; 11078c2ecf20Sopenharmony_ci struct thunderx_ocx *ocx = container_of(msix, struct thunderx_ocx, 11088c2ecf20Sopenharmony_ci msix_ent[msix->entry]); 11098c2ecf20Sopenharmony_ci 11108c2ecf20Sopenharmony_ci irqreturn_t ret = IRQ_NONE; 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_ci unsigned long tail; 11138c2ecf20Sopenharmony_ci struct ocx_com_err_ctx *ctx; 11148c2ecf20Sopenharmony_ci int lane; 11158c2ecf20Sopenharmony_ci char *msg; 11168c2ecf20Sopenharmony_ci char *other; 11178c2ecf20Sopenharmony_ci 11188c2ecf20Sopenharmony_ci msg = kmalloc(OCX_MESSAGE_SIZE, GFP_KERNEL); 11198c2ecf20Sopenharmony_ci other = kmalloc(OCX_OTHER_SIZE, GFP_KERNEL); 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_ci if (!msg || !other) 11228c2ecf20Sopenharmony_ci goto err_free; 11238c2ecf20Sopenharmony_ci 11248c2ecf20Sopenharmony_ci while (CIRC_CNT(ocx->com_ring_head, ocx->com_ring_tail, 11258c2ecf20Sopenharmony_ci ARRAY_SIZE(ocx->com_err_ctx))) { 11268c2ecf20Sopenharmony_ci tail = ring_pos(ocx->com_ring_tail, 11278c2ecf20Sopenharmony_ci ARRAY_SIZE(ocx->com_err_ctx)); 11288c2ecf20Sopenharmony_ci ctx = &ocx->com_err_ctx[tail]; 11298c2ecf20Sopenharmony_ci 11308c2ecf20Sopenharmony_ci snprintf(msg, OCX_MESSAGE_SIZE, "%s: OCX_COM_INT: %016llx", 11318c2ecf20Sopenharmony_ci ocx->edac_dev->ctl_name, ctx->reg_com_int); 11328c2ecf20Sopenharmony_ci 11338c2ecf20Sopenharmony_ci decode_register(other, OCX_OTHER_SIZE, 11348c2ecf20Sopenharmony_ci ocx_com_errors, ctx->reg_com_int); 11358c2ecf20Sopenharmony_ci 11368c2ecf20Sopenharmony_ci strlcat(msg, other, OCX_MESSAGE_SIZE); 11378c2ecf20Sopenharmony_ci 11388c2ecf20Sopenharmony_ci for (lane = 0; lane < OCX_RX_LANES; lane++) 11398c2ecf20Sopenharmony_ci if (ctx->reg_com_int & BIT(lane)) { 11408c2ecf20Sopenharmony_ci snprintf(other, OCX_OTHER_SIZE, 11418c2ecf20Sopenharmony_ci "\n\tOCX_LNE_INT[%02d]: %016llx OCX_LNE_STAT11[%02d]: %016llx", 11428c2ecf20Sopenharmony_ci lane, ctx->reg_lane_int[lane], 11438c2ecf20Sopenharmony_ci lane, ctx->reg_lane_stat11[lane]); 11448c2ecf20Sopenharmony_ci 11458c2ecf20Sopenharmony_ci strlcat(msg, other, OCX_MESSAGE_SIZE); 11468c2ecf20Sopenharmony_ci 11478c2ecf20Sopenharmony_ci decode_register(other, OCX_OTHER_SIZE, 11488c2ecf20Sopenharmony_ci ocx_lane_errors, 11498c2ecf20Sopenharmony_ci ctx->reg_lane_int[lane]); 11508c2ecf20Sopenharmony_ci strlcat(msg, other, OCX_MESSAGE_SIZE); 11518c2ecf20Sopenharmony_ci } 11528c2ecf20Sopenharmony_ci 11538c2ecf20Sopenharmony_ci if (ctx->reg_com_int & OCX_COM_INT_CE) 11548c2ecf20Sopenharmony_ci edac_device_handle_ce(ocx->edac_dev, 0, 0, msg); 11558c2ecf20Sopenharmony_ci 11568c2ecf20Sopenharmony_ci ocx->com_ring_tail++; 11578c2ecf20Sopenharmony_ci } 11588c2ecf20Sopenharmony_ci 11598c2ecf20Sopenharmony_ci ret = IRQ_HANDLED; 11608c2ecf20Sopenharmony_ci 11618c2ecf20Sopenharmony_cierr_free: 11628c2ecf20Sopenharmony_ci kfree(other); 11638c2ecf20Sopenharmony_ci kfree(msg); 11648c2ecf20Sopenharmony_ci 11658c2ecf20Sopenharmony_ci return ret; 11668c2ecf20Sopenharmony_ci} 11678c2ecf20Sopenharmony_ci 11688c2ecf20Sopenharmony_cistatic irqreturn_t thunderx_ocx_lnk_isr(int irq, void *irq_id) 11698c2ecf20Sopenharmony_ci{ 11708c2ecf20Sopenharmony_ci struct msix_entry *msix = irq_id; 11718c2ecf20Sopenharmony_ci struct thunderx_ocx *ocx = container_of(msix, struct thunderx_ocx, 11728c2ecf20Sopenharmony_ci msix_ent[msix->entry]); 11738c2ecf20Sopenharmony_ci unsigned long head = ring_pos(ocx->link_ring_head, 11748c2ecf20Sopenharmony_ci ARRAY_SIZE(ocx->link_err_ctx)); 11758c2ecf20Sopenharmony_ci struct ocx_link_err_ctx *ctx = &ocx->link_err_ctx[head]; 11768c2ecf20Sopenharmony_ci 11778c2ecf20Sopenharmony_ci ctx->link = msix->entry; 11788c2ecf20Sopenharmony_ci ctx->reg_com_link_int = readq(ocx->regs + OCX_COM_LINKX_INT(ctx->link)); 11798c2ecf20Sopenharmony_ci 11808c2ecf20Sopenharmony_ci writeq(ctx->reg_com_link_int, ocx->regs + OCX_COM_LINKX_INT(ctx->link)); 11818c2ecf20Sopenharmony_ci 11828c2ecf20Sopenharmony_ci ocx->link_ring_head++; 11838c2ecf20Sopenharmony_ci 11848c2ecf20Sopenharmony_ci return IRQ_WAKE_THREAD; 11858c2ecf20Sopenharmony_ci} 11868c2ecf20Sopenharmony_ci 11878c2ecf20Sopenharmony_cistatic irqreturn_t thunderx_ocx_lnk_threaded_isr(int irq, void *irq_id) 11888c2ecf20Sopenharmony_ci{ 11898c2ecf20Sopenharmony_ci struct msix_entry *msix = irq_id; 11908c2ecf20Sopenharmony_ci struct thunderx_ocx *ocx = container_of(msix, struct thunderx_ocx, 11918c2ecf20Sopenharmony_ci msix_ent[msix->entry]); 11928c2ecf20Sopenharmony_ci irqreturn_t ret = IRQ_NONE; 11938c2ecf20Sopenharmony_ci unsigned long tail; 11948c2ecf20Sopenharmony_ci struct ocx_link_err_ctx *ctx; 11958c2ecf20Sopenharmony_ci 11968c2ecf20Sopenharmony_ci char *msg; 11978c2ecf20Sopenharmony_ci char *other; 11988c2ecf20Sopenharmony_ci 11998c2ecf20Sopenharmony_ci msg = kmalloc(OCX_MESSAGE_SIZE, GFP_KERNEL); 12008c2ecf20Sopenharmony_ci other = kmalloc(OCX_OTHER_SIZE, GFP_KERNEL); 12018c2ecf20Sopenharmony_ci 12028c2ecf20Sopenharmony_ci if (!msg || !other) 12038c2ecf20Sopenharmony_ci goto err_free; 12048c2ecf20Sopenharmony_ci 12058c2ecf20Sopenharmony_ci while (CIRC_CNT(ocx->link_ring_head, ocx->link_ring_tail, 12068c2ecf20Sopenharmony_ci ARRAY_SIZE(ocx->link_err_ctx))) { 12078c2ecf20Sopenharmony_ci tail = ring_pos(ocx->link_ring_head, 12088c2ecf20Sopenharmony_ci ARRAY_SIZE(ocx->link_err_ctx)); 12098c2ecf20Sopenharmony_ci 12108c2ecf20Sopenharmony_ci ctx = &ocx->link_err_ctx[tail]; 12118c2ecf20Sopenharmony_ci 12128c2ecf20Sopenharmony_ci snprintf(msg, OCX_MESSAGE_SIZE, 12138c2ecf20Sopenharmony_ci "%s: OCX_COM_LINK_INT[%d]: %016llx", 12148c2ecf20Sopenharmony_ci ocx->edac_dev->ctl_name, 12158c2ecf20Sopenharmony_ci ctx->link, ctx->reg_com_link_int); 12168c2ecf20Sopenharmony_ci 12178c2ecf20Sopenharmony_ci decode_register(other, OCX_OTHER_SIZE, 12188c2ecf20Sopenharmony_ci ocx_com_link_errors, ctx->reg_com_link_int); 12198c2ecf20Sopenharmony_ci 12208c2ecf20Sopenharmony_ci strlcat(msg, other, OCX_MESSAGE_SIZE); 12218c2ecf20Sopenharmony_ci 12228c2ecf20Sopenharmony_ci if (ctx->reg_com_link_int & OCX_COM_LINK_INT_UE) 12238c2ecf20Sopenharmony_ci edac_device_handle_ue(ocx->edac_dev, 0, 0, msg); 12248c2ecf20Sopenharmony_ci else if (ctx->reg_com_link_int & OCX_COM_LINK_INT_CE) 12258c2ecf20Sopenharmony_ci edac_device_handle_ce(ocx->edac_dev, 0, 0, msg); 12268c2ecf20Sopenharmony_ci 12278c2ecf20Sopenharmony_ci ocx->link_ring_tail++; 12288c2ecf20Sopenharmony_ci } 12298c2ecf20Sopenharmony_ci 12308c2ecf20Sopenharmony_ci ret = IRQ_HANDLED; 12318c2ecf20Sopenharmony_cierr_free: 12328c2ecf20Sopenharmony_ci kfree(other); 12338c2ecf20Sopenharmony_ci kfree(msg); 12348c2ecf20Sopenharmony_ci 12358c2ecf20Sopenharmony_ci return ret; 12368c2ecf20Sopenharmony_ci} 12378c2ecf20Sopenharmony_ci 12388c2ecf20Sopenharmony_ci#define OCX_DEBUGFS_ATTR(_name, _reg) DEBUGFS_REG_ATTR(ocx, _name, _reg) 12398c2ecf20Sopenharmony_ci 12408c2ecf20Sopenharmony_ciOCX_DEBUGFS_ATTR(tlk0_ecc_ctl, OCX_TLKX_ECC_CTL(0)); 12418c2ecf20Sopenharmony_ciOCX_DEBUGFS_ATTR(tlk1_ecc_ctl, OCX_TLKX_ECC_CTL(1)); 12428c2ecf20Sopenharmony_ciOCX_DEBUGFS_ATTR(tlk2_ecc_ctl, OCX_TLKX_ECC_CTL(2)); 12438c2ecf20Sopenharmony_ci 12448c2ecf20Sopenharmony_ciOCX_DEBUGFS_ATTR(rlk0_ecc_ctl, OCX_RLKX_ECC_CTL(0)); 12458c2ecf20Sopenharmony_ciOCX_DEBUGFS_ATTR(rlk1_ecc_ctl, OCX_RLKX_ECC_CTL(1)); 12468c2ecf20Sopenharmony_ciOCX_DEBUGFS_ATTR(rlk2_ecc_ctl, OCX_RLKX_ECC_CTL(2)); 12478c2ecf20Sopenharmony_ci 12488c2ecf20Sopenharmony_ciOCX_DEBUGFS_ATTR(com_link0_int, OCX_COM_LINKX_INT_W1S(0)); 12498c2ecf20Sopenharmony_ciOCX_DEBUGFS_ATTR(com_link1_int, OCX_COM_LINKX_INT_W1S(1)); 12508c2ecf20Sopenharmony_ciOCX_DEBUGFS_ATTR(com_link2_int, OCX_COM_LINKX_INT_W1S(2)); 12518c2ecf20Sopenharmony_ci 12528c2ecf20Sopenharmony_ciOCX_DEBUGFS_ATTR(lne00_badcnt, OCX_LNE_BAD_CNT(0)); 12538c2ecf20Sopenharmony_ciOCX_DEBUGFS_ATTR(lne01_badcnt, OCX_LNE_BAD_CNT(1)); 12548c2ecf20Sopenharmony_ciOCX_DEBUGFS_ATTR(lne02_badcnt, OCX_LNE_BAD_CNT(2)); 12558c2ecf20Sopenharmony_ciOCX_DEBUGFS_ATTR(lne03_badcnt, OCX_LNE_BAD_CNT(3)); 12568c2ecf20Sopenharmony_ciOCX_DEBUGFS_ATTR(lne04_badcnt, OCX_LNE_BAD_CNT(4)); 12578c2ecf20Sopenharmony_ciOCX_DEBUGFS_ATTR(lne05_badcnt, OCX_LNE_BAD_CNT(5)); 12588c2ecf20Sopenharmony_ciOCX_DEBUGFS_ATTR(lne06_badcnt, OCX_LNE_BAD_CNT(6)); 12598c2ecf20Sopenharmony_ciOCX_DEBUGFS_ATTR(lne07_badcnt, OCX_LNE_BAD_CNT(7)); 12608c2ecf20Sopenharmony_ci 12618c2ecf20Sopenharmony_ciOCX_DEBUGFS_ATTR(lne08_badcnt, OCX_LNE_BAD_CNT(8)); 12628c2ecf20Sopenharmony_ciOCX_DEBUGFS_ATTR(lne09_badcnt, OCX_LNE_BAD_CNT(9)); 12638c2ecf20Sopenharmony_ciOCX_DEBUGFS_ATTR(lne10_badcnt, OCX_LNE_BAD_CNT(10)); 12648c2ecf20Sopenharmony_ciOCX_DEBUGFS_ATTR(lne11_badcnt, OCX_LNE_BAD_CNT(11)); 12658c2ecf20Sopenharmony_ciOCX_DEBUGFS_ATTR(lne12_badcnt, OCX_LNE_BAD_CNT(12)); 12668c2ecf20Sopenharmony_ciOCX_DEBUGFS_ATTR(lne13_badcnt, OCX_LNE_BAD_CNT(13)); 12678c2ecf20Sopenharmony_ciOCX_DEBUGFS_ATTR(lne14_badcnt, OCX_LNE_BAD_CNT(14)); 12688c2ecf20Sopenharmony_ciOCX_DEBUGFS_ATTR(lne15_badcnt, OCX_LNE_BAD_CNT(15)); 12698c2ecf20Sopenharmony_ci 12708c2ecf20Sopenharmony_ciOCX_DEBUGFS_ATTR(lne16_badcnt, OCX_LNE_BAD_CNT(16)); 12718c2ecf20Sopenharmony_ciOCX_DEBUGFS_ATTR(lne17_badcnt, OCX_LNE_BAD_CNT(17)); 12728c2ecf20Sopenharmony_ciOCX_DEBUGFS_ATTR(lne18_badcnt, OCX_LNE_BAD_CNT(18)); 12738c2ecf20Sopenharmony_ciOCX_DEBUGFS_ATTR(lne19_badcnt, OCX_LNE_BAD_CNT(19)); 12748c2ecf20Sopenharmony_ciOCX_DEBUGFS_ATTR(lne20_badcnt, OCX_LNE_BAD_CNT(20)); 12758c2ecf20Sopenharmony_ciOCX_DEBUGFS_ATTR(lne21_badcnt, OCX_LNE_BAD_CNT(21)); 12768c2ecf20Sopenharmony_ciOCX_DEBUGFS_ATTR(lne22_badcnt, OCX_LNE_BAD_CNT(22)); 12778c2ecf20Sopenharmony_ciOCX_DEBUGFS_ATTR(lne23_badcnt, OCX_LNE_BAD_CNT(23)); 12788c2ecf20Sopenharmony_ci 12798c2ecf20Sopenharmony_ciOCX_DEBUGFS_ATTR(com_int, OCX_COM_INT_W1S); 12808c2ecf20Sopenharmony_ci 12818c2ecf20Sopenharmony_cistatic struct debugfs_entry *ocx_dfs_ents[] = { 12828c2ecf20Sopenharmony_ci &debugfs_tlk0_ecc_ctl, 12838c2ecf20Sopenharmony_ci &debugfs_tlk1_ecc_ctl, 12848c2ecf20Sopenharmony_ci &debugfs_tlk2_ecc_ctl, 12858c2ecf20Sopenharmony_ci 12868c2ecf20Sopenharmony_ci &debugfs_rlk0_ecc_ctl, 12878c2ecf20Sopenharmony_ci &debugfs_rlk1_ecc_ctl, 12888c2ecf20Sopenharmony_ci &debugfs_rlk2_ecc_ctl, 12898c2ecf20Sopenharmony_ci 12908c2ecf20Sopenharmony_ci &debugfs_com_link0_int, 12918c2ecf20Sopenharmony_ci &debugfs_com_link1_int, 12928c2ecf20Sopenharmony_ci &debugfs_com_link2_int, 12938c2ecf20Sopenharmony_ci 12948c2ecf20Sopenharmony_ci &debugfs_lne00_badcnt, 12958c2ecf20Sopenharmony_ci &debugfs_lne01_badcnt, 12968c2ecf20Sopenharmony_ci &debugfs_lne02_badcnt, 12978c2ecf20Sopenharmony_ci &debugfs_lne03_badcnt, 12988c2ecf20Sopenharmony_ci &debugfs_lne04_badcnt, 12998c2ecf20Sopenharmony_ci &debugfs_lne05_badcnt, 13008c2ecf20Sopenharmony_ci &debugfs_lne06_badcnt, 13018c2ecf20Sopenharmony_ci &debugfs_lne07_badcnt, 13028c2ecf20Sopenharmony_ci &debugfs_lne08_badcnt, 13038c2ecf20Sopenharmony_ci &debugfs_lne09_badcnt, 13048c2ecf20Sopenharmony_ci &debugfs_lne10_badcnt, 13058c2ecf20Sopenharmony_ci &debugfs_lne11_badcnt, 13068c2ecf20Sopenharmony_ci &debugfs_lne12_badcnt, 13078c2ecf20Sopenharmony_ci &debugfs_lne13_badcnt, 13088c2ecf20Sopenharmony_ci &debugfs_lne14_badcnt, 13098c2ecf20Sopenharmony_ci &debugfs_lne15_badcnt, 13108c2ecf20Sopenharmony_ci &debugfs_lne16_badcnt, 13118c2ecf20Sopenharmony_ci &debugfs_lne17_badcnt, 13128c2ecf20Sopenharmony_ci &debugfs_lne18_badcnt, 13138c2ecf20Sopenharmony_ci &debugfs_lne19_badcnt, 13148c2ecf20Sopenharmony_ci &debugfs_lne20_badcnt, 13158c2ecf20Sopenharmony_ci &debugfs_lne21_badcnt, 13168c2ecf20Sopenharmony_ci &debugfs_lne22_badcnt, 13178c2ecf20Sopenharmony_ci &debugfs_lne23_badcnt, 13188c2ecf20Sopenharmony_ci 13198c2ecf20Sopenharmony_ci &debugfs_com_int, 13208c2ecf20Sopenharmony_ci}; 13218c2ecf20Sopenharmony_ci 13228c2ecf20Sopenharmony_cistatic const struct pci_device_id thunderx_ocx_pci_tbl[] = { 13238c2ecf20Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, PCI_DEVICE_ID_THUNDER_OCX) }, 13248c2ecf20Sopenharmony_ci { 0, }, 13258c2ecf20Sopenharmony_ci}; 13268c2ecf20Sopenharmony_ci 13278c2ecf20Sopenharmony_cistatic void thunderx_ocx_clearstats(struct thunderx_ocx *ocx) 13288c2ecf20Sopenharmony_ci{ 13298c2ecf20Sopenharmony_ci int lane, stat, cfg; 13308c2ecf20Sopenharmony_ci 13318c2ecf20Sopenharmony_ci for (lane = 0; lane < OCX_RX_LANES; lane++) { 13328c2ecf20Sopenharmony_ci cfg = readq(ocx->regs + OCX_LNE_CFG(lane)); 13338c2ecf20Sopenharmony_ci cfg |= OCX_LNE_CFG_RX_STAT_RDCLR; 13348c2ecf20Sopenharmony_ci cfg &= ~OCX_LNE_CFG_RX_STAT_ENA; 13358c2ecf20Sopenharmony_ci writeq(cfg, ocx->regs + OCX_LNE_CFG(lane)); 13368c2ecf20Sopenharmony_ci 13378c2ecf20Sopenharmony_ci for (stat = 0; stat < OCX_RX_LANE_STATS; stat++) 13388c2ecf20Sopenharmony_ci readq(ocx->regs + OCX_LNE_STAT(lane, stat)); 13398c2ecf20Sopenharmony_ci } 13408c2ecf20Sopenharmony_ci} 13418c2ecf20Sopenharmony_ci 13428c2ecf20Sopenharmony_cistatic int thunderx_ocx_probe(struct pci_dev *pdev, 13438c2ecf20Sopenharmony_ci const struct pci_device_id *id) 13448c2ecf20Sopenharmony_ci{ 13458c2ecf20Sopenharmony_ci struct thunderx_ocx *ocx; 13468c2ecf20Sopenharmony_ci struct edac_device_ctl_info *edac_dev; 13478c2ecf20Sopenharmony_ci char name[32]; 13488c2ecf20Sopenharmony_ci int idx; 13498c2ecf20Sopenharmony_ci int i; 13508c2ecf20Sopenharmony_ci int ret; 13518c2ecf20Sopenharmony_ci u64 reg; 13528c2ecf20Sopenharmony_ci 13538c2ecf20Sopenharmony_ci ret = pcim_enable_device(pdev); 13548c2ecf20Sopenharmony_ci if (ret) { 13558c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Cannot enable PCI device: %d\n", ret); 13568c2ecf20Sopenharmony_ci return ret; 13578c2ecf20Sopenharmony_ci } 13588c2ecf20Sopenharmony_ci 13598c2ecf20Sopenharmony_ci ret = pcim_iomap_regions(pdev, BIT(0), "thunderx_ocx"); 13608c2ecf20Sopenharmony_ci if (ret) { 13618c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Cannot map PCI resources: %d\n", ret); 13628c2ecf20Sopenharmony_ci return ret; 13638c2ecf20Sopenharmony_ci } 13648c2ecf20Sopenharmony_ci 13658c2ecf20Sopenharmony_ci idx = edac_device_alloc_index(); 13668c2ecf20Sopenharmony_ci snprintf(name, sizeof(name), "OCX%d", idx); 13678c2ecf20Sopenharmony_ci edac_dev = edac_device_alloc_ctl_info(sizeof(struct thunderx_ocx), 13688c2ecf20Sopenharmony_ci name, 1, "CCPI", 1, 13698c2ecf20Sopenharmony_ci 0, NULL, 0, idx); 13708c2ecf20Sopenharmony_ci if (!edac_dev) { 13718c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Cannot allocate EDAC device: %d\n", ret); 13728c2ecf20Sopenharmony_ci return -ENOMEM; 13738c2ecf20Sopenharmony_ci } 13748c2ecf20Sopenharmony_ci ocx = edac_dev->pvt_info; 13758c2ecf20Sopenharmony_ci ocx->edac_dev = edac_dev; 13768c2ecf20Sopenharmony_ci ocx->com_ring_head = 0; 13778c2ecf20Sopenharmony_ci ocx->com_ring_tail = 0; 13788c2ecf20Sopenharmony_ci ocx->link_ring_head = 0; 13798c2ecf20Sopenharmony_ci ocx->link_ring_tail = 0; 13808c2ecf20Sopenharmony_ci 13818c2ecf20Sopenharmony_ci ocx->regs = pcim_iomap_table(pdev)[0]; 13828c2ecf20Sopenharmony_ci if (!ocx->regs) { 13838c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Cannot map PCI resources: %d\n", ret); 13848c2ecf20Sopenharmony_ci ret = -ENODEV; 13858c2ecf20Sopenharmony_ci goto err_free; 13868c2ecf20Sopenharmony_ci } 13878c2ecf20Sopenharmony_ci 13888c2ecf20Sopenharmony_ci ocx->pdev = pdev; 13898c2ecf20Sopenharmony_ci 13908c2ecf20Sopenharmony_ci for (i = 0; i < OCX_INTS; i++) { 13918c2ecf20Sopenharmony_ci ocx->msix_ent[i].entry = i; 13928c2ecf20Sopenharmony_ci ocx->msix_ent[i].vector = 0; 13938c2ecf20Sopenharmony_ci } 13948c2ecf20Sopenharmony_ci 13958c2ecf20Sopenharmony_ci ret = pci_enable_msix_exact(pdev, ocx->msix_ent, OCX_INTS); 13968c2ecf20Sopenharmony_ci if (ret) { 13978c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Cannot enable interrupt: %d\n", ret); 13988c2ecf20Sopenharmony_ci goto err_free; 13998c2ecf20Sopenharmony_ci } 14008c2ecf20Sopenharmony_ci 14018c2ecf20Sopenharmony_ci for (i = 0; i < OCX_INTS; i++) { 14028c2ecf20Sopenharmony_ci ret = devm_request_threaded_irq(&pdev->dev, 14038c2ecf20Sopenharmony_ci ocx->msix_ent[i].vector, 14048c2ecf20Sopenharmony_ci (i == 3) ? 14058c2ecf20Sopenharmony_ci thunderx_ocx_com_isr : 14068c2ecf20Sopenharmony_ci thunderx_ocx_lnk_isr, 14078c2ecf20Sopenharmony_ci (i == 3) ? 14088c2ecf20Sopenharmony_ci thunderx_ocx_com_threaded_isr : 14098c2ecf20Sopenharmony_ci thunderx_ocx_lnk_threaded_isr, 14108c2ecf20Sopenharmony_ci 0, "[EDAC] ThunderX OCX", 14118c2ecf20Sopenharmony_ci &ocx->msix_ent[i]); 14128c2ecf20Sopenharmony_ci if (ret) 14138c2ecf20Sopenharmony_ci goto err_free; 14148c2ecf20Sopenharmony_ci } 14158c2ecf20Sopenharmony_ci 14168c2ecf20Sopenharmony_ci edac_dev->dev = &pdev->dev; 14178c2ecf20Sopenharmony_ci edac_dev->dev_name = dev_name(&pdev->dev); 14188c2ecf20Sopenharmony_ci edac_dev->mod_name = "thunderx-ocx"; 14198c2ecf20Sopenharmony_ci edac_dev->ctl_name = "thunderx-ocx"; 14208c2ecf20Sopenharmony_ci 14218c2ecf20Sopenharmony_ci ret = edac_device_add_device(edac_dev); 14228c2ecf20Sopenharmony_ci if (ret) { 14238c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Cannot add EDAC device: %d\n", ret); 14248c2ecf20Sopenharmony_ci goto err_free; 14258c2ecf20Sopenharmony_ci } 14268c2ecf20Sopenharmony_ci 14278c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_EDAC_DEBUG)) { 14288c2ecf20Sopenharmony_ci ocx->debugfs = edac_debugfs_create_dir(pdev->dev.kobj.name); 14298c2ecf20Sopenharmony_ci 14308c2ecf20Sopenharmony_ci ret = thunderx_create_debugfs_nodes(ocx->debugfs, 14318c2ecf20Sopenharmony_ci ocx_dfs_ents, 14328c2ecf20Sopenharmony_ci ocx, 14338c2ecf20Sopenharmony_ci ARRAY_SIZE(ocx_dfs_ents)); 14348c2ecf20Sopenharmony_ci if (ret != ARRAY_SIZE(ocx_dfs_ents)) { 14358c2ecf20Sopenharmony_ci dev_warn(&pdev->dev, "Error creating debugfs entries: %d%s\n", 14368c2ecf20Sopenharmony_ci ret, ret >= 0 ? " created" : ""); 14378c2ecf20Sopenharmony_ci } 14388c2ecf20Sopenharmony_ci } 14398c2ecf20Sopenharmony_ci 14408c2ecf20Sopenharmony_ci pci_set_drvdata(pdev, edac_dev); 14418c2ecf20Sopenharmony_ci 14428c2ecf20Sopenharmony_ci thunderx_ocx_clearstats(ocx); 14438c2ecf20Sopenharmony_ci 14448c2ecf20Sopenharmony_ci for (i = 0; i < OCX_RX_LANES; i++) { 14458c2ecf20Sopenharmony_ci writeq(OCX_LNE_INT_ENA_ALL, 14468c2ecf20Sopenharmony_ci ocx->regs + OCX_LNE_INT_EN(i)); 14478c2ecf20Sopenharmony_ci 14488c2ecf20Sopenharmony_ci reg = readq(ocx->regs + OCX_LNE_INT(i)); 14498c2ecf20Sopenharmony_ci writeq(reg, ocx->regs + OCX_LNE_INT(i)); 14508c2ecf20Sopenharmony_ci 14518c2ecf20Sopenharmony_ci } 14528c2ecf20Sopenharmony_ci 14538c2ecf20Sopenharmony_ci for (i = 0; i < OCX_LINK_INTS; i++) { 14548c2ecf20Sopenharmony_ci reg = readq(ocx->regs + OCX_COM_LINKX_INT(i)); 14558c2ecf20Sopenharmony_ci writeq(reg, ocx->regs + OCX_COM_LINKX_INT(i)); 14568c2ecf20Sopenharmony_ci 14578c2ecf20Sopenharmony_ci writeq(OCX_COM_LINKX_INT_ENA_ALL, 14588c2ecf20Sopenharmony_ci ocx->regs + OCX_COM_LINKX_INT_ENA_W1S(i)); 14598c2ecf20Sopenharmony_ci } 14608c2ecf20Sopenharmony_ci 14618c2ecf20Sopenharmony_ci reg = readq(ocx->regs + OCX_COM_INT); 14628c2ecf20Sopenharmony_ci writeq(reg, ocx->regs + OCX_COM_INT); 14638c2ecf20Sopenharmony_ci 14648c2ecf20Sopenharmony_ci writeq(OCX_COM_INT_ENA_ALL, ocx->regs + OCX_COM_INT_ENA_W1S); 14658c2ecf20Sopenharmony_ci 14668c2ecf20Sopenharmony_ci return 0; 14678c2ecf20Sopenharmony_cierr_free: 14688c2ecf20Sopenharmony_ci edac_device_free_ctl_info(edac_dev); 14698c2ecf20Sopenharmony_ci 14708c2ecf20Sopenharmony_ci return ret; 14718c2ecf20Sopenharmony_ci} 14728c2ecf20Sopenharmony_ci 14738c2ecf20Sopenharmony_cistatic void thunderx_ocx_remove(struct pci_dev *pdev) 14748c2ecf20Sopenharmony_ci{ 14758c2ecf20Sopenharmony_ci struct edac_device_ctl_info *edac_dev = pci_get_drvdata(pdev); 14768c2ecf20Sopenharmony_ci struct thunderx_ocx *ocx = edac_dev->pvt_info; 14778c2ecf20Sopenharmony_ci int i; 14788c2ecf20Sopenharmony_ci 14798c2ecf20Sopenharmony_ci writeq(OCX_COM_INT_ENA_ALL, ocx->regs + OCX_COM_INT_ENA_W1C); 14808c2ecf20Sopenharmony_ci 14818c2ecf20Sopenharmony_ci for (i = 0; i < OCX_INTS; i++) { 14828c2ecf20Sopenharmony_ci writeq(OCX_COM_LINKX_INT_ENA_ALL, 14838c2ecf20Sopenharmony_ci ocx->regs + OCX_COM_LINKX_INT_ENA_W1C(i)); 14848c2ecf20Sopenharmony_ci } 14858c2ecf20Sopenharmony_ci 14868c2ecf20Sopenharmony_ci edac_debugfs_remove_recursive(ocx->debugfs); 14878c2ecf20Sopenharmony_ci 14888c2ecf20Sopenharmony_ci edac_device_del_device(&pdev->dev); 14898c2ecf20Sopenharmony_ci edac_device_free_ctl_info(edac_dev); 14908c2ecf20Sopenharmony_ci} 14918c2ecf20Sopenharmony_ci 14928c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, thunderx_ocx_pci_tbl); 14938c2ecf20Sopenharmony_ci 14948c2ecf20Sopenharmony_cistatic struct pci_driver thunderx_ocx_driver = { 14958c2ecf20Sopenharmony_ci .name = "thunderx_ocx_edac", 14968c2ecf20Sopenharmony_ci .probe = thunderx_ocx_probe, 14978c2ecf20Sopenharmony_ci .remove = thunderx_ocx_remove, 14988c2ecf20Sopenharmony_ci .id_table = thunderx_ocx_pci_tbl, 14998c2ecf20Sopenharmony_ci}; 15008c2ecf20Sopenharmony_ci 15018c2ecf20Sopenharmony_ci/*---------------------- L2C driver ---------------------------------*/ 15028c2ecf20Sopenharmony_ci 15038c2ecf20Sopenharmony_ci#define PCI_DEVICE_ID_THUNDER_L2C_TAD 0xa02e 15048c2ecf20Sopenharmony_ci#define PCI_DEVICE_ID_THUNDER_L2C_CBC 0xa02f 15058c2ecf20Sopenharmony_ci#define PCI_DEVICE_ID_THUNDER_L2C_MCI 0xa030 15068c2ecf20Sopenharmony_ci 15078c2ecf20Sopenharmony_ci#define L2C_TAD_INT_W1C 0x40000 15088c2ecf20Sopenharmony_ci#define L2C_TAD_INT_W1S 0x40008 15098c2ecf20Sopenharmony_ci 15108c2ecf20Sopenharmony_ci#define L2C_TAD_INT_ENA_W1C 0x40020 15118c2ecf20Sopenharmony_ci#define L2C_TAD_INT_ENA_W1S 0x40028 15128c2ecf20Sopenharmony_ci 15138c2ecf20Sopenharmony_ci 15148c2ecf20Sopenharmony_ci#define L2C_TAD_INT_L2DDBE BIT(1) 15158c2ecf20Sopenharmony_ci#define L2C_TAD_INT_SBFSBE BIT(2) 15168c2ecf20Sopenharmony_ci#define L2C_TAD_INT_SBFDBE BIT(3) 15178c2ecf20Sopenharmony_ci#define L2C_TAD_INT_FBFSBE BIT(4) 15188c2ecf20Sopenharmony_ci#define L2C_TAD_INT_FBFDBE BIT(5) 15198c2ecf20Sopenharmony_ci#define L2C_TAD_INT_TAGDBE BIT(9) 15208c2ecf20Sopenharmony_ci#define L2C_TAD_INT_RDDISLMC BIT(15) 15218c2ecf20Sopenharmony_ci#define L2C_TAD_INT_WRDISLMC BIT(16) 15228c2ecf20Sopenharmony_ci#define L2C_TAD_INT_LFBTO BIT(17) 15238c2ecf20Sopenharmony_ci#define L2C_TAD_INT_GSYNCTO BIT(18) 15248c2ecf20Sopenharmony_ci#define L2C_TAD_INT_RTGSBE BIT(32) 15258c2ecf20Sopenharmony_ci#define L2C_TAD_INT_RTGDBE BIT(33) 15268c2ecf20Sopenharmony_ci#define L2C_TAD_INT_RDDISOCI BIT(34) 15278c2ecf20Sopenharmony_ci#define L2C_TAD_INT_WRDISOCI BIT(35) 15288c2ecf20Sopenharmony_ci 15298c2ecf20Sopenharmony_ci#define L2C_TAD_INT_ECC (L2C_TAD_INT_L2DDBE | \ 15308c2ecf20Sopenharmony_ci L2C_TAD_INT_SBFSBE | L2C_TAD_INT_SBFDBE | \ 15318c2ecf20Sopenharmony_ci L2C_TAD_INT_FBFSBE | L2C_TAD_INT_FBFDBE) 15328c2ecf20Sopenharmony_ci 15338c2ecf20Sopenharmony_ci#define L2C_TAD_INT_CE (L2C_TAD_INT_SBFSBE | \ 15348c2ecf20Sopenharmony_ci L2C_TAD_INT_FBFSBE) 15358c2ecf20Sopenharmony_ci 15368c2ecf20Sopenharmony_ci#define L2C_TAD_INT_UE (L2C_TAD_INT_L2DDBE | \ 15378c2ecf20Sopenharmony_ci L2C_TAD_INT_SBFDBE | \ 15388c2ecf20Sopenharmony_ci L2C_TAD_INT_FBFDBE | \ 15398c2ecf20Sopenharmony_ci L2C_TAD_INT_TAGDBE | \ 15408c2ecf20Sopenharmony_ci L2C_TAD_INT_RTGDBE | \ 15418c2ecf20Sopenharmony_ci L2C_TAD_INT_WRDISOCI | \ 15428c2ecf20Sopenharmony_ci L2C_TAD_INT_RDDISOCI | \ 15438c2ecf20Sopenharmony_ci L2C_TAD_INT_WRDISLMC | \ 15448c2ecf20Sopenharmony_ci L2C_TAD_INT_RDDISLMC | \ 15458c2ecf20Sopenharmony_ci L2C_TAD_INT_LFBTO | \ 15468c2ecf20Sopenharmony_ci L2C_TAD_INT_GSYNCTO) 15478c2ecf20Sopenharmony_ci 15488c2ecf20Sopenharmony_cistatic const struct error_descr l2_tad_errors[] = { 15498c2ecf20Sopenharmony_ci { 15508c2ecf20Sopenharmony_ci .type = ERR_CORRECTED, 15518c2ecf20Sopenharmony_ci .mask = L2C_TAD_INT_SBFSBE, 15528c2ecf20Sopenharmony_ci .descr = "SBF single-bit error", 15538c2ecf20Sopenharmony_ci }, 15548c2ecf20Sopenharmony_ci { 15558c2ecf20Sopenharmony_ci .type = ERR_CORRECTED, 15568c2ecf20Sopenharmony_ci .mask = L2C_TAD_INT_FBFSBE, 15578c2ecf20Sopenharmony_ci .descr = "FBF single-bit error", 15588c2ecf20Sopenharmony_ci }, 15598c2ecf20Sopenharmony_ci { 15608c2ecf20Sopenharmony_ci .type = ERR_UNCORRECTED, 15618c2ecf20Sopenharmony_ci .mask = L2C_TAD_INT_L2DDBE, 15628c2ecf20Sopenharmony_ci .descr = "L2D double-bit error", 15638c2ecf20Sopenharmony_ci }, 15648c2ecf20Sopenharmony_ci { 15658c2ecf20Sopenharmony_ci .type = ERR_UNCORRECTED, 15668c2ecf20Sopenharmony_ci .mask = L2C_TAD_INT_SBFDBE, 15678c2ecf20Sopenharmony_ci .descr = "SBF double-bit error", 15688c2ecf20Sopenharmony_ci }, 15698c2ecf20Sopenharmony_ci { 15708c2ecf20Sopenharmony_ci .type = ERR_UNCORRECTED, 15718c2ecf20Sopenharmony_ci .mask = L2C_TAD_INT_FBFDBE, 15728c2ecf20Sopenharmony_ci .descr = "FBF double-bit error", 15738c2ecf20Sopenharmony_ci }, 15748c2ecf20Sopenharmony_ci { 15758c2ecf20Sopenharmony_ci .type = ERR_UNCORRECTED, 15768c2ecf20Sopenharmony_ci .mask = L2C_TAD_INT_TAGDBE, 15778c2ecf20Sopenharmony_ci .descr = "TAG double-bit error", 15788c2ecf20Sopenharmony_ci }, 15798c2ecf20Sopenharmony_ci { 15808c2ecf20Sopenharmony_ci .type = ERR_UNCORRECTED, 15818c2ecf20Sopenharmony_ci .mask = L2C_TAD_INT_RTGDBE, 15828c2ecf20Sopenharmony_ci .descr = "RTG double-bit error", 15838c2ecf20Sopenharmony_ci }, 15848c2ecf20Sopenharmony_ci { 15858c2ecf20Sopenharmony_ci .type = ERR_UNCORRECTED, 15868c2ecf20Sopenharmony_ci .mask = L2C_TAD_INT_WRDISOCI, 15878c2ecf20Sopenharmony_ci .descr = "Write to a disabled CCPI", 15888c2ecf20Sopenharmony_ci }, 15898c2ecf20Sopenharmony_ci { 15908c2ecf20Sopenharmony_ci .type = ERR_UNCORRECTED, 15918c2ecf20Sopenharmony_ci .mask = L2C_TAD_INT_RDDISOCI, 15928c2ecf20Sopenharmony_ci .descr = "Read from a disabled CCPI", 15938c2ecf20Sopenharmony_ci }, 15948c2ecf20Sopenharmony_ci { 15958c2ecf20Sopenharmony_ci .type = ERR_UNCORRECTED, 15968c2ecf20Sopenharmony_ci .mask = L2C_TAD_INT_WRDISLMC, 15978c2ecf20Sopenharmony_ci .descr = "Write to a disabled LMC", 15988c2ecf20Sopenharmony_ci }, 15998c2ecf20Sopenharmony_ci { 16008c2ecf20Sopenharmony_ci .type = ERR_UNCORRECTED, 16018c2ecf20Sopenharmony_ci .mask = L2C_TAD_INT_RDDISLMC, 16028c2ecf20Sopenharmony_ci .descr = "Read from a disabled LMC", 16038c2ecf20Sopenharmony_ci }, 16048c2ecf20Sopenharmony_ci { 16058c2ecf20Sopenharmony_ci .type = ERR_UNCORRECTED, 16068c2ecf20Sopenharmony_ci .mask = L2C_TAD_INT_LFBTO, 16078c2ecf20Sopenharmony_ci .descr = "LFB entry timeout", 16088c2ecf20Sopenharmony_ci }, 16098c2ecf20Sopenharmony_ci { 16108c2ecf20Sopenharmony_ci .type = ERR_UNCORRECTED, 16118c2ecf20Sopenharmony_ci .mask = L2C_TAD_INT_GSYNCTO, 16128c2ecf20Sopenharmony_ci .descr = "Global sync CCPI timeout", 16138c2ecf20Sopenharmony_ci }, 16148c2ecf20Sopenharmony_ci {0, 0, NULL}, 16158c2ecf20Sopenharmony_ci}; 16168c2ecf20Sopenharmony_ci 16178c2ecf20Sopenharmony_ci#define L2C_TAD_INT_TAG (L2C_TAD_INT_TAGDBE) 16188c2ecf20Sopenharmony_ci 16198c2ecf20Sopenharmony_ci#define L2C_TAD_INT_RTG (L2C_TAD_INT_RTGDBE) 16208c2ecf20Sopenharmony_ci 16218c2ecf20Sopenharmony_ci#define L2C_TAD_INT_DISLMC (L2C_TAD_INT_WRDISLMC | L2C_TAD_INT_RDDISLMC) 16228c2ecf20Sopenharmony_ci 16238c2ecf20Sopenharmony_ci#define L2C_TAD_INT_DISOCI (L2C_TAD_INT_WRDISOCI | L2C_TAD_INT_RDDISOCI) 16248c2ecf20Sopenharmony_ci 16258c2ecf20Sopenharmony_ci#define L2C_TAD_INT_ENA_ALL (L2C_TAD_INT_ECC | L2C_TAD_INT_TAG | \ 16268c2ecf20Sopenharmony_ci L2C_TAD_INT_RTG | \ 16278c2ecf20Sopenharmony_ci L2C_TAD_INT_DISLMC | L2C_TAD_INT_DISOCI | \ 16288c2ecf20Sopenharmony_ci L2C_TAD_INT_LFBTO) 16298c2ecf20Sopenharmony_ci 16308c2ecf20Sopenharmony_ci#define L2C_TAD_TIMETWO 0x50000 16318c2ecf20Sopenharmony_ci#define L2C_TAD_TIMEOUT 0x50100 16328c2ecf20Sopenharmony_ci#define L2C_TAD_ERR 0x60000 16338c2ecf20Sopenharmony_ci#define L2C_TAD_TQD_ERR 0x60100 16348c2ecf20Sopenharmony_ci#define L2C_TAD_TTG_ERR 0x60200 16358c2ecf20Sopenharmony_ci 16368c2ecf20Sopenharmony_ci 16378c2ecf20Sopenharmony_ci#define L2C_CBC_INT_W1C 0x60000 16388c2ecf20Sopenharmony_ci 16398c2ecf20Sopenharmony_ci#define L2C_CBC_INT_RSDSBE BIT(0) 16408c2ecf20Sopenharmony_ci#define L2C_CBC_INT_RSDDBE BIT(1) 16418c2ecf20Sopenharmony_ci 16428c2ecf20Sopenharmony_ci#define L2C_CBC_INT_RSD (L2C_CBC_INT_RSDSBE | L2C_CBC_INT_RSDDBE) 16438c2ecf20Sopenharmony_ci 16448c2ecf20Sopenharmony_ci#define L2C_CBC_INT_MIBSBE BIT(4) 16458c2ecf20Sopenharmony_ci#define L2C_CBC_INT_MIBDBE BIT(5) 16468c2ecf20Sopenharmony_ci 16478c2ecf20Sopenharmony_ci#define L2C_CBC_INT_MIB (L2C_CBC_INT_MIBSBE | L2C_CBC_INT_MIBDBE) 16488c2ecf20Sopenharmony_ci 16498c2ecf20Sopenharmony_ci#define L2C_CBC_INT_IORDDISOCI BIT(6) 16508c2ecf20Sopenharmony_ci#define L2C_CBC_INT_IOWRDISOCI BIT(7) 16518c2ecf20Sopenharmony_ci 16528c2ecf20Sopenharmony_ci#define L2C_CBC_INT_IODISOCI (L2C_CBC_INT_IORDDISOCI | \ 16538c2ecf20Sopenharmony_ci L2C_CBC_INT_IOWRDISOCI) 16548c2ecf20Sopenharmony_ci 16558c2ecf20Sopenharmony_ci#define L2C_CBC_INT_CE (L2C_CBC_INT_RSDSBE | L2C_CBC_INT_MIBSBE) 16568c2ecf20Sopenharmony_ci#define L2C_CBC_INT_UE (L2C_CBC_INT_RSDDBE | L2C_CBC_INT_MIBDBE) 16578c2ecf20Sopenharmony_ci 16588c2ecf20Sopenharmony_ci 16598c2ecf20Sopenharmony_cistatic const struct error_descr l2_cbc_errors[] = { 16608c2ecf20Sopenharmony_ci { 16618c2ecf20Sopenharmony_ci .type = ERR_CORRECTED, 16628c2ecf20Sopenharmony_ci .mask = L2C_CBC_INT_RSDSBE, 16638c2ecf20Sopenharmony_ci .descr = "RSD single-bit error", 16648c2ecf20Sopenharmony_ci }, 16658c2ecf20Sopenharmony_ci { 16668c2ecf20Sopenharmony_ci .type = ERR_CORRECTED, 16678c2ecf20Sopenharmony_ci .mask = L2C_CBC_INT_MIBSBE, 16688c2ecf20Sopenharmony_ci .descr = "MIB single-bit error", 16698c2ecf20Sopenharmony_ci }, 16708c2ecf20Sopenharmony_ci { 16718c2ecf20Sopenharmony_ci .type = ERR_UNCORRECTED, 16728c2ecf20Sopenharmony_ci .mask = L2C_CBC_INT_RSDDBE, 16738c2ecf20Sopenharmony_ci .descr = "RSD double-bit error", 16748c2ecf20Sopenharmony_ci }, 16758c2ecf20Sopenharmony_ci { 16768c2ecf20Sopenharmony_ci .type = ERR_UNCORRECTED, 16778c2ecf20Sopenharmony_ci .mask = L2C_CBC_INT_MIBDBE, 16788c2ecf20Sopenharmony_ci .descr = "MIB double-bit error", 16798c2ecf20Sopenharmony_ci }, 16808c2ecf20Sopenharmony_ci { 16818c2ecf20Sopenharmony_ci .type = ERR_UNCORRECTED, 16828c2ecf20Sopenharmony_ci .mask = L2C_CBC_INT_IORDDISOCI, 16838c2ecf20Sopenharmony_ci .descr = "Read from a disabled CCPI", 16848c2ecf20Sopenharmony_ci }, 16858c2ecf20Sopenharmony_ci { 16868c2ecf20Sopenharmony_ci .type = ERR_UNCORRECTED, 16878c2ecf20Sopenharmony_ci .mask = L2C_CBC_INT_IOWRDISOCI, 16888c2ecf20Sopenharmony_ci .descr = "Write to a disabled CCPI", 16898c2ecf20Sopenharmony_ci }, 16908c2ecf20Sopenharmony_ci {0, 0, NULL}, 16918c2ecf20Sopenharmony_ci}; 16928c2ecf20Sopenharmony_ci 16938c2ecf20Sopenharmony_ci#define L2C_CBC_INT_W1S 0x60008 16948c2ecf20Sopenharmony_ci#define L2C_CBC_INT_ENA_W1C 0x60020 16958c2ecf20Sopenharmony_ci 16968c2ecf20Sopenharmony_ci#define L2C_CBC_INT_ENA_ALL (L2C_CBC_INT_RSD | L2C_CBC_INT_MIB | \ 16978c2ecf20Sopenharmony_ci L2C_CBC_INT_IODISOCI) 16988c2ecf20Sopenharmony_ci 16998c2ecf20Sopenharmony_ci#define L2C_CBC_INT_ENA_W1S 0x60028 17008c2ecf20Sopenharmony_ci 17018c2ecf20Sopenharmony_ci#define L2C_CBC_IODISOCIERR 0x80008 17028c2ecf20Sopenharmony_ci#define L2C_CBC_IOCERR 0x80010 17038c2ecf20Sopenharmony_ci#define L2C_CBC_RSDERR 0x80018 17048c2ecf20Sopenharmony_ci#define L2C_CBC_MIBERR 0x80020 17058c2ecf20Sopenharmony_ci 17068c2ecf20Sopenharmony_ci 17078c2ecf20Sopenharmony_ci#define L2C_MCI_INT_W1C 0x0 17088c2ecf20Sopenharmony_ci 17098c2ecf20Sopenharmony_ci#define L2C_MCI_INT_VBFSBE BIT(0) 17108c2ecf20Sopenharmony_ci#define L2C_MCI_INT_VBFDBE BIT(1) 17118c2ecf20Sopenharmony_ci 17128c2ecf20Sopenharmony_cistatic const struct error_descr l2_mci_errors[] = { 17138c2ecf20Sopenharmony_ci { 17148c2ecf20Sopenharmony_ci .type = ERR_CORRECTED, 17158c2ecf20Sopenharmony_ci .mask = L2C_MCI_INT_VBFSBE, 17168c2ecf20Sopenharmony_ci .descr = "VBF single-bit error", 17178c2ecf20Sopenharmony_ci }, 17188c2ecf20Sopenharmony_ci { 17198c2ecf20Sopenharmony_ci .type = ERR_UNCORRECTED, 17208c2ecf20Sopenharmony_ci .mask = L2C_MCI_INT_VBFDBE, 17218c2ecf20Sopenharmony_ci .descr = "VBF double-bit error", 17228c2ecf20Sopenharmony_ci }, 17238c2ecf20Sopenharmony_ci {0, 0, NULL}, 17248c2ecf20Sopenharmony_ci}; 17258c2ecf20Sopenharmony_ci 17268c2ecf20Sopenharmony_ci#define L2C_MCI_INT_W1S 0x8 17278c2ecf20Sopenharmony_ci#define L2C_MCI_INT_ENA_W1C 0x20 17288c2ecf20Sopenharmony_ci 17298c2ecf20Sopenharmony_ci#define L2C_MCI_INT_ENA_ALL (L2C_MCI_INT_VBFSBE | L2C_MCI_INT_VBFDBE) 17308c2ecf20Sopenharmony_ci 17318c2ecf20Sopenharmony_ci#define L2C_MCI_INT_ENA_W1S 0x28 17328c2ecf20Sopenharmony_ci 17338c2ecf20Sopenharmony_ci#define L2C_MCI_ERR 0x10000 17348c2ecf20Sopenharmony_ci 17358c2ecf20Sopenharmony_ci#define L2C_MESSAGE_SIZE SZ_1K 17368c2ecf20Sopenharmony_ci#define L2C_OTHER_SIZE (50 * ARRAY_SIZE(l2_tad_errors)) 17378c2ecf20Sopenharmony_ci 17388c2ecf20Sopenharmony_cistruct l2c_err_ctx { 17398c2ecf20Sopenharmony_ci char *reg_ext_name; 17408c2ecf20Sopenharmony_ci u64 reg_int; 17418c2ecf20Sopenharmony_ci u64 reg_ext; 17428c2ecf20Sopenharmony_ci}; 17438c2ecf20Sopenharmony_ci 17448c2ecf20Sopenharmony_cistruct thunderx_l2c { 17458c2ecf20Sopenharmony_ci void __iomem *regs; 17468c2ecf20Sopenharmony_ci struct pci_dev *pdev; 17478c2ecf20Sopenharmony_ci struct edac_device_ctl_info *edac_dev; 17488c2ecf20Sopenharmony_ci 17498c2ecf20Sopenharmony_ci struct dentry *debugfs; 17508c2ecf20Sopenharmony_ci 17518c2ecf20Sopenharmony_ci int index; 17528c2ecf20Sopenharmony_ci 17538c2ecf20Sopenharmony_ci struct msix_entry msix_ent; 17548c2ecf20Sopenharmony_ci 17558c2ecf20Sopenharmony_ci struct l2c_err_ctx err_ctx[RING_ENTRIES]; 17568c2ecf20Sopenharmony_ci unsigned long ring_head; 17578c2ecf20Sopenharmony_ci unsigned long ring_tail; 17588c2ecf20Sopenharmony_ci}; 17598c2ecf20Sopenharmony_ci 17608c2ecf20Sopenharmony_cistatic irqreturn_t thunderx_l2c_tad_isr(int irq, void *irq_id) 17618c2ecf20Sopenharmony_ci{ 17628c2ecf20Sopenharmony_ci struct msix_entry *msix = irq_id; 17638c2ecf20Sopenharmony_ci struct thunderx_l2c *tad = container_of(msix, struct thunderx_l2c, 17648c2ecf20Sopenharmony_ci msix_ent); 17658c2ecf20Sopenharmony_ci 17668c2ecf20Sopenharmony_ci unsigned long head = ring_pos(tad->ring_head, ARRAY_SIZE(tad->err_ctx)); 17678c2ecf20Sopenharmony_ci struct l2c_err_ctx *ctx = &tad->err_ctx[head]; 17688c2ecf20Sopenharmony_ci 17698c2ecf20Sopenharmony_ci ctx->reg_int = readq(tad->regs + L2C_TAD_INT_W1C); 17708c2ecf20Sopenharmony_ci 17718c2ecf20Sopenharmony_ci if (ctx->reg_int & L2C_TAD_INT_ECC) { 17728c2ecf20Sopenharmony_ci ctx->reg_ext_name = "TQD_ERR"; 17738c2ecf20Sopenharmony_ci ctx->reg_ext = readq(tad->regs + L2C_TAD_TQD_ERR); 17748c2ecf20Sopenharmony_ci } else if (ctx->reg_int & L2C_TAD_INT_TAG) { 17758c2ecf20Sopenharmony_ci ctx->reg_ext_name = "TTG_ERR"; 17768c2ecf20Sopenharmony_ci ctx->reg_ext = readq(tad->regs + L2C_TAD_TTG_ERR); 17778c2ecf20Sopenharmony_ci } else if (ctx->reg_int & L2C_TAD_INT_LFBTO) { 17788c2ecf20Sopenharmony_ci ctx->reg_ext_name = "TIMEOUT"; 17798c2ecf20Sopenharmony_ci ctx->reg_ext = readq(tad->regs + L2C_TAD_TIMEOUT); 17808c2ecf20Sopenharmony_ci } else if (ctx->reg_int & L2C_TAD_INT_DISOCI) { 17818c2ecf20Sopenharmony_ci ctx->reg_ext_name = "ERR"; 17828c2ecf20Sopenharmony_ci ctx->reg_ext = readq(tad->regs + L2C_TAD_ERR); 17838c2ecf20Sopenharmony_ci } 17848c2ecf20Sopenharmony_ci 17858c2ecf20Sopenharmony_ci writeq(ctx->reg_int, tad->regs + L2C_TAD_INT_W1C); 17868c2ecf20Sopenharmony_ci 17878c2ecf20Sopenharmony_ci tad->ring_head++; 17888c2ecf20Sopenharmony_ci 17898c2ecf20Sopenharmony_ci return IRQ_WAKE_THREAD; 17908c2ecf20Sopenharmony_ci} 17918c2ecf20Sopenharmony_ci 17928c2ecf20Sopenharmony_cistatic irqreturn_t thunderx_l2c_cbc_isr(int irq, void *irq_id) 17938c2ecf20Sopenharmony_ci{ 17948c2ecf20Sopenharmony_ci struct msix_entry *msix = irq_id; 17958c2ecf20Sopenharmony_ci struct thunderx_l2c *cbc = container_of(msix, struct thunderx_l2c, 17968c2ecf20Sopenharmony_ci msix_ent); 17978c2ecf20Sopenharmony_ci 17988c2ecf20Sopenharmony_ci unsigned long head = ring_pos(cbc->ring_head, ARRAY_SIZE(cbc->err_ctx)); 17998c2ecf20Sopenharmony_ci struct l2c_err_ctx *ctx = &cbc->err_ctx[head]; 18008c2ecf20Sopenharmony_ci 18018c2ecf20Sopenharmony_ci ctx->reg_int = readq(cbc->regs + L2C_CBC_INT_W1C); 18028c2ecf20Sopenharmony_ci 18038c2ecf20Sopenharmony_ci if (ctx->reg_int & L2C_CBC_INT_RSD) { 18048c2ecf20Sopenharmony_ci ctx->reg_ext_name = "RSDERR"; 18058c2ecf20Sopenharmony_ci ctx->reg_ext = readq(cbc->regs + L2C_CBC_RSDERR); 18068c2ecf20Sopenharmony_ci } else if (ctx->reg_int & L2C_CBC_INT_MIB) { 18078c2ecf20Sopenharmony_ci ctx->reg_ext_name = "MIBERR"; 18088c2ecf20Sopenharmony_ci ctx->reg_ext = readq(cbc->regs + L2C_CBC_MIBERR); 18098c2ecf20Sopenharmony_ci } else if (ctx->reg_int & L2C_CBC_INT_IODISOCI) { 18108c2ecf20Sopenharmony_ci ctx->reg_ext_name = "IODISOCIERR"; 18118c2ecf20Sopenharmony_ci ctx->reg_ext = readq(cbc->regs + L2C_CBC_IODISOCIERR); 18128c2ecf20Sopenharmony_ci } 18138c2ecf20Sopenharmony_ci 18148c2ecf20Sopenharmony_ci writeq(ctx->reg_int, cbc->regs + L2C_CBC_INT_W1C); 18158c2ecf20Sopenharmony_ci 18168c2ecf20Sopenharmony_ci cbc->ring_head++; 18178c2ecf20Sopenharmony_ci 18188c2ecf20Sopenharmony_ci return IRQ_WAKE_THREAD; 18198c2ecf20Sopenharmony_ci} 18208c2ecf20Sopenharmony_ci 18218c2ecf20Sopenharmony_cistatic irqreturn_t thunderx_l2c_mci_isr(int irq, void *irq_id) 18228c2ecf20Sopenharmony_ci{ 18238c2ecf20Sopenharmony_ci struct msix_entry *msix = irq_id; 18248c2ecf20Sopenharmony_ci struct thunderx_l2c *mci = container_of(msix, struct thunderx_l2c, 18258c2ecf20Sopenharmony_ci msix_ent); 18268c2ecf20Sopenharmony_ci 18278c2ecf20Sopenharmony_ci unsigned long head = ring_pos(mci->ring_head, ARRAY_SIZE(mci->err_ctx)); 18288c2ecf20Sopenharmony_ci struct l2c_err_ctx *ctx = &mci->err_ctx[head]; 18298c2ecf20Sopenharmony_ci 18308c2ecf20Sopenharmony_ci ctx->reg_int = readq(mci->regs + L2C_MCI_INT_W1C); 18318c2ecf20Sopenharmony_ci ctx->reg_ext = readq(mci->regs + L2C_MCI_ERR); 18328c2ecf20Sopenharmony_ci 18338c2ecf20Sopenharmony_ci writeq(ctx->reg_int, mci->regs + L2C_MCI_INT_W1C); 18348c2ecf20Sopenharmony_ci 18358c2ecf20Sopenharmony_ci ctx->reg_ext_name = "ERR"; 18368c2ecf20Sopenharmony_ci 18378c2ecf20Sopenharmony_ci mci->ring_head++; 18388c2ecf20Sopenharmony_ci 18398c2ecf20Sopenharmony_ci return IRQ_WAKE_THREAD; 18408c2ecf20Sopenharmony_ci} 18418c2ecf20Sopenharmony_ci 18428c2ecf20Sopenharmony_cistatic irqreturn_t thunderx_l2c_threaded_isr(int irq, void *irq_id) 18438c2ecf20Sopenharmony_ci{ 18448c2ecf20Sopenharmony_ci struct msix_entry *msix = irq_id; 18458c2ecf20Sopenharmony_ci struct thunderx_l2c *l2c = container_of(msix, struct thunderx_l2c, 18468c2ecf20Sopenharmony_ci msix_ent); 18478c2ecf20Sopenharmony_ci 18488c2ecf20Sopenharmony_ci unsigned long tail = ring_pos(l2c->ring_tail, ARRAY_SIZE(l2c->err_ctx)); 18498c2ecf20Sopenharmony_ci struct l2c_err_ctx *ctx = &l2c->err_ctx[tail]; 18508c2ecf20Sopenharmony_ci irqreturn_t ret = IRQ_NONE; 18518c2ecf20Sopenharmony_ci 18528c2ecf20Sopenharmony_ci u64 mask_ue, mask_ce; 18538c2ecf20Sopenharmony_ci const struct error_descr *l2_errors; 18548c2ecf20Sopenharmony_ci char *reg_int_name; 18558c2ecf20Sopenharmony_ci 18568c2ecf20Sopenharmony_ci char *msg; 18578c2ecf20Sopenharmony_ci char *other; 18588c2ecf20Sopenharmony_ci 18598c2ecf20Sopenharmony_ci msg = kmalloc(OCX_MESSAGE_SIZE, GFP_KERNEL); 18608c2ecf20Sopenharmony_ci other = kmalloc(OCX_OTHER_SIZE, GFP_KERNEL); 18618c2ecf20Sopenharmony_ci 18628c2ecf20Sopenharmony_ci if (!msg || !other) 18638c2ecf20Sopenharmony_ci goto err_free; 18648c2ecf20Sopenharmony_ci 18658c2ecf20Sopenharmony_ci switch (l2c->pdev->device) { 18668c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_THUNDER_L2C_TAD: 18678c2ecf20Sopenharmony_ci reg_int_name = "L2C_TAD_INT"; 18688c2ecf20Sopenharmony_ci mask_ue = L2C_TAD_INT_UE; 18698c2ecf20Sopenharmony_ci mask_ce = L2C_TAD_INT_CE; 18708c2ecf20Sopenharmony_ci l2_errors = l2_tad_errors; 18718c2ecf20Sopenharmony_ci break; 18728c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_THUNDER_L2C_CBC: 18738c2ecf20Sopenharmony_ci reg_int_name = "L2C_CBC_INT"; 18748c2ecf20Sopenharmony_ci mask_ue = L2C_CBC_INT_UE; 18758c2ecf20Sopenharmony_ci mask_ce = L2C_CBC_INT_CE; 18768c2ecf20Sopenharmony_ci l2_errors = l2_cbc_errors; 18778c2ecf20Sopenharmony_ci break; 18788c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_THUNDER_L2C_MCI: 18798c2ecf20Sopenharmony_ci reg_int_name = "L2C_MCI_INT"; 18808c2ecf20Sopenharmony_ci mask_ue = L2C_MCI_INT_VBFDBE; 18818c2ecf20Sopenharmony_ci mask_ce = L2C_MCI_INT_VBFSBE; 18828c2ecf20Sopenharmony_ci l2_errors = l2_mci_errors; 18838c2ecf20Sopenharmony_ci break; 18848c2ecf20Sopenharmony_ci default: 18858c2ecf20Sopenharmony_ci dev_err(&l2c->pdev->dev, "Unsupported device: %04x\n", 18868c2ecf20Sopenharmony_ci l2c->pdev->device); 18878c2ecf20Sopenharmony_ci goto err_free; 18888c2ecf20Sopenharmony_ci } 18898c2ecf20Sopenharmony_ci 18908c2ecf20Sopenharmony_ci while (CIRC_CNT(l2c->ring_head, l2c->ring_tail, 18918c2ecf20Sopenharmony_ci ARRAY_SIZE(l2c->err_ctx))) { 18928c2ecf20Sopenharmony_ci snprintf(msg, L2C_MESSAGE_SIZE, 18938c2ecf20Sopenharmony_ci "%s: %s: %016llx, %s: %016llx", 18948c2ecf20Sopenharmony_ci l2c->edac_dev->ctl_name, reg_int_name, ctx->reg_int, 18958c2ecf20Sopenharmony_ci ctx->reg_ext_name, ctx->reg_ext); 18968c2ecf20Sopenharmony_ci 18978c2ecf20Sopenharmony_ci decode_register(other, L2C_OTHER_SIZE, l2_errors, ctx->reg_int); 18988c2ecf20Sopenharmony_ci 18998c2ecf20Sopenharmony_ci strlcat(msg, other, L2C_MESSAGE_SIZE); 19008c2ecf20Sopenharmony_ci 19018c2ecf20Sopenharmony_ci if (ctx->reg_int & mask_ue) 19028c2ecf20Sopenharmony_ci edac_device_handle_ue(l2c->edac_dev, 0, 0, msg); 19038c2ecf20Sopenharmony_ci else if (ctx->reg_int & mask_ce) 19048c2ecf20Sopenharmony_ci edac_device_handle_ce(l2c->edac_dev, 0, 0, msg); 19058c2ecf20Sopenharmony_ci 19068c2ecf20Sopenharmony_ci l2c->ring_tail++; 19078c2ecf20Sopenharmony_ci } 19088c2ecf20Sopenharmony_ci 19098c2ecf20Sopenharmony_ci ret = IRQ_HANDLED; 19108c2ecf20Sopenharmony_ci 19118c2ecf20Sopenharmony_cierr_free: 19128c2ecf20Sopenharmony_ci kfree(other); 19138c2ecf20Sopenharmony_ci kfree(msg); 19148c2ecf20Sopenharmony_ci 19158c2ecf20Sopenharmony_ci return ret; 19168c2ecf20Sopenharmony_ci} 19178c2ecf20Sopenharmony_ci 19188c2ecf20Sopenharmony_ci#define L2C_DEBUGFS_ATTR(_name, _reg) DEBUGFS_REG_ATTR(l2c, _name, _reg) 19198c2ecf20Sopenharmony_ci 19208c2ecf20Sopenharmony_ciL2C_DEBUGFS_ATTR(tad_int, L2C_TAD_INT_W1S); 19218c2ecf20Sopenharmony_ci 19228c2ecf20Sopenharmony_cistatic struct debugfs_entry *l2c_tad_dfs_ents[] = { 19238c2ecf20Sopenharmony_ci &debugfs_tad_int, 19248c2ecf20Sopenharmony_ci}; 19258c2ecf20Sopenharmony_ci 19268c2ecf20Sopenharmony_ciL2C_DEBUGFS_ATTR(cbc_int, L2C_CBC_INT_W1S); 19278c2ecf20Sopenharmony_ci 19288c2ecf20Sopenharmony_cistatic struct debugfs_entry *l2c_cbc_dfs_ents[] = { 19298c2ecf20Sopenharmony_ci &debugfs_cbc_int, 19308c2ecf20Sopenharmony_ci}; 19318c2ecf20Sopenharmony_ci 19328c2ecf20Sopenharmony_ciL2C_DEBUGFS_ATTR(mci_int, L2C_MCI_INT_W1S); 19338c2ecf20Sopenharmony_ci 19348c2ecf20Sopenharmony_cistatic struct debugfs_entry *l2c_mci_dfs_ents[] = { 19358c2ecf20Sopenharmony_ci &debugfs_mci_int, 19368c2ecf20Sopenharmony_ci}; 19378c2ecf20Sopenharmony_ci 19388c2ecf20Sopenharmony_cistatic const struct pci_device_id thunderx_l2c_pci_tbl[] = { 19398c2ecf20Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, PCI_DEVICE_ID_THUNDER_L2C_TAD), }, 19408c2ecf20Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, PCI_DEVICE_ID_THUNDER_L2C_CBC), }, 19418c2ecf20Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, PCI_DEVICE_ID_THUNDER_L2C_MCI), }, 19428c2ecf20Sopenharmony_ci { 0, }, 19438c2ecf20Sopenharmony_ci}; 19448c2ecf20Sopenharmony_ci 19458c2ecf20Sopenharmony_cistatic int thunderx_l2c_probe(struct pci_dev *pdev, 19468c2ecf20Sopenharmony_ci const struct pci_device_id *id) 19478c2ecf20Sopenharmony_ci{ 19488c2ecf20Sopenharmony_ci struct thunderx_l2c *l2c; 19498c2ecf20Sopenharmony_ci struct edac_device_ctl_info *edac_dev; 19508c2ecf20Sopenharmony_ci struct debugfs_entry **l2c_devattr; 19518c2ecf20Sopenharmony_ci size_t dfs_entries; 19528c2ecf20Sopenharmony_ci irqreturn_t (*thunderx_l2c_isr)(int, void *) = NULL; 19538c2ecf20Sopenharmony_ci char name[32]; 19548c2ecf20Sopenharmony_ci const char *fmt; 19558c2ecf20Sopenharmony_ci u64 reg_en_offs, reg_en_mask; 19568c2ecf20Sopenharmony_ci int idx; 19578c2ecf20Sopenharmony_ci int ret; 19588c2ecf20Sopenharmony_ci 19598c2ecf20Sopenharmony_ci ret = pcim_enable_device(pdev); 19608c2ecf20Sopenharmony_ci if (ret) { 19618c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Cannot enable PCI device: %d\n", ret); 19628c2ecf20Sopenharmony_ci return ret; 19638c2ecf20Sopenharmony_ci } 19648c2ecf20Sopenharmony_ci 19658c2ecf20Sopenharmony_ci ret = pcim_iomap_regions(pdev, BIT(0), "thunderx_l2c"); 19668c2ecf20Sopenharmony_ci if (ret) { 19678c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Cannot map PCI resources: %d\n", ret); 19688c2ecf20Sopenharmony_ci return ret; 19698c2ecf20Sopenharmony_ci } 19708c2ecf20Sopenharmony_ci 19718c2ecf20Sopenharmony_ci switch (pdev->device) { 19728c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_THUNDER_L2C_TAD: 19738c2ecf20Sopenharmony_ci thunderx_l2c_isr = thunderx_l2c_tad_isr; 19748c2ecf20Sopenharmony_ci l2c_devattr = l2c_tad_dfs_ents; 19758c2ecf20Sopenharmony_ci dfs_entries = ARRAY_SIZE(l2c_tad_dfs_ents); 19768c2ecf20Sopenharmony_ci fmt = "L2C-TAD%d"; 19778c2ecf20Sopenharmony_ci reg_en_offs = L2C_TAD_INT_ENA_W1S; 19788c2ecf20Sopenharmony_ci reg_en_mask = L2C_TAD_INT_ENA_ALL; 19798c2ecf20Sopenharmony_ci break; 19808c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_THUNDER_L2C_CBC: 19818c2ecf20Sopenharmony_ci thunderx_l2c_isr = thunderx_l2c_cbc_isr; 19828c2ecf20Sopenharmony_ci l2c_devattr = l2c_cbc_dfs_ents; 19838c2ecf20Sopenharmony_ci dfs_entries = ARRAY_SIZE(l2c_cbc_dfs_ents); 19848c2ecf20Sopenharmony_ci fmt = "L2C-CBC%d"; 19858c2ecf20Sopenharmony_ci reg_en_offs = L2C_CBC_INT_ENA_W1S; 19868c2ecf20Sopenharmony_ci reg_en_mask = L2C_CBC_INT_ENA_ALL; 19878c2ecf20Sopenharmony_ci break; 19888c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_THUNDER_L2C_MCI: 19898c2ecf20Sopenharmony_ci thunderx_l2c_isr = thunderx_l2c_mci_isr; 19908c2ecf20Sopenharmony_ci l2c_devattr = l2c_mci_dfs_ents; 19918c2ecf20Sopenharmony_ci dfs_entries = ARRAY_SIZE(l2c_mci_dfs_ents); 19928c2ecf20Sopenharmony_ci fmt = "L2C-MCI%d"; 19938c2ecf20Sopenharmony_ci reg_en_offs = L2C_MCI_INT_ENA_W1S; 19948c2ecf20Sopenharmony_ci reg_en_mask = L2C_MCI_INT_ENA_ALL; 19958c2ecf20Sopenharmony_ci break; 19968c2ecf20Sopenharmony_ci default: 19978c2ecf20Sopenharmony_ci //Should never ever get here 19988c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Unsupported PCI device: %04x\n", 19998c2ecf20Sopenharmony_ci pdev->device); 20008c2ecf20Sopenharmony_ci return -EINVAL; 20018c2ecf20Sopenharmony_ci } 20028c2ecf20Sopenharmony_ci 20038c2ecf20Sopenharmony_ci idx = edac_device_alloc_index(); 20048c2ecf20Sopenharmony_ci snprintf(name, sizeof(name), fmt, idx); 20058c2ecf20Sopenharmony_ci 20068c2ecf20Sopenharmony_ci edac_dev = edac_device_alloc_ctl_info(sizeof(struct thunderx_l2c), 20078c2ecf20Sopenharmony_ci name, 1, "L2C", 1, 0, 20088c2ecf20Sopenharmony_ci NULL, 0, idx); 20098c2ecf20Sopenharmony_ci if (!edac_dev) { 20108c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Cannot allocate EDAC device\n"); 20118c2ecf20Sopenharmony_ci return -ENOMEM; 20128c2ecf20Sopenharmony_ci } 20138c2ecf20Sopenharmony_ci 20148c2ecf20Sopenharmony_ci l2c = edac_dev->pvt_info; 20158c2ecf20Sopenharmony_ci l2c->edac_dev = edac_dev; 20168c2ecf20Sopenharmony_ci 20178c2ecf20Sopenharmony_ci l2c->regs = pcim_iomap_table(pdev)[0]; 20188c2ecf20Sopenharmony_ci if (!l2c->regs) { 20198c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Cannot map PCI resources\n"); 20208c2ecf20Sopenharmony_ci ret = -ENODEV; 20218c2ecf20Sopenharmony_ci goto err_free; 20228c2ecf20Sopenharmony_ci } 20238c2ecf20Sopenharmony_ci 20248c2ecf20Sopenharmony_ci l2c->pdev = pdev; 20258c2ecf20Sopenharmony_ci 20268c2ecf20Sopenharmony_ci l2c->ring_head = 0; 20278c2ecf20Sopenharmony_ci l2c->ring_tail = 0; 20288c2ecf20Sopenharmony_ci 20298c2ecf20Sopenharmony_ci l2c->msix_ent.entry = 0; 20308c2ecf20Sopenharmony_ci l2c->msix_ent.vector = 0; 20318c2ecf20Sopenharmony_ci 20328c2ecf20Sopenharmony_ci ret = pci_enable_msix_exact(pdev, &l2c->msix_ent, 1); 20338c2ecf20Sopenharmony_ci if (ret) { 20348c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Cannot enable interrupt: %d\n", ret); 20358c2ecf20Sopenharmony_ci goto err_free; 20368c2ecf20Sopenharmony_ci } 20378c2ecf20Sopenharmony_ci 20388c2ecf20Sopenharmony_ci ret = devm_request_threaded_irq(&pdev->dev, l2c->msix_ent.vector, 20398c2ecf20Sopenharmony_ci thunderx_l2c_isr, 20408c2ecf20Sopenharmony_ci thunderx_l2c_threaded_isr, 20418c2ecf20Sopenharmony_ci 0, "[EDAC] ThunderX L2C", 20428c2ecf20Sopenharmony_ci &l2c->msix_ent); 20438c2ecf20Sopenharmony_ci if (ret) 20448c2ecf20Sopenharmony_ci goto err_free; 20458c2ecf20Sopenharmony_ci 20468c2ecf20Sopenharmony_ci edac_dev->dev = &pdev->dev; 20478c2ecf20Sopenharmony_ci edac_dev->dev_name = dev_name(&pdev->dev); 20488c2ecf20Sopenharmony_ci edac_dev->mod_name = "thunderx-l2c"; 20498c2ecf20Sopenharmony_ci edac_dev->ctl_name = "thunderx-l2c"; 20508c2ecf20Sopenharmony_ci 20518c2ecf20Sopenharmony_ci ret = edac_device_add_device(edac_dev); 20528c2ecf20Sopenharmony_ci if (ret) { 20538c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Cannot add EDAC device: %d\n", ret); 20548c2ecf20Sopenharmony_ci goto err_free; 20558c2ecf20Sopenharmony_ci } 20568c2ecf20Sopenharmony_ci 20578c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_EDAC_DEBUG)) { 20588c2ecf20Sopenharmony_ci l2c->debugfs = edac_debugfs_create_dir(pdev->dev.kobj.name); 20598c2ecf20Sopenharmony_ci 20608c2ecf20Sopenharmony_ci ret = thunderx_create_debugfs_nodes(l2c->debugfs, l2c_devattr, 20618c2ecf20Sopenharmony_ci l2c, dfs_entries); 20628c2ecf20Sopenharmony_ci 20638c2ecf20Sopenharmony_ci if (ret != dfs_entries) { 20648c2ecf20Sopenharmony_ci dev_warn(&pdev->dev, "Error creating debugfs entries: %d%s\n", 20658c2ecf20Sopenharmony_ci ret, ret >= 0 ? " created" : ""); 20668c2ecf20Sopenharmony_ci } 20678c2ecf20Sopenharmony_ci } 20688c2ecf20Sopenharmony_ci 20698c2ecf20Sopenharmony_ci pci_set_drvdata(pdev, edac_dev); 20708c2ecf20Sopenharmony_ci 20718c2ecf20Sopenharmony_ci writeq(reg_en_mask, l2c->regs + reg_en_offs); 20728c2ecf20Sopenharmony_ci 20738c2ecf20Sopenharmony_ci return 0; 20748c2ecf20Sopenharmony_ci 20758c2ecf20Sopenharmony_cierr_free: 20768c2ecf20Sopenharmony_ci edac_device_free_ctl_info(edac_dev); 20778c2ecf20Sopenharmony_ci 20788c2ecf20Sopenharmony_ci return ret; 20798c2ecf20Sopenharmony_ci} 20808c2ecf20Sopenharmony_ci 20818c2ecf20Sopenharmony_cistatic void thunderx_l2c_remove(struct pci_dev *pdev) 20828c2ecf20Sopenharmony_ci{ 20838c2ecf20Sopenharmony_ci struct edac_device_ctl_info *edac_dev = pci_get_drvdata(pdev); 20848c2ecf20Sopenharmony_ci struct thunderx_l2c *l2c = edac_dev->pvt_info; 20858c2ecf20Sopenharmony_ci 20868c2ecf20Sopenharmony_ci switch (pdev->device) { 20878c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_THUNDER_L2C_TAD: 20888c2ecf20Sopenharmony_ci writeq(L2C_TAD_INT_ENA_ALL, l2c->regs + L2C_TAD_INT_ENA_W1C); 20898c2ecf20Sopenharmony_ci break; 20908c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_THUNDER_L2C_CBC: 20918c2ecf20Sopenharmony_ci writeq(L2C_CBC_INT_ENA_ALL, l2c->regs + L2C_CBC_INT_ENA_W1C); 20928c2ecf20Sopenharmony_ci break; 20938c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_THUNDER_L2C_MCI: 20948c2ecf20Sopenharmony_ci writeq(L2C_MCI_INT_ENA_ALL, l2c->regs + L2C_MCI_INT_ENA_W1C); 20958c2ecf20Sopenharmony_ci break; 20968c2ecf20Sopenharmony_ci } 20978c2ecf20Sopenharmony_ci 20988c2ecf20Sopenharmony_ci edac_debugfs_remove_recursive(l2c->debugfs); 20998c2ecf20Sopenharmony_ci 21008c2ecf20Sopenharmony_ci edac_device_del_device(&pdev->dev); 21018c2ecf20Sopenharmony_ci edac_device_free_ctl_info(edac_dev); 21028c2ecf20Sopenharmony_ci} 21038c2ecf20Sopenharmony_ci 21048c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, thunderx_l2c_pci_tbl); 21058c2ecf20Sopenharmony_ci 21068c2ecf20Sopenharmony_cistatic struct pci_driver thunderx_l2c_driver = { 21078c2ecf20Sopenharmony_ci .name = "thunderx_l2c_edac", 21088c2ecf20Sopenharmony_ci .probe = thunderx_l2c_probe, 21098c2ecf20Sopenharmony_ci .remove = thunderx_l2c_remove, 21108c2ecf20Sopenharmony_ci .id_table = thunderx_l2c_pci_tbl, 21118c2ecf20Sopenharmony_ci}; 21128c2ecf20Sopenharmony_ci 21138c2ecf20Sopenharmony_cistatic int __init thunderx_edac_init(void) 21148c2ecf20Sopenharmony_ci{ 21158c2ecf20Sopenharmony_ci int rc = 0; 21168c2ecf20Sopenharmony_ci 21178c2ecf20Sopenharmony_ci rc = pci_register_driver(&thunderx_lmc_driver); 21188c2ecf20Sopenharmony_ci if (rc) 21198c2ecf20Sopenharmony_ci return rc; 21208c2ecf20Sopenharmony_ci 21218c2ecf20Sopenharmony_ci rc = pci_register_driver(&thunderx_ocx_driver); 21228c2ecf20Sopenharmony_ci if (rc) 21238c2ecf20Sopenharmony_ci goto err_lmc; 21248c2ecf20Sopenharmony_ci 21258c2ecf20Sopenharmony_ci rc = pci_register_driver(&thunderx_l2c_driver); 21268c2ecf20Sopenharmony_ci if (rc) 21278c2ecf20Sopenharmony_ci goto err_ocx; 21288c2ecf20Sopenharmony_ci 21298c2ecf20Sopenharmony_ci return rc; 21308c2ecf20Sopenharmony_cierr_ocx: 21318c2ecf20Sopenharmony_ci pci_unregister_driver(&thunderx_ocx_driver); 21328c2ecf20Sopenharmony_cierr_lmc: 21338c2ecf20Sopenharmony_ci pci_unregister_driver(&thunderx_lmc_driver); 21348c2ecf20Sopenharmony_ci 21358c2ecf20Sopenharmony_ci return rc; 21368c2ecf20Sopenharmony_ci} 21378c2ecf20Sopenharmony_ci 21388c2ecf20Sopenharmony_cistatic void __exit thunderx_edac_exit(void) 21398c2ecf20Sopenharmony_ci{ 21408c2ecf20Sopenharmony_ci pci_unregister_driver(&thunderx_l2c_driver); 21418c2ecf20Sopenharmony_ci pci_unregister_driver(&thunderx_ocx_driver); 21428c2ecf20Sopenharmony_ci pci_unregister_driver(&thunderx_lmc_driver); 21438c2ecf20Sopenharmony_ci 21448c2ecf20Sopenharmony_ci} 21458c2ecf20Sopenharmony_ci 21468c2ecf20Sopenharmony_cimodule_init(thunderx_edac_init); 21478c2ecf20Sopenharmony_cimodule_exit(thunderx_edac_exit); 21488c2ecf20Sopenharmony_ci 21498c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 21508c2ecf20Sopenharmony_ciMODULE_AUTHOR("Cavium, Inc."); 21518c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("EDAC Driver for Cavium ThunderX"); 2152