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>
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+
3945static int wwdt_timeout ;
4046static 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 */
5866struct 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
6676static 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