Message ID | 1605182457-86046-2-git-send-email-yash.shah@sifive.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | [1/3] dt-bindings: riscv: Add DT documentation for SiFive Bus Error Unit | expand |
Any updates on this patch? - Yash > -----Original Message----- > From: Yash Shah <yash.shah@openfive.com> > Sent: 12 November 2020 17:31 > To: robh+dt@kernel.org; Paul Walmsley ( Sifive) > <paul.walmsley@sifive.com>; palmer@dabbelt.com; bp@alien8.de; > mchehab@kernel.org; tony.luck@intel.com; james.morse@arm.com; > rric@kernel.org > Cc: aou@eecs.berkeley.edu; devicetree@vger.kernel.org; linux- > riscv@lists.infradead.org; linux-kernel@vger.kernel.org; linux- > edac@vger.kernel.org; Sachin Ghadi <sachin.ghadi@openfive.com>; Yash > Shah <yash.shah@openfive.com> > Subject: [PATCH 2/3] soc: sifive: beu: Add support for SiFive Bus Error Unit > > Add driver support for Bus Error Unit present in SiFive's FU740 chip. > Currently the driver reports erroneous events only using Platform-level > interrupts. The support for reporting events using hart-local interrupts can > be added in future. > > Signed-off-by: Yash Shah <yash.shah@sifive.com> > --- > drivers/soc/sifive/Kconfig | 5 + > drivers/soc/sifive/Makefile | 1 + > drivers/soc/sifive/sifive_beu.c | 197 > ++++++++++++++++++++++++++++++++++++++++ > include/soc/sifive/sifive_beu.h | 16 ++++ > 4 files changed, 219 insertions(+) > create mode 100644 drivers/soc/sifive/sifive_beu.c create mode 100644 > include/soc/sifive/sifive_beu.h > > diff --git a/drivers/soc/sifive/Kconfig b/drivers/soc/sifive/Kconfig index > 58cf8c4..d575fc1 100644 > --- a/drivers/soc/sifive/Kconfig > +++ b/drivers/soc/sifive/Kconfig > @@ -7,4 +7,9 @@ config SIFIVE_L2 > help > Support for the L2 cache controller on SiFive platforms. > > +config SIFIVE_BEU > + bool "Sifive Bus Error Unit" > + help > + Support for the Bus Error Unit on SiFive platforms. > + > endif > diff --git a/drivers/soc/sifive/Makefile b/drivers/soc/sifive/Makefile index > b5caff7..1b43ecd 100644 > --- a/drivers/soc/sifive/Makefile > +++ b/drivers/soc/sifive/Makefile > @@ -1,3 +1,4 @@ > # SPDX-License-Identifier: GPL-2.0 > > obj-$(CONFIG_SIFIVE_L2) += sifive_l2_cache.o > +obj-$(CONFIG_SIFIVE_BEU) += sifive_beu.o > diff --git a/drivers/soc/sifive/sifive_beu.c b/drivers/soc/sifive/sifive_beu.c > new file mode 100644 index 0000000..87b69ba > --- /dev/null > +++ b/drivers/soc/sifive/sifive_beu.c > @@ -0,0 +1,197 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * SiFive Bus Error Unit driver > + * Copyright (C) 2020 SiFive > + * Author: Yash Shah <yash.shah@sifive.com> > + * > + */ > + > +#include <linux/module.h> > +#include <linux/platform_device.h> > +#include <linux/of_platform.h> > +#include <linux/interrupt.h> > +#include <linux/io.h> > +#include <soc/sifive/sifive_beu.h> > + > +#define SIFIVE_BEU_CAUSE 0x00 > +#define SIFIVE_BEU_VALUE 0x08 > +#define SIFIVE_BEU_ENABLE 0x10 > +#define SIFIVE_BEU_PLIC_INTR 0x18 > +#define SIFIVE_BEU_ACCRUED 0x20 > +#define SIFIVE_BEU_LOCAL_INTR 0x28 > + > +#define LOCAL_INTERRUPT 0 > +#define PLIC_INTERRUPT 1 > +#define MAX_ERR_EVENTS 5 > + > +enum beu_err_events { > + RESERVED = -1, > + NO_ERR, > + ITIM_CORR_ECC = 2, > + ITIM_UNCORR_ECC, > + TILINKBUS_ERR = 5, > + DCACHE_CORR_ECC, > + DCACHE_UNCORR_ECC > +}; > + > +static > +int err_events[MAX_ERR_EVENTS] = {ITIM_CORR_ECC, ITIM_UNCORR_ECC, > TILINKBUS_ERR, > + DCACHE_CORR_ECC, > DCACHE_UNCORR_ECC}; > + > +struct beu_sifive_ddata { > + void __iomem *regs; > + int irq; > +}; > + > +static int beu_enable_event(struct beu_sifive_ddata *ddata, > + int event, int intr_type) > +{ > + unsigned char event_mask = BIT(event), val; > + > + val = readb(ddata->regs + SIFIVE_BEU_ENABLE); > + val |= event_mask; > + writeb(val, ddata->regs + SIFIVE_BEU_ENABLE); > + > + if (intr_type == PLIC_INTERRUPT) { > + val = readb(ddata->regs + SIFIVE_BEU_PLIC_INTR); > + val |= event_mask; > + writeb(val, ddata->regs + SIFIVE_BEU_PLIC_INTR); > + } else if (intr_type == LOCAL_INTERRUPT) { > + val = readb(ddata->regs + SIFIVE_BEU_LOCAL_INTR); > + val |= event_mask; > + writeb(event_mask, ddata->regs + SIFIVE_BEU_LOCAL_INTR); > + } > + > + return 0; > +} > + > +static ATOMIC_NOTIFIER_HEAD(beu_chain); > + > +int register_sifive_beu_error_notifier(struct notifier_block *nb) { > + return atomic_notifier_chain_register(&beu_chain, nb); } > + > +int unregister_sifive_beu_error_notifier(struct notifier_block *nb) { > + return atomic_notifier_chain_unregister(&beu_chain, nb); } > + > +static irqreturn_t beu_sifive_irq(int irq, void *data) { > + struct beu_sifive_ddata *ddata = data; > + unsigned char cause, addr; > + > + addr = readb(ddata->regs + SIFIVE_BEU_VALUE); > + cause = readb(ddata->regs + SIFIVE_BEU_CAUSE); > + switch (cause) { > + case NO_ERR: > + break; > + case ITIM_CORR_ECC: > + pr_err("BEU: ITIM ECCFIX @ %d\n", addr); > + atomic_notifier_call_chain(&beu_chain, > SIFIVE_BEU_ERR_TYPE_CE, > + "ITIM ECCFIX"); > + break; > + case ITIM_UNCORR_ECC: > + pr_err("BEU: ITIM ECCFAIL @ %d\n", addr); > + atomic_notifier_call_chain(&beu_chain, > SIFIVE_BEU_ERR_TYPE_UE, > + "ITIM ECCFAIL"); > + break; > + case TILINKBUS_ERR: > + pr_err("BEU: Load or Store TILINK BUS ERR occurred\n"); > + break; > + case DCACHE_CORR_ECC: > + pr_err("BEU: DATACACHE ECCFIX @ %d\n", addr); > + atomic_notifier_call_chain(&beu_chain, > SIFIVE_BEU_ERR_TYPE_CE, > + "DCACHE ECCFIX"); > + break; > + case DCACHE_UNCORR_ECC: > + pr_err("BEU: DATACACHE ECCFAIL @ %d\n", addr); > + atomic_notifier_call_chain(&beu_chain, > SIFIVE_BEU_ERR_TYPE_UE, > + "DCACHE ECCFAIL"); > + break; > + default: > + pr_err("BEU: Unidentified cause\n"); > + break; > + } > + writeb(0, ddata->regs + SIFIVE_BEU_CAUSE); > + writeb(0, ddata->regs + SIFIVE_BEU_ACCRUED); > + > + return IRQ_HANDLED; > +} > + > +static int beu_sifive_probe(struct platform_device *pdev) { > + struct device *dev = &pdev->dev; > + struct beu_sifive_ddata *ddata; > + struct resource *res; > + int ret, i; > + > + ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL); > + if (!ddata) > + return -ENOMEM; > + > + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > + ddata->regs = devm_ioremap_resource(dev, res); > + if (IS_ERR(ddata->regs)) { > + dev_err(dev, "Unable to map IO resources\n"); > + return PTR_ERR(ddata->regs); > + } > + > + ddata->irq = platform_get_irq(pdev, 0); > + if (ddata->irq < 0) { > + dev_err(dev, "Unable to find interrupt\n"); > + ret = ddata->irq; > + return ret; > + } > + > + ret = devm_request_irq(dev, ddata->irq, beu_sifive_irq, 0, > + dev_name(dev), ddata); > + if (ret) { > + dev_err(dev, "Unable to request IRQ\n"); > + return ret; > + } > + > + for (i = 0; i < MAX_ERR_EVENTS; i++) { > + ret = beu_enable_event(ddata, err_events[i], > PLIC_INTERRUPT); > + if (ret) { > + dev_err(dev, "Unable to register PLIC interrupt\n"); > + return ret; > + } > + } > + > + platform_set_drvdata(pdev, ddata); > + > + return 0; > +} > + > +static int beu_sifive_remove(struct platform_device *pdev) { > + struct beu_sifive_ddata *ddata = platform_get_drvdata(pdev); > + > + /* Mask all error events */ > + writeb(0, ddata->regs + SIFIVE_BEU_ENABLE); > + writeb(0, ddata->regs + SIFIVE_BEU_PLIC_INTR); > + writeb(0, ddata->regs + SIFIVE_BEU_LOCAL_INTR); > + > + return 0; > +} > + > +static const struct of_device_id beu_sifive_of_match[] = { > + { .compatible = "sifive,beu0" }, > + {}, > +}; > +MODULE_DEVICE_TABLE(of, beu_sifive_of_match); > + > +static struct platform_driver beu_sifive_driver = { > + .probe = beu_sifive_probe, > + .remove = beu_sifive_remove, > + .driver = { > + .name = "beu-sifive", > + .of_match_table = beu_sifive_of_match, > + }, > +}; > +module_platform_driver(beu_sifive_driver); > + > +MODULE_DESCRIPTION("SiFive BEU driver"); MODULE_LICENSE("GPL v2"); > diff --git a/include/soc/sifive/sifive_beu.h b/include/soc/sifive/sifive_beu.h > new file mode 100644 index 0000000..c2ab688 > --- /dev/null > +++ b/include/soc/sifive/sifive_beu.h > @@ -0,0 +1,16 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +/* > + * SiFive Bus Error unit header file > + * > + */ > + > +#ifndef __SOC_SIFIVE_BEU_H > +#define __SOC_SIFIVE_BEU_H > + > +extern int register_sifive_beu_error_notifier(struct notifier_block > +*nb); extern int unregister_sifive_beu_error_notifier(struct > +notifier_block *nb); > + > +#define SIFIVE_BEU_ERR_TYPE_CE 0 > +#define SIFIVE_BEU_ERR_TYPE_UE 1 > + > +#endif /* __SOC_SIFIVE_BEU_H */ > -- > 2.7.4
diff --git a/drivers/soc/sifive/Kconfig b/drivers/soc/sifive/Kconfig index 58cf8c4..d575fc1 100644 --- a/drivers/soc/sifive/Kconfig +++ b/drivers/soc/sifive/Kconfig @@ -7,4 +7,9 @@ config SIFIVE_L2 help Support for the L2 cache controller on SiFive platforms. +config SIFIVE_BEU + bool "Sifive Bus Error Unit" + help + Support for the Bus Error Unit on SiFive platforms. + endif diff --git a/drivers/soc/sifive/Makefile b/drivers/soc/sifive/Makefile index b5caff7..1b43ecd 100644 --- a/drivers/soc/sifive/Makefile +++ b/drivers/soc/sifive/Makefile @@ -1,3 +1,4 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_SIFIVE_L2) += sifive_l2_cache.o +obj-$(CONFIG_SIFIVE_BEU) += sifive_beu.o diff --git a/drivers/soc/sifive/sifive_beu.c b/drivers/soc/sifive/sifive_beu.c new file mode 100644 index 0000000..87b69ba --- /dev/null +++ b/drivers/soc/sifive/sifive_beu.c @@ -0,0 +1,197 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * SiFive Bus Error Unit driver + * Copyright (C) 2020 SiFive + * Author: Yash Shah <yash.shah@sifive.com> + * + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/of_platform.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <soc/sifive/sifive_beu.h> + +#define SIFIVE_BEU_CAUSE 0x00 +#define SIFIVE_BEU_VALUE 0x08 +#define SIFIVE_BEU_ENABLE 0x10 +#define SIFIVE_BEU_PLIC_INTR 0x18 +#define SIFIVE_BEU_ACCRUED 0x20 +#define SIFIVE_BEU_LOCAL_INTR 0x28 + +#define LOCAL_INTERRUPT 0 +#define PLIC_INTERRUPT 1 +#define MAX_ERR_EVENTS 5 + +enum beu_err_events { + RESERVED = -1, + NO_ERR, + ITIM_CORR_ECC = 2, + ITIM_UNCORR_ECC, + TILINKBUS_ERR = 5, + DCACHE_CORR_ECC, + DCACHE_UNCORR_ECC +}; + +static +int err_events[MAX_ERR_EVENTS] = {ITIM_CORR_ECC, ITIM_UNCORR_ECC, TILINKBUS_ERR, + DCACHE_CORR_ECC, DCACHE_UNCORR_ECC}; + +struct beu_sifive_ddata { + void __iomem *regs; + int irq; +}; + +static int beu_enable_event(struct beu_sifive_ddata *ddata, + int event, int intr_type) +{ + unsigned char event_mask = BIT(event), val; + + val = readb(ddata->regs + SIFIVE_BEU_ENABLE); + val |= event_mask; + writeb(val, ddata->regs + SIFIVE_BEU_ENABLE); + + if (intr_type == PLIC_INTERRUPT) { + val = readb(ddata->regs + SIFIVE_BEU_PLIC_INTR); + val |= event_mask; + writeb(val, ddata->regs + SIFIVE_BEU_PLIC_INTR); + } else if (intr_type == LOCAL_INTERRUPT) { + val = readb(ddata->regs + SIFIVE_BEU_LOCAL_INTR); + val |= event_mask; + writeb(event_mask, ddata->regs + SIFIVE_BEU_LOCAL_INTR); + } + + return 0; +} + +static ATOMIC_NOTIFIER_HEAD(beu_chain); + +int register_sifive_beu_error_notifier(struct notifier_block *nb) +{ + return atomic_notifier_chain_register(&beu_chain, nb); +} + +int unregister_sifive_beu_error_notifier(struct notifier_block *nb) +{ + return atomic_notifier_chain_unregister(&beu_chain, nb); +} + +static irqreturn_t beu_sifive_irq(int irq, void *data) +{ + struct beu_sifive_ddata *ddata = data; + unsigned char cause, addr; + + addr = readb(ddata->regs + SIFIVE_BEU_VALUE); + cause = readb(ddata->regs + SIFIVE_BEU_CAUSE); + switch (cause) { + case NO_ERR: + break; + case ITIM_CORR_ECC: + pr_err("BEU: ITIM ECCFIX @ %d\n", addr); + atomic_notifier_call_chain(&beu_chain, SIFIVE_BEU_ERR_TYPE_CE, + "ITIM ECCFIX"); + break; + case ITIM_UNCORR_ECC: + pr_err("BEU: ITIM ECCFAIL @ %d\n", addr); + atomic_notifier_call_chain(&beu_chain, SIFIVE_BEU_ERR_TYPE_UE, + "ITIM ECCFAIL"); + break; + case TILINKBUS_ERR: + pr_err("BEU: Load or Store TILINK BUS ERR occurred\n"); + break; + case DCACHE_CORR_ECC: + pr_err("BEU: DATACACHE ECCFIX @ %d\n", addr); + atomic_notifier_call_chain(&beu_chain, SIFIVE_BEU_ERR_TYPE_CE, + "DCACHE ECCFIX"); + break; + case DCACHE_UNCORR_ECC: + pr_err("BEU: DATACACHE ECCFAIL @ %d\n", addr); + atomic_notifier_call_chain(&beu_chain, SIFIVE_BEU_ERR_TYPE_UE, + "DCACHE ECCFAIL"); + break; + default: + pr_err("BEU: Unidentified cause\n"); + break; + } + writeb(0, ddata->regs + SIFIVE_BEU_CAUSE); + writeb(0, ddata->regs + SIFIVE_BEU_ACCRUED); + + return IRQ_HANDLED; +} + +static int beu_sifive_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct beu_sifive_ddata *ddata; + struct resource *res; + int ret, i; + + ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL); + if (!ddata) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + ddata->regs = devm_ioremap_resource(dev, res); + if (IS_ERR(ddata->regs)) { + dev_err(dev, "Unable to map IO resources\n"); + return PTR_ERR(ddata->regs); + } + + ddata->irq = platform_get_irq(pdev, 0); + if (ddata->irq < 0) { + dev_err(dev, "Unable to find interrupt\n"); + ret = ddata->irq; + return ret; + } + + ret = devm_request_irq(dev, ddata->irq, beu_sifive_irq, 0, + dev_name(dev), ddata); + if (ret) { + dev_err(dev, "Unable to request IRQ\n"); + return ret; + } + + for (i = 0; i < MAX_ERR_EVENTS; i++) { + ret = beu_enable_event(ddata, err_events[i], PLIC_INTERRUPT); + if (ret) { + dev_err(dev, "Unable to register PLIC interrupt\n"); + return ret; + } + } + + platform_set_drvdata(pdev, ddata); + + return 0; +} + +static int beu_sifive_remove(struct platform_device *pdev) +{ + struct beu_sifive_ddata *ddata = platform_get_drvdata(pdev); + + /* Mask all error events */ + writeb(0, ddata->regs + SIFIVE_BEU_ENABLE); + writeb(0, ddata->regs + SIFIVE_BEU_PLIC_INTR); + writeb(0, ddata->regs + SIFIVE_BEU_LOCAL_INTR); + + return 0; +} + +static const struct of_device_id beu_sifive_of_match[] = { + { .compatible = "sifive,beu0" }, + {}, +}; +MODULE_DEVICE_TABLE(of, beu_sifive_of_match); + +static struct platform_driver beu_sifive_driver = { + .probe = beu_sifive_probe, + .remove = beu_sifive_remove, + .driver = { + .name = "beu-sifive", + .of_match_table = beu_sifive_of_match, + }, +}; +module_platform_driver(beu_sifive_driver); + +MODULE_DESCRIPTION("SiFive BEU driver"); +MODULE_LICENSE("GPL v2"); diff --git a/include/soc/sifive/sifive_beu.h b/include/soc/sifive/sifive_beu.h new file mode 100644 index 0000000..c2ab688 --- /dev/null +++ b/include/soc/sifive/sifive_beu.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * SiFive Bus Error unit header file + * + */ + +#ifndef __SOC_SIFIVE_BEU_H +#define __SOC_SIFIVE_BEU_H + +extern int register_sifive_beu_error_notifier(struct notifier_block *nb); +extern int unregister_sifive_beu_error_notifier(struct notifier_block *nb); + +#define SIFIVE_BEU_ERR_TYPE_CE 0 +#define SIFIVE_BEU_ERR_TYPE_UE 1 + +#endif /* __SOC_SIFIVE_BEU_H */
Add driver support for Bus Error Unit present in SiFive's FU740 chip. Currently the driver reports erroneous events only using Platform-level interrupts. The support for reporting events using hart-local interrupts can be added in future. Signed-off-by: Yash Shah <yash.shah@sifive.com> --- drivers/soc/sifive/Kconfig | 5 + drivers/soc/sifive/Makefile | 1 + drivers/soc/sifive/sifive_beu.c | 197 ++++++++++++++++++++++++++++++++++++++++ include/soc/sifive/sifive_beu.h | 16 ++++ 4 files changed, 219 insertions(+) create mode 100644 drivers/soc/sifive/sifive_beu.c create mode 100644 include/soc/sifive/sifive_beu.h