File Index
Files and directories
Sign in
FILE

xilinx_wwdt.c

File preview with syntax highlighting.

linux-6.19.14/drivers/watchdog/xilinx_wwdt.c C
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * Window watchdog device driver for Xilinx Versal WWDT
  4 *
  5 * Copyright (C) 2022 - 2024, Advanced Micro Devices, Inc.
  6 */
  7
  8#include <linux/clk.h>
  9#include <linux/interrupt.h>
 10#include <linux/io.h>
 11#include <linux/ioport.h>
 12#include <linux/math64.h>
 13#include <linux/mod_devicetable.h>
 14#include <linux/module.h>
 15#include <linux/platform_device.h>
 16#include <linux/watchdog.h>
 17
 18/* Max timeout is calculated at 100MHz source clock */
 19#define XWWDT_DEFAULT_TIMEOUT	42
 20#define XWWDT_MIN_TIMEOUT	1
 21
 22/* Register offsets for the WWDT device */
 23#define XWWDT_MWR_OFFSET	0x00
 24#define XWWDT_ESR_OFFSET	0x04
 25#define XWWDT_FCR_OFFSET	0x08
 26#define XWWDT_FWR_OFFSET	0x0c
 27#define XWWDT_SWR_OFFSET	0x10
 28
 29/* Master Write Control Register Masks */
 30#define XWWDT_MWR_MASK		BIT(0)
 31
 32/* Enable and Status Register Masks */
 33#define XWWDT_ESR_WINT_MASK	BIT(16)
 34#define XWWDT_ESR_WSW_MASK	BIT(8)
 35#define XWWDT_ESR_WEN_MASK	BIT(0)
 36
 37#define XWWDT_CLOSE_WINDOW_PERCENT	50
 38
 39/* Maximum count value of each 32 bit window */
 40#define XWWDT_MAX_COUNT_WINDOW		GENMASK(31, 0)
 41
 42/* Maximum count value of closed and open window combined */
 43#define XWWDT_MAX_COUNT_WINDOW_COMBINED	GENMASK_ULL(32, 1)
 44
 45static int wwdt_timeout;
 46static int closed_window_percent;
 47
 48module_param(wwdt_timeout, int, 0);
 49MODULE_PARM_DESC(wwdt_timeout,
 50		 "Watchdog time in seconds. (default="
 51		 __MODULE_STRING(XWWDT_DEFAULT_TIMEOUT) ")");
 52module_param(closed_window_percent, int, 0);
 53MODULE_PARM_DESC(closed_window_percent,
 54		 "Watchdog closed window percentage. (default="
 55		 __MODULE_STRING(XWWDT_CLOSE_WINDOW_PERCENT) ")");
 56/**
 57 * struct xwwdt_device - Watchdog device structure
 58 * @base: base io address of WDT device
 59 * @spinlock: spinlock for IO register access
 60 * @xilinx_wwdt_wdd: watchdog device structure
 61 * @freq: source clock frequency of WWDT
 62 * @close_percent: Closed window percent
 63 * @closed_timeout: Closed window timeout in ticks
 64 * @open_timeout: Open window timeout in ticks
 65 */
 66struct xwwdt_device {
 67	void __iomem *base;
 68	spinlock_t spinlock; /* spinlock for register handling */
 69	struct watchdog_device xilinx_wwdt_wdd;
 70	unsigned long freq;
 71	u32 close_percent;
 72	u64 closed_timeout;
 73	u64 open_timeout;
 74};
 75
 76static int xilinx_wwdt_start(struct watchdog_device *wdd)
 77{
 78	struct xwwdt_device *xdev = watchdog_get_drvdata(wdd);
 79	struct watchdog_device *xilinx_wwdt_wdd = &xdev->xilinx_wwdt_wdd;
 80	u32 control_status_reg;
 81
 82	spin_lock(&xdev->spinlock);
 83
 84	iowrite32(XWWDT_MWR_MASK, xdev->base + XWWDT_MWR_OFFSET);
 85	iowrite32(~(u32)XWWDT_ESR_WEN_MASK, xdev->base + XWWDT_ESR_OFFSET);
 86	iowrite32((u32)xdev->closed_timeout, xdev->base + XWWDT_FWR_OFFSET);
 87	iowrite32((u32)xdev->open_timeout, xdev->base + XWWDT_SWR_OFFSET);
 88
 89	/* Enable the window watchdog timer */
 90	control_status_reg = ioread32(xdev->base + XWWDT_ESR_OFFSET);
 91	control_status_reg |= XWWDT_ESR_WEN_MASK;
 92	iowrite32(control_status_reg, xdev->base + XWWDT_ESR_OFFSET);
 93
 94	spin_unlock(&xdev->spinlock);
 95
 96	dev_dbg(xilinx_wwdt_wdd->parent, "Watchdog Started!\n");
 97
 98	return 0;
 99}
