Blame view
drivers/watchdog/orion_wdt.c
6.6 KB
22ac92322 [WATCHDOG] Orion:... |
1 |
/* |
3b937a7db [ARM] Orion/Kirkw... |
2 |
* drivers/watchdog/orion_wdt.c |
22ac92322 [WATCHDOG] Orion:... |
3 |
* |
3b937a7db [ARM] Orion/Kirkw... |
4 |
* Watchdog driver for Orion/Kirkwood processors |
22ac92322 [WATCHDOG] Orion:... |
5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
* * Author: Sylver Bruneau <sylver.bruneau@googlemail.com> * * This file is licensed under the terms of the GNU General Public * License version 2. This program is licensed "as is" without any * warranty of any kind, whether express or implied. */ #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/types.h> #include <linux/kernel.h> #include <linux/fs.h> #include <linux/miscdevice.h> |
9e058d4f5 [WATCHDOG] orion5... |
19 |
#include <linux/platform_device.h> |
22ac92322 [WATCHDOG] Orion:... |
20 21 22 23 |
#include <linux/watchdog.h> #include <linux/init.h> #include <linux/uaccess.h> #include <linux/io.h> |
6d0f0dfdb [WATCHDOG] orion5... |
24 |
#include <linux/spinlock.h> |
fdd8b079e [ARM] 5460/1: Ori... |
25 |
#include <mach/bridge-regs.h> |
3b937a7db [ARM] Orion/Kirkw... |
26 |
#include <plat/orion_wdt.h> |
22ac92322 [WATCHDOG] Orion:... |
27 28 29 30 31 32 33 |
/* * Watchdog timer block registers. */ #define TIMER_CTRL (TIMER_VIRT_BASE + 0x0000) #define WDT_EN 0x0010 #define WDT_VAL (TIMER_VIRT_BASE + 0x0024) |
9e058d4f5 [WATCHDOG] orion5... |
34 |
#define WDT_MAX_CYCLE_COUNT 0xffffffff |
22ac92322 [WATCHDOG] Orion:... |
35 36 37 38 |
#define WDT_IN_USE 0 #define WDT_OK_TO_CLOSE 1 static int nowayout = WATCHDOG_NOWAYOUT; |
9e058d4f5 [WATCHDOG] orion5... |
39 40 41 |
static int heartbeat = -1; /* module parameter (seconds) */ static unsigned int wdt_max_duration; /* (seconds) */ static unsigned int wdt_tclk; |
22ac92322 [WATCHDOG] Orion:... |
42 |
static unsigned long wdt_status; |
1334f3293 watchdog: Use DEF... |
43 |
static DEFINE_SPINLOCK(wdt_lock); |
22ac92322 [WATCHDOG] Orion:... |
44 |
|
3b937a7db [ARM] Orion/Kirkw... |
45 |
static void orion_wdt_ping(void) |
df6707b21 [WATCHDOG] orion5... |
46 47 48 49 50 51 52 53 |
{ spin_lock(&wdt_lock); /* Reload watchdog duration */ writel(wdt_tclk * heartbeat, WDT_VAL); spin_unlock(&wdt_lock); } |
3b937a7db [ARM] Orion/Kirkw... |
54 |
static void orion_wdt_enable(void) |
22ac92322 [WATCHDOG] Orion:... |
55 56 |
{ u32 reg; |
6d0f0dfdb [WATCHDOG] orion5... |
57 |
spin_lock(&wdt_lock); |
22ac92322 [WATCHDOG] Orion:... |
58 |
/* Set watchdog duration */ |
9e058d4f5 [WATCHDOG] orion5... |
59 |
writel(wdt_tclk * heartbeat, WDT_VAL); |
22ac92322 [WATCHDOG] Orion:... |
60 61 62 63 64 65 66 67 68 69 70 71 |
/* Clear watchdog timer interrupt */ reg = readl(BRIDGE_CAUSE); reg &= ~WDT_INT_REQ; writel(reg, BRIDGE_CAUSE); /* Enable watchdog timer */ reg = readl(TIMER_CTRL); reg |= WDT_EN; writel(reg, TIMER_CTRL); /* Enable reset on watchdog */ |
6462c6160 [ARM] orion5x: Ch... |
72 73 74 |
reg = readl(RSTOUTn_MASK); reg |= WDT_RESET_OUT_EN; writel(reg, RSTOUTn_MASK); |
6d0f0dfdb [WATCHDOG] orion5... |
75 76 |
spin_unlock(&wdt_lock); |
22ac92322 [WATCHDOG] Orion:... |
77 |
} |
3b937a7db [ARM] Orion/Kirkw... |
78 |
static void orion_wdt_disable(void) |
22ac92322 [WATCHDOG] Orion:... |
79 80 |
{ u32 reg; |
6d0f0dfdb [WATCHDOG] orion5... |
81 |
spin_lock(&wdt_lock); |
22ac92322 [WATCHDOG] Orion:... |
82 |
/* Disable reset on watchdog */ |
6462c6160 [ARM] orion5x: Ch... |
83 84 85 |
reg = readl(RSTOUTn_MASK); reg &= ~WDT_RESET_OUT_EN; writel(reg, RSTOUTn_MASK); |
22ac92322 [WATCHDOG] Orion:... |
86 87 88 89 90 |
/* Disable watchdog timer */ reg = readl(TIMER_CTRL); reg &= ~WDT_EN; writel(reg, TIMER_CTRL); |
6d0f0dfdb [WATCHDOG] orion5... |
91 92 93 |
spin_unlock(&wdt_lock); } |
3b937a7db [ARM] Orion/Kirkw... |
94 |
static int orion_wdt_get_timeleft(int *time_left) |
6d0f0dfdb [WATCHDOG] orion5... |
95 96 |
{ spin_lock(&wdt_lock); |
9e058d4f5 [WATCHDOG] orion5... |
97 |
*time_left = readl(WDT_VAL) / wdt_tclk; |
6d0f0dfdb [WATCHDOG] orion5... |
98 99 |
spin_unlock(&wdt_lock); return 0; |
22ac92322 [WATCHDOG] Orion:... |
100 |
} |
3b937a7db [ARM] Orion/Kirkw... |
101 |
static int orion_wdt_open(struct inode *inode, struct file *file) |
22ac92322 [WATCHDOG] Orion:... |
102 103 104 105 |
{ if (test_and_set_bit(WDT_IN_USE, &wdt_status)) return -EBUSY; clear_bit(WDT_OK_TO_CLOSE, &wdt_status); |
3b937a7db [ARM] Orion/Kirkw... |
106 |
orion_wdt_enable(); |
22ac92322 [WATCHDOG] Orion:... |
107 108 |
return nonseekable_open(inode, file); } |
3b937a7db [ARM] Orion/Kirkw... |
109 |
static ssize_t orion_wdt_write(struct file *file, const char *data, |
22ac92322 [WATCHDOG] Orion:... |
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 |
size_t len, loff_t *ppos) { if (len) { if (!nowayout) { size_t i; clear_bit(WDT_OK_TO_CLOSE, &wdt_status); for (i = 0; i != len; i++) { char c; if (get_user(c, data + i)) return -EFAULT; if (c == 'V') set_bit(WDT_OK_TO_CLOSE, &wdt_status); } } |
3b937a7db [ARM] Orion/Kirkw... |
126 |
orion_wdt_ping(); |
22ac92322 [WATCHDOG] Orion:... |
127 128 129 |
} return len; } |
3b937a7db [ARM] Orion/Kirkw... |
130 |
static int orion_wdt_settimeout(int new_time) |
df6707b21 [WATCHDOG] orion5... |
131 132 133 134 135 |
{ if ((new_time <= 0) || (new_time > wdt_max_duration)) return -EINVAL; /* Set new watchdog time to be used when |
3b937a7db [ARM] Orion/Kirkw... |
136 |
* orion_wdt_enable() or orion_wdt_ping() is called. */ |
df6707b21 [WATCHDOG] orion5... |
137 138 139 140 141 |
heartbeat = new_time; return 0; } static const struct watchdog_info ident = { |
22ac92322 [WATCHDOG] Orion:... |
142 143 |
.options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, |
3b937a7db [ARM] Orion/Kirkw... |
144 |
.identity = "Orion Watchdog", |
22ac92322 [WATCHDOG] Orion:... |
145 |
}; |
3b937a7db [ARM] Orion/Kirkw... |
146 |
static long orion_wdt_ioctl(struct file *file, unsigned int cmd, |
22ac92322 [WATCHDOG] Orion:... |
147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 |
unsigned long arg) { int ret = -ENOTTY; int time; switch (cmd) { case WDIOC_GETSUPPORT: ret = copy_to_user((struct watchdog_info *)arg, &ident, sizeof(ident)) ? -EFAULT : 0; break; case WDIOC_GETSTATUS: case WDIOC_GETBOOTSTATUS: ret = put_user(0, (int *)arg); break; case WDIOC_KEEPALIVE: |
3b937a7db [ARM] Orion/Kirkw... |
164 |
orion_wdt_ping(); |
22ac92322 [WATCHDOG] Orion:... |
165 166 167 168 169 170 171 |
ret = 0; break; case WDIOC_SETTIMEOUT: ret = get_user(time, (int *)arg); if (ret) break; |
3b937a7db [ARM] Orion/Kirkw... |
172 |
if (orion_wdt_settimeout(time)) { |
22ac92322 [WATCHDOG] Orion:... |
173 174 175 |
ret = -EINVAL; break; } |
3b937a7db [ARM] Orion/Kirkw... |
176 |
orion_wdt_ping(); |
22ac92322 [WATCHDOG] Orion:... |
177 178 179 180 181 182 183 |
/* Fall through */ case WDIOC_GETTIMEOUT: ret = put_user(heartbeat, (int *)arg); break; case WDIOC_GETTIMELEFT: |
3b937a7db [ARM] Orion/Kirkw... |
184 |
if (orion_wdt_get_timeleft(&time)) { |
22ac92322 [WATCHDOG] Orion:... |
185 186 187 188 189 190 191 192 |
ret = -EINVAL; break; } ret = put_user(time, (int *)arg); break; } return ret; } |
3b937a7db [ARM] Orion/Kirkw... |
193 |
static int orion_wdt_release(struct inode *inode, struct file *file) |
22ac92322 [WATCHDOG] Orion:... |
194 195 |
{ if (test_bit(WDT_OK_TO_CLOSE, &wdt_status)) |
3b937a7db [ARM] Orion/Kirkw... |
196 |
orion_wdt_disable(); |
22ac92322 [WATCHDOG] Orion:... |
197 198 199 200 201 202 203 204 205 |
else printk(KERN_CRIT "WATCHDOG: Device closed unexpectedly - " "timer will not stop "); clear_bit(WDT_IN_USE, &wdt_status); clear_bit(WDT_OK_TO_CLOSE, &wdt_status); return 0; } |
3b937a7db [ARM] Orion/Kirkw... |
206 |
static const struct file_operations orion_wdt_fops = { |
22ac92322 [WATCHDOG] Orion:... |
207 208 |
.owner = THIS_MODULE, .llseek = no_llseek, |
3b937a7db [ARM] Orion/Kirkw... |
209 210 211 212 |
.write = orion_wdt_write, .unlocked_ioctl = orion_wdt_ioctl, .open = orion_wdt_open, .release = orion_wdt_release, |
22ac92322 [WATCHDOG] Orion:... |
213 |
}; |
3b937a7db [ARM] Orion/Kirkw... |
214 |
static struct miscdevice orion_wdt_miscdev = { |
22ac92322 [WATCHDOG] Orion:... |
215 216 |
.minor = WATCHDOG_MINOR, .name = "watchdog", |
3b937a7db [ARM] Orion/Kirkw... |
217 |
.fops = &orion_wdt_fops, |
22ac92322 [WATCHDOG] Orion:... |
218 |
}; |
3b937a7db [ARM] Orion/Kirkw... |
219 |
static int __devinit orion_wdt_probe(struct platform_device *pdev) |
22ac92322 [WATCHDOG] Orion:... |
220 |
{ |
3b937a7db [ARM] Orion/Kirkw... |
221 |
struct orion_wdt_platform_data *pdata = pdev->dev.platform_data; |
22ac92322 [WATCHDOG] Orion:... |
222 |
int ret; |
9e058d4f5 [WATCHDOG] orion5... |
223 224 225 |
if (pdata) { wdt_tclk = pdata->tclk; } else { |
3b937a7db [ARM] Orion/Kirkw... |
226 227 |
printk(KERN_ERR "Orion Watchdog misses platform data "); |
9e058d4f5 [WATCHDOG] orion5... |
228 229 |
return -ENODEV; } |
3b937a7db [ARM] Orion/Kirkw... |
230 |
if (orion_wdt_miscdev.parent) |
9e058d4f5 [WATCHDOG] orion5... |
231 |
return -EBUSY; |
3b937a7db [ARM] Orion/Kirkw... |
232 |
orion_wdt_miscdev.parent = &pdev->dev; |
9e058d4f5 [WATCHDOG] orion5... |
233 234 |
wdt_max_duration = WDT_MAX_CYCLE_COUNT / wdt_tclk; |
3b937a7db [ARM] Orion/Kirkw... |
235 |
if (orion_wdt_settimeout(heartbeat)) |
9e058d4f5 [WATCHDOG] orion5... |
236 |
heartbeat = wdt_max_duration; |
6d0f0dfdb [WATCHDOG] orion5... |
237 |
|
3b937a7db [ARM] Orion/Kirkw... |
238 |
ret = misc_register(&orion_wdt_miscdev); |
9e058d4f5 [WATCHDOG] orion5... |
239 240 |
if (ret) return ret; |
3b937a7db [ARM] Orion/Kirkw... |
241 242 |
printk(KERN_INFO "Orion Watchdog Timer: Initial timeout %d sec%s ", |
9e058d4f5 [WATCHDOG] orion5... |
243 244 245 |
heartbeat, nowayout ? ", nowayout" : ""); return 0; } |
3b937a7db [ARM] Orion/Kirkw... |
246 |
static int __devexit orion_wdt_remove(struct platform_device *pdev) |
9e058d4f5 [WATCHDOG] orion5... |
247 248 249 250 |
{ int ret; if (test_bit(WDT_IN_USE, &wdt_status)) { |
3b937a7db [ARM] Orion/Kirkw... |
251 |
orion_wdt_disable(); |
9e058d4f5 [WATCHDOG] orion5... |
252 253 |
clear_bit(WDT_IN_USE, &wdt_status); } |
3b937a7db [ARM] Orion/Kirkw... |
254 |
ret = misc_deregister(&orion_wdt_miscdev); |
9e058d4f5 [WATCHDOG] orion5... |
255 |
if (!ret) |
3b937a7db [ARM] Orion/Kirkw... |
256 |
orion_wdt_miscdev.parent = NULL; |
22ac92322 [WATCHDOG] Orion:... |
257 258 259 |
return ret; } |
3b937a7db [ARM] Orion/Kirkw... |
260 |
static void orion_wdt_shutdown(struct platform_device *pdev) |
df6707b21 [WATCHDOG] orion5... |
261 262 |
{ if (test_bit(WDT_IN_USE, &wdt_status)) |
3b937a7db [ARM] Orion/Kirkw... |
263 |
orion_wdt_disable(); |
df6707b21 [WATCHDOG] orion5... |
264 |
} |
3b937a7db [ARM] Orion/Kirkw... |
265 266 267 268 |
static struct platform_driver orion_wdt_driver = { .probe = orion_wdt_probe, .remove = __devexit_p(orion_wdt_remove), .shutdown = orion_wdt_shutdown, |
9e058d4f5 [WATCHDOG] orion5... |
269 270 |
.driver = { .owner = THIS_MODULE, |
3b937a7db [ARM] Orion/Kirkw... |
271 |
.name = "orion_wdt", |
9e058d4f5 [WATCHDOG] orion5... |
272 273 |
}, }; |
b8ec61189 watchdog: convert... |
274 |
module_platform_driver(orion_wdt_driver); |
22ac92322 [WATCHDOG] Orion:... |
275 276 |
MODULE_AUTHOR("Sylver Bruneau <sylver.bruneau@googlemail.com>"); |
3b937a7db [ARM] Orion/Kirkw... |
277 |
MODULE_DESCRIPTION("Orion Processor Watchdog"); |
22ac92322 [WATCHDOG] Orion:... |
278 279 |
module_param(heartbeat, int, 0); |
df6707b21 [WATCHDOG] orion5... |
280 |
MODULE_PARM_DESC(heartbeat, "Initial watchdog heartbeat in seconds"); |
22ac92322 [WATCHDOG] Orion:... |
281 282 |
module_param(nowayout, int, 0); |
df6707b21 [WATCHDOG] orion5... |
283 284 |
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); |
22ac92322 [WATCHDOG] Orion:... |
285 286 287 |
MODULE_LICENSE("GPL"); MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); |