[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>

authored by Dan Williams and committed by Russell King 70c14ff0 7dea1b20

+365 -32
+8
arch/arm/plat-iop/time.c
··· 78 .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, 79 }; 80 81 void __init iop_init_time(unsigned long tick_rate) 82 { 83 u32 timer_ctl; ··· 92 ticks_per_jiffy = (tick_rate + HZ/2) / HZ; 93 ticks_per_usec = tick_rate / 1000000; 94 next_jiffy_time = 0xffffffff; 95 96 timer_ctl = IOP_TMR_EN | IOP_TMR_PRIVILEGED | 97 IOP_TMR_RELOAD | IOP_TMR_RATIO_1_1;
··· 78 .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, 79 }; 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 + 88 void __init iop_init_time(unsigned long tick_rate) 89 { 90 u32 timer_ctl; ··· 85 ticks_per_jiffy = (tick_rate + HZ/2) / HZ; 86 ticks_per_usec = tick_rate / 1000000; 87 next_jiffy_time = 0xffffffff; 88 + iop_tick_rate = tick_rate; 89 90 timer_ctl = IOP_TMR_EN | IOP_TMR_PRIVILEGED | 91 IOP_TMR_RELOAD | IOP_TMR_RATIO_1_1;
+16
drivers/char/watchdog/Kconfig
··· 187 188 Say N if you are unsure. 189 190 # AVR32 Architecture 191 192 config AT32AP700X_WDT
··· 187 188 Say N if you are unsure. 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 + 206 # AVR32 Architecture 207 208 config AT32AP700X_WDT
+1
drivers/char/watchdog/Makefile
··· 35 obj-$(CONFIG_MPCORE_WATCHDOG) += mpcore_wdt.o 36 obj-$(CONFIG_EP93XX_WATCHDOG) += ep93xx_wdt.o 37 obj-$(CONFIG_PNX4008_WATCHDOG) += pnx4008_wdt.o 38 39 # AVR32 Architecture 40 obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o
··· 35 obj-$(CONFIG_MPCORE_WATCHDOG) += mpcore_wdt.o 36 obj-$(CONFIG_EP93XX_WATCHDOG) += ep93xx_wdt.o 37 obj-$(CONFIG_PNX4008_WATCHDOG) += pnx4008_wdt.o 38 + obj-$(CONFIG_IOP_WATCHDOG) += iop_wdt.o 39 40 # AVR32 Architecture 41 obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o
+262
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);
+43
include/asm-arm/arch-iop13xx/iop13xx.h
··· 19 return id; 20 } 21 22 #endif 23 24 /* ··· 513 #define IOP13XX_PBI_LR1 IOP13XX_PBI_OFFSET(0x14) 514 515 #define IOP13XX_PROCESSOR_FREQ IOP13XX_REG_ADDR32(0x2180) 516 #endif /* _IOP13XX_HW_H_ */
··· 19 return id; 20 } 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); 55 #endif 56 57 /* ··· 480 #define IOP13XX_PBI_LR1 IOP13XX_PBI_OFFSET(0x14) 481 482 #define IOP13XX_PROCESSOR_FREQ IOP13XX_REG_ADDR32(0x2180) 483 + 484 + /* Watchdog timer definitions */ 485 + #define IOP_WDTCR_EN_ARM 0x1e1e1e1e 486 + #define IOP_WDTCR_EN 0xe1e1e1e1 487 + #define IOP_WDTCR_DIS_ARM 0x1f1f1f1f 488 + #define IOP_WDTCR_DIS 0xf1f1f1f1 489 + #define IOP_RCSR_WDT (1 << 5) /* reset caused by watchdog timer */ 490 + #define IOP13XX_WDTSR_WRITE_EN (1 << 31) /* used to speed up reset requests */ 491 + #define IOP13XX_WDTCR_IB_RESET (1 << 0) 492 + 493 #endif /* _IOP13XX_HW_H_ */
+2 -32
include/asm-arm/arch-iop13xx/system.h
··· 13 cpu_do_idle(); 14 } 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 static inline void arch_reset(char mode) 47 { 48 /* 49 * Reset the internal bus (warning both cores are reset) 50 */ 51 - write_wdtcr(IOP13XX_WDTCR_EN_ARM); 52 - write_wdtcr(IOP13XX_WDTCR_EN); 53 write_wdtsr(IOP13XX_WDTSR_WRITE_EN | IOP13XX_WDTCR_IB_RESET); 54 write_wdtcr(0x1000); 55
··· 13 cpu_do_idle(); 14 } 15 16 static inline void arch_reset(char mode) 17 { 18 /* 19 * Reset the internal bus (warning both cores are reset) 20 */ 21 + write_wdtcr(IOP_WDTCR_EN_ARM); 22 + write_wdtcr(IOP_WDTCR_EN); 23 write_wdtsr(IOP13XX_WDTSR_WRITE_EN | IOP13XX_WDTCR_IB_RESET); 24 write_wdtcr(0x1000); 25
+33
include/asm-arm/hardware/iop3xx.h
··· 194 #define IOP_TMR_PRIVILEGED 0x08 195 #define IOP_TMR_RATIO_1_1 0x00 196 197 /* Application accelerator unit */ 198 #define IOP3XX_AAU_PHYS_BASE (IOP3XX_PERIPHERAL_PHYS_BASE + 0x800) 199 #define IOP3XX_AAU_UPPER_PA (IOP3XX_AAU_PHYS_BASE + 0xa7) ··· 279 static inline void write_tisr(u32 val) 280 { 281 asm volatile("mcr p6, 0, %0, c6, c1, 0" : : "r" (val)); 282 } 283 284 extern struct platform_device iop3xx_dma_0_channel;
··· 194 #define IOP_TMR_PRIVILEGED 0x08 195 #define IOP_TMR_RATIO_1_1 0x00 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 + 204 /* Application accelerator unit */ 205 #define IOP3XX_AAU_PHYS_BASE (IOP3XX_PERIPHERAL_PHYS_BASE + 0x800) 206 #define IOP3XX_AAU_UPPER_PA (IOP3XX_AAU_PHYS_BASE + 0xa7) ··· 272 static inline void write_tisr(u32 val) 273 { 274 asm volatile("mcr p6, 0, %0, c6, c1, 0" : : "r" (val)); 275 + } 276 + 277 + static inline u32 read_wdtcr(void) 278 + { 279 + u32 val; 280 + asm volatile("mrc p6, 0, %0, c7, c1, 0":"=r" (val)); 281 + return val; 282 + } 283 + static inline void write_wdtcr(u32 val) 284 + { 285 + asm volatile("mcr p6, 0, %0, c7, c1, 0"::"r" (val)); 286 + } 287 + 288 + extern unsigned long get_iop_tick_rate(void); 289 + 290 + /* only iop13xx has these registers, we define these to present a 291 + * common register interface for the iop_wdt driver. 292 + */ 293 + #define IOP_RCSR_WDT (0) 294 + static inline u32 read_rcsr(void) 295 + { 296 + return 0; 297 + } 298 + static inline void write_wdtsr(u32 val) 299 + { 300 + do { } while (0); 301 } 302 303 extern struct platform_device iop3xx_dma_0_channel;