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