aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLaxman Dewangan <ldewangan@nvidia.com>2016-06-09 09:29:05 -0400
committerWim Van Sebroeck <wim@iguana.be>2016-07-17 15:01:07 -0400
commitff3bb2f5c3cd70f4dd642525916f300bffaff0ae (patch)
tree2ddbf2d0d7223311b440ebbc48a2421450ffb705
parent14b24a88a36602c2c4e3bbb955be1aa9378c3be2 (diff)
watchdog: max77620: Add support for watchdog timer
Maxim PMIC MAX77620 is Power management IC which have multiple sub blocks like regulators (DCDC/LDOs), GPIO, RTC, Clock, Watchdog timer etc. Add the driver for watchdog timer under watchdog framework. The driver implements the watchdog callbacks to start, stop, ping and set timeout for watchodg framework. Signed-off-by: Laxman Dewangan <ldewangan@nvidia.com> Reviewed-by: Guenter Roeck <linux@roeck-us.net> Signed-off-by: Guenter Roeck <linux@roeck-us.net> Signed-off-by: Wim Van Sebroeck <wim@iguana.be>
-rw-r--r--drivers/watchdog/Kconfig9
-rw-r--r--drivers/watchdog/Makefile1
-rw-r--r--drivers/watchdog/max77620_wdt.c227
3 files changed, 237 insertions, 0 deletions
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index a12bf540c0b1..65058d659d52 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -516,6 +516,15 @@ config MAX63XX_WATCHDOG
516 help 516 help
517 Support for memory mapped max63{69,70,71,72,73,74} watchdog timer. 517 Support for memory mapped max63{69,70,71,72,73,74} watchdog timer.
518 518
519config MAX77620_WATCHDOG
520 tristate "Maxim Max77620 Watchdog Timer"
521 depends on MFD_MAX77620
522 help
523 This is the driver for the Max77620 watchdog timer.
524 Say 'Y' here to enable the watchdog timer support for
525 MAX77620 chips. To compile this driver as a module,
526 choose M here: the module will be called max77620_wdt.
527
519config IMX2_WDT 528config IMX2_WDT
520 tristate "IMX2+ Watchdog" 529 tristate "IMX2+ Watchdog"
521 depends on ARCH_MXC || ARCH_LAYERSCAPE 530 depends on ARCH_MXC || ARCH_LAYERSCAPE
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index 4990c50b04e5..3c0ed73b2cab 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -204,6 +204,7 @@ obj-$(CONFIG_TANGOX_WATCHDOG) += tangox_wdt.o
204obj-$(CONFIG_WM831X_WATCHDOG) += wm831x_wdt.o 204obj-$(CONFIG_WM831X_WATCHDOG) += wm831x_wdt.o
205obj-$(CONFIG_WM8350_WATCHDOG) += wm8350_wdt.o 205obj-$(CONFIG_WM8350_WATCHDOG) += wm8350_wdt.o
206obj-$(CONFIG_MAX63XX_WATCHDOG) += max63xx_wdt.o 206obj-$(CONFIG_MAX63XX_WATCHDOG) += max63xx_wdt.o
207obj-$(CONFIG_MAX77620_WATCHDOG) += max77620_wdt.o
207obj-$(CONFIG_ZIIRAVE_WATCHDOG) += ziirave_wdt.o 208obj-$(CONFIG_ZIIRAVE_WATCHDOG) += ziirave_wdt.o
208obj-$(CONFIG_SOFT_WATCHDOG) += softdog.o 209obj-$(CONFIG_SOFT_WATCHDOG) += softdog.o
209obj-$(CONFIG_MENF21BMC_WATCHDOG) += menf21bmc_wdt.o 210obj-$(CONFIG_MENF21BMC_WATCHDOG) += menf21bmc_wdt.o
diff --git a/drivers/watchdog/max77620_wdt.c b/drivers/watchdog/max77620_wdt.c
new file mode 100644
index 000000000000..48b84df2afda
--- /dev/null
+++ b/drivers/watchdog/max77620_wdt.c
@@ -0,0 +1,227 @@
1/*
2 * Maxim MAX77620 Watchdog Driver
3 *
4 * Copyright (C) 2016 NVIDIA CORPORATION. All rights reserved.
5 *
6 * Author: Laxman Dewangan <ldewangan@nvidia.com>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
11 */
12
13#include <linux/err.h>
14#include <linux/init.h>
15#include <linux/kernel.h>
16#include <linux/module.h>
17#include <linux/mfd/max77620.h>
18#include <linux/platform_device.h>
19#include <linux/regmap.h>
20#include <linux/slab.h>
21#include <linux/watchdog.h>
22
23static bool nowayout = WATCHDOG_NOWAYOUT;
24
25struct max77620_wdt {
26 struct device *dev;
27 struct regmap *rmap;
28 struct watchdog_device wdt_dev;
29};
30
31static int max77620_wdt_start(struct watchdog_device *wdt_dev)
32{
33 struct max77620_wdt *wdt = watchdog_get_drvdata(wdt_dev);
34
35 return regmap_update_bits(wdt->rmap, MAX77620_REG_CNFGGLBL2,
36 MAX77620_WDTEN, MAX77620_WDTEN);
37}
38
39static int max77620_wdt_stop(struct watchdog_device *wdt_dev)
40{
41 struct max77620_wdt *wdt = watchdog_get_drvdata(wdt_dev);
42
43 return regmap_update_bits(wdt->rmap, MAX77620_REG_CNFGGLBL2,
44 MAX77620_WDTEN, 0);
45}
46
47static int max77620_wdt_ping(struct watchdog_device *wdt_dev)
48{
49 struct max77620_wdt *wdt = watchdog_get_drvdata(wdt_dev);
50
51 return regmap_update_bits(wdt->rmap, MAX77620_REG_CNFGGLBL3,
52 MAX77620_WDTC_MASK, 0x1);
53}
54
55static int max77620_wdt_set_timeout(struct watchdog_device *wdt_dev,
56 unsigned int timeout)
57{
58 struct max77620_wdt *wdt = watchdog_get_drvdata(wdt_dev);
59 unsigned int wdt_timeout;
60 u8 regval;
61 int ret;
62
63 switch (timeout) {
64 case 0 ... 2:
65 regval = MAX77620_TWD_2s;
66 wdt_timeout = 2;
67 break;
68
69 case 3 ... 16:
70 regval = MAX77620_TWD_16s;
71 wdt_timeout = 16;
72 break;
73
74 case 17 ... 64:
75 regval = MAX77620_TWD_64s;
76 wdt_timeout = 64;
77 break;
78
79 default:
80 regval = MAX77620_TWD_128s;
81 wdt_timeout = 128;
82 break;
83 }
84
85 ret = regmap_update_bits(wdt->rmap, MAX77620_REG_CNFGGLBL3,
86 MAX77620_WDTC_MASK, 0x1);
87 if (ret < 0)
88 return ret;
89
90 ret = regmap_update_bits(wdt->rmap, MAX77620_REG_CNFGGLBL2,
91 MAX77620_TWD_MASK, regval);
92 if (ret < 0)
93 return ret;
94
95 wdt_dev->timeout = wdt_timeout;
96
97 return 0;
98}
99
100static const struct watchdog_info max77620_wdt_info = {
101 .identity = "max77620-watchdog",
102 .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
103};
104
105static const struct watchdog_ops max77620_wdt_ops = {
106 .start = max77620_wdt_start,
107 .stop = max77620_wdt_stop,
108 .ping = max77620_wdt_ping,
109 .set_timeout = max77620_wdt_set_timeout,
110};
111
112static int max77620_wdt_probe(struct platform_device *pdev)
113{
114 struct max77620_wdt *wdt;
115 struct watchdog_device *wdt_dev;
116 unsigned int regval;
117 int ret;
118
119 wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL);
120 if (!wdt)
121 return -ENOMEM;
122
123 wdt->dev = &pdev->dev;
124 wdt->rmap = dev_get_regmap(pdev->dev.parent, NULL);
125 if (!wdt->rmap) {
126 dev_err(wdt->dev, "Failed to get parent regmap\n");
127 return -ENODEV;
128 }
129
130 wdt_dev = &wdt->wdt_dev;
131 wdt_dev->info = &max77620_wdt_info;
132 wdt_dev->ops = &max77620_wdt_ops;
133 wdt_dev->min_timeout = 2;
134 wdt_dev->max_timeout = 128;
135 wdt_dev->max_hw_heartbeat_ms = 128 * 1000;
136
137 platform_set_drvdata(pdev, wdt);
138
139 /* Enable WD_RST_WK - WDT expire results in a restart */
140 ret = regmap_update_bits(wdt->rmap, MAX77620_REG_ONOFFCNFG2,
141 MAX77620_ONOFFCNFG2_WD_RST_WK,
142 MAX77620_ONOFFCNFG2_WD_RST_WK);
143 if (ret < 0) {
144 dev_err(wdt->dev, "Failed to set WD_RST_WK: %d\n", ret);
145 return ret;
146 }
147
148 /* Set WDT clear in OFF and sleep mode */
149 ret = regmap_update_bits(wdt->rmap, MAX77620_REG_CNFGGLBL2,
150 MAX77620_WDTOFFC | MAX77620_WDTSLPC,
151 MAX77620_WDTOFFC | MAX77620_WDTSLPC);
152 if (ret < 0) {
153 dev_err(wdt->dev, "Failed to set WDT OFF mode: %d\n", ret);
154 return ret;
155 }
156
157 /* Check if WDT running and if yes then set flags properly */
158 ret = regmap_read(wdt->rmap, MAX77620_REG_CNFGGLBL2, &regval);
159 if (ret < 0) {
160 dev_err(wdt->dev, "Failed to read WDT CFG register: %d\n", ret);
161 return ret;
162 }
163
164 switch (regval & MAX77620_TWD_MASK) {
165 case MAX77620_TWD_2s:
166 wdt_dev->timeout = 2;
167 break;
168 case MAX77620_TWD_16s:
169 wdt_dev->timeout = 16;
170 break;
171 case MAX77620_TWD_64s:
172 wdt_dev->timeout = 64;
173 break;
174 default:
175 wdt_dev->timeout = 128;
176 break;
177 }
178
179 if (regval & MAX77620_WDTEN)
180 set_bit(WDOG_HW_RUNNING, &wdt_dev->status);
181
182 watchdog_set_nowayout(wdt_dev, nowayout);
183 watchdog_set_drvdata(wdt_dev, wdt);
184
185 ret = watchdog_register_device(wdt_dev);
186 if (ret < 0) {
187 dev_err(&pdev->dev, "watchdog registration failed: %d\n", ret);
188 return ret;
189 }
190
191 return 0;
192}
193
194static int max77620_wdt_remove(struct platform_device *pdev)
195{
196 struct max77620_wdt *wdt = platform_get_drvdata(pdev);
197
198 max77620_wdt_stop(&wdt->wdt_dev);
199 watchdog_unregister_device(&wdt->wdt_dev);
200
201 return 0;
202}
203
204static struct platform_device_id max77620_wdt_devtype[] = {
205 { .name = "max77620-watchdog", },
206 { },
207};
208
209static struct platform_driver max77620_wdt_driver = {
210 .driver = {
211 .name = "max77620-watchdog",
212 },
213 .probe = max77620_wdt_probe,
214 .remove = max77620_wdt_remove,
215 .id_table = max77620_wdt_devtype,
216};
217
218module_platform_driver(max77620_wdt_driver);
219
220MODULE_DESCRIPTION("Max77620 watchdog timer driver");
221
222module_param(nowayout, bool, 0);
223MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "
224 "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
225
226MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>");
227MODULE_LICENSE("GPL v2");