aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/watchdog/sp805_wdt.c
diff options
context:
space:
mode:
authorViresh Kumar <viresh.kumar@st.com>2012-03-12 00:22:16 -0400
committerWim Van Sebroeck <wim@iguana.be>2012-05-30 01:56:41 -0400
commit4a516539faba13deca2399cff8faaa84d251a4ea (patch)
tree7397aae38c373ef22aa7a7edabdd1b3e0cc66ccf /drivers/watchdog/sp805_wdt.c
parent2d8c7ff52c2459a25034ac8ddc230e67cc0e2b67 (diff)
watchdog: sp805_wdt: convert to watchdog core
This patch converts existing sp805 watchdog driver to use already in place common infrastructure present in watchdog core. With this lot of code goes away. Signed-off-by: Viresh Kumar <viresh.kumar@st.com> Signed-off-by: Wim Van Sebroeck <wim@iguana.be>
Diffstat (limited to 'drivers/watchdog/sp805_wdt.c')
-rw-r--r--drivers/watchdog/sp805_wdt.c241
1 files changed, 82 insertions, 159 deletions
diff --git a/drivers/watchdog/sp805_wdt.c b/drivers/watchdog/sp805_wdt.c
index bbb170e50055..18d4bcfd1bfc 100644
--- a/drivers/watchdog/sp805_wdt.c
+++ b/drivers/watchdog/sp805_wdt.c
@@ -16,20 +16,17 @@
16#include <linux/amba/bus.h> 16#include <linux/amba/bus.h>
17#include <linux/bitops.h> 17#include <linux/bitops.h>
18#include <linux/clk.h> 18#include <linux/clk.h>
19#include <linux/fs.h>
20#include <linux/init.h> 19#include <linux/init.h>
21#include <linux/io.h> 20#include <linux/io.h>
22#include <linux/ioport.h> 21#include <linux/ioport.h>
23#include <linux/kernel.h> 22#include <linux/kernel.h>
24#include <linux/math64.h> 23#include <linux/math64.h>
25#include <linux/miscdevice.h>
26#include <linux/module.h> 24#include <linux/module.h>
27#include <linux/moduleparam.h> 25#include <linux/moduleparam.h>
28#include <linux/pm.h> 26#include <linux/pm.h>
29#include <linux/slab.h> 27#include <linux/slab.h>
30#include <linux/spinlock.h> 28#include <linux/spinlock.h>
31#include <linux/types.h> 29#include <linux/types.h>
32#include <linux/uaccess.h>
33#include <linux/watchdog.h> 30#include <linux/watchdog.h>
34 31
35/* default timeout in seconds */ 32/* default timeout in seconds */
@@ -56,6 +53,7 @@
56 53
57/** 54/**
58 * struct sp805_wdt: sp805 wdt device structure 55 * struct sp805_wdt: sp805 wdt device structure
56 * @wdd: instance of struct watchdog_device
59 * @lock: spin lock protecting dev structure and io access 57 * @lock: spin lock protecting dev structure and io access
60 * @base: base address of wdt 58 * @base: base address of wdt
61 * @clk: clock structure of wdt 59 * @clk: clock structure of wdt
@@ -65,24 +63,24 @@
65 * @timeout: current programmed timeout 63 * @timeout: current programmed timeout
66 */ 64 */
67struct sp805_wdt { 65struct sp805_wdt {
66 struct watchdog_device wdd;
68 spinlock_t lock; 67 spinlock_t lock;
69 void __iomem *base; 68 void __iomem *base;
70 struct clk *clk; 69 struct clk *clk;
71 struct amba_device *adev; 70 struct amba_device *adev;
72 unsigned long status;
73 #define WDT_BUSY 0
74 #define WDT_CAN_BE_CLOSED 1
75 unsigned int load_val; 71 unsigned int load_val;
76 unsigned int timeout; 72 unsigned int timeout;
77}; 73};
78 74
79/* local variables */
80static struct sp805_wdt *wdt;
81static bool nowayout = WATCHDOG_NOWAYOUT; 75static bool nowayout = WATCHDOG_NOWAYOUT;
76module_param(nowayout, bool, 0);
77MODULE_PARM_DESC(nowayout,
78 "Set to 1 to keep watchdog running after device release");
82 79
83/* This routine finds load value that will reset system in required timout */ 80/* This routine finds load value that will reset system in required timout */
84static void wdt_setload(unsigned int timeout) 81static int wdt_setload(struct watchdog_device *wdd, unsigned int timeout)
85{ 82{
83 struct sp805_wdt *wdt = watchdog_get_drvdata(wdd);
86 u64 load, rate; 84 u64 load, rate;
87 85
88 rate = clk_get_rate(wdt->clk); 86 rate = clk_get_rate(wdt->clk);
@@ -103,11 +101,14 @@ static void wdt_setload(unsigned int timeout)
103 /* roundup timeout to closest positive integer value */ 101 /* roundup timeout to closest positive integer value */
104 wdt->timeout = div_u64((load + 1) * 2 + (rate / 2), rate); 102 wdt->timeout = div_u64((load + 1) * 2 + (rate / 2), rate);
105 spin_unlock(&wdt->lock); 103 spin_unlock(&wdt->lock);
104
105 return 0;
106} 106}
107 107
108/* returns number of seconds left for reset to occur */ 108/* returns number of seconds left for reset to occur */
109static u32 wdt_timeleft(void) 109static unsigned int wdt_timeleft(struct watchdog_device *wdd)
110{ 110{
111 struct sp805_wdt *wdt = watchdog_get_drvdata(wdd);
111 u64 load, rate; 112 u64 load, rate;
112 113
113 rate = clk_get_rate(wdt->clk); 114 rate = clk_get_rate(wdt->clk);
@@ -123,166 +124,88 @@ static u32 wdt_timeleft(void)
123 return div_u64(load, rate); 124 return div_u64(load, rate);
124} 125}
125 126
126/* enables watchdog timers reset */ 127static int wdt_config(struct watchdog_device *wdd, bool ping)
127static void wdt_enable(void)
128{ 128{
129 struct sp805_wdt *wdt = watchdog_get_drvdata(wdd);
130 int ret;
131
132 if (!ping) {
133 ret = clk_enable(wdt->clk);
134 if (ret) {
135 dev_err(&wdt->adev->dev, "clock enable fail");
136 return ret;
137 }
138 }
139
129 spin_lock(&wdt->lock); 140 spin_lock(&wdt->lock);
130 141
131 writel_relaxed(UNLOCK, wdt->base + WDTLOCK); 142 writel_relaxed(UNLOCK, wdt->base + WDTLOCK);
132 writel_relaxed(wdt->load_val, wdt->base + WDTLOAD); 143 writel_relaxed(wdt->load_val, wdt->base + WDTLOAD);
133 writel_relaxed(INT_MASK, wdt->base + WDTINTCLR);
134 writel_relaxed(INT_ENABLE | RESET_ENABLE, wdt->base + WDTCONTROL);
135 writel_relaxed(LOCK, wdt->base + WDTLOCK);
136
137 /* Flush posted writes. */
138 readl_relaxed(wdt->base + WDTLOCK);
139 spin_unlock(&wdt->lock);
140}
141 144
142/* disables watchdog timers reset */ 145 if (!ping) {
143static void wdt_disable(void) 146 writel_relaxed(INT_MASK, wdt->base + WDTINTCLR);
144{ 147 writel_relaxed(INT_ENABLE | RESET_ENABLE, wdt->base +
145 spin_lock(&wdt->lock); 148 WDTCONTROL);
149 }
146 150
147 writel_relaxed(UNLOCK, wdt->base + WDTLOCK);
148 writel_relaxed(0, wdt->base + WDTCONTROL);
149 writel_relaxed(LOCK, wdt->base + WDTLOCK); 151 writel_relaxed(LOCK, wdt->base + WDTLOCK);
150 152
151 /* Flush posted writes. */ 153 /* Flush posted writes. */
152 readl_relaxed(wdt->base + WDTLOCK); 154 readl_relaxed(wdt->base + WDTLOCK);
153 spin_unlock(&wdt->lock); 155 spin_unlock(&wdt->lock);
156
157 return 0;
154} 158}
155 159
156static ssize_t sp805_wdt_write(struct file *file, const char *data, 160static int wdt_ping(struct watchdog_device *wdd)
157 size_t len, loff_t *ppos)
158{ 161{
159 if (len) { 162 return wdt_config(wdd, true);
160 if (!nowayout) {
161 size_t i;
162
163 clear_bit(WDT_CAN_BE_CLOSED, &wdt->status);
164
165 for (i = 0; i != len; i++) {
166 char c;
167
168 if (get_user(c, data + i))
169 return -EFAULT;
170 /* Check for Magic Close character */
171 if (c == 'V') {
172 set_bit(WDT_CAN_BE_CLOSED,
173 &wdt->status);
174 break;
175 }
176 }
177 }
178 wdt_enable();
179 }
180 return len;
181} 163}
182 164
183static const struct watchdog_info ident = { 165/* enables watchdog timers reset */
184 .options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, 166static int wdt_enable(struct watchdog_device *wdd)
185 .identity = MODULE_NAME,
186};
187
188static long sp805_wdt_ioctl(struct file *file, unsigned int cmd,
189 unsigned long arg)
190{ 167{
191 int ret = -ENOTTY; 168 return wdt_config(wdd, false);
192 unsigned int timeout;
193
194 switch (cmd) {
195 case WDIOC_GETSUPPORT:
196 ret = copy_to_user((struct watchdog_info *)arg, &ident,
197 sizeof(ident)) ? -EFAULT : 0;
198 break;
199
200 case WDIOC_GETSTATUS:
201 ret = put_user(0, (int *)arg);
202 break;
203
204 case WDIOC_KEEPALIVE:
205 wdt_enable();
206 ret = 0;
207 break;
208
209 case WDIOC_SETTIMEOUT:
210 ret = get_user(timeout, (unsigned int *)arg);
211 if (ret)
212 break;
213
214 wdt_setload(timeout);
215
216 wdt_enable();
217 /* Fall through */
218
219 case WDIOC_GETTIMEOUT:
220 ret = put_user(wdt->timeout, (unsigned int *)arg);
221 break;
222 case WDIOC_GETTIMELEFT:
223 ret = put_user(wdt_timeleft(), (unsigned int *)arg);
224 break;
225 }
226 return ret;
227} 169}
228 170
229static int sp805_wdt_open(struct inode *inode, struct file *file) 171/* disables watchdog timers reset */
172static int wdt_disable(struct watchdog_device *wdd)
230{ 173{
231 int ret = 0; 174 struct sp805_wdt *wdt = watchdog_get_drvdata(wdd);
232 175
233 if (test_and_set_bit(WDT_BUSY, &wdt->status)) 176 spin_lock(&wdt->lock);
234 return -EBUSY;
235
236 ret = clk_enable(wdt->clk);
237 if (ret) {
238 dev_err(&wdt->adev->dev, "clock enable fail");
239 goto err;
240 }
241
242 wdt_enable();
243
244 /* can not be closed, once enabled */
245 clear_bit(WDT_CAN_BE_CLOSED, &wdt->status);
246 return nonseekable_open(inode, file);
247 177
248err: 178 writel_relaxed(UNLOCK, wdt->base + WDTLOCK);
249 clear_bit(WDT_BUSY, &wdt->status); 179 writel_relaxed(0, wdt->base + WDTCONTROL);
250 return ret; 180 writel_relaxed(LOCK, wdt->base + WDTLOCK);
251}
252 181
253static int sp805_wdt_release(struct inode *inode, struct file *file) 182 /* Flush posted writes. */
254{ 183 readl_relaxed(wdt->base + WDTLOCK);
255 if (!test_bit(WDT_CAN_BE_CLOSED, &wdt->status)) { 184 spin_unlock(&wdt->lock);
256 clear_bit(WDT_BUSY, &wdt->status);
257 dev_warn(&wdt->adev->dev, "Device closed unexpectedly\n");
258 return 0;
259 }
260 185
261 wdt_disable();
262 clk_disable(wdt->clk); 186 clk_disable(wdt->clk);
263 clear_bit(WDT_BUSY, &wdt->status);
264 187
265 return 0; 188 return 0;
266} 189}
267 190
268static const struct file_operations sp805_wdt_fops = { 191static const struct watchdog_info wdt_info = {
269 .owner = THIS_MODULE, 192 .options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
270 .llseek = no_llseek, 193 .identity = MODULE_NAME,
271 .write = sp805_wdt_write,
272 .unlocked_ioctl = sp805_wdt_ioctl,
273 .open = sp805_wdt_open,
274 .release = sp805_wdt_release,
275}; 194};
276 195
277static struct miscdevice sp805_wdt_miscdev = { 196static const struct watchdog_ops wdt_ops = {
278 .minor = WATCHDOG_MINOR, 197 .owner = THIS_MODULE,
279 .name = "watchdog", 198 .start = wdt_enable,
280 .fops = &sp805_wdt_fops, 199 .stop = wdt_disable,
200 .ping = wdt_ping,
201 .set_timeout = wdt_setload,
202 .get_timeleft = wdt_timeleft,
281}; 203};
282 204
283static int __devinit 205static int __devinit
284sp805_wdt_probe(struct amba_device *adev, const struct amba_id *id) 206sp805_wdt_probe(struct amba_device *adev, const struct amba_id *id)
285{ 207{
208 struct sp805_wdt *wdt;
286 int ret = 0; 209 int ret = 0;
287 210
288 if (!devm_request_mem_region(&adev->dev, adev->res.start, 211 if (!devm_request_mem_region(&adev->dev, adev->res.start,
@@ -315,19 +238,26 @@ sp805_wdt_probe(struct amba_device *adev, const struct amba_id *id)
315 } 238 }
316 239
317 wdt->adev = adev; 240 wdt->adev = adev;
241 wdt->wdd.info = &wdt_info;
242 wdt->wdd.ops = &wdt_ops;
243
318 spin_lock_init(&wdt->lock); 244 spin_lock_init(&wdt->lock);
319 wdt_setload(DEFAULT_TIMEOUT); 245 watchdog_set_nowayout(&wdt->wdd, nowayout);
246 watchdog_set_drvdata(&wdt->wdd, wdt);
247 wdt_setload(&wdt->wdd, DEFAULT_TIMEOUT);
320 248
321 ret = misc_register(&sp805_wdt_miscdev); 249 ret = watchdog_register_device(&wdt->wdd);
322 if (ret < 0) { 250 if (ret) {
323 dev_warn(&adev->dev, "cannot register misc device\n"); 251 dev_err(&adev->dev, "watchdog_register_device() failed: %d\n",
324 goto err_misc_register; 252 ret);
253 goto err_register;
325 } 254 }
255 amba_set_drvdata(adev, wdt);
326 256
327 dev_info(&adev->dev, "registration successful\n"); 257 dev_info(&adev->dev, "registration successful\n");
328 return 0; 258 return 0;
329 259
330err_misc_register: 260err_register:
331 clk_put(wdt->clk); 261 clk_put(wdt->clk);
332err: 262err:
333 dev_err(&adev->dev, "Probe Failed!!!\n"); 263 dev_err(&adev->dev, "Probe Failed!!!\n");
@@ -336,7 +266,11 @@ err:
336 266
337static int __devexit sp805_wdt_remove(struct amba_device *adev) 267static int __devexit sp805_wdt_remove(struct amba_device *adev)
338{ 268{
339 misc_deregister(&sp805_wdt_miscdev); 269 struct sp805_wdt *wdt = amba_get_drvdata(adev);
270
271 watchdog_unregister_device(&wdt->wdd);
272 amba_set_drvdata(adev, NULL);
273 watchdog_set_drvdata(&wdt->wdd, NULL);
340 clk_put(wdt->clk); 274 clk_put(wdt->clk);
341 275
342 return 0; 276 return 0;
@@ -345,28 +279,22 @@ static int __devexit sp805_wdt_remove(struct amba_device *adev)
345#ifdef CONFIG_PM 279#ifdef CONFIG_PM
346static int sp805_wdt_suspend(struct device *dev) 280static int sp805_wdt_suspend(struct device *dev)
347{ 281{
348 if (test_bit(WDT_BUSY, &wdt->status)) { 282 struct sp805_wdt *wdt = dev_get_drvdata(dev);
349 wdt_disable(); 283
350 clk_disable(wdt->clk); 284 if (watchdog_active(&wdt->wdd))
351 } 285 return wdt_disable(&wdt->wdd);
352 286
353 return 0; 287 return 0;
354} 288}
355 289
356static int sp805_wdt_resume(struct device *dev) 290static int sp805_wdt_resume(struct device *dev)
357{ 291{
358 int ret = 0; 292 struct sp805_wdt *wdt = dev_get_drvdata(dev);
359 293
360 if (test_bit(WDT_BUSY, &wdt->status)) { 294 if (watchdog_active(&wdt->wdd))
361 ret = clk_enable(wdt->clk); 295 return wdt_enable(&wdt->wdd);
362 if (ret) {
363 dev_err(dev, "clock enable fail");
364 return ret;
365 }
366 wdt_enable();
367 }
368 296
369 return ret; 297 return 0;
370} 298}
371#endif /* CONFIG_PM */ 299#endif /* CONFIG_PM */
372 300
@@ -395,11 +323,6 @@ static struct amba_driver sp805_wdt_driver = {
395 323
396module_amba_driver(sp805_wdt_driver); 324module_amba_driver(sp805_wdt_driver);
397 325
398module_param(nowayout, bool, 0);
399MODULE_PARM_DESC(nowayout,
400 "Set to 1 to keep watchdog running after device release");
401
402MODULE_AUTHOR("Viresh Kumar <viresh.kumar@st.com>"); 326MODULE_AUTHOR("Viresh Kumar <viresh.kumar@st.com>");
403MODULE_DESCRIPTION("ARM SP805 Watchdog Driver"); 327MODULE_DESCRIPTION("ARM SP805 Watchdog Driver");
404MODULE_LICENSE("GPL"); 328MODULE_LICENSE("GPL");
405MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);