Linux Audio

Check our new training course

Loading...
Note: File does not exist in v5.14.15.
  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, 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, "%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(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}
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, 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 */
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(&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
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");