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 
23 static bool nowayout = WATCHDOG_NOWAYOUT;
24 
25 struct max77620_wdt {
26 	struct device			*dev;
27 	struct regmap			*rmap;
28 	struct watchdog_device		wdt_dev;
29 };
30 
31 static 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 
39 static 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 
47 static 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 
55 static 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 
100 static const struct watchdog_info max77620_wdt_info = {
101 	.identity = "max77620-watchdog",
102 	.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
103 };
104 
105 static 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 
112 static 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 
194 static 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 
204 static const struct platform_device_id max77620_wdt_devtype[] = {
205 	{ .name = "max77620-watchdog", },
206 	{ },
207 };
208 MODULE_DEVICE_TABLE(platform, max77620_wdt_devtype);
209 
210 static struct platform_driver max77620_wdt_driver = {
211 	.driver	= {
212 		.name	= "max77620-watchdog",
213 	},
214 	.probe	= max77620_wdt_probe,
215 	.remove	= max77620_wdt_remove,
216 	.id_table = max77620_wdt_devtype,
217 };
218 
219 module_platform_driver(max77620_wdt_driver);
220 
221 MODULE_DESCRIPTION("Max77620 watchdog timer driver");
222 
223 module_param(nowayout, bool, 0);
224 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "
225 	"(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
226 
227 MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>");
228 MODULE_LICENSE("GPL v2");
229