[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <1598357182-4226-4-git-send-email-yash.shah@sifive.com>
Date: Tue, 25 Aug 2020 17:36:22 +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 3/3] edac: sifive: Add EDAC support for Memory Controller in SiFive SoCs
Add Memory controller EDAC support in exisiting SiFive platform EDAC
driver. It registers for notifier events from the SiFive DDR controller
driver for DDR ECC events.
Signed-off-by: Yash Shah <yash.shah@...ive.com>
---
drivers/edac/Kconfig | 2 +-
drivers/edac/sifive_edac.c | 117 +++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 118 insertions(+), 1 deletion(-)
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..cf032685 100644
--- a/drivers/edac/sifive_edac.c
+++ b/drivers/edac/sifive_edac.c
@@ -11,14 +11,120 @@
#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 SIFIVE_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 = SIFIVE_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, SIFIVE_EDAC_MOD_NAME,
+ "Failed to register with EDAC core\n");
+ goto err;
+ }
+
+#ifdef CONFIG_SIFIVE_DDR
+ register_sifive_ddr_error_notifier(&p->notifier);
+#endif
+
+ 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);
+
+#ifdef CONFIG_SIFIVE_DDR
+ unregister_sifive_ddr_error_notifier(&p->notifier);
+#endif
+ edac_mc_del_mc(&pdev->dev);
+ edac_mc_free(p->mci);
+
+ return 0;
+}
+
/**
* EDAC error callback
*
@@ -67,7 +173,9 @@ static int ecc_register(struct platform_device *pdev)
goto err;
}
+#ifdef CONFIG_SIFIVE_L2
register_sifive_l2_error_notifier(&p->notifier);
+#endif
return 0;
@@ -81,7 +189,9 @@ static int ecc_unregister(struct platform_device *pdev)
{
struct sifive_edac_priv *p = platform_get_drvdata(pdev);
+#ifdef CONFIG_SIFIVE_L2
unregister_sifive_l2_error_notifier(&p->notifier);
+#endif
edac_device_del_device(&pdev->dev);
edac_device_free_ctl_info(p->dci);
@@ -102,12 +212,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