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