xref: /openbmc/linux/drivers/watchdog/da9063_wdt.c (revision ca55b2fe)
1 /*
2  * Watchdog driver for DA9063 PMICs.
3  *
4  * Copyright(c) 2012 Dialog Semiconductor Ltd.
5  *
6  * Author: Mariusz Wojtasik <mariusz.wojtasik@diasemi.com>
7  *
8  * This program is free software; you can redistribute  it and/or modify it
9  * under  the terms of  the GNU General  Public License as published by the
10  * Free Software Foundation;  either version 2 of the  License, or (at your
11  * option) any later version.
12  */
13 
14 #include <linux/kernel.h>
15 #include <linux/module.h>
16 #include <linux/watchdog.h>
17 #include <linux/platform_device.h>
18 #include <linux/uaccess.h>
19 #include <linux/slab.h>
20 #include <linux/delay.h>
21 #include <linux/mfd/da9063/registers.h>
22 #include <linux/mfd/da9063/core.h>
23 #include <linux/reboot.h>
24 #include <linux/regmap.h>
25 
26 /*
27  * Watchdog selector to timeout in seconds.
28  *   0: WDT disabled;
29  *   others: timeout = 2048 ms * 2^(TWDSCALE-1).
30  */
31 static const unsigned int wdt_timeout[] = { 0, 2, 4, 8, 16, 32, 65, 131 };
32 #define DA9063_TWDSCALE_DISABLE		0
33 #define DA9063_TWDSCALE_MIN		1
34 #define DA9063_TWDSCALE_MAX		(ARRAY_SIZE(wdt_timeout) - 1)
35 #define DA9063_WDT_MIN_TIMEOUT		wdt_timeout[DA9063_TWDSCALE_MIN]
36 #define DA9063_WDT_MAX_TIMEOUT		wdt_timeout[DA9063_TWDSCALE_MAX]
37 #define DA9063_WDG_TIMEOUT		wdt_timeout[3]
38 
39 struct da9063_watchdog {
40 	struct da9063 *da9063;
41 	struct watchdog_device wdtdev;
42 	struct notifier_block restart_handler;
43 };
44 
45 static unsigned int da9063_wdt_timeout_to_sel(unsigned int secs)
46 {
47 	unsigned int i;
48 
49 	for (i = DA9063_TWDSCALE_MIN; i <= DA9063_TWDSCALE_MAX; i++) {
50 		if (wdt_timeout[i] >= secs)
51 			return i;
52 	}
53 
54 	return DA9063_TWDSCALE_MAX;
55 }
56 
57 static int _da9063_wdt_set_timeout(struct da9063 *da9063, unsigned int regval)
58 {
59 	return regmap_update_bits(da9063->regmap, DA9063_REG_CONTROL_D,
60 				  DA9063_TWDSCALE_MASK, regval);
61 }
62 
63 static int da9063_wdt_start(struct watchdog_device *wdd)
64 {
65 	struct da9063_watchdog *wdt = watchdog_get_drvdata(wdd);
66 	unsigned int selector;
67 	int ret;
68 
69 	selector = da9063_wdt_timeout_to_sel(wdt->wdtdev.timeout);
70 	ret = _da9063_wdt_set_timeout(wdt->da9063, selector);
71 	if (ret)
72 		dev_err(wdt->da9063->dev, "Watchdog failed to start (err = %d)\n",
73 			ret);
74 
75 	return ret;
76 }
77 
78 static int da9063_wdt_stop(struct watchdog_device *wdd)
79 {
80 	struct da9063_watchdog *wdt = watchdog_get_drvdata(wdd);
81 	int ret;
82 
83 	ret = regmap_update_bits(wdt->da9063->regmap, DA9063_REG_CONTROL_D,
84 				 DA9063_TWDSCALE_MASK, DA9063_TWDSCALE_DISABLE);
85 	if (ret)
86 		dev_alert(wdt->da9063->dev, "Watchdog failed to stop (err = %d)\n",
87 			  ret);
88 
89 	return ret;
90 }
91 
92 static int da9063_wdt_ping(struct watchdog_device *wdd)
93 {
94 	struct da9063_watchdog *wdt = watchdog_get_drvdata(wdd);
95 	int ret;
96 
97 	ret = regmap_write(wdt->da9063->regmap, DA9063_REG_CONTROL_F,
98 			   DA9063_WATCHDOG);
99 	if (ret)
100 		dev_alert(wdt->da9063->dev, "Failed to ping the watchdog (err = %d)\n",
101 			  ret);
102 
103 	return ret;
104 }
105 
106 static int da9063_wdt_set_timeout(struct watchdog_device *wdd,
107 				  unsigned int timeout)
108 {
109 	struct da9063_watchdog *wdt = watchdog_get_drvdata(wdd);
110 	unsigned int selector;
111 	int ret;
112 
113 	selector = da9063_wdt_timeout_to_sel(timeout);
114 	ret = _da9063_wdt_set_timeout(wdt->da9063, selector);
115 	if (ret)
116 		dev_err(wdt->da9063->dev, "Failed to set watchdog timeout (err = %d)\n",
117 			ret);
118 	else
119 		wdd->timeout = wdt_timeout[selector];
120 
121 	return ret;
122 }
123 
124 static int da9063_wdt_restart_handler(struct notifier_block *this,
125 				      unsigned long mode, void *cmd)
126 {
127 	struct da9063_watchdog *wdt = container_of(this,
128 						   struct da9063_watchdog,
129 						   restart_handler);
130 	int ret;
131 
132 	ret = regmap_write(wdt->da9063->regmap, DA9063_REG_CONTROL_F,
133 			   DA9063_SHUTDOWN);
134 	if (ret)
135 		dev_alert(wdt->da9063->dev, "Failed to shutdown (err = %d)\n",
136 			  ret);
137 
138 	return NOTIFY_DONE;
139 }
140 
141 static const struct watchdog_info da9063_watchdog_info = {
142 	.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
143 	.identity = "DA9063 Watchdog",
144 };
145 
146 static const struct watchdog_ops da9063_watchdog_ops = {
147 	.owner = THIS_MODULE,
148 	.start = da9063_wdt_start,
149 	.stop = da9063_wdt_stop,
150 	.ping = da9063_wdt_ping,
151 	.set_timeout = da9063_wdt_set_timeout,
152 };
153 
154 static int da9063_wdt_probe(struct platform_device *pdev)
155 {
156 	int ret;
157 	struct da9063 *da9063;
158 	struct da9063_watchdog *wdt;
159 
160 	if (!pdev->dev.parent)
161 		return -EINVAL;
162 
163 	da9063 = dev_get_drvdata(pdev->dev.parent);
164 	if (!da9063)
165 		return -EINVAL;
166 
167 	wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL);
168 	if (!wdt)
169 		return -ENOMEM;
170 
171 	wdt->da9063 = da9063;
172 
173 	wdt->wdtdev.info = &da9063_watchdog_info;
174 	wdt->wdtdev.ops = &da9063_watchdog_ops;
175 	wdt->wdtdev.min_timeout = DA9063_WDT_MIN_TIMEOUT;
176 	wdt->wdtdev.max_timeout = DA9063_WDT_MAX_TIMEOUT;
177 	wdt->wdtdev.timeout = DA9063_WDG_TIMEOUT;
178 	wdt->wdtdev.parent = &pdev->dev;
179 
180 	wdt->wdtdev.status = WATCHDOG_NOWAYOUT_INIT_STATUS;
181 
182 	watchdog_set_drvdata(&wdt->wdtdev, wdt);
183 	dev_set_drvdata(&pdev->dev, wdt);
184 
185 	ret = watchdog_register_device(&wdt->wdtdev);
186 	if (ret)
187 		return ret;
188 
189 	wdt->restart_handler.notifier_call = da9063_wdt_restart_handler;
190 	wdt->restart_handler.priority = 128;
191 	ret = register_restart_handler(&wdt->restart_handler);
192 	if (ret)
193 		dev_err(wdt->da9063->dev,
194 			"Failed to register restart handler (err = %d)\n", ret);
195 
196 	return 0;
197 }
198 
199 static int da9063_wdt_remove(struct platform_device *pdev)
200 {
201 	struct da9063_watchdog *wdt = dev_get_drvdata(&pdev->dev);
202 
203 	unregister_restart_handler(&wdt->restart_handler);
204 
205 	watchdog_unregister_device(&wdt->wdtdev);
206 
207 	return 0;
208 }
209 
210 static struct platform_driver da9063_wdt_driver = {
211 	.probe = da9063_wdt_probe,
212 	.remove = da9063_wdt_remove,
213 	.driver = {
214 		.name = DA9063_DRVNAME_WATCHDOG,
215 	},
216 };
217 module_platform_driver(da9063_wdt_driver);
218 
219 MODULE_AUTHOR("Mariusz Wojtasik <mariusz.wojtasik@diasemi.com>");
220 MODULE_DESCRIPTION("Watchdog driver for Dialog DA9063");
221 MODULE_LICENSE("GPL");
222 MODULE_ALIAS("platform:" DA9063_DRVNAME_WATCHDOG);
223