1 // SPDX-License-Identifier: GPL-2.0-only
3 * Watchdog driver for TS-4800 based boards
5 * Copyright (c) 2015 - Savoir-faire Linux
9 #include <linux/kernel.h>
10 #include <linux/mfd/syscon.h>
11 #include <linux/module.h>
13 #include <linux/platform_device.h>
14 #include <linux/regmap.h>
15 #include <linux/watchdog.h>
17 static bool nowayout
= WATCHDOG_NOWAYOUT
;
18 module_param(nowayout
, bool, 0);
19 MODULE_PARM_DESC(nowayout
,
20 "Watchdog cannot be stopped once started (default="
21 __MODULE_STRING(WATCHDOG_NOWAYOUT
) ")");
23 /* possible feed values */
24 #define TS4800_WDT_FEED_2S 0x1
25 #define TS4800_WDT_FEED_10S 0x2
26 #define TS4800_WDT_DISABLE 0x3
29 struct watchdog_device wdd
;
30 struct regmap
*regmap
;
36 * TS-4800 supports the following timeout values:
39 * ---------------------
45 * Keep the regmap/timeout map ordered by timeout
50 } ts4800_wdt_map
[] = {
51 { 2, TS4800_WDT_FEED_2S
},
52 { 10, TS4800_WDT_FEED_10S
},
55 #define MAX_TIMEOUT_INDEX (ARRAY_SIZE(ts4800_wdt_map) - 1)
57 static void ts4800_write_feed(struct ts4800_wdt
*wdt
, u32 val
)
59 regmap_write(wdt
->regmap
, wdt
->feed_offset
, val
);
62 static int ts4800_wdt_start(struct watchdog_device
*wdd
)
64 struct ts4800_wdt
*wdt
= watchdog_get_drvdata(wdd
);
66 ts4800_write_feed(wdt
, wdt
->feed_val
);
70 static int ts4800_wdt_stop(struct watchdog_device
*wdd
)
72 struct ts4800_wdt
*wdt
= watchdog_get_drvdata(wdd
);
74 ts4800_write_feed(wdt
, TS4800_WDT_DISABLE
);
78 static int ts4800_wdt_set_timeout(struct watchdog_device
*wdd
,
81 struct ts4800_wdt
*wdt
= watchdog_get_drvdata(wdd
);
84 for (i
= 0; i
< MAX_TIMEOUT_INDEX
; i
++) {
85 if (ts4800_wdt_map
[i
].timeout
>= timeout
)
89 wdd
->timeout
= ts4800_wdt_map
[i
].timeout
;
90 wdt
->feed_val
= ts4800_wdt_map
[i
].regval
;
95 static const struct watchdog_ops ts4800_wdt_ops
= {
97 .start
= ts4800_wdt_start
,
98 .stop
= ts4800_wdt_stop
,
99 .set_timeout
= ts4800_wdt_set_timeout
,
102 static const struct watchdog_info ts4800_wdt_info
= {
103 .options
= WDIOF_SETTIMEOUT
| WDIOF_MAGICCLOSE
| WDIOF_KEEPALIVEPING
,
104 .identity
= "TS-4800 Watchdog",
107 static int ts4800_wdt_probe(struct platform_device
*pdev
)
109 struct device
*dev
= &pdev
->dev
;
110 struct device_node
*np
= dev
->of_node
;
111 struct device_node
*syscon_np
;
112 struct watchdog_device
*wdd
;
113 struct ts4800_wdt
*wdt
;
117 syscon_np
= of_parse_phandle(np
, "syscon", 0);
119 dev_err(dev
, "no syscon property\n");
123 ret
= of_property_read_u32_index(np
, "syscon", 1, ®
);
125 dev_err(dev
, "no offset in syscon\n");
126 of_node_put(syscon_np
);
130 /* allocate memory for watchdog struct */
131 wdt
= devm_kzalloc(dev
, sizeof(*wdt
), GFP_KERNEL
);
133 of_node_put(syscon_np
);
137 /* set regmap and offset to know where to write */
138 wdt
->feed_offset
= reg
;
139 wdt
->regmap
= syscon_node_to_regmap(syscon_np
);
140 of_node_put(syscon_np
);
141 if (IS_ERR(wdt
->regmap
)) {
142 dev_err(dev
, "cannot get parent's regmap\n");
143 return PTR_ERR(wdt
->regmap
);
146 /* Initialize struct watchdog_device */
149 wdd
->info
= &ts4800_wdt_info
;
150 wdd
->ops
= &ts4800_wdt_ops
;
151 wdd
->min_timeout
= ts4800_wdt_map
[0].timeout
;
152 wdd
->max_timeout
= ts4800_wdt_map
[MAX_TIMEOUT_INDEX
].timeout
;
154 watchdog_set_drvdata(wdd
, wdt
);
155 watchdog_set_nowayout(wdd
, nowayout
);
156 watchdog_init_timeout(wdd
, 0, dev
);
159 * As this watchdog supports only a few values, ts4800_wdt_set_timeout
160 * must be called to initialize timeout and feed_val with valid values.
161 * Default to maximum timeout if none, or an invalid one, is provided in
165 wdd
->timeout
= wdd
->max_timeout
;
166 ts4800_wdt_set_timeout(wdd
, wdd
->timeout
);
169 * The feed register is write-only, so it is not possible to determine
170 * watchdog's state. Disable it to be in a known state.
172 ts4800_wdt_stop(wdd
);
174 ret
= devm_watchdog_register_device(dev
, wdd
);
178 platform_set_drvdata(pdev
, wdt
);
180 dev_info(dev
, "initialized (timeout = %d sec, nowayout = %d)\n",
181 wdd
->timeout
, nowayout
);
186 static const struct of_device_id ts4800_wdt_of_match
[] = {
187 { .compatible
= "technologic,ts4800-wdt", },
190 MODULE_DEVICE_TABLE(of
, ts4800_wdt_of_match
);
192 static struct platform_driver ts4800_wdt_driver
= {
193 .probe
= ts4800_wdt_probe
,
195 .name
= "ts4800_wdt",
196 .of_match_table
= ts4800_wdt_of_match
,
200 module_platform_driver(ts4800_wdt_driver
);
202 MODULE_AUTHOR("Damien Riegel <damien.riegel@savoirfairelinux.com>");
203 MODULE_DESCRIPTION("Watchdog driver for TS-4800 based boards");
204 MODULE_LICENSE("GPL v2");
205 MODULE_ALIAS("platform:ts4800_wdt");