summaryrefslogtreecommitdiffstats
path: root/drivers/watchdog/wm8350_wdt.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/watchdog/wm8350_wdt.c')
-rw-r--r--drivers/watchdog/wm8350_wdt.c178
1 files changed, 178 insertions, 0 deletions
diff --git a/drivers/watchdog/wm8350_wdt.c b/drivers/watchdog/wm8350_wdt.c
new file mode 100644
index 000000000..33c62d51f
--- /dev/null
+++ b/drivers/watchdog/wm8350_wdt.c
@@ -0,0 +1,178 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Watchdog driver for the wm8350
+ *
+ * Copyright (C) 2007, 2008 Wolfson Microelectronics <linux@wolfsonmicro.com>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/watchdog.h>
+#include <linux/uaccess.h>
+#include <linux/mfd/wm8350/core.h>
+
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
+MODULE_PARM_DESC(nowayout,
+ "Watchdog cannot be stopped once started (default="
+ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+static DEFINE_MUTEX(wdt_mutex);
+
+static struct {
+ unsigned int time; /* Seconds */
+ u16 val; /* To be set in WM8350_SYSTEM_CONTROL_2 */
+} wm8350_wdt_cfgs[] = {
+ { 1, 0x02 },
+ { 2, 0x04 },
+ { 4, 0x05 },
+};
+
+static int wm8350_wdt_set_timeout(struct watchdog_device *wdt_dev,
+ unsigned int timeout)
+{
+ struct wm8350 *wm8350 = watchdog_get_drvdata(wdt_dev);
+ int ret, i;
+ u16 reg;
+
+ for (i = 0; i < ARRAY_SIZE(wm8350_wdt_cfgs); i++)
+ if (wm8350_wdt_cfgs[i].time == timeout)
+ break;
+ if (i == ARRAY_SIZE(wm8350_wdt_cfgs))
+ return -EINVAL;
+
+ mutex_lock(&wdt_mutex);
+ wm8350_reg_unlock(wm8350);
+
+ reg = wm8350_reg_read(wm8350, WM8350_SYSTEM_CONTROL_2);
+ reg &= ~WM8350_WDOG_TO_MASK;
+ reg |= wm8350_wdt_cfgs[i].val;
+ ret = wm8350_reg_write(wm8350, WM8350_SYSTEM_CONTROL_2, reg);
+
+ wm8350_reg_lock(wm8350);
+ mutex_unlock(&wdt_mutex);
+
+ wdt_dev->timeout = timeout;
+ return ret;
+}
+
+static int wm8350_wdt_start(struct watchdog_device *wdt_dev)
+{
+ struct wm8350 *wm8350 = watchdog_get_drvdata(wdt_dev);
+ int ret;
+ u16 reg;
+
+ mutex_lock(&wdt_mutex);
+ wm8350_reg_unlock(wm8350);
+
+ reg = wm8350_reg_read(wm8350, WM8350_SYSTEM_CONTROL_2);
+ reg &= ~WM8350_WDOG_MODE_MASK;
+ reg |= 0x20;
+ ret = wm8350_reg_write(wm8350, WM8350_SYSTEM_CONTROL_2, reg);
+
+ wm8350_reg_lock(wm8350);
+ mutex_unlock(&wdt_mutex);
+
+ return ret;
+}
+
+static int wm8350_wdt_stop(struct watchdog_device *wdt_dev)
+{
+ struct wm8350 *wm8350 = watchdog_get_drvdata(wdt_dev);
+ int ret;
+ u16 reg;
+
+ mutex_lock(&wdt_mutex);
+ wm8350_reg_unlock(wm8350);
+
+ reg = wm8350_reg_read(wm8350, WM8350_SYSTEM_CONTROL_2);
+ reg &= ~WM8350_WDOG_MODE_MASK;
+ ret = wm8350_reg_write(wm8350, WM8350_SYSTEM_CONTROL_2, reg);
+
+ wm8350_reg_lock(wm8350);
+ mutex_unlock(&wdt_mutex);
+
+ return ret;
+}
+
+static int wm8350_wdt_ping(struct watchdog_device *wdt_dev)
+{
+ struct wm8350 *wm8350 = watchdog_get_drvdata(wdt_dev);
+ int ret;
+ u16 reg;
+
+ mutex_lock(&wdt_mutex);
+
+ reg = wm8350_reg_read(wm8350, WM8350_SYSTEM_CONTROL_2);
+ ret = wm8350_reg_write(wm8350, WM8350_SYSTEM_CONTROL_2, reg);
+
+ mutex_unlock(&wdt_mutex);
+
+ return ret;
+}
+
+static const struct watchdog_info wm8350_wdt_info = {
+ .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
+ .identity = "WM8350 Watchdog",
+};
+
+static const struct watchdog_ops wm8350_wdt_ops = {
+ .owner = THIS_MODULE,
+ .start = wm8350_wdt_start,
+ .stop = wm8350_wdt_stop,
+ .ping = wm8350_wdt_ping,
+ .set_timeout = wm8350_wdt_set_timeout,
+};
+
+static struct watchdog_device wm8350_wdt = {
+ .info = &wm8350_wdt_info,
+ .ops = &wm8350_wdt_ops,
+ .timeout = 4,
+ .min_timeout = 1,
+ .max_timeout = 4,
+};
+
+static int wm8350_wdt_probe(struct platform_device *pdev)
+{
+ struct wm8350 *wm8350 = platform_get_drvdata(pdev);
+
+ if (!wm8350) {
+ pr_err("No driver data supplied\n");
+ return -ENODEV;
+ }
+
+ watchdog_set_nowayout(&wm8350_wdt, nowayout);
+ watchdog_set_drvdata(&wm8350_wdt, wm8350);
+ wm8350_wdt.parent = &pdev->dev;
+
+ /* Default to 4s timeout */
+ wm8350_wdt_set_timeout(&wm8350_wdt, 4);
+
+ return watchdog_register_device(&wm8350_wdt);
+}
+
+static int wm8350_wdt_remove(struct platform_device *pdev)
+{
+ watchdog_unregister_device(&wm8350_wdt);
+ return 0;
+}
+
+static struct platform_driver wm8350_wdt_driver = {
+ .probe = wm8350_wdt_probe,
+ .remove = wm8350_wdt_remove,
+ .driver = {
+ .name = "wm8350-wdt",
+ },
+};
+
+module_platform_driver(wm8350_wdt_driver);
+
+MODULE_AUTHOR("Mark Brown");
+MODULE_DESCRIPTION("WM8350 Watchdog");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:wm8350-wdt");