diff options
-rw-r--r-- | MAINTAINERS | 6 | ||||
-rw-r--r-- | arch/riscv/Kconfig | 1 | ||||
-rw-r--r-- | drivers/edac/Kconfig | 6 | ||||
-rw-r--r-- | drivers/edac/Makefile | 1 | ||||
-rw-r--r-- | drivers/edac/sifive_edac.c | 119 |
5 files changed, 133 insertions, 0 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index 57f496cff999..046596f6d490 100644 --- a/MAINTAINERS +++ b/MAINTAINERS | |||
@@ -5809,6 +5809,12 @@ L: linux-edac@vger.kernel.org | |||
5809 | S: Maintained | 5809 | S: Maintained |
5810 | F: drivers/edac/sb_edac.c | 5810 | F: drivers/edac/sb_edac.c |
5811 | 5811 | ||
5812 | EDAC-SIFIVE | ||
5813 | M: Yash Shah <yash.shah@sifive.com> | ||
5814 | L: linux-edac@vger.kernel.org | ||
5815 | S: Supported | ||
5816 | F: drivers/edac/sifive_edac.c | ||
5817 | |||
5812 | EDAC-SKYLAKE | 5818 | EDAC-SKYLAKE |
5813 | M: Tony Luck <tony.luck@intel.com> | 5819 | M: Tony Luck <tony.luck@intel.com> |
5814 | L: linux-edac@vger.kernel.org | 5820 | L: linux-edac@vger.kernel.org |
diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index 0c4b12205632..4961deaa3b1d 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig | |||
@@ -50,6 +50,7 @@ config RISCV | |||
50 | select ARCH_HAS_PTE_SPECIAL | 50 | select ARCH_HAS_PTE_SPECIAL |
51 | select ARCH_HAS_MMIOWB | 51 | select ARCH_HAS_MMIOWB |
52 | select HAVE_EBPF_JIT if 64BIT | 52 | select HAVE_EBPF_JIT if 64BIT |
53 | select EDAC_SUPPORT | ||
53 | 54 | ||
54 | config MMU | 55 | config MMU |
55 | def_bool y | 56 | def_bool y |
diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig index 5e2e0348d460..200c04ce5b0e 100644 --- a/drivers/edac/Kconfig +++ b/drivers/edac/Kconfig | |||
@@ -460,6 +460,12 @@ config EDAC_ALTERA_SDMMC | |||
460 | Support for error detection and correction on the | 460 | Support for error detection and correction on the |
461 | Altera SDMMC FIFO Memory for Altera SoCs. | 461 | Altera SDMMC FIFO Memory for Altera SoCs. |
462 | 462 | ||
463 | config EDAC_SIFIVE | ||
464 | bool "Sifive platform EDAC driver" | ||
465 | depends on EDAC=y && RISCV | ||
466 | help | ||
467 | Support for error detection and correction on the SiFive SoCs. | ||
468 | |||
463 | config EDAC_SYNOPSYS | 469 | config EDAC_SYNOPSYS |
464 | tristate "Synopsys DDR Memory Controller" | 470 | tristate "Synopsys DDR Memory Controller" |
465 | depends on ARCH_ZYNQ || ARCH_ZYNQMP | 471 | depends on ARCH_ZYNQ || ARCH_ZYNQMP |
diff --git a/drivers/edac/Makefile b/drivers/edac/Makefile index 89ad4a84a0f6..165ca65e1a3a 100644 --- a/drivers/edac/Makefile +++ b/drivers/edac/Makefile | |||
@@ -79,6 +79,7 @@ obj-$(CONFIG_EDAC_OCTEON_PCI) += octeon_edac-pci.o | |||
79 | obj-$(CONFIG_EDAC_THUNDERX) += thunderx_edac.o | 79 | obj-$(CONFIG_EDAC_THUNDERX) += thunderx_edac.o |
80 | 80 | ||
81 | obj-$(CONFIG_EDAC_ALTERA) += altera_edac.o | 81 | obj-$(CONFIG_EDAC_ALTERA) += altera_edac.o |
82 | obj-$(CONFIG_EDAC_SIFIVE) += sifive_edac.o | ||
82 | obj-$(CONFIG_EDAC_SYNOPSYS) += synopsys_edac.o | 83 | obj-$(CONFIG_EDAC_SYNOPSYS) += synopsys_edac.o |
83 | obj-$(CONFIG_EDAC_XGENE) += xgene_edac.o | 84 | obj-$(CONFIG_EDAC_XGENE) += xgene_edac.o |
84 | obj-$(CONFIG_EDAC_TI) += ti_edac.o | 85 | obj-$(CONFIG_EDAC_TI) += ti_edac.o |
diff --git a/drivers/edac/sifive_edac.c b/drivers/edac/sifive_edac.c new file mode 100644 index 000000000000..413cdb4a591d --- /dev/null +++ b/drivers/edac/sifive_edac.c | |||
@@ -0,0 +1,119 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | /* | ||
3 | * SiFive Platform EDAC Driver | ||
4 | * | ||
5 | * Copyright (C) 2018-2019 SiFive, Inc. | ||
6 | * | ||
7 | * This driver is partially based on octeon_edac-pc.c | ||
8 | * | ||
9 | */ | ||
10 | #include <linux/edac.h> | ||
11 | #include <linux/platform_device.h> | ||
12 | #include "edac_module.h" | ||
13 | #include <asm/sifive_l2_cache.h> | ||
14 | |||
15 | #define DRVNAME "sifive_edac" | ||
16 | |||
17 | struct sifive_edac_priv { | ||
18 | struct notifier_block notifier; | ||
19 | struct edac_device_ctl_info *dci; | ||
20 | }; | ||
21 | |||
22 | /** | ||
23 | * EDAC error callback | ||
24 | * | ||
25 | * @event: non-zero if unrecoverable. | ||
26 | */ | ||
27 | static | ||
28 | int ecc_err_event(struct notifier_block *this, unsigned long event, void *ptr) | ||
29 | { | ||
30 | const char *msg = (char *)ptr; | ||
31 | struct sifive_edac_priv *p; | ||
32 | |||
33 | p = container_of(this, struct sifive_edac_priv, notifier); | ||
34 | |||
35 | if (event == SIFIVE_L2_ERR_TYPE_UE) | ||
36 | edac_device_handle_ue(p->dci, 0, 0, msg); | ||
37 | else if (event == SIFIVE_L2_ERR_TYPE_CE) | ||
38 | edac_device_handle_ce(p->dci, 0, 0, msg); | ||
39 | |||
40 | return NOTIFY_OK; | ||
41 | } | ||
42 | |||
43 | static int ecc_register(struct platform_device *pdev) | ||
44 | { | ||
45 | struct sifive_edac_priv *p; | ||
46 | |||
47 | p = devm_kzalloc(&pdev->dev, sizeof(*p), GFP_KERNEL); | ||
48 | if (!p) | ||
49 | return -ENOMEM; | ||
50 | |||
51 | p->notifier.notifier_call = ecc_err_event; | ||
52 | platform_set_drvdata(pdev, p); | ||
53 | |||
54 | p->dci = edac_device_alloc_ctl_info(0, "sifive_ecc", 1, "sifive_ecc", | ||
55 | 1, 1, NULL, 0, | ||
56 | edac_device_alloc_index()); | ||
57 | if (IS_ERR(p->dci)) | ||
58 | return PTR_ERR(p->dci); | ||
59 | |||
60 | p->dci->dev = &pdev->dev; | ||
61 | p->dci->mod_name = "Sifive ECC Manager"; | ||
62 | p->dci->ctl_name = dev_name(&pdev->dev); | ||
63 | p->dci->dev_name = dev_name(&pdev->dev); | ||
64 | |||
65 | if (edac_device_add_device(p->dci)) { | ||
66 | dev_err(p->dci->dev, "failed to register with EDAC core\n"); | ||
67 | goto err; | ||
68 | } | ||
69 | |||
70 | register_sifive_l2_error_notifier(&p->notifier); | ||
71 | |||
72 | return 0; | ||
73 | |||
74 | err: | ||
75 | edac_device_free_ctl_info(p->dci); | ||
76 | |||
77 | return -ENXIO; | ||
78 | } | ||
79 | |||
80 | static int ecc_unregister(struct platform_device *pdev) | ||
81 | { | ||
82 | struct sifive_edac_priv *p = platform_get_drvdata(pdev); | ||
83 | |||
84 | unregister_sifive_l2_error_notifier(&p->notifier); | ||
85 | edac_device_del_device(&pdev->dev); | ||
86 | edac_device_free_ctl_info(p->dci); | ||
87 | |||
88 | return 0; | ||
89 | } | ||
90 | |||
91 | static struct platform_device *sifive_pdev; | ||
92 | |||
93 | static int __init sifive_edac_init(void) | ||
94 | { | ||
95 | int ret; | ||
96 | |||
97 | sifive_pdev = platform_device_register_simple(DRVNAME, 0, NULL, 0); | ||
98 | if (IS_ERR(sifive_pdev)) | ||
99 | return PTR_ERR(sifive_pdev); | ||
100 | |||
101 | ret = ecc_register(sifive_pdev); | ||
102 | if (ret) | ||
103 | platform_device_unregister(sifive_pdev); | ||
104 | |||
105 | return ret; | ||
106 | } | ||
107 | |||
108 | static void __exit sifive_edac_exit(void) | ||
109 | { | ||
110 | ecc_unregister(sifive_pdev); | ||
111 | platform_device_unregister(sifive_pdev); | ||
112 | } | ||
113 | |||
114 | module_init(sifive_edac_init); | ||
115 | module_exit(sifive_edac_exit); | ||
116 | |||
117 | MODULE_AUTHOR("SiFive Inc."); | ||
118 | MODULE_DESCRIPTION("SiFive platform EDAC driver"); | ||
119 | MODULE_LICENSE("GPL v2"); | ||