xref: /openbmc/linux/drivers/watchdog/wm8350_wdt.c (revision 6551881c)
1006948baSMark Brown /*
2006948baSMark Brown  * Watchdog driver for the wm8350
3006948baSMark Brown  *
4006948baSMark Brown  * Copyright (C) 2007, 2008 Wolfson Microelectronics <linux@wolfsonmicro.com>
5006948baSMark Brown  *
6006948baSMark Brown  * This program is free software; you can redistribute it and/or
7006948baSMark Brown  * modify it under the terms of the GNU General Public License
8006948baSMark Brown  * as published by the Free Software Foundation
9006948baSMark Brown  */
10006948baSMark Brown 
1127c766aaSJoe Perches #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
1227c766aaSJoe Perches 
13006948baSMark Brown #include <linux/module.h>
14006948baSMark Brown #include <linux/moduleparam.h>
15006948baSMark Brown #include <linux/types.h>
16006948baSMark Brown #include <linux/kernel.h>
17006948baSMark Brown #include <linux/platform_device.h>
18006948baSMark Brown #include <linux/watchdog.h>
19006948baSMark Brown #include <linux/uaccess.h>
20006948baSMark Brown #include <linux/mfd/wm8350/core.h>
21006948baSMark Brown 
2286a1e189SWim Van Sebroeck static bool nowayout = WATCHDOG_NOWAYOUT;
2386a1e189SWim Van Sebroeck module_param(nowayout, bool, 0);
24006948baSMark Brown MODULE_PARM_DESC(nowayout,
25006948baSMark Brown 		 "Watchdog cannot be stopped once started (default="
26006948baSMark Brown 		 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
27006948baSMark Brown 
28006948baSMark Brown static DEFINE_MUTEX(wdt_mutex);
29006948baSMark Brown 
30006948baSMark Brown static struct {
312e51d90fSAxel Lin 	unsigned int time;  /* Seconds */
32006948baSMark Brown 	u16 val;	    /* To be set in WM8350_SYSTEM_CONTROL_2 */
33006948baSMark Brown } wm8350_wdt_cfgs[] = {
34006948baSMark Brown 	{ 1, 0x02 },
35006948baSMark Brown 	{ 2, 0x04 },
36006948baSMark Brown 	{ 4, 0x05 },
37006948baSMark Brown };
38006948baSMark Brown 
392e51d90fSAxel Lin static int wm8350_wdt_set_timeout(struct watchdog_device *wdt_dev,
402e51d90fSAxel Lin 				  unsigned int timeout)
41006948baSMark Brown {
422e51d90fSAxel Lin 	struct wm8350 *wm8350 = watchdog_get_drvdata(wdt_dev);
432e51d90fSAxel Lin 	int ret, i;
44006948baSMark Brown 	u16 reg;
45006948baSMark Brown 
462e51d90fSAxel Lin 	for (i = 0; i < ARRAY_SIZE(wm8350_wdt_cfgs); i++)
472e51d90fSAxel Lin 		if (wm8350_wdt_cfgs[i].time == timeout)
482e51d90fSAxel Lin 			break;
492e51d90fSAxel Lin 	if (i == ARRAY_SIZE(wm8350_wdt_cfgs))
502e51d90fSAxel Lin 		return -EINVAL;
512e51d90fSAxel Lin 
52006948baSMark Brown 	mutex_lock(&wdt_mutex);
53006948baSMark Brown 	wm8350_reg_unlock(wm8350);
54006948baSMark Brown 
55006948baSMark Brown 	reg = wm8350_reg_read(wm8350, WM8350_SYSTEM_CONTROL_2);
56006948baSMark Brown 	reg &= ~WM8350_WDOG_TO_MASK;
572e51d90fSAxel Lin 	reg |= wm8350_wdt_cfgs[i].val;
58006948baSMark Brown 	ret = wm8350_reg_write(wm8350, WM8350_SYSTEM_CONTROL_2, reg);
59006948baSMark Brown 
60006948baSMark Brown 	wm8350_reg_lock(wm8350);
61006948baSMark Brown 	mutex_unlock(&wdt_mutex);
62006948baSMark Brown 
630197c1c4SWim Van Sebroeck 	wdt_dev->timeout = timeout;
64006948baSMark Brown 	return ret;
65006948baSMark Brown }
66006948baSMark Brown 
672e51d90fSAxel Lin static int wm8350_wdt_start(struct watchdog_device *wdt_dev)
68006948baSMark Brown {
692e51d90fSAxel Lin 	struct wm8350 *wm8350 = watchdog_get_drvdata(wdt_dev);
70006948baSMark Brown 	int ret;
71006948baSMark Brown 	u16 reg;
72006948baSMark Brown 
73006948baSMark Brown 	mutex_lock(&wdt_mutex);
74006948baSMark Brown 	wm8350_reg_unlock(wm8350);
75006948baSMark Brown 
76006948baSMark Brown 	reg = wm8350_reg_read(wm8350, WM8350_SYSTEM_CONTROL_2);
77006948baSMark Brown 	reg &= ~WM8350_WDOG_MODE_MASK;
78006948baSMark Brown 	reg |= 0x20;
79006948baSMark Brown 	ret = wm8350_reg_write(wm8350, WM8350_SYSTEM_CONTROL_2, reg);
80006948baSMark Brown 
81006948baSMark Brown 	wm8350_reg_lock(wm8350);
82006948baSMark Brown 	mutex_unlock(&wdt_mutex);
83006948baSMark Brown 
84006948baSMark Brown 	return ret;
85006948baSMark Brown }
86006948baSMark Brown 
872e51d90fSAxel Lin static int wm8350_wdt_stop(struct watchdog_device *wdt_dev)
88006948baSMark Brown {
892e51d90fSAxel Lin 	struct wm8350 *wm8350 = watchdog_get_drvdata(wdt_dev);
90006948baSMark Brown 	int ret;
91006948baSMark Brown 	u16 reg;
92006948baSMark Brown 
93006948baSMark Brown 	mutex_lock(&wdt_mutex);
94006948baSMark Brown 	wm8350_reg_unlock(wm8350);
95006948baSMark Brown 
96006948baSMark Brown 	reg = wm8350_reg_read(wm8350, WM8350_SYSTEM_CONTROL_2);
97006948baSMark Brown 	reg &= ~WM8350_WDOG_MODE_MASK;
98006948baSMark Brown 	ret = wm8350_reg_write(wm8350, WM8350_SYSTEM_CONTROL_2, reg);
99006948baSMark Brown 
100006948baSMark Brown 	wm8350_reg_lock(wm8350);
101006948baSMark Brown 	mutex_unlock(&wdt_mutex);
102006948baSMark Brown 
103006948baSMark Brown 	return ret;
104006948baSMark Brown }
105006948baSMark Brown 
1062e51d90fSAxel Lin static int wm8350_wdt_ping(struct watchdog_device *wdt_dev)
107006948baSMark Brown {
1082e51d90fSAxel Lin 	struct wm8350 *wm8350 = watchdog_get_drvdata(wdt_dev);
109006948baSMark Brown 	int ret;
110006948baSMark Brown 	u16 reg;
111006948baSMark Brown 
112006948baSMark Brown 	mutex_lock(&wdt_mutex);
113006948baSMark Brown 
114006948baSMark Brown 	reg = wm8350_reg_read(wm8350, WM8350_SYSTEM_CONTROL_2);
115006948baSMark Brown 	ret = wm8350_reg_write(wm8350, WM8350_SYSTEM_CONTROL_2, reg);
116006948baSMark Brown 
117006948baSMark Brown 	mutex_unlock(&wdt_mutex);
118006948baSMark Brown 
119006948baSMark Brown 	return ret;
120006948baSMark Brown }
121006948baSMark Brown 
1222e51d90fSAxel Lin static const struct watchdog_info wm8350_wdt_info = {
123006948baSMark Brown 	.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
124006948baSMark Brown 	.identity = "WM8350 Watchdog",
125006948baSMark Brown };
126006948baSMark Brown 
1272e51d90fSAxel Lin static const struct watchdog_ops wm8350_wdt_ops = {
128006948baSMark Brown 	.owner = THIS_MODULE,
1292e51d90fSAxel Lin 	.start = wm8350_wdt_start,
1302e51d90fSAxel Lin 	.stop = wm8350_wdt_stop,
1312e51d90fSAxel Lin 	.ping = wm8350_wdt_ping,
1322e51d90fSAxel Lin 	.set_timeout = wm8350_wdt_set_timeout,
133006948baSMark Brown };
134006948baSMark Brown 
1352e51d90fSAxel Lin static struct watchdog_device wm8350_wdt = {
1362e51d90fSAxel Lin 	.info = &wm8350_wdt_info,
1372e51d90fSAxel Lin 	.ops = &wm8350_wdt_ops,
1382e51d90fSAxel Lin 	.timeout = 4,
1392e51d90fSAxel Lin 	.min_timeout = 1,
1402e51d90fSAxel Lin 	.max_timeout = 4,
141006948baSMark Brown };
142006948baSMark Brown 
1432d991a16SBill Pemberton static int wm8350_wdt_probe(struct platform_device *pdev)
144006948baSMark Brown {
145006948baSMark Brown 	struct wm8350 *wm8350 = platform_get_drvdata(pdev);
146006948baSMark Brown 
147006948baSMark Brown 	if (!wm8350) {
148cfca31ceSJulia Lawall 		pr_err("No driver data supplied\n");
149006948baSMark Brown 		return -ENODEV;
150006948baSMark Brown 	}
151006948baSMark Brown 
1522e51d90fSAxel Lin 	watchdog_set_nowayout(&wm8350_wdt, nowayout);
1532e51d90fSAxel Lin 	watchdog_set_drvdata(&wm8350_wdt, wm8350);
1546551881cSPratyush Anand 	wm8350_wdt.parent = &pdev->dev;
1552e51d90fSAxel Lin 
156006948baSMark Brown 	/* Default to 4s timeout */
1572e51d90fSAxel Lin 	wm8350_wdt_set_timeout(&wm8350_wdt, 4);
158006948baSMark Brown 
1592e51d90fSAxel Lin 	return watchdog_register_device(&wm8350_wdt);
160006948baSMark Brown }
161006948baSMark Brown 
1624b12b896SBill Pemberton static int wm8350_wdt_remove(struct platform_device *pdev)
163006948baSMark Brown {
1642e51d90fSAxel Lin 	watchdog_unregister_device(&wm8350_wdt);
165006948baSMark Brown 	return 0;
166006948baSMark Brown }
167006948baSMark Brown 
168006948baSMark Brown static struct platform_driver wm8350_wdt_driver = {
169006948baSMark Brown 	.probe = wm8350_wdt_probe,
17082268714SBill Pemberton 	.remove = wm8350_wdt_remove,
171006948baSMark Brown 	.driver = {
172006948baSMark Brown 		.name = "wm8350-wdt",
173006948baSMark Brown 	},
174006948baSMark Brown };
175006948baSMark Brown 
176216f3ad9SMark Brown module_platform_driver(wm8350_wdt_driver);
177006948baSMark Brown 
178006948baSMark Brown MODULE_AUTHOR("Mark Brown");
179006948baSMark Brown MODULE_DESCRIPTION("WM8350 Watchdog");
180006948baSMark Brown MODULE_LICENSE("GPL");
181006948baSMark Brown MODULE_ALIAS("platform:wm8350-wdt");
182