1 From 8dd2c6ae6d9c858d9c4c4d55aa4bf180669ddfe9 Mon Sep 17 00:00:00 2001
2 From: John Crispin <blogic@openwrt.org>
3 Date: Tue, 22 Jan 2013 18:23:50 +0100
4 Subject: [PATCH 121/121] watchdog: adds ralink wdt
6 Adds the watchdog driver for ralink SoC.
8 Signed-off-by: John Crispin <blogic@openwrt.org>
10 drivers/watchdog/Kconfig | 6 +
11 drivers/watchdog/Makefile | 1 +
12 drivers/watchdog/ralink_wdt.c | 352 +++++++++++++++++++++++++++++++++++++++++
13 3 files changed, 359 insertions(+)
14 create mode 100644 drivers/watchdog/ralink_wdt.c
16 diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
17 index 9fcc70c..c4b508e 100644
18 --- a/drivers/watchdog/Kconfig
19 +++ b/drivers/watchdog/Kconfig
20 @@ -1104,6 +1104,12 @@ config LANTIQ_WDT
22 Hardware driver for the Lantiq SoC Watchdog Timer.
25 + tristate "Ralink SoC watchdog"
28 + Hardware driver for the Ralink SoC Watchdog Timer.
32 # POWERPC Architecture
33 diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
34 index a300b94..3a8ad6a 100644
35 --- a/drivers/watchdog/Makefile
36 +++ b/drivers/watchdog/Makefile
37 @@ -134,6 +134,7 @@ obj-$(CONFIG_TXX9_WDT) += txx9wdt.o
38 obj-$(CONFIG_OCTEON_WDT) += octeon-wdt.o
39 octeon-wdt-y := octeon-wdt-main.o octeon-wdt-nmi.o
40 obj-$(CONFIG_LANTIQ_WDT) += lantiq_wdt.o
41 +obj-$(CONFIG_RALINK_WDT) += ralink_wdt.o
45 diff --git a/drivers/watchdog/ralink_wdt.c b/drivers/watchdog/ralink_wdt.c
47 index 0000000..8a8dc76
49 +++ b/drivers/watchdog/ralink_wdt.c
52 + * Ralink RT288X/RT305X built-in hardware watchdog timer
54 + * Copyright (C) 2011 Gabor Juhos <juhosg@openwrt.org>
56 + * This driver was based on: drivers/watchdog/ixp4xx_wdt.c
57 + * Author: Deepak Saxena <dsaxena@plexity.net>
58 + * Copyright 2004 (c) MontaVista, Software, Inc.
60 + * which again was based on sa1100 driver,
61 + * Copyright (C) 2000 Oleg Drokin <green@crimea.edu>
63 + * parts of the driver are based on Ralink's 2.6.21 BSP
65 + * This program is free software; you can redistribute it and/or modify it
66 + * under the terms of the GNU General Public License version 2 as published
67 + * by the Free Software Foundation.
70 +#include <linux/bitops.h>
71 +#include <linux/errno.h>
72 +#include <linux/fs.h>
73 +#include <linux/init.h>
74 +#include <linux/kernel.h>
75 +#include <linux/miscdevice.h>
76 +#include <linux/module.h>
77 +#include <linux/moduleparam.h>
78 +#include <linux/platform_device.h>
79 +#include <linux/types.h>
80 +#include <linux/watchdog.h>
81 +#include <linux/clk.h>
82 +#include <linux/err.h>
84 +#define DRIVER_NAME "ralink-wdt"
86 +#define RALINK_WDT_TIMEOUT 30 /* seconds */
87 +#define RALINK_WDT_PRESCALE 65536
89 +#define TIMER_REG_TMR1LOAD 0x00
90 +#define TIMER_REG_TMR1CTL 0x08
92 +#define TMRSTAT_TMR1RST BIT(5)
94 +#define TMR1CTL_ENABLE BIT(7)
95 +#define TMR1CTL_MODE_SHIFT 4
96 +#define TMR1CTL_MODE_MASK 0x3
97 +#define TMR1CTL_MODE_FREE_RUNNING 0x0
98 +#define TMR1CTL_MODE_PERIODIC 0x1
99 +#define TMR1CTL_MODE_TIMEOUT 0x2
100 +#define TMR1CTL_MODE_WDT 0x3
101 +#define TMR1CTL_PRESCALE_MASK 0xf
102 +#define TMR1CTL_PRESCALE_65536 0xf
104 +static int nowayout = WATCHDOG_NOWAYOUT;
105 +module_param(nowayout, int, 0);
106 +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "
107 + "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
109 +static int ralink_wdt_timeout = RALINK_WDT_TIMEOUT;
110 +module_param_named(timeout, ralink_wdt_timeout, int, 0);
111 +MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds, 0 means use maximum "
112 + "(default=" __MODULE_STRING(RALINK_WDT_TIMEOUT) "s)");
114 +static unsigned long ralink_wdt_flags;
116 +#define WDT_FLAGS_BUSY 0
117 +#define WDT_FLAGS_EXPECT_CLOSE 1
119 +static struct clk *ralink_wdt_clk;
120 +static unsigned long ralink_wdt_freq;
121 +static int ralink_wdt_max_timeout;
122 +static void __iomem *ralink_wdt_base;
124 +static inline void rt_wdt_w32(unsigned reg, u32 val)
126 + __raw_writel(val, ralink_wdt_base + reg);
129 +static inline u32 rt_wdt_r32(unsigned reg)
131 + return __raw_readl(ralink_wdt_base + reg);
134 +static inline void ralink_wdt_keepalive(void)
136 + rt_wdt_w32(TIMER_REG_TMR1LOAD, ralink_wdt_timeout * ralink_wdt_freq);
139 +static inline void ralink_wdt_enable(void)
143 + ralink_wdt_keepalive();
145 + t = rt_wdt_r32(TIMER_REG_TMR1CTL);
146 + t |= TMR1CTL_ENABLE;
147 + rt_wdt_w32(TIMER_REG_TMR1CTL, t);
150 +static inline void ralink_wdt_disable(void)
154 + ralink_wdt_keepalive();
156 + t = rt_wdt_r32(TIMER_REG_TMR1CTL);
157 + t &= ~TMR1CTL_ENABLE;
158 + rt_wdt_w32(TIMER_REG_TMR1CTL, t);
161 +static int ralink_wdt_set_timeout(int val)
163 + if (val < 1 || val > ralink_wdt_max_timeout) {
164 + pr_warn(DRIVER_NAME
165 + ": timeout value %d must be 0 < timeout <= %d, using %d\n",
166 + val, ralink_wdt_max_timeout, ralink_wdt_timeout);
170 + ralink_wdt_timeout = val;
171 + ralink_wdt_keepalive();
176 +static int ralink_wdt_open(struct inode *inode, struct file *file)
180 + if (test_and_set_bit(WDT_FLAGS_BUSY, &ralink_wdt_flags))
183 + clear_bit(WDT_FLAGS_EXPECT_CLOSE, &ralink_wdt_flags);
185 + t = rt_wdt_r32(TIMER_REG_TMR1CTL);
186 + t &= ~(TMR1CTL_MODE_MASK << TMR1CTL_MODE_SHIFT |
187 + TMR1CTL_PRESCALE_MASK);
188 + t |= (TMR1CTL_MODE_WDT << TMR1CTL_MODE_SHIFT |
189 + TMR1CTL_PRESCALE_65536);
190 + rt_wdt_w32(TIMER_REG_TMR1CTL, t);
192 + ralink_wdt_enable();
194 + return nonseekable_open(inode, file);
197 +static int ralink_wdt_release(struct inode *inode, struct file *file)
199 + if (test_bit(WDT_FLAGS_EXPECT_CLOSE, &ralink_wdt_flags))
200 + ralink_wdt_disable();
202 + pr_crit(DRIVER_NAME ": device closed unexpectedly, "
203 + "watchdog timer will not stop!\n");
204 + ralink_wdt_keepalive();
207 + clear_bit(WDT_FLAGS_BUSY, &ralink_wdt_flags);
208 + clear_bit(WDT_FLAGS_EXPECT_CLOSE, &ralink_wdt_flags);
213 +static ssize_t rt_wdt_w32ite(struct file *file, const char *data,
214 + size_t len, loff_t *ppos)
220 + clear_bit(WDT_FLAGS_EXPECT_CLOSE, &ralink_wdt_flags);
222 + for (i = 0; i != len; i++) {
225 + if (get_user(c, data + i))
229 + set_bit(WDT_FLAGS_EXPECT_CLOSE,
230 + &ralink_wdt_flags);
234 + ralink_wdt_keepalive();
240 +static const struct watchdog_info ralink_wdt_info = {
241 + .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING |
243 + .firmware_version = 0,
244 + .identity = "RALINK watchdog",
247 +static long ralink_wdt_ioctl(struct file *file, unsigned int cmd,
250 + void __user *argp = (void __user *)arg;
251 + int __user *p = argp;
256 + case WDIOC_GETSUPPORT:
257 + err = copy_to_user(argp, &ralink_wdt_info,
258 + sizeof(ralink_wdt_info)) ? -EFAULT : 0;
261 + case WDIOC_GETSTATUS:
262 + err = put_user(0, p);
265 + case WDIOC_KEEPALIVE:
266 + ralink_wdt_keepalive();
270 + case WDIOC_SETTIMEOUT:
271 + err = get_user(t, p);
275 + err = ralink_wdt_set_timeout(t);
280 + case WDIOC_GETTIMEOUT:
281 + err = put_user(ralink_wdt_timeout, p);
292 +static const struct file_operations ralink_wdt_fops = {
293 + .owner = THIS_MODULE,
294 + .llseek = no_llseek,
295 + .write = rt_wdt_w32ite,
296 + .unlocked_ioctl = ralink_wdt_ioctl,
297 + .open = ralink_wdt_open,
298 + .release = ralink_wdt_release,
301 +static struct miscdevice ralink_wdt_miscdev = {
302 + .minor = WATCHDOG_MINOR,
303 + .name = "watchdog",
304 + .fops = &ralink_wdt_fops,
307 +static int ralink_wdt_probe(struct platform_device *pdev)
309 + struct resource *res;
312 + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
314 + dev_err(&pdev->dev, "no memory resource found\n");
318 + ralink_wdt_base = ioremap(res->start, resource_size(res));
319 + if (!ralink_wdt_base)
322 + ralink_wdt_clk = clk_get(&pdev->dev, NULL);
323 + if (IS_ERR(ralink_wdt_clk)) {
324 + err = PTR_ERR(ralink_wdt_clk);
328 + err = clk_enable(ralink_wdt_clk);
332 + ralink_wdt_freq = clk_get_rate(ralink_wdt_clk) / RALINK_WDT_PRESCALE;
333 + if (!ralink_wdt_freq) {
335 + goto err_clk_disable;
338 + ralink_wdt_max_timeout = (0xfffful / ralink_wdt_freq);
339 + if (ralink_wdt_timeout < 1 ||
340 + ralink_wdt_timeout > ralink_wdt_max_timeout) {
341 + ralink_wdt_timeout = ralink_wdt_max_timeout;
342 + dev_info(&pdev->dev,
343 + "timeout value must be 0 < timeout <= %d, using %d\n",
344 + ralink_wdt_max_timeout, ralink_wdt_timeout);
347 + err = misc_register(&ralink_wdt_miscdev);
349 + dev_err(&pdev->dev,
350 + "unable to register misc device, err=%d\n", err);
351 + goto err_clk_disable;
357 + clk_disable(ralink_wdt_clk);
359 + clk_put(ralink_wdt_clk);
361 + iounmap(ralink_wdt_base);
365 +static int ralink_wdt_remove(struct platform_device *pdev)
367 + misc_deregister(&ralink_wdt_miscdev);
368 + clk_disable(ralink_wdt_clk);
369 + clk_put(ralink_wdt_clk);
370 + iounmap(ralink_wdt_base);
374 +static void ralink_wdt_shutdown(struct platform_device *pdev)
376 + ralink_wdt_disable();
379 +static const struct of_device_id ralink_wdt_match[] = {
380 + { .compatible = "ralink,rt2880-wdt" },
383 +MODULE_DEVICE_TABLE(of, ralink_wdt_match);
385 +static struct platform_driver ralink_wdt_driver = {
386 + .probe = ralink_wdt_probe,
387 + .remove = ralink_wdt_remove,
388 + .shutdown = ralink_wdt_shutdown,
390 + .name = DRIVER_NAME,
391 + .owner = THIS_MODULE,
392 + .of_match_table = ralink_wdt_match,
396 +module_platform_driver(ralink_wdt_driver);
398 +MODULE_DESCRIPTION("MediaTek/Ralink RT288X/RT305X hardware watchdog driver");
399 +MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org");
400 +MODULE_LICENSE("GPL v2");
401 +MODULE_ALIAS("platform:" DRIVER_NAME);
402 +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);