Commit 22b1c841e31510c3124c88a13b8a7ada14e2e2d1
Committed by
Wim Van Sebroeck
1 parent
2b9366b669
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>
Showing 3 changed files with 210 additions and 0 deletions Side-by-side Diff
drivers/watchdog/Kconfig
... | ... | @@ -327,6 +327,17 @@ |
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 |
drivers/watchdog/Makefile
... | ... | @@ -48,6 +48,7 @@ |
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 |
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"); |