xref: /linux/drivers/misc/xilinx_tmr_manager.c (revision a940daa52167e9db8ecce82213813b735a9d9f23)
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  */
51 struct 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 */
60 static 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 
66 static inline u32 xtmr_manager_read(struct xtmr_manager_dev *xtmr_manager,
67 				    u32 addr)
68 {
69 	return ioread32(xtmr_manager->regs + addr);
70 }
71 
72 static 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, 0);
76 }
77 
78 static void xmb_manager_update_errcnt(struct xtmr_manager_dev *xtmr_manager)
79 {
80 	xtmr_manager->err_cnt++;
81 }
82 
83 static 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, "%x\n", xtmr_manager->err_cnt);
89 }
90 static DEVICE_ATTR_RO(errcnt);
91 
92 static 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(buf, 16, &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 			   xtmr_manager->cr_val);
108 	return size;
109 }
110 static DEVICE_ATTR_WO(dis_block_break);
111 
112 static struct attribute *xtmr_manager_dev_attrs[] = {
113 	&dev_attr_dis_block_break.attr,
114 	&dev_attr_errcnt.attr,
115 	NULL,
116 };
117 ATTRIBUTE_GROUPS(xtmr_manager_dev);
118 
119 static 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, 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 			   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, 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  */
163 static 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(&pdev->dev, 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, 0, &res);
175 	if (IS_ERR(xtmr_manager->regs))
176 		return PTR_ERR(xtmr_manager->regs);
177 
178 	xtmr_manager->phys_baseaddr = res->start;
179 
180 	err = of_property_read_u32(pdev->dev.of_node, "xlnx,magic1",
181 				   &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, xtmr_manager);
196 
197 	return 0;
198 }
199 
200 static const struct of_device_id xtmr_manager_of_match[] = {
201 	{
202 		.compatible = "xlnx,tmr-manager-1.0",
203 	},
204 	{ /* end of table */ }
205 };
206 MODULE_DEVICE_TABLE(of, xtmr_manager_of_match);
207 
208 static 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 };
216 module_platform_driver(xtmr_manager_driver);
217 
218 MODULE_AUTHOR("Advanced Micro Devices, Inc");
219 MODULE_DESCRIPTION("Xilinx TMR Manager Driver");
220 MODULE_LICENSE("GPL");
221