summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/ABI/testing/sysfs-driver-xilinx-tmr-manager16
-rw-r--r--MAINTAINERS7
-rw-r--r--drivers/misc/Kconfig10
-rw-r--r--drivers/misc/Makefile1
-rw-r--r--drivers/misc/xilinx_tmr_manager.c220
5 files changed, 254 insertions, 0 deletions
diff --git a/Documentation/ABI/testing/sysfs-driver-xilinx-tmr-manager b/Documentation/ABI/testing/sysfs-driver-xilinx-tmr-manager
new file mode 100644
index 000000000000..57b9b68a73ee
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-driver-xilinx-tmr-manager
@@ -0,0 +1,16 @@
+What: /sys/devices/platform/amba_pl/<dev>/errcnt
+Date: Nov 2022
+Contact: appana.durga.kedareswara.rao@amd.com
+Description: This control file provides the fault detection count.
+ This file cannot be written.
+ Example:
+ # cat /sys/devices/platform/amba_pl/44a10000.tmr_manager/errcnt
+ 1
+
+What: /sys/devices/platform/amba_pl/<dev>/dis_block_break
+Date: Nov 2022
+Contact: appana.durga.kedareswara.rao@amd.com
+Description: Write any value to it, This control file enables the break signal.
+ This file is write only.
+ Example:
+ # echo <any value> > /sys/devices/platform/amba_pl/44a10000.tmr_manager/dis_block_break
diff --git a/MAINTAINERS b/MAINTAINERS
index 42fc47c6edfd..1b1c0f7e3355 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -13618,6 +13618,13 @@ W: http://www.monstr.eu/fdt/
T: git git://git.monstr.eu/linux-2.6-microblaze.git
F: arch/microblaze/
+MICROBLAZE TMR MANAGER
+M: Appana Durga Kedareswara rao <appana.durga.kedareswara.rao@amd.com>
+S: Supported
+F: Documentation/ABI/testing/sysfs-driver-xilinx-tmr-manager
+F: Documentation/devicetree/bindings/misc/xlnx,tmr-manager.yaml
+F: drivers/misc/xilinx_tmr_manager.c
+
MICROCHIP AT91 DMA DRIVERS
M: Ludovic Desroches <ludovic.desroches@microchip.com>
M: Tudor Ambarus <tudor.ambarus@linaro.org>
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 9947b7892bd5..3afaf0b50854 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -518,6 +518,16 @@ config VCPU_STALL_DETECTOR
If you do not intend to run this kernel as a guest, say N.
+config TMR_MANAGER
+ tristate "Select TMR Manager"
+ depends on MICROBLAZE && MB_MANAGER
+ help
+ This option enables the driver developed for TMR Manager.
+ The Triple Modular Redundancy(TMR) manager provides support for
+ fault detection.
+
+ Say N here unless you know what you are doing.
+
source "drivers/misc/c2port/Kconfig"
source "drivers/misc/eeprom/Kconfig"
source "drivers/misc/cb710/Kconfig"
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 87b54a4a4422..d61912315f0d 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -64,3 +64,4 @@ obj-$(CONFIG_HI6421V600_IRQ) += hi6421v600-irq.o
obj-$(CONFIG_OPEN_DICE) += open-dice.o
obj-$(CONFIG_GP_PCI1XXXX) += mchp_pci1xxxx/
obj-$(CONFIG_VCPU_STALL_DETECTOR) += vcpu_stall_detector.o
+obj-$(CONFIG_TMR_MANAGER) += xilinx_tmr_manager.o
diff --git a/drivers/misc/xilinx_tmr_manager.c b/drivers/misc/xilinx_tmr_manager.c
new file mode 100644
index 000000000000..0ef55e06d3a0
--- /dev/null
+++ b/drivers/misc/xilinx_tmr_manager.c
@@ -0,0 +1,220 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for Xilinx TMR Manager IP.
+ *
+ * Copyright (C) 2022 Advanced Micro Devices, Inc.
+ *
+ * Description:
+ * This driver is developed for TMR Manager,The Triple Modular Redundancy(TMR)
+ * Manager is responsible for handling the TMR subsystem state, including
+ * fault detection and error recovery. The core is triplicated in each of
+ * the sub-blocks in the TMR subsystem, and provides majority voting of
+ * its internal state provides soft error detection, correction and
+ * recovery.
+ */
+
+#include <asm/xilinx_mb_manager.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+
+/* TMR Manager Register offsets */
+#define XTMR_MANAGER_CR_OFFSET 0x0
+#define XTMR_MANAGER_FFR_OFFSET 0x4
+#define XTMR_MANAGER_CMR0_OFFSET 0x8
+#define XTMR_MANAGER_CMR1_OFFSET 0xC
+#define XTMR_MANAGER_BDIR_OFFSET 0x10
+#define XTMR_MANAGER_SEMIMR_OFFSET 0x1C
+
+/* Register Bitmasks/shifts */
+#define XTMR_MANAGER_CR_MAGIC1_MASK GENMASK(7, 0)
+#define XTMR_MANAGER_CR_MAGIC2_MASK GENMASK(15, 8)
+#define XTMR_MANAGER_CR_RIR_MASK BIT(16)
+#define XTMR_MANAGER_FFR_LM12_MASK BIT(0)
+#define XTMR_MANAGER_FFR_LM13_MASK BIT(1)
+#define XTMR_MANAGER_FFR_LM23_MASK BIT(2)
+
+#define XTMR_MANAGER_CR_MAGIC2_SHIFT 4
+#define XTMR_MANAGER_CR_RIR_SHIFT 16
+#define XTMR_MANAGER_CR_BB_SHIFT 18
+
+#define XTMR_MANAGER_MAGIC1_MAX_VAL 255
+
+/**
+ * struct xtmr_manager_dev - Driver data for TMR Manager
+ * @regs: device physical base address
+ * @cr_val: control register value
+ * @magic1: Magic 1 hardware configuration value
+ * @err_cnt: error statistics count
+ * @phys_baseaddr: Physical base address
+ */
+struct xtmr_manager_dev {
+ void __iomem *regs;
+ u32 cr_val;
+ u32 magic1;
+ u32 err_cnt;
+ resource_size_t phys_baseaddr;
+};
+
+/* IO accessors */
+static inline void xtmr_manager_write(struct xtmr_manager_dev *xtmr_manager,
+ u32 addr, u32 value)
+{
+ iowrite32(value, xtmr_manager->regs + addr);
+}
+
+static inline u32 xtmr_manager_read(struct xtmr_manager_dev *xtmr_manager,
+ u32 addr)
+{
+ return ioread32(xtmr_manager->regs + addr);
+}
+
+static void xmb_manager_reset_handler(struct xtmr_manager_dev *xtmr_manager)
+{
+ /* Clear the FFR Register contents as a part of recovery process. */
+ xtmr_manager_write(xtmr_manager, XTMR_MANAGER_FFR_OFFSET, 0);
+}
+
+static void xmb_manager_update_errcnt(struct xtmr_manager_dev *xtmr_manager)
+{
+ xtmr_manager->err_cnt++;
+}
+
+static ssize_t errcnt_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct xtmr_manager_dev *xtmr_manager = dev_get_drvdata(dev);
+
+ return sysfs_emit(buf, "%x\n", xtmr_manager->err_cnt);
+}
+static DEVICE_ATTR_RO(errcnt);
+
+static ssize_t dis_block_break_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct xtmr_manager_dev *xtmr_manager = dev_get_drvdata(dev);
+ int ret;
+ long value;
+
+ ret = kstrtoul(buf, 16, &value);
+ if (ret)
+ return ret;
+
+ /* unblock the break signal*/
+ xtmr_manager->cr_val &= ~(1 << XTMR_MANAGER_CR_BB_SHIFT);
+ xtmr_manager_write(xtmr_manager, XTMR_MANAGER_CR_OFFSET,
+ xtmr_manager->cr_val);
+ return size;
+}
+static DEVICE_ATTR_WO(dis_block_break);
+
+static struct attribute *xtmr_manager_dev_attrs[] = {
+ &dev_attr_dis_block_break.attr,
+ &dev_attr_errcnt.attr,
+ NULL,
+};
+ATTRIBUTE_GROUPS(xtmr_manager_dev);
+
+static void xtmr_manager_init(struct xtmr_manager_dev *xtmr_manager)
+{
+ /* Clear the SEM interrupt mask register to disable the interrupt */
+ xtmr_manager_write(xtmr_manager, XTMR_MANAGER_SEMIMR_OFFSET, 0);
+
+ /* Allow recovery reset by default */
+ xtmr_manager->cr_val = (1 << XTMR_MANAGER_CR_RIR_SHIFT) |
+ xtmr_manager->magic1;
+ xtmr_manager_write(xtmr_manager, XTMR_MANAGER_CR_OFFSET,
+ xtmr_manager->cr_val);
+ /*
+ * Configure Break Delay Initialization Register to zero so that
+ * break occurs immediately
+ */
+ xtmr_manager_write(xtmr_manager, XTMR_MANAGER_BDIR_OFFSET, 0);
+
+ /*
+ * To come out of break handler need to block the break signal
+ * in the tmr manager, update the xtmr_manager cr_val for the same
+ */
+ xtmr_manager->cr_val |= (1 << XTMR_MANAGER_CR_BB_SHIFT);
+
+ /*
+ * When the break vector gets asserted because of error injection,
+ * the break signal must be blocked before exiting from the
+ * break handler, Below api updates the TMR manager address and
+ * control register and error counter callback arguments,
+ * which will be used by the break handler to block the
+ * break and call the callback function.
+ */
+ xmb_manager_register(xtmr_manager->phys_baseaddr, xtmr_manager->cr_val,
+ (void *)xmb_manager_update_errcnt,
+ xtmr_manager, (void *)xmb_manager_reset_handler);
+}
+
+/**
+ * xtmr_manager_probe - Driver probe function
+ * @pdev: Pointer to the platform_device structure
+ *
+ * This is the driver probe routine. It does all the memory
+ * allocation for the device.
+ *
+ * Return: 0 on success and failure value on error
+ */
+static int xtmr_manager_probe(struct platform_device *pdev)
+{
+ struct xtmr_manager_dev *xtmr_manager;
+ struct resource *res;
+ int err;
+
+ xtmr_manager = devm_kzalloc(&pdev->dev, sizeof(*xtmr_manager),
+ GFP_KERNEL);
+ if (!xtmr_manager)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ xtmr_manager->regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(xtmr_manager->regs))
+ return PTR_ERR(xtmr_manager->regs);
+
+ xtmr_manager->phys_baseaddr = res->start;
+
+ err = of_property_read_u32(pdev->dev.of_node, "xlnx,magic1",
+ &xtmr_manager->magic1);
+ if (err < 0) {
+ dev_err(&pdev->dev, "unable to read xlnx,magic1 property");
+ return err;
+ }
+
+ if (xtmr_manager->magic1 > XTMR_MANAGER_MAGIC1_MAX_VAL) {
+ dev_err(&pdev->dev, "invalid xlnx,magic1 property value");
+ return -EINVAL;
+ }
+
+ /* Initialize TMR Manager */
+ xtmr_manager_init(xtmr_manager);
+
+ platform_set_drvdata(pdev, xtmr_manager);
+
+ return 0;
+}
+
+static const struct of_device_id xtmr_manager_of_match[] = {
+ {
+ .compatible = "xlnx,tmr-manager-1.0",
+ },
+ { /* end of table */ }
+};
+MODULE_DEVICE_TABLE(of, xtmr_manager_of_match);
+
+static struct platform_driver xtmr_manager_driver = {
+ .driver = {
+ .name = "xilinx-tmr_manager",
+ .of_match_table = xtmr_manager_of_match,
+ .dev_groups = xtmr_manager_dev_groups,
+ },
+ .probe = xtmr_manager_probe,
+};
+module_platform_driver(xtmr_manager_driver);
+
+MODULE_AUTHOR("Advanced Micro Devices, Inc");
+MODULE_DESCRIPTION("Xilinx TMR Manager Driver");
+MODULE_LICENSE("GPL");