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
*dev
= &pdev
->dev
;
112 struct device_node
*np
= dev
->of_node
;
113 struct device_node
*syscon_np
;
114 struct watchdog_device
*wdd
;
115 struct ts4800_wdt
*wdt
;
119 syscon_np
= of_parse_phandle(np
, "syscon", 0);
121 dev_err(dev
, "no syscon property\n");
125 ret
= of_property_read_u32_index(np
, "syscon", 1, ®
);
127 dev_err(dev
, "no offset in syscon\n");
131 /* allocate memory for watchdog struct */
132 wdt
= devm_kzalloc(dev
, sizeof(*wdt
), GFP_KERNEL
);
136 /* set regmap and offset to know where to write */
137 wdt
->feed_offset
= reg
;
138 wdt
->regmap
= syscon_node_to_regmap(syscon_np
);
139 of_node_put(syscon_np
);
140 if (IS_ERR(wdt
->regmap
)) {
141 dev_err(dev
, "cannot get parent's regmap\n");
142 return PTR_ERR(wdt
->regmap
);
145 /* Initialize struct watchdog_device */
148 wdd
->info
= &ts4800_wdt_info
;
149 wdd
->ops
= &ts4800_wdt_ops
;
150 wdd
->min_timeout
= ts4800_wdt_map
[0].timeout
;
151 wdd
->max_timeout
= ts4800_wdt_map
[MAX_TIMEOUT_INDEX
].timeout
;
153 watchdog_set_drvdata(wdd
, wdt
);
154 watchdog_set_nowayout(wdd
, nowayout
);
155 watchdog_init_timeout(wdd
, 0, dev
);
158 * As this watchdog supports only a few values, ts4800_wdt_set_timeout
159 * must be called to initialize timeout and feed_val with valid values.
160 * Default to maximum timeout if none, or an invalid one, is provided in
164 wdd
->timeout
= wdd
->max_timeout
;
165 ts4800_wdt_set_timeout(wdd
, wdd
->timeout
);
168 * The feed register is write-only, so it is not possible to determine
169 * watchdog's state. Disable it to be in a known state.
171 ts4800_wdt_stop(wdd
);
173 ret
= devm_watchdog_register_device(dev
, wdd
);
177 platform_set_drvdata(pdev
, wdt
);
179 dev_info(dev
, "initialized (timeout = %d sec, nowayout = %d)\n",
180 wdd
->timeout
, nowayout
);
185 static const struct of_device_id ts4800_wdt_of_match
[] = {
186 { .compatible
= "technologic,ts4800-wdt", },
189 MODULE_DEVICE_TABLE(of
, ts4800_wdt_of_match
);
191 static struct platform_driver ts4800_wdt_driver
= {
192 .probe
= ts4800_wdt_probe
,
194 .name
= "ts4800_wdt",
195 .of_match_table
= ts4800_wdt_of_match
,
199 module_platform_driver(ts4800_wdt_driver
);
201 MODULE_AUTHOR("Damien Riegel <damien.riegel@savoirfairelinux.com>");
202 MODULE_LICENSE("GPL v2");
203 MODULE_ALIAS("platform:ts4800_wdt");