2 * Copyright (C) 2008-2009 Avionic Design GmbH
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
10 #include <linux/gfp.h>
12 #include <linux/miscdevice.h>
13 #include <linux/module.h>
14 #include <linux/platform_device.h>
15 #include <linux/types.h>
16 #include <linux/uaccess.h>
17 #include <linux/watchdog.h>
19 #define WATCHDOG_NAME "adx-wdt"
21 /* register offsets */
22 #define ADX_WDT_CONTROL 0x00
23 #define ADX_WDT_CONTROL_ENABLE (1 << 0)
24 #define ADX_WDT_CONTROL_nRESET (1 << 1)
25 #define ADX_WDT_TIMEOUT 0x08
27 static struct platform_device
*adx_wdt_dev
;
28 static unsigned long driver_open
;
30 #define WDT_STATE_STOP 0
31 #define WDT_STATE_START 1
35 unsigned long timeout
;
41 static const struct watchdog_info adx_wdt_info
= {
42 .identity
= "Avionic Design Xanthos Watchdog",
43 .options
= WDIOF_SETTIMEOUT
| WDIOF_KEEPALIVEPING
,
46 static void adx_wdt_start_locked(struct adx_wdt
*wdt
)
50 ctrl
= readl(wdt
->base
+ ADX_WDT_CONTROL
);
51 ctrl
|= ADX_WDT_CONTROL_ENABLE
;
52 writel(ctrl
, wdt
->base
+ ADX_WDT_CONTROL
);
53 wdt
->state
= WDT_STATE_START
;
56 static void adx_wdt_start(struct adx_wdt
*wdt
)
60 spin_lock_irqsave(&wdt
->lock
, flags
);
61 adx_wdt_start_locked(wdt
);
62 spin_unlock_irqrestore(&wdt
->lock
, flags
);
65 static void adx_wdt_stop_locked(struct adx_wdt
*wdt
)
69 ctrl
= readl(wdt
->base
+ ADX_WDT_CONTROL
);
70 ctrl
&= ~ADX_WDT_CONTROL_ENABLE
;
71 writel(ctrl
, wdt
->base
+ ADX_WDT_CONTROL
);
72 wdt
->state
= WDT_STATE_STOP
;
75 static void adx_wdt_stop(struct adx_wdt
*wdt
)
79 spin_lock_irqsave(&wdt
->lock
, flags
);
80 adx_wdt_stop_locked(wdt
);
81 spin_unlock_irqrestore(&wdt
->lock
, flags
);
84 static void adx_wdt_set_timeout(struct adx_wdt
*wdt
, unsigned long seconds
)
86 unsigned long timeout
= seconds
* 1000;
90 spin_lock_irqsave(&wdt
->lock
, flags
);
92 adx_wdt_stop_locked(wdt
);
93 writel(timeout
, wdt
->base
+ ADX_WDT_TIMEOUT
);
95 if (state
== WDT_STATE_START
)
96 adx_wdt_start_locked(wdt
);
98 wdt
->timeout
= timeout
;
99 spin_unlock_irqrestore(&wdt
->lock
, flags
);
102 static void adx_wdt_get_timeout(struct adx_wdt
*wdt
, unsigned long *seconds
)
104 *seconds
= wdt
->timeout
/ 1000;
107 static void adx_wdt_keepalive(struct adx_wdt
*wdt
)
111 spin_lock_irqsave(&wdt
->lock
, flags
);
112 writel(wdt
->timeout
, wdt
->base
+ ADX_WDT_TIMEOUT
);
113 spin_unlock_irqrestore(&wdt
->lock
, flags
);
116 static int adx_wdt_open(struct inode
*inode
, struct file
*file
)
118 struct adx_wdt
*wdt
= platform_get_drvdata(adx_wdt_dev
);
120 if (test_and_set_bit(0, &driver_open
))
123 file
->private_data
= wdt
;
124 adx_wdt_set_timeout(wdt
, 30);
127 return nonseekable_open(inode
, file
);
130 static int adx_wdt_release(struct inode
*inode
, struct file
*file
)
132 struct adx_wdt
*wdt
= file
->private_data
;
135 clear_bit(0, &driver_open
);
140 static long adx_wdt_ioctl(struct file
*file
, unsigned int cmd
, unsigned long arg
)
142 struct adx_wdt
*wdt
= file
->private_data
;
143 void __user
*argp
= (void __user
*)arg
;
144 unsigned long __user
*p
= argp
;
145 unsigned long seconds
= 0;
146 unsigned int options
;
150 case WDIOC_GETSUPPORT
:
151 if (copy_to_user(argp
, &adx_wdt_info
, sizeof(adx_wdt_info
)))
156 case WDIOC_GETSTATUS
:
157 case WDIOC_GETBOOTSTATUS
:
158 return put_user(0, p
);
160 case WDIOC_KEEPALIVE
:
161 adx_wdt_keepalive(wdt
);
164 case WDIOC_SETTIMEOUT
:
165 if (get_user(seconds
, p
))
168 adx_wdt_set_timeout(wdt
, seconds
);
171 case WDIOC_GETTIMEOUT
:
172 adx_wdt_get_timeout(wdt
, &seconds
);
173 return put_user(seconds
, p
);
175 case WDIOC_SETOPTIONS
:
176 if (copy_from_user(&options
, argp
, sizeof(options
)))
179 if (options
& WDIOS_DISABLECARD
) {
184 if (options
& WDIOS_ENABLECARD
) {
198 static ssize_t
adx_wdt_write(struct file
*file
, const char __user
*data
,
199 size_t len
, loff_t
*ppos
)
201 struct adx_wdt
*wdt
= file
->private_data
;
204 adx_wdt_keepalive(wdt
);
209 static const struct file_operations adx_wdt_fops
= {
210 .owner
= THIS_MODULE
,
212 .open
= adx_wdt_open
,
213 .release
= adx_wdt_release
,
214 .unlocked_ioctl
= adx_wdt_ioctl
,
215 .write
= adx_wdt_write
,
218 static struct miscdevice adx_wdt_miscdev
= {
219 .minor
= WATCHDOG_MINOR
,
221 .fops
= &adx_wdt_fops
,
224 static int __devinit
adx_wdt_probe(struct platform_device
*pdev
)
226 struct resource
*res
;
231 wdt
= devm_kzalloc(&pdev
->dev
, sizeof(*wdt
), GFP_KERNEL
);
233 dev_err(&pdev
->dev
, "cannot allocate WDT structure\n");
237 spin_lock_init(&wdt
->lock
);
239 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
241 dev_err(&pdev
->dev
, "cannot obtain I/O memory region\n");
245 res
= devm_request_mem_region(&pdev
->dev
, res
->start
,
246 resource_size(res
), res
->name
);
248 dev_err(&pdev
->dev
, "cannot request I/O memory region\n");
252 wdt
->base
= devm_ioremap_nocache(&pdev
->dev
, res
->start
,
255 dev_err(&pdev
->dev
, "cannot remap I/O memory region\n");
259 /* disable watchdog and reboot on timeout */
260 ctrl
= readl(wdt
->base
+ ADX_WDT_CONTROL
);
261 ctrl
&= ~ADX_WDT_CONTROL_ENABLE
;
262 ctrl
&= ~ADX_WDT_CONTROL_nRESET
;
263 writel(ctrl
, wdt
->base
+ ADX_WDT_CONTROL
);
265 platform_set_drvdata(pdev
, wdt
);
268 ret
= misc_register(&adx_wdt_miscdev
);
270 dev_err(&pdev
->dev
, "cannot register miscdev on minor %d "
271 "(err=%d)\n", WATCHDOG_MINOR
, ret
);
278 static int __devexit
adx_wdt_remove(struct platform_device
*pdev
)
280 struct adx_wdt
*wdt
= platform_get_drvdata(pdev
);
282 misc_deregister(&adx_wdt_miscdev
);
284 platform_set_drvdata(pdev
, NULL
);
289 static void adx_wdt_shutdown(struct platform_device
*pdev
)
291 struct adx_wdt
*wdt
= platform_get_drvdata(pdev
);
296 static int adx_wdt_suspend(struct device
*dev
)
298 struct platform_device
*pdev
= to_platform_device(dev
);
299 struct adx_wdt
*wdt
= platform_get_drvdata(pdev
);
301 wdt
->wake
= (wdt
->state
== WDT_STATE_START
) ? 1 : 0;
307 static int adx_wdt_resume(struct device
*dev
)
309 struct platform_device
*pdev
= to_platform_device(dev
);
310 struct adx_wdt
*wdt
= platform_get_drvdata(pdev
);
318 static const struct dev_pm_ops adx_wdt_pm_ops
= {
319 .suspend
= adx_wdt_suspend
,
320 .resume
= adx_wdt_resume
,
323 # define ADX_WDT_PM_OPS (&adx_wdt_pm_ops)
325 # define ADX_WDT_PM_OPS NULL
328 static struct platform_driver adx_wdt_driver
= {
329 .probe
= adx_wdt_probe
,
330 .remove
= __devexit_p(adx_wdt_remove
),
331 .shutdown
= adx_wdt_shutdown
,
333 .name
= WATCHDOG_NAME
,
334 .owner
= THIS_MODULE
,
335 .pm
= ADX_WDT_PM_OPS
,
339 static int __init
adx_wdt_init(void)
341 return platform_driver_register(&adx_wdt_driver
);
344 static void __exit
adx_wdt_exit(void)
346 platform_driver_unregister(&adx_wdt_driver
);
349 module_init(adx_wdt_init
);
350 module_exit(adx_wdt_exit
);
352 MODULE_DESCRIPTION("Avionic Design Xanthos Watchdog Driver");
353 MODULE_LICENSE("GPL v2");
354 MODULE_AUTHOR("Thierry Reding <thierry.reding@avionic-design.de>");
355 MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR
);