35
35
36
36
#define RTIWWDRX_NMI 0xa
37
37
38
- #define RTIWWDSIZE_50P 0x50
38
+ #define RTIWWDSIZE_50P 0x50
39
+ #define RTIWWDSIZE_25P 0x500
40
+ #define RTIWWDSIZE_12P5 0x5000
41
+ #define RTIWWDSIZE_6P25 0x50000
42
+ #define RTIWWDSIZE_3P125 0x500000
39
43
40
44
#define WDENABLE_KEY 0xa98559da
41
45
48
52
49
53
#define DWDST BIT(1)
50
54
51
- static int heartbeat ;
55
+ static int heartbeat = DEFAULT_HEARTBEAT ;
52
56
53
57
/*
54
58
* struct to hold data for each WDT device
@@ -79,11 +83,9 @@ static int rti_wdt_start(struct watchdog_device *wdd)
79
83
* be petted during the open window; not too early or not too late.
80
84
* The HW configuration options only allow for the open window size
81
85
* to be 50% or less than that; we obviouly want to configure the open
82
- * window as large as possible so we select the 50% option. To avoid
83
- * any glitches, we accommodate 5% safety margin also, so we setup
84
- * the min_hw_hearbeat at 55% of the timeout period.
86
+ * window as large as possible so we select the 50% option.
85
87
*/
86
- wdd -> min_hw_heartbeat_ms = 11 * wdd -> timeout * 1000 / 20 ;
88
+ wdd -> min_hw_heartbeat_ms = 500 * wdd -> timeout ;
87
89
88
90
/* Generate NMI when wdt expires */
89
91
writel_relaxed (RTIWWDRX_NMI , wdt -> base + RTIWWDRXCTRL );
@@ -110,7 +112,48 @@ static int rti_wdt_ping(struct watchdog_device *wdd)
110
112
return 0 ;
111
113
}
112
114
113
- static unsigned int rti_wdt_get_timeleft (struct watchdog_device * wdd )
115
+ static int rti_wdt_setup_hw_hb (struct watchdog_device * wdd , u32 wsize )
116
+ {
117
+ /*
118
+ * RTI only supports a windowed mode, where the watchdog can only
119
+ * be petted during the open window; not too early or not too late.
120
+ * The HW configuration options only allow for the open window size
121
+ * to be 50% or less than that.
122
+ */
123
+ switch (wsize ) {
124
+ case RTIWWDSIZE_50P :
125
+ /* 50% open window => 50% min heartbeat */
126
+ wdd -> min_hw_heartbeat_ms = 500 * heartbeat ;
127
+ break ;
128
+
129
+ case RTIWWDSIZE_25P :
130
+ /* 25% open window => 75% min heartbeat */
131
+ wdd -> min_hw_heartbeat_ms = 750 * heartbeat ;
132
+ break ;
133
+
134
+ case RTIWWDSIZE_12P5 :
135
+ /* 12.5% open window => 87.5% min heartbeat */
136
+ wdd -> min_hw_heartbeat_ms = 875 * heartbeat ;
137
+ break ;
138
+
139
+ case RTIWWDSIZE_6P25 :
140
+ /* 6.5% open window => 93.5% min heartbeat */
141
+ wdd -> min_hw_heartbeat_ms = 935 * heartbeat ;
142
+ break ;
143
+
144
+ case RTIWWDSIZE_3P125 :
145
+ /* 3.125% open window => 96.9% min heartbeat */
146
+ wdd -> min_hw_heartbeat_ms = 969 * heartbeat ;
147
+ break ;
148
+
149
+ default :
150
+ return - EINVAL ;
151
+ }
152
+
153
+ return 0 ;
154
+ }
155
+
156
+ static unsigned int rti_wdt_get_timeleft_ms (struct watchdog_device * wdd )
114
157
{
115
158
u64 timer_counter ;
116
159
u32 val ;
@@ -123,11 +166,18 @@ static unsigned int rti_wdt_get_timeleft(struct watchdog_device *wdd)
123
166
124
167
timer_counter = readl_relaxed (wdt -> base + RTIDWDCNTR );
125
168
169
+ timer_counter *= 1000 ;
170
+
126
171
do_div (timer_counter , wdt -> freq );
127
172
128
173
return timer_counter ;
129
174
}
130
175
176
+ static unsigned int rti_wdt_get_timeleft (struct watchdog_device * wdd )
177
+ {
178
+ return rti_wdt_get_timeleft_ms (wdd ) / 1000 ;
179
+ }
180
+
131
181
static const struct watchdog_info rti_wdt_info = {
132
182
.options = WDIOF_KEEPALIVEPING ,
133
183
.identity = "K3 RTI Watchdog" ,
@@ -148,6 +198,7 @@ static int rti_wdt_probe(struct platform_device *pdev)
148
198
struct watchdog_device * wdd ;
149
199
struct rti_wdt_device * wdt ;
150
200
struct clk * clk ;
201
+ u32 last_ping = 0 ;
151
202
152
203
wdt = devm_kzalloc (dev , sizeof (* wdt ), GFP_KERNEL );
153
204
if (!wdt )
@@ -169,6 +220,14 @@ static int rti_wdt_probe(struct platform_device *pdev)
169
220
return - EINVAL ;
170
221
}
171
222
223
+ /*
224
+ * If watchdog is running at 32k clock, it is not accurate.
225
+ * Adjust frequency down in this case so that we don't pet
226
+ * the watchdog too often.
227
+ */
228
+ if (wdt -> freq < 32768 )
229
+ wdt -> freq = wdt -> freq * 9 / 10 ;
230
+
172
231
pm_runtime_enable (dev );
173
232
ret = pm_runtime_get_sync (dev );
174
233
if (ret ) {
@@ -185,11 +244,8 @@ static int rti_wdt_probe(struct platform_device *pdev)
185
244
wdd -> min_timeout = 1 ;
186
245
wdd -> max_hw_heartbeat_ms = (WDT_PRELOAD_MAX << WDT_PRELOAD_SHIFT ) /
187
246
wdt -> freq * 1000 ;
188
- wdd -> timeout = DEFAULT_HEARTBEAT ;
189
247
wdd -> parent = dev ;
190
248
191
- watchdog_init_timeout (wdd , heartbeat , dev );
192
-
193
249
watchdog_set_drvdata (wdd , wdt );
194
250
watchdog_set_nowayout (wdd , 1 );
195
251
watchdog_set_restart_priority (wdd , 128 );
@@ -201,12 +257,48 @@ static int rti_wdt_probe(struct platform_device *pdev)
201
257
goto err_iomap ;
202
258
}
203
259
260
+ if (readl (wdt -> base + RTIDWDCTRL ) == WDENABLE_KEY ) {
261
+ u32 time_left_ms ;
262
+ u64 heartbeat_ms ;
263
+ u32 wsize ;
264
+
265
+ set_bit (WDOG_HW_RUNNING , & wdd -> status );
266
+ time_left_ms = rti_wdt_get_timeleft_ms (wdd );
267
+ heartbeat_ms = readl (wdt -> base + RTIDWDPRLD );
268
+ heartbeat_ms <<= WDT_PRELOAD_SHIFT ;
269
+ heartbeat_ms *= 1000 ;
270
+ do_div (heartbeat_ms , wdt -> freq );
271
+ if (heartbeat_ms != heartbeat * 1000 )
272
+ dev_warn (dev , "watchdog already running, ignoring heartbeat config!\n" );
273
+
274
+ heartbeat = heartbeat_ms ;
275
+ heartbeat /= 1000 ;
276
+
277
+ wsize = readl (wdt -> base + RTIWWDSIZECTRL );
278
+ ret = rti_wdt_setup_hw_hb (wdd , wsize );
279
+ if (ret ) {
280
+ dev_err (dev , "bad window size.\n" );
281
+ goto err_iomap ;
282
+ }
283
+
284
+ last_ping = heartbeat_ms - time_left_ms ;
285
+ if (time_left_ms > heartbeat_ms ) {
286
+ dev_warn (dev , "time_left > heartbeat? Assuming last ping just before now.\n" );
287
+ last_ping = 0 ;
288
+ }
289
+ }
290
+
291
+ watchdog_init_timeout (wdd , heartbeat , dev );
292
+
204
293
ret = watchdog_register_device (wdd );
205
294
if (ret ) {
206
295
dev_err (dev , "cannot register watchdog device\n" );
207
296
goto err_iomap ;
208
297
}
209
298
299
+ if (last_ping )
300
+ watchdog_set_last_hw_keepalive (wdd , last_ping );
301
+
210
302
return 0 ;
211
303
212
304
err_iomap :
0 commit comments