Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

watchdog: add driver for Ricoh RN5T618 watchdog

This adds a driver for the watchdog timer available in Ricoh RN5T618
PMIC. The device supports a programmable expiration time of 1, 8, 32
or 128 seconds.

Signed-off-by: Beniamino Galvani <b.galvani@gmail.com>
Reviewed-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Wim Van Sebroeck <wim@iguana.be>

authored by

Beniamino Galvani and committed by
Wim Van Sebroeck
22b1c841 2b9366b6

+210
+11
drivers/watchdog/Kconfig
··· 327 327 To compile this driver as a module, choose M here: the 328 328 module will be called orion_wdt. 329 329 330 + config RN5T618_WATCHDOG 331 + tristate "Ricoh RN5T618 watchdog" 332 + depends on MFD_RN5T618 333 + select WATCHDOG_CORE 334 + help 335 + If you say yes here you get support for watchdog on the Ricoh 336 + RN5T618 PMIC. 337 + 338 + This driver can also be built as a module. If so, the module 339 + will be called rn5t618_wdt. 340 + 330 341 config SUNXI_WATCHDOG 331 342 tristate "Allwinner SoCs watchdog support" 332 343 depends on ARCH_SUNXI
+1
drivers/watchdog/Makefile
··· 48 48 obj-$(CONFIG_DAVINCI_WATCHDOG) += davinci_wdt.o 49 49 obj-$(CONFIG_ORION_WATCHDOG) += orion_wdt.o 50 50 obj-$(CONFIG_SUNXI_WATCHDOG) += sunxi_wdt.o 51 + obj-$(CONFIG_RN5T618_WATCHDOG) += rn5t618_wdt.o 51 52 obj-$(CONFIG_COH901327_WATCHDOG) += coh901327_wdt.o 52 53 obj-$(CONFIG_STMP3XXX_RTC_WATCHDOG) += stmp3xxx_rtc_wdt.o 53 54 obj-$(CONFIG_NUC900_WATCHDOG) += nuc900_wdt.o
+198
drivers/watchdog/rn5t618_wdt.c
··· 1 + /* 2 + * Watchdog driver for Ricoh RN5T618 PMIC 3 + * 4 + * Copyright (C) 2014 Beniamino Galvani <b.galvani@gmail.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 + * version 2 as published by the Free Software Foundation. 9 + * 10 + * You should have received a copy of the GNU General Public License 11 + * along with this program. If not, see <http://www.gnu.org/licenses/>. 12 + */ 13 + 14 + #include <linux/device.h> 15 + #include <linux/mfd/rn5t618.h> 16 + #include <linux/module.h> 17 + #include <linux/platform_device.h> 18 + #include <linux/watchdog.h> 19 + 20 + #define DRIVER_NAME "rn5t618-wdt" 21 + 22 + static bool nowayout = WATCHDOG_NOWAYOUT; 23 + static unsigned int timeout; 24 + 25 + module_param(timeout, uint, 0); 26 + MODULE_PARM_DESC(timeout, "Initial watchdog timeout in seconds"); 27 + 28 + module_param(nowayout, bool, 0); 29 + MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" 30 + __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); 31 + 32 + struct rn5t618_wdt { 33 + struct watchdog_device wdt_dev; 34 + struct rn5t618 *rn5t618; 35 + }; 36 + 37 + /* 38 + * This array encodes the values of WDOGTIM field for the supported 39 + * watchdog expiration times. If the watchdog is not accessed before 40 + * the timer expiration, the PMU generates an interrupt and if the CPU 41 + * doesn't clear it within one second the system is restarted. 42 + */ 43 + static const struct { 44 + u8 reg_val; 45 + unsigned int time; 46 + } rn5t618_wdt_map[] = { 47 + { 0, 1 }, 48 + { 1, 8 }, 49 + { 2, 32 }, 50 + { 3, 128 }, 51 + }; 52 + 53 + static int rn5t618_wdt_set_timeout(struct watchdog_device *wdt_dev, 54 + unsigned int t) 55 + { 56 + struct rn5t618_wdt *wdt = watchdog_get_drvdata(wdt_dev); 57 + int ret, i; 58 + 59 + for (i = 0; i < ARRAY_SIZE(rn5t618_wdt_map); i++) { 60 + if (rn5t618_wdt_map[i].time + 1 >= t) 61 + break; 62 + } 63 + 64 + if (i == ARRAY_SIZE(rn5t618_wdt_map)) 65 + return -EINVAL; 66 + 67 + ret = regmap_update_bits(wdt->rn5t618->regmap, RN5T618_WATCHDOG, 68 + RN5T618_WATCHDOG_WDOGTIM_M, 69 + rn5t618_wdt_map[i].reg_val); 70 + if (!ret) 71 + wdt_dev->timeout = rn5t618_wdt_map[i].time; 72 + 73 + return ret; 74 + } 75 + 76 + static int rn5t618_wdt_start(struct watchdog_device *wdt_dev) 77 + { 78 + struct rn5t618_wdt *wdt = watchdog_get_drvdata(wdt_dev); 79 + int ret; 80 + 81 + ret = rn5t618_wdt_set_timeout(wdt_dev, wdt_dev->timeout); 82 + if (ret) 83 + return ret; 84 + 85 + /* enable repower-on */ 86 + ret = regmap_update_bits(wdt->rn5t618->regmap, RN5T618_REPCNT, 87 + RN5T618_REPCNT_REPWRON, 88 + RN5T618_REPCNT_REPWRON); 89 + if (ret) 90 + return ret; 91 + 92 + /* enable watchdog */ 93 + ret = regmap_update_bits(wdt->rn5t618->regmap, RN5T618_WATCHDOG, 94 + RN5T618_WATCHDOG_WDOGEN, 95 + RN5T618_WATCHDOG_WDOGEN); 96 + if (ret) 97 + return ret; 98 + 99 + /* enable watchdog interrupt */ 100 + return regmap_update_bits(wdt->rn5t618->regmap, RN5T618_PWRIREN, 101 + RN5T618_PWRIRQ_IR_WDOG, 102 + RN5T618_PWRIRQ_IR_WDOG); 103 + } 104 + 105 + static int rn5t618_wdt_stop(struct watchdog_device *wdt_dev) 106 + { 107 + struct rn5t618_wdt *wdt = watchdog_get_drvdata(wdt_dev); 108 + 109 + return regmap_update_bits(wdt->rn5t618->regmap, RN5T618_WATCHDOG, 110 + RN5T618_WATCHDOG_WDOGEN, 0); 111 + } 112 + 113 + static int rn5t618_wdt_ping(struct watchdog_device *wdt_dev) 114 + { 115 + struct rn5t618_wdt *wdt = watchdog_get_drvdata(wdt_dev); 116 + unsigned int val; 117 + int ret; 118 + 119 + /* The counter is restarted after a R/W access to watchdog register */ 120 + ret = regmap_read(wdt->rn5t618->regmap, RN5T618_WATCHDOG, &val); 121 + if (ret) 122 + return ret; 123 + 124 + ret = regmap_write(wdt->rn5t618->regmap, RN5T618_WATCHDOG, val); 125 + if (ret) 126 + return ret; 127 + 128 + /* Clear pending watchdog interrupt */ 129 + return regmap_update_bits(wdt->rn5t618->regmap, RN5T618_PWRIRQ, 130 + RN5T618_PWRIRQ_IR_WDOG, 0); 131 + } 132 + 133 + static struct watchdog_info rn5t618_wdt_info = { 134 + .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | 135 + WDIOF_KEEPALIVEPING, 136 + .identity = DRIVER_NAME, 137 + }; 138 + 139 + static struct watchdog_ops rn5t618_wdt_ops = { 140 + .owner = THIS_MODULE, 141 + .start = rn5t618_wdt_start, 142 + .stop = rn5t618_wdt_stop, 143 + .ping = rn5t618_wdt_ping, 144 + .set_timeout = rn5t618_wdt_set_timeout, 145 + }; 146 + 147 + static int rn5t618_wdt_probe(struct platform_device *pdev) 148 + { 149 + struct rn5t618 *rn5t618 = dev_get_drvdata(pdev->dev.parent); 150 + struct rn5t618_wdt *wdt; 151 + int min_timeout, max_timeout; 152 + 153 + wdt = devm_kzalloc(&pdev->dev, sizeof(struct rn5t618_wdt), GFP_KERNEL); 154 + if (!wdt) 155 + return -ENOMEM; 156 + 157 + min_timeout = rn5t618_wdt_map[0].time; 158 + max_timeout = rn5t618_wdt_map[ARRAY_SIZE(rn5t618_wdt_map) - 1].time; 159 + 160 + wdt->rn5t618 = rn5t618; 161 + wdt->wdt_dev.info = &rn5t618_wdt_info; 162 + wdt->wdt_dev.ops = &rn5t618_wdt_ops; 163 + wdt->wdt_dev.min_timeout = min_timeout; 164 + wdt->wdt_dev.max_timeout = max_timeout; 165 + wdt->wdt_dev.timeout = max_timeout; 166 + wdt->wdt_dev.parent = &pdev->dev; 167 + 168 + watchdog_set_drvdata(&wdt->wdt_dev, wdt); 169 + watchdog_init_timeout(&wdt->wdt_dev, timeout, &pdev->dev); 170 + watchdog_set_nowayout(&wdt->wdt_dev, nowayout); 171 + 172 + platform_set_drvdata(pdev, wdt); 173 + 174 + return watchdog_register_device(&wdt->wdt_dev); 175 + } 176 + 177 + static int rn5t618_wdt_remove(struct platform_device *pdev) 178 + { 179 + struct rn5t618_wdt *wdt = platform_get_drvdata(pdev); 180 + 181 + watchdog_unregister_device(&wdt->wdt_dev); 182 + 183 + return 0; 184 + } 185 + 186 + static struct platform_driver rn5t618_wdt_driver = { 187 + .probe = rn5t618_wdt_probe, 188 + .remove = rn5t618_wdt_remove, 189 + .driver = { 190 + .name = DRIVER_NAME, 191 + }, 192 + }; 193 + 194 + module_platform_driver(rn5t618_wdt_driver); 195 + 196 + MODULE_AUTHOR("Beniamino Galvani <b.galvani@gmail.com>"); 197 + MODULE_DESCRIPTION("RN5T618 watchdog driver"); 198 + MODULE_LICENSE("GPL v2");