cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

qcom_edac.c (11212B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Copyright (c) 2018, The Linux Foundation. All rights reserved.
      4 */
      5
      6#include <linux/edac.h>
      7#include <linux/interrupt.h>
      8#include <linux/kernel.h>
      9#include <linux/of.h>
     10#include <linux/platform_device.h>
     11#include <linux/regmap.h>
     12#include <linux/soc/qcom/llcc-qcom.h>
     13
     14#include "edac_mc.h"
     15#include "edac_device.h"
     16
     17#define EDAC_LLCC                       "qcom_llcc"
     18
     19#define LLCC_ERP_PANIC_ON_UE            1
     20
     21#define TRP_SYN_REG_CNT                 6
     22#define DRP_SYN_REG_CNT                 8
     23
     24#define LLCC_COMMON_STATUS0             0x0003000c
     25#define LLCC_LB_CNT_MASK                GENMASK(31, 28)
     26#define LLCC_LB_CNT_SHIFT               28
     27
     28/* Single & double bit syndrome register offsets */
     29#define TRP_ECC_SB_ERR_SYN0             0x0002304c
     30#define TRP_ECC_DB_ERR_SYN0             0x00020370
     31#define DRP_ECC_SB_ERR_SYN0             0x0004204c
     32#define DRP_ECC_DB_ERR_SYN0             0x00042070
     33
     34/* Error register offsets */
     35#define TRP_ECC_ERROR_STATUS1           0x00020348
     36#define TRP_ECC_ERROR_STATUS0           0x00020344
     37#define DRP_ECC_ERROR_STATUS1           0x00042048
     38#define DRP_ECC_ERROR_STATUS0           0x00042044
     39
     40/* TRP, DRP interrupt register offsets */
     41#define DRP_INTERRUPT_STATUS            0x00041000
     42#define TRP_INTERRUPT_0_STATUS          0x00020480
     43#define DRP_INTERRUPT_CLEAR             0x00041008
     44#define DRP_ECC_ERROR_CNTR_CLEAR        0x00040004
     45#define TRP_INTERRUPT_0_CLEAR           0x00020484
     46#define TRP_ECC_ERROR_CNTR_CLEAR        0x00020440
     47
     48/* Mask and shift macros */
     49#define ECC_DB_ERR_COUNT_MASK           GENMASK(4, 0)
     50#define ECC_DB_ERR_WAYS_MASK            GENMASK(31, 16)
     51#define ECC_DB_ERR_WAYS_SHIFT           BIT(4)
     52
     53#define ECC_SB_ERR_COUNT_MASK           GENMASK(23, 16)
     54#define ECC_SB_ERR_COUNT_SHIFT          BIT(4)
     55#define ECC_SB_ERR_WAYS_MASK            GENMASK(15, 0)
     56
     57#define SB_ECC_ERROR                    BIT(0)
     58#define DB_ECC_ERROR                    BIT(1)
     59
     60#define DRP_TRP_INT_CLEAR               GENMASK(1, 0)
     61#define DRP_TRP_CNT_CLEAR               GENMASK(1, 0)
     62
     63/* Config registers offsets*/
     64#define DRP_ECC_ERROR_CFG               0x00040000
     65
     66/* Tag RAM, Data RAM interrupt register offsets */
     67#define CMN_INTERRUPT_0_ENABLE          0x0003001c
     68#define CMN_INTERRUPT_2_ENABLE          0x0003003c
     69#define TRP_INTERRUPT_0_ENABLE          0x00020488
     70#define DRP_INTERRUPT_ENABLE            0x0004100c
     71
     72#define SB_ERROR_THRESHOLD              0x1
     73#define SB_ERROR_THRESHOLD_SHIFT        24
     74#define SB_DB_TRP_INTERRUPT_ENABLE      0x3
     75#define TRP0_INTERRUPT_ENABLE           0x1
     76#define DRP0_INTERRUPT_ENABLE           BIT(6)
     77#define SB_DB_DRP_INTERRUPT_ENABLE      0x3
     78
     79enum {
     80	LLCC_DRAM_CE = 0,
     81	LLCC_DRAM_UE,
     82	LLCC_TRAM_CE,
     83	LLCC_TRAM_UE,
     84};
     85
     86static const struct llcc_edac_reg_data edac_reg_data[] = {
     87	[LLCC_DRAM_CE] = {
     88		.name = "DRAM Single-bit",
     89		.synd_reg = DRP_ECC_SB_ERR_SYN0,
     90		.count_status_reg = DRP_ECC_ERROR_STATUS1,
     91		.ways_status_reg = DRP_ECC_ERROR_STATUS0,
     92		.reg_cnt = DRP_SYN_REG_CNT,
     93		.count_mask = ECC_SB_ERR_COUNT_MASK,
     94		.ways_mask = ECC_SB_ERR_WAYS_MASK,
     95		.count_shift = ECC_SB_ERR_COUNT_SHIFT,
     96	},
     97	[LLCC_DRAM_UE] = {
     98		.name = "DRAM Double-bit",
     99		.synd_reg = DRP_ECC_DB_ERR_SYN0,
    100		.count_status_reg = DRP_ECC_ERROR_STATUS1,
    101		.ways_status_reg = DRP_ECC_ERROR_STATUS0,
    102		.reg_cnt = DRP_SYN_REG_CNT,
    103		.count_mask = ECC_DB_ERR_COUNT_MASK,
    104		.ways_mask = ECC_DB_ERR_WAYS_MASK,
    105		.ways_shift = ECC_DB_ERR_WAYS_SHIFT,
    106	},
    107	[LLCC_TRAM_CE] = {
    108		.name = "TRAM Single-bit",
    109		.synd_reg = TRP_ECC_SB_ERR_SYN0,
    110		.count_status_reg = TRP_ECC_ERROR_STATUS1,
    111		.ways_status_reg = TRP_ECC_ERROR_STATUS0,
    112		.reg_cnt = TRP_SYN_REG_CNT,
    113		.count_mask = ECC_SB_ERR_COUNT_MASK,
    114		.ways_mask = ECC_SB_ERR_WAYS_MASK,
    115		.count_shift = ECC_SB_ERR_COUNT_SHIFT,
    116	},
    117	[LLCC_TRAM_UE] = {
    118		.name = "TRAM Double-bit",
    119		.synd_reg = TRP_ECC_DB_ERR_SYN0,
    120		.count_status_reg = TRP_ECC_ERROR_STATUS1,
    121		.ways_status_reg = TRP_ECC_ERROR_STATUS0,
    122		.reg_cnt = TRP_SYN_REG_CNT,
    123		.count_mask = ECC_DB_ERR_COUNT_MASK,
    124		.ways_mask = ECC_DB_ERR_WAYS_MASK,
    125		.ways_shift = ECC_DB_ERR_WAYS_SHIFT,
    126	},
    127};
    128
    129static int qcom_llcc_core_setup(struct regmap *llcc_bcast_regmap)
    130{
    131	u32 sb_err_threshold;
    132	int ret;
    133
    134	/*
    135	 * Configure interrupt enable registers such that Tag, Data RAM related
    136	 * interrupts are propagated to interrupt controller for servicing
    137	 */
    138	ret = regmap_update_bits(llcc_bcast_regmap, CMN_INTERRUPT_2_ENABLE,
    139				 TRP0_INTERRUPT_ENABLE,
    140				 TRP0_INTERRUPT_ENABLE);
    141	if (ret)
    142		return ret;
    143
    144	ret = regmap_update_bits(llcc_bcast_regmap, TRP_INTERRUPT_0_ENABLE,
    145				 SB_DB_TRP_INTERRUPT_ENABLE,
    146				 SB_DB_TRP_INTERRUPT_ENABLE);
    147	if (ret)
    148		return ret;
    149
    150	sb_err_threshold = (SB_ERROR_THRESHOLD << SB_ERROR_THRESHOLD_SHIFT);
    151	ret = regmap_write(llcc_bcast_regmap, DRP_ECC_ERROR_CFG,
    152			   sb_err_threshold);
    153	if (ret)
    154		return ret;
    155
    156	ret = regmap_update_bits(llcc_bcast_regmap, CMN_INTERRUPT_2_ENABLE,
    157				 DRP0_INTERRUPT_ENABLE,
    158				 DRP0_INTERRUPT_ENABLE);
    159	if (ret)
    160		return ret;
    161
    162	ret = regmap_write(llcc_bcast_regmap, DRP_INTERRUPT_ENABLE,
    163			   SB_DB_DRP_INTERRUPT_ENABLE);
    164	return ret;
    165}
    166
    167/* Clear the error interrupt and counter registers */
    168static int
    169qcom_llcc_clear_error_status(int err_type, struct llcc_drv_data *drv)
    170{
    171	int ret = 0;
    172
    173	switch (err_type) {
    174	case LLCC_DRAM_CE:
    175	case LLCC_DRAM_UE:
    176		ret = regmap_write(drv->bcast_regmap, DRP_INTERRUPT_CLEAR,
    177				   DRP_TRP_INT_CLEAR);
    178		if (ret)
    179			return ret;
    180
    181		ret = regmap_write(drv->bcast_regmap, DRP_ECC_ERROR_CNTR_CLEAR,
    182				   DRP_TRP_CNT_CLEAR);
    183		if (ret)
    184			return ret;
    185		break;
    186	case LLCC_TRAM_CE:
    187	case LLCC_TRAM_UE:
    188		ret = regmap_write(drv->bcast_regmap, TRP_INTERRUPT_0_CLEAR,
    189				   DRP_TRP_INT_CLEAR);
    190		if (ret)
    191			return ret;
    192
    193		ret = regmap_write(drv->bcast_regmap, TRP_ECC_ERROR_CNTR_CLEAR,
    194				   DRP_TRP_CNT_CLEAR);
    195		if (ret)
    196			return ret;
    197		break;
    198	default:
    199		ret = -EINVAL;
    200		edac_printk(KERN_CRIT, EDAC_LLCC, "Unexpected error type: %d\n",
    201			    err_type);
    202	}
    203	return ret;
    204}
    205
    206/* Dump Syndrome registers data for Tag RAM, Data RAM bit errors*/
    207static int
    208dump_syn_reg_values(struct llcc_drv_data *drv, u32 bank, int err_type)
    209{
    210	struct llcc_edac_reg_data reg_data = edac_reg_data[err_type];
    211	int err_cnt, err_ways, ret, i;
    212	u32 synd_reg, synd_val;
    213
    214	for (i = 0; i < reg_data.reg_cnt; i++) {
    215		synd_reg = reg_data.synd_reg + (i * 4);
    216		ret = regmap_read(drv->regmap, drv->offsets[bank] + synd_reg,
    217				  &synd_val);
    218		if (ret)
    219			goto clear;
    220
    221		edac_printk(KERN_CRIT, EDAC_LLCC, "%s: ECC_SYN%d: 0x%8x\n",
    222			    reg_data.name, i, synd_val);
    223	}
    224
    225	ret = regmap_read(drv->regmap,
    226			  drv->offsets[bank] + reg_data.count_status_reg,
    227			  &err_cnt);
    228	if (ret)
    229		goto clear;
    230
    231	err_cnt &= reg_data.count_mask;
    232	err_cnt >>= reg_data.count_shift;
    233	edac_printk(KERN_CRIT, EDAC_LLCC, "%s: Error count: 0x%4x\n",
    234		    reg_data.name, err_cnt);
    235
    236	ret = regmap_read(drv->regmap,
    237			  drv->offsets[bank] + reg_data.ways_status_reg,
    238			  &err_ways);
    239	if (ret)
    240		goto clear;
    241
    242	err_ways &= reg_data.ways_mask;
    243	err_ways >>= reg_data.ways_shift;
    244
    245	edac_printk(KERN_CRIT, EDAC_LLCC, "%s: Error ways: 0x%4x\n",
    246		    reg_data.name, err_ways);
    247
    248clear:
    249	return qcom_llcc_clear_error_status(err_type, drv);
    250}
    251
    252static int
    253dump_syn_reg(struct edac_device_ctl_info *edev_ctl, int err_type, u32 bank)
    254{
    255	struct llcc_drv_data *drv = edev_ctl->pvt_info;
    256	int ret;
    257
    258	ret = dump_syn_reg_values(drv, bank, err_type);
    259	if (ret)
    260		return ret;
    261
    262	switch (err_type) {
    263	case LLCC_DRAM_CE:
    264		edac_device_handle_ce(edev_ctl, 0, bank,
    265				      "LLCC Data RAM correctable Error");
    266		break;
    267	case LLCC_DRAM_UE:
    268		edac_device_handle_ue(edev_ctl, 0, bank,
    269				      "LLCC Data RAM uncorrectable Error");
    270		break;
    271	case LLCC_TRAM_CE:
    272		edac_device_handle_ce(edev_ctl, 0, bank,
    273				      "LLCC Tag RAM correctable Error");
    274		break;
    275	case LLCC_TRAM_UE:
    276		edac_device_handle_ue(edev_ctl, 0, bank,
    277				      "LLCC Tag RAM uncorrectable Error");
    278		break;
    279	default:
    280		ret = -EINVAL;
    281		edac_printk(KERN_CRIT, EDAC_LLCC, "Unexpected error type: %d\n",
    282			    err_type);
    283	}
    284
    285	return ret;
    286}
    287
    288static irqreturn_t
    289llcc_ecc_irq_handler(int irq, void *edev_ctl)
    290{
    291	struct edac_device_ctl_info *edac_dev_ctl = edev_ctl;
    292	struct llcc_drv_data *drv = edac_dev_ctl->pvt_info;
    293	irqreturn_t irq_rc = IRQ_NONE;
    294	u32 drp_error, trp_error, i;
    295	int ret;
    296
    297	/* Iterate over the banks and look for Tag RAM or Data RAM errors */
    298	for (i = 0; i < drv->num_banks; i++) {
    299		ret = regmap_read(drv->regmap,
    300				  drv->offsets[i] + DRP_INTERRUPT_STATUS,
    301				  &drp_error);
    302
    303		if (!ret && (drp_error & SB_ECC_ERROR)) {
    304			edac_printk(KERN_CRIT, EDAC_LLCC,
    305				    "Single Bit Error detected in Data RAM\n");
    306			ret = dump_syn_reg(edev_ctl, LLCC_DRAM_CE, i);
    307		} else if (!ret && (drp_error & DB_ECC_ERROR)) {
    308			edac_printk(KERN_CRIT, EDAC_LLCC,
    309				    "Double Bit Error detected in Data RAM\n");
    310			ret = dump_syn_reg(edev_ctl, LLCC_DRAM_UE, i);
    311		}
    312		if (!ret)
    313			irq_rc = IRQ_HANDLED;
    314
    315		ret = regmap_read(drv->regmap,
    316				  drv->offsets[i] + TRP_INTERRUPT_0_STATUS,
    317				  &trp_error);
    318
    319		if (!ret && (trp_error & SB_ECC_ERROR)) {
    320			edac_printk(KERN_CRIT, EDAC_LLCC,
    321				    "Single Bit Error detected in Tag RAM\n");
    322			ret = dump_syn_reg(edev_ctl, LLCC_TRAM_CE, i);
    323		} else if (!ret && (trp_error & DB_ECC_ERROR)) {
    324			edac_printk(KERN_CRIT, EDAC_LLCC,
    325				    "Double Bit Error detected in Tag RAM\n");
    326			ret = dump_syn_reg(edev_ctl, LLCC_TRAM_UE, i);
    327		}
    328		if (!ret)
    329			irq_rc = IRQ_HANDLED;
    330	}
    331
    332	return irq_rc;
    333}
    334
    335static int qcom_llcc_edac_probe(struct platform_device *pdev)
    336{
    337	struct llcc_drv_data *llcc_driv_data = pdev->dev.platform_data;
    338	struct edac_device_ctl_info *edev_ctl;
    339	struct device *dev = &pdev->dev;
    340	int ecc_irq;
    341	int rc;
    342
    343	rc = qcom_llcc_core_setup(llcc_driv_data->bcast_regmap);
    344	if (rc)
    345		return rc;
    346
    347	/* Allocate edac control info */
    348	edev_ctl = edac_device_alloc_ctl_info(0, "qcom-llcc", 1, "bank",
    349					      llcc_driv_data->num_banks, 1,
    350					      NULL, 0,
    351					      edac_device_alloc_index());
    352
    353	if (!edev_ctl)
    354		return -ENOMEM;
    355
    356	edev_ctl->dev = dev;
    357	edev_ctl->mod_name = dev_name(dev);
    358	edev_ctl->dev_name = dev_name(dev);
    359	edev_ctl->ctl_name = "llcc";
    360	edev_ctl->panic_on_ue = LLCC_ERP_PANIC_ON_UE;
    361	edev_ctl->pvt_info = llcc_driv_data;
    362
    363	rc = edac_device_add_device(edev_ctl);
    364	if (rc)
    365		goto out_mem;
    366
    367	platform_set_drvdata(pdev, edev_ctl);
    368
    369	/* Request for ecc irq */
    370	ecc_irq = llcc_driv_data->ecc_irq;
    371	if (ecc_irq < 0) {
    372		rc = -ENODEV;
    373		goto out_dev;
    374	}
    375	rc = devm_request_irq(dev, ecc_irq, llcc_ecc_irq_handler,
    376			      IRQF_TRIGGER_HIGH, "llcc_ecc", edev_ctl);
    377	if (rc)
    378		goto out_dev;
    379
    380	return rc;
    381
    382out_dev:
    383	edac_device_del_device(edev_ctl->dev);
    384out_mem:
    385	edac_device_free_ctl_info(edev_ctl);
    386
    387	return rc;
    388}
    389
    390static int qcom_llcc_edac_remove(struct platform_device *pdev)
    391{
    392	struct edac_device_ctl_info *edev_ctl = dev_get_drvdata(&pdev->dev);
    393
    394	edac_device_del_device(edev_ctl->dev);
    395	edac_device_free_ctl_info(edev_ctl);
    396
    397	return 0;
    398}
    399
    400static struct platform_driver qcom_llcc_edac_driver = {
    401	.probe = qcom_llcc_edac_probe,
    402	.remove = qcom_llcc_edac_remove,
    403	.driver = {
    404		.name = "qcom_llcc_edac",
    405	},
    406};
    407module_platform_driver(qcom_llcc_edac_driver);
    408
    409MODULE_DESCRIPTION("QCOM EDAC driver");
    410MODULE_LICENSE("GPL v2");