Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Copyright (c) 2018, The Linux Foundation. All rights reserved.
0004  */
0005 
0006 #include <linux/edac.h>
0007 #include <linux/interrupt.h>
0008 #include <linux/kernel.h>
0009 #include <linux/of.h>
0010 #include <linux/platform_device.h>
0011 #include <linux/regmap.h>
0012 #include <linux/soc/qcom/llcc-qcom.h>
0013 
0014 #include "edac_mc.h"
0015 #include "edac_device.h"
0016 
0017 #define EDAC_LLCC                       "qcom_llcc"
0018 
0019 #define LLCC_ERP_PANIC_ON_UE            1
0020 
0021 #define TRP_SYN_REG_CNT                 6
0022 #define DRP_SYN_REG_CNT                 8
0023 
0024 #define LLCC_COMMON_STATUS0             0x0003000c
0025 #define LLCC_LB_CNT_MASK                GENMASK(31, 28)
0026 #define LLCC_LB_CNT_SHIFT               28
0027 
0028 /* Single & double bit syndrome register offsets */
0029 #define TRP_ECC_SB_ERR_SYN0             0x0002304c
0030 #define TRP_ECC_DB_ERR_SYN0             0x00020370
0031 #define DRP_ECC_SB_ERR_SYN0             0x0004204c
0032 #define DRP_ECC_DB_ERR_SYN0             0x00042070
0033 
0034 /* Error register offsets */
0035 #define TRP_ECC_ERROR_STATUS1           0x00020348
0036 #define TRP_ECC_ERROR_STATUS0           0x00020344
0037 #define DRP_ECC_ERROR_STATUS1           0x00042048
0038 #define DRP_ECC_ERROR_STATUS0           0x00042044
0039 
0040 /* TRP, DRP interrupt register offsets */
0041 #define DRP_INTERRUPT_STATUS            0x00041000
0042 #define TRP_INTERRUPT_0_STATUS          0x00020480
0043 #define DRP_INTERRUPT_CLEAR             0x00041008
0044 #define DRP_ECC_ERROR_CNTR_CLEAR        0x00040004
0045 #define TRP_INTERRUPT_0_CLEAR           0x00020484
0046 #define TRP_ECC_ERROR_CNTR_CLEAR        0x00020440
0047 
0048 /* Mask and shift macros */
0049 #define ECC_DB_ERR_COUNT_MASK           GENMASK(4, 0)
0050 #define ECC_DB_ERR_WAYS_MASK            GENMASK(31, 16)
0051 #define ECC_DB_ERR_WAYS_SHIFT           BIT(4)
0052 
0053 #define ECC_SB_ERR_COUNT_MASK           GENMASK(23, 16)
0054 #define ECC_SB_ERR_COUNT_SHIFT          BIT(4)
0055 #define ECC_SB_ERR_WAYS_MASK            GENMASK(15, 0)
0056 
0057 #define SB_ECC_ERROR                    BIT(0)
0058 #define DB_ECC_ERROR                    BIT(1)
0059 
0060 #define DRP_TRP_INT_CLEAR               GENMASK(1, 0)
0061 #define DRP_TRP_CNT_CLEAR               GENMASK(1, 0)
0062 
0063 /* Config registers offsets*/
0064 #define DRP_ECC_ERROR_CFG               0x00040000
0065 
0066 /* Tag RAM, Data RAM interrupt register offsets */
0067 #define CMN_INTERRUPT_0_ENABLE          0x0003001c
0068 #define CMN_INTERRUPT_2_ENABLE          0x0003003c
0069 #define TRP_INTERRUPT_0_ENABLE          0x00020488
0070 #define DRP_INTERRUPT_ENABLE            0x0004100c
0071 
0072 #define SB_ERROR_THRESHOLD              0x1
0073 #define SB_ERROR_THRESHOLD_SHIFT        24
0074 #define SB_DB_TRP_INTERRUPT_ENABLE      0x3
0075 #define TRP0_INTERRUPT_ENABLE           0x1
0076 #define DRP0_INTERRUPT_ENABLE           BIT(6)
0077 #define SB_DB_DRP_INTERRUPT_ENABLE      0x3
0078 
0079 enum {
0080     LLCC_DRAM_CE = 0,
0081     LLCC_DRAM_UE,
0082     LLCC_TRAM_CE,
0083     LLCC_TRAM_UE,
0084 };
0085 
0086 static const struct llcc_edac_reg_data edac_reg_data[] = {
0087     [LLCC_DRAM_CE] = {
0088         .name = "DRAM Single-bit",
0089         .synd_reg = DRP_ECC_SB_ERR_SYN0,
0090         .count_status_reg = DRP_ECC_ERROR_STATUS1,
0091         .ways_status_reg = DRP_ECC_ERROR_STATUS0,
0092         .reg_cnt = DRP_SYN_REG_CNT,
0093         .count_mask = ECC_SB_ERR_COUNT_MASK,
0094         .ways_mask = ECC_SB_ERR_WAYS_MASK,
0095         .count_shift = ECC_SB_ERR_COUNT_SHIFT,
0096     },
0097     [LLCC_DRAM_UE] = {
0098         .name = "DRAM Double-bit",
0099         .synd_reg = DRP_ECC_DB_ERR_SYN0,
0100         .count_status_reg = DRP_ECC_ERROR_STATUS1,
0101         .ways_status_reg = DRP_ECC_ERROR_STATUS0,
0102         .reg_cnt = DRP_SYN_REG_CNT,
0103         .count_mask = ECC_DB_ERR_COUNT_MASK,
0104         .ways_mask = ECC_DB_ERR_WAYS_MASK,
0105         .ways_shift = ECC_DB_ERR_WAYS_SHIFT,
0106     },
0107     [LLCC_TRAM_CE] = {
0108         .name = "TRAM Single-bit",
0109         .synd_reg = TRP_ECC_SB_ERR_SYN0,
0110         .count_status_reg = TRP_ECC_ERROR_STATUS1,
0111         .ways_status_reg = TRP_ECC_ERROR_STATUS0,
0112         .reg_cnt = TRP_SYN_REG_CNT,
0113         .count_mask = ECC_SB_ERR_COUNT_MASK,
0114         .ways_mask = ECC_SB_ERR_WAYS_MASK,
0115         .count_shift = ECC_SB_ERR_COUNT_SHIFT,
0116     },
0117     [LLCC_TRAM_UE] = {
0118         .name = "TRAM Double-bit",
0119         .synd_reg = TRP_ECC_DB_ERR_SYN0,
0120         .count_status_reg = TRP_ECC_ERROR_STATUS1,
0121         .ways_status_reg = TRP_ECC_ERROR_STATUS0,
0122         .reg_cnt = TRP_SYN_REG_CNT,
0123         .count_mask = ECC_DB_ERR_COUNT_MASK,
0124         .ways_mask = ECC_DB_ERR_WAYS_MASK,
0125         .ways_shift = ECC_DB_ERR_WAYS_SHIFT,
0126     },
0127 };
0128 
0129 static int qcom_llcc_core_setup(struct regmap *llcc_bcast_regmap)
0130 {
0131     u32 sb_err_threshold;
0132     int ret;
0133 
0134     /*
0135      * Configure interrupt enable registers such that Tag, Data RAM related
0136      * interrupts are propagated to interrupt controller for servicing
0137      */
0138     ret = regmap_update_bits(llcc_bcast_regmap, CMN_INTERRUPT_2_ENABLE,
0139                  TRP0_INTERRUPT_ENABLE,
0140                  TRP0_INTERRUPT_ENABLE);
0141     if (ret)
0142         return ret;
0143 
0144     ret = regmap_update_bits(llcc_bcast_regmap, TRP_INTERRUPT_0_ENABLE,
0145                  SB_DB_TRP_INTERRUPT_ENABLE,
0146                  SB_DB_TRP_INTERRUPT_ENABLE);
0147     if (ret)
0148         return ret;
0149 
0150     sb_err_threshold = (SB_ERROR_THRESHOLD << SB_ERROR_THRESHOLD_SHIFT);
0151     ret = regmap_write(llcc_bcast_regmap, DRP_ECC_ERROR_CFG,
0152                sb_err_threshold);
0153     if (ret)
0154         return ret;
0155 
0156     ret = regmap_update_bits(llcc_bcast_regmap, CMN_INTERRUPT_2_ENABLE,
0157                  DRP0_INTERRUPT_ENABLE,
0158                  DRP0_INTERRUPT_ENABLE);
0159     if (ret)
0160         return ret;
0161 
0162     ret = regmap_write(llcc_bcast_regmap, DRP_INTERRUPT_ENABLE,
0163                SB_DB_DRP_INTERRUPT_ENABLE);
0164     return ret;
0165 }
0166 
0167 /* Clear the error interrupt and counter registers */
0168 static int
0169 qcom_llcc_clear_error_status(int err_type, struct llcc_drv_data *drv)
0170 {
0171     int ret = 0;
0172 
0173     switch (err_type) {
0174     case LLCC_DRAM_CE:
0175     case LLCC_DRAM_UE:
0176         ret = regmap_write(drv->bcast_regmap, DRP_INTERRUPT_CLEAR,
0177                    DRP_TRP_INT_CLEAR);
0178         if (ret)
0179             return ret;
0180 
0181         ret = regmap_write(drv->bcast_regmap, DRP_ECC_ERROR_CNTR_CLEAR,
0182                    DRP_TRP_CNT_CLEAR);
0183         if (ret)
0184             return ret;
0185         break;
0186     case LLCC_TRAM_CE:
0187     case LLCC_TRAM_UE:
0188         ret = regmap_write(drv->bcast_regmap, TRP_INTERRUPT_0_CLEAR,
0189                    DRP_TRP_INT_CLEAR);
0190         if (ret)
0191             return ret;
0192 
0193         ret = regmap_write(drv->bcast_regmap, TRP_ECC_ERROR_CNTR_CLEAR,
0194                    DRP_TRP_CNT_CLEAR);
0195         if (ret)
0196             return ret;
0197         break;
0198     default:
0199         ret = -EINVAL;
0200         edac_printk(KERN_CRIT, EDAC_LLCC, "Unexpected error type: %d\n",
0201                 err_type);
0202     }
0203     return ret;
0204 }
0205 
0206 /* Dump Syndrome registers data for Tag RAM, Data RAM bit errors*/
0207 static int
0208 dump_syn_reg_values(struct llcc_drv_data *drv, u32 bank, int err_type)
0209 {
0210     struct llcc_edac_reg_data reg_data = edac_reg_data[err_type];
0211     int err_cnt, err_ways, ret, i;
0212     u32 synd_reg, synd_val;
0213 
0214     for (i = 0; i < reg_data.reg_cnt; i++) {
0215         synd_reg = reg_data.synd_reg + (i * 4);
0216         ret = regmap_read(drv->regmap, drv->offsets[bank] + synd_reg,
0217                   &synd_val);
0218         if (ret)
0219             goto clear;
0220 
0221         edac_printk(KERN_CRIT, EDAC_LLCC, "%s: ECC_SYN%d: 0x%8x\n",
0222                 reg_data.name, i, synd_val);
0223     }
0224 
0225     ret = regmap_read(drv->regmap,
0226               drv->offsets[bank] + reg_data.count_status_reg,
0227               &err_cnt);
0228     if (ret)
0229         goto clear;
0230 
0231     err_cnt &= reg_data.count_mask;
0232     err_cnt >>= reg_data.count_shift;
0233     edac_printk(KERN_CRIT, EDAC_LLCC, "%s: Error count: 0x%4x\n",
0234             reg_data.name, err_cnt);
0235 
0236     ret = regmap_read(drv->regmap,
0237               drv->offsets[bank] + reg_data.ways_status_reg,
0238               &err_ways);
0239     if (ret)
0240         goto clear;
0241 
0242     err_ways &= reg_data.ways_mask;
0243     err_ways >>= reg_data.ways_shift;
0244 
0245     edac_printk(KERN_CRIT, EDAC_LLCC, "%s: Error ways: 0x%4x\n",
0246             reg_data.name, err_ways);
0247 
0248 clear:
0249     return qcom_llcc_clear_error_status(err_type, drv);
0250 }
0251 
0252 static int
0253 dump_syn_reg(struct edac_device_ctl_info *edev_ctl, int err_type, u32 bank)
0254 {
0255     struct llcc_drv_data *drv = edev_ctl->pvt_info;
0256     int ret;
0257 
0258     ret = dump_syn_reg_values(drv, bank, err_type);
0259     if (ret)
0260         return ret;
0261 
0262     switch (err_type) {
0263     case LLCC_DRAM_CE:
0264         edac_device_handle_ce(edev_ctl, 0, bank,
0265                       "LLCC Data RAM correctable Error");
0266         break;
0267     case LLCC_DRAM_UE:
0268         edac_device_handle_ue(edev_ctl, 0, bank,
0269                       "LLCC Data RAM uncorrectable Error");
0270         break;
0271     case LLCC_TRAM_CE:
0272         edac_device_handle_ce(edev_ctl, 0, bank,
0273                       "LLCC Tag RAM correctable Error");
0274         break;
0275     case LLCC_TRAM_UE:
0276         edac_device_handle_ue(edev_ctl, 0, bank,
0277                       "LLCC Tag RAM uncorrectable Error");
0278         break;
0279     default:
0280         ret = -EINVAL;
0281         edac_printk(KERN_CRIT, EDAC_LLCC, "Unexpected error type: %d\n",
0282                 err_type);
0283     }
0284 
0285     return ret;
0286 }
0287 
0288 static irqreturn_t
0289 llcc_ecc_irq_handler(int irq, void *edev_ctl)
0290 {
0291     struct edac_device_ctl_info *edac_dev_ctl = edev_ctl;
0292     struct llcc_drv_data *drv = edac_dev_ctl->pvt_info;
0293     irqreturn_t irq_rc = IRQ_NONE;
0294     u32 drp_error, trp_error, i;
0295     int ret;
0296 
0297     /* Iterate over the banks and look for Tag RAM or Data RAM errors */
0298     for (i = 0; i < drv->num_banks; i++) {
0299         ret = regmap_read(drv->regmap,
0300                   drv->offsets[i] + DRP_INTERRUPT_STATUS,
0301                   &drp_error);
0302 
0303         if (!ret && (drp_error & SB_ECC_ERROR)) {
0304             edac_printk(KERN_CRIT, EDAC_LLCC,
0305                     "Single Bit Error detected in Data RAM\n");
0306             ret = dump_syn_reg(edev_ctl, LLCC_DRAM_CE, i);
0307         } else if (!ret && (drp_error & DB_ECC_ERROR)) {
0308             edac_printk(KERN_CRIT, EDAC_LLCC,
0309                     "Double Bit Error detected in Data RAM\n");
0310             ret = dump_syn_reg(edev_ctl, LLCC_DRAM_UE, i);
0311         }
0312         if (!ret)
0313             irq_rc = IRQ_HANDLED;
0314 
0315         ret = regmap_read(drv->regmap,
0316                   drv->offsets[i] + TRP_INTERRUPT_0_STATUS,
0317                   &trp_error);
0318 
0319         if (!ret && (trp_error & SB_ECC_ERROR)) {
0320             edac_printk(KERN_CRIT, EDAC_LLCC,
0321                     "Single Bit Error detected in Tag RAM\n");
0322             ret = dump_syn_reg(edev_ctl, LLCC_TRAM_CE, i);
0323         } else if (!ret && (trp_error & DB_ECC_ERROR)) {
0324             edac_printk(KERN_CRIT, EDAC_LLCC,
0325                     "Double Bit Error detected in Tag RAM\n");
0326             ret = dump_syn_reg(edev_ctl, LLCC_TRAM_UE, i);
0327         }
0328         if (!ret)
0329             irq_rc = IRQ_HANDLED;
0330     }
0331 
0332     return irq_rc;
0333 }
0334 
0335 static int qcom_llcc_edac_probe(struct platform_device *pdev)
0336 {
0337     struct llcc_drv_data *llcc_driv_data = pdev->dev.platform_data;
0338     struct edac_device_ctl_info *edev_ctl;
0339     struct device *dev = &pdev->dev;
0340     int ecc_irq;
0341     int rc;
0342 
0343     rc = qcom_llcc_core_setup(llcc_driv_data->bcast_regmap);
0344     if (rc)
0345         return rc;
0346 
0347     /* Allocate edac control info */
0348     edev_ctl = edac_device_alloc_ctl_info(0, "qcom-llcc", 1, "bank",
0349                           llcc_driv_data->num_banks, 1,
0350                           NULL, 0,
0351                           edac_device_alloc_index());
0352 
0353     if (!edev_ctl)
0354         return -ENOMEM;
0355 
0356     edev_ctl->dev = dev;
0357     edev_ctl->mod_name = dev_name(dev);
0358     edev_ctl->dev_name = dev_name(dev);
0359     edev_ctl->ctl_name = "llcc";
0360     edev_ctl->panic_on_ue = LLCC_ERP_PANIC_ON_UE;
0361     edev_ctl->pvt_info = llcc_driv_data;
0362 
0363     rc = edac_device_add_device(edev_ctl);
0364     if (rc)
0365         goto out_mem;
0366 
0367     platform_set_drvdata(pdev, edev_ctl);
0368 
0369     /* Request for ecc irq */
0370     ecc_irq = llcc_driv_data->ecc_irq;
0371     if (ecc_irq < 0) {
0372         rc = -ENODEV;
0373         goto out_dev;
0374     }
0375     rc = devm_request_irq(dev, ecc_irq, llcc_ecc_irq_handler,
0376                   IRQF_TRIGGER_HIGH, "llcc_ecc", edev_ctl);
0377     if (rc)
0378         goto out_dev;
0379 
0380     return rc;
0381 
0382 out_dev:
0383     edac_device_del_device(edev_ctl->dev);
0384 out_mem:
0385     edac_device_free_ctl_info(edev_ctl);
0386 
0387     return rc;
0388 }
0389 
0390 static int qcom_llcc_edac_remove(struct platform_device *pdev)
0391 {
0392     struct edac_device_ctl_info *edev_ctl = dev_get_drvdata(&pdev->dev);
0393 
0394     edac_device_del_device(edev_ctl->dev);
0395     edac_device_free_ctl_info(edev_ctl);
0396 
0397     return 0;
0398 }
0399 
0400 static struct platform_driver qcom_llcc_edac_driver = {
0401     .probe = qcom_llcc_edac_probe,
0402     .remove = qcom_llcc_edac_remove,
0403     .driver = {
0404         .name = "qcom_llcc_edac",
0405     },
0406 };
0407 module_platform_driver(qcom_llcc_edac_driver);
0408 
0409 MODULE_DESCRIPTION("QCOM EDAC driver");
0410 MODULE_LICENSE("GPL v2");