aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/watchdog
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/watchdog')
-rw-r--r--drivers/watchdog/adx_wdt.c355
-rw-r--r--drivers/watchdog/ixp2000_wdt.c215
-rw-r--r--drivers/watchdog/tegra_wdt.c491
-rw-r--r--drivers/watchdog/watchdog_dev.h33
4 files changed, 1094 insertions, 0 deletions
diff --git a/drivers/watchdog/adx_wdt.c b/drivers/watchdog/adx_wdt.c
new file mode 100644
index 00000000000..af6e6b16475
--- /dev/null
+++ b/drivers/watchdog/adx_wdt.c
@@ -0,0 +1,355 @@
1/*
2 * Copyright (C) 2008-2009 Avionic Design GmbH
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 */
8
9#include <linux/fs.h>
10#include <linux/gfp.h>
11#include <linux/io.h>
12#include <linux/miscdevice.h>
13#include <linux/module.h>
14#include <linux/platform_device.h>
15#include <linux/types.h>
16#include <linux/uaccess.h>
17#include <linux/watchdog.h>
18
19#define WATCHDOG_NAME "adx-wdt"
20
21/* register offsets */
22#define ADX_WDT_CONTROL 0x00
23#define ADX_WDT_CONTROL_ENABLE (1 << 0)
24#define ADX_WDT_CONTROL_nRESET (1 << 1)
25#define ADX_WDT_TIMEOUT 0x08
26
27static struct platform_device *adx_wdt_dev;
28static unsigned long driver_open;
29
30#define WDT_STATE_STOP 0
31#define WDT_STATE_START 1
32
33struct adx_wdt {
34 void __iomem *base;
35 unsigned long timeout;
36 unsigned int state;
37 unsigned int wake;
38 spinlock_t lock;
39};
40
41static const struct watchdog_info adx_wdt_info = {
42 .identity = "Avionic Design Xanthos Watchdog",
43 .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
44};
45
46static void adx_wdt_start_locked(struct adx_wdt *wdt)
47{
48 u32 ctrl;
49
50 ctrl = readl(wdt->base + ADX_WDT_CONTROL);
51 ctrl |= ADX_WDT_CONTROL_ENABLE;
52 writel(ctrl, wdt->base + ADX_WDT_CONTROL);
53 wdt->state = WDT_STATE_START;
54}
55
56static void adx_wdt_start(struct adx_wdt *wdt)
57{
58 unsigned long flags;
59
60 spin_lock_irqsave(&wdt->lock, flags);
61 adx_wdt_start_locked(wdt);
62 spin_unlock_irqrestore(&wdt->lock, flags);
63}
64
65static void adx_wdt_stop_locked(struct adx_wdt *wdt)
66{
67 u32 ctrl;
68
69 ctrl = readl(wdt->base + ADX_WDT_CONTROL);
70 ctrl &= ~ADX_WDT_CONTROL_ENABLE;
71 writel(ctrl, wdt->base + ADX_WDT_CONTROL);
72 wdt->state = WDT_STATE_STOP;
73}
74
75static void adx_wdt_stop(struct adx_wdt *wdt)
76{
77 unsigned long flags;
78
79 spin_lock_irqsave(&wdt->lock, flags);
80 adx_wdt_stop_locked(wdt);
81 spin_unlock_irqrestore(&wdt->lock, flags);
82}
83
84static void adx_wdt_set_timeout(struct adx_wdt *wdt, unsigned long seconds)
85{
86 unsigned long timeout = seconds * 1000;
87 unsigned long flags;
88 unsigned int state;
89
90 spin_lock_irqsave(&wdt->lock, flags);
91 state = wdt->state;
92 adx_wdt_stop_locked(wdt);
93 writel(timeout, wdt->base + ADX_WDT_TIMEOUT);
94
95 if (state == WDT_STATE_START)
96 adx_wdt_start_locked(wdt);
97
98 wdt->timeout = timeout;
99 spin_unlock_irqrestore(&wdt->lock, flags);
100}
101
102static void adx_wdt_get_timeout(struct adx_wdt *wdt, unsigned long *seconds)
103{
104 *seconds = wdt->timeout / 1000;
105}
106
107static void adx_wdt_keepalive(struct adx_wdt *wdt)
108{
109 unsigned long flags;
110
111 spin_lock_irqsave(&wdt->lock, flags);
112 writel(wdt->timeout, wdt->base + ADX_WDT_TIMEOUT);
113 spin_unlock_irqrestore(&wdt->lock, flags);
114}
115
116static int adx_wdt_open(struct inode *inode, struct file *file)
117{
118 struct adx_wdt *wdt = platform_get_drvdata(adx_wdt_dev);
119
120 if (test_and_set_bit(0, &driver_open))
121 return -EBUSY;
122
123 file->private_data = wdt;
124 adx_wdt_set_timeout(wdt, 30);
125 adx_wdt_start(wdt);
126
127 return nonseekable_open(inode, file);
128}
129
130static int adx_wdt_release(struct inode *inode, struct file *file)
131{
132 struct adx_wdt *wdt = file->private_data;
133
134 adx_wdt_stop(wdt);
135 clear_bit(0, &driver_open);
136
137 return 0;
138}
139
140static long adx_wdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
141{
142 struct adx_wdt *wdt = file->private_data;
143 void __user *argp = (void __user *)arg;
144 unsigned long __user *p = argp;
145 unsigned long seconds = 0;
146 unsigned int options;
147 long ret = -EINVAL;
148
149 switch (cmd) {
150 case WDIOC_GETSUPPORT:
151 if (copy_to_user(argp, &adx_wdt_info, sizeof(adx_wdt_info)))
152 return -EFAULT;
153 else
154 return 0;
155
156 case WDIOC_GETSTATUS:
157 case WDIOC_GETBOOTSTATUS:
158 return put_user(0, p);
159
160 case WDIOC_KEEPALIVE:
161 adx_wdt_keepalive(wdt);
162 return 0;
163
164 case WDIOC_SETTIMEOUT:
165 if (get_user(seconds, p))
166 return -EFAULT;
167
168 adx_wdt_set_timeout(wdt, seconds);
169
170 /* fallthrough */
171 case WDIOC_GETTIMEOUT:
172 adx_wdt_get_timeout(wdt, &seconds);
173 return put_user(seconds, p);
174
175 case WDIOC_SETOPTIONS:
176 if (copy_from_user(&options, argp, sizeof(options)))
177 return -EFAULT;
178
179 if (options & WDIOS_DISABLECARD) {
180 adx_wdt_stop(wdt);
181 ret = 0;
182 }
183
184 if (options & WDIOS_ENABLECARD) {
185 adx_wdt_start(wdt);
186 ret = 0;
187 }
188
189 return ret;
190
191 default:
192 break;
193 }
194
195 return -ENOTTY;
196}
197
198static ssize_t adx_wdt_write(struct file *file, const char __user *data,
199 size_t len, loff_t *ppos)
200{
201 struct adx_wdt *wdt = file->private_data;
202
203 if (len)
204 adx_wdt_keepalive(wdt);
205
206 return len;
207}
208
209static const struct file_operations adx_wdt_fops = {
210 .owner = THIS_MODULE,
211 .llseek = no_llseek,
212 .open = adx_wdt_open,
213 .release = adx_wdt_release,
214 .unlocked_ioctl = adx_wdt_ioctl,
215 .write = adx_wdt_write,
216};
217
218static struct miscdevice adx_wdt_miscdev = {
219 .minor = WATCHDOG_MINOR,
220 .name = "watchdog",
221 .fops = &adx_wdt_fops,
222};
223
224static int __devinit adx_wdt_probe(struct platform_device *pdev)
225{
226 struct resource *res;
227 struct adx_wdt *wdt;
228 int ret = 0;
229 u32 ctrl;
230
231 wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL);
232 if (!wdt) {
233 dev_err(&pdev->dev, "cannot allocate WDT structure\n");
234 return -ENOMEM;
235 }
236
237 spin_lock_init(&wdt->lock);
238
239 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
240 if (!res) {
241 dev_err(&pdev->dev, "cannot obtain I/O memory region\n");
242 return -ENXIO;
243 }
244
245 res = devm_request_mem_region(&pdev->dev, res->start,
246 resource_size(res), res->name);
247 if (!res) {
248 dev_err(&pdev->dev, "cannot request I/O memory region\n");
249 return -ENXIO;
250 }
251
252 wdt->base = devm_ioremap_nocache(&pdev->dev, res->start,
253 resource_size(res));
254 if (!wdt->base) {
255 dev_err(&pdev->dev, "cannot remap I/O memory region\n");
256 return -ENXIO;
257 }
258
259 /* disable watchdog and reboot on timeout */
260 ctrl = readl(wdt->base + ADX_WDT_CONTROL);
261 ctrl &= ~ADX_WDT_CONTROL_ENABLE;
262 ctrl &= ~ADX_WDT_CONTROL_nRESET;
263 writel(ctrl, wdt->base + ADX_WDT_CONTROL);
264
265 platform_set_drvdata(pdev, wdt);
266 adx_wdt_dev = pdev;
267
268 ret = misc_register(&adx_wdt_miscdev);
269 if (ret) {
270 dev_err(&pdev->dev, "cannot register miscdev on minor %d "
271 "(err=%d)\n", WATCHDOG_MINOR, ret);
272 return ret;
273 }
274
275 return 0;
276}
277
278static int __devexit adx_wdt_remove(struct platform_device *pdev)
279{
280 struct adx_wdt *wdt = platform_get_drvdata(pdev);
281
282 misc_deregister(&adx_wdt_miscdev);
283 adx_wdt_stop(wdt);
284 platform_set_drvdata(pdev, NULL);
285
286 return 0;
287}
288
289static void adx_wdt_shutdown(struct platform_device *pdev)
290{
291 struct adx_wdt *wdt = platform_get_drvdata(pdev);
292 adx_wdt_stop(wdt);
293}
294
295#ifdef CONFIG_PM
296static int adx_wdt_suspend(struct device *dev)
297{
298 struct platform_device *pdev = to_platform_device(dev);
299 struct adx_wdt *wdt = platform_get_drvdata(pdev);
300
301 wdt->wake = (wdt->state == WDT_STATE_START) ? 1 : 0;
302 adx_wdt_stop(wdt);
303
304 return 0;
305}
306
307static int adx_wdt_resume(struct device *dev)
308{
309 struct platform_device *pdev = to_platform_device(dev);
310 struct adx_wdt *wdt = platform_get_drvdata(pdev);
311
312 if (wdt->wake)
313 adx_wdt_start(wdt);
314
315 return 0;
316}
317
318static const struct dev_pm_ops adx_wdt_pm_ops = {
319 .suspend = adx_wdt_suspend,
320 .resume = adx_wdt_resume,
321};
322
323# define ADX_WDT_PM_OPS (&adx_wdt_pm_ops)
324#else
325# define ADX_WDT_PM_OPS NULL
326#endif
327
328static struct platform_driver adx_wdt_driver = {
329 .probe = adx_wdt_probe,
330 .remove = __devexit_p(adx_wdt_remove),
331 .shutdown = adx_wdt_shutdown,
332 .driver = {
333 .name = WATCHDOG_NAME,
334 .owner = THIS_MODULE,
335 .pm = ADX_WDT_PM_OPS,
336 },
337};
338
339static int __init adx_wdt_init(void)
340{
341 return platform_driver_register(&adx_wdt_driver);
342}
343
344static void __exit adx_wdt_exit(void)
345{
346 platform_driver_unregister(&adx_wdt_driver);
347}
348
349module_init(adx_wdt_init);
350module_exit(adx_wdt_exit);
351
352MODULE_DESCRIPTION("Avionic Design Xanthos Watchdog Driver");
353MODULE_LICENSE("GPL v2");
354MODULE_AUTHOR("Thierry Reding <thierry.reding@avionic-design.de>");
355MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/ixp2000_wdt.c b/drivers/watchdog/ixp2000_wdt.c
new file mode 100644
index 00000000000..e86952a7168
--- /dev/null
+++ b/drivers/watchdog/ixp2000_wdt.c
@@ -0,0 +1,215 @@
1/*
2 * drivers/char/watchdog/ixp2000_wdt.c
3 *
4 * Watchdog driver for Intel IXP2000 network processors
5 *
6 * Adapted from the IXP4xx watchdog driver by Lennert Buytenhek.
7 * The original version carries these notices:
8 *
9 * Author: Deepak Saxena <dsaxena@plexity.net>
10 *
11 * Copyright 2004 (c) MontaVista, Software, Inc.
12 * Based on sa1100 driver, Copyright (C) 2000 Oleg Drokin <green@crimea.edu>
13 *
14 * This file is licensed under the terms of the GNU General Public
15 * License version 2. This program is licensed "as is" without any
16 * warranty of any kind, whether express or implied.
17 */
18
19#include <linux/module.h>
20#include <linux/moduleparam.h>
21#include <linux/types.h>
22#include <linux/timer.h>
23#include <linux/kernel.h>
24#include <linux/fs.h>
25#include <linux/miscdevice.h>
26#include <linux/watchdog.h>
27#include <linux/init.h>
28#include <linux/bitops.h>
29#include <linux/uaccess.h>
30#include <mach/hardware.h>
31
32static int nowayout = WATCHDOG_NOWAYOUT;
33static unsigned int heartbeat = 60; /* (secs) Default is 1 minute */
34static unsigned long wdt_status;
35static spinlock_t wdt_lock;
36
37#define WDT_IN_USE 0
38#define WDT_OK_TO_CLOSE 1
39
40static unsigned long wdt_tick_rate;
41
42static void wdt_enable(void)
43{
44 spin_lock(&wdt_lock);
45 ixp2000_reg_write(IXP2000_RESET0, *(IXP2000_RESET0) | WDT_RESET_ENABLE);
46 ixp2000_reg_write(IXP2000_TWDE, WDT_ENABLE);
47 ixp2000_reg_write(IXP2000_T4_CLD, heartbeat * wdt_tick_rate);
48 ixp2000_reg_write(IXP2000_T4_CTL, TIMER_DIVIDER_256 | TIMER_ENABLE);
49 spin_unlock(&wdt_lock);
50}
51
52static void wdt_disable(void)
53{
54 spin_lock(&wdt_lock);
55 ixp2000_reg_write(IXP2000_T4_CTL, 0);
56 spin_unlock(&wdt_lock);
57}
58
59static void wdt_keepalive(void)
60{
61 spin_lock(&wdt_lock);
62 ixp2000_reg_write(IXP2000_T4_CLD, heartbeat * wdt_tick_rate);
63 spin_unlock(&wdt_lock);
64}
65
66static int ixp2000_wdt_open(struct inode *inode, struct file *file)
67{
68 if (test_and_set_bit(WDT_IN_USE, &wdt_status))
69 return -EBUSY;
70
71 clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
72
73 wdt_enable();
74
75 return nonseekable_open(inode, file);
76}
77
78static ssize_t ixp2000_wdt_write(struct file *file, const char *data,
79 size_t len, loff_t *ppos)
80{
81 if (len) {
82 if (!nowayout) {
83 size_t i;
84
85 clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
86
87 for (i = 0; i != len; i++) {
88 char c;
89
90 if (get_user(c, data + i))
91 return -EFAULT;
92 if (c == 'V')
93 set_bit(WDT_OK_TO_CLOSE, &wdt_status);
94 }
95 }
96 wdt_keepalive();
97 }
98
99 return len;
100}
101
102
103static const struct watchdog_info ident = {
104 .options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT |
105 WDIOF_KEEPALIVEPING,
106 .identity = "IXP2000 Watchdog",
107};
108
109static long ixp2000_wdt_ioctl(struct file *file, unsigned int cmd,
110 unsigned long arg)
111{
112 int ret = -ENOTTY;
113 int time;
114
115 switch (cmd) {
116 case WDIOC_GETSUPPORT:
117 ret = copy_to_user((struct watchdog_info *)arg, &ident,
118 sizeof(ident)) ? -EFAULT : 0;
119 break;
120
121 case WDIOC_GETSTATUS:
122 ret = put_user(0, (int *)arg);
123 break;
124
125 case WDIOC_GETBOOTSTATUS:
126 ret = put_user(0, (int *)arg);
127 break;
128
129 case WDIOC_KEEPALIVE:
130 wdt_enable();
131 ret = 0;
132 break;
133
134 case WDIOC_SETTIMEOUT:
135 ret = get_user(time, (int *)arg);
136 if (ret)
137 break;
138
139 if (time <= 0 || time > 60) {
140 ret = -EINVAL;
141 break;
142 }
143
144 heartbeat = time;
145 wdt_keepalive();
146 /* Fall through */
147
148 case WDIOC_GETTIMEOUT:
149 ret = put_user(heartbeat, (int *)arg);
150 break;
151 }
152
153 return ret;
154}
155
156static int ixp2000_wdt_release(struct inode *inode, struct file *file)
157{
158 if (test_bit(WDT_OK_TO_CLOSE, &wdt_status))
159 wdt_disable();
160 else
161 printk(KERN_CRIT "WATCHDOG: Device closed unexpectedly - "
162 "timer will not stop\n");
163 clear_bit(WDT_IN_USE, &wdt_status);
164 clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
165
166 return 0;
167}
168
169
170static const struct file_operations ixp2000_wdt_fops = {
171 .owner = THIS_MODULE,
172 .llseek = no_llseek,
173 .write = ixp2000_wdt_write,
174 .unlocked_ioctl = ixp2000_wdt_ioctl,
175 .open = ixp2000_wdt_open,
176 .release = ixp2000_wdt_release,
177};
178
179static struct miscdevice ixp2000_wdt_miscdev = {
180 .minor = WATCHDOG_MINOR,
181 .name = "watchdog",
182 .fops = &ixp2000_wdt_fops,
183};
184
185static int __init ixp2000_wdt_init(void)
186{
187 if ((*IXP2000_PRODUCT_ID & 0x001ffef0) == 0x00000000) {
188 printk(KERN_INFO "Unable to use IXP2000 watchdog due to IXP2800 erratum #25.\n");
189 return -EIO;
190 }
191 wdt_tick_rate = (*IXP2000_T1_CLD * HZ) / 256;
192 spin_lock_init(&wdt_lock);
193 return misc_register(&ixp2000_wdt_miscdev);
194}
195
196static void __exit ixp2000_wdt_exit(void)
197{
198 misc_deregister(&ixp2000_wdt_miscdev);
199}
200
201module_init(ixp2000_wdt_init);
202module_exit(ixp2000_wdt_exit);
203
204MODULE_AUTHOR("Deepak Saxena <dsaxena@plexity.net>");
205MODULE_DESCRIPTION("IXP2000 Network Processor Watchdog");
206
207module_param(heartbeat, int, 0);
208MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds (default 60s)");
209
210module_param(nowayout, int, 0);
211MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started");
212
213MODULE_LICENSE("GPL");
214MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
215
diff --git a/drivers/watchdog/tegra_wdt.c b/drivers/watchdog/tegra_wdt.c
new file mode 100644
index 00000000000..4fa31b25a36
--- /dev/null
+++ b/drivers/watchdog/tegra_wdt.c
@@ -0,0 +1,491 @@
1/*
2 * drivers/watchdog/tegra_wdt.c
3 *
4 * watchdog driver for NVIDIA tegra internal watchdog
5 *
6 * Copyright (c) 2011, NVIDIA Corporation.
7 *
8 * based on drivers/watchdog/softdog.c and drivers/watchdog/omap_wdt.c
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful, but WITHOUT
16 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
17 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
18 * more details.
19 *
20 * You should have received a copy of the GNU General Public License along
21 * with this program; if not, write to the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23 */
24
25#include <linux/fs.h>
26#include <linux/interrupt.h>
27#include <linux/io.h>
28#include <linux/kernel.h>
29#include <linux/miscdevice.h>
30#include <linux/notifier.h>
31#include <linux/platform_device.h>
32#include <linux/reboot.h>
33#include <linux/slab.h>
34#include <linux/spinlock.h>
35#include <linux/uaccess.h>
36#include <linux/watchdog.h>
37
38/* minimum and maximum watchdog trigger periods, in seconds */
39#define MIN_WDT_PERIOD 5
40#define MAX_WDT_PERIOD 1000
41
42enum tegra_wdt_status {
43 WDT_DISABLED = 1 << 0,
44 WDT_ENABLED = 1 << 1,
45 WDT_IOCTL_ENABBLED_AT_PROBE = 1 << 2,
46};
47
48struct tegra_wdt {
49 struct miscdevice miscdev;
50 struct notifier_block notifier;
51 struct resource *res_src;
52 struct resource *res_wdt;
53 unsigned long users;
54 void __iomem *wdt_source;
55 void __iomem *wdt_timer;
56 int irq;
57 int timeout;
58 int status;
59};
60
61static struct platform_device *tegra_wdt_dev;
62/*
63 * For spinlock lockup detection to work, the heartbeat should be 2*lockup
64 * for cases where the spinlock disabled irqs.
65 */
66static int heartbeat = 120; /* must be greater than MIN_WDT_PERIOD and lower than MAX_WDT_PERIOD */
67
68#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
69
70#define TIMER_PTV 0x0
71 #define TIMER_EN (1 << 31)
72 #define TIMER_PERIODIC (1 << 30)
73#define TIMER_PCR 0x4
74 #define TIMER_PCR_INTR (1 << 30)
75#define WDT_EN (1 << 5)
76#define WDT_SEL_TMR1 (0 << 4)
77#define WDT_SYS_RST (1 << 2)
78
79static void tegra_wdt_enable(struct tegra_wdt *wdt)
80{
81 u32 val;
82
83 /* since the watchdog reset occurs when a second interrupt
84 * is asserted before the first is processed, program the
85 * timer period to one-half of the watchdog period */
86 val = wdt->timeout * 1000000ul / 2;
87 val |= (TIMER_EN | TIMER_PERIODIC);
88 writel(val, wdt->wdt_timer + TIMER_PTV);
89
90 val = WDT_EN | WDT_SEL_TMR1 | WDT_SYS_RST;
91 writel(val, wdt->wdt_source);
92}
93
94static void tegra_wdt_disable(struct tegra_wdt *wdt)
95{
96 writel(0, wdt->wdt_source);
97 writel(0, wdt->wdt_timer + TIMER_PTV);
98}
99
100static inline void tegra_wdt_ping(struct tegra_wdt *wdt)
101{
102 return;
103}
104
105static irqreturn_t tegra_wdt_interrupt(int irq, void *dev_id)
106{
107 struct tegra_wdt *wdt = dev_id;
108
109 writel(TIMER_PCR_INTR, wdt->wdt_timer + TIMER_PCR);
110 return IRQ_HANDLED;
111}
112#elif defined(CONFIG_ARCH_TEGRA_3x_SOC)
113
114#define TIMER_PTV 0
115 #define TIMER_EN (1 << 31)
116 #define TIMER_PERIODIC (1 << 30)
117#define TIMER_PCR 0x4
118 #define TIMER_PCR_INTR (1 << 30)
119#define WDT_CFG (0)
120 #define WDT_CFG_TMR_SRC (0 << 0) /* for TMR10. */
121 #define WDT_CFG_PERIOD (1 << 4)
122 #define WDT_CFG_INT_EN (1 << 12)
123 #define WDT_CFG_SYS_RST_EN (1 << 14)
124 #define WDT_CFG_PMC2CAR_RST_EN (1 << 15)
125#define WDT_CMD (8)
126 #define WDT_CMD_START_COUNTER (1 << 0)
127 #define WDT_CMD_DISABLE_COUNTER (1 << 1)
128#define WDT_UNLOCK (0xC)
129 #define WDT_UNLOCK_PATTERN (0xC45A << 0)
130
131static void tegra_wdt_set_timeout(struct tegra_wdt *wdt, int sec)
132{
133 u32 ptv;
134
135 ptv = readl(wdt->wdt_timer + TIMER_PTV);
136
137 wdt->timeout = clamp(sec, MIN_WDT_PERIOD, MAX_WDT_PERIOD);
138 if (ptv & TIMER_EN) {
139 /* since the watchdog reset occurs when a fourth interrupt
140 * is asserted before the first is processed, program the
141 * timer period to one-fourth of the watchdog period */
142 ptv = (wdt->timeout * 1000000ul) / 4;
143 ptv |= (TIMER_EN | TIMER_PERIODIC);
144 writel(ptv, wdt->wdt_timer + TIMER_PTV);
145 }
146}
147
148static inline void tegra_wdt_ping(struct tegra_wdt *wdt)
149{
150 writel(WDT_CMD_START_COUNTER, wdt->wdt_source + WDT_CMD);
151}
152
153static void tegra_wdt_enable(struct tegra_wdt *wdt)
154{
155 u32 val;
156
157 writel(TIMER_PCR_INTR, wdt->wdt_timer + TIMER_PCR);
158 val = (wdt->timeout * 1000000ul) / 4;
159 val |= (TIMER_EN | TIMER_PERIODIC);
160 writel(val, wdt->wdt_timer + TIMER_PTV);
161
162 val = WDT_CFG_TMR_SRC | WDT_CFG_PERIOD | /*WDT_CFG_INT_EN |*/
163 /*WDT_CFG_SYS_RST_EN |*/ WDT_CFG_PMC2CAR_RST_EN;
164 writel(val, wdt->wdt_source + WDT_CFG);
165 writel(WDT_CMD_START_COUNTER, wdt->wdt_source + WDT_CMD);
166}
167
168static void tegra_wdt_disable(struct tegra_wdt *wdt)
169{
170 writel(WDT_UNLOCK_PATTERN, wdt->wdt_source + WDT_UNLOCK);
171 writel(WDT_CMD_DISABLE_COUNTER, wdt->wdt_source + WDT_CMD);
172
173 writel(0, wdt->wdt_timer + TIMER_PTV);
174}
175
176static irqreturn_t tegra_wdt_interrupt(int irq, void *dev_id)
177{
178 struct tegra_wdt *wdt = dev_id;
179
180 tegra_wdt_ping(wdt);
181 return IRQ_HANDLED;
182}
183#endif
184
185static int tegra_wdt_notify(struct notifier_block *this,
186 unsigned long code, void *dev)
187{
188 struct tegra_wdt *wdt = container_of(this, struct tegra_wdt, notifier);
189
190 if (code == SYS_DOWN || code == SYS_HALT)
191 tegra_wdt_disable(wdt);
192 return NOTIFY_DONE;
193}
194
195static int tegra_wdt_open(struct inode *inode, struct file *file)
196{
197 struct tegra_wdt *wdt = platform_get_drvdata(tegra_wdt_dev);
198
199 if (test_and_set_bit(1, &wdt->users))
200 return -EBUSY;
201
202 wdt->status |= WDT_ENABLED;
203 wdt->timeout = heartbeat;
204 tegra_wdt_enable(wdt);
205 file->private_data = wdt;
206 return nonseekable_open(inode, file);
207}
208
209static int tegra_wdt_release(struct inode *inode, struct file *file)
210{
211 struct tegra_wdt *wdt = file->private_data;
212
213 if (wdt->status == WDT_ENABLED) {
214#ifndef CONFIG_WATCHDOG_NOWAYOUT
215 tegra_wdt_disable(wdt);
216 wdt->status = WDT_DISABLED;
217#endif
218 }
219 wdt->users = 0;
220 return 0;
221}
222
223static long tegra_wdt_ioctl(struct file *file, unsigned int cmd,
224 unsigned long arg)
225{
226 struct tegra_wdt *wdt = file->private_data;
227 static DEFINE_SPINLOCK(lock);
228 int new_timeout;
229 int option;
230 static const struct watchdog_info ident = {
231 .identity = "Tegra Watchdog",
232 .options = WDIOF_SETTIMEOUT,
233 .firmware_version = 0,
234 };
235
236 switch (cmd) {
237 case WDIOC_GETSUPPORT:
238 return copy_to_user((struct watchdog_info __user *)arg, &ident,
239 sizeof(ident));
240 case WDIOC_GETSTATUS:
241 case WDIOC_GETBOOTSTATUS:
242 return put_user(0, (int __user *)arg);
243
244 case WDIOC_KEEPALIVE:
245 spin_lock(&lock);
246 tegra_wdt_ping(wdt);
247 spin_unlock(&lock);
248 return 0;
249
250 case WDIOC_SETTIMEOUT:
251 if (get_user(new_timeout, (int __user *)arg))
252 return -EFAULT;
253 spin_lock(&lock);
254 tegra_wdt_disable(wdt);
255 wdt->timeout = clamp(new_timeout, MIN_WDT_PERIOD, MAX_WDT_PERIOD);
256 tegra_wdt_enable(wdt);
257 spin_unlock(&lock);
258 case WDIOC_GETTIMEOUT:
259 return put_user(wdt->timeout, (int __user *)arg);
260
261 case WDIOC_SETOPTIONS:
262#ifndef CONFIG_WATCHDOG_NOWAYOUT
263 if (get_user(option, (int __user *)arg))
264 return -EFAULT;
265 spin_lock(&lock);
266 if (option & WDIOS_DISABLECARD) {
267 wdt->status &= ~WDT_ENABLED;
268 wdt->status |= WDT_DISABLED;
269 tegra_wdt_disable(wdt);
270 } else if (option & WDIOS_ENABLECARD) {
271 tegra_wdt_enable(wdt);
272 wdt->status |= WDT_ENABLED;
273 wdt->status &= ~WDT_DISABLED;
274 } else {
275 spin_unlock(&lock);
276 return -EINVAL;
277 }
278 spin_unlock(&lock);
279 return 0;
280#else
281 return -EINVAL;
282#endif
283 }
284 return -ENOTTY;
285}
286
287static ssize_t tegra_wdt_write(struct file *file, const char __user *data,
288 size_t len, loff_t *ppos)
289{
290 return len;
291}
292
293static const struct file_operations tegra_wdt_fops = {
294 .owner = THIS_MODULE,
295 .llseek = no_llseek,
296 .write = tegra_wdt_write,
297 .unlocked_ioctl = tegra_wdt_ioctl,
298 .open = tegra_wdt_open,
299 .release = tegra_wdt_release,
300};
301
302static int tegra_wdt_probe(struct platform_device *pdev)
303{
304 struct resource *res_src, *res_wdt, *res_irq;
305 struct tegra_wdt *wdt;
306 u32 src;
307 int ret = 0;
308
309 if (pdev->id != -1) {
310 dev_err(&pdev->dev, "only id -1 supported\n");
311 return -ENODEV;
312 }
313
314 if (tegra_wdt_dev != NULL) {
315 dev_err(&pdev->dev, "watchdog already registered\n");
316 return -EIO;
317 }
318
319 res_src = platform_get_resource(pdev, IORESOURCE_MEM, 0);
320 res_wdt = platform_get_resource(pdev, IORESOURCE_MEM, 1);
321 res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
322
323 if (!res_src || !res_wdt || !res_irq) {
324 dev_err(&pdev->dev, "incorrect resources\n");
325 return -ENOENT;
326 }
327
328 wdt = kzalloc(sizeof(*wdt), GFP_KERNEL);
329 if (!wdt) {
330 dev_err(&pdev->dev, "out of memory\n");
331 return -ENOMEM;
332 }
333
334 wdt->irq = -1;
335 wdt->miscdev.parent = &pdev->dev;
336 wdt->miscdev.minor = WATCHDOG_MINOR;
337 wdt->miscdev.name = "watchdog";
338 wdt->miscdev.fops = &tegra_wdt_fops;
339
340 wdt->notifier.notifier_call = tegra_wdt_notify;
341
342 res_src = request_mem_region(res_src->start, resource_size(res_src),
343 pdev->name);
344 res_wdt = request_mem_region(res_wdt->start, resource_size(res_wdt),
345 pdev->name);
346
347 if (!res_src || !res_wdt) {
348 dev_err(&pdev->dev, "unable to request memory resources\n");
349 ret = -EBUSY;
350 goto fail;
351 }
352
353 wdt->wdt_source = ioremap(res_src->start, resource_size(res_src));
354 wdt->wdt_timer = ioremap(res_wdt->start, resource_size(res_wdt));
355 if (!wdt->wdt_source || !wdt->wdt_timer) {
356 dev_err(&pdev->dev, "unable to map registers\n");
357 ret = -ENOMEM;
358 goto fail;
359 }
360
361 src = readl(wdt->wdt_source);
362 if (src & BIT(12))
363 dev_info(&pdev->dev, "last reset due to watchdog timeout\n");
364
365 tegra_wdt_disable(wdt);
366 writel(TIMER_PCR_INTR, wdt->wdt_timer + TIMER_PCR);
367
368 ret = request_irq(res_irq->start, tegra_wdt_interrupt, IRQF_DISABLED,
369 dev_name(&pdev->dev), wdt);
370 if (ret) {
371 dev_err(&pdev->dev, "unable to configure IRQ\n");
372 goto fail;
373 }
374
375 wdt->irq = res_irq->start;
376 wdt->res_src = res_src;
377 wdt->res_wdt = res_wdt;
378
379 ret = register_reboot_notifier(&wdt->notifier);
380 if (ret) {
381 dev_err(&pdev->dev, "cannot register reboot notifier\n");
382 goto fail;
383 }
384
385 ret = misc_register(&wdt->miscdev);
386 if (ret) {
387 dev_err(&pdev->dev, "failed to register misc device\n");
388 unregister_reboot_notifier(&wdt->notifier);
389 goto fail;
390 }
391
392 platform_set_drvdata(pdev, wdt);
393 tegra_wdt_dev = pdev;
394#ifdef CONFIG_TEGRA_WATCHDOG_ENABLE_ON_PROBE
395 wdt->status = WDT_ENABLED | WDT_ENABLED_AT_PROBE;
396 wdt->timeout = heartbeat;
397 tegra_wdt_enable(wdt);
398#else
399 wdt->status = WDT_DISABLED;
400#endif
401 return 0;
402fail:
403 if (wdt->irq != -1)
404 free_irq(wdt->irq, wdt);
405 if (wdt->wdt_source)
406 iounmap(wdt->wdt_source);
407 if (wdt->wdt_timer)
408 iounmap(wdt->wdt_timer);
409 if (res_src)
410 release_mem_region(res_src->start, resource_size(res_src));
411 if (res_wdt)
412 release_mem_region(res_wdt->start, resource_size(res_wdt));
413 kfree(wdt);
414 return ret;
415}
416
417static int tegra_wdt_remove(struct platform_device *pdev)
418{
419 struct tegra_wdt *wdt = platform_get_drvdata(pdev);
420
421 tegra_wdt_disable(wdt);
422
423 unregister_reboot_notifier(&wdt->notifier);
424 misc_deregister(&wdt->miscdev);
425 free_irq(wdt->irq, wdt);
426 iounmap(wdt->wdt_source);
427 iounmap(wdt->wdt_timer);
428 release_mem_region(wdt->res_src->start, resource_size(wdt->res_src));
429 release_mem_region(wdt->res_wdt->start, resource_size(wdt->res_wdt));
430 kfree(wdt);
431 platform_set_drvdata(pdev, NULL);
432 return 0;
433}
434
435#ifdef CONFIG_PM
436static int tegra_wdt_suspend(struct platform_device *pdev, pm_message_t state)
437{
438 struct tegra_wdt *wdt = platform_get_drvdata(pdev);
439
440 tegra_wdt_disable(wdt);
441 return 0;
442}
443
444static int tegra_wdt_resume(struct platform_device *pdev)
445{
446 struct tegra_wdt *wdt = platform_get_drvdata(pdev);
447
448 if (wdt->status & WDT_ENABLED)
449 tegra_wdt_enable(wdt);
450
451 return 0;
452}
453#endif
454
455static struct platform_driver tegra_wdt_driver = {
456 .probe = tegra_wdt_probe,
457 .remove = __devexit_p(tegra_wdt_remove),
458#ifdef CONFIG_PM
459 .suspend = tegra_wdt_suspend,
460 .resume = tegra_wdt_resume,
461#endif
462 .driver = {
463 .owner = THIS_MODULE,
464 .name = "tegra_wdt",
465 },
466};
467
468static int __init tegra_wdt_init(void)
469{
470 return platform_driver_register(&tegra_wdt_driver);
471}
472
473static void __exit tegra_wdt_exit(void)
474{
475 platform_driver_unregister(&tegra_wdt_driver);
476}
477
478module_init(tegra_wdt_init);
479module_exit(tegra_wdt_exit);
480
481MODULE_AUTHOR("NVIDIA Corporation");
482MODULE_DESCRIPTION("Tegra Watchdog Driver");
483
484module_param(heartbeat, int, 0);
485MODULE_PARM_DESC(heartbeat,
486 "Watchdog heartbeat period in seconds");
487
488MODULE_LICENSE("GPL");
489MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
490MODULE_ALIAS("platform:tegra_wdt");
491
diff --git a/drivers/watchdog/watchdog_dev.h b/drivers/watchdog/watchdog_dev.h
new file mode 100644
index 00000000000..bc7612be25c
--- /dev/null
+++ b/drivers/watchdog/watchdog_dev.h
@@ -0,0 +1,33 @@
1/*
2 * watchdog_core.h
3 *
4 * (c) Copyright 2008-2011 Alan Cox <alan@lxorguk.ukuu.org.uk>,
5 * All Rights Reserved.
6 *
7 * (c) Copyright 2008-2011 Wim Van Sebroeck <wim@iguana.be>.
8 *
9 * This source code is part of the generic code that can be used
10 * by all the watchdog timer drivers.
11 *
12 * Based on source code of the following authors:
13 * Matt Domsch <Matt_Domsch@dell.com>,
14 * Rob Radez <rob@osinvestor.com>,
15 * Rusty Lynch <rusty@linux.co.intel.com>
16 * Satyam Sharma <satyam@infradead.org>
17 * Randy Dunlap <randy.dunlap@oracle.com>
18 *
19 * This program is free software; you can redistribute it and/or
20 * modify it under the terms of the GNU General Public License
21 * as published by the Free Software Foundation; either version
22 * 2 of the License, or (at your option) any later version.
23 *
24 * Neither Alan Cox, CymruNet Ltd., Wim Van Sebroeck nor Iguana vzw.
25 * admit liability nor provide warranty for any of this software.
26 * This material is provided "AS-IS" and at no charge.
27 */
28
29/*
30 * Functions/procedures to be called by the core
31 */
32int watchdog_dev_register(struct watchdog_device *);
33int watchdog_dev_unregister(struct watchdog_device *);