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