Merge "drivers: llcc edac: Add Cache Error Reporting driver for llcc"

This commit is contained in:
qctecmdr 2019-12-16 10:09:15 -08:00 • committed by Gerrit - the friendly Code Review server
commit 1e336131de
2 changed files with 67 additions and 20 deletions

View file

@ -550,6 +550,29 @@ config EDAC_QCOM
For debugging issues having to do with stability and overall system
health, you should probably say 'Y' here.
config EDAC_QCOM_LLCC_PANIC_ON_CE
depends on EDAC_QCOM
select EDAC_PANIC_ON_CE
bool "panic on correctable errors - qcom llcc"
help
Forcibly cause kernel panic if a correctable error (CE) is
detected, even though the error is (by definition) correctable and
would otherwise result in no adverse system effects. This can reduce
debugging times on hardware which my be operating at voltages or
frequencies outside normal specifications.
For production builds, you should definitely say 'N' here.
config EDAC_QCOM_LLCC_PANIC_ON_UE
depends on EDAC_QCOM
bool "Panic on uncorrectable errors - qcom llcc"
help
Forcibly cause a kernel panic if an uncorrectable error (UE) is
detected. This can reduce debugging times on hardware which may be
operating at voltages or frequencies outside normal specification.
For production builds, you should probably say 'N' here.
config EDAC_ASPEED
tristate "Aspeed AST 2500 SoC"
depends on MACH_ASPEED_G5

View file

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2018, The Linux Foundation. All rights reserved.
* Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
*/
#include <linux/edac.h>
@ -16,7 +16,13 @@
#define EDAC_LLCC "qcom_llcc"
#define LLCC_ERP_PANIC_ON_CE 1
#ifdef CONFIG_EDAC_QCOM_LLCC_PANIC_ON_UE
#define LLCC_ERP_PANIC_ON_UE 1
#else
#define LLCC_ERP_PANIC_ON_UE 0
#endif
#define TRP_SYN_REG_CNT 6
#define DRP_SYN_REG_CNT 8
@ -26,7 +32,7 @@
#define LLCC_LB_CNT_SHIFT 28
/* Single & double bit syndrome register offsets */
#define TRP_ECC_SB_ERR_SYN0 0x0002304c
#define TRP_ECC_SB_ERR_SYN0 0x0002034c
#define TRP_ECC_DB_ERR_SYN0 0x00020370
#define DRP_ECC_SB_ERR_SYN0 0x0004204c
#define DRP_ECC_DB_ERR_SYN0 0x00042070
@ -76,6 +82,9 @@
#define DRP0_INTERRUPT_ENABLE BIT(6)
#define SB_DB_DRP_INTERRUPT_ENABLE 0x3
static int poll_msec = 5000;
module_param(poll_msec, int, 0444);
enum {
LLCC_DRAM_CE = 0,
LLCC_DRAM_UE,
@ -292,6 +301,7 @@ llcc_ecc_irq_handler(int irq, void *edev_ctl)
struct llcc_drv_data *drv = edac_dev_ctl->pvt_info;
irqreturn_t irq_rc = IRQ_NONE;
u32 drp_error, trp_error, i;
bool irq_handled = false;
int ret;
/* Iterate over the banks and look for Tag RAM or Data RAM errors */
@ -310,7 +320,7 @@ llcc_ecc_irq_handler(int irq, void *edev_ctl)
ret = dump_syn_reg(edev_ctl, LLCC_DRAM_UE, i);
}
if (!ret)
irq_rc = IRQ_HANDLED;
irq_handled = true;
ret = regmap_read(drv->regmap,
drv->offsets[i] + TRP_INTERRUPT_0_STATUS,
@ -326,12 +336,20 @@ llcc_ecc_irq_handler(int irq, void *edev_ctl)
ret = dump_syn_reg(edev_ctl, LLCC_TRAM_UE, i);
}
if (!ret)
irq_rc = IRQ_HANDLED;
irq_handled = true;
}
if (irq_handled)
irq_rc = IRQ_HANDLED;
return irq_rc;
}
static void qcom_llcc_poll_cache_errors(struct edac_device_ctl_info *edev_ctl)
{
llcc_ecc_irq_handler(0, edev_ctl);
}
static int qcom_llcc_edac_probe(struct platform_device *pdev)
{
struct llcc_drv_data *llcc_driv_data = pdev->dev.platform_data;
@ -340,10 +358,6 @@ static int qcom_llcc_edac_probe(struct platform_device *pdev)
int ecc_irq;
int rc;
rc = qcom_llcc_core_setup(llcc_driv_data->bcast_regmap);
if (rc)
return rc;
/* Allocate edac control info */
edev_ctl = edac_device_alloc_ctl_info(0, "qcom-llcc", 1, "bank",
llcc_driv_data->num_banks, 1,
@ -358,32 +372,42 @@ static int qcom_llcc_edac_probe(struct platform_device *pdev)
edev_ctl->dev_name = dev_name(dev);
edev_ctl->ctl_name = "llcc";
edev_ctl->panic_on_ue = LLCC_ERP_PANIC_ON_UE;
#ifdef CONFIG_EDAC_QCOM_LLCC_PANIC_ON_CE
edev_ctl->panic_on_ce = LLCC_ERP_PANIC_ON_CE;
#endif
edev_ctl->pvt_info = llcc_driv_data;
/* Request for ecc irq */
ecc_irq = llcc_driv_data->ecc_irq;
if (ecc_irq < 0) {
dev_info(dev, "No ECC IRQ; defaulting to polling mode\n");
edev_ctl->poll_msec = poll_msec;
edev_ctl->edac_check = qcom_llcc_poll_cache_errors;
}
rc = edac_device_add_device(edev_ctl);
if (rc)
goto out_mem;
platform_set_drvdata(pdev, edev_ctl);
if (ecc_irq >= 0) {
rc = qcom_llcc_core_setup(llcc_driv_data->bcast_regmap);
if (rc)
goto out_dev;
/* Request for ecc irq */
ecc_irq = llcc_driv_data->ecc_irq;
if (ecc_irq < 0) {
rc = -ENODEV;
goto out_dev;
rc = devm_request_irq(dev, ecc_irq, llcc_ecc_irq_handler,
IRQF_SHARED | IRQF_ONESHOT | IRQF_TRIGGER_HIGH,
"llcc_ecc", edev_ctl);
if (rc)
goto out_dev;
}
rc = devm_request_irq(dev, ecc_irq, llcc_ecc_irq_handler,
IRQF_TRIGGER_HIGH, "llcc_ecc", edev_ctl);
if (rc)
goto out_dev;
platform_set_drvdata(pdev, edev_ctl);
return rc;
out_dev:
edac_device_del_device(edev_ctl->dev);
out_mem:
edac_device_free_ctl_info(edev_ctl);
return rc;
}