162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Cavium ThunderX memory controller kernel module 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 562306a36Sopenharmony_ci * License. See the file "COPYING" in the main directory of this archive 662306a36Sopenharmony_ci * for more details. 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Copyright Cavium, Inc. (C) 2015-2017. All rights reserved. 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci#include <linux/pci.h> 1462306a36Sopenharmony_ci#include <linux/edac.h> 1562306a36Sopenharmony_ci#include <linux/interrupt.h> 1662306a36Sopenharmony_ci#include <linux/string.h> 1762306a36Sopenharmony_ci#include <linux/stop_machine.h> 1862306a36Sopenharmony_ci#include <linux/delay.h> 1962306a36Sopenharmony_ci#include <linux/sizes.h> 2062306a36Sopenharmony_ci#include <linux/atomic.h> 2162306a36Sopenharmony_ci#include <linux/bitfield.h> 2262306a36Sopenharmony_ci#include <linux/circ_buf.h> 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#include <asm/page.h> 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#include "edac_module.h" 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#define phys_to_pfn(phys) (PFN_DOWN(phys)) 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#define THUNDERX_NODE GENMASK(45, 44) 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_cienum { 3362306a36Sopenharmony_ci ERR_CORRECTED = 1, 3462306a36Sopenharmony_ci ERR_UNCORRECTED = 2, 3562306a36Sopenharmony_ci ERR_UNKNOWN = 3, 3662306a36Sopenharmony_ci}; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci#define MAX_SYNDROME_REGS 4 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistruct error_syndrome { 4162306a36Sopenharmony_ci u64 reg[MAX_SYNDROME_REGS]; 4262306a36Sopenharmony_ci}; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cistruct error_descr { 4562306a36Sopenharmony_ci int type; 4662306a36Sopenharmony_ci u64 mask; 4762306a36Sopenharmony_ci char *descr; 4862306a36Sopenharmony_ci}; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistatic void decode_register(char *str, size_t size, 5162306a36Sopenharmony_ci const struct error_descr *descr, 5262306a36Sopenharmony_ci const uint64_t reg) 5362306a36Sopenharmony_ci{ 5462306a36Sopenharmony_ci int ret = 0; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci while (descr->type && descr->mask && descr->descr) { 5762306a36Sopenharmony_ci if (reg & descr->mask) { 5862306a36Sopenharmony_ci ret = snprintf(str, size, "\n\t%s, %s", 5962306a36Sopenharmony_ci descr->type == ERR_CORRECTED ? 6062306a36Sopenharmony_ci "Corrected" : "Uncorrected", 6162306a36Sopenharmony_ci descr->descr); 6262306a36Sopenharmony_ci str += ret; 6362306a36Sopenharmony_ci size -= ret; 6462306a36Sopenharmony_ci } 6562306a36Sopenharmony_ci descr++; 6662306a36Sopenharmony_ci } 6762306a36Sopenharmony_ci} 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_cistatic unsigned long get_bits(unsigned long data, int pos, int width) 7062306a36Sopenharmony_ci{ 7162306a36Sopenharmony_ci return (data >> pos) & ((1 << width) - 1); 7262306a36Sopenharmony_ci} 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci#define L2C_CTL 0x87E080800000 7562306a36Sopenharmony_ci#define L2C_CTL_DISIDXALIAS BIT(0) 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci#define PCI_DEVICE_ID_THUNDER_LMC 0xa022 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci#define LMC_FADR 0x20 8062306a36Sopenharmony_ci#define LMC_FADR_FDIMM(x) ((x >> 37) & 0x1) 8162306a36Sopenharmony_ci#define LMC_FADR_FBUNK(x) ((x >> 36) & 0x1) 8262306a36Sopenharmony_ci#define LMC_FADR_FBANK(x) ((x >> 32) & 0xf) 8362306a36Sopenharmony_ci#define LMC_FADR_FROW(x) ((x >> 14) & 0xffff) 8462306a36Sopenharmony_ci#define LMC_FADR_FCOL(x) ((x >> 0) & 0x1fff) 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci#define LMC_NXM_FADR 0x28 8762306a36Sopenharmony_ci#define LMC_ECC_SYND 0x38 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci#define LMC_ECC_PARITY_TEST 0x108 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci#define LMC_INT_W1S 0x150 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci#define LMC_INT_ENA_W1C 0x158 9462306a36Sopenharmony_ci#define LMC_INT_ENA_W1S 0x160 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci#define LMC_CONFIG 0x188 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci#define LMC_CONFIG_BG2 BIT(62) 9962306a36Sopenharmony_ci#define LMC_CONFIG_RANK_ENA BIT(42) 10062306a36Sopenharmony_ci#define LMC_CONFIG_PBANK_LSB(x) (((x) >> 5) & 0xF) 10162306a36Sopenharmony_ci#define LMC_CONFIG_ROW_LSB(x) (((x) >> 2) & 0x7) 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci#define LMC_CONTROL 0x190 10462306a36Sopenharmony_ci#define LMC_CONTROL_XOR_BANK BIT(16) 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci#define LMC_INT 0x1F0 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci#define LMC_INT_DDR_ERR BIT(11) 10962306a36Sopenharmony_ci#define LMC_INT_DED_ERR (0xFUL << 5) 11062306a36Sopenharmony_ci#define LMC_INT_SEC_ERR (0xFUL << 1) 11162306a36Sopenharmony_ci#define LMC_INT_NXM_WR_MASK BIT(0) 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci#define LMC_DDR_PLL_CTL 0x258 11462306a36Sopenharmony_ci#define LMC_DDR_PLL_CTL_DDR4 BIT(29) 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci#define LMC_FADR_SCRAMBLED 0x330 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci#define LMC_INT_UE (LMC_INT_DDR_ERR | LMC_INT_DED_ERR | \ 11962306a36Sopenharmony_ci LMC_INT_NXM_WR_MASK) 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci#define LMC_INT_CE (LMC_INT_SEC_ERR) 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_cistatic const struct error_descr lmc_errors[] = { 12462306a36Sopenharmony_ci { 12562306a36Sopenharmony_ci .type = ERR_CORRECTED, 12662306a36Sopenharmony_ci .mask = LMC_INT_SEC_ERR, 12762306a36Sopenharmony_ci .descr = "Single-bit ECC error", 12862306a36Sopenharmony_ci }, 12962306a36Sopenharmony_ci { 13062306a36Sopenharmony_ci .type = ERR_UNCORRECTED, 13162306a36Sopenharmony_ci .mask = LMC_INT_DDR_ERR, 13262306a36Sopenharmony_ci .descr = "DDR chip error", 13362306a36Sopenharmony_ci }, 13462306a36Sopenharmony_ci { 13562306a36Sopenharmony_ci .type = ERR_UNCORRECTED, 13662306a36Sopenharmony_ci .mask = LMC_INT_DED_ERR, 13762306a36Sopenharmony_ci .descr = "Double-bit ECC error", 13862306a36Sopenharmony_ci }, 13962306a36Sopenharmony_ci { 14062306a36Sopenharmony_ci .type = ERR_UNCORRECTED, 14162306a36Sopenharmony_ci .mask = LMC_INT_NXM_WR_MASK, 14262306a36Sopenharmony_ci .descr = "Non-existent memory write", 14362306a36Sopenharmony_ci }, 14462306a36Sopenharmony_ci {0, 0, NULL}, 14562306a36Sopenharmony_ci}; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci#define LMC_INT_EN_DDR_ERROR_ALERT_ENA BIT(5) 14862306a36Sopenharmony_ci#define LMC_INT_EN_DLCRAM_DED_ERR BIT(4) 14962306a36Sopenharmony_ci#define LMC_INT_EN_DLCRAM_SEC_ERR BIT(3) 15062306a36Sopenharmony_ci#define LMC_INT_INTR_DED_ENA BIT(2) 15162306a36Sopenharmony_ci#define LMC_INT_INTR_SEC_ENA BIT(1) 15262306a36Sopenharmony_ci#define LMC_INT_INTR_NXM_WR_ENA BIT(0) 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci#define LMC_INT_ENA_ALL GENMASK(5, 0) 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci#define LMC_DDR_PLL_CTL 0x258 15762306a36Sopenharmony_ci#define LMC_DDR_PLL_CTL_DDR4 BIT(29) 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci#define LMC_CONTROL 0x190 16062306a36Sopenharmony_ci#define LMC_CONTROL_RDIMM BIT(0) 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci#define LMC_SCRAM_FADR 0x330 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci#define LMC_CHAR_MASK0 0x228 16562306a36Sopenharmony_ci#define LMC_CHAR_MASK2 0x238 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci#define RING_ENTRIES 8 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_cistruct debugfs_entry { 17062306a36Sopenharmony_ci const char *name; 17162306a36Sopenharmony_ci umode_t mode; 17262306a36Sopenharmony_ci const struct file_operations fops; 17362306a36Sopenharmony_ci}; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_cistruct lmc_err_ctx { 17662306a36Sopenharmony_ci u64 reg_int; 17762306a36Sopenharmony_ci u64 reg_fadr; 17862306a36Sopenharmony_ci u64 reg_nxm_fadr; 17962306a36Sopenharmony_ci u64 reg_scram_fadr; 18062306a36Sopenharmony_ci u64 reg_ecc_synd; 18162306a36Sopenharmony_ci}; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_cistruct thunderx_lmc { 18462306a36Sopenharmony_ci void __iomem *regs; 18562306a36Sopenharmony_ci struct pci_dev *pdev; 18662306a36Sopenharmony_ci struct msix_entry msix_ent; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci atomic_t ecc_int; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci u64 mask0; 19162306a36Sopenharmony_ci u64 mask2; 19262306a36Sopenharmony_ci u64 parity_test; 19362306a36Sopenharmony_ci u64 node; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci int xbits; 19662306a36Sopenharmony_ci int bank_width; 19762306a36Sopenharmony_ci int pbank_lsb; 19862306a36Sopenharmony_ci int dimm_lsb; 19962306a36Sopenharmony_ci int rank_lsb; 20062306a36Sopenharmony_ci int bank_lsb; 20162306a36Sopenharmony_ci int row_lsb; 20262306a36Sopenharmony_ci int col_hi_lsb; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci int xor_bank; 20562306a36Sopenharmony_ci int l2c_alias; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci struct page *mem; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci struct lmc_err_ctx err_ctx[RING_ENTRIES]; 21062306a36Sopenharmony_ci unsigned long ring_head; 21162306a36Sopenharmony_ci unsigned long ring_tail; 21262306a36Sopenharmony_ci}; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci#define ring_pos(pos, size) ((pos) & (size - 1)) 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci#define DEBUGFS_STRUCT(_name, _mode, _write, _read) \ 21762306a36Sopenharmony_cistatic struct debugfs_entry debugfs_##_name = { \ 21862306a36Sopenharmony_ci .name = __stringify(_name), \ 21962306a36Sopenharmony_ci .mode = VERIFY_OCTAL_PERMISSIONS(_mode), \ 22062306a36Sopenharmony_ci .fops = { \ 22162306a36Sopenharmony_ci .open = simple_open, \ 22262306a36Sopenharmony_ci .write = _write, \ 22362306a36Sopenharmony_ci .read = _read, \ 22462306a36Sopenharmony_ci .llseek = generic_file_llseek, \ 22562306a36Sopenharmony_ci }, \ 22662306a36Sopenharmony_ci} 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci#define DEBUGFS_FIELD_ATTR(_type, _field) \ 22962306a36Sopenharmony_cistatic ssize_t thunderx_##_type##_##_field##_read(struct file *file, \ 23062306a36Sopenharmony_ci char __user *data, \ 23162306a36Sopenharmony_ci size_t count, loff_t *ppos) \ 23262306a36Sopenharmony_ci{ \ 23362306a36Sopenharmony_ci struct thunderx_##_type *pdata = file->private_data; \ 23462306a36Sopenharmony_ci char buf[20]; \ 23562306a36Sopenharmony_ci \ 23662306a36Sopenharmony_ci snprintf(buf, count, "0x%016llx", pdata->_field); \ 23762306a36Sopenharmony_ci return simple_read_from_buffer(data, count, ppos, \ 23862306a36Sopenharmony_ci buf, sizeof(buf)); \ 23962306a36Sopenharmony_ci} \ 24062306a36Sopenharmony_ci \ 24162306a36Sopenharmony_cistatic ssize_t thunderx_##_type##_##_field##_write(struct file *file, \ 24262306a36Sopenharmony_ci const char __user *data, \ 24362306a36Sopenharmony_ci size_t count, loff_t *ppos) \ 24462306a36Sopenharmony_ci{ \ 24562306a36Sopenharmony_ci struct thunderx_##_type *pdata = file->private_data; \ 24662306a36Sopenharmony_ci int res; \ 24762306a36Sopenharmony_ci \ 24862306a36Sopenharmony_ci res = kstrtoull_from_user(data, count, 0, &pdata->_field); \ 24962306a36Sopenharmony_ci \ 25062306a36Sopenharmony_ci return res ? res : count; \ 25162306a36Sopenharmony_ci} \ 25262306a36Sopenharmony_ci \ 25362306a36Sopenharmony_ciDEBUGFS_STRUCT(_field, 0600, \ 25462306a36Sopenharmony_ci thunderx_##_type##_##_field##_write, \ 25562306a36Sopenharmony_ci thunderx_##_type##_##_field##_read) \ 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci#define DEBUGFS_REG_ATTR(_type, _name, _reg) \ 25862306a36Sopenharmony_cistatic ssize_t thunderx_##_type##_##_name##_read(struct file *file, \ 25962306a36Sopenharmony_ci char __user *data, \ 26062306a36Sopenharmony_ci size_t count, loff_t *ppos) \ 26162306a36Sopenharmony_ci{ \ 26262306a36Sopenharmony_ci struct thunderx_##_type *pdata = file->private_data; \ 26362306a36Sopenharmony_ci char buf[20]; \ 26462306a36Sopenharmony_ci \ 26562306a36Sopenharmony_ci sprintf(buf, "0x%016llx", readq(pdata->regs + _reg)); \ 26662306a36Sopenharmony_ci return simple_read_from_buffer(data, count, ppos, \ 26762306a36Sopenharmony_ci buf, sizeof(buf)); \ 26862306a36Sopenharmony_ci} \ 26962306a36Sopenharmony_ci \ 27062306a36Sopenharmony_cistatic ssize_t thunderx_##_type##_##_name##_write(struct file *file, \ 27162306a36Sopenharmony_ci const char __user *data, \ 27262306a36Sopenharmony_ci size_t count, loff_t *ppos) \ 27362306a36Sopenharmony_ci{ \ 27462306a36Sopenharmony_ci struct thunderx_##_type *pdata = file->private_data; \ 27562306a36Sopenharmony_ci u64 val; \ 27662306a36Sopenharmony_ci int res; \ 27762306a36Sopenharmony_ci \ 27862306a36Sopenharmony_ci res = kstrtoull_from_user(data, count, 0, &val); \ 27962306a36Sopenharmony_ci \ 28062306a36Sopenharmony_ci if (!res) { \ 28162306a36Sopenharmony_ci writeq(val, pdata->regs + _reg); \ 28262306a36Sopenharmony_ci res = count; \ 28362306a36Sopenharmony_ci } \ 28462306a36Sopenharmony_ci \ 28562306a36Sopenharmony_ci return res; \ 28662306a36Sopenharmony_ci} \ 28762306a36Sopenharmony_ci \ 28862306a36Sopenharmony_ciDEBUGFS_STRUCT(_name, 0600, \ 28962306a36Sopenharmony_ci thunderx_##_type##_##_name##_write, \ 29062306a36Sopenharmony_ci thunderx_##_type##_##_name##_read) 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci#define LMC_DEBUGFS_ENT(_field) DEBUGFS_FIELD_ATTR(lmc, _field) 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci/* 29562306a36Sopenharmony_ci * To get an ECC error injected, the following steps are needed: 29662306a36Sopenharmony_ci * - Setup the ECC injection by writing the appropriate parameters: 29762306a36Sopenharmony_ci * echo <bit mask value> > /sys/kernel/debug/<device number>/ecc_mask0 29862306a36Sopenharmony_ci * echo <bit mask value> > /sys/kernel/debug/<device number>/ecc_mask2 29962306a36Sopenharmony_ci * echo 0x802 > /sys/kernel/debug/<device number>/ecc_parity_test 30062306a36Sopenharmony_ci * - Do the actual injection: 30162306a36Sopenharmony_ci * echo 1 > /sys/kernel/debug/<device number>/inject_ecc 30262306a36Sopenharmony_ci */ 30362306a36Sopenharmony_cistatic ssize_t thunderx_lmc_inject_int_write(struct file *file, 30462306a36Sopenharmony_ci const char __user *data, 30562306a36Sopenharmony_ci size_t count, loff_t *ppos) 30662306a36Sopenharmony_ci{ 30762306a36Sopenharmony_ci struct thunderx_lmc *lmc = file->private_data; 30862306a36Sopenharmony_ci u64 val; 30962306a36Sopenharmony_ci int res; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci res = kstrtoull_from_user(data, count, 0, &val); 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci if (!res) { 31462306a36Sopenharmony_ci /* Trigger the interrupt */ 31562306a36Sopenharmony_ci writeq(val, lmc->regs + LMC_INT_W1S); 31662306a36Sopenharmony_ci res = count; 31762306a36Sopenharmony_ci } 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci return res; 32062306a36Sopenharmony_ci} 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_cistatic ssize_t thunderx_lmc_int_read(struct file *file, 32362306a36Sopenharmony_ci char __user *data, 32462306a36Sopenharmony_ci size_t count, loff_t *ppos) 32562306a36Sopenharmony_ci{ 32662306a36Sopenharmony_ci struct thunderx_lmc *lmc = file->private_data; 32762306a36Sopenharmony_ci char buf[20]; 32862306a36Sopenharmony_ci u64 lmc_int = readq(lmc->regs + LMC_INT); 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci snprintf(buf, sizeof(buf), "0x%016llx", lmc_int); 33162306a36Sopenharmony_ci return simple_read_from_buffer(data, count, ppos, buf, sizeof(buf)); 33262306a36Sopenharmony_ci} 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci#define TEST_PATTERN 0xa5 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_cistatic int inject_ecc_fn(void *arg) 33762306a36Sopenharmony_ci{ 33862306a36Sopenharmony_ci struct thunderx_lmc *lmc = arg; 33962306a36Sopenharmony_ci uintptr_t addr, phys; 34062306a36Sopenharmony_ci unsigned int cline_size = cache_line_size(); 34162306a36Sopenharmony_ci const unsigned int lines = PAGE_SIZE / cline_size; 34262306a36Sopenharmony_ci unsigned int i, cl_idx; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci addr = (uintptr_t)page_address(lmc->mem); 34562306a36Sopenharmony_ci phys = (uintptr_t)page_to_phys(lmc->mem); 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci cl_idx = (phys & 0x7f) >> 4; 34862306a36Sopenharmony_ci lmc->parity_test &= ~(7ULL << 8); 34962306a36Sopenharmony_ci lmc->parity_test |= (cl_idx << 8); 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci writeq(lmc->mask0, lmc->regs + LMC_CHAR_MASK0); 35262306a36Sopenharmony_ci writeq(lmc->mask2, lmc->regs + LMC_CHAR_MASK2); 35362306a36Sopenharmony_ci writeq(lmc->parity_test, lmc->regs + LMC_ECC_PARITY_TEST); 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci readq(lmc->regs + LMC_CHAR_MASK0); 35662306a36Sopenharmony_ci readq(lmc->regs + LMC_CHAR_MASK2); 35762306a36Sopenharmony_ci readq(lmc->regs + LMC_ECC_PARITY_TEST); 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci for (i = 0; i < lines; i++) { 36062306a36Sopenharmony_ci memset((void *)addr, TEST_PATTERN, cline_size); 36162306a36Sopenharmony_ci barrier(); 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci /* 36462306a36Sopenharmony_ci * Flush L1 cachelines to the PoC (L2). 36562306a36Sopenharmony_ci * This will cause cacheline eviction to the L2. 36662306a36Sopenharmony_ci */ 36762306a36Sopenharmony_ci asm volatile("dc civac, %0\n" 36862306a36Sopenharmony_ci "dsb sy\n" 36962306a36Sopenharmony_ci : : "r"(addr + i * cline_size)); 37062306a36Sopenharmony_ci } 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci for (i = 0; i < lines; i++) { 37362306a36Sopenharmony_ci /* 37462306a36Sopenharmony_ci * Flush L2 cachelines to the DRAM. 37562306a36Sopenharmony_ci * This will cause cacheline eviction to the DRAM 37662306a36Sopenharmony_ci * and ECC corruption according to the masks set. 37762306a36Sopenharmony_ci */ 37862306a36Sopenharmony_ci __asm__ volatile("sys #0,c11,C1,#2, %0\n" 37962306a36Sopenharmony_ci : : "r"(phys + i * cline_size)); 38062306a36Sopenharmony_ci } 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci for (i = 0; i < lines; i++) { 38362306a36Sopenharmony_ci /* 38462306a36Sopenharmony_ci * Invalidate L2 cachelines. 38562306a36Sopenharmony_ci * The subsequent load will cause cacheline fetch 38662306a36Sopenharmony_ci * from the DRAM and an error interrupt 38762306a36Sopenharmony_ci */ 38862306a36Sopenharmony_ci __asm__ volatile("sys #0,c11,C1,#1, %0" 38962306a36Sopenharmony_ci : : "r"(phys + i * cline_size)); 39062306a36Sopenharmony_ci } 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci for (i = 0; i < lines; i++) { 39362306a36Sopenharmony_ci /* 39462306a36Sopenharmony_ci * Invalidate L1 cachelines. 39562306a36Sopenharmony_ci * The subsequent load will cause cacheline fetch 39662306a36Sopenharmony_ci * from the L2 and/or DRAM 39762306a36Sopenharmony_ci */ 39862306a36Sopenharmony_ci asm volatile("dc ivac, %0\n" 39962306a36Sopenharmony_ci "dsb sy\n" 40062306a36Sopenharmony_ci : : "r"(addr + i * cline_size)); 40162306a36Sopenharmony_ci } 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci return 0; 40462306a36Sopenharmony_ci} 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_cistatic ssize_t thunderx_lmc_inject_ecc_write(struct file *file, 40762306a36Sopenharmony_ci const char __user *data, 40862306a36Sopenharmony_ci size_t count, loff_t *ppos) 40962306a36Sopenharmony_ci{ 41062306a36Sopenharmony_ci struct thunderx_lmc *lmc = file->private_data; 41162306a36Sopenharmony_ci unsigned int cline_size = cache_line_size(); 41262306a36Sopenharmony_ci u8 *tmp; 41362306a36Sopenharmony_ci void __iomem *addr; 41462306a36Sopenharmony_ci unsigned int offs, timeout = 100000; 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci atomic_set(&lmc->ecc_int, 0); 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci lmc->mem = alloc_pages_node(lmc->node, GFP_KERNEL, 0); 41962306a36Sopenharmony_ci if (!lmc->mem) 42062306a36Sopenharmony_ci return -ENOMEM; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci tmp = kmalloc(cline_size, GFP_KERNEL); 42362306a36Sopenharmony_ci if (!tmp) { 42462306a36Sopenharmony_ci __free_pages(lmc->mem, 0); 42562306a36Sopenharmony_ci return -ENOMEM; 42662306a36Sopenharmony_ci } 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci addr = page_address(lmc->mem); 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci while (!atomic_read(&lmc->ecc_int) && timeout--) { 43162306a36Sopenharmony_ci stop_machine(inject_ecc_fn, lmc, NULL); 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci for (offs = 0; offs < PAGE_SIZE; offs += cline_size) { 43462306a36Sopenharmony_ci /* 43562306a36Sopenharmony_ci * Do a load from the previously rigged location 43662306a36Sopenharmony_ci * This should generate an error interrupt. 43762306a36Sopenharmony_ci */ 43862306a36Sopenharmony_ci memcpy(tmp, addr + offs, cline_size); 43962306a36Sopenharmony_ci asm volatile("dsb ld\n"); 44062306a36Sopenharmony_ci } 44162306a36Sopenharmony_ci } 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci kfree(tmp); 44462306a36Sopenharmony_ci __free_pages(lmc->mem, 0); 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci return count; 44762306a36Sopenharmony_ci} 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ciLMC_DEBUGFS_ENT(mask0); 45062306a36Sopenharmony_ciLMC_DEBUGFS_ENT(mask2); 45162306a36Sopenharmony_ciLMC_DEBUGFS_ENT(parity_test); 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ciDEBUGFS_STRUCT(inject_int, 0200, thunderx_lmc_inject_int_write, NULL); 45462306a36Sopenharmony_ciDEBUGFS_STRUCT(inject_ecc, 0200, thunderx_lmc_inject_ecc_write, NULL); 45562306a36Sopenharmony_ciDEBUGFS_STRUCT(int_w1c, 0400, NULL, thunderx_lmc_int_read); 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_cistatic struct debugfs_entry *lmc_dfs_ents[] = { 45862306a36Sopenharmony_ci &debugfs_mask0, 45962306a36Sopenharmony_ci &debugfs_mask2, 46062306a36Sopenharmony_ci &debugfs_parity_test, 46162306a36Sopenharmony_ci &debugfs_inject_ecc, 46262306a36Sopenharmony_ci &debugfs_inject_int, 46362306a36Sopenharmony_ci &debugfs_int_w1c, 46462306a36Sopenharmony_ci}; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_cistatic int thunderx_create_debugfs_nodes(struct dentry *parent, 46762306a36Sopenharmony_ci struct debugfs_entry *attrs[], 46862306a36Sopenharmony_ci void *data, 46962306a36Sopenharmony_ci size_t num) 47062306a36Sopenharmony_ci{ 47162306a36Sopenharmony_ci int i; 47262306a36Sopenharmony_ci struct dentry *ent; 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci if (!IS_ENABLED(CONFIG_EDAC_DEBUG)) 47562306a36Sopenharmony_ci return 0; 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci if (!parent) 47862306a36Sopenharmony_ci return -ENOENT; 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci for (i = 0; i < num; i++) { 48162306a36Sopenharmony_ci ent = edac_debugfs_create_file(attrs[i]->name, attrs[i]->mode, 48262306a36Sopenharmony_ci parent, data, &attrs[i]->fops); 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci if (IS_ERR(ent)) 48562306a36Sopenharmony_ci break; 48662306a36Sopenharmony_ci } 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci return i; 48962306a36Sopenharmony_ci} 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_cistatic phys_addr_t thunderx_faddr_to_phys(u64 faddr, struct thunderx_lmc *lmc) 49262306a36Sopenharmony_ci{ 49362306a36Sopenharmony_ci phys_addr_t addr = 0; 49462306a36Sopenharmony_ci int bank, xbits; 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci addr |= lmc->node << 40; 49762306a36Sopenharmony_ci addr |= LMC_FADR_FDIMM(faddr) << lmc->dimm_lsb; 49862306a36Sopenharmony_ci addr |= LMC_FADR_FBUNK(faddr) << lmc->rank_lsb; 49962306a36Sopenharmony_ci addr |= LMC_FADR_FROW(faddr) << lmc->row_lsb; 50062306a36Sopenharmony_ci addr |= (LMC_FADR_FCOL(faddr) >> 4) << lmc->col_hi_lsb; 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci bank = LMC_FADR_FBANK(faddr) << lmc->bank_lsb; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci if (lmc->xor_bank) 50562306a36Sopenharmony_ci bank ^= get_bits(addr, 12 + lmc->xbits, lmc->bank_width); 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci addr |= bank << lmc->bank_lsb; 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci xbits = PCI_FUNC(lmc->pdev->devfn); 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci if (lmc->l2c_alias) 51262306a36Sopenharmony_ci xbits ^= get_bits(addr, 20, lmc->xbits) ^ 51362306a36Sopenharmony_ci get_bits(addr, 12, lmc->xbits); 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci addr |= xbits << 7; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci return addr; 51862306a36Sopenharmony_ci} 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_cistatic unsigned int thunderx_get_num_lmcs(unsigned int node) 52162306a36Sopenharmony_ci{ 52262306a36Sopenharmony_ci unsigned int number = 0; 52362306a36Sopenharmony_ci struct pci_dev *pdev = NULL; 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci do { 52662306a36Sopenharmony_ci pdev = pci_get_device(PCI_VENDOR_ID_CAVIUM, 52762306a36Sopenharmony_ci PCI_DEVICE_ID_THUNDER_LMC, 52862306a36Sopenharmony_ci pdev); 52962306a36Sopenharmony_ci if (pdev) { 53062306a36Sopenharmony_ci#ifdef CONFIG_NUMA 53162306a36Sopenharmony_ci if (pdev->dev.numa_node == node) 53262306a36Sopenharmony_ci number++; 53362306a36Sopenharmony_ci#else 53462306a36Sopenharmony_ci number++; 53562306a36Sopenharmony_ci#endif 53662306a36Sopenharmony_ci } 53762306a36Sopenharmony_ci } while (pdev); 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci return number; 54062306a36Sopenharmony_ci} 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci#define LMC_MESSAGE_SIZE 120 54362306a36Sopenharmony_ci#define LMC_OTHER_SIZE (50 * ARRAY_SIZE(lmc_errors)) 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_cistatic irqreturn_t thunderx_lmc_err_isr(int irq, void *dev_id) 54662306a36Sopenharmony_ci{ 54762306a36Sopenharmony_ci struct mem_ctl_info *mci = dev_id; 54862306a36Sopenharmony_ci struct thunderx_lmc *lmc = mci->pvt_info; 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci unsigned long head = ring_pos(lmc->ring_head, ARRAY_SIZE(lmc->err_ctx)); 55162306a36Sopenharmony_ci struct lmc_err_ctx *ctx = &lmc->err_ctx[head]; 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci writeq(0, lmc->regs + LMC_CHAR_MASK0); 55462306a36Sopenharmony_ci writeq(0, lmc->regs + LMC_CHAR_MASK2); 55562306a36Sopenharmony_ci writeq(0x2, lmc->regs + LMC_ECC_PARITY_TEST); 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci ctx->reg_int = readq(lmc->regs + LMC_INT); 55862306a36Sopenharmony_ci ctx->reg_fadr = readq(lmc->regs + LMC_FADR); 55962306a36Sopenharmony_ci ctx->reg_nxm_fadr = readq(lmc->regs + LMC_NXM_FADR); 56062306a36Sopenharmony_ci ctx->reg_scram_fadr = readq(lmc->regs + LMC_SCRAM_FADR); 56162306a36Sopenharmony_ci ctx->reg_ecc_synd = readq(lmc->regs + LMC_ECC_SYND); 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci lmc->ring_head++; 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci atomic_set(&lmc->ecc_int, 1); 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci /* Clear the interrupt */ 56862306a36Sopenharmony_ci writeq(ctx->reg_int, lmc->regs + LMC_INT); 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci return IRQ_WAKE_THREAD; 57162306a36Sopenharmony_ci} 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_cistatic irqreturn_t thunderx_lmc_threaded_isr(int irq, void *dev_id) 57462306a36Sopenharmony_ci{ 57562306a36Sopenharmony_ci struct mem_ctl_info *mci = dev_id; 57662306a36Sopenharmony_ci struct thunderx_lmc *lmc = mci->pvt_info; 57762306a36Sopenharmony_ci phys_addr_t phys_addr; 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci unsigned long tail; 58062306a36Sopenharmony_ci struct lmc_err_ctx *ctx; 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci irqreturn_t ret = IRQ_NONE; 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci char *msg; 58562306a36Sopenharmony_ci char *other; 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci msg = kmalloc(LMC_MESSAGE_SIZE, GFP_KERNEL); 58862306a36Sopenharmony_ci other = kmalloc(LMC_OTHER_SIZE, GFP_KERNEL); 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci if (!msg || !other) 59162306a36Sopenharmony_ci goto err_free; 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci while (CIRC_CNT(lmc->ring_head, lmc->ring_tail, 59462306a36Sopenharmony_ci ARRAY_SIZE(lmc->err_ctx))) { 59562306a36Sopenharmony_ci tail = ring_pos(lmc->ring_tail, ARRAY_SIZE(lmc->err_ctx)); 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci ctx = &lmc->err_ctx[tail]; 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci dev_dbg(&lmc->pdev->dev, "LMC_INT: %016llx\n", 60062306a36Sopenharmony_ci ctx->reg_int); 60162306a36Sopenharmony_ci dev_dbg(&lmc->pdev->dev, "LMC_FADR: %016llx\n", 60262306a36Sopenharmony_ci ctx->reg_fadr); 60362306a36Sopenharmony_ci dev_dbg(&lmc->pdev->dev, "LMC_NXM_FADR: %016llx\n", 60462306a36Sopenharmony_ci ctx->reg_nxm_fadr); 60562306a36Sopenharmony_ci dev_dbg(&lmc->pdev->dev, "LMC_SCRAM_FADR: %016llx\n", 60662306a36Sopenharmony_ci ctx->reg_scram_fadr); 60762306a36Sopenharmony_ci dev_dbg(&lmc->pdev->dev, "LMC_ECC_SYND: %016llx\n", 60862306a36Sopenharmony_ci ctx->reg_ecc_synd); 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci snprintf(msg, LMC_MESSAGE_SIZE, 61162306a36Sopenharmony_ci "DIMM %lld rank %lld bank %lld row %lld col %lld", 61262306a36Sopenharmony_ci LMC_FADR_FDIMM(ctx->reg_scram_fadr), 61362306a36Sopenharmony_ci LMC_FADR_FBUNK(ctx->reg_scram_fadr), 61462306a36Sopenharmony_ci LMC_FADR_FBANK(ctx->reg_scram_fadr), 61562306a36Sopenharmony_ci LMC_FADR_FROW(ctx->reg_scram_fadr), 61662306a36Sopenharmony_ci LMC_FADR_FCOL(ctx->reg_scram_fadr)); 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci decode_register(other, LMC_OTHER_SIZE, lmc_errors, 61962306a36Sopenharmony_ci ctx->reg_int); 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci phys_addr = thunderx_faddr_to_phys(ctx->reg_fadr, lmc); 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci if (ctx->reg_int & LMC_INT_UE) 62462306a36Sopenharmony_ci edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, 62562306a36Sopenharmony_ci phys_to_pfn(phys_addr), 62662306a36Sopenharmony_ci offset_in_page(phys_addr), 62762306a36Sopenharmony_ci 0, -1, -1, -1, msg, other); 62862306a36Sopenharmony_ci else if (ctx->reg_int & LMC_INT_CE) 62962306a36Sopenharmony_ci edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1, 63062306a36Sopenharmony_ci phys_to_pfn(phys_addr), 63162306a36Sopenharmony_ci offset_in_page(phys_addr), 63262306a36Sopenharmony_ci 0, -1, -1, -1, msg, other); 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci lmc->ring_tail++; 63562306a36Sopenharmony_ci } 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci ret = IRQ_HANDLED; 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_cierr_free: 64062306a36Sopenharmony_ci kfree(msg); 64162306a36Sopenharmony_ci kfree(other); 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci return ret; 64462306a36Sopenharmony_ci} 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_cistatic const struct pci_device_id thunderx_lmc_pci_tbl[] = { 64762306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, PCI_DEVICE_ID_THUNDER_LMC) }, 64862306a36Sopenharmony_ci { 0, }, 64962306a36Sopenharmony_ci}; 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_cistatic inline int pci_dev_to_mc_idx(struct pci_dev *pdev) 65262306a36Sopenharmony_ci{ 65362306a36Sopenharmony_ci int node = dev_to_node(&pdev->dev); 65462306a36Sopenharmony_ci int ret = PCI_FUNC(pdev->devfn); 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci ret += max(node, 0) << 3; 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci return ret; 65962306a36Sopenharmony_ci} 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_cistatic int thunderx_lmc_probe(struct pci_dev *pdev, 66262306a36Sopenharmony_ci const struct pci_device_id *id) 66362306a36Sopenharmony_ci{ 66462306a36Sopenharmony_ci struct thunderx_lmc *lmc; 66562306a36Sopenharmony_ci struct edac_mc_layer layer; 66662306a36Sopenharmony_ci struct mem_ctl_info *mci; 66762306a36Sopenharmony_ci u64 lmc_control, lmc_ddr_pll_ctl, lmc_config; 66862306a36Sopenharmony_ci int ret; 66962306a36Sopenharmony_ci u64 lmc_int; 67062306a36Sopenharmony_ci void *l2c_ioaddr; 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci layer.type = EDAC_MC_LAYER_SLOT; 67362306a36Sopenharmony_ci layer.size = 2; 67462306a36Sopenharmony_ci layer.is_virt_csrow = false; 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci ret = pcim_enable_device(pdev); 67762306a36Sopenharmony_ci if (ret) { 67862306a36Sopenharmony_ci dev_err(&pdev->dev, "Cannot enable PCI device: %d\n", ret); 67962306a36Sopenharmony_ci return ret; 68062306a36Sopenharmony_ci } 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci ret = pcim_iomap_regions(pdev, BIT(0), "thunderx_lmc"); 68362306a36Sopenharmony_ci if (ret) { 68462306a36Sopenharmony_ci dev_err(&pdev->dev, "Cannot map PCI resources: %d\n", ret); 68562306a36Sopenharmony_ci return ret; 68662306a36Sopenharmony_ci } 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci mci = edac_mc_alloc(pci_dev_to_mc_idx(pdev), 1, &layer, 68962306a36Sopenharmony_ci sizeof(struct thunderx_lmc)); 69062306a36Sopenharmony_ci if (!mci) 69162306a36Sopenharmony_ci return -ENOMEM; 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci mci->pdev = &pdev->dev; 69462306a36Sopenharmony_ci lmc = mci->pvt_info; 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci pci_set_drvdata(pdev, mci); 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci lmc->regs = pcim_iomap_table(pdev)[0]; 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci lmc_control = readq(lmc->regs + LMC_CONTROL); 70162306a36Sopenharmony_ci lmc_ddr_pll_ctl = readq(lmc->regs + LMC_DDR_PLL_CTL); 70262306a36Sopenharmony_ci lmc_config = readq(lmc->regs + LMC_CONFIG); 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci if (lmc_control & LMC_CONTROL_RDIMM) { 70562306a36Sopenharmony_ci mci->mtype_cap = FIELD_GET(LMC_DDR_PLL_CTL_DDR4, 70662306a36Sopenharmony_ci lmc_ddr_pll_ctl) ? 70762306a36Sopenharmony_ci MEM_RDDR4 : MEM_RDDR3; 70862306a36Sopenharmony_ci } else { 70962306a36Sopenharmony_ci mci->mtype_cap = FIELD_GET(LMC_DDR_PLL_CTL_DDR4, 71062306a36Sopenharmony_ci lmc_ddr_pll_ctl) ? 71162306a36Sopenharmony_ci MEM_DDR4 : MEM_DDR3; 71262306a36Sopenharmony_ci } 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED; 71562306a36Sopenharmony_ci mci->edac_cap = EDAC_FLAG_SECDED; 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci mci->mod_name = "thunderx-lmc"; 71862306a36Sopenharmony_ci mci->ctl_name = "thunderx-lmc"; 71962306a36Sopenharmony_ci mci->dev_name = dev_name(&pdev->dev); 72062306a36Sopenharmony_ci mci->scrub_mode = SCRUB_NONE; 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci lmc->pdev = pdev; 72362306a36Sopenharmony_ci lmc->msix_ent.entry = 0; 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci lmc->ring_head = 0; 72662306a36Sopenharmony_ci lmc->ring_tail = 0; 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci ret = pci_enable_msix_exact(pdev, &lmc->msix_ent, 1); 72962306a36Sopenharmony_ci if (ret) { 73062306a36Sopenharmony_ci dev_err(&pdev->dev, "Cannot enable interrupt: %d\n", ret); 73162306a36Sopenharmony_ci goto err_free; 73262306a36Sopenharmony_ci } 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci ret = devm_request_threaded_irq(&pdev->dev, lmc->msix_ent.vector, 73562306a36Sopenharmony_ci thunderx_lmc_err_isr, 73662306a36Sopenharmony_ci thunderx_lmc_threaded_isr, 0, 73762306a36Sopenharmony_ci "[EDAC] ThunderX LMC", mci); 73862306a36Sopenharmony_ci if (ret) { 73962306a36Sopenharmony_ci dev_err(&pdev->dev, "Cannot set ISR: %d\n", ret); 74062306a36Sopenharmony_ci goto err_free; 74162306a36Sopenharmony_ci } 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci lmc->node = FIELD_GET(THUNDERX_NODE, pci_resource_start(pdev, 0)); 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci lmc->xbits = thunderx_get_num_lmcs(lmc->node) >> 1; 74662306a36Sopenharmony_ci lmc->bank_width = (FIELD_GET(LMC_DDR_PLL_CTL_DDR4, lmc_ddr_pll_ctl) && 74762306a36Sopenharmony_ci FIELD_GET(LMC_CONFIG_BG2, lmc_config)) ? 4 : 3; 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci lmc->pbank_lsb = (lmc_config >> 5) & 0xf; 75062306a36Sopenharmony_ci lmc->dimm_lsb = 28 + lmc->pbank_lsb + lmc->xbits; 75162306a36Sopenharmony_ci lmc->rank_lsb = lmc->dimm_lsb; 75262306a36Sopenharmony_ci lmc->rank_lsb -= FIELD_GET(LMC_CONFIG_RANK_ENA, lmc_config) ? 1 : 0; 75362306a36Sopenharmony_ci lmc->bank_lsb = 7 + lmc->xbits; 75462306a36Sopenharmony_ci lmc->row_lsb = 14 + LMC_CONFIG_ROW_LSB(lmc_config) + lmc->xbits; 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci lmc->col_hi_lsb = lmc->bank_lsb + lmc->bank_width; 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci lmc->xor_bank = lmc_control & LMC_CONTROL_XOR_BANK; 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ci l2c_ioaddr = ioremap(L2C_CTL | FIELD_PREP(THUNDERX_NODE, lmc->node), PAGE_SIZE); 76162306a36Sopenharmony_ci if (!l2c_ioaddr) { 76262306a36Sopenharmony_ci dev_err(&pdev->dev, "Cannot map L2C_CTL\n"); 76362306a36Sopenharmony_ci ret = -ENOMEM; 76462306a36Sopenharmony_ci goto err_free; 76562306a36Sopenharmony_ci } 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci lmc->l2c_alias = !(readq(l2c_ioaddr) & L2C_CTL_DISIDXALIAS); 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci iounmap(l2c_ioaddr); 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci ret = edac_mc_add_mc(mci); 77262306a36Sopenharmony_ci if (ret) { 77362306a36Sopenharmony_ci dev_err(&pdev->dev, "Cannot add the MC: %d\n", ret); 77462306a36Sopenharmony_ci goto err_free; 77562306a36Sopenharmony_ci } 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci lmc_int = readq(lmc->regs + LMC_INT); 77862306a36Sopenharmony_ci writeq(lmc_int, lmc->regs + LMC_INT); 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci writeq(LMC_INT_ENA_ALL, lmc->regs + LMC_INT_ENA_W1S); 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_EDAC_DEBUG)) { 78362306a36Sopenharmony_ci ret = thunderx_create_debugfs_nodes(mci->debugfs, 78462306a36Sopenharmony_ci lmc_dfs_ents, 78562306a36Sopenharmony_ci lmc, 78662306a36Sopenharmony_ci ARRAY_SIZE(lmc_dfs_ents)); 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci if (ret != ARRAY_SIZE(lmc_dfs_ents)) { 78962306a36Sopenharmony_ci dev_warn(&pdev->dev, "Error creating debugfs entries: %d%s\n", 79062306a36Sopenharmony_ci ret, ret >= 0 ? " created" : ""); 79162306a36Sopenharmony_ci } 79262306a36Sopenharmony_ci } 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci return 0; 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_cierr_free: 79762306a36Sopenharmony_ci pci_set_drvdata(pdev, NULL); 79862306a36Sopenharmony_ci edac_mc_free(mci); 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci return ret; 80162306a36Sopenharmony_ci} 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_cistatic void thunderx_lmc_remove(struct pci_dev *pdev) 80462306a36Sopenharmony_ci{ 80562306a36Sopenharmony_ci struct mem_ctl_info *mci = pci_get_drvdata(pdev); 80662306a36Sopenharmony_ci struct thunderx_lmc *lmc = mci->pvt_info; 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci writeq(LMC_INT_ENA_ALL, lmc->regs + LMC_INT_ENA_W1C); 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci edac_mc_del_mc(&pdev->dev); 81162306a36Sopenharmony_ci edac_mc_free(mci); 81262306a36Sopenharmony_ci} 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, thunderx_lmc_pci_tbl); 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_cistatic struct pci_driver thunderx_lmc_driver = { 81762306a36Sopenharmony_ci .name = "thunderx_lmc_edac", 81862306a36Sopenharmony_ci .probe = thunderx_lmc_probe, 81962306a36Sopenharmony_ci .remove = thunderx_lmc_remove, 82062306a36Sopenharmony_ci .id_table = thunderx_lmc_pci_tbl, 82162306a36Sopenharmony_ci}; 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci/*---------------------- OCX driver ---------------------------------*/ 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci#define PCI_DEVICE_ID_THUNDER_OCX 0xa013 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci#define OCX_LINK_INTS 3 82862306a36Sopenharmony_ci#define OCX_INTS (OCX_LINK_INTS + 1) 82962306a36Sopenharmony_ci#define OCX_RX_LANES 24 83062306a36Sopenharmony_ci#define OCX_RX_LANE_STATS 15 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci#define OCX_COM_INT 0x100 83362306a36Sopenharmony_ci#define OCX_COM_INT_W1S 0x108 83462306a36Sopenharmony_ci#define OCX_COM_INT_ENA_W1S 0x110 83562306a36Sopenharmony_ci#define OCX_COM_INT_ENA_W1C 0x118 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci#define OCX_COM_IO_BADID BIT(54) 83862306a36Sopenharmony_ci#define OCX_COM_MEM_BADID BIT(53) 83962306a36Sopenharmony_ci#define OCX_COM_COPR_BADID BIT(52) 84062306a36Sopenharmony_ci#define OCX_COM_WIN_REQ_BADID BIT(51) 84162306a36Sopenharmony_ci#define OCX_COM_WIN_REQ_TOUT BIT(50) 84262306a36Sopenharmony_ci#define OCX_COM_RX_LANE GENMASK(23, 0) 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci#define OCX_COM_INT_CE (OCX_COM_IO_BADID | \ 84562306a36Sopenharmony_ci OCX_COM_MEM_BADID | \ 84662306a36Sopenharmony_ci OCX_COM_COPR_BADID | \ 84762306a36Sopenharmony_ci OCX_COM_WIN_REQ_BADID | \ 84862306a36Sopenharmony_ci OCX_COM_WIN_REQ_TOUT) 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_cistatic const struct error_descr ocx_com_errors[] = { 85162306a36Sopenharmony_ci { 85262306a36Sopenharmony_ci .type = ERR_CORRECTED, 85362306a36Sopenharmony_ci .mask = OCX_COM_IO_BADID, 85462306a36Sopenharmony_ci .descr = "Invalid IO transaction node ID", 85562306a36Sopenharmony_ci }, 85662306a36Sopenharmony_ci { 85762306a36Sopenharmony_ci .type = ERR_CORRECTED, 85862306a36Sopenharmony_ci .mask = OCX_COM_MEM_BADID, 85962306a36Sopenharmony_ci .descr = "Invalid memory transaction node ID", 86062306a36Sopenharmony_ci }, 86162306a36Sopenharmony_ci { 86262306a36Sopenharmony_ci .type = ERR_CORRECTED, 86362306a36Sopenharmony_ci .mask = OCX_COM_COPR_BADID, 86462306a36Sopenharmony_ci .descr = "Invalid coprocessor transaction node ID", 86562306a36Sopenharmony_ci }, 86662306a36Sopenharmony_ci { 86762306a36Sopenharmony_ci .type = ERR_CORRECTED, 86862306a36Sopenharmony_ci .mask = OCX_COM_WIN_REQ_BADID, 86962306a36Sopenharmony_ci .descr = "Invalid SLI transaction node ID", 87062306a36Sopenharmony_ci }, 87162306a36Sopenharmony_ci { 87262306a36Sopenharmony_ci .type = ERR_CORRECTED, 87362306a36Sopenharmony_ci .mask = OCX_COM_WIN_REQ_TOUT, 87462306a36Sopenharmony_ci .descr = "Window/core request timeout", 87562306a36Sopenharmony_ci }, 87662306a36Sopenharmony_ci {0, 0, NULL}, 87762306a36Sopenharmony_ci}; 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_ci#define OCX_COM_LINKX_INT(x) (0x120 + (x) * 8) 88062306a36Sopenharmony_ci#define OCX_COM_LINKX_INT_W1S(x) (0x140 + (x) * 8) 88162306a36Sopenharmony_ci#define OCX_COM_LINKX_INT_ENA_W1S(x) (0x160 + (x) * 8) 88262306a36Sopenharmony_ci#define OCX_COM_LINKX_INT_ENA_W1C(x) (0x180 + (x) * 8) 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci#define OCX_COM_LINK_BAD_WORD BIT(13) 88562306a36Sopenharmony_ci#define OCX_COM_LINK_ALIGN_FAIL BIT(12) 88662306a36Sopenharmony_ci#define OCX_COM_LINK_ALIGN_DONE BIT(11) 88762306a36Sopenharmony_ci#define OCX_COM_LINK_UP BIT(10) 88862306a36Sopenharmony_ci#define OCX_COM_LINK_STOP BIT(9) 88962306a36Sopenharmony_ci#define OCX_COM_LINK_BLK_ERR BIT(8) 89062306a36Sopenharmony_ci#define OCX_COM_LINK_REINIT BIT(7) 89162306a36Sopenharmony_ci#define OCX_COM_LINK_LNK_DATA BIT(6) 89262306a36Sopenharmony_ci#define OCX_COM_LINK_RXFIFO_DBE BIT(5) 89362306a36Sopenharmony_ci#define OCX_COM_LINK_RXFIFO_SBE BIT(4) 89462306a36Sopenharmony_ci#define OCX_COM_LINK_TXFIFO_DBE BIT(3) 89562306a36Sopenharmony_ci#define OCX_COM_LINK_TXFIFO_SBE BIT(2) 89662306a36Sopenharmony_ci#define OCX_COM_LINK_REPLAY_DBE BIT(1) 89762306a36Sopenharmony_ci#define OCX_COM_LINK_REPLAY_SBE BIT(0) 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_cistatic const struct error_descr ocx_com_link_errors[] = { 90062306a36Sopenharmony_ci { 90162306a36Sopenharmony_ci .type = ERR_CORRECTED, 90262306a36Sopenharmony_ci .mask = OCX_COM_LINK_REPLAY_SBE, 90362306a36Sopenharmony_ci .descr = "Replay buffer single-bit error", 90462306a36Sopenharmony_ci }, 90562306a36Sopenharmony_ci { 90662306a36Sopenharmony_ci .type = ERR_CORRECTED, 90762306a36Sopenharmony_ci .mask = OCX_COM_LINK_TXFIFO_SBE, 90862306a36Sopenharmony_ci .descr = "TX FIFO single-bit error", 90962306a36Sopenharmony_ci }, 91062306a36Sopenharmony_ci { 91162306a36Sopenharmony_ci .type = ERR_CORRECTED, 91262306a36Sopenharmony_ci .mask = OCX_COM_LINK_RXFIFO_SBE, 91362306a36Sopenharmony_ci .descr = "RX FIFO single-bit error", 91462306a36Sopenharmony_ci }, 91562306a36Sopenharmony_ci { 91662306a36Sopenharmony_ci .type = ERR_CORRECTED, 91762306a36Sopenharmony_ci .mask = OCX_COM_LINK_BLK_ERR, 91862306a36Sopenharmony_ci .descr = "Block code error", 91962306a36Sopenharmony_ci }, 92062306a36Sopenharmony_ci { 92162306a36Sopenharmony_ci .type = ERR_CORRECTED, 92262306a36Sopenharmony_ci .mask = OCX_COM_LINK_ALIGN_FAIL, 92362306a36Sopenharmony_ci .descr = "Link alignment failure", 92462306a36Sopenharmony_ci }, 92562306a36Sopenharmony_ci { 92662306a36Sopenharmony_ci .type = ERR_CORRECTED, 92762306a36Sopenharmony_ci .mask = OCX_COM_LINK_BAD_WORD, 92862306a36Sopenharmony_ci .descr = "Bad code word", 92962306a36Sopenharmony_ci }, 93062306a36Sopenharmony_ci { 93162306a36Sopenharmony_ci .type = ERR_UNCORRECTED, 93262306a36Sopenharmony_ci .mask = OCX_COM_LINK_REPLAY_DBE, 93362306a36Sopenharmony_ci .descr = "Replay buffer double-bit error", 93462306a36Sopenharmony_ci }, 93562306a36Sopenharmony_ci { 93662306a36Sopenharmony_ci .type = ERR_UNCORRECTED, 93762306a36Sopenharmony_ci .mask = OCX_COM_LINK_TXFIFO_DBE, 93862306a36Sopenharmony_ci .descr = "TX FIFO double-bit error", 93962306a36Sopenharmony_ci }, 94062306a36Sopenharmony_ci { 94162306a36Sopenharmony_ci .type = ERR_UNCORRECTED, 94262306a36Sopenharmony_ci .mask = OCX_COM_LINK_RXFIFO_DBE, 94362306a36Sopenharmony_ci .descr = "RX FIFO double-bit error", 94462306a36Sopenharmony_ci }, 94562306a36Sopenharmony_ci { 94662306a36Sopenharmony_ci .type = ERR_UNCORRECTED, 94762306a36Sopenharmony_ci .mask = OCX_COM_LINK_STOP, 94862306a36Sopenharmony_ci .descr = "Link stopped", 94962306a36Sopenharmony_ci }, 95062306a36Sopenharmony_ci {0, 0, NULL}, 95162306a36Sopenharmony_ci}; 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_ci#define OCX_COM_LINK_INT_UE (OCX_COM_LINK_REPLAY_DBE | \ 95462306a36Sopenharmony_ci OCX_COM_LINK_TXFIFO_DBE | \ 95562306a36Sopenharmony_ci OCX_COM_LINK_RXFIFO_DBE | \ 95662306a36Sopenharmony_ci OCX_COM_LINK_STOP) 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_ci#define OCX_COM_LINK_INT_CE (OCX_COM_LINK_REPLAY_SBE | \ 95962306a36Sopenharmony_ci OCX_COM_LINK_TXFIFO_SBE | \ 96062306a36Sopenharmony_ci OCX_COM_LINK_RXFIFO_SBE | \ 96162306a36Sopenharmony_ci OCX_COM_LINK_BLK_ERR | \ 96262306a36Sopenharmony_ci OCX_COM_LINK_ALIGN_FAIL | \ 96362306a36Sopenharmony_ci OCX_COM_LINK_BAD_WORD) 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_ci#define OCX_LNE_INT(x) (0x8018 + (x) * 0x100) 96662306a36Sopenharmony_ci#define OCX_LNE_INT_EN(x) (0x8020 + (x) * 0x100) 96762306a36Sopenharmony_ci#define OCX_LNE_BAD_CNT(x) (0x8028 + (x) * 0x100) 96862306a36Sopenharmony_ci#define OCX_LNE_CFG(x) (0x8000 + (x) * 0x100) 96962306a36Sopenharmony_ci#define OCX_LNE_STAT(x, y) (0x8040 + (x) * 0x100 + (y) * 8) 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci#define OCX_LNE_CFG_RX_BDRY_LOCK_DIS BIT(8) 97262306a36Sopenharmony_ci#define OCX_LNE_CFG_RX_STAT_WRAP_DIS BIT(2) 97362306a36Sopenharmony_ci#define OCX_LNE_CFG_RX_STAT_RDCLR BIT(1) 97462306a36Sopenharmony_ci#define OCX_LNE_CFG_RX_STAT_ENA BIT(0) 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_ci#define OCX_LANE_BAD_64B67B BIT(8) 97862306a36Sopenharmony_ci#define OCX_LANE_DSKEW_FIFO_OVFL BIT(5) 97962306a36Sopenharmony_ci#define OCX_LANE_SCRM_SYNC_LOSS BIT(4) 98062306a36Sopenharmony_ci#define OCX_LANE_UKWN_CNTL_WORD BIT(3) 98162306a36Sopenharmony_ci#define OCX_LANE_CRC32_ERR BIT(2) 98262306a36Sopenharmony_ci#define OCX_LANE_BDRY_SYNC_LOSS BIT(1) 98362306a36Sopenharmony_ci#define OCX_LANE_SERDES_LOCK_LOSS BIT(0) 98462306a36Sopenharmony_ci 98562306a36Sopenharmony_ci#define OCX_COM_LANE_INT_UE (0) 98662306a36Sopenharmony_ci#define OCX_COM_LANE_INT_CE (OCX_LANE_SERDES_LOCK_LOSS | \ 98762306a36Sopenharmony_ci OCX_LANE_BDRY_SYNC_LOSS | \ 98862306a36Sopenharmony_ci OCX_LANE_CRC32_ERR | \ 98962306a36Sopenharmony_ci OCX_LANE_UKWN_CNTL_WORD | \ 99062306a36Sopenharmony_ci OCX_LANE_SCRM_SYNC_LOSS | \ 99162306a36Sopenharmony_ci OCX_LANE_DSKEW_FIFO_OVFL | \ 99262306a36Sopenharmony_ci OCX_LANE_BAD_64B67B) 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_cistatic const struct error_descr ocx_lane_errors[] = { 99562306a36Sopenharmony_ci { 99662306a36Sopenharmony_ci .type = ERR_CORRECTED, 99762306a36Sopenharmony_ci .mask = OCX_LANE_SERDES_LOCK_LOSS, 99862306a36Sopenharmony_ci .descr = "RX SerDes lock lost", 99962306a36Sopenharmony_ci }, 100062306a36Sopenharmony_ci { 100162306a36Sopenharmony_ci .type = ERR_CORRECTED, 100262306a36Sopenharmony_ci .mask = OCX_LANE_BDRY_SYNC_LOSS, 100362306a36Sopenharmony_ci .descr = "RX word boundary lost", 100462306a36Sopenharmony_ci }, 100562306a36Sopenharmony_ci { 100662306a36Sopenharmony_ci .type = ERR_CORRECTED, 100762306a36Sopenharmony_ci .mask = OCX_LANE_CRC32_ERR, 100862306a36Sopenharmony_ci .descr = "CRC32 error", 100962306a36Sopenharmony_ci }, 101062306a36Sopenharmony_ci { 101162306a36Sopenharmony_ci .type = ERR_CORRECTED, 101262306a36Sopenharmony_ci .mask = OCX_LANE_UKWN_CNTL_WORD, 101362306a36Sopenharmony_ci .descr = "Unknown control word", 101462306a36Sopenharmony_ci }, 101562306a36Sopenharmony_ci { 101662306a36Sopenharmony_ci .type = ERR_CORRECTED, 101762306a36Sopenharmony_ci .mask = OCX_LANE_SCRM_SYNC_LOSS, 101862306a36Sopenharmony_ci .descr = "Scrambler synchronization lost", 101962306a36Sopenharmony_ci }, 102062306a36Sopenharmony_ci { 102162306a36Sopenharmony_ci .type = ERR_CORRECTED, 102262306a36Sopenharmony_ci .mask = OCX_LANE_DSKEW_FIFO_OVFL, 102362306a36Sopenharmony_ci .descr = "RX deskew FIFO overflow", 102462306a36Sopenharmony_ci }, 102562306a36Sopenharmony_ci { 102662306a36Sopenharmony_ci .type = ERR_CORRECTED, 102762306a36Sopenharmony_ci .mask = OCX_LANE_BAD_64B67B, 102862306a36Sopenharmony_ci .descr = "Bad 64B/67B codeword", 102962306a36Sopenharmony_ci }, 103062306a36Sopenharmony_ci {0, 0, NULL}, 103162306a36Sopenharmony_ci}; 103262306a36Sopenharmony_ci 103362306a36Sopenharmony_ci#define OCX_LNE_INT_ENA_ALL (GENMASK(9, 8) | GENMASK(6, 0)) 103462306a36Sopenharmony_ci#define OCX_COM_INT_ENA_ALL (GENMASK(54, 50) | GENMASK(23, 0)) 103562306a36Sopenharmony_ci#define OCX_COM_LINKX_INT_ENA_ALL (GENMASK(13, 12) | \ 103662306a36Sopenharmony_ci GENMASK(9, 7) | GENMASK(5, 0)) 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_ci#define OCX_TLKX_ECC_CTL(x) (0x10018 + (x) * 0x2000) 103962306a36Sopenharmony_ci#define OCX_RLKX_ECC_CTL(x) (0x18018 + (x) * 0x2000) 104062306a36Sopenharmony_ci 104162306a36Sopenharmony_cistruct ocx_com_err_ctx { 104262306a36Sopenharmony_ci u64 reg_com_int; 104362306a36Sopenharmony_ci u64 reg_lane_int[OCX_RX_LANES]; 104462306a36Sopenharmony_ci u64 reg_lane_stat11[OCX_RX_LANES]; 104562306a36Sopenharmony_ci}; 104662306a36Sopenharmony_ci 104762306a36Sopenharmony_cistruct ocx_link_err_ctx { 104862306a36Sopenharmony_ci u64 reg_com_link_int; 104962306a36Sopenharmony_ci int link; 105062306a36Sopenharmony_ci}; 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_cistruct thunderx_ocx { 105362306a36Sopenharmony_ci void __iomem *regs; 105462306a36Sopenharmony_ci int com_link; 105562306a36Sopenharmony_ci struct pci_dev *pdev; 105662306a36Sopenharmony_ci struct edac_device_ctl_info *edac_dev; 105762306a36Sopenharmony_ci 105862306a36Sopenharmony_ci struct dentry *debugfs; 105962306a36Sopenharmony_ci struct msix_entry msix_ent[OCX_INTS]; 106062306a36Sopenharmony_ci 106162306a36Sopenharmony_ci struct ocx_com_err_ctx com_err_ctx[RING_ENTRIES]; 106262306a36Sopenharmony_ci struct ocx_link_err_ctx link_err_ctx[RING_ENTRIES]; 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_ci unsigned long com_ring_head; 106562306a36Sopenharmony_ci unsigned long com_ring_tail; 106662306a36Sopenharmony_ci 106762306a36Sopenharmony_ci unsigned long link_ring_head; 106862306a36Sopenharmony_ci unsigned long link_ring_tail; 106962306a36Sopenharmony_ci}; 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_ci#define OCX_MESSAGE_SIZE SZ_1K 107262306a36Sopenharmony_ci#define OCX_OTHER_SIZE (50 * ARRAY_SIZE(ocx_com_link_errors)) 107362306a36Sopenharmony_ci 107462306a36Sopenharmony_ci/* This handler is threaded */ 107562306a36Sopenharmony_cistatic irqreturn_t thunderx_ocx_com_isr(int irq, void *irq_id) 107662306a36Sopenharmony_ci{ 107762306a36Sopenharmony_ci struct msix_entry *msix = irq_id; 107862306a36Sopenharmony_ci struct thunderx_ocx *ocx = container_of(msix, struct thunderx_ocx, 107962306a36Sopenharmony_ci msix_ent[msix->entry]); 108062306a36Sopenharmony_ci 108162306a36Sopenharmony_ci int lane; 108262306a36Sopenharmony_ci unsigned long head = ring_pos(ocx->com_ring_head, 108362306a36Sopenharmony_ci ARRAY_SIZE(ocx->com_err_ctx)); 108462306a36Sopenharmony_ci struct ocx_com_err_ctx *ctx = &ocx->com_err_ctx[head]; 108562306a36Sopenharmony_ci 108662306a36Sopenharmony_ci ctx->reg_com_int = readq(ocx->regs + OCX_COM_INT); 108762306a36Sopenharmony_ci 108862306a36Sopenharmony_ci for (lane = 0; lane < OCX_RX_LANES; lane++) { 108962306a36Sopenharmony_ci ctx->reg_lane_int[lane] = 109062306a36Sopenharmony_ci readq(ocx->regs + OCX_LNE_INT(lane)); 109162306a36Sopenharmony_ci ctx->reg_lane_stat11[lane] = 109262306a36Sopenharmony_ci readq(ocx->regs + OCX_LNE_STAT(lane, 11)); 109362306a36Sopenharmony_ci 109462306a36Sopenharmony_ci writeq(ctx->reg_lane_int[lane], ocx->regs + OCX_LNE_INT(lane)); 109562306a36Sopenharmony_ci } 109662306a36Sopenharmony_ci 109762306a36Sopenharmony_ci writeq(ctx->reg_com_int, ocx->regs + OCX_COM_INT); 109862306a36Sopenharmony_ci 109962306a36Sopenharmony_ci ocx->com_ring_head++; 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_ci return IRQ_WAKE_THREAD; 110262306a36Sopenharmony_ci} 110362306a36Sopenharmony_ci 110462306a36Sopenharmony_cistatic irqreturn_t thunderx_ocx_com_threaded_isr(int irq, void *irq_id) 110562306a36Sopenharmony_ci{ 110662306a36Sopenharmony_ci struct msix_entry *msix = irq_id; 110762306a36Sopenharmony_ci struct thunderx_ocx *ocx = container_of(msix, struct thunderx_ocx, 110862306a36Sopenharmony_ci msix_ent[msix->entry]); 110962306a36Sopenharmony_ci 111062306a36Sopenharmony_ci irqreturn_t ret = IRQ_NONE; 111162306a36Sopenharmony_ci 111262306a36Sopenharmony_ci unsigned long tail; 111362306a36Sopenharmony_ci struct ocx_com_err_ctx *ctx; 111462306a36Sopenharmony_ci int lane; 111562306a36Sopenharmony_ci char *msg; 111662306a36Sopenharmony_ci char *other; 111762306a36Sopenharmony_ci 111862306a36Sopenharmony_ci msg = kmalloc(OCX_MESSAGE_SIZE, GFP_KERNEL); 111962306a36Sopenharmony_ci other = kmalloc(OCX_OTHER_SIZE, GFP_KERNEL); 112062306a36Sopenharmony_ci 112162306a36Sopenharmony_ci if (!msg || !other) 112262306a36Sopenharmony_ci goto err_free; 112362306a36Sopenharmony_ci 112462306a36Sopenharmony_ci while (CIRC_CNT(ocx->com_ring_head, ocx->com_ring_tail, 112562306a36Sopenharmony_ci ARRAY_SIZE(ocx->com_err_ctx))) { 112662306a36Sopenharmony_ci tail = ring_pos(ocx->com_ring_tail, 112762306a36Sopenharmony_ci ARRAY_SIZE(ocx->com_err_ctx)); 112862306a36Sopenharmony_ci ctx = &ocx->com_err_ctx[tail]; 112962306a36Sopenharmony_ci 113062306a36Sopenharmony_ci snprintf(msg, OCX_MESSAGE_SIZE, "%s: OCX_COM_INT: %016llx", 113162306a36Sopenharmony_ci ocx->edac_dev->ctl_name, ctx->reg_com_int); 113262306a36Sopenharmony_ci 113362306a36Sopenharmony_ci decode_register(other, OCX_OTHER_SIZE, 113462306a36Sopenharmony_ci ocx_com_errors, ctx->reg_com_int); 113562306a36Sopenharmony_ci 113662306a36Sopenharmony_ci strlcat(msg, other, OCX_MESSAGE_SIZE); 113762306a36Sopenharmony_ci 113862306a36Sopenharmony_ci for (lane = 0; lane < OCX_RX_LANES; lane++) 113962306a36Sopenharmony_ci if (ctx->reg_com_int & BIT(lane)) { 114062306a36Sopenharmony_ci snprintf(other, OCX_OTHER_SIZE, 114162306a36Sopenharmony_ci "\n\tOCX_LNE_INT[%02d]: %016llx OCX_LNE_STAT11[%02d]: %016llx", 114262306a36Sopenharmony_ci lane, ctx->reg_lane_int[lane], 114362306a36Sopenharmony_ci lane, ctx->reg_lane_stat11[lane]); 114462306a36Sopenharmony_ci 114562306a36Sopenharmony_ci strlcat(msg, other, OCX_MESSAGE_SIZE); 114662306a36Sopenharmony_ci 114762306a36Sopenharmony_ci decode_register(other, OCX_OTHER_SIZE, 114862306a36Sopenharmony_ci ocx_lane_errors, 114962306a36Sopenharmony_ci ctx->reg_lane_int[lane]); 115062306a36Sopenharmony_ci strlcat(msg, other, OCX_MESSAGE_SIZE); 115162306a36Sopenharmony_ci } 115262306a36Sopenharmony_ci 115362306a36Sopenharmony_ci if (ctx->reg_com_int & OCX_COM_INT_CE) 115462306a36Sopenharmony_ci edac_device_handle_ce(ocx->edac_dev, 0, 0, msg); 115562306a36Sopenharmony_ci 115662306a36Sopenharmony_ci ocx->com_ring_tail++; 115762306a36Sopenharmony_ci } 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_ci ret = IRQ_HANDLED; 116062306a36Sopenharmony_ci 116162306a36Sopenharmony_cierr_free: 116262306a36Sopenharmony_ci kfree(other); 116362306a36Sopenharmony_ci kfree(msg); 116462306a36Sopenharmony_ci 116562306a36Sopenharmony_ci return ret; 116662306a36Sopenharmony_ci} 116762306a36Sopenharmony_ci 116862306a36Sopenharmony_cistatic irqreturn_t thunderx_ocx_lnk_isr(int irq, void *irq_id) 116962306a36Sopenharmony_ci{ 117062306a36Sopenharmony_ci struct msix_entry *msix = irq_id; 117162306a36Sopenharmony_ci struct thunderx_ocx *ocx = container_of(msix, struct thunderx_ocx, 117262306a36Sopenharmony_ci msix_ent[msix->entry]); 117362306a36Sopenharmony_ci unsigned long head = ring_pos(ocx->link_ring_head, 117462306a36Sopenharmony_ci ARRAY_SIZE(ocx->link_err_ctx)); 117562306a36Sopenharmony_ci struct ocx_link_err_ctx *ctx = &ocx->link_err_ctx[head]; 117662306a36Sopenharmony_ci 117762306a36Sopenharmony_ci ctx->link = msix->entry; 117862306a36Sopenharmony_ci ctx->reg_com_link_int = readq(ocx->regs + OCX_COM_LINKX_INT(ctx->link)); 117962306a36Sopenharmony_ci 118062306a36Sopenharmony_ci writeq(ctx->reg_com_link_int, ocx->regs + OCX_COM_LINKX_INT(ctx->link)); 118162306a36Sopenharmony_ci 118262306a36Sopenharmony_ci ocx->link_ring_head++; 118362306a36Sopenharmony_ci 118462306a36Sopenharmony_ci return IRQ_WAKE_THREAD; 118562306a36Sopenharmony_ci} 118662306a36Sopenharmony_ci 118762306a36Sopenharmony_cistatic irqreturn_t thunderx_ocx_lnk_threaded_isr(int irq, void *irq_id) 118862306a36Sopenharmony_ci{ 118962306a36Sopenharmony_ci struct msix_entry *msix = irq_id; 119062306a36Sopenharmony_ci struct thunderx_ocx *ocx = container_of(msix, struct thunderx_ocx, 119162306a36Sopenharmony_ci msix_ent[msix->entry]); 119262306a36Sopenharmony_ci irqreturn_t ret = IRQ_NONE; 119362306a36Sopenharmony_ci unsigned long tail; 119462306a36Sopenharmony_ci struct ocx_link_err_ctx *ctx; 119562306a36Sopenharmony_ci 119662306a36Sopenharmony_ci char *msg; 119762306a36Sopenharmony_ci char *other; 119862306a36Sopenharmony_ci 119962306a36Sopenharmony_ci msg = kmalloc(OCX_MESSAGE_SIZE, GFP_KERNEL); 120062306a36Sopenharmony_ci other = kmalloc(OCX_OTHER_SIZE, GFP_KERNEL); 120162306a36Sopenharmony_ci 120262306a36Sopenharmony_ci if (!msg || !other) 120362306a36Sopenharmony_ci goto err_free; 120462306a36Sopenharmony_ci 120562306a36Sopenharmony_ci while (CIRC_CNT(ocx->link_ring_head, ocx->link_ring_tail, 120662306a36Sopenharmony_ci ARRAY_SIZE(ocx->link_err_ctx))) { 120762306a36Sopenharmony_ci tail = ring_pos(ocx->link_ring_head, 120862306a36Sopenharmony_ci ARRAY_SIZE(ocx->link_err_ctx)); 120962306a36Sopenharmony_ci 121062306a36Sopenharmony_ci ctx = &ocx->link_err_ctx[tail]; 121162306a36Sopenharmony_ci 121262306a36Sopenharmony_ci snprintf(msg, OCX_MESSAGE_SIZE, 121362306a36Sopenharmony_ci "%s: OCX_COM_LINK_INT[%d]: %016llx", 121462306a36Sopenharmony_ci ocx->edac_dev->ctl_name, 121562306a36Sopenharmony_ci ctx->link, ctx->reg_com_link_int); 121662306a36Sopenharmony_ci 121762306a36Sopenharmony_ci decode_register(other, OCX_OTHER_SIZE, 121862306a36Sopenharmony_ci ocx_com_link_errors, ctx->reg_com_link_int); 121962306a36Sopenharmony_ci 122062306a36Sopenharmony_ci strlcat(msg, other, OCX_MESSAGE_SIZE); 122162306a36Sopenharmony_ci 122262306a36Sopenharmony_ci if (ctx->reg_com_link_int & OCX_COM_LINK_INT_UE) 122362306a36Sopenharmony_ci edac_device_handle_ue(ocx->edac_dev, 0, 0, msg); 122462306a36Sopenharmony_ci else if (ctx->reg_com_link_int & OCX_COM_LINK_INT_CE) 122562306a36Sopenharmony_ci edac_device_handle_ce(ocx->edac_dev, 0, 0, msg); 122662306a36Sopenharmony_ci 122762306a36Sopenharmony_ci ocx->link_ring_tail++; 122862306a36Sopenharmony_ci } 122962306a36Sopenharmony_ci 123062306a36Sopenharmony_ci ret = IRQ_HANDLED; 123162306a36Sopenharmony_cierr_free: 123262306a36Sopenharmony_ci kfree(other); 123362306a36Sopenharmony_ci kfree(msg); 123462306a36Sopenharmony_ci 123562306a36Sopenharmony_ci return ret; 123662306a36Sopenharmony_ci} 123762306a36Sopenharmony_ci 123862306a36Sopenharmony_ci#define OCX_DEBUGFS_ATTR(_name, _reg) DEBUGFS_REG_ATTR(ocx, _name, _reg) 123962306a36Sopenharmony_ci 124062306a36Sopenharmony_ciOCX_DEBUGFS_ATTR(tlk0_ecc_ctl, OCX_TLKX_ECC_CTL(0)); 124162306a36Sopenharmony_ciOCX_DEBUGFS_ATTR(tlk1_ecc_ctl, OCX_TLKX_ECC_CTL(1)); 124262306a36Sopenharmony_ciOCX_DEBUGFS_ATTR(tlk2_ecc_ctl, OCX_TLKX_ECC_CTL(2)); 124362306a36Sopenharmony_ci 124462306a36Sopenharmony_ciOCX_DEBUGFS_ATTR(rlk0_ecc_ctl, OCX_RLKX_ECC_CTL(0)); 124562306a36Sopenharmony_ciOCX_DEBUGFS_ATTR(rlk1_ecc_ctl, OCX_RLKX_ECC_CTL(1)); 124662306a36Sopenharmony_ciOCX_DEBUGFS_ATTR(rlk2_ecc_ctl, OCX_RLKX_ECC_CTL(2)); 124762306a36Sopenharmony_ci 124862306a36Sopenharmony_ciOCX_DEBUGFS_ATTR(com_link0_int, OCX_COM_LINKX_INT_W1S(0)); 124962306a36Sopenharmony_ciOCX_DEBUGFS_ATTR(com_link1_int, OCX_COM_LINKX_INT_W1S(1)); 125062306a36Sopenharmony_ciOCX_DEBUGFS_ATTR(com_link2_int, OCX_COM_LINKX_INT_W1S(2)); 125162306a36Sopenharmony_ci 125262306a36Sopenharmony_ciOCX_DEBUGFS_ATTR(lne00_badcnt, OCX_LNE_BAD_CNT(0)); 125362306a36Sopenharmony_ciOCX_DEBUGFS_ATTR(lne01_badcnt, OCX_LNE_BAD_CNT(1)); 125462306a36Sopenharmony_ciOCX_DEBUGFS_ATTR(lne02_badcnt, OCX_LNE_BAD_CNT(2)); 125562306a36Sopenharmony_ciOCX_DEBUGFS_ATTR(lne03_badcnt, OCX_LNE_BAD_CNT(3)); 125662306a36Sopenharmony_ciOCX_DEBUGFS_ATTR(lne04_badcnt, OCX_LNE_BAD_CNT(4)); 125762306a36Sopenharmony_ciOCX_DEBUGFS_ATTR(lne05_badcnt, OCX_LNE_BAD_CNT(5)); 125862306a36Sopenharmony_ciOCX_DEBUGFS_ATTR(lne06_badcnt, OCX_LNE_BAD_CNT(6)); 125962306a36Sopenharmony_ciOCX_DEBUGFS_ATTR(lne07_badcnt, OCX_LNE_BAD_CNT(7)); 126062306a36Sopenharmony_ci 126162306a36Sopenharmony_ciOCX_DEBUGFS_ATTR(lne08_badcnt, OCX_LNE_BAD_CNT(8)); 126262306a36Sopenharmony_ciOCX_DEBUGFS_ATTR(lne09_badcnt, OCX_LNE_BAD_CNT(9)); 126362306a36Sopenharmony_ciOCX_DEBUGFS_ATTR(lne10_badcnt, OCX_LNE_BAD_CNT(10)); 126462306a36Sopenharmony_ciOCX_DEBUGFS_ATTR(lne11_badcnt, OCX_LNE_BAD_CNT(11)); 126562306a36Sopenharmony_ciOCX_DEBUGFS_ATTR(lne12_badcnt, OCX_LNE_BAD_CNT(12)); 126662306a36Sopenharmony_ciOCX_DEBUGFS_ATTR(lne13_badcnt, OCX_LNE_BAD_CNT(13)); 126762306a36Sopenharmony_ciOCX_DEBUGFS_ATTR(lne14_badcnt, OCX_LNE_BAD_CNT(14)); 126862306a36Sopenharmony_ciOCX_DEBUGFS_ATTR(lne15_badcnt, OCX_LNE_BAD_CNT(15)); 126962306a36Sopenharmony_ci 127062306a36Sopenharmony_ciOCX_DEBUGFS_ATTR(lne16_badcnt, OCX_LNE_BAD_CNT(16)); 127162306a36Sopenharmony_ciOCX_DEBUGFS_ATTR(lne17_badcnt, OCX_LNE_BAD_CNT(17)); 127262306a36Sopenharmony_ciOCX_DEBUGFS_ATTR(lne18_badcnt, OCX_LNE_BAD_CNT(18)); 127362306a36Sopenharmony_ciOCX_DEBUGFS_ATTR(lne19_badcnt, OCX_LNE_BAD_CNT(19)); 127462306a36Sopenharmony_ciOCX_DEBUGFS_ATTR(lne20_badcnt, OCX_LNE_BAD_CNT(20)); 127562306a36Sopenharmony_ciOCX_DEBUGFS_ATTR(lne21_badcnt, OCX_LNE_BAD_CNT(21)); 127662306a36Sopenharmony_ciOCX_DEBUGFS_ATTR(lne22_badcnt, OCX_LNE_BAD_CNT(22)); 127762306a36Sopenharmony_ciOCX_DEBUGFS_ATTR(lne23_badcnt, OCX_LNE_BAD_CNT(23)); 127862306a36Sopenharmony_ci 127962306a36Sopenharmony_ciOCX_DEBUGFS_ATTR(com_int, OCX_COM_INT_W1S); 128062306a36Sopenharmony_ci 128162306a36Sopenharmony_cistatic struct debugfs_entry *ocx_dfs_ents[] = { 128262306a36Sopenharmony_ci &debugfs_tlk0_ecc_ctl, 128362306a36Sopenharmony_ci &debugfs_tlk1_ecc_ctl, 128462306a36Sopenharmony_ci &debugfs_tlk2_ecc_ctl, 128562306a36Sopenharmony_ci 128662306a36Sopenharmony_ci &debugfs_rlk0_ecc_ctl, 128762306a36Sopenharmony_ci &debugfs_rlk1_ecc_ctl, 128862306a36Sopenharmony_ci &debugfs_rlk2_ecc_ctl, 128962306a36Sopenharmony_ci 129062306a36Sopenharmony_ci &debugfs_com_link0_int, 129162306a36Sopenharmony_ci &debugfs_com_link1_int, 129262306a36Sopenharmony_ci &debugfs_com_link2_int, 129362306a36Sopenharmony_ci 129462306a36Sopenharmony_ci &debugfs_lne00_badcnt, 129562306a36Sopenharmony_ci &debugfs_lne01_badcnt, 129662306a36Sopenharmony_ci &debugfs_lne02_badcnt, 129762306a36Sopenharmony_ci &debugfs_lne03_badcnt, 129862306a36Sopenharmony_ci &debugfs_lne04_badcnt, 129962306a36Sopenharmony_ci &debugfs_lne05_badcnt, 130062306a36Sopenharmony_ci &debugfs_lne06_badcnt, 130162306a36Sopenharmony_ci &debugfs_lne07_badcnt, 130262306a36Sopenharmony_ci &debugfs_lne08_badcnt, 130362306a36Sopenharmony_ci &debugfs_lne09_badcnt, 130462306a36Sopenharmony_ci &debugfs_lne10_badcnt, 130562306a36Sopenharmony_ci &debugfs_lne11_badcnt, 130662306a36Sopenharmony_ci &debugfs_lne12_badcnt, 130762306a36Sopenharmony_ci &debugfs_lne13_badcnt, 130862306a36Sopenharmony_ci &debugfs_lne14_badcnt, 130962306a36Sopenharmony_ci &debugfs_lne15_badcnt, 131062306a36Sopenharmony_ci &debugfs_lne16_badcnt, 131162306a36Sopenharmony_ci &debugfs_lne17_badcnt, 131262306a36Sopenharmony_ci &debugfs_lne18_badcnt, 131362306a36Sopenharmony_ci &debugfs_lne19_badcnt, 131462306a36Sopenharmony_ci &debugfs_lne20_badcnt, 131562306a36Sopenharmony_ci &debugfs_lne21_badcnt, 131662306a36Sopenharmony_ci &debugfs_lne22_badcnt, 131762306a36Sopenharmony_ci &debugfs_lne23_badcnt, 131862306a36Sopenharmony_ci 131962306a36Sopenharmony_ci &debugfs_com_int, 132062306a36Sopenharmony_ci}; 132162306a36Sopenharmony_ci 132262306a36Sopenharmony_cistatic const struct pci_device_id thunderx_ocx_pci_tbl[] = { 132362306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, PCI_DEVICE_ID_THUNDER_OCX) }, 132462306a36Sopenharmony_ci { 0, }, 132562306a36Sopenharmony_ci}; 132662306a36Sopenharmony_ci 132762306a36Sopenharmony_cistatic void thunderx_ocx_clearstats(struct thunderx_ocx *ocx) 132862306a36Sopenharmony_ci{ 132962306a36Sopenharmony_ci int lane, stat, cfg; 133062306a36Sopenharmony_ci 133162306a36Sopenharmony_ci for (lane = 0; lane < OCX_RX_LANES; lane++) { 133262306a36Sopenharmony_ci cfg = readq(ocx->regs + OCX_LNE_CFG(lane)); 133362306a36Sopenharmony_ci cfg |= OCX_LNE_CFG_RX_STAT_RDCLR; 133462306a36Sopenharmony_ci cfg &= ~OCX_LNE_CFG_RX_STAT_ENA; 133562306a36Sopenharmony_ci writeq(cfg, ocx->regs + OCX_LNE_CFG(lane)); 133662306a36Sopenharmony_ci 133762306a36Sopenharmony_ci for (stat = 0; stat < OCX_RX_LANE_STATS; stat++) 133862306a36Sopenharmony_ci readq(ocx->regs + OCX_LNE_STAT(lane, stat)); 133962306a36Sopenharmony_ci } 134062306a36Sopenharmony_ci} 134162306a36Sopenharmony_ci 134262306a36Sopenharmony_cistatic int thunderx_ocx_probe(struct pci_dev *pdev, 134362306a36Sopenharmony_ci const struct pci_device_id *id) 134462306a36Sopenharmony_ci{ 134562306a36Sopenharmony_ci struct thunderx_ocx *ocx; 134662306a36Sopenharmony_ci struct edac_device_ctl_info *edac_dev; 134762306a36Sopenharmony_ci char name[32]; 134862306a36Sopenharmony_ci int idx; 134962306a36Sopenharmony_ci int i; 135062306a36Sopenharmony_ci int ret; 135162306a36Sopenharmony_ci u64 reg; 135262306a36Sopenharmony_ci 135362306a36Sopenharmony_ci ret = pcim_enable_device(pdev); 135462306a36Sopenharmony_ci if (ret) { 135562306a36Sopenharmony_ci dev_err(&pdev->dev, "Cannot enable PCI device: %d\n", ret); 135662306a36Sopenharmony_ci return ret; 135762306a36Sopenharmony_ci } 135862306a36Sopenharmony_ci 135962306a36Sopenharmony_ci ret = pcim_iomap_regions(pdev, BIT(0), "thunderx_ocx"); 136062306a36Sopenharmony_ci if (ret) { 136162306a36Sopenharmony_ci dev_err(&pdev->dev, "Cannot map PCI resources: %d\n", ret); 136262306a36Sopenharmony_ci return ret; 136362306a36Sopenharmony_ci } 136462306a36Sopenharmony_ci 136562306a36Sopenharmony_ci idx = edac_device_alloc_index(); 136662306a36Sopenharmony_ci snprintf(name, sizeof(name), "OCX%d", idx); 136762306a36Sopenharmony_ci edac_dev = edac_device_alloc_ctl_info(sizeof(struct thunderx_ocx), 136862306a36Sopenharmony_ci name, 1, "CCPI", 1, 136962306a36Sopenharmony_ci 0, NULL, 0, idx); 137062306a36Sopenharmony_ci if (!edac_dev) { 137162306a36Sopenharmony_ci dev_err(&pdev->dev, "Cannot allocate EDAC device\n"); 137262306a36Sopenharmony_ci return -ENOMEM; 137362306a36Sopenharmony_ci } 137462306a36Sopenharmony_ci ocx = edac_dev->pvt_info; 137562306a36Sopenharmony_ci ocx->edac_dev = edac_dev; 137662306a36Sopenharmony_ci ocx->com_ring_head = 0; 137762306a36Sopenharmony_ci ocx->com_ring_tail = 0; 137862306a36Sopenharmony_ci ocx->link_ring_head = 0; 137962306a36Sopenharmony_ci ocx->link_ring_tail = 0; 138062306a36Sopenharmony_ci 138162306a36Sopenharmony_ci ocx->regs = pcim_iomap_table(pdev)[0]; 138262306a36Sopenharmony_ci if (!ocx->regs) { 138362306a36Sopenharmony_ci dev_err(&pdev->dev, "Cannot map PCI resources\n"); 138462306a36Sopenharmony_ci ret = -ENODEV; 138562306a36Sopenharmony_ci goto err_free; 138662306a36Sopenharmony_ci } 138762306a36Sopenharmony_ci 138862306a36Sopenharmony_ci ocx->pdev = pdev; 138962306a36Sopenharmony_ci 139062306a36Sopenharmony_ci for (i = 0; i < OCX_INTS; i++) { 139162306a36Sopenharmony_ci ocx->msix_ent[i].entry = i; 139262306a36Sopenharmony_ci ocx->msix_ent[i].vector = 0; 139362306a36Sopenharmony_ci } 139462306a36Sopenharmony_ci 139562306a36Sopenharmony_ci ret = pci_enable_msix_exact(pdev, ocx->msix_ent, OCX_INTS); 139662306a36Sopenharmony_ci if (ret) { 139762306a36Sopenharmony_ci dev_err(&pdev->dev, "Cannot enable interrupt: %d\n", ret); 139862306a36Sopenharmony_ci goto err_free; 139962306a36Sopenharmony_ci } 140062306a36Sopenharmony_ci 140162306a36Sopenharmony_ci for (i = 0; i < OCX_INTS; i++) { 140262306a36Sopenharmony_ci ret = devm_request_threaded_irq(&pdev->dev, 140362306a36Sopenharmony_ci ocx->msix_ent[i].vector, 140462306a36Sopenharmony_ci (i == 3) ? 140562306a36Sopenharmony_ci thunderx_ocx_com_isr : 140662306a36Sopenharmony_ci thunderx_ocx_lnk_isr, 140762306a36Sopenharmony_ci (i == 3) ? 140862306a36Sopenharmony_ci thunderx_ocx_com_threaded_isr : 140962306a36Sopenharmony_ci thunderx_ocx_lnk_threaded_isr, 141062306a36Sopenharmony_ci 0, "[EDAC] ThunderX OCX", 141162306a36Sopenharmony_ci &ocx->msix_ent[i]); 141262306a36Sopenharmony_ci if (ret) 141362306a36Sopenharmony_ci goto err_free; 141462306a36Sopenharmony_ci } 141562306a36Sopenharmony_ci 141662306a36Sopenharmony_ci edac_dev->dev = &pdev->dev; 141762306a36Sopenharmony_ci edac_dev->dev_name = dev_name(&pdev->dev); 141862306a36Sopenharmony_ci edac_dev->mod_name = "thunderx-ocx"; 141962306a36Sopenharmony_ci edac_dev->ctl_name = "thunderx-ocx"; 142062306a36Sopenharmony_ci 142162306a36Sopenharmony_ci ret = edac_device_add_device(edac_dev); 142262306a36Sopenharmony_ci if (ret) { 142362306a36Sopenharmony_ci dev_err(&pdev->dev, "Cannot add EDAC device: %d\n", ret); 142462306a36Sopenharmony_ci goto err_free; 142562306a36Sopenharmony_ci } 142662306a36Sopenharmony_ci 142762306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_EDAC_DEBUG)) { 142862306a36Sopenharmony_ci ocx->debugfs = edac_debugfs_create_dir(pdev->dev.kobj.name); 142962306a36Sopenharmony_ci 143062306a36Sopenharmony_ci ret = thunderx_create_debugfs_nodes(ocx->debugfs, 143162306a36Sopenharmony_ci ocx_dfs_ents, 143262306a36Sopenharmony_ci ocx, 143362306a36Sopenharmony_ci ARRAY_SIZE(ocx_dfs_ents)); 143462306a36Sopenharmony_ci if (ret != ARRAY_SIZE(ocx_dfs_ents)) { 143562306a36Sopenharmony_ci dev_warn(&pdev->dev, "Error creating debugfs entries: %d%s\n", 143662306a36Sopenharmony_ci ret, ret >= 0 ? " created" : ""); 143762306a36Sopenharmony_ci } 143862306a36Sopenharmony_ci } 143962306a36Sopenharmony_ci 144062306a36Sopenharmony_ci pci_set_drvdata(pdev, edac_dev); 144162306a36Sopenharmony_ci 144262306a36Sopenharmony_ci thunderx_ocx_clearstats(ocx); 144362306a36Sopenharmony_ci 144462306a36Sopenharmony_ci for (i = 0; i < OCX_RX_LANES; i++) { 144562306a36Sopenharmony_ci writeq(OCX_LNE_INT_ENA_ALL, 144662306a36Sopenharmony_ci ocx->regs + OCX_LNE_INT_EN(i)); 144762306a36Sopenharmony_ci 144862306a36Sopenharmony_ci reg = readq(ocx->regs + OCX_LNE_INT(i)); 144962306a36Sopenharmony_ci writeq(reg, ocx->regs + OCX_LNE_INT(i)); 145062306a36Sopenharmony_ci 145162306a36Sopenharmony_ci } 145262306a36Sopenharmony_ci 145362306a36Sopenharmony_ci for (i = 0; i < OCX_LINK_INTS; i++) { 145462306a36Sopenharmony_ci reg = readq(ocx->regs + OCX_COM_LINKX_INT(i)); 145562306a36Sopenharmony_ci writeq(reg, ocx->regs + OCX_COM_LINKX_INT(i)); 145662306a36Sopenharmony_ci 145762306a36Sopenharmony_ci writeq(OCX_COM_LINKX_INT_ENA_ALL, 145862306a36Sopenharmony_ci ocx->regs + OCX_COM_LINKX_INT_ENA_W1S(i)); 145962306a36Sopenharmony_ci } 146062306a36Sopenharmony_ci 146162306a36Sopenharmony_ci reg = readq(ocx->regs + OCX_COM_INT); 146262306a36Sopenharmony_ci writeq(reg, ocx->regs + OCX_COM_INT); 146362306a36Sopenharmony_ci 146462306a36Sopenharmony_ci writeq(OCX_COM_INT_ENA_ALL, ocx->regs + OCX_COM_INT_ENA_W1S); 146562306a36Sopenharmony_ci 146662306a36Sopenharmony_ci return 0; 146762306a36Sopenharmony_cierr_free: 146862306a36Sopenharmony_ci edac_device_free_ctl_info(edac_dev); 146962306a36Sopenharmony_ci 147062306a36Sopenharmony_ci return ret; 147162306a36Sopenharmony_ci} 147262306a36Sopenharmony_ci 147362306a36Sopenharmony_cistatic void thunderx_ocx_remove(struct pci_dev *pdev) 147462306a36Sopenharmony_ci{ 147562306a36Sopenharmony_ci struct edac_device_ctl_info *edac_dev = pci_get_drvdata(pdev); 147662306a36Sopenharmony_ci struct thunderx_ocx *ocx = edac_dev->pvt_info; 147762306a36Sopenharmony_ci int i; 147862306a36Sopenharmony_ci 147962306a36Sopenharmony_ci writeq(OCX_COM_INT_ENA_ALL, ocx->regs + OCX_COM_INT_ENA_W1C); 148062306a36Sopenharmony_ci 148162306a36Sopenharmony_ci for (i = 0; i < OCX_INTS; i++) { 148262306a36Sopenharmony_ci writeq(OCX_COM_LINKX_INT_ENA_ALL, 148362306a36Sopenharmony_ci ocx->regs + OCX_COM_LINKX_INT_ENA_W1C(i)); 148462306a36Sopenharmony_ci } 148562306a36Sopenharmony_ci 148662306a36Sopenharmony_ci edac_debugfs_remove_recursive(ocx->debugfs); 148762306a36Sopenharmony_ci 148862306a36Sopenharmony_ci edac_device_del_device(&pdev->dev); 148962306a36Sopenharmony_ci edac_device_free_ctl_info(edac_dev); 149062306a36Sopenharmony_ci} 149162306a36Sopenharmony_ci 149262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, thunderx_ocx_pci_tbl); 149362306a36Sopenharmony_ci 149462306a36Sopenharmony_cistatic struct pci_driver thunderx_ocx_driver = { 149562306a36Sopenharmony_ci .name = "thunderx_ocx_edac", 149662306a36Sopenharmony_ci .probe = thunderx_ocx_probe, 149762306a36Sopenharmony_ci .remove = thunderx_ocx_remove, 149862306a36Sopenharmony_ci .id_table = thunderx_ocx_pci_tbl, 149962306a36Sopenharmony_ci}; 150062306a36Sopenharmony_ci 150162306a36Sopenharmony_ci/*---------------------- L2C driver ---------------------------------*/ 150262306a36Sopenharmony_ci 150362306a36Sopenharmony_ci#define PCI_DEVICE_ID_THUNDER_L2C_TAD 0xa02e 150462306a36Sopenharmony_ci#define PCI_DEVICE_ID_THUNDER_L2C_CBC 0xa02f 150562306a36Sopenharmony_ci#define PCI_DEVICE_ID_THUNDER_L2C_MCI 0xa030 150662306a36Sopenharmony_ci 150762306a36Sopenharmony_ci#define L2C_TAD_INT_W1C 0x40000 150862306a36Sopenharmony_ci#define L2C_TAD_INT_W1S 0x40008 150962306a36Sopenharmony_ci 151062306a36Sopenharmony_ci#define L2C_TAD_INT_ENA_W1C 0x40020 151162306a36Sopenharmony_ci#define L2C_TAD_INT_ENA_W1S 0x40028 151262306a36Sopenharmony_ci 151362306a36Sopenharmony_ci 151462306a36Sopenharmony_ci#define L2C_TAD_INT_L2DDBE BIT(1) 151562306a36Sopenharmony_ci#define L2C_TAD_INT_SBFSBE BIT(2) 151662306a36Sopenharmony_ci#define L2C_TAD_INT_SBFDBE BIT(3) 151762306a36Sopenharmony_ci#define L2C_TAD_INT_FBFSBE BIT(4) 151862306a36Sopenharmony_ci#define L2C_TAD_INT_FBFDBE BIT(5) 151962306a36Sopenharmony_ci#define L2C_TAD_INT_TAGDBE BIT(9) 152062306a36Sopenharmony_ci#define L2C_TAD_INT_RDDISLMC BIT(15) 152162306a36Sopenharmony_ci#define L2C_TAD_INT_WRDISLMC BIT(16) 152262306a36Sopenharmony_ci#define L2C_TAD_INT_LFBTO BIT(17) 152362306a36Sopenharmony_ci#define L2C_TAD_INT_GSYNCTO BIT(18) 152462306a36Sopenharmony_ci#define L2C_TAD_INT_RTGSBE BIT(32) 152562306a36Sopenharmony_ci#define L2C_TAD_INT_RTGDBE BIT(33) 152662306a36Sopenharmony_ci#define L2C_TAD_INT_RDDISOCI BIT(34) 152762306a36Sopenharmony_ci#define L2C_TAD_INT_WRDISOCI BIT(35) 152862306a36Sopenharmony_ci 152962306a36Sopenharmony_ci#define L2C_TAD_INT_ECC (L2C_TAD_INT_L2DDBE | \ 153062306a36Sopenharmony_ci L2C_TAD_INT_SBFSBE | L2C_TAD_INT_SBFDBE | \ 153162306a36Sopenharmony_ci L2C_TAD_INT_FBFSBE | L2C_TAD_INT_FBFDBE) 153262306a36Sopenharmony_ci 153362306a36Sopenharmony_ci#define L2C_TAD_INT_CE (L2C_TAD_INT_SBFSBE | \ 153462306a36Sopenharmony_ci L2C_TAD_INT_FBFSBE) 153562306a36Sopenharmony_ci 153662306a36Sopenharmony_ci#define L2C_TAD_INT_UE (L2C_TAD_INT_L2DDBE | \ 153762306a36Sopenharmony_ci L2C_TAD_INT_SBFDBE | \ 153862306a36Sopenharmony_ci L2C_TAD_INT_FBFDBE | \ 153962306a36Sopenharmony_ci L2C_TAD_INT_TAGDBE | \ 154062306a36Sopenharmony_ci L2C_TAD_INT_RTGDBE | \ 154162306a36Sopenharmony_ci L2C_TAD_INT_WRDISOCI | \ 154262306a36Sopenharmony_ci L2C_TAD_INT_RDDISOCI | \ 154362306a36Sopenharmony_ci L2C_TAD_INT_WRDISLMC | \ 154462306a36Sopenharmony_ci L2C_TAD_INT_RDDISLMC | \ 154562306a36Sopenharmony_ci L2C_TAD_INT_LFBTO | \ 154662306a36Sopenharmony_ci L2C_TAD_INT_GSYNCTO) 154762306a36Sopenharmony_ci 154862306a36Sopenharmony_cistatic const struct error_descr l2_tad_errors[] = { 154962306a36Sopenharmony_ci { 155062306a36Sopenharmony_ci .type = ERR_CORRECTED, 155162306a36Sopenharmony_ci .mask = L2C_TAD_INT_SBFSBE, 155262306a36Sopenharmony_ci .descr = "SBF single-bit error", 155362306a36Sopenharmony_ci }, 155462306a36Sopenharmony_ci { 155562306a36Sopenharmony_ci .type = ERR_CORRECTED, 155662306a36Sopenharmony_ci .mask = L2C_TAD_INT_FBFSBE, 155762306a36Sopenharmony_ci .descr = "FBF single-bit error", 155862306a36Sopenharmony_ci }, 155962306a36Sopenharmony_ci { 156062306a36Sopenharmony_ci .type = ERR_UNCORRECTED, 156162306a36Sopenharmony_ci .mask = L2C_TAD_INT_L2DDBE, 156262306a36Sopenharmony_ci .descr = "L2D double-bit error", 156362306a36Sopenharmony_ci }, 156462306a36Sopenharmony_ci { 156562306a36Sopenharmony_ci .type = ERR_UNCORRECTED, 156662306a36Sopenharmony_ci .mask = L2C_TAD_INT_SBFDBE, 156762306a36Sopenharmony_ci .descr = "SBF double-bit error", 156862306a36Sopenharmony_ci }, 156962306a36Sopenharmony_ci { 157062306a36Sopenharmony_ci .type = ERR_UNCORRECTED, 157162306a36Sopenharmony_ci .mask = L2C_TAD_INT_FBFDBE, 157262306a36Sopenharmony_ci .descr = "FBF double-bit error", 157362306a36Sopenharmony_ci }, 157462306a36Sopenharmony_ci { 157562306a36Sopenharmony_ci .type = ERR_UNCORRECTED, 157662306a36Sopenharmony_ci .mask = L2C_TAD_INT_TAGDBE, 157762306a36Sopenharmony_ci .descr = "TAG double-bit error", 157862306a36Sopenharmony_ci }, 157962306a36Sopenharmony_ci { 158062306a36Sopenharmony_ci .type = ERR_UNCORRECTED, 158162306a36Sopenharmony_ci .mask = L2C_TAD_INT_RTGDBE, 158262306a36Sopenharmony_ci .descr = "RTG double-bit error", 158362306a36Sopenharmony_ci }, 158462306a36Sopenharmony_ci { 158562306a36Sopenharmony_ci .type = ERR_UNCORRECTED, 158662306a36Sopenharmony_ci .mask = L2C_TAD_INT_WRDISOCI, 158762306a36Sopenharmony_ci .descr = "Write to a disabled CCPI", 158862306a36Sopenharmony_ci }, 158962306a36Sopenharmony_ci { 159062306a36Sopenharmony_ci .type = ERR_UNCORRECTED, 159162306a36Sopenharmony_ci .mask = L2C_TAD_INT_RDDISOCI, 159262306a36Sopenharmony_ci .descr = "Read from a disabled CCPI", 159362306a36Sopenharmony_ci }, 159462306a36Sopenharmony_ci { 159562306a36Sopenharmony_ci .type = ERR_UNCORRECTED, 159662306a36Sopenharmony_ci .mask = L2C_TAD_INT_WRDISLMC, 159762306a36Sopenharmony_ci .descr = "Write to a disabled LMC", 159862306a36Sopenharmony_ci }, 159962306a36Sopenharmony_ci { 160062306a36Sopenharmony_ci .type = ERR_UNCORRECTED, 160162306a36Sopenharmony_ci .mask = L2C_TAD_INT_RDDISLMC, 160262306a36Sopenharmony_ci .descr = "Read from a disabled LMC", 160362306a36Sopenharmony_ci }, 160462306a36Sopenharmony_ci { 160562306a36Sopenharmony_ci .type = ERR_UNCORRECTED, 160662306a36Sopenharmony_ci .mask = L2C_TAD_INT_LFBTO, 160762306a36Sopenharmony_ci .descr = "LFB entry timeout", 160862306a36Sopenharmony_ci }, 160962306a36Sopenharmony_ci { 161062306a36Sopenharmony_ci .type = ERR_UNCORRECTED, 161162306a36Sopenharmony_ci .mask = L2C_TAD_INT_GSYNCTO, 161262306a36Sopenharmony_ci .descr = "Global sync CCPI timeout", 161362306a36Sopenharmony_ci }, 161462306a36Sopenharmony_ci {0, 0, NULL}, 161562306a36Sopenharmony_ci}; 161662306a36Sopenharmony_ci 161762306a36Sopenharmony_ci#define L2C_TAD_INT_TAG (L2C_TAD_INT_TAGDBE) 161862306a36Sopenharmony_ci 161962306a36Sopenharmony_ci#define L2C_TAD_INT_RTG (L2C_TAD_INT_RTGDBE) 162062306a36Sopenharmony_ci 162162306a36Sopenharmony_ci#define L2C_TAD_INT_DISLMC (L2C_TAD_INT_WRDISLMC | L2C_TAD_INT_RDDISLMC) 162262306a36Sopenharmony_ci 162362306a36Sopenharmony_ci#define L2C_TAD_INT_DISOCI (L2C_TAD_INT_WRDISOCI | L2C_TAD_INT_RDDISOCI) 162462306a36Sopenharmony_ci 162562306a36Sopenharmony_ci#define L2C_TAD_INT_ENA_ALL (L2C_TAD_INT_ECC | L2C_TAD_INT_TAG | \ 162662306a36Sopenharmony_ci L2C_TAD_INT_RTG | \ 162762306a36Sopenharmony_ci L2C_TAD_INT_DISLMC | L2C_TAD_INT_DISOCI | \ 162862306a36Sopenharmony_ci L2C_TAD_INT_LFBTO) 162962306a36Sopenharmony_ci 163062306a36Sopenharmony_ci#define L2C_TAD_TIMETWO 0x50000 163162306a36Sopenharmony_ci#define L2C_TAD_TIMEOUT 0x50100 163262306a36Sopenharmony_ci#define L2C_TAD_ERR 0x60000 163362306a36Sopenharmony_ci#define L2C_TAD_TQD_ERR 0x60100 163462306a36Sopenharmony_ci#define L2C_TAD_TTG_ERR 0x60200 163562306a36Sopenharmony_ci 163662306a36Sopenharmony_ci 163762306a36Sopenharmony_ci#define L2C_CBC_INT_W1C 0x60000 163862306a36Sopenharmony_ci 163962306a36Sopenharmony_ci#define L2C_CBC_INT_RSDSBE BIT(0) 164062306a36Sopenharmony_ci#define L2C_CBC_INT_RSDDBE BIT(1) 164162306a36Sopenharmony_ci 164262306a36Sopenharmony_ci#define L2C_CBC_INT_RSD (L2C_CBC_INT_RSDSBE | L2C_CBC_INT_RSDDBE) 164362306a36Sopenharmony_ci 164462306a36Sopenharmony_ci#define L2C_CBC_INT_MIBSBE BIT(4) 164562306a36Sopenharmony_ci#define L2C_CBC_INT_MIBDBE BIT(5) 164662306a36Sopenharmony_ci 164762306a36Sopenharmony_ci#define L2C_CBC_INT_MIB (L2C_CBC_INT_MIBSBE | L2C_CBC_INT_MIBDBE) 164862306a36Sopenharmony_ci 164962306a36Sopenharmony_ci#define L2C_CBC_INT_IORDDISOCI BIT(6) 165062306a36Sopenharmony_ci#define L2C_CBC_INT_IOWRDISOCI BIT(7) 165162306a36Sopenharmony_ci 165262306a36Sopenharmony_ci#define L2C_CBC_INT_IODISOCI (L2C_CBC_INT_IORDDISOCI | \ 165362306a36Sopenharmony_ci L2C_CBC_INT_IOWRDISOCI) 165462306a36Sopenharmony_ci 165562306a36Sopenharmony_ci#define L2C_CBC_INT_CE (L2C_CBC_INT_RSDSBE | L2C_CBC_INT_MIBSBE) 165662306a36Sopenharmony_ci#define L2C_CBC_INT_UE (L2C_CBC_INT_RSDDBE | L2C_CBC_INT_MIBDBE) 165762306a36Sopenharmony_ci 165862306a36Sopenharmony_ci 165962306a36Sopenharmony_cistatic const struct error_descr l2_cbc_errors[] = { 166062306a36Sopenharmony_ci { 166162306a36Sopenharmony_ci .type = ERR_CORRECTED, 166262306a36Sopenharmony_ci .mask = L2C_CBC_INT_RSDSBE, 166362306a36Sopenharmony_ci .descr = "RSD single-bit error", 166462306a36Sopenharmony_ci }, 166562306a36Sopenharmony_ci { 166662306a36Sopenharmony_ci .type = ERR_CORRECTED, 166762306a36Sopenharmony_ci .mask = L2C_CBC_INT_MIBSBE, 166862306a36Sopenharmony_ci .descr = "MIB single-bit error", 166962306a36Sopenharmony_ci }, 167062306a36Sopenharmony_ci { 167162306a36Sopenharmony_ci .type = ERR_UNCORRECTED, 167262306a36Sopenharmony_ci .mask = L2C_CBC_INT_RSDDBE, 167362306a36Sopenharmony_ci .descr = "RSD double-bit error", 167462306a36Sopenharmony_ci }, 167562306a36Sopenharmony_ci { 167662306a36Sopenharmony_ci .type = ERR_UNCORRECTED, 167762306a36Sopenharmony_ci .mask = L2C_CBC_INT_MIBDBE, 167862306a36Sopenharmony_ci .descr = "MIB double-bit error", 167962306a36Sopenharmony_ci }, 168062306a36Sopenharmony_ci { 168162306a36Sopenharmony_ci .type = ERR_UNCORRECTED, 168262306a36Sopenharmony_ci .mask = L2C_CBC_INT_IORDDISOCI, 168362306a36Sopenharmony_ci .descr = "Read from a disabled CCPI", 168462306a36Sopenharmony_ci }, 168562306a36Sopenharmony_ci { 168662306a36Sopenharmony_ci .type = ERR_UNCORRECTED, 168762306a36Sopenharmony_ci .mask = L2C_CBC_INT_IOWRDISOCI, 168862306a36Sopenharmony_ci .descr = "Write to a disabled CCPI", 168962306a36Sopenharmony_ci }, 169062306a36Sopenharmony_ci {0, 0, NULL}, 169162306a36Sopenharmony_ci}; 169262306a36Sopenharmony_ci 169362306a36Sopenharmony_ci#define L2C_CBC_INT_W1S 0x60008 169462306a36Sopenharmony_ci#define L2C_CBC_INT_ENA_W1C 0x60020 169562306a36Sopenharmony_ci 169662306a36Sopenharmony_ci#define L2C_CBC_INT_ENA_ALL (L2C_CBC_INT_RSD | L2C_CBC_INT_MIB | \ 169762306a36Sopenharmony_ci L2C_CBC_INT_IODISOCI) 169862306a36Sopenharmony_ci 169962306a36Sopenharmony_ci#define L2C_CBC_INT_ENA_W1S 0x60028 170062306a36Sopenharmony_ci 170162306a36Sopenharmony_ci#define L2C_CBC_IODISOCIERR 0x80008 170262306a36Sopenharmony_ci#define L2C_CBC_IOCERR 0x80010 170362306a36Sopenharmony_ci#define L2C_CBC_RSDERR 0x80018 170462306a36Sopenharmony_ci#define L2C_CBC_MIBERR 0x80020 170562306a36Sopenharmony_ci 170662306a36Sopenharmony_ci 170762306a36Sopenharmony_ci#define L2C_MCI_INT_W1C 0x0 170862306a36Sopenharmony_ci 170962306a36Sopenharmony_ci#define L2C_MCI_INT_VBFSBE BIT(0) 171062306a36Sopenharmony_ci#define L2C_MCI_INT_VBFDBE BIT(1) 171162306a36Sopenharmony_ci 171262306a36Sopenharmony_cistatic const struct error_descr l2_mci_errors[] = { 171362306a36Sopenharmony_ci { 171462306a36Sopenharmony_ci .type = ERR_CORRECTED, 171562306a36Sopenharmony_ci .mask = L2C_MCI_INT_VBFSBE, 171662306a36Sopenharmony_ci .descr = "VBF single-bit error", 171762306a36Sopenharmony_ci }, 171862306a36Sopenharmony_ci { 171962306a36Sopenharmony_ci .type = ERR_UNCORRECTED, 172062306a36Sopenharmony_ci .mask = L2C_MCI_INT_VBFDBE, 172162306a36Sopenharmony_ci .descr = "VBF double-bit error", 172262306a36Sopenharmony_ci }, 172362306a36Sopenharmony_ci {0, 0, NULL}, 172462306a36Sopenharmony_ci}; 172562306a36Sopenharmony_ci 172662306a36Sopenharmony_ci#define L2C_MCI_INT_W1S 0x8 172762306a36Sopenharmony_ci#define L2C_MCI_INT_ENA_W1C 0x20 172862306a36Sopenharmony_ci 172962306a36Sopenharmony_ci#define L2C_MCI_INT_ENA_ALL (L2C_MCI_INT_VBFSBE | L2C_MCI_INT_VBFDBE) 173062306a36Sopenharmony_ci 173162306a36Sopenharmony_ci#define L2C_MCI_INT_ENA_W1S 0x28 173262306a36Sopenharmony_ci 173362306a36Sopenharmony_ci#define L2C_MCI_ERR 0x10000 173462306a36Sopenharmony_ci 173562306a36Sopenharmony_ci#define L2C_MESSAGE_SIZE SZ_1K 173662306a36Sopenharmony_ci#define L2C_OTHER_SIZE (50 * ARRAY_SIZE(l2_tad_errors)) 173762306a36Sopenharmony_ci 173862306a36Sopenharmony_cistruct l2c_err_ctx { 173962306a36Sopenharmony_ci char *reg_ext_name; 174062306a36Sopenharmony_ci u64 reg_int; 174162306a36Sopenharmony_ci u64 reg_ext; 174262306a36Sopenharmony_ci}; 174362306a36Sopenharmony_ci 174462306a36Sopenharmony_cistruct thunderx_l2c { 174562306a36Sopenharmony_ci void __iomem *regs; 174662306a36Sopenharmony_ci struct pci_dev *pdev; 174762306a36Sopenharmony_ci struct edac_device_ctl_info *edac_dev; 174862306a36Sopenharmony_ci 174962306a36Sopenharmony_ci struct dentry *debugfs; 175062306a36Sopenharmony_ci 175162306a36Sopenharmony_ci int index; 175262306a36Sopenharmony_ci 175362306a36Sopenharmony_ci struct msix_entry msix_ent; 175462306a36Sopenharmony_ci 175562306a36Sopenharmony_ci struct l2c_err_ctx err_ctx[RING_ENTRIES]; 175662306a36Sopenharmony_ci unsigned long ring_head; 175762306a36Sopenharmony_ci unsigned long ring_tail; 175862306a36Sopenharmony_ci}; 175962306a36Sopenharmony_ci 176062306a36Sopenharmony_cistatic irqreturn_t thunderx_l2c_tad_isr(int irq, void *irq_id) 176162306a36Sopenharmony_ci{ 176262306a36Sopenharmony_ci struct msix_entry *msix = irq_id; 176362306a36Sopenharmony_ci struct thunderx_l2c *tad = container_of(msix, struct thunderx_l2c, 176462306a36Sopenharmony_ci msix_ent); 176562306a36Sopenharmony_ci 176662306a36Sopenharmony_ci unsigned long head = ring_pos(tad->ring_head, ARRAY_SIZE(tad->err_ctx)); 176762306a36Sopenharmony_ci struct l2c_err_ctx *ctx = &tad->err_ctx[head]; 176862306a36Sopenharmony_ci 176962306a36Sopenharmony_ci ctx->reg_int = readq(tad->regs + L2C_TAD_INT_W1C); 177062306a36Sopenharmony_ci 177162306a36Sopenharmony_ci if (ctx->reg_int & L2C_TAD_INT_ECC) { 177262306a36Sopenharmony_ci ctx->reg_ext_name = "TQD_ERR"; 177362306a36Sopenharmony_ci ctx->reg_ext = readq(tad->regs + L2C_TAD_TQD_ERR); 177462306a36Sopenharmony_ci } else if (ctx->reg_int & L2C_TAD_INT_TAG) { 177562306a36Sopenharmony_ci ctx->reg_ext_name = "TTG_ERR"; 177662306a36Sopenharmony_ci ctx->reg_ext = readq(tad->regs + L2C_TAD_TTG_ERR); 177762306a36Sopenharmony_ci } else if (ctx->reg_int & L2C_TAD_INT_LFBTO) { 177862306a36Sopenharmony_ci ctx->reg_ext_name = "TIMEOUT"; 177962306a36Sopenharmony_ci ctx->reg_ext = readq(tad->regs + L2C_TAD_TIMEOUT); 178062306a36Sopenharmony_ci } else if (ctx->reg_int & L2C_TAD_INT_DISOCI) { 178162306a36Sopenharmony_ci ctx->reg_ext_name = "ERR"; 178262306a36Sopenharmony_ci ctx->reg_ext = readq(tad->regs + L2C_TAD_ERR); 178362306a36Sopenharmony_ci } 178462306a36Sopenharmony_ci 178562306a36Sopenharmony_ci writeq(ctx->reg_int, tad->regs + L2C_TAD_INT_W1C); 178662306a36Sopenharmony_ci 178762306a36Sopenharmony_ci tad->ring_head++; 178862306a36Sopenharmony_ci 178962306a36Sopenharmony_ci return IRQ_WAKE_THREAD; 179062306a36Sopenharmony_ci} 179162306a36Sopenharmony_ci 179262306a36Sopenharmony_cistatic irqreturn_t thunderx_l2c_cbc_isr(int irq, void *irq_id) 179362306a36Sopenharmony_ci{ 179462306a36Sopenharmony_ci struct msix_entry *msix = irq_id; 179562306a36Sopenharmony_ci struct thunderx_l2c *cbc = container_of(msix, struct thunderx_l2c, 179662306a36Sopenharmony_ci msix_ent); 179762306a36Sopenharmony_ci 179862306a36Sopenharmony_ci unsigned long head = ring_pos(cbc->ring_head, ARRAY_SIZE(cbc->err_ctx)); 179962306a36Sopenharmony_ci struct l2c_err_ctx *ctx = &cbc->err_ctx[head]; 180062306a36Sopenharmony_ci 180162306a36Sopenharmony_ci ctx->reg_int = readq(cbc->regs + L2C_CBC_INT_W1C); 180262306a36Sopenharmony_ci 180362306a36Sopenharmony_ci if (ctx->reg_int & L2C_CBC_INT_RSD) { 180462306a36Sopenharmony_ci ctx->reg_ext_name = "RSDERR"; 180562306a36Sopenharmony_ci ctx->reg_ext = readq(cbc->regs + L2C_CBC_RSDERR); 180662306a36Sopenharmony_ci } else if (ctx->reg_int & L2C_CBC_INT_MIB) { 180762306a36Sopenharmony_ci ctx->reg_ext_name = "MIBERR"; 180862306a36Sopenharmony_ci ctx->reg_ext = readq(cbc->regs + L2C_CBC_MIBERR); 180962306a36Sopenharmony_ci } else if (ctx->reg_int & L2C_CBC_INT_IODISOCI) { 181062306a36Sopenharmony_ci ctx->reg_ext_name = "IODISOCIERR"; 181162306a36Sopenharmony_ci ctx->reg_ext = readq(cbc->regs + L2C_CBC_IODISOCIERR); 181262306a36Sopenharmony_ci } 181362306a36Sopenharmony_ci 181462306a36Sopenharmony_ci writeq(ctx->reg_int, cbc->regs + L2C_CBC_INT_W1C); 181562306a36Sopenharmony_ci 181662306a36Sopenharmony_ci cbc->ring_head++; 181762306a36Sopenharmony_ci 181862306a36Sopenharmony_ci return IRQ_WAKE_THREAD; 181962306a36Sopenharmony_ci} 182062306a36Sopenharmony_ci 182162306a36Sopenharmony_cistatic irqreturn_t thunderx_l2c_mci_isr(int irq, void *irq_id) 182262306a36Sopenharmony_ci{ 182362306a36Sopenharmony_ci struct msix_entry *msix = irq_id; 182462306a36Sopenharmony_ci struct thunderx_l2c *mci = container_of(msix, struct thunderx_l2c, 182562306a36Sopenharmony_ci msix_ent); 182662306a36Sopenharmony_ci 182762306a36Sopenharmony_ci unsigned long head = ring_pos(mci->ring_head, ARRAY_SIZE(mci->err_ctx)); 182862306a36Sopenharmony_ci struct l2c_err_ctx *ctx = &mci->err_ctx[head]; 182962306a36Sopenharmony_ci 183062306a36Sopenharmony_ci ctx->reg_int = readq(mci->regs + L2C_MCI_INT_W1C); 183162306a36Sopenharmony_ci ctx->reg_ext = readq(mci->regs + L2C_MCI_ERR); 183262306a36Sopenharmony_ci 183362306a36Sopenharmony_ci writeq(ctx->reg_int, mci->regs + L2C_MCI_INT_W1C); 183462306a36Sopenharmony_ci 183562306a36Sopenharmony_ci ctx->reg_ext_name = "ERR"; 183662306a36Sopenharmony_ci 183762306a36Sopenharmony_ci mci->ring_head++; 183862306a36Sopenharmony_ci 183962306a36Sopenharmony_ci return IRQ_WAKE_THREAD; 184062306a36Sopenharmony_ci} 184162306a36Sopenharmony_ci 184262306a36Sopenharmony_cistatic irqreturn_t thunderx_l2c_threaded_isr(int irq, void *irq_id) 184362306a36Sopenharmony_ci{ 184462306a36Sopenharmony_ci struct msix_entry *msix = irq_id; 184562306a36Sopenharmony_ci struct thunderx_l2c *l2c = container_of(msix, struct thunderx_l2c, 184662306a36Sopenharmony_ci msix_ent); 184762306a36Sopenharmony_ci 184862306a36Sopenharmony_ci unsigned long tail = ring_pos(l2c->ring_tail, ARRAY_SIZE(l2c->err_ctx)); 184962306a36Sopenharmony_ci struct l2c_err_ctx *ctx = &l2c->err_ctx[tail]; 185062306a36Sopenharmony_ci irqreturn_t ret = IRQ_NONE; 185162306a36Sopenharmony_ci 185262306a36Sopenharmony_ci u64 mask_ue, mask_ce; 185362306a36Sopenharmony_ci const struct error_descr *l2_errors; 185462306a36Sopenharmony_ci char *reg_int_name; 185562306a36Sopenharmony_ci 185662306a36Sopenharmony_ci char *msg; 185762306a36Sopenharmony_ci char *other; 185862306a36Sopenharmony_ci 185962306a36Sopenharmony_ci msg = kmalloc(OCX_MESSAGE_SIZE, GFP_KERNEL); 186062306a36Sopenharmony_ci other = kmalloc(OCX_OTHER_SIZE, GFP_KERNEL); 186162306a36Sopenharmony_ci 186262306a36Sopenharmony_ci if (!msg || !other) 186362306a36Sopenharmony_ci goto err_free; 186462306a36Sopenharmony_ci 186562306a36Sopenharmony_ci switch (l2c->pdev->device) { 186662306a36Sopenharmony_ci case PCI_DEVICE_ID_THUNDER_L2C_TAD: 186762306a36Sopenharmony_ci reg_int_name = "L2C_TAD_INT"; 186862306a36Sopenharmony_ci mask_ue = L2C_TAD_INT_UE; 186962306a36Sopenharmony_ci mask_ce = L2C_TAD_INT_CE; 187062306a36Sopenharmony_ci l2_errors = l2_tad_errors; 187162306a36Sopenharmony_ci break; 187262306a36Sopenharmony_ci case PCI_DEVICE_ID_THUNDER_L2C_CBC: 187362306a36Sopenharmony_ci reg_int_name = "L2C_CBC_INT"; 187462306a36Sopenharmony_ci mask_ue = L2C_CBC_INT_UE; 187562306a36Sopenharmony_ci mask_ce = L2C_CBC_INT_CE; 187662306a36Sopenharmony_ci l2_errors = l2_cbc_errors; 187762306a36Sopenharmony_ci break; 187862306a36Sopenharmony_ci case PCI_DEVICE_ID_THUNDER_L2C_MCI: 187962306a36Sopenharmony_ci reg_int_name = "L2C_MCI_INT"; 188062306a36Sopenharmony_ci mask_ue = L2C_MCI_INT_VBFDBE; 188162306a36Sopenharmony_ci mask_ce = L2C_MCI_INT_VBFSBE; 188262306a36Sopenharmony_ci l2_errors = l2_mci_errors; 188362306a36Sopenharmony_ci break; 188462306a36Sopenharmony_ci default: 188562306a36Sopenharmony_ci dev_err(&l2c->pdev->dev, "Unsupported device: %04x\n", 188662306a36Sopenharmony_ci l2c->pdev->device); 188762306a36Sopenharmony_ci goto err_free; 188862306a36Sopenharmony_ci } 188962306a36Sopenharmony_ci 189062306a36Sopenharmony_ci while (CIRC_CNT(l2c->ring_head, l2c->ring_tail, 189162306a36Sopenharmony_ci ARRAY_SIZE(l2c->err_ctx))) { 189262306a36Sopenharmony_ci snprintf(msg, L2C_MESSAGE_SIZE, 189362306a36Sopenharmony_ci "%s: %s: %016llx, %s: %016llx", 189462306a36Sopenharmony_ci l2c->edac_dev->ctl_name, reg_int_name, ctx->reg_int, 189562306a36Sopenharmony_ci ctx->reg_ext_name, ctx->reg_ext); 189662306a36Sopenharmony_ci 189762306a36Sopenharmony_ci decode_register(other, L2C_OTHER_SIZE, l2_errors, ctx->reg_int); 189862306a36Sopenharmony_ci 189962306a36Sopenharmony_ci strlcat(msg, other, L2C_MESSAGE_SIZE); 190062306a36Sopenharmony_ci 190162306a36Sopenharmony_ci if (ctx->reg_int & mask_ue) 190262306a36Sopenharmony_ci edac_device_handle_ue(l2c->edac_dev, 0, 0, msg); 190362306a36Sopenharmony_ci else if (ctx->reg_int & mask_ce) 190462306a36Sopenharmony_ci edac_device_handle_ce(l2c->edac_dev, 0, 0, msg); 190562306a36Sopenharmony_ci 190662306a36Sopenharmony_ci l2c->ring_tail++; 190762306a36Sopenharmony_ci } 190862306a36Sopenharmony_ci 190962306a36Sopenharmony_ci ret = IRQ_HANDLED; 191062306a36Sopenharmony_ci 191162306a36Sopenharmony_cierr_free: 191262306a36Sopenharmony_ci kfree(other); 191362306a36Sopenharmony_ci kfree(msg); 191462306a36Sopenharmony_ci 191562306a36Sopenharmony_ci return ret; 191662306a36Sopenharmony_ci} 191762306a36Sopenharmony_ci 191862306a36Sopenharmony_ci#define L2C_DEBUGFS_ATTR(_name, _reg) DEBUGFS_REG_ATTR(l2c, _name, _reg) 191962306a36Sopenharmony_ci 192062306a36Sopenharmony_ciL2C_DEBUGFS_ATTR(tad_int, L2C_TAD_INT_W1S); 192162306a36Sopenharmony_ci 192262306a36Sopenharmony_cistatic struct debugfs_entry *l2c_tad_dfs_ents[] = { 192362306a36Sopenharmony_ci &debugfs_tad_int, 192462306a36Sopenharmony_ci}; 192562306a36Sopenharmony_ci 192662306a36Sopenharmony_ciL2C_DEBUGFS_ATTR(cbc_int, L2C_CBC_INT_W1S); 192762306a36Sopenharmony_ci 192862306a36Sopenharmony_cistatic struct debugfs_entry *l2c_cbc_dfs_ents[] = { 192962306a36Sopenharmony_ci &debugfs_cbc_int, 193062306a36Sopenharmony_ci}; 193162306a36Sopenharmony_ci 193262306a36Sopenharmony_ciL2C_DEBUGFS_ATTR(mci_int, L2C_MCI_INT_W1S); 193362306a36Sopenharmony_ci 193462306a36Sopenharmony_cistatic struct debugfs_entry *l2c_mci_dfs_ents[] = { 193562306a36Sopenharmony_ci &debugfs_mci_int, 193662306a36Sopenharmony_ci}; 193762306a36Sopenharmony_ci 193862306a36Sopenharmony_cistatic const struct pci_device_id thunderx_l2c_pci_tbl[] = { 193962306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, PCI_DEVICE_ID_THUNDER_L2C_TAD), }, 194062306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, PCI_DEVICE_ID_THUNDER_L2C_CBC), }, 194162306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, PCI_DEVICE_ID_THUNDER_L2C_MCI), }, 194262306a36Sopenharmony_ci { 0, }, 194362306a36Sopenharmony_ci}; 194462306a36Sopenharmony_ci 194562306a36Sopenharmony_cistatic int thunderx_l2c_probe(struct pci_dev *pdev, 194662306a36Sopenharmony_ci const struct pci_device_id *id) 194762306a36Sopenharmony_ci{ 194862306a36Sopenharmony_ci struct thunderx_l2c *l2c; 194962306a36Sopenharmony_ci struct edac_device_ctl_info *edac_dev; 195062306a36Sopenharmony_ci struct debugfs_entry **l2c_devattr; 195162306a36Sopenharmony_ci size_t dfs_entries; 195262306a36Sopenharmony_ci irqreturn_t (*thunderx_l2c_isr)(int, void *) = NULL; 195362306a36Sopenharmony_ci char name[32]; 195462306a36Sopenharmony_ci const char *fmt; 195562306a36Sopenharmony_ci u64 reg_en_offs, reg_en_mask; 195662306a36Sopenharmony_ci int idx; 195762306a36Sopenharmony_ci int ret; 195862306a36Sopenharmony_ci 195962306a36Sopenharmony_ci ret = pcim_enable_device(pdev); 196062306a36Sopenharmony_ci if (ret) { 196162306a36Sopenharmony_ci dev_err(&pdev->dev, "Cannot enable PCI device: %d\n", ret); 196262306a36Sopenharmony_ci return ret; 196362306a36Sopenharmony_ci } 196462306a36Sopenharmony_ci 196562306a36Sopenharmony_ci ret = pcim_iomap_regions(pdev, BIT(0), "thunderx_l2c"); 196662306a36Sopenharmony_ci if (ret) { 196762306a36Sopenharmony_ci dev_err(&pdev->dev, "Cannot map PCI resources: %d\n", ret); 196862306a36Sopenharmony_ci return ret; 196962306a36Sopenharmony_ci } 197062306a36Sopenharmony_ci 197162306a36Sopenharmony_ci switch (pdev->device) { 197262306a36Sopenharmony_ci case PCI_DEVICE_ID_THUNDER_L2C_TAD: 197362306a36Sopenharmony_ci thunderx_l2c_isr = thunderx_l2c_tad_isr; 197462306a36Sopenharmony_ci l2c_devattr = l2c_tad_dfs_ents; 197562306a36Sopenharmony_ci dfs_entries = ARRAY_SIZE(l2c_tad_dfs_ents); 197662306a36Sopenharmony_ci fmt = "L2C-TAD%d"; 197762306a36Sopenharmony_ci reg_en_offs = L2C_TAD_INT_ENA_W1S; 197862306a36Sopenharmony_ci reg_en_mask = L2C_TAD_INT_ENA_ALL; 197962306a36Sopenharmony_ci break; 198062306a36Sopenharmony_ci case PCI_DEVICE_ID_THUNDER_L2C_CBC: 198162306a36Sopenharmony_ci thunderx_l2c_isr = thunderx_l2c_cbc_isr; 198262306a36Sopenharmony_ci l2c_devattr = l2c_cbc_dfs_ents; 198362306a36Sopenharmony_ci dfs_entries = ARRAY_SIZE(l2c_cbc_dfs_ents); 198462306a36Sopenharmony_ci fmt = "L2C-CBC%d"; 198562306a36Sopenharmony_ci reg_en_offs = L2C_CBC_INT_ENA_W1S; 198662306a36Sopenharmony_ci reg_en_mask = L2C_CBC_INT_ENA_ALL; 198762306a36Sopenharmony_ci break; 198862306a36Sopenharmony_ci case PCI_DEVICE_ID_THUNDER_L2C_MCI: 198962306a36Sopenharmony_ci thunderx_l2c_isr = thunderx_l2c_mci_isr; 199062306a36Sopenharmony_ci l2c_devattr = l2c_mci_dfs_ents; 199162306a36Sopenharmony_ci dfs_entries = ARRAY_SIZE(l2c_mci_dfs_ents); 199262306a36Sopenharmony_ci fmt = "L2C-MCI%d"; 199362306a36Sopenharmony_ci reg_en_offs = L2C_MCI_INT_ENA_W1S; 199462306a36Sopenharmony_ci reg_en_mask = L2C_MCI_INT_ENA_ALL; 199562306a36Sopenharmony_ci break; 199662306a36Sopenharmony_ci default: 199762306a36Sopenharmony_ci //Should never ever get here 199862306a36Sopenharmony_ci dev_err(&pdev->dev, "Unsupported PCI device: %04x\n", 199962306a36Sopenharmony_ci pdev->device); 200062306a36Sopenharmony_ci return -EINVAL; 200162306a36Sopenharmony_ci } 200262306a36Sopenharmony_ci 200362306a36Sopenharmony_ci idx = edac_device_alloc_index(); 200462306a36Sopenharmony_ci snprintf(name, sizeof(name), fmt, idx); 200562306a36Sopenharmony_ci 200662306a36Sopenharmony_ci edac_dev = edac_device_alloc_ctl_info(sizeof(struct thunderx_l2c), 200762306a36Sopenharmony_ci name, 1, "L2C", 1, 0, 200862306a36Sopenharmony_ci NULL, 0, idx); 200962306a36Sopenharmony_ci if (!edac_dev) { 201062306a36Sopenharmony_ci dev_err(&pdev->dev, "Cannot allocate EDAC device\n"); 201162306a36Sopenharmony_ci return -ENOMEM; 201262306a36Sopenharmony_ci } 201362306a36Sopenharmony_ci 201462306a36Sopenharmony_ci l2c = edac_dev->pvt_info; 201562306a36Sopenharmony_ci l2c->edac_dev = edac_dev; 201662306a36Sopenharmony_ci 201762306a36Sopenharmony_ci l2c->regs = pcim_iomap_table(pdev)[0]; 201862306a36Sopenharmony_ci if (!l2c->regs) { 201962306a36Sopenharmony_ci dev_err(&pdev->dev, "Cannot map PCI resources\n"); 202062306a36Sopenharmony_ci ret = -ENODEV; 202162306a36Sopenharmony_ci goto err_free; 202262306a36Sopenharmony_ci } 202362306a36Sopenharmony_ci 202462306a36Sopenharmony_ci l2c->pdev = pdev; 202562306a36Sopenharmony_ci 202662306a36Sopenharmony_ci l2c->ring_head = 0; 202762306a36Sopenharmony_ci l2c->ring_tail = 0; 202862306a36Sopenharmony_ci 202962306a36Sopenharmony_ci l2c->msix_ent.entry = 0; 203062306a36Sopenharmony_ci l2c->msix_ent.vector = 0; 203162306a36Sopenharmony_ci 203262306a36Sopenharmony_ci ret = pci_enable_msix_exact(pdev, &l2c->msix_ent, 1); 203362306a36Sopenharmony_ci if (ret) { 203462306a36Sopenharmony_ci dev_err(&pdev->dev, "Cannot enable interrupt: %d\n", ret); 203562306a36Sopenharmony_ci goto err_free; 203662306a36Sopenharmony_ci } 203762306a36Sopenharmony_ci 203862306a36Sopenharmony_ci ret = devm_request_threaded_irq(&pdev->dev, l2c->msix_ent.vector, 203962306a36Sopenharmony_ci thunderx_l2c_isr, 204062306a36Sopenharmony_ci thunderx_l2c_threaded_isr, 204162306a36Sopenharmony_ci 0, "[EDAC] ThunderX L2C", 204262306a36Sopenharmony_ci &l2c->msix_ent); 204362306a36Sopenharmony_ci if (ret) 204462306a36Sopenharmony_ci goto err_free; 204562306a36Sopenharmony_ci 204662306a36Sopenharmony_ci edac_dev->dev = &pdev->dev; 204762306a36Sopenharmony_ci edac_dev->dev_name = dev_name(&pdev->dev); 204862306a36Sopenharmony_ci edac_dev->mod_name = "thunderx-l2c"; 204962306a36Sopenharmony_ci edac_dev->ctl_name = "thunderx-l2c"; 205062306a36Sopenharmony_ci 205162306a36Sopenharmony_ci ret = edac_device_add_device(edac_dev); 205262306a36Sopenharmony_ci if (ret) { 205362306a36Sopenharmony_ci dev_err(&pdev->dev, "Cannot add EDAC device: %d\n", ret); 205462306a36Sopenharmony_ci goto err_free; 205562306a36Sopenharmony_ci } 205662306a36Sopenharmony_ci 205762306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_EDAC_DEBUG)) { 205862306a36Sopenharmony_ci l2c->debugfs = edac_debugfs_create_dir(pdev->dev.kobj.name); 205962306a36Sopenharmony_ci 206062306a36Sopenharmony_ci ret = thunderx_create_debugfs_nodes(l2c->debugfs, l2c_devattr, 206162306a36Sopenharmony_ci l2c, dfs_entries); 206262306a36Sopenharmony_ci 206362306a36Sopenharmony_ci if (ret != dfs_entries) { 206462306a36Sopenharmony_ci dev_warn(&pdev->dev, "Error creating debugfs entries: %d%s\n", 206562306a36Sopenharmony_ci ret, ret >= 0 ? " created" : ""); 206662306a36Sopenharmony_ci } 206762306a36Sopenharmony_ci } 206862306a36Sopenharmony_ci 206962306a36Sopenharmony_ci pci_set_drvdata(pdev, edac_dev); 207062306a36Sopenharmony_ci 207162306a36Sopenharmony_ci writeq(reg_en_mask, l2c->regs + reg_en_offs); 207262306a36Sopenharmony_ci 207362306a36Sopenharmony_ci return 0; 207462306a36Sopenharmony_ci 207562306a36Sopenharmony_cierr_free: 207662306a36Sopenharmony_ci edac_device_free_ctl_info(edac_dev); 207762306a36Sopenharmony_ci 207862306a36Sopenharmony_ci return ret; 207962306a36Sopenharmony_ci} 208062306a36Sopenharmony_ci 208162306a36Sopenharmony_cistatic void thunderx_l2c_remove(struct pci_dev *pdev) 208262306a36Sopenharmony_ci{ 208362306a36Sopenharmony_ci struct edac_device_ctl_info *edac_dev = pci_get_drvdata(pdev); 208462306a36Sopenharmony_ci struct thunderx_l2c *l2c = edac_dev->pvt_info; 208562306a36Sopenharmony_ci 208662306a36Sopenharmony_ci switch (pdev->device) { 208762306a36Sopenharmony_ci case PCI_DEVICE_ID_THUNDER_L2C_TAD: 208862306a36Sopenharmony_ci writeq(L2C_TAD_INT_ENA_ALL, l2c->regs + L2C_TAD_INT_ENA_W1C); 208962306a36Sopenharmony_ci break; 209062306a36Sopenharmony_ci case PCI_DEVICE_ID_THUNDER_L2C_CBC: 209162306a36Sopenharmony_ci writeq(L2C_CBC_INT_ENA_ALL, l2c->regs + L2C_CBC_INT_ENA_W1C); 209262306a36Sopenharmony_ci break; 209362306a36Sopenharmony_ci case PCI_DEVICE_ID_THUNDER_L2C_MCI: 209462306a36Sopenharmony_ci writeq(L2C_MCI_INT_ENA_ALL, l2c->regs + L2C_MCI_INT_ENA_W1C); 209562306a36Sopenharmony_ci break; 209662306a36Sopenharmony_ci } 209762306a36Sopenharmony_ci 209862306a36Sopenharmony_ci edac_debugfs_remove_recursive(l2c->debugfs); 209962306a36Sopenharmony_ci 210062306a36Sopenharmony_ci edac_device_del_device(&pdev->dev); 210162306a36Sopenharmony_ci edac_device_free_ctl_info(edac_dev); 210262306a36Sopenharmony_ci} 210362306a36Sopenharmony_ci 210462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, thunderx_l2c_pci_tbl); 210562306a36Sopenharmony_ci 210662306a36Sopenharmony_cistatic struct pci_driver thunderx_l2c_driver = { 210762306a36Sopenharmony_ci .name = "thunderx_l2c_edac", 210862306a36Sopenharmony_ci .probe = thunderx_l2c_probe, 210962306a36Sopenharmony_ci .remove = thunderx_l2c_remove, 211062306a36Sopenharmony_ci .id_table = thunderx_l2c_pci_tbl, 211162306a36Sopenharmony_ci}; 211262306a36Sopenharmony_ci 211362306a36Sopenharmony_cistatic int __init thunderx_edac_init(void) 211462306a36Sopenharmony_ci{ 211562306a36Sopenharmony_ci int rc = 0; 211662306a36Sopenharmony_ci 211762306a36Sopenharmony_ci if (ghes_get_devices()) 211862306a36Sopenharmony_ci return -EBUSY; 211962306a36Sopenharmony_ci 212062306a36Sopenharmony_ci rc = pci_register_driver(&thunderx_lmc_driver); 212162306a36Sopenharmony_ci if (rc) 212262306a36Sopenharmony_ci return rc; 212362306a36Sopenharmony_ci 212462306a36Sopenharmony_ci rc = pci_register_driver(&thunderx_ocx_driver); 212562306a36Sopenharmony_ci if (rc) 212662306a36Sopenharmony_ci goto err_lmc; 212762306a36Sopenharmony_ci 212862306a36Sopenharmony_ci rc = pci_register_driver(&thunderx_l2c_driver); 212962306a36Sopenharmony_ci if (rc) 213062306a36Sopenharmony_ci goto err_ocx; 213162306a36Sopenharmony_ci 213262306a36Sopenharmony_ci return rc; 213362306a36Sopenharmony_cierr_ocx: 213462306a36Sopenharmony_ci pci_unregister_driver(&thunderx_ocx_driver); 213562306a36Sopenharmony_cierr_lmc: 213662306a36Sopenharmony_ci pci_unregister_driver(&thunderx_lmc_driver); 213762306a36Sopenharmony_ci 213862306a36Sopenharmony_ci return rc; 213962306a36Sopenharmony_ci} 214062306a36Sopenharmony_ci 214162306a36Sopenharmony_cistatic void __exit thunderx_edac_exit(void) 214262306a36Sopenharmony_ci{ 214362306a36Sopenharmony_ci pci_unregister_driver(&thunderx_l2c_driver); 214462306a36Sopenharmony_ci pci_unregister_driver(&thunderx_ocx_driver); 214562306a36Sopenharmony_ci pci_unregister_driver(&thunderx_lmc_driver); 214662306a36Sopenharmony_ci 214762306a36Sopenharmony_ci} 214862306a36Sopenharmony_ci 214962306a36Sopenharmony_cimodule_init(thunderx_edac_init); 215062306a36Sopenharmony_cimodule_exit(thunderx_edac_exit); 215162306a36Sopenharmony_ci 215262306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 215362306a36Sopenharmony_ciMODULE_AUTHOR("Cavium, Inc."); 215462306a36Sopenharmony_ciMODULE_DESCRIPTION("EDAC Driver for Cavium ThunderX"); 2155