Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

[PATCH] ppc32: Added support for the Book-E style Watchdog Timer

PowerPC 40x and Book-E processors support a watchdog timer at the processor
core level. The timer has implementation dependent timeout frequencies
that can be configured by software.

One the first Watchdog timeout we get a critical exception. It is left to
board specific code to determine what should happen at this point. If
nothing is done and another timeout period expires the processor may
attempt to reset the machine.

Command line parameters:
wdt=0 : disable watchdog (default)
wdt=1 : enable watchdog

wdt_period=N : N sets the value of the Watchdog Timer Period.

The Watchdog Timer Period meaning is implementation specific. Check
User Manual for the processor for more details.

This patch is based off of work done by Takeharu Kato.

Signed-off-by: Matt McClintock <msm@freescale.com>
Signed-off-by: Kumar Gala <kumar.gala@freescale.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>

authored by

Kumar Gala and committed by
Linus Torvalds
a2f40ccd 886b9fa4

+270 -27
+20
Documentation/watchdog/watchdog-api.txt
··· 228 228 The GETSTATUS call returns if the device is open or not. 229 229 [FIXME -- silliness again?] 230 230 231 + booke_wdt.c -- PowerPC BookE Watchdog Timer 232 + 233 + Timeout default varies according to frequency, supports 234 + SETTIMEOUT 235 + 236 + Watchdog can not be turned off, CONFIG_WATCHDOG_NOWAYOUT 237 + does not make sense 238 + 239 + GETSUPPORT returns the watchdog_info struct, and 240 + GETSTATUS returns the supported options. GETBOOTSTATUS 241 + returns a 1 if the last reset was caused by the 242 + watchdog and a 0 otherwise. This watchdog can not be 243 + disabled once it has been started. The wdt_period kernel 244 + parameter selects which bit of the time base changing 245 + from 0->1 will trigger the watchdog exception. Changing 246 + the timeout from the ioctl calls will change the 247 + wdt_period as defined above. Finally if you would like to 248 + replace the default Watchdog Handler you can implement the 249 + WatchdogHandler() function in your own code. 250 + 231 251 eurotechwdt.c -- Eurotech CPU-1220/1410 232 252 233 253 The timeout can be set using the SETTIMEOUT ioctl and defaults
+4
arch/ppc/kernel/head_44x.S
··· 462 462 463 463 /* Watchdog Timer Interrupt */ 464 464 /* TODO: Add watchdog support */ 465 + #ifdef CONFIG_BOOKE_WDT 466 + CRITICAL_EXCEPTION(0x1020, WatchdogTimer, WatchdogException) 467 + #else 465 468 CRITICAL_EXCEPTION(0x1020, WatchdogTimer, UnknownException) 469 + #endif 466 470 467 471 /* Data TLB Error Interrupt */ 468 472 START_EXCEPTION(DataTLBError)
+3 -1
arch/ppc/kernel/head_4xx.S
··· 448 448 449 449 /* 0x1020 - Watchdog Timer (WDT) Exception 450 450 */ 451 - 451 + #ifdef CONFIG_BOOKE_WDT 452 + CRITICAL_EXCEPTION(0x1020, WDTException, WatchdogException) 453 + #else 452 454 CRITICAL_EXCEPTION(0x1020, WDTException, UnknownException) 453 455 #endif 454 456
+4 -1
arch/ppc/kernel/head_fsl_booke.S
··· 564 564 EXCEPTION(0x3100, FixedIntervalTimer, UnknownException, EXC_XFER_EE) 565 565 566 566 /* Watchdog Timer Interrupt */ 567 - /* TODO: Add watchdog support */ 567 + #ifdef CONFIG_BOOKE_WDT 568 + CRITICAL_EXCEPTION(0x3200, WatchdogTimer, WatchdogException) 569 + #else 568 570 CRITICAL_EXCEPTION(0x3200, WatchdogTimer, UnknownException) 571 + #endif 569 572 570 573 /* Data TLB Error Interrupt */ 571 574 START_EXCEPTION(DataTLBError)
+24
arch/ppc/kernel/setup.c
··· 615 615 if (ppc_md.progress) 616 616 ppc_md.progress("id mach(): done", 0x200); 617 617 } 618 + #ifdef CONFIG_BOOKE_WDT 619 + /* Checks wdt=x and wdt_period=xx command-line option */ 620 + int __init early_parse_wdt(char *p) 621 + { 622 + extern u32 wdt_enable; 623 + 624 + if (p && strncmp(p, "0", 1) != 0) 625 + wdt_enable = 1; 626 + 627 + return 0; 628 + } 629 + early_param("wdt", early_parse_wdt); 630 + 631 + int __init early_parse_wdt_period (char *p) 632 + { 633 + extern u32 wdt_period; 634 + 635 + if (p) 636 + wdt_period = simple_strtoul(p, NULL, 0); 637 + 638 + return 0; 639 + } 640 + early_param("wdt_period", early_parse_wdt_period); 641 + #endif /* CONFIG_BOOKE_WDT */ 618 642 619 643 /* Checks "l2cr=xxxx" command-line option */ 620 644 int __init ppc_setup_l2cr(char *str)
+19
arch/ppc/kernel/traps.c
··· 904 904 } 905 905 #endif 906 906 907 + #ifdef CONFIG_BOOKE_WDT 908 + /* 909 + * Default handler for a Watchdog exception, 910 + * spins until a reboot occurs 911 + */ 912 + void __attribute__ ((weak)) WatchdogHandler(struct pt_regs *regs) 913 + { 914 + /* Generic WatchdogHandler, implement your own */ 915 + mtspr(SPRN_TCR, mfspr(SPRN_TCR)&(~TCR_WIE)); 916 + return; 917 + } 918 + 919 + void WatchdogException(struct pt_regs *regs) 920 + { 921 + printk (KERN_EMERG "PowerPC Book-E Watchdog Exception\n"); 922 + WatchdogHandler(regs); 923 + } 924 + #endif 925 + 907 926 void __init trap_init(void) 908 927 { 909 928 }
-25
arch/ppc/syslib/ppc4xx_setup.c
··· 48 48 extern void abort(void); 49 49 extern void ppc4xx_find_bridges(void); 50 50 51 - extern void ppc4xx_wdt_heartbeat(void); 52 - extern int wdt_enable; 53 - extern unsigned long wdt_period; 54 - 55 51 /* Global Variables */ 56 52 bd_t __res; 57 53 ··· 253 257 *(char *) (r7 + KERNELBASE) = 0; 254 258 strcpy(cmd_line, (char *) (r6 + KERNELBASE)); 255 259 } 256 - #if defined(CONFIG_PPC405_WDT) 257 - /* Look for wdt= option on command line */ 258 - if (strstr(cmd_line, "wdt=")) { 259 - int valid_wdt = 0; 260 - char *p, *q; 261 - for (q = cmd_line; (p = strstr(q, "wdt=")) != 0;) { 262 - q = p + 4; 263 - if (p > cmd_line && p[-1] != ' ') 264 - continue; 265 - wdt_period = simple_strtoul(q, &q, 0); 266 - valid_wdt = 1; 267 - ++q; 268 - } 269 - wdt_enable = valid_wdt; 270 - } 271 - #endif 272 260 273 261 /* Initialize machine-dependent vectors */ 274 262 ··· 266 286 ppc_md.halt = ppc4xx_halt; 267 287 268 288 ppc_md.calibrate_decr = ppc4xx_calibrate_decr; 269 - 270 - #ifdef CONFIG_PPC405_WDT 271 - ppc_md.heartbeat = ppc4xx_wdt_heartbeat; 272 - #endif 273 - ppc_md.heartbeat_count = 0; 274 289 275 290 ppc_md.find_end_of_memory = ppc4xx_find_end_of_memory; 276 291 ppc_md.setup_io_mappings = ppc4xx_map_io;
+4
drivers/char/watchdog/Kconfig
··· 346 346 tristate "MPC8xx Watchdog Timer" 347 347 depends on WATCHDOG && 8xx 348 348 349 + config BOOKE_WDT 350 + tristate "PowerPC Book-E Watchdog Timer" 351 + depends on WATCHDOG && (BOOKE || 4xx) 352 + 349 353 # MIPS Architecture 350 354 351 355 config INDYDOG
+1
drivers/char/watchdog/Makefile
··· 34 34 obj-$(CONFIG_IXP2000_WATCHDOG) += ixp2000_wdt.o 35 35 obj-$(CONFIG_8xx_WDT) += mpc8xx_wdt.o 36 36 obj-$(CONFIG_WATCHDOG_RTAS) += wdrtas.o 37 + obj-$(CONFIG_BOOKE_WDT) += booke_wdt.o 37 38 38 39 # Only one watchdog can succeed. We probe the hardware watchdog 39 40 # drivers first, then the softdog driver. This means if your hardware
+191
drivers/char/watchdog/booke_wdt.c
··· 1 + /* 2 + * drivers/char/watchdog/booke_wdt.c 3 + * 4 + * Watchdog timer for PowerPC Book-E systems 5 + * 6 + * Author: Matthew McClintock 7 + * Maintainer: Kumar Gala <kumar.gala@freescale.com> 8 + * 9 + * Copyright 2005 Freescale Semiconductor Inc. 10 + * 11 + * This program is free software; you can redistribute it and/or modify it 12 + * under the terms of the GNU General Public License as published by the 13 + * Free Software Foundation; either version 2 of the License, or (at your 14 + * option) any later version. 15 + */ 16 + 17 + #include <linux/config.h> 18 + #include <linux/module.h> 19 + #include <linux/fs.h> 20 + #include <linux/miscdevice.h> 21 + #include <linux/notifier.h> 22 + #include <linux/watchdog.h> 23 + 24 + #include <asm/reg_booke.h> 25 + #include <asm/uaccess.h> 26 + 27 + /* If the kernel parameter wdt_enable=1, the watchdog will be enabled at boot. 28 + * Also, the wdt_period sets the watchdog timer period timeout. 29 + * For E500 cpus the wdt_period sets which bit changing from 0->1 will 30 + * trigger a watchog timeout. This watchdog timeout will occur 3 times, the 31 + * first time nothing will happen, the second time a watchdog exception will 32 + * occur, and the final time the board will reset. 33 + */ 34 + 35 + #ifdef CONFIG_FSL_BOOKE 36 + #define WDT_PERIOD_DEFAULT 63 /* Ex. wdt_period=28 bus=333Mhz , reset=~40sec */ 37 + #else 38 + #define WDT_PERIOD_DEFAULT 4 /* Refer to the PPC40x and PPC4xx manuals */ 39 + #endif /* for timing information */ 40 + 41 + u32 wdt_enable = 0; 42 + u32 wdt_period = WDT_PERIOD_DEFAULT; 43 + 44 + #ifdef CONFIG_FSL_BOOKE 45 + #define WDTP(x) ((((63-x)&0x3)<<30)|(((63-x)&0x3c)<<15)) 46 + #else 47 + #define WDTP(x) (TCR_WP(x)) 48 + #endif 49 + 50 + /* 51 + * booke_wdt_enable: 52 + */ 53 + static __inline__ void booke_wdt_enable(void) 54 + { 55 + u32 val; 56 + 57 + val = mfspr(SPRN_TCR); 58 + val |= (TCR_WIE|TCR_WRC(WRC_CHIP)|WDTP(wdt_period)); 59 + 60 + mtspr(SPRN_TCR, val); 61 + } 62 + 63 + /* 64 + * booke_wdt_ping: 65 + */ 66 + static __inline__ void booke_wdt_ping(void) 67 + { 68 + mtspr(SPRN_TSR, TSR_ENW|TSR_WIS); 69 + } 70 + 71 + /* 72 + * booke_wdt_write: 73 + */ 74 + static ssize_t booke_wdt_write (struct file *file, const char *buf, 75 + size_t count, loff_t *ppos) 76 + { 77 + booke_wdt_ping(); 78 + return count; 79 + } 80 + 81 + static struct watchdog_info ident = { 82 + .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, 83 + .firmware_version = 0, 84 + .identity = "PowerPC Book-E Watchdog", 85 + }; 86 + 87 + /* 88 + * booke_wdt_ioctl: 89 + */ 90 + static int booke_wdt_ioctl (struct inode *inode, struct file *file, 91 + unsigned int cmd, unsigned long arg) 92 + { 93 + u32 tmp = 0; 94 + 95 + switch (cmd) { 96 + case WDIOC_GETSUPPORT: 97 + if (copy_to_user ((struct watchdog_info *) arg, &ident, 98 + sizeof(struct watchdog_info))) 99 + return -EFAULT; 100 + case WDIOC_GETSTATUS: 101 + return put_user(ident.options, (u32 *) arg); 102 + case WDIOC_GETBOOTSTATUS: 103 + /* XXX: something is clearing TSR */ 104 + tmp = mfspr(SPRN_TSR) & TSR_WRS(3); 105 + /* returns 1 if last reset was caused by the WDT */ 106 + return (tmp ? 1 : 0); 107 + case WDIOC_KEEPALIVE: 108 + booke_wdt_ping(); 109 + return 0; 110 + case WDIOC_SETTIMEOUT: 111 + if (get_user(wdt_period, (u32 *) arg)) 112 + return -EFAULT; 113 + mtspr(SPRN_TCR, (mfspr(SPRN_TCR)&~WDTP(0))|WDTP(wdt_period)); 114 + return 0; 115 + case WDIOC_GETTIMEOUT: 116 + return put_user(wdt_period, (u32 *) arg); 117 + case WDIOC_SETOPTIONS: 118 + if (get_user(tmp, (u32 *) arg)) 119 + return -EINVAL; 120 + if (tmp == WDIOS_ENABLECARD) { 121 + booke_wdt_ping(); 122 + break; 123 + } else 124 + return -EINVAL; 125 + return 0; 126 + default: 127 + return -ENOIOCTLCMD; 128 + } 129 + 130 + return 0; 131 + } 132 + /* 133 + * booke_wdt_open: 134 + */ 135 + static int booke_wdt_open (struct inode *inode, struct file *file) 136 + { 137 + if (wdt_enable == 0) { 138 + wdt_enable = 1; 139 + booke_wdt_enable(); 140 + printk (KERN_INFO "PowerPC Book-E Watchdog Timer Enabled (wdt_period=%d)\n", 141 + wdt_period); 142 + } 143 + 144 + return 0; 145 + } 146 + 147 + static struct file_operations booke_wdt_fops = { 148 + .owner = THIS_MODULE, 149 + .llseek = no_llseek, 150 + .write = booke_wdt_write, 151 + .ioctl = booke_wdt_ioctl, 152 + .open = booke_wdt_open, 153 + }; 154 + 155 + static struct miscdevice booke_wdt_miscdev = { 156 + .minor = WATCHDOG_MINOR, 157 + .name = "watchdog", 158 + .fops = &booke_wdt_fops, 159 + }; 160 + 161 + static void __exit booke_wdt_exit(void) 162 + { 163 + misc_deregister(&booke_wdt_miscdev); 164 + } 165 + 166 + /* 167 + * booke_wdt_init: 168 + */ 169 + static int __init booke_wdt_init(void) 170 + { 171 + int ret = 0; 172 + 173 + printk (KERN_INFO "PowerPC Book-E Watchdog Timer Loaded\n"); 174 + ident.firmware_version = cpu_specs[0].pvr_value; 175 + 176 + ret = misc_register(&booke_wdt_miscdev); 177 + if (ret) { 178 + printk (KERN_CRIT "Cannot register miscdev on minor=%d (err=%d)\n", 179 + WATCHDOG_MINOR, ret); 180 + return ret; 181 + } 182 + 183 + if (wdt_enable == 1) { 184 + printk (KERN_INFO "PowerPC Book-E Watchdog Timer Enabled (wdt_period=%d)\n", 185 + wdt_period); 186 + booke_wdt_enable(); 187 + } 188 + 189 + return ret; 190 + } 191 + device_initcall(booke_wdt_init);