1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Watchdog driver for the wm8350 4 * 5 * Copyright (C) 2007, 2008 Wolfson Microelectronics <linux@wolfsonmicro.com> 6 */ 7 8 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 9 10 #include <linux/module.h> 11 #include <linux/moduleparam.h> 12 #include <linux/types.h> 13 #include <linux/kernel.h> 14 #include <linux/platform_device.h> 15 #include <linux/watchdog.h> 16 #include <linux/uaccess.h> 17 #include <linux/mfd/wm8350/core.h> 18 19 static bool nowayout = WATCHDOG_NOWAYOUT; 20 module_param(nowayout, bool, 0); 21 MODULE_PARM_DESC(nowayout, 22 "Watchdog cannot be stopped once started (default=" 23 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); 24 25 static DEFINE_MUTEX(wdt_mutex); 26 27 static struct { 28 unsigned int time; /* Seconds */ 29 u16 val; /* To be set in WM8350_SYSTEM_CONTROL_2 */ 30 } wm8350_wdt_cfgs[] = { 31 { 1, 0x02 }, 32 { 2, 0x04 }, 33 { 4, 0x05 }, 34 }; 35 36 static int wm8350_wdt_set_timeout(struct watchdog_device *wdt_dev, 37 unsigned int timeout) 38 { 39 struct wm8350 *wm8350 = watchdog_get_drvdata(wdt_dev); 40 int ret, i; 41 u16 reg; 42 43 for (i = 0; i < ARRAY_SIZE(wm8350_wdt_cfgs); i++) 44 if (wm8350_wdt_cfgs[i].time == timeout) 45 break; 46 if (i == ARRAY_SIZE(wm8350_wdt_cfgs)) 47 return -EINVAL; 48 49 mutex_lock(&wdt_mutex); 50 wm8350_reg_unlock(wm8350); 51 52 reg = wm8350_reg_read(wm8350, WM8350_SYSTEM_CONTROL_2); 53 reg &= ~WM8350_WDOG_TO_MASK; 54 reg |= wm8350_wdt_cfgs[i].val; 55 ret = wm8350_reg_write(wm8350, WM8350_SYSTEM_CONTROL_2, reg); 56 57 wm8350_reg_lock(wm8350); 58 mutex_unlock(&wdt_mutex); 59 60 wdt_dev->timeout = timeout; 61 return ret; 62 } 63 64 static int wm8350_wdt_start(struct watchdog_device *wdt_dev) 65 { 66 struct wm8350 *wm8350 = watchdog_get_drvdata(wdt_dev); 67 int ret; 68 u16 reg; 69 70 mutex_lock(&wdt_mutex); 71 wm8350_reg_unlock(wm8350); 72 73 reg = wm8350_reg_read(wm8350, WM8350_SYSTEM_CONTROL_2); 74 reg &= ~WM8350_WDOG_MODE_MASK; 75 reg |= 0x20; 76 ret = wm8350_reg_write(wm8350, WM8350_SYSTEM_CONTROL_2, reg); 77 78 wm8350_reg_lock(wm8350); 79 mutex_unlock(&wdt_mutex); 80 81 return ret; 82 } 83 84 static int wm8350_wdt_stop(struct watchdog_device *wdt_dev) 85 { 86 struct wm8350 *wm8350 = watchdog_get_drvdata(wdt_dev); 87 int ret; 88 u16 reg; 89 90 mutex_lock(&wdt_mutex); 91 wm8350_reg_unlock(wm8350); 92 93 reg = wm8350_reg_read(wm8350, WM8350_SYSTEM_CONTROL_2); 94 reg &= ~WM8350_WDOG_MODE_MASK; 95 ret = wm8350_reg_write(wm8350, WM8350_SYSTEM_CONTROL_2, reg); 96 97 wm8350_reg_lock(wm8350); 98 mutex_unlock(&wdt_mutex); 99 100 return ret; 101 } 102 103 static int wm8350_wdt_ping(struct watchdog_device *wdt_dev) 104 { 105 struct wm8350 *wm8350 = watchdog_get_drvdata(wdt_dev); 106 int ret; 107 u16 reg; 108 109 mutex_lock(&wdt_mutex); 110 111 reg = wm8350_reg_read(wm8350, WM8350_SYSTEM_CONTROL_2); 112 ret = wm8350_reg_write(wm8350, WM8350_SYSTEM_CONTROL_2, reg); 113 114 mutex_unlock(&wdt_mutex); 115 116 return ret; 117 } 118 119 static const struct watchdog_info wm8350_wdt_info = { 120 .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, 121 .identity = "WM8350 Watchdog", 122 }; 123 124 static const struct watchdog_ops wm8350_wdt_ops = { 125 .owner = THIS_MODULE, 126 .start = wm8350_wdt_start, 127 .stop = wm8350_wdt_stop, 128 .ping = wm8350_wdt_ping, 129 .set_timeout = wm8350_wdt_set_timeout, 130 }; 131 132 static struct watchdog_device wm8350_wdt = { 133 .info = &wm8350_wdt_info, 134 .ops = &wm8350_wdt_ops, 135 .timeout = 4, 136 .min_timeout = 1, 137 .max_timeout = 4, 138 }; 139 140 static int wm8350_wdt_probe(struct platform_device *pdev) 141 { 142 struct wm8350 *wm8350 = platform_get_drvdata(pdev); 143 144 if (!wm8350) { 145 pr_err("No driver data supplied\n"); 146 return -ENODEV; 147 } 148 149 watchdog_set_nowayout(&wm8350_wdt, nowayout); 150 watchdog_set_drvdata(&wm8350_wdt, wm8350); 151 wm8350_wdt.parent = &pdev->dev; 152 153 /* Default to 4s timeout */ 154 wm8350_wdt_set_timeout(&wm8350_wdt, 4); 155 156 return devm_watchdog_register_device(&pdev->dev, &wm8350_wdt); 157 } 158 159 static struct platform_driver wm8350_wdt_driver = { 160 .probe = wm8350_wdt_probe, 161 .driver = { 162 .name = "wm8350-wdt", 163 }, 164 }; 165 166 module_platform_driver(wm8350_wdt_driver); 167 168 MODULE_AUTHOR("Mark Brown"); 169 MODULE_DESCRIPTION("WM8350 Watchdog"); 170 MODULE_LICENSE("GPL"); 171 MODULE_ALIAS("platform:wm8350-wdt"); 172