• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/linux/linux-2.6/drivers/watchdog/
1/*
2 * omap_wdt.c
3 *
4 * Watchdog driver for the TI OMAP 16xx & 24xx/34xx 32KHz (non-secure) watchdog
5 *
6 * Author: MontaVista Software, Inc.
7 *	 <gdavis@mvista.com> or <source@mvista.com>
8 *
9 * 2003 (c) MontaVista Software, Inc. This file is licensed under the
10 * terms of the GNU General Public License version 2. This program is
11 * licensed "as is" without any warranty of any kind, whether express
12 * or implied.
13 *
14 * History:
15 *
16 * 20030527: George G. Davis <gdavis@mvista.com>
17 *	Initially based on linux-2.4.19-rmk7-pxa1/drivers/char/sa1100_wdt.c
18 *	(c) Copyright 2000 Oleg Drokin <green@crimea.edu>
19 *	Based on SoftDog driver by Alan Cox <alan@lxorguk.ukuu.org.uk>
20 *
21 * Copyright (c) 2004 Texas Instruments.
22 *	1. Modified to support OMAP1610 32-KHz watchdog timer
23 *	2. Ported to 2.6 kernel
24 *
25 * Copyright (c) 2005 David Brownell
26 *	Use the driver model and standard identifiers; handle bigger timeouts.
27 */
28
29#include <linux/module.h>
30#include <linux/types.h>
31#include <linux/kernel.h>
32#include <linux/fs.h>
33#include <linux/mm.h>
34#include <linux/miscdevice.h>
35#include <linux/watchdog.h>
36#include <linux/reboot.h>
37#include <linux/init.h>
38#include <linux/err.h>
39#include <linux/platform_device.h>
40#include <linux/moduleparam.h>
41#include <linux/clk.h>
42#include <linux/bitops.h>
43#include <linux/io.h>
44#include <linux/uaccess.h>
45#include <linux/slab.h>
46#include <mach/hardware.h>
47#include <plat/prcm.h>
48
49#include "omap_wdt.h"
50
51static struct platform_device *omap_wdt_dev;
52
53static unsigned timer_margin;
54module_param(timer_margin, uint, 0);
55MODULE_PARM_DESC(timer_margin, "initial watchdog timeout (in seconds)");
56
57static unsigned int wdt_trgr_pattern = 0x1234;
58static spinlock_t wdt_lock;
59
60struct omap_wdt_dev {
61	void __iomem    *base;          /* physical */
62	struct device   *dev;
63	int             omap_wdt_users;
64	struct clk      *ick;
65	struct clk      *fck;
66	struct resource *mem;
67	struct miscdevice omap_wdt_miscdev;
68};
69
70static void omap_wdt_ping(struct omap_wdt_dev *wdev)
71{
72	void __iomem    *base = wdev->base;
73
74	/* wait for posted write to complete */
75	while ((__raw_readl(base + OMAP_WATCHDOG_WPS)) & 0x08)
76		cpu_relax();
77
78	wdt_trgr_pattern = ~wdt_trgr_pattern;
79	__raw_writel(wdt_trgr_pattern, (base + OMAP_WATCHDOG_TGR));
80
81	/* wait for posted write to complete */
82	while ((__raw_readl(base + OMAP_WATCHDOG_WPS)) & 0x08)
83		cpu_relax();
84	/* reloaded WCRR from WLDR */
85}
86
87static void omap_wdt_enable(struct omap_wdt_dev *wdev)
88{
89	void __iomem *base = wdev->base;
90
91	/* Sequence to enable the watchdog */
92	__raw_writel(0xBBBB, base + OMAP_WATCHDOG_SPR);
93	while ((__raw_readl(base + OMAP_WATCHDOG_WPS)) & 0x10)
94		cpu_relax();
95
96	__raw_writel(0x4444, base + OMAP_WATCHDOG_SPR);
97	while ((__raw_readl(base + OMAP_WATCHDOG_WPS)) & 0x10)
98		cpu_relax();
99}
100
101static void omap_wdt_disable(struct omap_wdt_dev *wdev)
102{
103	void __iomem *base = wdev->base;
104
105	/* sequence required to disable watchdog */
106	__raw_writel(0xAAAA, base + OMAP_WATCHDOG_SPR);	/* TIMER_MODE */
107	while (__raw_readl(base + OMAP_WATCHDOG_WPS) & 0x10)
108		cpu_relax();
109
110	__raw_writel(0x5555, base + OMAP_WATCHDOG_SPR);	/* TIMER_MODE */
111	while (__raw_readl(base + OMAP_WATCHDOG_WPS) & 0x10)
112		cpu_relax();
113}
114
115static void omap_wdt_adjust_timeout(unsigned new_timeout)
116{
117	if (new_timeout < TIMER_MARGIN_MIN)
118		new_timeout = TIMER_MARGIN_DEFAULT;
119	if (new_timeout > TIMER_MARGIN_MAX)
120		new_timeout = TIMER_MARGIN_MAX;
121	timer_margin = new_timeout;
122}
123
124static void omap_wdt_set_timeout(struct omap_wdt_dev *wdev)
125{
126	u32 pre_margin = GET_WLDR_VAL(timer_margin);
127	void __iomem *base = wdev->base;
128
129	/* just count up at 32 KHz */
130	while (__raw_readl(base + OMAP_WATCHDOG_WPS) & 0x04)
131		cpu_relax();
132
133	__raw_writel(pre_margin, base + OMAP_WATCHDOG_LDR);
134	while (__raw_readl(base + OMAP_WATCHDOG_WPS) & 0x04)
135		cpu_relax();
136}
137
138/*
139 *	Allow only one task to hold it open
140 */
141static int omap_wdt_open(struct inode *inode, struct file *file)
142{
143	struct omap_wdt_dev *wdev = platform_get_drvdata(omap_wdt_dev);
144	void __iomem *base = wdev->base;
145
146	if (test_and_set_bit(1, (unsigned long *)&(wdev->omap_wdt_users)))
147		return -EBUSY;
148
149	clk_enable(wdev->ick);    /* Enable the interface clock */
150	clk_enable(wdev->fck);    /* Enable the functional clock */
151
152	/* initialize prescaler */
153	while (__raw_readl(base + OMAP_WATCHDOG_WPS) & 0x01)
154		cpu_relax();
155
156	__raw_writel((1 << 5) | (PTV << 2), base + OMAP_WATCHDOG_CNTRL);
157	while (__raw_readl(base + OMAP_WATCHDOG_WPS) & 0x01)
158		cpu_relax();
159
160	file->private_data = (void *) wdev;
161
162	omap_wdt_set_timeout(wdev);
163	omap_wdt_ping(wdev); /* trigger loading of new timeout value */
164	omap_wdt_enable(wdev);
165
166	return nonseekable_open(inode, file);
167}
168
169static int omap_wdt_release(struct inode *inode, struct file *file)
170{
171	struct omap_wdt_dev *wdev = file->private_data;
172
173	/*
174	 *      Shut off the timer unless NOWAYOUT is defined.
175	 */
176#ifndef CONFIG_WATCHDOG_NOWAYOUT
177
178	omap_wdt_disable(wdev);
179
180	clk_disable(wdev->ick);
181	clk_disable(wdev->fck);
182#else
183	printk(KERN_CRIT "omap_wdt: Unexpected close, not stopping!\n");
184#endif
185	wdev->omap_wdt_users = 0;
186
187	return 0;
188}
189
190static ssize_t omap_wdt_write(struct file *file, const char __user *data,
191		size_t len, loff_t *ppos)
192{
193	struct omap_wdt_dev *wdev = file->private_data;
194
195	/* Refresh LOAD_TIME. */
196	if (len) {
197		spin_lock(&wdt_lock);
198		omap_wdt_ping(wdev);
199		spin_unlock(&wdt_lock);
200	}
201	return len;
202}
203
204static long omap_wdt_ioctl(struct file *file, unsigned int cmd,
205						unsigned long arg)
206{
207	struct omap_wdt_dev *wdev;
208	int new_margin;
209	static const struct watchdog_info ident = {
210		.identity = "OMAP Watchdog",
211		.options = WDIOF_SETTIMEOUT,
212		.firmware_version = 0,
213	};
214
215	wdev = file->private_data;
216
217	switch (cmd) {
218	case WDIOC_GETSUPPORT:
219		return copy_to_user((struct watchdog_info __user *)arg, &ident,
220				sizeof(ident));
221	case WDIOC_GETSTATUS:
222		return put_user(0, (int __user *)arg);
223	case WDIOC_GETBOOTSTATUS:
224		if (cpu_is_omap16xx())
225			return put_user(__raw_readw(ARM_SYSST),
226					(int __user *)arg);
227		if (cpu_is_omap24xx())
228			return put_user(omap_prcm_get_reset_sources(),
229					(int __user *)arg);
230	case WDIOC_KEEPALIVE:
231		spin_lock(&wdt_lock);
232		omap_wdt_ping(wdev);
233		spin_unlock(&wdt_lock);
234		return 0;
235	case WDIOC_SETTIMEOUT:
236		if (get_user(new_margin, (int __user *)arg))
237			return -EFAULT;
238		omap_wdt_adjust_timeout(new_margin);
239
240		spin_lock(&wdt_lock);
241		omap_wdt_disable(wdev);
242		omap_wdt_set_timeout(wdev);
243		omap_wdt_enable(wdev);
244
245		omap_wdt_ping(wdev);
246		spin_unlock(&wdt_lock);
247		/* Fall */
248	case WDIOC_GETTIMEOUT:
249		return put_user(timer_margin, (int __user *)arg);
250	default:
251		return -ENOTTY;
252	}
253}
254
255static const struct file_operations omap_wdt_fops = {
256	.owner = THIS_MODULE,
257	.write = omap_wdt_write,
258	.unlocked_ioctl = omap_wdt_ioctl,
259	.open = omap_wdt_open,
260	.release = omap_wdt_release,
261};
262
263static int __devinit omap_wdt_probe(struct platform_device *pdev)
264{
265	struct resource *res, *mem;
266	struct omap_wdt_dev *wdev;
267	int ret;
268
269	/* reserve static register mappings */
270	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
271	if (!res) {
272		ret = -ENOENT;
273		goto err_get_resource;
274	}
275
276	if (omap_wdt_dev) {
277		ret = -EBUSY;
278		goto err_busy;
279	}
280
281	mem = request_mem_region(res->start, resource_size(res), pdev->name);
282	if (!mem) {
283		ret = -EBUSY;
284		goto err_busy;
285	}
286
287	wdev = kzalloc(sizeof(struct omap_wdt_dev), GFP_KERNEL);
288	if (!wdev) {
289		ret = -ENOMEM;
290		goto err_kzalloc;
291	}
292
293	wdev->omap_wdt_users = 0;
294	wdev->mem = mem;
295
296	wdev->ick = clk_get(&pdev->dev, "ick");
297	if (IS_ERR(wdev->ick)) {
298		ret = PTR_ERR(wdev->ick);
299		wdev->ick = NULL;
300		goto err_clk;
301	}
302	wdev->fck = clk_get(&pdev->dev, "fck");
303	if (IS_ERR(wdev->fck)) {
304		ret = PTR_ERR(wdev->fck);
305		wdev->fck = NULL;
306		goto err_clk;
307	}
308
309	wdev->base = ioremap(res->start, resource_size(res));
310	if (!wdev->base) {
311		ret = -ENOMEM;
312		goto err_ioremap;
313	}
314
315	platform_set_drvdata(pdev, wdev);
316
317	clk_enable(wdev->ick);
318	clk_enable(wdev->fck);
319
320	omap_wdt_disable(wdev);
321	omap_wdt_adjust_timeout(timer_margin);
322
323	wdev->omap_wdt_miscdev.parent = &pdev->dev;
324	wdev->omap_wdt_miscdev.minor = WATCHDOG_MINOR;
325	wdev->omap_wdt_miscdev.name = "watchdog";
326	wdev->omap_wdt_miscdev.fops = &omap_wdt_fops;
327
328	ret = misc_register(&(wdev->omap_wdt_miscdev));
329	if (ret)
330		goto err_misc;
331
332	pr_info("OMAP Watchdog Timer Rev 0x%02x: initial timeout %d sec\n",
333		__raw_readl(wdev->base + OMAP_WATCHDOG_REV) & 0xFF,
334		timer_margin);
335
336	/* autogate OCP interface clock */
337	__raw_writel(0x01, wdev->base + OMAP_WATCHDOG_SYS_CONFIG);
338
339	clk_disable(wdev->ick);
340	clk_disable(wdev->fck);
341
342	omap_wdt_dev = pdev;
343
344	return 0;
345
346err_misc:
347	platform_set_drvdata(pdev, NULL);
348	iounmap(wdev->base);
349
350err_ioremap:
351	wdev->base = NULL;
352
353err_clk:
354	if (wdev->ick)
355		clk_put(wdev->ick);
356	if (wdev->fck)
357		clk_put(wdev->fck);
358	kfree(wdev);
359
360err_kzalloc:
361	release_mem_region(res->start, resource_size(res));
362
363err_busy:
364err_get_resource:
365
366	return ret;
367}
368
369static void omap_wdt_shutdown(struct platform_device *pdev)
370{
371	struct omap_wdt_dev *wdev = platform_get_drvdata(pdev);
372
373	if (wdev->omap_wdt_users)
374		omap_wdt_disable(wdev);
375}
376
377static int __devexit omap_wdt_remove(struct platform_device *pdev)
378{
379	struct omap_wdt_dev *wdev = platform_get_drvdata(pdev);
380	struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
381
382	if (!res)
383		return -ENOENT;
384
385	misc_deregister(&(wdev->omap_wdt_miscdev));
386	release_mem_region(res->start, resource_size(res));
387	platform_set_drvdata(pdev, NULL);
388
389	clk_put(wdev->ick);
390	clk_put(wdev->fck);
391	iounmap(wdev->base);
392
393	kfree(wdev);
394	omap_wdt_dev = NULL;
395
396	return 0;
397}
398
399#ifdef	CONFIG_PM
400
401/* REVISIT ... not clear this is the best way to handle system suspend; and
402 * it's very inappropriate for selective device suspend (e.g. suspending this
403 * through sysfs rather than by stopping the watchdog daemon).  Also, this
404 * may not play well enough with NOWAYOUT...
405 */
406
407static int omap_wdt_suspend(struct platform_device *pdev, pm_message_t state)
408{
409	struct omap_wdt_dev *wdev = platform_get_drvdata(pdev);
410
411	if (wdev->omap_wdt_users)
412		omap_wdt_disable(wdev);
413
414	return 0;
415}
416
417static int omap_wdt_resume(struct platform_device *pdev)
418{
419	struct omap_wdt_dev *wdev = platform_get_drvdata(pdev);
420
421	if (wdev->omap_wdt_users) {
422		omap_wdt_enable(wdev);
423		omap_wdt_ping(wdev);
424	}
425
426	return 0;
427}
428
429#else
430#define	omap_wdt_suspend	NULL
431#define	omap_wdt_resume		NULL
432#endif
433
434static struct platform_driver omap_wdt_driver = {
435	.probe		= omap_wdt_probe,
436	.remove		= __devexit_p(omap_wdt_remove),
437	.shutdown	= omap_wdt_shutdown,
438	.suspend	= omap_wdt_suspend,
439	.resume		= omap_wdt_resume,
440	.driver		= {
441		.owner	= THIS_MODULE,
442		.name	= "omap_wdt",
443	},
444};
445
446static int __init omap_wdt_init(void)
447{
448	spin_lock_init(&wdt_lock);
449	return platform_driver_register(&omap_wdt_driver);
450}
451
452static void __exit omap_wdt_exit(void)
453{
454	platform_driver_unregister(&omap_wdt_driver);
455}
456
457module_init(omap_wdt_init);
458module_exit(omap_wdt_exit);
459
460MODULE_AUTHOR("George G. Davis");
461MODULE_LICENSE("GPL");
462MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
463MODULE_ALIAS("platform:omap_wdt");
464