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

Configure Feed

Select the types of activity you want to include in your feed.

at v2.6.37 269 lines 6.7 kB view raw
1/* 2 * Watchdog timer for PowerPC Book-E systems 3 * 4 * Author: Matthew McClintock 5 * Maintainer: Kumar Gala <galak@kernel.crashing.org> 6 * 7 * Copyright 2005, 2008, 2010 Freescale Semiconductor Inc. 8 * 9 * This program is free software; you can redistribute it and/or modify it 10 * under the terms of the GNU General Public License as published by the 11 * Free Software Foundation; either version 2 of the License, or (at your 12 * option) any later version. 13 */ 14 15#include <linux/module.h> 16#include <linux/fs.h> 17#include <linux/smp.h> 18#include <linux/miscdevice.h> 19#include <linux/notifier.h> 20#include <linux/watchdog.h> 21#include <linux/uaccess.h> 22 23#include <asm/reg_booke.h> 24#include <asm/system.h> 25#include <asm/time.h> 26#include <asm/div64.h> 27 28/* If the kernel parameter wdt=1, the watchdog will be enabled at boot. 29 * Also, the wdt_period sets the watchdog timer period timeout. 30 * For E500 cpus the wdt_period sets which bit changing from 0->1 will 31 * trigger a watchog timeout. This watchdog timeout will occur 3 times, the 32 * first time nothing will happen, the second time a watchdog exception will 33 * occur, and the final time the board will reset. 34 */ 35 36u32 booke_wdt_enabled; 37u32 booke_wdt_period = CONFIG_BOOKE_WDT_DEFAULT_TIMEOUT; 38 39#ifdef CONFIG_FSL_BOOKE 40#define WDTP(x) ((((x)&0x3)<<30)|(((x)&0x3c)<<15)) 41#define WDTP_MASK (WDTP(0x3f)) 42#else 43#define WDTP(x) (TCR_WP(x)) 44#define WDTP_MASK (TCR_WP_MASK) 45#endif 46 47static DEFINE_SPINLOCK(booke_wdt_lock); 48 49/* For the specified period, determine the number of seconds 50 * corresponding to the reset time. There will be a watchdog 51 * exception at approximately 3/5 of this time. 52 * 53 * The formula to calculate this is given by: 54 * 2.5 * (2^(63-period+1)) / timebase_freq 55 * 56 * In order to simplify things, we assume that period is 57 * at least 1. This will still result in a very long timeout. 58 */ 59static unsigned long long period_to_sec(unsigned int period) 60{ 61 unsigned long long tmp = 1ULL << (64 - period); 62 unsigned long tmp2 = ppc_tb_freq; 63 64 /* tmp may be a very large number and we don't want to overflow, 65 * so divide the timebase freq instead of multiplying tmp 66 */ 67 tmp2 = tmp2 / 5 * 2; 68 69 do_div(tmp, tmp2); 70 return tmp; 71} 72 73/* 74 * This procedure will find the highest period which will give a timeout 75 * greater than the one required. e.g. for a bus speed of 66666666 and 76 * and a parameter of 2 secs, then this procedure will return a value of 38. 77 */ 78static unsigned int sec_to_period(unsigned int secs) 79{ 80 unsigned int period; 81 for (period = 63; period > 0; period--) { 82 if (period_to_sec(period) >= secs) 83 return period; 84 } 85 return 0; 86} 87 88static void __booke_wdt_ping(void *data) 89{ 90 mtspr(SPRN_TSR, TSR_ENW|TSR_WIS); 91} 92 93static void booke_wdt_ping(void) 94{ 95 on_each_cpu(__booke_wdt_ping, NULL, 0); 96} 97 98static void __booke_wdt_enable(void *data) 99{ 100 u32 val; 101 102 /* clear status before enabling watchdog */ 103 __booke_wdt_ping(NULL); 104 val = mfspr(SPRN_TCR); 105 val &= ~WDTP_MASK; 106 val |= (TCR_WIE|TCR_WRC(WRC_CHIP)|WDTP(booke_wdt_period)); 107 108 mtspr(SPRN_TCR, val); 109} 110 111/** 112 * booke_wdt_disable - disable the watchdog on the given CPU 113 * 114 * This function is called on each CPU. It disables the watchdog on that CPU. 115 * 116 * TCR[WRC] cannot be changed once it has been set to non-zero, but we can 117 * effectively disable the watchdog by setting its period to the maximum value. 118 */ 119static void __booke_wdt_disable(void *data) 120{ 121 u32 val; 122 123 val = mfspr(SPRN_TCR); 124 val &= ~(TCR_WIE | WDTP_MASK); 125 mtspr(SPRN_TCR, val); 126 127 /* clear status to make sure nothing is pending */ 128 __booke_wdt_ping(NULL); 129 130} 131 132static ssize_t booke_wdt_write(struct file *file, const char __user *buf, 133 size_t count, loff_t *ppos) 134{ 135 booke_wdt_ping(); 136 return count; 137} 138 139static struct watchdog_info ident = { 140 .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, 141 .identity = "PowerPC Book-E Watchdog", 142}; 143 144static long booke_wdt_ioctl(struct file *file, 145 unsigned int cmd, unsigned long arg) 146{ 147 u32 tmp = 0; 148 u32 __user *p = (u32 __user *)arg; 149 150 switch (cmd) { 151 case WDIOC_GETSUPPORT: 152 if (copy_to_user((void *)arg, &ident, sizeof(ident))) 153 return -EFAULT; 154 case WDIOC_GETSTATUS: 155 return put_user(0, p); 156 case WDIOC_GETBOOTSTATUS: 157 /* XXX: something is clearing TSR */ 158 tmp = mfspr(SPRN_TSR) & TSR_WRS(3); 159 /* returns CARDRESET if last reset was caused by the WDT */ 160 return (tmp ? WDIOF_CARDRESET : 0); 161 case WDIOC_SETOPTIONS: 162 if (get_user(tmp, p)) 163 return -EINVAL; 164 if (tmp == WDIOS_ENABLECARD) { 165 booke_wdt_ping(); 166 break; 167 } else 168 return -EINVAL; 169 return 0; 170 case WDIOC_KEEPALIVE: 171 booke_wdt_ping(); 172 return 0; 173 case WDIOC_SETTIMEOUT: 174 if (get_user(tmp, p)) 175 return -EFAULT; 176#ifdef CONFIG_FSL_BOOKE 177 /* period of 1 gives the largest possible timeout */ 178 if (tmp > period_to_sec(1)) 179 return -EINVAL; 180 booke_wdt_period = sec_to_period(tmp); 181#else 182 booke_wdt_period = tmp; 183#endif 184 mtspr(SPRN_TCR, (mfspr(SPRN_TCR) & ~WDTP_MASK) | 185 WDTP(booke_wdt_period)); 186 return 0; 187 case WDIOC_GETTIMEOUT: 188 return put_user(booke_wdt_period, p); 189 default: 190 return -ENOTTY; 191 } 192 193 return 0; 194} 195 196static int booke_wdt_open(struct inode *inode, struct file *file) 197{ 198 spin_lock(&booke_wdt_lock); 199 if (booke_wdt_enabled == 0) { 200 booke_wdt_enabled = 1; 201 on_each_cpu(__booke_wdt_enable, NULL, 0); 202 printk(KERN_INFO 203 "PowerPC Book-E Watchdog Timer Enabled (wdt_period=%d)\n", 204 booke_wdt_period); 205 } 206 spin_unlock(&booke_wdt_lock); 207 208 return nonseekable_open(inode, file); 209} 210 211static int booke_wdt_release(struct inode *inode, struct file *file) 212{ 213 on_each_cpu(__booke_wdt_disable, NULL, 0); 214 booke_wdt_enabled = 0; 215 216 return 0; 217} 218 219static const struct file_operations booke_wdt_fops = { 220 .owner = THIS_MODULE, 221 .llseek = no_llseek, 222 .write = booke_wdt_write, 223 .unlocked_ioctl = booke_wdt_ioctl, 224 .open = booke_wdt_open, 225 .release = booke_wdt_release, 226}; 227 228static struct miscdevice booke_wdt_miscdev = { 229 .minor = WATCHDOG_MINOR, 230 .name = "watchdog", 231 .fops = &booke_wdt_fops, 232}; 233 234static void __exit booke_wdt_exit(void) 235{ 236 misc_deregister(&booke_wdt_miscdev); 237} 238 239static int __init booke_wdt_init(void) 240{ 241 int ret = 0; 242 243 printk(KERN_INFO "PowerPC Book-E Watchdog Timer Loaded\n"); 244 ident.firmware_version = cur_cpu_spec->pvr_value; 245 246 ret = misc_register(&booke_wdt_miscdev); 247 if (ret) { 248 printk(KERN_CRIT "Cannot register miscdev on minor=%d: %d\n", 249 WATCHDOG_MINOR, ret); 250 return ret; 251 } 252 253 spin_lock(&booke_wdt_lock); 254 if (booke_wdt_enabled == 1) { 255 printk(KERN_INFO 256 "PowerPC Book-E Watchdog Timer Enabled (wdt_period=%d)\n", 257 booke_wdt_period); 258 on_each_cpu(__booke_wdt_enable, NULL, 0); 259 } 260 spin_unlock(&booke_wdt_lock); 261 262 return ret; 263} 264 265module_init(booke_wdt_init); 266module_exit(booke_wdt_exit); 267 268MODULE_DESCRIPTION("PowerPC Book-E watchdog driver"); 269MODULE_LICENSE("GPL");