[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <1599457679-8947-4-git-send-email-yash.shah@sifive.com>
Date: Mon, 7 Sep 2020 11:17:59 +0530
From: Yash Shah <yash.shah@...ive.com>
To: robh+dt@...nel.org, palmer@...belt.com, paul.walmsley@...ive.com,
bp@...en8.de, mchehab@...nel.org, tony.luck@...el.com
Cc: aou@...s.berkeley.edu, james.morse@....com, rrichter@...vell.com,
devicetree@...r.kernel.org, linux-riscv@...ts.infradead.org,
linux-kernel@...r.kernel.org, linux-edac@...r.kernel.org,
sachin.ghadi@...ive.com, Yash Shah <yash.shah@...ive.com>
Subject: [PATCH v2 3/3] EDAC/sifive: Add EDAC support for Memory Controller in SiFive SoCs
Add Memory controller EDAC support to the SiFive platform EDAC driver.
It registers for ECC notifier events from the memory controller.
Signed-off-by: Yash Shah <yash.shah@...ive.com>
Reviewed-by: Palmer Dabbelt <palmerdabbelt@...gle.com>
Acked-by: Palmer Dabbelt <palmerdabbelt@...gle.com>
---
drivers/edac/Kconfig | 2 +-
drivers/edac/sifive_edac.c | 119 ++++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 118 insertions(+), 3 deletions(-)
diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig
index 7b6ec30..f8b3b53 100644
--- a/drivers/edac/Kconfig
+++ b/drivers/edac/Kconfig
@@ -462,7 +462,7 @@ config EDAC_ALTERA_SDMMC
config EDAC_SIFIVE
bool "Sifive platform EDAC driver"
- depends on EDAC=y && SIFIVE_L2
+ depends on EDAC=y && (SIFIVE_L2 || SIFIVE_DDR)
help
Support for error detection and correction on the SiFive SoCs.
diff --git a/drivers/edac/sifive_edac.c b/drivers/edac/sifive_edac.c
index 3a3dcb1..17dd556 100644
--- a/drivers/edac/sifive_edac.c
+++ b/drivers/edac/sifive_edac.c
@@ -11,14 +11,119 @@
#include <linux/platform_device.h>
#include "edac_module.h"
#include <soc/sifive/sifive_l2_cache.h>
+#include <soc/sifive/sifive_ddr.h>
#define DRVNAME "sifive_edac"
+#define EDAC_MOD_NAME "Sifive ECC Manager"
struct sifive_edac_priv {
struct notifier_block notifier;
struct edac_device_ctl_info *dci;
};
+struct sifive_edac_mc_priv {
+ struct notifier_block notifier;
+ struct mem_ctl_info *mci;
+};
+
+/**
+ * EDAC MC error callback
+ *
+ * @event: non-zero if unrecoverable.
+ */
+static
+int ecc_mc_err_event(struct notifier_block *this, unsigned long event, void *ptr)
+{
+ struct sifive_ddr_priv *priv = ptr;
+ struct sifive_edac_mc_priv *p;
+
+ p = container_of(this, struct sifive_edac_mc_priv, notifier);
+ if (event == SIFIVE_DDR_ERR_TYPE_UE) {
+ edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, p->mci,
+ priv->error_count, priv->page_frame_number,
+ priv->offset_in_page, priv->syndrome,
+ priv->top_layer, priv->mid_layer,
+ priv->low_layer, p->mci->ctl_name, "");
+ } else if (event == SIFIVE_DDR_ERR_TYPE_CE) {
+ edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, p->mci,
+ priv->error_count, priv->page_frame_number,
+ priv->offset_in_page, priv->syndrome,
+ priv->top_layer, priv->mid_layer,
+ priv->low_layer, p->mci->ctl_name, "");
+ }
+
+ return NOTIFY_OK;
+}
+
+static int ecc_mc_register(struct platform_device *pdev)
+{
+ struct sifive_edac_mc_priv *p;
+ struct edac_mc_layer layers[1];
+ int ret;
+
+ p = devm_kzalloc(&pdev->dev, sizeof(*p), GFP_KERNEL);
+ if (!p)
+ return -ENOMEM;
+
+ p->notifier.notifier_call = ecc_mc_err_event;
+ platform_set_drvdata(pdev, p);
+
+ layers[0].type = EDAC_MC_LAYER_CHIP_SELECT;
+ layers[0].size = 1;
+ layers[0].is_virt_csrow = true;
+
+ p->mci = edac_mc_alloc(0, ARRAY_SIZE(layers), layers, 0);
+ if (!p->mci) {
+ dev_err(&pdev->dev, "Failed mem allocation for mc instance\n");
+ return -ENOMEM;
+ }
+
+ p->mci->pdev = &pdev->dev;
+ /* Initialize controller capabilities */
+ p->mci->mtype_cap = MEM_FLAG_DDR4;
+ p->mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED;
+ p->mci->edac_cap = EDAC_FLAG_SECDED;
+ p->mci->scrub_cap = SCRUB_UNKNOWN;
+ p->mci->scrub_mode = SCRUB_HW_PROG;
+ p->mci->ctl_name = dev_name(&pdev->dev);
+ p->mci->dev_name = dev_name(&pdev->dev);
+ p->mci->mod_name = EDAC_MOD_NAME;
+ p->mci->ctl_page_to_phys = NULL;
+
+ /* Interrupt feature is supported by cadence mc */
+ edac_op_state = EDAC_OPSTATE_INT;
+
+ ret = edac_mc_add_mc(p->mci);
+ if (ret) {
+ edac_printk(KERN_ERR, EDAC_MOD_NAME,
+ "Failed to register with EDAC core\n");
+ goto err;
+ }
+
+ if (IS_ENABLED(CONFIG_SIFIVE_DDR))
+ register_sifive_ddr_error_notifier(&p->notifier);
+
+ return 0;
+
+err:
+ edac_mc_free(p->mci);
+
+ return -ENXIO;
+}
+
+static int ecc_mc_unregister(struct platform_device *pdev)
+{
+ struct sifive_edac_mc_priv *p = platform_get_drvdata(pdev);
+
+ if (IS_ENABLED(CONFIG_SIFIVE_DDR))
+ unregister_sifive_ddr_error_notifier(&p->notifier);
+
+ edac_mc_del_mc(&pdev->dev);
+ edac_mc_free(p->mci);
+
+ return 0;
+}
+
/**
* EDAC error callback
*
@@ -67,7 +172,8 @@ static int ecc_register(struct platform_device *pdev)
goto err;
}
- register_sifive_l2_error_notifier(&p->notifier);
+ if (IS_ENABLED(CONFIG_SIFIVE_L2))
+ register_sifive_l2_error_notifier(&p->notifier);
return 0;
@@ -81,7 +187,9 @@ static int ecc_unregister(struct platform_device *pdev)
{
struct sifive_edac_priv *p = platform_get_drvdata(pdev);
- unregister_sifive_l2_error_notifier(&p->notifier);
+ if (IS_ENABLED(CONFIG_SIFIVE_L2))
+ unregister_sifive_l2_error_notifier(&p->notifier);
+
edac_device_del_device(&pdev->dev);
edac_device_free_ctl_info(p->dci);
@@ -102,12 +210,19 @@ static int __init sifive_edac_init(void)
if (ret)
platform_device_unregister(sifive_pdev);
+ ret = ecc_mc_register(sifive_pdev);
+ if (ret) {
+ ecc_unregister(sifive_pdev);
+ platform_device_unregister(sifive_pdev);
+ }
+
return ret;
}
static void __exit sifive_edac_exit(void)
{
ecc_unregister(sifive_pdev);
+ ecc_mc_unregister(sifive_pdev);
platform_device_unregister(sifive_pdev);
}
--
2.7.4
Powered by blists - more mailing lists