Merge tag 'trace-v5.16-3' of git://git.kernel.org/pub/scm/linux/kernel/git/rostedt...
[linux-2.6-microblaze.git] / drivers / watchdog / mlx_wdt.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Mellanox watchdog driver
4  *
5  * Copyright (C) 2019 Mellanox Technologies
6  * Copyright (C) 2019 Michael Shych <mshych@mellanox.com>
7  */
8
9 #include <linux/bitops.h>
10 #include <linux/device.h>
11 #include <linux/errno.h>
12 #include <linux/log2.h>
13 #include <linux/module.h>
14 #include <linux/platform_data/mlxreg.h>
15 #include <linux/platform_device.h>
16 #include <linux/regmap.h>
17 #include <linux/spinlock.h>
18 #include <linux/types.h>
19 #include <linux/watchdog.h>
20
21 #define MLXREG_WDT_CLOCK_SCALE          1000
22 #define MLXREG_WDT_MAX_TIMEOUT_TYPE1    32
23 #define MLXREG_WDT_MAX_TIMEOUT_TYPE2    255
24 #define MLXREG_WDT_MAX_TIMEOUT_TYPE3    65535
25 #define MLXREG_WDT_MIN_TIMEOUT          1
26 #define MLXREG_WDT_OPTIONS_BASE (WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE | \
27                                  WDIOF_SETTIMEOUT)
28
29 /**
30  * struct mlxreg_wdt - wd private data:
31  *
32  * @wdd:        watchdog device;
33  * @device:     basic device;
34  * @pdata:      data received from platform driver;
35  * @regmap:     register map of parent device;
36  * @timeout:    defined timeout in sec.;
37  * @action_idx: index for direct access to action register;
38  * @timeout_idx:index for direct access to TO register;
39  * @tleft_idx:  index for direct access to time left register;
40  * @ping_idx:   index for direct access to ping register;
41  * @reset_idx:  index for direct access to reset cause register;
42  * @wd_type:    watchdog HW type;
43  */
44 struct mlxreg_wdt {
45         struct watchdog_device wdd;
46         struct mlxreg_core_platform_data *pdata;
47         void *regmap;
48         int action_idx;
49         int timeout_idx;
50         int tleft_idx;
51         int ping_idx;
52         int reset_idx;
53         int regmap_val_sz;
54         enum mlxreg_wdt_type wdt_type;
55 };
56
57 static void mlxreg_wdt_check_card_reset(struct mlxreg_wdt *wdt)
58 {
59         struct mlxreg_core_data *reg_data;
60         u32 regval;
61         int rc;
62
63         if (wdt->reset_idx == -EINVAL)
64                 return;
65
66         if (!(wdt->wdd.info->options & WDIOF_CARDRESET))
67                 return;
68
69         reg_data = &wdt->pdata->data[wdt->reset_idx];
70         rc = regmap_read(wdt->regmap, reg_data->reg, &regval);
71         if (!rc) {
72                 if (regval & ~reg_data->mask) {
73                         wdt->wdd.bootstatus = WDIOF_CARDRESET;
74                         dev_info(wdt->wdd.parent,
75                                  "watchdog previously reset the CPU\n");
76                 }
77         }
78 }
79
80 static int mlxreg_wdt_start(struct watchdog_device *wdd)
81 {
82         struct mlxreg_wdt *wdt = watchdog_get_drvdata(wdd);
83         struct mlxreg_core_data *reg_data = &wdt->pdata->data[wdt->action_idx];
84
85         return regmap_update_bits(wdt->regmap, reg_data->reg, ~reg_data->mask,
86                                   BIT(reg_data->bit));
87 }
88
89 static int mlxreg_wdt_stop(struct watchdog_device *wdd)
90 {
91         struct mlxreg_wdt *wdt = watchdog_get_drvdata(wdd);
92         struct mlxreg_core_data *reg_data = &wdt->pdata->data[wdt->action_idx];
93
94         return regmap_update_bits(wdt->regmap, reg_data->reg, ~reg_data->mask,
95                                   ~BIT(reg_data->bit));
96 }
97
98 static int mlxreg_wdt_ping(struct watchdog_device *wdd)
99 {
100         struct mlxreg_wdt *wdt = watchdog_get_drvdata(wdd);
101         struct mlxreg_core_data *reg_data = &wdt->pdata->data[wdt->ping_idx];
102
103         return regmap_write_bits(wdt->regmap, reg_data->reg, ~reg_data->mask,
104                                  BIT(reg_data->bit));
105 }
106
107 static int mlxreg_wdt_set_timeout(struct watchdog_device *wdd,
108                                   unsigned int timeout)
109 {
110         struct mlxreg_wdt *wdt = watchdog_get_drvdata(wdd);
111         struct mlxreg_core_data *reg_data = &wdt->pdata->data[wdt->timeout_idx];
112         u32 regval, set_time, hw_timeout;
113         int rc;
114
115         switch (wdt->wdt_type) {
116         case MLX_WDT_TYPE1:
117                 rc = regmap_read(wdt->regmap, reg_data->reg, &regval);
118                 if (rc)
119                         return rc;
120
121                 hw_timeout = order_base_2(timeout * MLXREG_WDT_CLOCK_SCALE);
122                 regval = (regval & reg_data->mask) | hw_timeout;
123                 /* Rowndown to actual closest number of sec. */
124                 set_time = BIT(hw_timeout) / MLXREG_WDT_CLOCK_SCALE;
125                 rc = regmap_write(wdt->regmap, reg_data->reg, regval);
126                 break;
127         case MLX_WDT_TYPE2:
128                 set_time = timeout;
129                 rc = regmap_write(wdt->regmap, reg_data->reg, timeout);
130                 break;
131         case MLX_WDT_TYPE3:
132                 /* WD_TYPE3 has 2B set time register */
133                 set_time = timeout;
134                 if (wdt->regmap_val_sz == 1) {
135                         regval = timeout & 0xff;
136                         rc = regmap_write(wdt->regmap, reg_data->reg, regval);
137                         if (!rc) {
138                                 regval = (timeout & 0xff00) >> 8;
139                                 rc = regmap_write(wdt->regmap,
140                                                 reg_data->reg + 1, regval);
141                         }
142                 } else {
143                         rc = regmap_write(wdt->regmap, reg_data->reg, timeout);
144                 }
145                 break;
146         default:
147                 return -EINVAL;
148         }
149
150         wdd->timeout = set_time;
151         if (!rc) {
152                 /*
153                  * Restart watchdog with new timeout period
154                  * if watchdog is already started.
155                  */
156                 if (watchdog_active(wdd)) {
157                         rc = mlxreg_wdt_stop(wdd);
158                         if (!rc)
159                                 rc = mlxreg_wdt_start(wdd);
160                 }
161         }
162
163         return rc;
164 }
165
166 static unsigned int mlxreg_wdt_get_timeleft(struct watchdog_device *wdd)
167 {
168         struct mlxreg_wdt *wdt = watchdog_get_drvdata(wdd);
169         struct mlxreg_core_data *reg_data = &wdt->pdata->data[wdt->tleft_idx];
170         u32 regval, msb, lsb;
171         int rc;
172
173         if (wdt->wdt_type == MLX_WDT_TYPE2) {
174                 rc = regmap_read(wdt->regmap, reg_data->reg, &regval);
175         } else {
176                 /* WD_TYPE3 has 2 byte timeleft register */
177                 if (wdt->regmap_val_sz == 1) {
178                         rc = regmap_read(wdt->regmap, reg_data->reg, &lsb);
179                         if (!rc) {
180                                 rc = regmap_read(wdt->regmap,
181                                                 reg_data->reg + 1, &msb);
182                                 regval = (msb & 0xff) << 8 | (lsb & 0xff);
183                         }
184                 } else {
185                         rc = regmap_read(wdt->regmap, reg_data->reg, &regval);
186                 }
187         }
188
189         /* Return 0 timeleft in case of failure register read. */
190         return rc == 0 ? regval : 0;
191 }
192
193 static const struct watchdog_ops mlxreg_wdt_ops_type1 = {
194         .start          = mlxreg_wdt_start,
195         .stop           = mlxreg_wdt_stop,
196         .ping           = mlxreg_wdt_ping,
197         .set_timeout    = mlxreg_wdt_set_timeout,
198         .owner          = THIS_MODULE,
199 };
200
201 static const struct watchdog_ops mlxreg_wdt_ops_type2 = {
202         .start          = mlxreg_wdt_start,
203         .stop           = mlxreg_wdt_stop,
204         .ping           = mlxreg_wdt_ping,
205         .set_timeout    = mlxreg_wdt_set_timeout,
206         .get_timeleft   = mlxreg_wdt_get_timeleft,
207         .owner          = THIS_MODULE,
208 };
209
210 static const struct watchdog_info mlxreg_wdt_main_info = {
211         .options        = MLXREG_WDT_OPTIONS_BASE
212                         | WDIOF_CARDRESET,
213         .identity       = "mlx-wdt-main",
214 };
215
216 static const struct watchdog_info mlxreg_wdt_aux_info = {
217         .options        = MLXREG_WDT_OPTIONS_BASE
218                         | WDIOF_ALARMONLY,
219         .identity       = "mlx-wdt-aux",
220 };
221
222 static void mlxreg_wdt_config(struct mlxreg_wdt *wdt,
223                               struct mlxreg_core_platform_data *pdata)
224 {
225         struct mlxreg_core_data *data = pdata->data;
226         int i;
227
228         wdt->reset_idx = -EINVAL;
229         for (i = 0; i < pdata->counter; i++, data++) {
230                 if (strnstr(data->label, "action", sizeof(data->label)))
231                         wdt->action_idx = i;
232                 else if (strnstr(data->label, "timeout", sizeof(data->label)))
233                         wdt->timeout_idx = i;
234                 else if (strnstr(data->label, "timeleft", sizeof(data->label)))
235                         wdt->tleft_idx = i;
236                 else if (strnstr(data->label, "ping", sizeof(data->label)))
237                         wdt->ping_idx = i;
238                 else if (strnstr(data->label, "reset", sizeof(data->label)))
239                         wdt->reset_idx = i;
240         }
241
242         wdt->pdata = pdata;
243         if (strnstr(pdata->identity, mlxreg_wdt_main_info.identity,
244                     sizeof(mlxreg_wdt_main_info.identity)))
245                 wdt->wdd.info = &mlxreg_wdt_main_info;
246         else
247                 wdt->wdd.info = &mlxreg_wdt_aux_info;
248
249         wdt->wdt_type = pdata->version;
250         switch (wdt->wdt_type) {
251         case MLX_WDT_TYPE1:
252                 wdt->wdd.ops = &mlxreg_wdt_ops_type1;
253                 wdt->wdd.max_timeout = MLXREG_WDT_MAX_TIMEOUT_TYPE1;
254                 break;
255         case MLX_WDT_TYPE2:
256                 wdt->wdd.ops = &mlxreg_wdt_ops_type2;
257                 wdt->wdd.max_timeout = MLXREG_WDT_MAX_TIMEOUT_TYPE2;
258                 break;
259         case MLX_WDT_TYPE3:
260                 wdt->wdd.ops = &mlxreg_wdt_ops_type2;
261                 wdt->wdd.max_timeout = MLXREG_WDT_MAX_TIMEOUT_TYPE3;
262                 break;
263         default:
264                 break;
265         }
266
267         wdt->wdd.min_timeout = MLXREG_WDT_MIN_TIMEOUT;
268 }
269
270 static int mlxreg_wdt_init_timeout(struct mlxreg_wdt *wdt,
271                                    struct mlxreg_core_platform_data *pdata)
272 {
273         u32 timeout;
274
275         timeout = pdata->data[wdt->timeout_idx].health_cntr;
276         return mlxreg_wdt_set_timeout(&wdt->wdd, timeout);
277 }
278
279 static int mlxreg_wdt_probe(struct platform_device *pdev)
280 {
281         struct device *dev = &pdev->dev;
282         struct mlxreg_core_platform_data *pdata;
283         struct mlxreg_wdt *wdt;
284         int rc;
285
286         pdata = dev_get_platdata(dev);
287         if (!pdata) {
288                 dev_err(dev, "Failed to get platform data.\n");
289                 return -EINVAL;
290         }
291         wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL);
292         if (!wdt)
293                 return -ENOMEM;
294
295         wdt->wdd.parent = dev;
296         wdt->regmap = pdata->regmap;
297         rc = regmap_get_val_bytes(wdt->regmap);
298         if (rc < 0)
299                 return -EINVAL;
300
301         wdt->regmap_val_sz = rc;
302         mlxreg_wdt_config(wdt, pdata);
303
304         if ((pdata->features & MLXREG_CORE_WD_FEATURE_NOWAYOUT))
305                 watchdog_set_nowayout(&wdt->wdd, WATCHDOG_NOWAYOUT);
306         watchdog_stop_on_reboot(&wdt->wdd);
307         watchdog_stop_on_unregister(&wdt->wdd);
308         watchdog_set_drvdata(&wdt->wdd, wdt);
309         rc = mlxreg_wdt_init_timeout(wdt, pdata);
310         if (rc)
311                 goto register_error;
312
313         if ((pdata->features & MLXREG_CORE_WD_FEATURE_START_AT_BOOT)) {
314                 rc = mlxreg_wdt_start(&wdt->wdd);
315                 if (rc)
316                         goto register_error;
317                 set_bit(WDOG_HW_RUNNING, &wdt->wdd.status);
318         }
319         mlxreg_wdt_check_card_reset(wdt);
320         rc = devm_watchdog_register_device(dev, &wdt->wdd);
321
322 register_error:
323         if (rc)
324                 dev_err(dev, "Cannot register watchdog device (err=%d)\n", rc);
325         return rc;
326 }
327
328 static struct platform_driver mlxreg_wdt_driver = {
329         .probe  = mlxreg_wdt_probe,
330         .driver = {
331                         .name = "mlx-wdt",
332         },
333 };
334
335 module_platform_driver(mlxreg_wdt_driver);
336
337 MODULE_AUTHOR("Michael Shych <michaelsh@mellanox.com>");
338 MODULE_DESCRIPTION("Mellanox watchdog driver");
339 MODULE_LICENSE("GPL");
340 MODULE_ALIAS("platform:mlx-wdt");