.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
---|
1 | 2 | /* |
---|
2 | 3 | * Copyright 2010-2011 Picochip Ltd., Jamie Iles |
---|
3 | | - * http://www.picochip.com |
---|
4 | | - * |
---|
5 | | - * This program is free software; you can redistribute it and/or |
---|
6 | | - * modify it under the terms of the GNU General Public License |
---|
7 | | - * as published by the Free Software Foundation; either version |
---|
8 | | - * 2 of the License, or (at your option) any later version. |
---|
| 4 | + * https://www.picochip.com |
---|
9 | 5 | * |
---|
10 | 6 | * This file implements a driver for the Synopsys DesignWare watchdog device |
---|
11 | 7 | * in the many subsystems. The watchdog has 16 different timeout periods |
---|
.. | .. |
---|
16 | 12 | * heartbeat requests after the watchdog device has been closed. |
---|
17 | 13 | */ |
---|
18 | 14 | |
---|
19 | | -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
---|
20 | | - |
---|
21 | 15 | #include <linux/bitops.h> |
---|
| 16 | +#include <linux/limits.h> |
---|
| 17 | +#include <linux/kernel.h> |
---|
22 | 18 | #include <linux/clk.h> |
---|
23 | 19 | #include <linux/delay.h> |
---|
24 | 20 | #include <linux/err.h> |
---|
.. | .. |
---|
26 | 22 | #include <linux/kernel.h> |
---|
27 | 23 | #include <linux/module.h> |
---|
28 | 24 | #include <linux/moduleparam.h> |
---|
| 25 | +#include <linux/interrupt.h> |
---|
29 | 26 | #include <linux/of.h> |
---|
30 | 27 | #include <linux/pm.h> |
---|
31 | 28 | #include <linux/platform_device.h> |
---|
32 | 29 | #include <linux/reset.h> |
---|
33 | 30 | #include <linux/watchdog.h> |
---|
| 31 | +#include <linux/debugfs.h> |
---|
34 | 32 | |
---|
35 | 33 | #define WDOG_CONTROL_REG_OFFSET 0x00 |
---|
36 | 34 | #define WDOG_CONTROL_REG_WDT_EN_MASK 0x01 |
---|
.. | .. |
---|
40 | 38 | #define WDOG_CURRENT_COUNT_REG_OFFSET 0x08 |
---|
41 | 39 | #define WDOG_COUNTER_RESTART_REG_OFFSET 0x0c |
---|
42 | 40 | #define WDOG_COUNTER_RESTART_KICK_VALUE 0x76 |
---|
| 41 | +#define WDOG_INTERRUPT_STATUS_REG_OFFSET 0x10 |
---|
| 42 | +#define WDOG_INTERRUPT_CLEAR_REG_OFFSET 0x14 |
---|
| 43 | +#define WDOG_COMP_PARAMS_5_REG_OFFSET 0xe4 |
---|
| 44 | +#define WDOG_COMP_PARAMS_4_REG_OFFSET 0xe8 |
---|
| 45 | +#define WDOG_COMP_PARAMS_3_REG_OFFSET 0xec |
---|
| 46 | +#define WDOG_COMP_PARAMS_2_REG_OFFSET 0xf0 |
---|
| 47 | +#define WDOG_COMP_PARAMS_1_REG_OFFSET 0xf4 |
---|
| 48 | +#define WDOG_COMP_PARAMS_1_USE_FIX_TOP BIT(6) |
---|
| 49 | +#define WDOG_COMP_VERSION_REG_OFFSET 0xf8 |
---|
| 50 | +#define WDOG_COMP_TYPE_REG_OFFSET 0xfc |
---|
43 | 51 | |
---|
44 | | -/* The maximum TOP (timeout period) value that can be set in the watchdog. */ |
---|
45 | | -#define DW_WDT_MAX_TOP 15 |
---|
| 52 | +/* There are sixteen TOPs (timeout periods) that can be set in the watchdog. */ |
---|
| 53 | +#define DW_WDT_NUM_TOPS 16 |
---|
| 54 | +#define DW_WDT_FIX_TOP(_idx) (1U << (16 + _idx)) |
---|
46 | 55 | |
---|
47 | 56 | #define DW_WDT_DEFAULT_SECONDS 30 |
---|
| 57 | + |
---|
| 58 | +static const u32 dw_wdt_fix_tops[DW_WDT_NUM_TOPS] = { |
---|
| 59 | + DW_WDT_FIX_TOP(0), DW_WDT_FIX_TOP(1), DW_WDT_FIX_TOP(2), |
---|
| 60 | + DW_WDT_FIX_TOP(3), DW_WDT_FIX_TOP(4), DW_WDT_FIX_TOP(5), |
---|
| 61 | + DW_WDT_FIX_TOP(6), DW_WDT_FIX_TOP(7), DW_WDT_FIX_TOP(8), |
---|
| 62 | + DW_WDT_FIX_TOP(9), DW_WDT_FIX_TOP(10), DW_WDT_FIX_TOP(11), |
---|
| 63 | + DW_WDT_FIX_TOP(12), DW_WDT_FIX_TOP(13), DW_WDT_FIX_TOP(14), |
---|
| 64 | + DW_WDT_FIX_TOP(15) |
---|
| 65 | +}; |
---|
48 | 66 | |
---|
49 | 67 | static bool nowayout = WATCHDOG_NOWAYOUT; |
---|
50 | 68 | module_param(nowayout, bool, 0); |
---|
51 | 69 | MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started " |
---|
52 | 70 | "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); |
---|
53 | 71 | |
---|
| 72 | +enum dw_wdt_rmod { |
---|
| 73 | + DW_WDT_RMOD_RESET = 1, |
---|
| 74 | + DW_WDT_RMOD_IRQ = 2 |
---|
| 75 | +}; |
---|
| 76 | + |
---|
| 77 | +struct dw_wdt_timeout { |
---|
| 78 | + u32 top_val; |
---|
| 79 | + unsigned int sec; |
---|
| 80 | + unsigned int msec; |
---|
| 81 | +}; |
---|
| 82 | + |
---|
54 | 83 | struct dw_wdt { |
---|
55 | 84 | void __iomem *regs; |
---|
56 | 85 | struct clk *clk; |
---|
57 | 86 | struct clk *pclk; |
---|
58 | 87 | unsigned long rate; |
---|
| 88 | + enum dw_wdt_rmod rmod; |
---|
| 89 | + struct dw_wdt_timeout timeouts[DW_WDT_NUM_TOPS]; |
---|
59 | 90 | struct watchdog_device wdd; |
---|
60 | 91 | struct reset_control *rst; |
---|
61 | 92 | /* Save/restore */ |
---|
62 | 93 | u32 control; |
---|
63 | 94 | u32 timeout; |
---|
| 95 | + |
---|
| 96 | +#ifdef CONFIG_DEBUG_FS |
---|
| 97 | + struct dentry *dbgfs_dir; |
---|
| 98 | +#endif |
---|
64 | 99 | }; |
---|
65 | 100 | |
---|
66 | 101 | #define to_dw_wdt(wdd) container_of(wdd, struct dw_wdt, wdd) |
---|
.. | .. |
---|
71 | 106 | WDOG_CONTROL_REG_WDT_EN_MASK; |
---|
72 | 107 | } |
---|
73 | 108 | |
---|
74 | | -static inline int dw_wdt_top_in_seconds(struct dw_wdt *dw_wdt, unsigned top) |
---|
| 109 | +static void dw_wdt_update_mode(struct dw_wdt *dw_wdt, enum dw_wdt_rmod rmod) |
---|
75 | 110 | { |
---|
76 | | - /* |
---|
77 | | - * There are 16 possible timeout values in 0..15 where the number of |
---|
78 | | - * cycles is 2 ^ (16 + i) and the watchdog counts down. |
---|
79 | | - */ |
---|
80 | | - return (1U << (16 + top)) / dw_wdt->rate; |
---|
| 111 | + u32 val; |
---|
| 112 | + |
---|
| 113 | + val = readl(dw_wdt->regs + WDOG_CONTROL_REG_OFFSET); |
---|
| 114 | + if (rmod == DW_WDT_RMOD_IRQ) |
---|
| 115 | + val |= WDOG_CONTROL_REG_RESP_MODE_MASK; |
---|
| 116 | + else |
---|
| 117 | + val &= ~WDOG_CONTROL_REG_RESP_MODE_MASK; |
---|
| 118 | + writel(val, dw_wdt->regs + WDOG_CONTROL_REG_OFFSET); |
---|
| 119 | + |
---|
| 120 | + dw_wdt->rmod = rmod; |
---|
81 | 121 | } |
---|
82 | 122 | |
---|
83 | | -static int dw_wdt_get_top(struct dw_wdt *dw_wdt) |
---|
| 123 | +static unsigned int dw_wdt_find_best_top(struct dw_wdt *dw_wdt, |
---|
| 124 | + unsigned int timeout, u32 *top_val) |
---|
84 | 125 | { |
---|
85 | | - int top = readl(dw_wdt->regs + WDOG_TIMEOUT_RANGE_REG_OFFSET) & 0xF; |
---|
| 126 | + int idx; |
---|
86 | 127 | |
---|
87 | | - return dw_wdt_top_in_seconds(dw_wdt, top); |
---|
| 128 | + /* |
---|
| 129 | + * Find a TOP with timeout greater or equal to the requested number. |
---|
| 130 | + * Note we'll select a TOP with maximum timeout if the requested |
---|
| 131 | + * timeout couldn't be reached. |
---|
| 132 | + */ |
---|
| 133 | + for (idx = 0; idx < DW_WDT_NUM_TOPS; ++idx) { |
---|
| 134 | + if (dw_wdt->timeouts[idx].sec >= timeout) |
---|
| 135 | + break; |
---|
| 136 | + } |
---|
| 137 | + |
---|
| 138 | + if (idx == DW_WDT_NUM_TOPS) |
---|
| 139 | + --idx; |
---|
| 140 | + |
---|
| 141 | + *top_val = dw_wdt->timeouts[idx].top_val; |
---|
| 142 | + |
---|
| 143 | + return dw_wdt->timeouts[idx].sec; |
---|
| 144 | +} |
---|
| 145 | + |
---|
| 146 | +static unsigned int dw_wdt_get_min_timeout(struct dw_wdt *dw_wdt) |
---|
| 147 | +{ |
---|
| 148 | + int idx; |
---|
| 149 | + |
---|
| 150 | + /* |
---|
| 151 | + * We'll find a timeout greater or equal to one second anyway because |
---|
| 152 | + * the driver probe would have failed if there was none. |
---|
| 153 | + */ |
---|
| 154 | + for (idx = 0; idx < DW_WDT_NUM_TOPS; ++idx) { |
---|
| 155 | + if (dw_wdt->timeouts[idx].sec) |
---|
| 156 | + break; |
---|
| 157 | + } |
---|
| 158 | + |
---|
| 159 | + return dw_wdt->timeouts[idx].sec; |
---|
| 160 | +} |
---|
| 161 | + |
---|
| 162 | +static unsigned int dw_wdt_get_max_timeout_ms(struct dw_wdt *dw_wdt) |
---|
| 163 | +{ |
---|
| 164 | + struct dw_wdt_timeout *timeout = &dw_wdt->timeouts[DW_WDT_NUM_TOPS - 1]; |
---|
| 165 | + u64 msec; |
---|
| 166 | + |
---|
| 167 | + msec = (u64)timeout->sec * MSEC_PER_SEC + timeout->msec; |
---|
| 168 | + |
---|
| 169 | + return msec < UINT_MAX ? msec : UINT_MAX; |
---|
| 170 | +} |
---|
| 171 | + |
---|
| 172 | +static unsigned int dw_wdt_get_timeout(struct dw_wdt *dw_wdt) |
---|
| 173 | +{ |
---|
| 174 | + int top_val = readl(dw_wdt->regs + WDOG_TIMEOUT_RANGE_REG_OFFSET) & 0xF; |
---|
| 175 | + int idx; |
---|
| 176 | + |
---|
| 177 | + for (idx = 0; idx < DW_WDT_NUM_TOPS; ++idx) { |
---|
| 178 | + if (dw_wdt->timeouts[idx].top_val == top_val) |
---|
| 179 | + break; |
---|
| 180 | + } |
---|
| 181 | + |
---|
| 182 | + /* |
---|
| 183 | + * In IRQ mode due to the two stages counter, the actual timeout is |
---|
| 184 | + * twice greater than the TOP setting. |
---|
| 185 | + */ |
---|
| 186 | + return dw_wdt->timeouts[idx].sec * dw_wdt->rmod; |
---|
88 | 187 | } |
---|
89 | 188 | |
---|
90 | 189 | static int dw_wdt_ping(struct watchdog_device *wdd) |
---|
.. | .. |
---|
100 | 199 | static int dw_wdt_set_timeout(struct watchdog_device *wdd, unsigned int top_s) |
---|
101 | 200 | { |
---|
102 | 201 | struct dw_wdt *dw_wdt = to_dw_wdt(wdd); |
---|
103 | | - int i, top_val = DW_WDT_MAX_TOP; |
---|
| 202 | + unsigned int timeout; |
---|
| 203 | + u32 top_val; |
---|
104 | 204 | |
---|
105 | 205 | /* |
---|
106 | | - * Iterate over the timeout values until we find the closest match. We |
---|
107 | | - * always look for >=. |
---|
| 206 | + * Note IRQ mode being enabled means having a non-zero pre-timeout |
---|
| 207 | + * setup. In this case we try to find a TOP as close to the half of the |
---|
| 208 | + * requested timeout as possible since DW Watchdog IRQ mode is designed |
---|
| 209 | + * in two stages way - first timeout rises the pre-timeout interrupt, |
---|
| 210 | + * second timeout performs the system reset. So basically the effective |
---|
| 211 | + * watchdog-caused reset happens after two watchdog TOPs elapsed. |
---|
108 | 212 | */ |
---|
109 | | - for (i = 0; i <= DW_WDT_MAX_TOP; ++i) |
---|
110 | | - if (dw_wdt_top_in_seconds(dw_wdt, i) >= top_s) { |
---|
111 | | - top_val = i; |
---|
112 | | - break; |
---|
113 | | - } |
---|
| 213 | + timeout = dw_wdt_find_best_top(dw_wdt, DIV_ROUND_UP(top_s, dw_wdt->rmod), |
---|
| 214 | + &top_val); |
---|
| 215 | + if (dw_wdt->rmod == DW_WDT_RMOD_IRQ) |
---|
| 216 | + wdd->pretimeout = timeout; |
---|
| 217 | + else |
---|
| 218 | + wdd->pretimeout = 0; |
---|
114 | 219 | |
---|
115 | 220 | /* |
---|
116 | 221 | * Set the new value in the watchdog. Some versions of dw_wdt |
---|
.. | .. |
---|
121 | 226 | writel(top_val | top_val << WDOG_TIMEOUT_RANGE_TOPINIT_SHIFT, |
---|
122 | 227 | dw_wdt->regs + WDOG_TIMEOUT_RANGE_REG_OFFSET); |
---|
123 | 228 | |
---|
124 | | - wdd->timeout = dw_wdt_top_in_seconds(dw_wdt, top_val); |
---|
| 229 | + /* Kick new TOP value into the watchdog counter if activated. */ |
---|
| 230 | + if (watchdog_active(wdd)) |
---|
| 231 | + dw_wdt_ping(wdd); |
---|
| 232 | + |
---|
| 233 | + /* |
---|
| 234 | + * In case users set bigger timeout value than HW can support, |
---|
| 235 | + * kernel(watchdog_dev.c) helps to feed watchdog before |
---|
| 236 | + * wdd->max_hw_heartbeat_ms |
---|
| 237 | + */ |
---|
| 238 | + if (top_s * 1000 <= wdd->max_hw_heartbeat_ms) |
---|
| 239 | + wdd->timeout = timeout * dw_wdt->rmod; |
---|
| 240 | + else |
---|
| 241 | + wdd->timeout = top_s; |
---|
| 242 | + |
---|
| 243 | + return 0; |
---|
| 244 | +} |
---|
| 245 | + |
---|
| 246 | +static int dw_wdt_set_pretimeout(struct watchdog_device *wdd, unsigned int req) |
---|
| 247 | +{ |
---|
| 248 | + struct dw_wdt *dw_wdt = to_dw_wdt(wdd); |
---|
| 249 | + |
---|
| 250 | + /* |
---|
| 251 | + * We ignore actual value of the timeout passed from user-space |
---|
| 252 | + * using it as a flag whether the pretimeout functionality is intended |
---|
| 253 | + * to be activated. |
---|
| 254 | + */ |
---|
| 255 | + dw_wdt_update_mode(dw_wdt, req ? DW_WDT_RMOD_IRQ : DW_WDT_RMOD_RESET); |
---|
| 256 | + dw_wdt_set_timeout(wdd, wdd->timeout); |
---|
125 | 257 | |
---|
126 | 258 | return 0; |
---|
127 | 259 | } |
---|
.. | .. |
---|
130 | 262 | { |
---|
131 | 263 | u32 val = readl(dw_wdt->regs + WDOG_CONTROL_REG_OFFSET); |
---|
132 | 264 | |
---|
133 | | - /* Disable interrupt mode; always perform system reset. */ |
---|
134 | | - val &= ~WDOG_CONTROL_REG_RESP_MODE_MASK; |
---|
| 265 | + /* Disable/enable interrupt mode depending on the RMOD flag. */ |
---|
| 266 | + if (dw_wdt->rmod == DW_WDT_RMOD_IRQ) |
---|
| 267 | + val |= WDOG_CONTROL_REG_RESP_MODE_MASK; |
---|
| 268 | + else |
---|
| 269 | + val &= ~WDOG_CONTROL_REG_RESP_MODE_MASK; |
---|
135 | 270 | /* Enable watchdog. */ |
---|
136 | 271 | val |= WDOG_CONTROL_REG_WDT_EN_MASK; |
---|
137 | 272 | writel(val, dw_wdt->regs + WDOG_CONTROL_REG_OFFSET); |
---|
.. | .. |
---|
169 | 304 | struct dw_wdt *dw_wdt = to_dw_wdt(wdd); |
---|
170 | 305 | |
---|
171 | 306 | writel(0, dw_wdt->regs + WDOG_TIMEOUT_RANGE_REG_OFFSET); |
---|
| 307 | + dw_wdt_update_mode(dw_wdt, DW_WDT_RMOD_RESET); |
---|
172 | 308 | if (dw_wdt_is_enabled(dw_wdt)) |
---|
173 | 309 | writel(WDOG_COUNTER_RESTART_KICK_VALUE, |
---|
174 | 310 | dw_wdt->regs + WDOG_COUNTER_RESTART_REG_OFFSET); |
---|
.. | .. |
---|
184 | 320 | static unsigned int dw_wdt_get_timeleft(struct watchdog_device *wdd) |
---|
185 | 321 | { |
---|
186 | 322 | struct dw_wdt *dw_wdt = to_dw_wdt(wdd); |
---|
| 323 | + unsigned int sec; |
---|
| 324 | + u32 val; |
---|
187 | 325 | |
---|
188 | | - return readl(dw_wdt->regs + WDOG_CURRENT_COUNT_REG_OFFSET) / |
---|
189 | | - dw_wdt->rate; |
---|
| 326 | + val = readl(dw_wdt->regs + WDOG_CURRENT_COUNT_REG_OFFSET); |
---|
| 327 | + sec = val / dw_wdt->rate; |
---|
| 328 | + |
---|
| 329 | + if (dw_wdt->rmod == DW_WDT_RMOD_IRQ) { |
---|
| 330 | + val = readl(dw_wdt->regs + WDOG_INTERRUPT_STATUS_REG_OFFSET); |
---|
| 331 | + if (!val) |
---|
| 332 | + sec += wdd->pretimeout; |
---|
| 333 | + } |
---|
| 334 | + |
---|
| 335 | + return sec; |
---|
190 | 336 | } |
---|
191 | 337 | |
---|
192 | 338 | static const struct watchdog_info dw_wdt_ident = { |
---|
193 | 339 | .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | |
---|
194 | 340 | WDIOF_MAGICCLOSE, |
---|
| 341 | + .identity = "Synopsys DesignWare Watchdog", |
---|
| 342 | +}; |
---|
| 343 | + |
---|
| 344 | +static const struct watchdog_info dw_wdt_pt_ident = { |
---|
| 345 | + .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | |
---|
| 346 | + WDIOF_PRETIMEOUT | WDIOF_MAGICCLOSE, |
---|
195 | 347 | .identity = "Synopsys DesignWare Watchdog", |
---|
196 | 348 | }; |
---|
197 | 349 | |
---|
.. | .. |
---|
201 | 353 | .stop = dw_wdt_stop, |
---|
202 | 354 | .ping = dw_wdt_ping, |
---|
203 | 355 | .set_timeout = dw_wdt_set_timeout, |
---|
| 356 | + .set_pretimeout = dw_wdt_set_pretimeout, |
---|
204 | 357 | .get_timeleft = dw_wdt_get_timeleft, |
---|
205 | 358 | .restart = dw_wdt_restart, |
---|
206 | 359 | }; |
---|
| 360 | + |
---|
| 361 | +static irqreturn_t dw_wdt_irq(int irq, void *devid) |
---|
| 362 | +{ |
---|
| 363 | + struct dw_wdt *dw_wdt = devid; |
---|
| 364 | + u32 val; |
---|
| 365 | + |
---|
| 366 | + /* |
---|
| 367 | + * We don't clear the IRQ status. It's supposed to be done by the |
---|
| 368 | + * following ping operations. |
---|
| 369 | + */ |
---|
| 370 | + val = readl(dw_wdt->regs + WDOG_INTERRUPT_STATUS_REG_OFFSET); |
---|
| 371 | + if (!val) |
---|
| 372 | + return IRQ_NONE; |
---|
| 373 | + |
---|
| 374 | + watchdog_notify_pretimeout(&dw_wdt->wdd); |
---|
| 375 | + |
---|
| 376 | + return IRQ_HANDLED; |
---|
| 377 | +} |
---|
207 | 378 | |
---|
208 | 379 | #ifdef CONFIG_PM_SLEEP |
---|
209 | 380 | static int dw_wdt_suspend(struct device *dev) |
---|
.. | .. |
---|
244 | 415 | |
---|
245 | 416 | static SIMPLE_DEV_PM_OPS(dw_wdt_pm_ops, dw_wdt_suspend, dw_wdt_resume); |
---|
246 | 417 | |
---|
| 418 | +/* |
---|
| 419 | + * In case if DW WDT IP core is synthesized with fixed TOP feature disabled the |
---|
| 420 | + * TOPs array can be arbitrary ordered with nearly any sixteen uint numbers |
---|
| 421 | + * depending on the system engineer imagination. The next method handles the |
---|
| 422 | + * passed TOPs array to pre-calculate the effective timeouts and to sort the |
---|
| 423 | + * TOP items out in the ascending order with respect to the timeouts. |
---|
| 424 | + */ |
---|
| 425 | + |
---|
| 426 | +static void dw_wdt_handle_tops(struct dw_wdt *dw_wdt, const u32 *tops) |
---|
| 427 | +{ |
---|
| 428 | + struct dw_wdt_timeout tout, *dst; |
---|
| 429 | + int val, tidx; |
---|
| 430 | + u64 msec; |
---|
| 431 | + |
---|
| 432 | + /* |
---|
| 433 | + * We walk over the passed TOPs array and calculate corresponding |
---|
| 434 | + * timeouts in seconds and milliseconds. The milliseconds granularity |
---|
| 435 | + * is needed to distinguish the TOPs with very close timeouts and to |
---|
| 436 | + * set the watchdog max heartbeat setting further. |
---|
| 437 | + */ |
---|
| 438 | + for (val = 0; val < DW_WDT_NUM_TOPS; ++val) { |
---|
| 439 | + tout.top_val = val; |
---|
| 440 | + tout.sec = tops[val] / dw_wdt->rate; |
---|
| 441 | + msec = (u64)tops[val] * MSEC_PER_SEC; |
---|
| 442 | + do_div(msec, dw_wdt->rate); |
---|
| 443 | + tout.msec = msec - ((u64)tout.sec * MSEC_PER_SEC); |
---|
| 444 | + |
---|
| 445 | + /* |
---|
| 446 | + * Find a suitable place for the current TOP in the timeouts |
---|
| 447 | + * array so that the list is remained in the ascending order. |
---|
| 448 | + */ |
---|
| 449 | + for (tidx = 0; tidx < val; ++tidx) { |
---|
| 450 | + dst = &dw_wdt->timeouts[tidx]; |
---|
| 451 | + if (tout.sec > dst->sec || (tout.sec == dst->sec && |
---|
| 452 | + tout.msec >= dst->msec)) |
---|
| 453 | + continue; |
---|
| 454 | + else |
---|
| 455 | + swap(*dst, tout); |
---|
| 456 | + } |
---|
| 457 | + |
---|
| 458 | + dw_wdt->timeouts[val] = tout; |
---|
| 459 | + } |
---|
| 460 | +} |
---|
| 461 | + |
---|
| 462 | +static int dw_wdt_init_timeouts(struct dw_wdt *dw_wdt, struct device *dev) |
---|
| 463 | +{ |
---|
| 464 | + u32 data, of_tops[DW_WDT_NUM_TOPS]; |
---|
| 465 | + const u32 *tops; |
---|
| 466 | + int ret; |
---|
| 467 | + |
---|
| 468 | + /* |
---|
| 469 | + * Retrieve custom or fixed counter values depending on the |
---|
| 470 | + * WDT_USE_FIX_TOP flag found in the component specific parameters |
---|
| 471 | + * #1 register. |
---|
| 472 | + */ |
---|
| 473 | + data = readl(dw_wdt->regs + WDOG_COMP_PARAMS_1_REG_OFFSET); |
---|
| 474 | + if (data & WDOG_COMP_PARAMS_1_USE_FIX_TOP) { |
---|
| 475 | + tops = dw_wdt_fix_tops; |
---|
| 476 | + } else { |
---|
| 477 | + ret = of_property_read_variable_u32_array(dev_of_node(dev), |
---|
| 478 | + "snps,watchdog-tops", of_tops, DW_WDT_NUM_TOPS, |
---|
| 479 | + DW_WDT_NUM_TOPS); |
---|
| 480 | + if (ret < 0) { |
---|
| 481 | + dev_warn(dev, "No valid TOPs array specified\n"); |
---|
| 482 | + tops = dw_wdt_fix_tops; |
---|
| 483 | + } else { |
---|
| 484 | + tops = of_tops; |
---|
| 485 | + } |
---|
| 486 | + } |
---|
| 487 | + |
---|
| 488 | + /* Convert the specified TOPs into an array of watchdog timeouts. */ |
---|
| 489 | + dw_wdt_handle_tops(dw_wdt, tops); |
---|
| 490 | + if (!dw_wdt->timeouts[DW_WDT_NUM_TOPS - 1].sec) { |
---|
| 491 | + dev_err(dev, "No any valid TOP detected\n"); |
---|
| 492 | + return -EINVAL; |
---|
| 493 | + } |
---|
| 494 | + |
---|
| 495 | + return 0; |
---|
| 496 | +} |
---|
| 497 | + |
---|
| 498 | +#ifdef CONFIG_DEBUG_FS |
---|
| 499 | + |
---|
| 500 | +#define DW_WDT_DBGFS_REG(_name, _off) \ |
---|
| 501 | +{ \ |
---|
| 502 | + .name = _name, \ |
---|
| 503 | + .offset = _off \ |
---|
| 504 | +} |
---|
| 505 | + |
---|
| 506 | +static const struct debugfs_reg32 dw_wdt_dbgfs_regs[] = { |
---|
| 507 | + DW_WDT_DBGFS_REG("cr", WDOG_CONTROL_REG_OFFSET), |
---|
| 508 | + DW_WDT_DBGFS_REG("torr", WDOG_TIMEOUT_RANGE_REG_OFFSET), |
---|
| 509 | + DW_WDT_DBGFS_REG("ccvr", WDOG_CURRENT_COUNT_REG_OFFSET), |
---|
| 510 | + DW_WDT_DBGFS_REG("crr", WDOG_COUNTER_RESTART_REG_OFFSET), |
---|
| 511 | + DW_WDT_DBGFS_REG("stat", WDOG_INTERRUPT_STATUS_REG_OFFSET), |
---|
| 512 | + DW_WDT_DBGFS_REG("param5", WDOG_COMP_PARAMS_5_REG_OFFSET), |
---|
| 513 | + DW_WDT_DBGFS_REG("param4", WDOG_COMP_PARAMS_4_REG_OFFSET), |
---|
| 514 | + DW_WDT_DBGFS_REG("param3", WDOG_COMP_PARAMS_3_REG_OFFSET), |
---|
| 515 | + DW_WDT_DBGFS_REG("param2", WDOG_COMP_PARAMS_2_REG_OFFSET), |
---|
| 516 | + DW_WDT_DBGFS_REG("param1", WDOG_COMP_PARAMS_1_REG_OFFSET), |
---|
| 517 | + DW_WDT_DBGFS_REG("version", WDOG_COMP_VERSION_REG_OFFSET), |
---|
| 518 | + DW_WDT_DBGFS_REG("type", WDOG_COMP_TYPE_REG_OFFSET) |
---|
| 519 | +}; |
---|
| 520 | + |
---|
| 521 | +static void dw_wdt_dbgfs_init(struct dw_wdt *dw_wdt) |
---|
| 522 | +{ |
---|
| 523 | + struct device *dev = dw_wdt->wdd.parent; |
---|
| 524 | + struct debugfs_regset32 *regset; |
---|
| 525 | + |
---|
| 526 | + regset = devm_kzalloc(dev, sizeof(*regset), GFP_KERNEL); |
---|
| 527 | + if (!regset) |
---|
| 528 | + return; |
---|
| 529 | + |
---|
| 530 | + regset->regs = dw_wdt_dbgfs_regs; |
---|
| 531 | + regset->nregs = ARRAY_SIZE(dw_wdt_dbgfs_regs); |
---|
| 532 | + regset->base = dw_wdt->regs; |
---|
| 533 | + |
---|
| 534 | + dw_wdt->dbgfs_dir = debugfs_create_dir(dev_name(dev), NULL); |
---|
| 535 | + |
---|
| 536 | + debugfs_create_regset32("registers", 0444, dw_wdt->dbgfs_dir, regset); |
---|
| 537 | +} |
---|
| 538 | + |
---|
| 539 | +static void dw_wdt_dbgfs_clear(struct dw_wdt *dw_wdt) |
---|
| 540 | +{ |
---|
| 541 | + debugfs_remove_recursive(dw_wdt->dbgfs_dir); |
---|
| 542 | +} |
---|
| 543 | + |
---|
| 544 | +#else /* !CONFIG_DEBUG_FS */ |
---|
| 545 | + |
---|
| 546 | +static void dw_wdt_dbgfs_init(struct dw_wdt *dw_wdt) {} |
---|
| 547 | +static void dw_wdt_dbgfs_clear(struct dw_wdt *dw_wdt) {} |
---|
| 548 | + |
---|
| 549 | +#endif /* !CONFIG_DEBUG_FS */ |
---|
| 550 | + |
---|
247 | 551 | static int dw_wdt_drv_probe(struct platform_device *pdev) |
---|
248 | 552 | { |
---|
249 | 553 | struct device *dev = &pdev->dev; |
---|
250 | 554 | struct watchdog_device *wdd; |
---|
251 | 555 | struct dw_wdt *dw_wdt; |
---|
252 | | - struct resource *mem; |
---|
253 | 556 | int ret; |
---|
254 | 557 | |
---|
255 | 558 | dw_wdt = devm_kzalloc(dev, sizeof(*dw_wdt), GFP_KERNEL); |
---|
256 | 559 | if (!dw_wdt) |
---|
257 | 560 | return -ENOMEM; |
---|
258 | 561 | |
---|
259 | | - mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
---|
260 | | - dw_wdt->regs = devm_ioremap_resource(dev, mem); |
---|
| 562 | + dw_wdt->regs = devm_platform_ioremap_resource(pdev, 0); |
---|
261 | 563 | if (IS_ERR(dw_wdt->regs)) |
---|
262 | 564 | return PTR_ERR(dw_wdt->regs); |
---|
263 | 565 | |
---|
.. | .. |
---|
307 | 609 | goto out_disable_pclk; |
---|
308 | 610 | } |
---|
309 | 611 | |
---|
| 612 | + /* Enable normal reset without pre-timeout by default. */ |
---|
| 613 | + dw_wdt_update_mode(dw_wdt, DW_WDT_RMOD_RESET); |
---|
| 614 | + |
---|
| 615 | + /* |
---|
| 616 | + * Pre-timeout IRQ is optional, since some hardware may lack support |
---|
| 617 | + * of it. Note we must request rising-edge IRQ, since the lane is left |
---|
| 618 | + * pending either until the next watchdog kick event or up to the |
---|
| 619 | + * system reset. |
---|
| 620 | + */ |
---|
| 621 | + ret = platform_get_irq_optional(pdev, 0); |
---|
| 622 | + if (ret > 0) { |
---|
| 623 | + ret = devm_request_irq(dev, ret, dw_wdt_irq, |
---|
| 624 | + IRQF_SHARED | IRQF_TRIGGER_RISING, |
---|
| 625 | + pdev->name, dw_wdt); |
---|
| 626 | + if (ret) |
---|
| 627 | + goto out_disable_pclk; |
---|
| 628 | + |
---|
| 629 | + dw_wdt->wdd.info = &dw_wdt_pt_ident; |
---|
| 630 | + } else { |
---|
| 631 | + if (ret == -EPROBE_DEFER) |
---|
| 632 | + goto out_disable_pclk; |
---|
| 633 | + |
---|
| 634 | + dw_wdt->wdd.info = &dw_wdt_ident; |
---|
| 635 | + } |
---|
| 636 | + |
---|
310 | 637 | reset_control_deassert(dw_wdt->rst); |
---|
311 | 638 | |
---|
| 639 | + ret = dw_wdt_init_timeouts(dw_wdt, dev); |
---|
| 640 | + if (ret) |
---|
| 641 | + goto out_disable_clk; |
---|
| 642 | + |
---|
312 | 643 | wdd = &dw_wdt->wdd; |
---|
313 | | - wdd->info = &dw_wdt_ident; |
---|
314 | 644 | wdd->ops = &dw_wdt_ops; |
---|
315 | | - wdd->min_timeout = 1; |
---|
316 | | - wdd->max_hw_heartbeat_ms = |
---|
317 | | - dw_wdt_top_in_seconds(dw_wdt, DW_WDT_MAX_TOP) * 1000; |
---|
| 645 | + wdd->min_timeout = dw_wdt_get_min_timeout(dw_wdt); |
---|
| 646 | + wdd->max_hw_heartbeat_ms = dw_wdt_get_max_timeout_ms(dw_wdt); |
---|
318 | 647 | wdd->parent = dev; |
---|
319 | 648 | |
---|
320 | 649 | watchdog_set_drvdata(wdd, dw_wdt); |
---|
.. | .. |
---|
327 | 656 | * devicetree. |
---|
328 | 657 | */ |
---|
329 | 658 | if (dw_wdt_is_enabled(dw_wdt)) { |
---|
330 | | - wdd->timeout = dw_wdt_get_top(dw_wdt); |
---|
| 659 | + wdd->timeout = dw_wdt_get_timeout(dw_wdt); |
---|
331 | 660 | set_bit(WDOG_HW_RUNNING, &wdd->status); |
---|
332 | 661 | } else { |
---|
333 | 662 | wdd->timeout = DW_WDT_DEFAULT_SECONDS; |
---|
.. | .. |
---|
342 | 671 | if (ret) |
---|
343 | 672 | goto out_disable_pclk; |
---|
344 | 673 | |
---|
| 674 | + dw_wdt_dbgfs_init(dw_wdt); |
---|
| 675 | + |
---|
345 | 676 | return 0; |
---|
346 | 677 | |
---|
347 | 678 | out_disable_pclk: |
---|
.. | .. |
---|
356 | 687 | { |
---|
357 | 688 | struct dw_wdt *dw_wdt = platform_get_drvdata(pdev); |
---|
358 | 689 | |
---|
| 690 | + dw_wdt_dbgfs_clear(dw_wdt); |
---|
| 691 | + |
---|
359 | 692 | watchdog_unregister_device(&dw_wdt->wdd); |
---|
360 | 693 | reset_control_assert(dw_wdt->rst); |
---|
361 | 694 | clk_disable_unprepare(dw_wdt->pclk); |
---|