1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Driver for Xilinx TMR Manager IP.
4 *
5 * Copyright (C) 2022 Advanced Micro Devices, Inc.
6 *
7 * Description:
8 * This driver is developed for TMR Manager,The Triple Modular Redundancy(TMR)
9 * Manager is responsible for handling the TMR subsystem state, including
10 * fault detection and error recovery. The core is triplicated in each of
11 * the sub-blocks in the TMR subsystem, and provides majority voting of
12 * its internal state provides soft error detection, correction and
13 * recovery.
14 */
15
16#include <asm/xilinx_mb_manager.h>
17#include <linux/module.h>
18#include <linux/of.h>
19#include <linux/platform_device.h>
20
21/* TMR Manager Register offsets */
22#define XTMR_MANAGER_CR_OFFSET 0x0
23#define XTMR_MANAGER_FFR_OFFSET 0x4
24#define XTMR_MANAGER_CMR0_OFFSET 0x8
25#define XTMR_MANAGER_CMR1_OFFSET 0xC
26#define XTMR_MANAGER_BDIR_OFFSET 0x10
27#define XTMR_MANAGER_SEMIMR_OFFSET 0x1C
28
29/* Register Bitmasks/shifts */
30#define XTMR_MANAGER_CR_MAGIC1_MASK GENMASK(7, 0)
31#define XTMR_MANAGER_CR_MAGIC2_MASK GENMASK(15, 8)
32#define XTMR_MANAGER_CR_RIR_MASK BIT(16)
33#define XTMR_MANAGER_FFR_LM12_MASK BIT(0)
34#define XTMR_MANAGER_FFR_LM13_MASK BIT(1)
35#define XTMR_MANAGER_FFR_LM23_MASK BIT(2)
36
37#define XTMR_MANAGER_CR_MAGIC2_SHIFT 4
38#define XTMR_MANAGER_CR_RIR_SHIFT 16
39#define XTMR_MANAGER_CR_BB_SHIFT 18
40
41#define XTMR_MANAGER_MAGIC1_MAX_VAL 255
42
43/**
44 * struct xtmr_manager_dev - Driver data for TMR Manager
45 * @regs: device physical base address
46 * @cr_val: control register value
47 * @magic1: Magic 1 hardware configuration value
48 * @err_cnt: error statistics count
49 * @phys_baseaddr: Physical base address
50 */
51struct xtmr_manager_dev {
52 void __iomem *regs;
53 u32 cr_val;
54 u32 magic1;
55 u32 err_cnt;
56 resource_size_t phys_baseaddr;
57};
58
59/* IO accessors */
60static inline void xtmr_manager_write(struct xtmr_manager_dev *xtmr_manager,
61 u32 addr, u32 value)
62{
63 iowrite32(value, xtmr_manager->regs + addr);
64}
65
66static inline u32 xtmr_manager_read(struct xtmr_manager_dev *xtmr_manager,
67 u32 addr)
68{
69 return ioread32(xtmr_manager->regs + addr);
70}
71
72static void xmb_manager_reset_handler(struct xtmr_manager_dev *xtmr_manager)
73{
74 /* Clear the FFR Register contents as a part of recovery process. */
75 xtmr_manager_write(xtmr_manager, XTMR_MANAGER_FFR_OFFSET, value: 0);
76}
77
78static void xmb_manager_update_errcnt(struct xtmr_manager_dev *xtmr_manager)
79{
80 xtmr_manager->err_cnt++;
81}
82
83static ssize_t errcnt_show(struct device *dev, struct device_attribute *attr,
84 char *buf)
85{
86 struct xtmr_manager_dev *xtmr_manager = dev_get_drvdata(dev);
87
88 return sysfs_emit(buf, fmt: "%x\n", xtmr_manager->err_cnt);
89}
90static DEVICE_ATTR_RO(errcnt);
91
92static ssize_t dis_block_break_store(struct device *dev,
93 struct device_attribute *attr,
94 const char *buf, size_t size)
95{
96 struct xtmr_manager_dev *xtmr_manager = dev_get_drvdata(dev);
97 int ret;
98 long value;
99
100 ret = kstrtoul(s: buf, base: 16, res: &value);
101 if (ret)
102 return ret;
103
104 /* unblock the break signal*/
105 xtmr_manager->cr_val &= ~(1 << XTMR_MANAGER_CR_BB_SHIFT);
106 xtmr_manager_write(xtmr_manager, XTMR_MANAGER_CR_OFFSET,
107 value: xtmr_manager->cr_val);
108 return size;
109}
110static DEVICE_ATTR_WO(dis_block_break);
111
112static struct attribute *xtmr_manager_dev_attrs[] = {
113 &dev_attr_dis_block_break.attr,
114 &dev_attr_errcnt.attr,
115 NULL,
116};
117ATTRIBUTE_GROUPS(xtmr_manager_dev);
118
119static void xtmr_manager_init(struct xtmr_manager_dev *xtmr_manager)
120{
121 /* Clear the SEM interrupt mask register to disable the interrupt */
122 xtmr_manager_write(xtmr_manager, XTMR_MANAGER_SEMIMR_OFFSET, value: 0);
123
124 /* Allow recovery reset by default */
125 xtmr_manager->cr_val = (1 << XTMR_MANAGER_CR_RIR_SHIFT) |
126 xtmr_manager->magic1;
127 xtmr_manager_write(xtmr_manager, XTMR_MANAGER_CR_OFFSET,
128 value: xtmr_manager->cr_val);
129 /*
130 * Configure Break Delay Initialization Register to zero so that
131 * break occurs immediately
132 */
133 xtmr_manager_write(xtmr_manager, XTMR_MANAGER_BDIR_OFFSET, value: 0);
134
135 /*
136 * To come out of break handler need to block the break signal
137 * in the tmr manager, update the xtmr_manager cr_val for the same
138 */
139 xtmr_manager->cr_val |= (1 << XTMR_MANAGER_CR_BB_SHIFT);
140
141 /*
142 * When the break vector gets asserted because of error injection,
143 * the break signal must be blocked before exiting from the
144 * break handler, Below api updates the TMR manager address and
145 * control register and error counter callback arguments,
146 * which will be used by the break handler to block the
147 * break and call the callback function.
148 */
149 xmb_manager_register(xtmr_manager->phys_baseaddr, xtmr_manager->cr_val,
150 (void *)xmb_manager_update_errcnt,
151 xtmr_manager, (void *)xmb_manager_reset_handler);
152}
153
154/**
155 * xtmr_manager_probe - Driver probe function
156 * @pdev: Pointer to the platform_device structure
157 *
158 * This is the driver probe routine. It does all the memory
159 * allocation for the device.
160 *
161 * Return: 0 on success and failure value on error
162 */
163static int xtmr_manager_probe(struct platform_device *pdev)
164{
165 struct xtmr_manager_dev *xtmr_manager;
166 struct resource *res;
167 int err;
168
169 xtmr_manager = devm_kzalloc(dev: &pdev->dev, size: sizeof(*xtmr_manager),
170 GFP_KERNEL);
171 if (!xtmr_manager)
172 return -ENOMEM;
173
174 xtmr_manager->regs = devm_platform_get_and_ioremap_resource(pdev, index: 0, res: &res);
175 if (IS_ERR(ptr: xtmr_manager->regs))
176 return PTR_ERR(ptr: xtmr_manager->regs);
177
178 xtmr_manager->phys_baseaddr = res->start;
179
180 err = of_property_read_u32(np: pdev->dev.of_node, propname: "xlnx,magic1",
181 out_value: &xtmr_manager->magic1);
182 if (err < 0) {
183 dev_err(&pdev->dev, "unable to read xlnx,magic1 property");
184 return err;
185 }
186
187 if (xtmr_manager->magic1 > XTMR_MANAGER_MAGIC1_MAX_VAL) {
188 dev_err(&pdev->dev, "invalid xlnx,magic1 property value");
189 return -EINVAL;
190 }
191
192 /* Initialize TMR Manager */
193 xtmr_manager_init(xtmr_manager);
194
195 platform_set_drvdata(pdev, data: xtmr_manager);
196
197 return 0;
198}
199
200static const struct of_device_id xtmr_manager_of_match[] = {
201 {
202 .compatible = "xlnx,tmr-manager-1.0",
203 },
204 { /* end of table */ }
205};
206MODULE_DEVICE_TABLE(of, xtmr_manager_of_match);
207
208static struct platform_driver xtmr_manager_driver = {
209 .driver = {
210 .name = "xilinx-tmr_manager",
211 .of_match_table = xtmr_manager_of_match,
212 .dev_groups = xtmr_manager_dev_groups,
213 },
214 .probe = xtmr_manager_probe,
215};
216module_platform_driver(xtmr_manager_driver);
217
218MODULE_AUTHOR("Advanced Micro Devices, Inc");
219MODULE_DESCRIPTION("Xilinx TMR Manager Driver");
220MODULE_LICENSE("GPL");
221

source code of linux/drivers/misc/xilinx_tmr_manager.c