1 // SPDX-License-Identifier: GPL-2.0+
3 * Watchdog driver for the SA11x0/PXA2xx
5 * (c) Copyright 2000 Oleg Drokin <green@crimea.edu>
6 * Based on SoftDog driver by Alan Cox <alan@lxorguk.ukuu.org.uk>
8 * Neither Oleg Drokin nor iXcelerator.com admit liability nor provide
9 * warranty for any of this software. This material is provided
10 * "AS-IS" and at no charge.
12 * (c) Copyright 2000 Oleg Drokin <green@crimea.edu>
14 * 27/11/2000 Initial release
17 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
19 #include <linux/module.h>
20 #include <linux/moduleparam.h>
21 #include <linux/clk.h>
22 #include <linux/types.h>
23 #include <linux/kernel.h>
25 #include <linux/platform_device.h>
26 #include <linux/miscdevice.h>
27 #include <linux/watchdog.h>
28 #include <linux/init.h>
30 #include <linux/bitops.h>
31 #include <linux/uaccess.h>
32 #include <linux/timex.h>
34 #define REG_OSMR0 0x0000 /* OS timer Match Reg. 0 */
35 #define REG_OSMR1 0x0004 /* OS timer Match Reg. 1 */
36 #define REG_OSMR2 0x0008 /* OS timer Match Reg. 2 */
37 #define REG_OSMR3 0x000c /* OS timer Match Reg. 3 */
38 #define REG_OSCR 0x0010 /* OS timer Counter Reg. */
39 #define REG_OSSR 0x0014 /* OS timer Status Reg. */
40 #define REG_OWER 0x0018 /* OS timer Watch-dog Enable Reg. */
41 #define REG_OIER 0x001C /* OS timer Interrupt Enable Reg. */
43 #define OSSR_M3 (1 << 3) /* Match status channel 3 */
44 #define OSSR_M2 (1 << 2) /* Match status channel 2 */
45 #define OSSR_M1 (1 << 1) /* Match status channel 1 */
46 #define OSSR_M0 (1 << 0) /* Match status channel 0 */
48 #define OWER_WME (1 << 0) /* Watchdog Match Enable */
50 #define OIER_E3 (1 << 3) /* Interrupt enable channel 3 */
51 #define OIER_E2 (1 << 2) /* Interrupt enable channel 2 */
52 #define OIER_E1 (1 << 1) /* Interrupt enable channel 1 */
53 #define OIER_E0 (1 << 0) /* Interrupt enable channel 0 */
55 static unsigned long oscr_freq
;
56 static unsigned long sa1100wdt_users
;
57 static unsigned int pre_margin
;
58 static int boot_status
;
59 static void __iomem
*reg_base
;
61 static inline void sa1100_wr(u32 val
, u32 offset
)
63 writel_relaxed(val
, reg_base
+ offset
);
66 static inline u32
sa1100_rd(u32 offset
)
68 return readl_relaxed(reg_base
+ offset
);
72 * Allow only one person to hold it open
74 static int sa1100dog_open(struct inode
*inode
, struct file
*file
)
76 if (test_and_set_bit(1, &sa1100wdt_users
))
79 /* Activate SA1100 Watchdog timer */
80 sa1100_wr(sa1100_rd(REG_OSCR
) + pre_margin
, REG_OSMR3
);
81 sa1100_wr(OSSR_M3
, REG_OSSR
);
82 sa1100_wr(OWER_WME
, REG_OWER
);
83 sa1100_wr(sa1100_rd(REG_OIER
) | OIER_E3
, REG_OIER
);
84 return stream_open(inode
, file
);
88 * The watchdog cannot be disabled.
90 * Previous comments suggested that turning off the interrupt by
91 * clearing REG_OIER[E3] would prevent the watchdog timing out but this
92 * does not appear to be true (at least on the PXA255).
94 static int sa1100dog_release(struct inode
*inode
, struct file
*file
)
96 pr_crit("Device closed - timer will not stop\n");
97 clear_bit(1, &sa1100wdt_users
);
101 static ssize_t
sa1100dog_write(struct file
*file
, const char __user
*data
,
102 size_t len
, loff_t
*ppos
)
105 /* Refresh OSMR3 timer. */
106 sa1100_wr(sa1100_rd(REG_OSCR
) + pre_margin
, REG_OSMR3
);
110 static const struct watchdog_info ident
= {
111 .options
= WDIOF_CARDRESET
| WDIOF_SETTIMEOUT
112 | WDIOF_KEEPALIVEPING
,
113 .identity
= "SA1100/PXA255 Watchdog",
114 .firmware_version
= 1,
117 static long sa1100dog_ioctl(struct file
*file
, unsigned int cmd
,
122 void __user
*argp
= (void __user
*)arg
;
123 int __user
*p
= argp
;
126 case WDIOC_GETSUPPORT
:
127 ret
= copy_to_user(argp
, &ident
,
128 sizeof(ident
)) ? -EFAULT
: 0;
131 case WDIOC_GETSTATUS
:
132 ret
= put_user(0, p
);
135 case WDIOC_GETBOOTSTATUS
:
136 ret
= put_user(boot_status
, p
);
139 case WDIOC_KEEPALIVE
:
140 sa1100_wr(sa1100_rd(REG_OSCR
) + pre_margin
, REG_OSMR3
);
144 case WDIOC_SETTIMEOUT
:
145 ret
= get_user(time
, p
);
149 if (time
<= 0 || (oscr_freq
* (long long)time
>= 0xffffffff)) {
154 pre_margin
= oscr_freq
* time
;
155 sa1100_wr(sa1100_rd(REG_OSCR
) + pre_margin
, REG_OSMR3
);
158 case WDIOC_GETTIMEOUT
:
159 ret
= put_user(pre_margin
/ oscr_freq
, p
);
165 static const struct file_operations sa1100dog_fops
= {
166 .owner
= THIS_MODULE
,
167 .write
= sa1100dog_write
,
168 .unlocked_ioctl
= sa1100dog_ioctl
,
169 .compat_ioctl
= compat_ptr_ioctl
,
170 .open
= sa1100dog_open
,
171 .release
= sa1100dog_release
,
174 static struct miscdevice sa1100dog_miscdev
= {
175 .minor
= WATCHDOG_MINOR
,
177 .fops
= &sa1100dog_fops
,
180 static int margin
= 60; /* (secs) Default is 1 minute */
181 static struct clk
*clk
;
183 static int sa1100dog_probe(struct platform_device
*pdev
)
187 struct resource
*res
;
189 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
192 reg_base
= devm_ioremap(&pdev
->dev
, res
->start
, resource_size(res
));
196 clk
= clk_get(NULL
, "OSTIMER0");
198 pr_err("SA1100/PXA2xx Watchdog Timer: clock not found: %d\n",
203 ret
= clk_prepare_enable(clk
);
205 pr_err("SA1100/PXA2xx Watchdog Timer: clock failed to prepare+enable: %d\n",
210 oscr_freq
= clk_get_rate(clk
);
212 platform_data
= pdev
->dev
.platform_data
;
213 if (platform_data
&& *platform_data
)
214 boot_status
= WDIOF_CARDRESET
;
215 pre_margin
= oscr_freq
* margin
;
217 ret
= misc_register(&sa1100dog_miscdev
);
219 pr_info("SA1100/PXA2xx Watchdog Timer: timer margin %d sec\n",
224 clk_disable_unprepare(clk
);
230 static void sa1100dog_remove(struct platform_device
*pdev
)
232 misc_deregister(&sa1100dog_miscdev
);
233 clk_disable_unprepare(clk
);
237 static struct platform_driver sa1100dog_driver
= {
238 .driver
.name
= "sa1100_wdt",
239 .probe
= sa1100dog_probe
,
240 .remove
= sa1100dog_remove
,
242 module_platform_driver(sa1100dog_driver
);
244 MODULE_AUTHOR("Oleg Drokin <green@crimea.edu>");
245 MODULE_DESCRIPTION("SA1100/PXA2xx Watchdog");
247 module_param(margin
, int, 0);
248 MODULE_PARM_DESC(margin
, "Watchdog margin in seconds (default 60s)");
250 MODULE_LICENSE("GPL");