1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Congatec Board Controller watchdog driver
5 * Copyright (C) 2024 Bootlin
6 * Author: Thomas Richard <thomas.richard@bootlin.com>
9 #include <linux/build_bug.h>
10 #include <linux/device.h>
11 #include <linux/limits.h>
12 #include <linux/module.h>
13 #include <linux/platform_device.h>
14 #include <linux/watchdog.h>
16 #include <linux/mfd/cgbc.h>
18 #define CGBC_WDT_CMD_TRIGGER 0x27
19 #define CGBC_WDT_CMD_INIT 0x28
20 #define CGBC_WDT_DISABLE 0x00
22 #define CGBC_WDT_MODE_SINGLE_EVENT 0x02
24 #define CGBC_WDT_MIN_TIMEOUT 1
25 #define CGBC_WDT_MAX_TIMEOUT ((U32_MAX >> 8) / 1000)
27 #define CGBC_WDT_DEFAULT_TIMEOUT 30
28 #define CGBC_WDT_DEFAULT_PRETIMEOUT 0
37 static unsigned int timeout
;
38 module_param(timeout
, uint
, 0);
39 MODULE_PARM_DESC(timeout
,
40 "Watchdog timeout in seconds. (>=0, default="
41 __MODULE_STRING(CGBC_WDT_DEFAULT_TIMEOUT
) ")");
43 static unsigned int pretimeout
= CGBC_WDT_DEFAULT_PRETIMEOUT
;
44 module_param(pretimeout
, uint
, 0);
45 MODULE_PARM_DESC(pretimeout
,
46 "Watchdog pretimeout in seconds. (>=0, default="
47 __MODULE_STRING(CGBC_WDT_DEFAULT_PRETIMEOUT
) ")");
49 static bool nowayout
= WATCHDOG_NOWAYOUT
;
50 module_param(nowayout
, bool, 0);
51 MODULE_PARM_DESC(nowayout
,
52 "Watchdog cannot be stopped once started (default="
53 __MODULE_STRING(WATCHDOG_NOWAYOUT
) ")");
55 struct cgbc_wdt_data
{
56 struct cgbc_device_data
*cgbc
;
57 struct watchdog_device wdd
;
60 struct cgbc_wdt_cmd_cfg
{
70 static_assert(sizeof(struct cgbc_wdt_cmd_cfg
) == 15);
72 static int cgbc_wdt_start(struct watchdog_device
*wdd
)
74 struct cgbc_wdt_data
*wdt_data
= watchdog_get_drvdata(wdd
);
75 struct cgbc_device_data
*cgbc
= wdt_data
->cgbc
;
76 unsigned int timeout1
= (wdd
->timeout
- wdd
->pretimeout
) * 1000;
77 unsigned int timeout2
= wdd
->pretimeout
* 1000;
80 struct cgbc_wdt_cmd_cfg cmd_start
= {
81 .cmd
= CGBC_WDT_CMD_INIT
,
82 .mode
= CGBC_WDT_MODE_SINGLE_EVENT
,
83 .timeout1
[0] = (u8
)timeout1
,
84 .timeout1
[1] = (u8
)(timeout1
>> 8),
85 .timeout1
[2] = (u8
)(timeout1
>> 16),
86 .timeout2
[0] = (u8
)timeout2
,
87 .timeout2
[1] = (u8
)(timeout2
>> 8),
88 .timeout2
[2] = (u8
)(timeout2
>> 16),
91 if (wdd
->pretimeout
) {
93 action
|= ACTION_SMI
<< 2;
94 action
|= ACTION_RESET
<< 4;
97 action
|= ACTION_RESET
<< 2;
100 cmd_start
.action
= action
;
102 return cgbc_command(cgbc
, &cmd_start
, sizeof(cmd_start
), NULL
, 0, NULL
);
105 static int cgbc_wdt_stop(struct watchdog_device
*wdd
)
107 struct cgbc_wdt_data
*wdt_data
= watchdog_get_drvdata(wdd
);
108 struct cgbc_device_data
*cgbc
= wdt_data
->cgbc
;
109 struct cgbc_wdt_cmd_cfg cmd_stop
= {
110 .cmd
= CGBC_WDT_CMD_INIT
,
111 .mode
= CGBC_WDT_DISABLE
,
114 return cgbc_command(cgbc
, &cmd_stop
, sizeof(cmd_stop
), NULL
, 0, NULL
);
117 static int cgbc_wdt_keepalive(struct watchdog_device
*wdd
)
119 struct cgbc_wdt_data
*wdt_data
= watchdog_get_drvdata(wdd
);
120 struct cgbc_device_data
*cgbc
= wdt_data
->cgbc
;
121 u8 cmd_ping
= CGBC_WDT_CMD_TRIGGER
;
123 return cgbc_command(cgbc
, &cmd_ping
, sizeof(cmd_ping
), NULL
, 0, NULL
);
126 static int cgbc_wdt_set_pretimeout(struct watchdog_device
*wdd
,
127 unsigned int pretimeout
)
129 wdd
->pretimeout
= pretimeout
;
131 if (watchdog_active(wdd
))
132 return cgbc_wdt_start(wdd
);
137 static int cgbc_wdt_set_timeout(struct watchdog_device
*wdd
,
138 unsigned int timeout
)
140 if (timeout
< wdd
->pretimeout
)
143 wdd
->timeout
= timeout
;
145 if (watchdog_active(wdd
))
146 return cgbc_wdt_start(wdd
);
151 static const struct watchdog_info cgbc_wdt_info
= {
152 .identity
= "CGBC Watchdog",
153 .options
= WDIOF_SETTIMEOUT
| WDIOF_KEEPALIVEPING
|
154 WDIOF_MAGICCLOSE
| WDIOF_PRETIMEOUT
157 static const struct watchdog_ops cgbc_wdt_ops
= {
158 .owner
= THIS_MODULE
,
159 .start
= cgbc_wdt_start
,
160 .stop
= cgbc_wdt_stop
,
161 .ping
= cgbc_wdt_keepalive
,
162 .set_timeout
= cgbc_wdt_set_timeout
,
163 .set_pretimeout
= cgbc_wdt_set_pretimeout
,
166 static int cgbc_wdt_probe(struct platform_device
*pdev
)
168 struct cgbc_device_data
*cgbc
= dev_get_drvdata(pdev
->dev
.parent
);
169 struct device
*dev
= &pdev
->dev
;
170 struct cgbc_wdt_data
*wdt_data
;
171 struct watchdog_device
*wdd
;
173 wdt_data
= devm_kzalloc(dev
, sizeof(*wdt_data
), GFP_KERNEL
);
177 wdt_data
->cgbc
= cgbc
;
178 wdd
= &wdt_data
->wdd
;
181 wdd
->info
= &cgbc_wdt_info
;
182 wdd
->ops
= &cgbc_wdt_ops
;
183 wdd
->max_timeout
= CGBC_WDT_MAX_TIMEOUT
;
184 wdd
->min_timeout
= CGBC_WDT_MIN_TIMEOUT
;
186 watchdog_set_drvdata(wdd
, wdt_data
);
187 watchdog_set_nowayout(wdd
, nowayout
);
189 wdd
->timeout
= CGBC_WDT_DEFAULT_TIMEOUT
;
190 watchdog_init_timeout(wdd
, timeout
, dev
);
191 cgbc_wdt_set_pretimeout(wdd
, pretimeout
);
193 platform_set_drvdata(pdev
, wdt_data
);
194 watchdog_stop_on_reboot(wdd
);
195 watchdog_stop_on_unregister(wdd
);
197 return devm_watchdog_register_device(dev
, wdd
);
200 static struct platform_driver cgbc_wdt_driver
= {
204 .probe
= cgbc_wdt_probe
,
207 module_platform_driver(cgbc_wdt_driver
);
209 MODULE_DESCRIPTION("Congatec Board Controller Watchdog Driver");
210 MODULE_AUTHOR("Thomas Richard <thomas.richard@bootlin.com>");
211 MODULE_LICENSE("GPL");