Commit 70c14ff0e9f5e1f5456587b827620e636ba70a09
Committed by
Russell King
1 parent
7dea1b2006
Exists in
master
and in
7 other branches
[ARM] 4495/1: iop: combined watchdog timer driver for iop3xx and iop13xx
In order for this driver to be shared across the iop architectures the iop3xx and iop13xx header files are modified to present a common interface for the iop_wdt driver. Details: * iop13xx supports disabling the timer while iop3xx does not. This requires a few 'compatibility' definitions in include/asm-arm/hardware/iop3xx.h to preclude adding #ifdef CONFIG_ARCH_IOP13XX blocks to the driver code. * The heartbeat interval is derived from the internal bus clock rate, so this this patch also exports the tick rate to the iop_wdt driver. Cc: Curt Bruns <curt.e.bruns@intel.com> Cc: Peter Milne <peter.milne@d-tacq.com> Signed-off-by: Dan Williams <dan.j.williams@intel.com> Acked-by: Wim Van Sebroeck <wim@iguana.be> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Showing 7 changed files with 365 additions and 32 deletions Side-by-side Diff
arch/arm/plat-iop/time.c
... | ... | @@ -78,6 +78,13 @@ |
78 | 78 | .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, |
79 | 79 | }; |
80 | 80 | |
81 | +static unsigned long iop_tick_rate; | |
82 | +unsigned long get_iop_tick_rate(void) | |
83 | +{ | |
84 | + return iop_tick_rate; | |
85 | +} | |
86 | +EXPORT_SYMBOL(get_iop_tick_rate); | |
87 | + | |
81 | 88 | void __init iop_init_time(unsigned long tick_rate) |
82 | 89 | { |
83 | 90 | u32 timer_ctl; |
... | ... | @@ -85,6 +92,7 @@ |
85 | 92 | ticks_per_jiffy = (tick_rate + HZ/2) / HZ; |
86 | 93 | ticks_per_usec = tick_rate / 1000000; |
87 | 94 | next_jiffy_time = 0xffffffff; |
95 | + iop_tick_rate = tick_rate; | |
88 | 96 | |
89 | 97 | timer_ctl = IOP_TMR_EN | IOP_TMR_PRIVILEGED | |
90 | 98 | IOP_TMR_RELOAD | IOP_TMR_RATIO_1_1; |
drivers/char/watchdog/Kconfig
... | ... | @@ -187,6 +187,22 @@ |
187 | 187 | |
188 | 188 | Say N if you are unsure. |
189 | 189 | |
190 | +config IOP_WATCHDOG | |
191 | + tristate "IOP Watchdog" | |
192 | + depends on WATCHDOG && PLAT_IOP | |
193 | + select WATCHDOG_NOWAYOUT if (ARCH_IOP32X || ARCH_IOP33X) | |
194 | + help | |
195 | + Say Y here if to include support for the watchdog timer | |
196 | + in the Intel IOP3XX & IOP13XX I/O Processors. This driver can | |
197 | + be built as a module by choosing M. The module will | |
198 | + be called iop_wdt. | |
199 | + | |
200 | + Note: The IOP13XX watchdog does an Internal Bus Reset which will | |
201 | + affect both cores and the peripherals of the IOP. The ATU-X | |
202 | + and/or ATUe configuration registers will remain intact, but if | |
203 | + operating as an Root Complex and/or Central Resource, the PCI-X | |
204 | + and/or PCIe busses will also be reset. THIS IS A VERY BIG HAMMER. | |
205 | + | |
190 | 206 | # AVR32 Architecture |
191 | 207 | |
192 | 208 | config AT32AP700X_WDT |
drivers/char/watchdog/Makefile
... | ... | @@ -35,6 +35,7 @@ |
35 | 35 | obj-$(CONFIG_MPCORE_WATCHDOG) += mpcore_wdt.o |
36 | 36 | obj-$(CONFIG_EP93XX_WATCHDOG) += ep93xx_wdt.o |
37 | 37 | obj-$(CONFIG_PNX4008_WATCHDOG) += pnx4008_wdt.o |
38 | +obj-$(CONFIG_IOP_WATCHDOG) += iop_wdt.o | |
38 | 39 | |
39 | 40 | # AVR32 Architecture |
40 | 41 | obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o |
drivers/char/watchdog/iop_wdt.c
1 | +/* | |
2 | + * drivers/char/watchdog/iop_wdt.c | |
3 | + * | |
4 | + * WDT driver for Intel I/O Processors | |
5 | + * Copyright (C) 2005, Intel Corporation. | |
6 | + * | |
7 | + * Based on ixp4xx driver, Copyright 2004 (c) MontaVista, Software, Inc. | |
8 | + * | |
9 | + * This program is free software; you can redistribute it and/or modify it | |
10 | + * under the terms and conditions of the GNU General Public License, | |
11 | + * version 2, as published by the Free Software Foundation. | |
12 | + * | |
13 | + * This program is distributed in the hope it will be useful, but WITHOUT | |
14 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
15 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
16 | + * more details. | |
17 | + * | |
18 | + * You should have received a copy of the GNU General Public License along with | |
19 | + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple | |
20 | + * Place - Suite 330, Boston, MA 02111-1307 USA. | |
21 | + * | |
22 | + * Curt E Bruns <curt.e.bruns@intel.com> | |
23 | + * Peter Milne <peter.milne@d-tacq.com> | |
24 | + * Dan Williams <dan.j.williams@intel.com> | |
25 | + */ | |
26 | + | |
27 | +#include <linux/module.h> | |
28 | +#include <linux/kernel.h> | |
29 | +#include <linux/fs.h> | |
30 | +#include <linux/init.h> | |
31 | +#include <linux/device.h> | |
32 | +#include <linux/miscdevice.h> | |
33 | +#include <linux/watchdog.h> | |
34 | +#include <linux/uaccess.h> | |
35 | +#include <asm/hardware.h> | |
36 | + | |
37 | +static int nowayout = WATCHDOG_NOWAYOUT; | |
38 | +static unsigned long wdt_status; | |
39 | +static unsigned long boot_status; | |
40 | + | |
41 | +#define WDT_IN_USE 0 | |
42 | +#define WDT_OK_TO_CLOSE 1 | |
43 | +#define WDT_ENABLED 2 | |
44 | + | |
45 | +static unsigned long iop_watchdog_timeout(void) | |
46 | +{ | |
47 | + return (0xffffffffUL / get_iop_tick_rate()); | |
48 | +} | |
49 | + | |
50 | +/** | |
51 | + * wdt_supports_disable - determine if we are accessing a iop13xx watchdog | |
52 | + * or iop3xx by whether it has a disable command | |
53 | + */ | |
54 | +static int wdt_supports_disable(void) | |
55 | +{ | |
56 | + int can_disable; | |
57 | + | |
58 | + if (IOP_WDTCR_EN_ARM != IOP_WDTCR_DIS_ARM) | |
59 | + can_disable = 1; | |
60 | + else | |
61 | + can_disable = 0; | |
62 | + | |
63 | + return can_disable; | |
64 | +} | |
65 | + | |
66 | +static void wdt_enable(void) | |
67 | +{ | |
68 | + /* Arm and enable the Timer to starting counting down from 0xFFFF.FFFF | |
69 | + * Takes approx. 10.7s to timeout | |
70 | + */ | |
71 | + write_wdtcr(IOP_WDTCR_EN_ARM); | |
72 | + write_wdtcr(IOP_WDTCR_EN); | |
73 | +} | |
74 | + | |
75 | +/* returns 0 if the timer was successfully disabled */ | |
76 | +static int wdt_disable(void) | |
77 | +{ | |
78 | + /* Stop Counting */ | |
79 | + if (wdt_supports_disable()) { | |
80 | + write_wdtcr(IOP_WDTCR_DIS_ARM); | |
81 | + write_wdtcr(IOP_WDTCR_DIS); | |
82 | + clear_bit(WDT_ENABLED, &wdt_status); | |
83 | + printk(KERN_INFO "WATCHDOG: Disabled\n"); | |
84 | + return 0; | |
85 | + } else | |
86 | + return 1; | |
87 | +} | |
88 | + | |
89 | +static int iop_wdt_open(struct inode *inode, struct file *file) | |
90 | +{ | |
91 | + if (test_and_set_bit(WDT_IN_USE, &wdt_status)) | |
92 | + return -EBUSY; | |
93 | + | |
94 | + clear_bit(WDT_OK_TO_CLOSE, &wdt_status); | |
95 | + | |
96 | + wdt_enable(); | |
97 | + | |
98 | + set_bit(WDT_ENABLED, &wdt_status); | |
99 | + | |
100 | + return nonseekable_open(inode, file); | |
101 | +} | |
102 | + | |
103 | +static ssize_t | |
104 | +iop_wdt_write(struct file *file, const char *data, size_t len, | |
105 | + loff_t *ppos) | |
106 | +{ | |
107 | + if (len) { | |
108 | + if (!nowayout) { | |
109 | + size_t i; | |
110 | + | |
111 | + clear_bit(WDT_OK_TO_CLOSE, &wdt_status); | |
112 | + | |
113 | + for (i = 0; i != len; i++) { | |
114 | + char c; | |
115 | + | |
116 | + if (get_user(c, data + i)) | |
117 | + return -EFAULT; | |
118 | + if (c == 'V') | |
119 | + set_bit(WDT_OK_TO_CLOSE, &wdt_status); | |
120 | + } | |
121 | + } | |
122 | + wdt_enable(); | |
123 | + } | |
124 | + | |
125 | + return len; | |
126 | +} | |
127 | + | |
128 | +static struct watchdog_info ident = { | |
129 | + .options = WDIOF_CARDRESET | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING, | |
130 | + .identity = "iop watchdog", | |
131 | +}; | |
132 | + | |
133 | +static int | |
134 | +iop_wdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, | |
135 | + unsigned long arg) | |
136 | +{ | |
137 | + int options; | |
138 | + int ret = -ENOTTY; | |
139 | + | |
140 | + switch (cmd) { | |
141 | + case WDIOC_GETSUPPORT: | |
142 | + if (copy_to_user | |
143 | + ((struct watchdog_info *)arg, &ident, sizeof ident)) | |
144 | + ret = -EFAULT; | |
145 | + else | |
146 | + ret = 0; | |
147 | + break; | |
148 | + | |
149 | + case WDIOC_GETSTATUS: | |
150 | + ret = put_user(0, (int *)arg); | |
151 | + break; | |
152 | + | |
153 | + case WDIOC_GETBOOTSTATUS: | |
154 | + ret = put_user(boot_status, (int *)arg); | |
155 | + break; | |
156 | + | |
157 | + case WDIOC_GETTIMEOUT: | |
158 | + ret = put_user(iop_watchdog_timeout(), (int *)arg); | |
159 | + break; | |
160 | + | |
161 | + case WDIOC_KEEPALIVE: | |
162 | + wdt_enable(); | |
163 | + ret = 0; | |
164 | + break; | |
165 | + | |
166 | + case WDIOC_SETOPTIONS: | |
167 | + if (get_user(options, (int *)arg)) | |
168 | + return -EFAULT; | |
169 | + | |
170 | + if (options & WDIOS_DISABLECARD) { | |
171 | + if (!nowayout) { | |
172 | + if (wdt_disable() == 0) { | |
173 | + set_bit(WDT_OK_TO_CLOSE, &wdt_status); | |
174 | + ret = 0; | |
175 | + } else | |
176 | + ret = -ENXIO; | |
177 | + } else | |
178 | + ret = 0; | |
179 | + } | |
180 | + | |
181 | + if (options & WDIOS_ENABLECARD) { | |
182 | + wdt_enable(); | |
183 | + ret = 0; | |
184 | + } | |
185 | + break; | |
186 | + } | |
187 | + | |
188 | + return ret; | |
189 | +} | |
190 | + | |
191 | +static int iop_wdt_release(struct inode *inode, struct file *file) | |
192 | +{ | |
193 | + int state = 1; | |
194 | + if (test_bit(WDT_OK_TO_CLOSE, &wdt_status)) | |
195 | + if (test_bit(WDT_ENABLED, &wdt_status)) | |
196 | + state = wdt_disable(); | |
197 | + | |
198 | + /* if the timer is not disbaled reload and notify that we are still | |
199 | + * going down | |
200 | + */ | |
201 | + if (state != 0) { | |
202 | + wdt_enable(); | |
203 | + printk(KERN_CRIT "WATCHDOG: Device closed unexpectedly - " | |
204 | + "reset in %lu seconds\n", iop_watchdog_timeout()); | |
205 | + } | |
206 | + | |
207 | + clear_bit(WDT_IN_USE, &wdt_status); | |
208 | + clear_bit(WDT_OK_TO_CLOSE, &wdt_status); | |
209 | + | |
210 | + return 0; | |
211 | +} | |
212 | + | |
213 | +static const struct file_operations iop_wdt_fops = { | |
214 | + .owner = THIS_MODULE, | |
215 | + .llseek = no_llseek, | |
216 | + .write = iop_wdt_write, | |
217 | + .ioctl = iop_wdt_ioctl, | |
218 | + .open = iop_wdt_open, | |
219 | + .release = iop_wdt_release, | |
220 | +}; | |
221 | + | |
222 | +static struct miscdevice iop_wdt_miscdev = { | |
223 | + .minor = WATCHDOG_MINOR, | |
224 | + .name = "watchdog", | |
225 | + .fops = &iop_wdt_fops, | |
226 | +}; | |
227 | + | |
228 | +static int __init iop_wdt_init(void) | |
229 | +{ | |
230 | + int ret; | |
231 | + | |
232 | + ret = misc_register(&iop_wdt_miscdev); | |
233 | + if (ret == 0) | |
234 | + printk("iop watchdog timer: timeout %lu sec\n", | |
235 | + iop_watchdog_timeout()); | |
236 | + | |
237 | + /* check if the reset was caused by the watchdog timer */ | |
238 | + boot_status = (read_rcsr() & IOP_RCSR_WDT) ? WDIOF_CARDRESET : 0; | |
239 | + | |
240 | + /* Configure Watchdog Timeout to cause an Internal Bus (IB) Reset | |
241 | + * NOTE: An IB Reset will Reset both cores in the IOP342 | |
242 | + */ | |
243 | + write_wdtsr(IOP13XX_WDTCR_IB_RESET); | |
244 | + | |
245 | + return ret; | |
246 | +} | |
247 | + | |
248 | +static void __exit iop_wdt_exit(void) | |
249 | +{ | |
250 | + misc_deregister(&iop_wdt_miscdev); | |
251 | +} | |
252 | + | |
253 | +module_init(iop_wdt_init); | |
254 | +module_exit(iop_wdt_exit); | |
255 | + | |
256 | +module_param(nowayout, int, 0); | |
257 | +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started"); | |
258 | + | |
259 | +MODULE_AUTHOR("Curt E Bruns <curt.e.bruns@intel.com>"); | |
260 | +MODULE_DESCRIPTION("iop watchdog timer driver"); | |
261 | +MODULE_LICENSE("GPL"); | |
262 | +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); |
include/asm-arm/arch-iop13xx/iop13xx.h
... | ... | @@ -19,6 +19,39 @@ |
19 | 19 | return id; |
20 | 20 | } |
21 | 21 | |
22 | +/* WDTCR CP6 R7 Page 9 */ | |
23 | +static inline u32 read_wdtcr(void) | |
24 | +{ | |
25 | + u32 val; | |
26 | + asm volatile("mrc p6, 0, %0, c7, c9, 0":"=r" (val)); | |
27 | + return val; | |
28 | +} | |
29 | +static inline void write_wdtcr(u32 val) | |
30 | +{ | |
31 | + asm volatile("mcr p6, 0, %0, c7, c9, 0"::"r" (val)); | |
32 | +} | |
33 | + | |
34 | +/* WDTSR CP6 R8 Page 9 */ | |
35 | +static inline u32 read_wdtsr(void) | |
36 | +{ | |
37 | + u32 val; | |
38 | + asm volatile("mrc p6, 0, %0, c8, c9, 0":"=r" (val)); | |
39 | + return val; | |
40 | +} | |
41 | +static inline void write_wdtsr(u32 val) | |
42 | +{ | |
43 | + asm volatile("mcr p6, 0, %0, c8, c9, 0"::"r" (val)); | |
44 | +} | |
45 | + | |
46 | +/* RCSR - Reset Cause Status Register */ | |
47 | +static inline u32 read_rcsr(void) | |
48 | +{ | |
49 | + u32 val; | |
50 | + asm volatile("mrc p6, 0, %0, c0, c1, 0":"=r" (val)); | |
51 | + return val; | |
52 | +} | |
53 | + | |
54 | +extern unsigned long get_iop_tick_rate(void); | |
22 | 55 | #endif |
23 | 56 | |
24 | 57 | /* |
... | ... | @@ -480,5 +513,15 @@ |
480 | 513 | #define IOP13XX_PBI_LR1 IOP13XX_PBI_OFFSET(0x14) |
481 | 514 | |
482 | 515 | #define IOP13XX_PROCESSOR_FREQ IOP13XX_REG_ADDR32(0x2180) |
516 | + | |
517 | +/* Watchdog timer definitions */ | |
518 | +#define IOP_WDTCR_EN_ARM 0x1e1e1e1e | |
519 | +#define IOP_WDTCR_EN 0xe1e1e1e1 | |
520 | +#define IOP_WDTCR_DIS_ARM 0x1f1f1f1f | |
521 | +#define IOP_WDTCR_DIS 0xf1f1f1f1 | |
522 | +#define IOP_RCSR_WDT (1 << 5) /* reset caused by watchdog timer */ | |
523 | +#define IOP13XX_WDTSR_WRITE_EN (1 << 31) /* used to speed up reset requests */ | |
524 | +#define IOP13XX_WDTCR_IB_RESET (1 << 0) | |
525 | + | |
483 | 526 | #endif /* _IOP13XX_HW_H_ */ |
include/asm-arm/arch-iop13xx/system.h
... | ... | @@ -13,43 +13,13 @@ |
13 | 13 | cpu_do_idle(); |
14 | 14 | } |
15 | 15 | |
16 | -/* WDTCR CP6 R7 Page 9 */ | |
17 | -static inline u32 read_wdtcr(void) | |
18 | -{ | |
19 | - u32 val; | |
20 | - asm volatile("mrc p6, 0, %0, c7, c9, 0":"=r" (val)); | |
21 | - return val; | |
22 | -} | |
23 | -static inline void write_wdtcr(u32 val) | |
24 | -{ | |
25 | - asm volatile("mcr p6, 0, %0, c7, c9, 0"::"r" (val)); | |
26 | -} | |
27 | - | |
28 | -/* WDTSR CP6 R8 Page 9 */ | |
29 | -static inline u32 read_wdtsr(void) | |
30 | -{ | |
31 | - u32 val; | |
32 | - asm volatile("mrc p6, 0, %0, c8, c9, 0":"=r" (val)); | |
33 | - return val; | |
34 | -} | |
35 | -static inline void write_wdtsr(u32 val) | |
36 | -{ | |
37 | - asm volatile("mcr p6, 0, %0, c8, c9, 0"::"r" (val)); | |
38 | -} | |
39 | - | |
40 | -#define IOP13XX_WDTCR_EN_ARM 0x1e1e1e1e | |
41 | -#define IOP13XX_WDTCR_EN 0xe1e1e1e1 | |
42 | -#define IOP13XX_WDTCR_DIS_ARM 0x1f1f1f1f | |
43 | -#define IOP13XX_WDTCR_DIS 0xf1f1f1f1 | |
44 | -#define IOP13XX_WDTSR_WRITE_EN (1 << 31) | |
45 | -#define IOP13XX_WDTCR_IB_RESET (1 << 0) | |
46 | 16 | static inline void arch_reset(char mode) |
47 | 17 | { |
48 | 18 | /* |
49 | 19 | * Reset the internal bus (warning both cores are reset) |
50 | 20 | */ |
51 | - write_wdtcr(IOP13XX_WDTCR_EN_ARM); | |
52 | - write_wdtcr(IOP13XX_WDTCR_EN); | |
21 | + write_wdtcr(IOP_WDTCR_EN_ARM); | |
22 | + write_wdtcr(IOP_WDTCR_EN); | |
53 | 23 | write_wdtsr(IOP13XX_WDTSR_WRITE_EN | IOP13XX_WDTCR_IB_RESET); |
54 | 24 | write_wdtcr(0x1000); |
55 | 25 |
include/asm-arm/hardware/iop3xx.h
... | ... | @@ -194,6 +194,13 @@ |
194 | 194 | #define IOP_TMR_PRIVILEGED 0x08 |
195 | 195 | #define IOP_TMR_RATIO_1_1 0x00 |
196 | 196 | |
197 | +/* Watchdog timer definitions */ | |
198 | +#define IOP_WDTCR_EN_ARM 0x1e1e1e1e | |
199 | +#define IOP_WDTCR_EN 0xe1e1e1e1 | |
200 | +/* iop3xx does not support stopping the watchdog, so we just re-arm */ | |
201 | +#define IOP_WDTCR_DIS_ARM (IOP_WDTCR_EN_ARM) | |
202 | +#define IOP_WDTCR_DIS (IOP_WDTCR_EN) | |
203 | + | |
197 | 204 | /* Application accelerator unit */ |
198 | 205 | #define IOP3XX_AAU_PHYS_BASE (IOP3XX_PERIPHERAL_PHYS_BASE + 0x800) |
199 | 206 | #define IOP3XX_AAU_UPPER_PA (IOP3XX_AAU_PHYS_BASE + 0xa7) |
... | ... | @@ -272,6 +279,32 @@ |
272 | 279 | static inline void write_tisr(u32 val) |
273 | 280 | { |
274 | 281 | asm volatile("mcr p6, 0, %0, c6, c1, 0" : : "r" (val)); |
282 | +} | |
283 | + | |
284 | +static inline u32 read_wdtcr(void) | |
285 | +{ | |
286 | + u32 val; | |
287 | + asm volatile("mrc p6, 0, %0, c7, c1, 0":"=r" (val)); | |
288 | + return val; | |
289 | +} | |
290 | +static inline void write_wdtcr(u32 val) | |
291 | +{ | |
292 | + asm volatile("mcr p6, 0, %0, c7, c1, 0"::"r" (val)); | |
293 | +} | |
294 | + | |
295 | +extern unsigned long get_iop_tick_rate(void); | |
296 | + | |
297 | +/* only iop13xx has these registers, we define these to present a | |
298 | + * common register interface for the iop_wdt driver. | |
299 | + */ | |
300 | +#define IOP_RCSR_WDT (0) | |
301 | +static inline u32 read_rcsr(void) | |
302 | +{ | |
303 | + return 0; | |
304 | +} | |
305 | +static inline void write_wdtsr(u32 val) | |
306 | +{ | |
307 | + do { } while (0); | |
275 | 308 | } |
276 | 309 | |
277 | 310 | extern struct platform_device iop3xx_dma_0_channel; |