xref: /openbmc/linux/drivers/watchdog/wm8350_wdt.c (revision 0e89b2c9)
12e62c498SMarcus Folkesson // SPDX-License-Identifier: GPL-2.0+
2006948baSMark Brown /*
3006948baSMark Brown  * Watchdog driver for the wm8350
4006948baSMark Brown  *
5006948baSMark Brown  * Copyright (C) 2007, 2008 Wolfson Microelectronics <linux@wolfsonmicro.com>
6006948baSMark Brown  */
7006948baSMark Brown 
827c766aaSJoe Perches #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
927c766aaSJoe Perches 
10006948baSMark Brown #include <linux/module.h>
11006948baSMark Brown #include <linux/moduleparam.h>
12006948baSMark Brown #include <linux/types.h>
13006948baSMark Brown #include <linux/kernel.h>
14006948baSMark Brown #include <linux/platform_device.h>
15006948baSMark Brown #include <linux/watchdog.h>
16006948baSMark Brown #include <linux/uaccess.h>
17006948baSMark Brown #include <linux/mfd/wm8350/core.h>
18006948baSMark Brown 
1986a1e189SWim Van Sebroeck static bool nowayout = WATCHDOG_NOWAYOUT;
2086a1e189SWim Van Sebroeck module_param(nowayout, bool, 0);
21006948baSMark Brown MODULE_PARM_DESC(nowayout,
22006948baSMark Brown 		 "Watchdog cannot be stopped once started (default="
23006948baSMark Brown 		 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
24006948baSMark Brown 
25006948baSMark Brown static DEFINE_MUTEX(wdt_mutex);
26006948baSMark Brown 
27006948baSMark Brown static struct {
282e51d90fSAxel Lin 	unsigned int time;  /* Seconds */
29006948baSMark Brown 	u16 val;	    /* To be set in WM8350_SYSTEM_CONTROL_2 */
30006948baSMark Brown } wm8350_wdt_cfgs[] = {
31006948baSMark Brown 	{ 1, 0x02 },
32006948baSMark Brown 	{ 2, 0x04 },
33006948baSMark Brown 	{ 4, 0x05 },
34006948baSMark Brown };
35006948baSMark Brown 
wm8350_wdt_set_timeout(struct watchdog_device * wdt_dev,unsigned int timeout)362e51d90fSAxel Lin static int wm8350_wdt_set_timeout(struct watchdog_device *wdt_dev,
372e51d90fSAxel Lin 				  unsigned int timeout)
38006948baSMark Brown {
392e51d90fSAxel Lin 	struct wm8350 *wm8350 = watchdog_get_drvdata(wdt_dev);
402e51d90fSAxel Lin 	int ret, i;
41006948baSMark Brown 	u16 reg;
42006948baSMark Brown 
432e51d90fSAxel Lin 	for (i = 0; i < ARRAY_SIZE(wm8350_wdt_cfgs); i++)
442e51d90fSAxel Lin 		if (wm8350_wdt_cfgs[i].time == timeout)
452e51d90fSAxel Lin 			break;
462e51d90fSAxel Lin 	if (i == ARRAY_SIZE(wm8350_wdt_cfgs))
472e51d90fSAxel Lin 		return -EINVAL;
482e51d90fSAxel Lin 
49006948baSMark Brown 	mutex_lock(&wdt_mutex);
50006948baSMark Brown 	wm8350_reg_unlock(wm8350);
51006948baSMark Brown 
52006948baSMark Brown 	reg = wm8350_reg_read(wm8350, WM8350_SYSTEM_CONTROL_2);
53006948baSMark Brown 	reg &= ~WM8350_WDOG_TO_MASK;
542e51d90fSAxel Lin 	reg |= wm8350_wdt_cfgs[i].val;
55006948baSMark Brown 	ret = wm8350_reg_write(wm8350, WM8350_SYSTEM_CONTROL_2, reg);
56006948baSMark Brown 
57006948baSMark Brown 	wm8350_reg_lock(wm8350);
58006948baSMark Brown 	mutex_unlock(&wdt_mutex);
59006948baSMark Brown 
600197c1c4SWim Van Sebroeck 	wdt_dev->timeout = timeout;
61006948baSMark Brown 	return ret;
62006948baSMark Brown }
63006948baSMark Brown 
wm8350_wdt_start(struct watchdog_device * wdt_dev)642e51d90fSAxel Lin static int wm8350_wdt_start(struct watchdog_device *wdt_dev)
65006948baSMark Brown {
662e51d90fSAxel Lin 	struct wm8350 *wm8350 = watchdog_get_drvdata(wdt_dev);
67006948baSMark Brown 	int ret;
68006948baSMark Brown 	u16 reg;
69006948baSMark Brown 
70006948baSMark Brown 	mutex_lock(&wdt_mutex);
71006948baSMark Brown 	wm8350_reg_unlock(wm8350);
72006948baSMark Brown 
73006948baSMark Brown 	reg = wm8350_reg_read(wm8350, WM8350_SYSTEM_CONTROL_2);
74006948baSMark Brown 	reg &= ~WM8350_WDOG_MODE_MASK;
75006948baSMark Brown 	reg |= 0x20;
76006948baSMark Brown 	ret = wm8350_reg_write(wm8350, WM8350_SYSTEM_CONTROL_2, reg);
77006948baSMark Brown 
78006948baSMark Brown 	wm8350_reg_lock(wm8350);
79006948baSMark Brown 	mutex_unlock(&wdt_mutex);
80006948baSMark Brown 
81006948baSMark Brown 	return ret;
82006948baSMark Brown }
83006948baSMark Brown 
wm8350_wdt_stop(struct watchdog_device * wdt_dev)842e51d90fSAxel Lin static int wm8350_wdt_stop(struct watchdog_device *wdt_dev)
85006948baSMark Brown {
862e51d90fSAxel Lin 	struct wm8350 *wm8350 = watchdog_get_drvdata(wdt_dev);
87006948baSMark Brown 	int ret;
88006948baSMark Brown 	u16 reg;
89006948baSMark Brown 
90006948baSMark Brown 	mutex_lock(&wdt_mutex);
91006948baSMark Brown 	wm8350_reg_unlock(wm8350);
92006948baSMark Brown 
93006948baSMark Brown 	reg = wm8350_reg_read(wm8350, WM8350_SYSTEM_CONTROL_2);
94006948baSMark Brown 	reg &= ~WM8350_WDOG_MODE_MASK;
95006948baSMark Brown 	ret = wm8350_reg_write(wm8350, WM8350_SYSTEM_CONTROL_2, reg);
96006948baSMark Brown 
97006948baSMark Brown 	wm8350_reg_lock(wm8350);
98006948baSMark Brown 	mutex_unlock(&wdt_mutex);
99006948baSMark Brown 
100006948baSMark Brown 	return ret;
101006948baSMark Brown }
102006948baSMark Brown 
wm8350_wdt_ping(struct watchdog_device * wdt_dev)1032e51d90fSAxel Lin static int wm8350_wdt_ping(struct watchdog_device *wdt_dev)
104006948baSMark Brown {
1052e51d90fSAxel Lin 	struct wm8350 *wm8350 = watchdog_get_drvdata(wdt_dev);
106006948baSMark Brown 	int ret;
107006948baSMark Brown 	u16 reg;
108006948baSMark Brown 
109006948baSMark Brown 	mutex_lock(&wdt_mutex);
110006948baSMark Brown 
111006948baSMark Brown 	reg = wm8350_reg_read(wm8350, WM8350_SYSTEM_CONTROL_2);
112006948baSMark Brown 	ret = wm8350_reg_write(wm8350, WM8350_SYSTEM_CONTROL_2, reg);
113006948baSMark Brown 
114006948baSMark Brown 	mutex_unlock(&wdt_mutex);
115006948baSMark Brown 
116006948baSMark Brown 	return ret;
117006948baSMark Brown }
118006948baSMark Brown 
1192e51d90fSAxel Lin static const struct watchdog_info wm8350_wdt_info = {
120006948baSMark Brown 	.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
121006948baSMark Brown 	.identity = "WM8350 Watchdog",
122006948baSMark Brown };
123006948baSMark Brown 
1242e51d90fSAxel Lin static const struct watchdog_ops wm8350_wdt_ops = {
125006948baSMark Brown 	.owner = THIS_MODULE,
1262e51d90fSAxel Lin 	.start = wm8350_wdt_start,
1272e51d90fSAxel Lin 	.stop = wm8350_wdt_stop,
1282e51d90fSAxel Lin 	.ping = wm8350_wdt_ping,
1292e51d90fSAxel Lin 	.set_timeout = wm8350_wdt_set_timeout,
130006948baSMark Brown };
131006948baSMark Brown 
1322e51d90fSAxel Lin static struct watchdog_device wm8350_wdt = {
1332e51d90fSAxel Lin 	.info = &wm8350_wdt_info,
1342e51d90fSAxel Lin 	.ops = &wm8350_wdt_ops,
1352e51d90fSAxel Lin 	.timeout = 4,
1362e51d90fSAxel Lin 	.min_timeout = 1,
1372e51d90fSAxel Lin 	.max_timeout = 4,
138006948baSMark Brown };
139006948baSMark Brown 
wm8350_wdt_probe(struct platform_device * pdev)1402d991a16SBill Pemberton static int wm8350_wdt_probe(struct platform_device *pdev)
141006948baSMark Brown {
142006948baSMark Brown 	struct wm8350 *wm8350 = platform_get_drvdata(pdev);
143006948baSMark Brown 
144006948baSMark Brown 	if (!wm8350) {
145cfca31ceSJulia Lawall 		pr_err("No driver data supplied\n");
146006948baSMark Brown 		return -ENODEV;
147006948baSMark Brown 	}
148006948baSMark Brown 
1492e51d90fSAxel Lin 	watchdog_set_nowayout(&wm8350_wdt, nowayout);
1502e51d90fSAxel Lin 	watchdog_set_drvdata(&wm8350_wdt, wm8350);
1516551881cSPratyush Anand 	wm8350_wdt.parent = &pdev->dev;
1522e51d90fSAxel Lin 
153006948baSMark Brown 	/* Default to 4s timeout */
1542e51d90fSAxel Lin 	wm8350_wdt_set_timeout(&wm8350_wdt, 4);
155006948baSMark Brown 
156*0e89b2c9SUwe Kleine-König 	return devm_watchdog_register_device(&pdev->dev, &wm8350_wdt);
157006948baSMark Brown }
158006948baSMark Brown 
159006948baSMark Brown static struct platform_driver wm8350_wdt_driver = {
160006948baSMark Brown 	.probe = wm8350_wdt_probe,
161006948baSMark Brown 	.driver = {
162006948baSMark Brown 		.name = "wm8350-wdt",
163006948baSMark Brown 	},
164006948baSMark Brown };
165006948baSMark Brown 
166216f3ad9SMark Brown module_platform_driver(wm8350_wdt_driver);
167006948baSMark Brown 
168006948baSMark Brown MODULE_AUTHOR("Mark Brown");
169006948baSMark Brown MODULE_DESCRIPTION("WM8350 Watchdog");
170006948baSMark Brown MODULE_LICENSE("GPL");
171006948baSMark Brown MODULE_ALIAS("platform:wm8350-wdt");
172