| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220 |
- // 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.h>
- #include <linux/platform_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;
- xtmr_manager->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &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");
|