• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0+
2 
3 /*
4  * Driver for watchdog aspect of for Zodiac Inflight Innovations RAVE
5  * Supervisory Processor(SP) MCU
6  *
7  * Copyright (C) 2017 Zodiac Inflight Innovation
8  *
9  */
10 
11 #include <linux/delay.h>
12 #include <linux/kernel.h>
13 #include <linux/mfd/rave-sp.h>
14 #include <linux/module.h>
15 #include <linux/nvmem-consumer.h>
16 #include <linux/of_device.h>
17 #include <linux/platform_device.h>
18 #include <linux/reboot.h>
19 #include <linux/slab.h>
20 #include <linux/watchdog.h>
21 
22 enum {
23 	RAVE_SP_RESET_BYTE = 1,
24 	RAVE_SP_RESET_REASON_NORMAL = 0,
25 	RAVE_SP_RESET_DELAY_MS = 500,
26 };
27 
28 /**
29  * struct rave_sp_wdt_variant - RAVE SP watchdog variant
30  *
31  * @max_timeout:	Largest possible watchdog timeout setting
32  * @min_timeout:	Smallest possible watchdog timeout setting
33  *
34  * @configure:		Function to send configuration command
35  * @restart:		Function to send "restart" command
36  */
37 struct rave_sp_wdt_variant {
38 	unsigned int max_timeout;
39 	unsigned int min_timeout;
40 
41 	int (*configure)(struct watchdog_device *, bool);
42 	int (*restart)(struct watchdog_device *);
43 };
44 
45 /**
46  * struct rave_sp_wdt - RAVE SP watchdog
47  *
48  * @wdd:		Underlying watchdog device
49  * @sp:			Pointer to parent RAVE SP device
50  * @variant:		Device specific variant information
51  * @reboot_notifier:	Reboot notifier implementing machine reset
52  */
53 struct rave_sp_wdt {
54 	struct watchdog_device wdd;
55 	struct rave_sp *sp;
56 	const struct rave_sp_wdt_variant *variant;
57 	struct notifier_block reboot_notifier;
58 };
59 
to_rave_sp_wdt(struct watchdog_device * wdd)60 static struct rave_sp_wdt *to_rave_sp_wdt(struct watchdog_device *wdd)
61 {
62 	return container_of(wdd, struct rave_sp_wdt, wdd);
63 }
64 
rave_sp_wdt_exec(struct watchdog_device * wdd,void * data,size_t data_size)65 static int rave_sp_wdt_exec(struct watchdog_device *wdd, void *data,
66 			    size_t data_size)
67 {
68 	return rave_sp_exec(to_rave_sp_wdt(wdd)->sp,
69 			    data, data_size, NULL, 0);
70 }
71 
rave_sp_wdt_legacy_configure(struct watchdog_device * wdd,bool on)72 static int rave_sp_wdt_legacy_configure(struct watchdog_device *wdd, bool on)
73 {
74 	u8 cmd[] = {
75 		[0] = RAVE_SP_CMD_SW_WDT,
76 		[1] = 0,
77 		[2] = 0,
78 		[3] = on,
79 		[4] = on ? wdd->timeout : 0,
80 	};
81 
82 	return rave_sp_wdt_exec(wdd, cmd, sizeof(cmd));
83 }
84 
rave_sp_wdt_rdu_configure(struct watchdog_device * wdd,bool on)85 static int rave_sp_wdt_rdu_configure(struct watchdog_device *wdd, bool on)
86 {
87 	u8 cmd[] = {
88 		[0] = RAVE_SP_CMD_SW_WDT,
89 		[1] = 0,
90 		[2] = on,
91 		[3] = (u8)wdd->timeout,
92 		[4] = (u8)(wdd->timeout >> 8),
93 	};
94 
95 	return rave_sp_wdt_exec(wdd, cmd, sizeof(cmd));
96 }
97 
98 /**
99  * rave_sp_wdt_configure - Configure watchdog device
100  *
101  * @wdd:	Device to configure
102  * @on:		Desired state of the watchdog timer (ON/OFF)
103  *
104  * This function configures two aspects of the watchdog timer:
105  *
106  *  - Wheither it is ON or OFF
107  *  - Its timeout duration
108  *
109  * with first aspect specified via function argument and second via
110  * the value of 'wdd->timeout'.
111  */
rave_sp_wdt_configure(struct watchdog_device * wdd,bool on)112 static int rave_sp_wdt_configure(struct watchdog_device *wdd, bool on)
113 {
114 	return to_rave_sp_wdt(wdd)->variant->configure(wdd, on);
115 }
116 
rave_sp_wdt_legacy_restart(struct watchdog_device * wdd)117 static int rave_sp_wdt_legacy_restart(struct watchdog_device *wdd)
118 {
119 	u8 cmd[] = {
120 		[0] = RAVE_SP_CMD_RESET,
121 		[1] = 0,
122 		[2] = RAVE_SP_RESET_BYTE
123 	};
124 
125 	return rave_sp_wdt_exec(wdd, cmd, sizeof(cmd));
126 }
127 
rave_sp_wdt_rdu_restart(struct watchdog_device * wdd)128 static int rave_sp_wdt_rdu_restart(struct watchdog_device *wdd)
129 {
130 	u8 cmd[] = {
131 		[0] = RAVE_SP_CMD_RESET,
132 		[1] = 0,
133 		[2] = RAVE_SP_RESET_BYTE,
134 		[3] = RAVE_SP_RESET_REASON_NORMAL
135 	};
136 
137 	return rave_sp_wdt_exec(wdd, cmd, sizeof(cmd));
138 }
139 
rave_sp_wdt_reboot_notifier(struct notifier_block * nb,unsigned long action,void * data)140 static int rave_sp_wdt_reboot_notifier(struct notifier_block *nb,
141 				       unsigned long action, void *data)
142 {
143 	/*
144 	 * Restart handler is called in atomic context which means we
145 	 * can't communicate to SP via UART. Luckily for use SP will
146 	 * wait 500ms before actually resetting us, so we ask it to do
147 	 * so here and let the rest of the system go on wrapping
148 	 * things up.
149 	 */
150 	if (action == SYS_DOWN || action == SYS_HALT) {
151 		struct rave_sp_wdt *sp_wd =
152 			container_of(nb, struct rave_sp_wdt, reboot_notifier);
153 
154 		const int ret = sp_wd->variant->restart(&sp_wd->wdd);
155 
156 		if (ret < 0)
157 			dev_err(sp_wd->wdd.parent,
158 				"Failed to issue restart command (%d)", ret);
159 		return NOTIFY_OK;
160 	}
161 
162 	return NOTIFY_DONE;
163 }
164 
rave_sp_wdt_restart(struct watchdog_device * wdd,unsigned long action,void * data)165 static int rave_sp_wdt_restart(struct watchdog_device *wdd,
166 			       unsigned long action, void *data)
167 {
168 	/*
169 	 * The actual work was done by reboot notifier above. SP
170 	 * firmware waits 500 ms before issuing reset, so let's hang
171 	 * here for twice that delay and hopefuly we'd never reach
172 	 * the return statement.
173 	 */
174 	mdelay(2 * RAVE_SP_RESET_DELAY_MS);
175 
176 	return -EIO;
177 }
178 
rave_sp_wdt_start(struct watchdog_device * wdd)179 static int rave_sp_wdt_start(struct watchdog_device *wdd)
180 {
181 	int ret;
182 
183 	ret = rave_sp_wdt_configure(wdd, true);
184 	if (!ret)
185 		set_bit(WDOG_HW_RUNNING, &wdd->status);
186 
187 	return ret;
188 }
189 
rave_sp_wdt_stop(struct watchdog_device * wdd)190 static int rave_sp_wdt_stop(struct watchdog_device *wdd)
191 {
192 	return rave_sp_wdt_configure(wdd, false);
193 }
194 
rave_sp_wdt_set_timeout(struct watchdog_device * wdd,unsigned int timeout)195 static int rave_sp_wdt_set_timeout(struct watchdog_device *wdd,
196 				   unsigned int timeout)
197 {
198 	wdd->timeout = timeout;
199 
200 	return rave_sp_wdt_configure(wdd, watchdog_active(wdd));
201 }
202 
rave_sp_wdt_ping(struct watchdog_device * wdd)203 static int rave_sp_wdt_ping(struct watchdog_device *wdd)
204 {
205 	u8 cmd[] = {
206 		[0] = RAVE_SP_CMD_PET_WDT,
207 		[1] = 0,
208 	};
209 
210 	return rave_sp_wdt_exec(wdd, cmd, sizeof(cmd));
211 }
212 
213 static const struct watchdog_info rave_sp_wdt_info = {
214 	.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
215 	.identity = "RAVE SP Watchdog",
216 };
217 
218 static const struct watchdog_ops rave_sp_wdt_ops = {
219 	.owner = THIS_MODULE,
220 	.start = rave_sp_wdt_start,
221 	.stop = rave_sp_wdt_stop,
222 	.ping = rave_sp_wdt_ping,
223 	.set_timeout = rave_sp_wdt_set_timeout,
224 	.restart = rave_sp_wdt_restart,
225 };
226 
227 static const struct rave_sp_wdt_variant rave_sp_wdt_legacy = {
228 	.max_timeout = 255,
229 	.min_timeout = 1,
230 	.configure = rave_sp_wdt_legacy_configure,
231 	.restart   = rave_sp_wdt_legacy_restart,
232 };
233 
234 static const struct rave_sp_wdt_variant rave_sp_wdt_rdu = {
235 	.max_timeout = 180,
236 	.min_timeout = 60,
237 	.configure = rave_sp_wdt_rdu_configure,
238 	.restart   = rave_sp_wdt_rdu_restart,
239 };
240 
241 static const struct of_device_id rave_sp_wdt_of_match[] = {
242 	{
243 		.compatible = "zii,rave-sp-watchdog-legacy",
244 		.data = &rave_sp_wdt_legacy,
245 	},
246 	{
247 		.compatible = "zii,rave-sp-watchdog",
248 		.data = &rave_sp_wdt_rdu,
249 	},
250 	{ /* sentinel */ }
251 };
252 
rave_sp_wdt_probe(struct platform_device * pdev)253 static int rave_sp_wdt_probe(struct platform_device *pdev)
254 {
255 	struct device *dev = &pdev->dev;
256 	struct watchdog_device *wdd;
257 	struct rave_sp_wdt *sp_wd;
258 	struct nvmem_cell *cell;
259 	__le16 timeout = 0;
260 	int ret;
261 
262 	sp_wd = devm_kzalloc(dev, sizeof(*sp_wd), GFP_KERNEL);
263 	if (!sp_wd)
264 		return -ENOMEM;
265 
266 	sp_wd->variant = of_device_get_match_data(dev);
267 	sp_wd->sp      = dev_get_drvdata(dev->parent);
268 
269 	wdd              = &sp_wd->wdd;
270 	wdd->parent      = dev;
271 	wdd->info        = &rave_sp_wdt_info;
272 	wdd->ops         = &rave_sp_wdt_ops;
273 	wdd->min_timeout = sp_wd->variant->min_timeout;
274 	wdd->max_timeout = sp_wd->variant->max_timeout;
275 	wdd->status      = WATCHDOG_NOWAYOUT_INIT_STATUS;
276 	wdd->timeout     = 60;
277 
278 	cell = nvmem_cell_get(dev, "wdt-timeout");
279 	if (!IS_ERR(cell)) {
280 		size_t len;
281 		void *value = nvmem_cell_read(cell, &len);
282 
283 		if (!IS_ERR(value)) {
284 			memcpy(&timeout, value, min(len, sizeof(timeout)));
285 			kfree(value);
286 		}
287 		nvmem_cell_put(cell);
288 	}
289 	watchdog_init_timeout(wdd, le16_to_cpu(timeout), dev);
290 	watchdog_set_restart_priority(wdd, 255);
291 	watchdog_stop_on_unregister(wdd);
292 
293 	sp_wd->reboot_notifier.notifier_call = rave_sp_wdt_reboot_notifier;
294 	ret = devm_register_reboot_notifier(dev, &sp_wd->reboot_notifier);
295 	if (ret) {
296 		dev_err(dev, "Failed to register reboot notifier\n");
297 		return ret;
298 	}
299 
300 	/*
301 	 * We don't know if watchdog is running now. To be sure, let's
302 	 * start it and depend on watchdog core to ping it
303 	 */
304 	wdd->max_hw_heartbeat_ms = wdd->max_timeout * 1000;
305 	ret = rave_sp_wdt_start(wdd);
306 	if (ret) {
307 		dev_err(dev, "Watchdog didn't start\n");
308 		return ret;
309 	}
310 
311 	ret = devm_watchdog_register_device(dev, wdd);
312 	if (ret) {
313 		rave_sp_wdt_stop(wdd);
314 		return ret;
315 	}
316 
317 	return 0;
318 }
319 
320 static struct platform_driver rave_sp_wdt_driver = {
321 	.probe = rave_sp_wdt_probe,
322 	.driver = {
323 		.name = KBUILD_MODNAME,
324 		.of_match_table = rave_sp_wdt_of_match,
325 	},
326 };
327 
328 module_platform_driver(rave_sp_wdt_driver);
329 
330 MODULE_DEVICE_TABLE(of, rave_sp_wdt_of_match);
331 MODULE_LICENSE("GPL");
332 MODULE_AUTHOR("Andrey Vostrikov <andrey.vostrikov@cogentembedded.com>");
333 MODULE_AUTHOR("Nikita Yushchenko <nikita.yoush@cogentembedded.com>");
334 MODULE_AUTHOR("Andrey Smirnov <andrew.smirnov@gmail.com>");
335 MODULE_DESCRIPTION("RAVE SP Watchdog driver");
336 MODULE_ALIAS("platform:rave-sp-watchdog");
337