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