]> git.proxmox.com Git - mirror_ubuntu-hirsute-kernel.git/blame - drivers/watchdog/da9063_wdt.c
watchdog: da9063: Fix updating timeout value
[mirror_ubuntu-hirsute-kernel.git] / drivers / watchdog / da9063_wdt.c
CommitLineData
2e62c498 1// SPDX-License-Identifier: GPL-2.0+
5e9c16e3
KG
2/*
3 * Watchdog driver for DA9063 PMICs.
4 *
5 * Copyright(c) 2012 Dialog Semiconductor Ltd.
6 *
7 * Author: Mariusz Wojtasik <mariusz.wojtasik@diasemi.com>
8 *
5e9c16e3
KG
9 */
10
11#include <linux/kernel.h>
12#include <linux/module.h>
13#include <linux/watchdog.h>
14#include <linux/platform_device.h>
15#include <linux/uaccess.h>
16#include <linux/slab.h>
17#include <linux/delay.h>
18#include <linux/mfd/da9063/registers.h>
19#include <linux/mfd/da9063/core.h>
20#include <linux/regmap.h>
21
22/*
23 * Watchdog selector to timeout in seconds.
24 * 0: WDT disabled;
25 * others: timeout = 2048 ms * 2^(TWDSCALE-1).
26 */
27static const unsigned int wdt_timeout[] = { 0, 2, 4, 8, 16, 32, 65, 131 };
28#define DA9063_TWDSCALE_DISABLE 0
29#define DA9063_TWDSCALE_MIN 1
30#define DA9063_TWDSCALE_MAX (ARRAY_SIZE(wdt_timeout) - 1)
31#define DA9063_WDT_MIN_TIMEOUT wdt_timeout[DA9063_TWDSCALE_MIN]
32#define DA9063_WDT_MAX_TIMEOUT wdt_timeout[DA9063_TWDSCALE_MAX]
33#define DA9063_WDG_TIMEOUT wdt_timeout[3]
a74cab40 34#define DA9063_RESET_PROTECTION_MS 256
5e9c16e3 35
5e9c16e3
KG
36static unsigned int da9063_wdt_timeout_to_sel(unsigned int secs)
37{
38 unsigned int i;
39
40 for (i = DA9063_TWDSCALE_MIN; i <= DA9063_TWDSCALE_MAX; i++) {
41 if (wdt_timeout[i] >= secs)
42 return i;
43 }
44
45 return DA9063_TWDSCALE_MAX;
46}
47
e46bb55d
MF
48static int da9063_wdt_disable_timer(struct da9063 *da9063)
49{
50 return regmap_update_bits(da9063->regmap, DA9063_REG_CONTROL_D,
51 DA9063_TWDSCALE_MASK,
52 DA9063_TWDSCALE_DISABLE);
53}
54
5e9c16e3
KG
55static int _da9063_wdt_set_timeout(struct da9063 *da9063, unsigned int regval)
56{
e46bb55d
MF
57 int ret;
58
59 /*
60 * The watchdog triggers a reboot if a timeout value is already
61 * programmed because the timeout value combines two functions
62 * in one: indicating the counter limit and starting the watchdog.
63 * The watchdog must be disabled to be able to change the timeout
64 * value if the watchdog is already running. Then we can set the
65 * new timeout value which enables the watchdog again.
66 */
67 ret = da9063_wdt_disable_timer(da9063);
68 if (ret)
69 return ret;
70
71 usleep_range(150, 300);
72
5e9c16e3
KG
73 return regmap_update_bits(da9063->regmap, DA9063_REG_CONTROL_D,
74 DA9063_TWDSCALE_MASK, regval);
75}
76
77static int da9063_wdt_start(struct watchdog_device *wdd)
78{
a1f2a820 79 struct da9063 *da9063 = watchdog_get_drvdata(wdd);
5e9c16e3
KG
80 unsigned int selector;
81 int ret;
82
a1f2a820 83 selector = da9063_wdt_timeout_to_sel(wdd->timeout);
84 ret = _da9063_wdt_set_timeout(da9063, selector);
5e9c16e3 85 if (ret)
a1f2a820 86 dev_err(da9063->dev, "Watchdog failed to start (err = %d)\n",
5e9c16e3
KG
87 ret);
88
89 return ret;
90}
91
92static int da9063_wdt_stop(struct watchdog_device *wdd)
93{
a1f2a820 94 struct da9063 *da9063 = watchdog_get_drvdata(wdd);
5e9c16e3
KG
95 int ret;
96
e46bb55d 97 ret = da9063_wdt_disable_timer(da9063);
5e9c16e3 98 if (ret)
a1f2a820 99 dev_alert(da9063->dev, "Watchdog failed to stop (err = %d)\n",
5e9c16e3
KG
100 ret);
101
102 return ret;
103}
104
105static int da9063_wdt_ping(struct watchdog_device *wdd)
106{
a1f2a820 107 struct da9063 *da9063 = watchdog_get_drvdata(wdd);
5e9c16e3
KG
108 int ret;
109
a1f2a820 110 ret = regmap_write(da9063->regmap, DA9063_REG_CONTROL_F,
5e9c16e3
KG
111 DA9063_WATCHDOG);
112 if (ret)
a1f2a820 113 dev_alert(da9063->dev, "Failed to ping the watchdog (err = %d)\n",
5e9c16e3
KG
114 ret);
115
116 return ret;
117}
118
119static int da9063_wdt_set_timeout(struct watchdog_device *wdd,
120 unsigned int timeout)
121{
a1f2a820 122 struct da9063 *da9063 = watchdog_get_drvdata(wdd);
5e9c16e3 123 unsigned int selector;
44ee54aa 124 int ret = 0;
5e9c16e3
KG
125
126 selector = da9063_wdt_timeout_to_sel(timeout);
44ee54aa
MF
127
128 /*
129 * There are two cases when a set_timeout() will be called:
130 * 1. The watchdog is off and someone wants to set the timeout for the
131 * further use.
132 * 2. The watchdog is already running and a new timeout value should be
133 * set.
134 *
135 * The watchdog can't store a timeout value not equal zero without
136 * enabling the watchdog, so the timeout must be buffered by the driver.
137 */
138 if (watchdog_active(wdd))
139 ret = _da9063_wdt_set_timeout(da9063, selector);
140
5e9c16e3 141 if (ret)
a1f2a820 142 dev_err(da9063->dev, "Failed to set watchdog timeout (err = %d)\n",
5e9c16e3
KG
143 ret);
144 else
145 wdd->timeout = wdt_timeout[selector];
146
147 return ret;
148}
149
4d8b229d
GR
150static int da9063_wdt_restart(struct watchdog_device *wdd, unsigned long action,
151 void *data)
396f163c 152{
a1f2a820 153 struct da9063 *da9063 = watchdog_get_drvdata(wdd);
396f163c
GU
154 int ret;
155
a1f2a820 156 ret = regmap_write(da9063->regmap, DA9063_REG_CONTROL_F,
396f163c
GU
157 DA9063_SHUTDOWN);
158 if (ret)
a1f2a820 159 dev_alert(da9063->dev, "Failed to shutdown (err = %d)\n",
396f163c
GU
160 ret);
161
f79781ce 162 return ret;
396f163c
GU
163}
164
5e9c16e3
KG
165static const struct watchdog_info da9063_watchdog_info = {
166 .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
167 .identity = "DA9063 Watchdog",
168};
169
170static const struct watchdog_ops da9063_watchdog_ops = {
171 .owner = THIS_MODULE,
172 .start = da9063_wdt_start,
173 .stop = da9063_wdt_stop,
174 .ping = da9063_wdt_ping,
175 .set_timeout = da9063_wdt_set_timeout,
f79781ce 176 .restart = da9063_wdt_restart,
5e9c16e3
KG
177};
178
179static int da9063_wdt_probe(struct platform_device *pdev)
180{
5e9c16e3 181 struct da9063 *da9063;
a1f2a820 182 struct watchdog_device *wdd;
5e9c16e3
KG
183
184 if (!pdev->dev.parent)
185 return -EINVAL;
186
187 da9063 = dev_get_drvdata(pdev->dev.parent);
188 if (!da9063)
189 return -EINVAL;
190
a1f2a820 191 wdd = devm_kzalloc(&pdev->dev, sizeof(*wdd), GFP_KERNEL);
192 if (!wdd)
5e9c16e3
KG
193 return -ENOMEM;
194
a1f2a820 195 wdd->info = &da9063_watchdog_info;
196 wdd->ops = &da9063_watchdog_ops;
197 wdd->min_timeout = DA9063_WDT_MIN_TIMEOUT;
198 wdd->max_timeout = DA9063_WDT_MAX_TIMEOUT;
199 wdd->min_hw_heartbeat_ms = DA9063_RESET_PROTECTION_MS;
200 wdd->timeout = DA9063_WDG_TIMEOUT;
201 wdd->parent = &pdev->dev;
5e9c16e3 202
a1f2a820 203 wdd->status = WATCHDOG_NOWAYOUT_INIT_STATUS;
5e9c16e3 204
a1f2a820 205 watchdog_set_restart_priority(wdd, 128);
f79781ce 206
a1f2a820 207 watchdog_set_drvdata(wdd, da9063);
5e9c16e3 208
a1f2a820 209 return devm_watchdog_register_device(&pdev->dev, wdd);
5e9c16e3
KG
210}
211
212static struct platform_driver da9063_wdt_driver = {
213 .probe = da9063_wdt_probe,
5e9c16e3
KG
214 .driver = {
215 .name = DA9063_DRVNAME_WATCHDOG,
216 },
217};
218module_platform_driver(da9063_wdt_driver);
219
220MODULE_AUTHOR("Mariusz Wojtasik <mariusz.wojtasik@diasemi.com>");
221MODULE_DESCRIPTION("Watchdog driver for Dialog DA9063");
222MODULE_LICENSE("GPL");
223MODULE_ALIAS("platform:" DA9063_DRVNAME_WATCHDOG);