2 * Watchdog driver for TS-4800 based boards
4 * Copyright (c) 2015 - Savoir-faire Linux
6 * This file is licensed under the terms of the GNU General Public
7 * License version 2. This program is licensed "as is" without any
8 * warranty of any kind, whether express or implied.
11 #include <linux/kernel.h>
12 #include <linux/mfd/syscon.h>
13 #include <linux/module.h>
15 #include <linux/platform_device.h>
16 #include <linux/regmap.h>
17 #include <linux/watchdog.h>
19 static bool nowayout
= WATCHDOG_NOWAYOUT
;
20 module_param(nowayout
, bool, 0);
21 MODULE_PARM_DESC(nowayout
,
22 "Watchdog cannot be stopped once started (default="
23 __MODULE_STRING(WATCHDOG_NOWAYOUT
) ")");
25 /* possible feed values */
26 #define TS4800_WDT_FEED_2S 0x1
27 #define TS4800_WDT_FEED_10S 0x2
28 #define TS4800_WDT_DISABLE 0x3
31 struct watchdog_device wdd
;
32 struct regmap
*regmap
;
38 * TS-4800 supports the following timeout values:
41 * ---------------------
47 * Keep the regmap/timeout map ordered by timeout
52 } ts4800_wdt_map
[] = {
53 { 2, TS4800_WDT_FEED_2S
},
54 { 10, TS4800_WDT_FEED_10S
},
57 #define MAX_TIMEOUT_INDEX (ARRAY_SIZE(ts4800_wdt_map) - 1)
59 static void ts4800_write_feed(struct ts4800_wdt
*wdt
, u32 val
)
61 regmap_write(wdt
->regmap
, wdt
->feed_offset
, val
);
64 static int ts4800_wdt_start(struct watchdog_device
*wdd
)
66 struct ts4800_wdt
*wdt
= watchdog_get_drvdata(wdd
);
68 ts4800_write_feed(wdt
, wdt
->feed_val
);
72 static int ts4800_wdt_stop(struct watchdog_device
*wdd
)
74 struct ts4800_wdt
*wdt
= watchdog_get_drvdata(wdd
);
76 ts4800_write_feed(wdt
, TS4800_WDT_DISABLE
);
80 static int ts4800_wdt_set_timeout(struct watchdog_device
*wdd
,
83 struct ts4800_wdt
*wdt
= watchdog_get_drvdata(wdd
);
86 for (i
= 0; i
< MAX_TIMEOUT_INDEX
; i
++) {
87 if (ts4800_wdt_map
[i
].timeout
>= timeout
)
91 wdd
->timeout
= ts4800_wdt_map
[i
].timeout
;
92 wdt
->feed_val
= ts4800_wdt_map
[i
].regval
;
97 static const struct watchdog_ops ts4800_wdt_ops
= {
99 .start
= ts4800_wdt_start
,
100 .stop
= ts4800_wdt_stop
,
101 .set_timeout
= ts4800_wdt_set_timeout
,
104 static const struct watchdog_info ts4800_wdt_info
= {
105 .options
= WDIOF_SETTIMEOUT
| WDIOF_MAGICCLOSE
| WDIOF_KEEPALIVEPING
,
106 .identity
= "TS-4800 Watchdog",
109 static int ts4800_wdt_probe(struct platform_device
*pdev
)
111 struct device_node
*np
= pdev
->dev
.of_node
;
112 struct device_node
*syscon_np
;
113 struct watchdog_device
*wdd
;
114 struct ts4800_wdt
*wdt
;
118 syscon_np
= of_parse_phandle(np
, "syscon", 0);
120 dev_err(&pdev
->dev
, "no syscon property\n");
124 ret
= of_property_read_u32_index(np
, "syscon", 1, ®
);
126 dev_err(&pdev
->dev
, "no offset in syscon\n");
130 /* allocate memory for watchdog struct */
131 wdt
= devm_kzalloc(&pdev
->dev
, sizeof(*wdt
), GFP_KERNEL
);
135 /* set regmap and offset to know where to write */
136 wdt
->feed_offset
= reg
;
137 wdt
->regmap
= syscon_node_to_regmap(syscon_np
);
138 if (IS_ERR(wdt
->regmap
)) {
139 dev_err(&pdev
->dev
, "cannot get parent's regmap\n");
140 return PTR_ERR(wdt
->regmap
);
143 /* Initialize struct watchdog_device */
145 wdd
->parent
= &pdev
->dev
;
146 wdd
->info
= &ts4800_wdt_info
;
147 wdd
->ops
= &ts4800_wdt_ops
;
148 wdd
->min_timeout
= ts4800_wdt_map
[0].timeout
;
149 wdd
->max_timeout
= ts4800_wdt_map
[MAX_TIMEOUT_INDEX
].timeout
;
151 watchdog_set_drvdata(wdd
, wdt
);
152 watchdog_set_nowayout(wdd
, nowayout
);
153 watchdog_init_timeout(wdd
, 0, &pdev
->dev
);
156 * As this watchdog supports only a few values, ts4800_wdt_set_timeout
157 * must be called to initialize timeout and feed_val with valid values.
158 * Default to maximum timeout if none, or an invalid one, is provided in
162 wdd
->timeout
= wdd
->max_timeout
;
163 ts4800_wdt_set_timeout(wdd
, wdd
->timeout
);
166 * The feed register is write-only, so it is not possible to determine
167 * watchdog's state. Disable it to be in a known state.
169 ts4800_wdt_stop(wdd
);
171 ret
= watchdog_register_device(wdd
);
174 "failed to register watchdog device\n");
178 platform_set_drvdata(pdev
, wdt
);
181 "initialized (timeout = %d sec, nowayout = %d)\n",
182 wdd
->timeout
, nowayout
);
187 static int ts4800_wdt_remove(struct platform_device
*pdev
)
189 struct ts4800_wdt
*wdt
= platform_get_drvdata(pdev
);
191 watchdog_unregister_device(&wdt
->wdd
);
196 static const struct of_device_id ts4800_wdt_of_match
[] = {
197 { .compatible
= "technologic,ts4800-wdt", },
200 MODULE_DEVICE_TABLE(of
, ts4800_wdt_of_match
);
202 static struct platform_driver ts4800_wdt_driver
= {
203 .probe
= ts4800_wdt_probe
,
204 .remove
= ts4800_wdt_remove
,
206 .name
= "ts4800_wdt",
207 .of_match_table
= ts4800_wdt_of_match
,
211 module_platform_driver(ts4800_wdt_driver
);
213 MODULE_AUTHOR("Damien Riegel <damien.riegel@savoirfairelinux.com>");
214 MODULE_LICENSE("GPL v2");
215 MODULE_ALIAS("platform:ts4800_wdt");