2 * Watchdog driver for Cortina Systems Gemini SoC
4 * Copyright (C) 2009 Paulius Zaleckas <paulius.zaleckas@teltonika.lt>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
11 #include <linux/kernel.h>
12 #include <linux/init.h>
13 #include <linux/module.h>
16 #include <linux/uaccess.h>
17 #include <linux/miscdevice.h>
18 #include <linux/platform_device.h>
19 #include <linux/watchdog.h>
20 #include <linux/slab.h>
22 #define GEMINI_WDCOUNTER 0x0
23 #define GEMINI_WDLOAD 0x4
24 #define GEMINI_WDRESTART 0x8
26 #define WDRESTART_MAGIC 0x5AB9
28 #define GEMINI_WDCR 0xC
30 #define WDCR_CLOCK_5MHZ (1 << 4)
31 #define WDCR_SYS_RST (1 << 1)
32 #define WDCR_ENABLE (1 << 0)
34 #define WDT_CLOCK 5000000 /* 5 MHz */
35 #define WDT_DEFAULT_TIMEOUT 13
36 #define WDT_MAX_TIMEOUT (0xFFFFFFFF / WDT_CLOCK)
40 #define WDT_OK_TO_CLOSE 1
42 static unsigned int timeout
= WDT_DEFAULT_TIMEOUT
;
43 static int nowayout
= WATCHDOG_NOWAYOUT
;
45 static DEFINE_SPINLOCK(gemini_wdt_lock
);
47 static struct platform_device
*gemini_wdt_dev
;
49 struct gemini_wdt_struct
{
56 static struct watchdog_info gemini_wdt_info
= {
57 .identity
= "Gemini watchdog",
58 .options
= WDIOF_MAGICCLOSE
| WDIOF_KEEPALIVEPING
|
62 /* Disable the watchdog. */
63 static void gemini_wdt_stop(struct gemini_wdt_struct
*gemini_wdt
)
65 spin_lock(&gemini_wdt_lock
);
67 __raw_writel(0, gemini_wdt
->base
+ GEMINI_WDCR
);
69 clear_bit(WDT_ACTIVE
, &gemini_wdt
->status
);
71 spin_unlock(&gemini_wdt_lock
);
74 /* Service the watchdog */
75 static void gemini_wdt_service(struct gemini_wdt_struct
*gemini_wdt
)
77 __raw_writel(WDRESTART_MAGIC
, gemini_wdt
->base
+ GEMINI_WDRESTART
);
80 /* Enable and reset the watchdog. */
81 static void gemini_wdt_start(struct gemini_wdt_struct
*gemini_wdt
)
83 spin_lock(&gemini_wdt_lock
);
85 __raw_writel(timeout
* WDT_CLOCK
, gemini_wdt
->base
+ GEMINI_WDLOAD
);
87 gemini_wdt_service(gemini_wdt
);
89 /* set clock before enabling */
90 __raw_writel(WDCR_CLOCK_5MHZ
| WDCR_SYS_RST
,
91 gemini_wdt
->base
+ GEMINI_WDCR
);
93 __raw_writel(WDCR_CLOCK_5MHZ
| WDCR_SYS_RST
| WDCR_ENABLE
,
94 gemini_wdt
->base
+ GEMINI_WDCR
);
96 set_bit(WDT_ACTIVE
, &gemini_wdt
->status
);
98 spin_unlock(&gemini_wdt_lock
);
101 /* Watchdog device is opened, and watchdog starts running. */
102 static int gemini_wdt_open(struct inode
*inode
, struct file
*file
)
104 struct gemini_wdt_struct
*gemini_wdt
= platform_get_drvdata(gemini_wdt_dev
);
106 if (test_bit(WDT_ACTIVE
, &gemini_wdt
->status
))
109 file
->private_data
= gemini_wdt
;
111 gemini_wdt_start(gemini_wdt
);
113 return nonseekable_open(inode
, file
);
116 /* Close the watchdog device. */
117 static int gemini_wdt_close(struct inode
*inode
, struct file
*file
)
119 struct gemini_wdt_struct
*gemini_wdt
= file
->private_data
;
121 /* Disable the watchdog if possible */
122 if (test_bit(WDT_OK_TO_CLOSE
, &gemini_wdt
->status
))
123 gemini_wdt_stop(gemini_wdt
);
125 dev_warn(gemini_wdt
->dev
, "Device closed unexpectedly - timer will not stop\n");
130 /* Handle commands from user-space. */
131 static long gemini_wdt_ioctl(struct file
*file
, unsigned int cmd
,
134 struct gemini_wdt_struct
*gemini_wdt
= file
->private_data
;
139 case WDIOC_KEEPALIVE
:
140 gemini_wdt_service(gemini_wdt
);
143 case WDIOC_GETSUPPORT
:
144 return copy_to_user((struct watchdog_info
*)arg
, &gemini_wdt_info
,
145 sizeof(gemini_wdt_info
)) ? -EFAULT
: 0;
147 case WDIOC_SETTIMEOUT
:
148 if (get_user(value
, (int *)arg
))
151 if ((value
< 1) || (value
> WDT_MAX_TIMEOUT
))
156 /* restart wdt to use new timeout */
157 gemini_wdt_stop(gemini_wdt
);
158 gemini_wdt_start(gemini_wdt
);
161 case WDIOC_GETTIMEOUT
:
162 return put_user(timeout
, (int *)arg
);
164 case WDIOC_GETTIMELEFT
:
165 value
= __raw_readl(gemini_wdt
->base
+ GEMINI_WDCOUNTER
);
166 return put_user(value
/ WDT_CLOCK
, (int *)arg
);
173 /* Refresh the watchdog whenever device is written to. */
174 static ssize_t
gemini_wdt_write(struct file
*file
, const char *data
,
175 size_t len
, loff_t
*ppos
)
177 struct gemini_wdt_struct
*gemini_wdt
= file
->private_data
;
183 clear_bit(WDT_OK_TO_CLOSE
, &gemini_wdt
->status
);
184 for (i
= 0; i
!= len
; i
++) {
187 if (get_user(c
, data
+ i
))
190 set_bit(WDT_OK_TO_CLOSE
,
191 &gemini_wdt
->status
);
194 gemini_wdt_service(gemini_wdt
);
200 static const struct file_operations gemini_wdt_fops
= {
201 .owner
= THIS_MODULE
,
203 .unlocked_ioctl
= gemini_wdt_ioctl
,
204 .open
= gemini_wdt_open
,
205 .release
= gemini_wdt_close
,
206 .write
= gemini_wdt_write
,
209 static struct miscdevice gemini_wdt_miscdev
= {
210 .minor
= WATCHDOG_MINOR
,
212 .fops
= &gemini_wdt_fops
,
215 static void gemini_wdt_shutdown(struct platform_device
*pdev
)
217 struct gemini_wdt_struct
*gemini_wdt
= platform_get_drvdata(pdev
);
219 gemini_wdt_stop(gemini_wdt
);
222 static int gemini_wdt_probe(struct platform_device
*pdev
)
226 struct resource
*res
;
228 struct gemini_wdt_struct
*gemini_wdt
;
231 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
233 dev_err(&pdev
->dev
, "can't get device resources\n");
237 res_size
= resource_size(res
);
238 if (!request_mem_region(res
->start
, res_size
, res
->name
)) {
239 dev_err(&pdev
->dev
, "can't allocate %d bytes at %d address\n",
240 res_size
, res
->start
);
244 base
= ioremap(res
->start
, res_size
);
246 dev_err(&pdev
->dev
, "ioremap failed\n");
251 gemini_wdt
= kzalloc(sizeof(struct gemini_wdt_struct
), GFP_KERNEL
);
253 dev_err(&pdev
->dev
, "can't allocate interface\n");
258 /* Setup gemini_wdt driver structure */
259 gemini_wdt
->base
= base
;
260 gemini_wdt
->res
= res
;
262 /* Set up platform driver data */
263 platform_set_drvdata(pdev
, gemini_wdt
);
264 gemini_wdt_dev
= pdev
;
266 if (gemini_wdt_miscdev
.parent
) {
271 gemini_wdt_miscdev
.parent
= &pdev
->dev
;
273 reg
= __raw_readw(gemini_wdt
->base
+ GEMINI_WDCR
);
274 if (reg
& WDCR_ENABLE
) {
275 /* Watchdog was enabled by the bootloader, disable it. */
276 reg
&= ~(WDCR_ENABLE
);
277 __raw_writel(reg
, gemini_wdt
->base
+ GEMINI_WDCR
);
280 ret
= misc_register(&gemini_wdt_miscdev
);
287 platform_set_drvdata(pdev
, NULL
);
292 release_mem_region(res
->start
, res_size
);
297 static int gemini_wdt_remove(struct platform_device
*pdev
)
299 struct gemini_wdt_struct
*gemini_wdt
= platform_get_drvdata(pdev
);
301 platform_set_drvdata(pdev
, NULL
);
302 misc_deregister(&gemini_wdt_miscdev
);
303 gemini_wdt_dev
= NULL
;
304 iounmap(gemini_wdt
->base
);
305 release_mem_region(gemini_wdt
->res
->start
, resource_size(gemini_wdt
->res
));
313 static int gemini_wdt_suspend(struct platform_device
*pdev
, pm_message_t message
)
315 struct gemini_wdt_struct
*gemini_wdt
= platform_get_drvdata(pdev
);
318 reg
= __raw_readw(gemini_wdt
->base
+ GEMINI_WDCR
);
319 reg
&= ~(WDCR_WDENABLE
);
320 __raw_writel(reg
, gemini_wdt
->base
+ GEMINI_WDCR
);
325 static int gemini_wdt_resume(struct platform_device
*pdev
)
327 struct gemini_wdt_struct
*gemini_wdt
= platform_get_drvdata(pdev
);
330 if (gemini_wdt
->status
) {
331 reg
= __raw_readw(gemini_wdt
->base
+ GEMINI_WDCR
);
332 reg
|= WDCR_WDENABLE
;
333 __raw_writel(reg
, gemini_wdt
->base
+ GEMINI_WDCR
);
339 #define gemini_wdt_suspend NULL
340 #define gemini_wdt_resume NULL
343 static struct platform_driver gemini_wdt_driver
= {
344 .probe
= gemini_wdt_probe
,
345 .remove
= gemini_wdt_remove
,
346 .shutdown
= gemini_wdt_shutdown
,
347 .suspend
= gemini_wdt_suspend
,
348 .resume
= gemini_wdt_resume
,
350 .name
= "gemini-wdt",
351 .owner
= THIS_MODULE
,
355 static int __init
gemini_wdt_init(void)
357 return platform_driver_probe(&gemini_wdt_driver
, gemini_wdt_probe
);
360 static void __exit
gemini_wdt_exit(void)
362 platform_driver_unregister(&gemini_wdt_driver
);
365 module_init(gemini_wdt_init
);
366 module_exit(gemini_wdt_exit
);
368 module_param(timeout
, uint
, 0);
369 MODULE_PARM_DESC(timeout
, "Watchdog timeout in seconds");
371 module_param(nowayout
, int, 0);
372 MODULE_PARM_DESC(nowayout
, "Watchdog cannot be stopped once started");
374 MODULE_AUTHOR("Paulius Zaleckas");
375 MODULE_DESCRIPTION("Watchdog driver for Gemini");
376 MODULE_LICENSE("GPL");
377 MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR
);
378 MODULE_ALIAS("platform:gemini-wdt");