Skip to content

Commit 0067788

Browse files
Harini TWim Van Sebroeck
authored andcommitted
watchdog: xilinx_wwdt: Calculate max_hw_heartbeat_ms using clock frequency
In the current implementation, the value of max_hw_heartbeat_ms is set to the timeout period expressed in milliseconds and fails to verify if the close window percentage exceeds the maximum value that the hardware supports. 1. Calculate max_hw_heartbeat_ms based on input clock frequency. 2. Update frequency check to require a minimum frequency of 1Mhz. 3. Limit the close and open window percent to hardware supported value to avoid truncation. 4. If the user input timeout exceeds the maximum timeout supported, use only open window and the framework supports the higher timeouts. Fixes: 12984ce ("watchdog: xilinx_wwdt: Add Versal window watchdog support") Signed-off-by: Harini T <harini.t@amd.com> Reviewed-by: Guenter Roeck <linux@roeck-us.net> Link: https://lore.kernel.org/r/20240913113230.1939373-1-harini.t@amd.com Signed-off-by: Guenter Roeck <linux@roeck-us.net> Signed-off-by: Wim Van Sebroeck <wim@linux-watchdog.org>
1 parent daa814d commit 0067788

1 file changed

Lines changed: 63 additions & 12 deletions

File tree

drivers/watchdog/xilinx_wwdt.c

Lines changed: 63 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
/*
33
* Window watchdog device driver for Xilinx Versal WWDT
44
*
5-
* Copyright (C) 2022 - 2023, Advanced Micro Devices, Inc.
5+
* Copyright (C) 2022 - 2024, Advanced Micro Devices, Inc.
66
*/
77

88
#include <linux/clk.h>
@@ -36,6 +36,12 @@
3636

3737
#define XWWDT_CLOSE_WINDOW_PERCENT 50
3838

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+
3945
static int wwdt_timeout;
4046
static int closed_window_percent;
4147

@@ -54,34 +60,31 @@ MODULE_PARM_DESC(closed_window_percent,
5460
* @xilinx_wwdt_wdd: watchdog device structure
5561
* @freq: source clock frequency of WWDT
5662
* @close_percent: Closed window percent
63+
* @closed_timeout: Closed window timeout in ticks
64+
* @open_timeout: Open window timeout in ticks
5765
*/
5866
struct xwwdt_device {
5967
void __iomem *base;
6068
spinlock_t spinlock; /* spinlock for register handling */
6169
struct watchdog_device xilinx_wwdt_wdd;
6270
unsigned long freq;
6371
u32 close_percent;
72+
u64 closed_timeout;
73+
u64 open_timeout;
6474
};
6575

6676
static int xilinx_wwdt_start(struct watchdog_device *wdd)
6777
{
6878
struct xwwdt_device *xdev = watchdog_get_drvdata(wdd);
6979
struct watchdog_device *xilinx_wwdt_wdd = &xdev->xilinx_wwdt_wdd;
70-
u64 time_out, closed_timeout, open_timeout;
7180
u32 control_status_reg;
7281

73-
/* Calculate timeout count */
74-
time_out = xdev->freq * wdd->timeout;
75-
closed_timeout = div_u64(time_out * xdev->close_percent, 100);
76-
open_timeout = time_out - closed_timeout;
77-
wdd->min_hw_heartbeat_ms = xdev->close_percent * 10 * wdd->timeout;
78-
7982
spin_lock(&xdev->spinlock);
8083

8184
iowrite32(XWWDT_MWR_MASK, xdev->base + XWWDT_MWR_OFFSET);
8285
iowrite32(~(u32)XWWDT_ESR_WEN_MASK, xdev->base + XWWDT_ESR_OFFSET);
83-
iowrite32((u32)closed_timeout, xdev->base + XWWDT_FWR_OFFSET);
84-
iowrite32((u32)open_timeout, xdev->base + XWWDT_SWR_OFFSET);
86+
iowrite32((u32)xdev->closed_timeout, xdev->base + XWWDT_FWR_OFFSET);
87+
iowrite32((u32)xdev->open_timeout, xdev->base + XWWDT_SWR_OFFSET);
8588

8689
/* Enable the window watchdog timer */
8790
control_status_reg = ioread32(xdev->base + XWWDT_ESR_OFFSET);
@@ -133,7 +136,12 @@ static int xwwdt_probe(struct platform_device *pdev)
133136
struct watchdog_device *xilinx_wwdt_wdd;
134137
struct device *dev = &pdev->dev;
135138
struct xwwdt_device *xdev;
139+
u64 max_per_window_ms;
140+
u64 min_per_window_ms;
141+
u64 timeout_count;
136142
struct clk *clk;
143+
u32 timeout_ms;
144+
u64 ms_count;
137145
int ret;
138146

139147
xdev = devm_kzalloc(dev, sizeof(*xdev), GFP_KERNEL);
@@ -154,19 +162,62 @@ static int xwwdt_probe(struct platform_device *pdev)
154162
return PTR_ERR(clk);
155163

156164
xdev->freq = clk_get_rate(clk);
157-
if (!xdev->freq)
165+
if (xdev->freq < 1000000)
158166
return -EINVAL;
159167

160168
xilinx_wwdt_wdd->min_timeout = XWWDT_MIN_TIMEOUT;
161169
xilinx_wwdt_wdd->timeout = XWWDT_DEFAULT_TIMEOUT;
162-
xilinx_wwdt_wdd->max_hw_heartbeat_ms = 1000 * xilinx_wwdt_wdd->timeout;
170+
xilinx_wwdt_wdd->max_hw_heartbeat_ms =
171+
div64_u64(XWWDT_MAX_COUNT_WINDOW_COMBINED, xdev->freq) * 1000;
163172

164173
if (closed_window_percent == 0 || closed_window_percent >= 100)
165174
xdev->close_percent = XWWDT_CLOSE_WINDOW_PERCENT;
166175
else
167176
xdev->close_percent = closed_window_percent;
168177

169178
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+
170221
spin_lock_init(&xdev->spinlock);
171222
watchdog_set_drvdata(xilinx_wwdt_wdd, xdev);
172223
watchdog_set_nowayout(xilinx_wwdt_wdd, 1);

0 commit comments

Comments
 (0)