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