100
101static int xilinx_wwdt_keepalive(struct watchdog_device *wdd)
102{
103	struct xwwdt_device *xdev = watchdog_get_drvdata(wdd);
104	u32 control_status_reg;
105
106	spin_lock(&xdev->spinlock);
107
108	/* Enable write access control bit for the window watchdog */
109	iowrite32(XWWDT_MWR_MASK, xdev->base + XWWDT_MWR_OFFSET);
110
111	/* Trigger restart kick to watchdog */
112	control_status_reg = ioread32(xdev->base + XWWDT_ESR_OFFSET);
113	control_status_reg |= XWWDT_ESR_WSW_MASK;
114	iowrite32(control_status_reg, xdev->base + XWWDT_ESR_OFFSET);
115
116	spin_unlock(&xdev->spinlock);
117
118	return 0;
119}
120
121static const struct watchdog_info xilinx_wwdt_ident = {
122	.options = WDIOF_KEEPALIVEPING |
123		WDIOF_SETTIMEOUT,
124	.firmware_version = 1,
125	.identity = "xlnx_window watchdog",
126};
127
128static const struct watchdog_ops xilinx_wwdt_ops = {
129	.owner = THIS_MODULE,
130	.start = xilinx_wwdt_start,
131	.ping = xilinx_wwdt_keepalive,
132};
133
134static int xwwdt_probe(struct platform_device *pdev)
135{
136	struct watchdog_device *xilinx_wwdt_wdd;
137	struct device *dev = &pdev->dev;
138	struct xwwdt_device *xdev;
139	u64 max_per_window_ms;
140	u64 min_per_window_ms;
141	u64 timeout_count;
142	struct clk *clk;
143	u32 timeout_ms;
144	u64 ms_count;
145	int ret;
146
147	xdev = devm_kzalloc(dev, sizeof(*xdev), GFP_KERNEL);
148	if (!xdev)
149		return -ENOMEM;
150
151	xilinx_wwdt_wdd = &xdev->xilinx_wwdt_wdd;
152	xilinx_wwdt_wdd->info = &xilinx_wwdt_ident;
153	xilinx_wwdt_wdd->ops = &xilinx_wwdt_ops;
154	xilinx_wwdt_wdd->parent = dev;
155
156	xdev->base = devm_platform_ioremap_resource(pdev, 0);
157	if (IS_ERR(xdev->base))
158		return PTR_ERR(xdev->base);
159
160	clk = devm_clk_get_enabled(dev, NULL);
161	if (IS_ERR(clk))
162		return PTR_ERR(clk);
163
164	xdev->freq = clk_get_rate(clk);
165	if (xdev->freq < 1000000)
166		return -EINVAL;
167
168	xilinx_wwdt_wdd->min_timeout = XWWDT_MIN_TIMEOUT;
169	xilinx_wwdt_wdd->timeout = XWWDT_DEFAULT_TIMEOUT;
170	xilinx_wwdt_wdd->max_hw_heartbeat_ms =
171		div64_u64(XWWDT_MAX_COUNT_WINDOW_COMBINED, xdev->freq) * 1000;
172
173	if (closed_window_percent == 0 || closed_window_percent >= 100)
174		xdev->close_percent = XWWDT_CLOSE_WINDOW_PERCENT;
175	else
176		xdev->close_percent = closed_window_percent;
177
178	watchdog_init_timeout(xilinx_wwdt_wdd, wwdt_timeout, &pdev->dev);
179
180	/* Calculate ticks for 1 milli-second */
181	ms_count = div_u64(xdev->freq, 1000);
182	timeout_ms = xilinx_wwdt_wdd->timeout * 1000;
183	timeout_count = timeout_ms * ms_count;
184
185	if (timeout_ms > xilinx_wwdt_wdd->max_hw_heartbeat_ms) {
186		/*
187		 * To avoid ping restrictions until the minimum hardware heartbeat,
188		 * we will solely rely on the open window and
189		 * adjust the minimum hardware heartbeat to 0.
190		 */
191		xdev->closed_timeout = 0;
192		xdev->open_timeout = XWWDT_MAX_COUNT_WINDOW;
193		xilinx_wwdt_wdd->min_hw_heartbeat_ms = 0;
194		xilinx_wwdt_wdd->max_hw_heartbeat_ms = xilinx_wwdt_wdd->max_hw_heartbeat_ms / 2;
195	} else {
196		xdev->closed_timeout  = div64_u64(timeout_count * xdev->close_percent, 100);
197		xilinx_wwdt_wdd->min_hw_heartbeat_ms =
198			div64_u64(timeout_ms * xdev->close_percent, 100);
199
200		if (timeout_ms > xilinx_wwdt_wdd->max_hw_heartbeat_ms / 2) {
201			max_per_window_ms = xilinx_wwdt_wdd->max_hw_heartbeat_ms / 2;
202			min_per_window_ms = timeout_ms - max_per_window_ms;
203
204			if (xilinx_wwdt_wdd->min_hw_heartbeat_ms > max_per_window_ms) {
205				dev_info(xilinx_wwdt_wdd->parent,
206					 "Closed window cannot be set to %d%%. Using maximum supported value.\n",
207					xdev->close_percent);
208				xdev->closed_timeout = max_per_window_ms * ms_count;
209				xilinx_wwdt_wdd->min_hw_heartbeat_ms = max_per_window_ms;
210			} else if (xilinx_wwdt_wdd->min_hw_heartbeat_ms < min_per_window_ms) {
211				dev_info(xilinx_wwdt_wdd->parent,
212					 "Closed window cannot be set to %d%%. Using minimum supported value.\n",
213					xdev->close_percent);
214				xdev->closed_timeout = min_per_window_ms * ms_count;
215				xilinx_wwdt_wdd->min_hw_heartbeat_ms = min_per_window_ms;
216			}
217		}
218		xdev->open_timeout = timeout_count - xdev->closed_timeout;
219	}
220
221	spin_lock_init(&xdev->spinlock);
222	watchdog_set_drvdata(xilinx_wwdt_wdd, xdev);
223	watchdog_set_nowayout(xilinx_wwdt_wdd, 1);
224
225	ret = devm_watchdog_register_device(dev, xilinx_wwdt_wdd);
226	if (ret)
227		return ret;
228
229	dev_info(dev, "Xilinx window watchdog Timer with timeout %ds\n",
230		 xilinx_wwdt_wdd->timeout);
231
232	return 0;
233}
234
235static const struct of_device_id xwwdt_of_match[] = {
236	{ .compatible = "xlnx,versal-wwdt", },
237	{},
238};
239MODULE_DEVICE_TABLE(of, xwwdt_of_match);
240
241static struct platform_driver xwwdt_driver = {
242	.probe = xwwdt_probe,
243	.driver = {
244		.name = "Xilinx window watchdog",
245		.of_match_table = xwwdt_of_match,
246	},
247};
248
249module_platform_driver(xwwdt_driver);
250
251MODULE_AUTHOR("Neeli Srinivas <srinivas.neeli@amd.com>");
252MODULE_DESCRIPTION("Xilinx window watchdog driver");
253MODULE_LICENSE("GPL");
~/ Read-only file preview.