1 // SPDX-License-Identifier: GPL-2.0
3 * Window watchdog device driver for Xilinx Versal WWDT
5 * Copyright (C) 2022 - 2024, Advanced Micro Devices, Inc.
9 #include <linux/interrupt.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>
18 /* Max timeout is calculated at 100MHz source clock */
19 #define XWWDT_DEFAULT_TIMEOUT 42
20 #define XWWDT_MIN_TIMEOUT 1
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
29 /* Master Write Control Register Masks */
30 #define XWWDT_MWR_MASK BIT(0)
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)
37 #define XWWDT_CLOSE_WINDOW_PERCENT 50
39 /* Maximum count value of each 32 bit window */
40 #define XWWDT_MAX_COUNT_WINDOW GENMASK(31, 0)
42 /* Maximum count value of closed and open window combined */
43 #define XWWDT_MAX_COUNT_WINDOW_COMBINED GENMASK_ULL(32, 1)
45 static int wwdt_timeout
;
46 static int closed_window_percent
;
48 module_param(wwdt_timeout
, int, 0);
49 MODULE_PARM_DESC(wwdt_timeout
,
50 "Watchdog time in seconds. (default="
51 __MODULE_STRING(XWWDT_DEFAULT_TIMEOUT
) ")");
52 module_param(closed_window_percent
, int, 0);
53 MODULE_PARM_DESC(closed_window_percent
,
54 "Watchdog closed window percentage. (default="
55 __MODULE_STRING(XWWDT_CLOSE_WINDOW_PERCENT
) ")");
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
68 spinlock_t spinlock
; /* spinlock for register handling */
69 struct watchdog_device xilinx_wwdt_wdd
;
76 static int xilinx_wwdt_start(struct watchdog_device
*wdd
)
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
;
82 spin_lock(&xdev
->spinlock
);
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
);
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
);
94 spin_unlock(&xdev
->spinlock
);
96 dev_dbg(xilinx_wwdt_wdd
->parent
, "Watchdog Started!\n");
101 static int xilinx_wwdt_keepalive(struct watchdog_device
*wdd
)
103 struct xwwdt_device
*xdev
= watchdog_get_drvdata(wdd
);
104 u32 control_status_reg
;
106 spin_lock(&xdev
->spinlock
);
108 /* Enable write access control bit for the window watchdog */
109 iowrite32(XWWDT_MWR_MASK
, xdev
->base
+ XWWDT_MWR_OFFSET
);
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
);
116 spin_unlock(&xdev
->spinlock
);
121 static const struct watchdog_info xilinx_wwdt_ident
= {
122 .options
= WDIOF_KEEPALIVEPING
|
124 .firmware_version
= 1,
125 .identity
= "xlnx_window watchdog",
128 static const struct watchdog_ops xilinx_wwdt_ops
= {
129 .owner
= THIS_MODULE
,
130 .start
= xilinx_wwdt_start
,
131 .ping
= xilinx_wwdt_keepalive
,
134 static int xwwdt_probe(struct platform_device
*pdev
)
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
;
147 xdev
= devm_kzalloc(dev
, sizeof(*xdev
), GFP_KERNEL
);
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
;
156 xdev
->base
= devm_platform_ioremap_resource(pdev
, 0);
157 if (IS_ERR(xdev
->base
))
158 return PTR_ERR(xdev
->base
);
160 clk
= devm_clk_get_enabled(dev
, NULL
);
164 xdev
->freq
= clk_get_rate(clk
);
165 if (xdev
->freq
< 1000000)
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;
173 if (closed_window_percent
== 0 || closed_window_percent
>= 100)
174 xdev
->close_percent
= XWWDT_CLOSE_WINDOW_PERCENT
;
176 xdev
->close_percent
= closed_window_percent
;
178 watchdog_init_timeout(xilinx_wwdt_wdd
, wwdt_timeout
, &pdev
->dev
);
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
;
185 if (timeout_ms
> xilinx_wwdt_wdd
->max_hw_heartbeat_ms
) {
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.
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;
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);
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
;
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
;
218 xdev
->open_timeout
= timeout_count
- xdev
->closed_timeout
;
221 spin_lock_init(&xdev
->spinlock
);
222 watchdog_set_drvdata(xilinx_wwdt_wdd
, xdev
);
223 watchdog_set_nowayout(xilinx_wwdt_wdd
, 1);
225 ret
= devm_watchdog_register_device(dev
, xilinx_wwdt_wdd
);
229 dev_info(dev
, "Xilinx window watchdog Timer with timeout %ds\n",
230 xilinx_wwdt_wdd
->timeout
);
235 static const struct of_device_id xwwdt_of_match
[] = {
236 { .compatible
= "xlnx,versal-wwdt", },
239 MODULE_DEVICE_TABLE(of
, xwwdt_of_match
);
241 static struct platform_driver xwwdt_driver
= {
242 .probe
= xwwdt_probe
,
244 .name
= "Xilinx window watchdog",
245 .of_match_table
= xwwdt_of_match
,
249 module_platform_driver(xwwdt_driver
);
251 MODULE_AUTHOR("Neeli Srinivas <srinivas.neeli@amd.com>");
252 MODULE_DESCRIPTION("Xilinx window watchdog driver");
253 MODULE_LICENSE("GPL");