[ramips] add patches for v3.8
[15.05/openwrt.git] / target / linux / ramips / patches-3.8 / 0121-watchdog-adds-ralink-wdt.patch
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
5
6 Adds the watchdog driver for ralink SoC.
7
8 Signed-off-by: John Crispin <blogic@openwrt.org>
9 ---
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
15
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
21         help
22           Hardware driver for the Lantiq SoC Watchdog Timer.
23  
24 +config RALINK_WDT
25 +       tristate "Ralink SoC watchdog"
26 +       depends on RALINK
27 +       help
28 +         Hardware driver for the Ralink SoC Watchdog Timer.
29 +
30  # PARISC Architecture
31  
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
42  
43  # PARISC Architecture
44  
45 diff --git a/drivers/watchdog/ralink_wdt.c b/drivers/watchdog/ralink_wdt.c
46 new file mode 100644
47 index 0000000..8a8dc76
48 --- /dev/null
49 +++ b/drivers/watchdog/ralink_wdt.c
50 @@ -0,0 +1,352 @@
51 +/*
52 + * Ralink RT288X/RT305X built-in hardware watchdog timer
53 + *
54 + * Copyright (C) 2011 Gabor Juhos <juhosg@openwrt.org>
55 + *
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.
59 + *
60 + * which again was based on sa1100 driver,
61 + *     Copyright (C) 2000 Oleg Drokin <green@crimea.edu>
62 + *
63 + * parts of the driver are based on Ralink's 2.6.21 BSP
64 + *
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.
68 + */
69 +
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>
83 +
84 +#define DRIVER_NAME    "ralink-wdt"
85 +
86 +#define RALINK_WDT_TIMEOUT             30      /* seconds */
87 +#define RALINK_WDT_PRESCALE            65536
88 +
89 +#define TIMER_REG_TMR1LOAD             0x00
90 +#define TIMER_REG_TMR1CTL              0x08
91 +
92 +#define TMRSTAT_TMR1RST                        BIT(5)
93 +
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
103 +
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) ")");
108 +
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)");
113 +
114 +static unsigned long ralink_wdt_flags;
115 +
116 +#define WDT_FLAGS_BUSY         0
117 +#define WDT_FLAGS_EXPECT_CLOSE 1
118 +
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;
123 +
124 +static inline void rt_wdt_w32(unsigned reg, u32 val)
125 +{
126 +       __raw_writel(val, ralink_wdt_base + reg);
127 +}
128 +
129 +static inline u32 rt_wdt_r32(unsigned reg)
130 +{
131 +       return __raw_readl(ralink_wdt_base + reg);
132 +}
133 +
134 +static inline void ralink_wdt_keepalive(void)
135 +{
136 +       rt_wdt_w32(TIMER_REG_TMR1LOAD, ralink_wdt_timeout * ralink_wdt_freq);
137 +}
138 +
139 +static inline void ralink_wdt_enable(void)
140 +{
141 +       u32 t;
142 +
143 +       ralink_wdt_keepalive();
144 +
145 +       t = rt_wdt_r32(TIMER_REG_TMR1CTL);
146 +       t |= TMR1CTL_ENABLE;
147 +       rt_wdt_w32(TIMER_REG_TMR1CTL, t);
148 +}
149 +
150 +static inline void ralink_wdt_disable(void)
151 +{
152 +       u32 t;
153 +
154 +       ralink_wdt_keepalive();
155 +
156 +       t = rt_wdt_r32(TIMER_REG_TMR1CTL);
157 +       t &= ~TMR1CTL_ENABLE;
158 +       rt_wdt_w32(TIMER_REG_TMR1CTL, t);
159 +}
160 +
161 +static int ralink_wdt_set_timeout(int val)
162 +{
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);
167 +               return -EINVAL;
168 +       }
169 +
170 +       ralink_wdt_timeout = val;
171 +       ralink_wdt_keepalive();
172 +
173 +       return 0;
174 +}
175 +
176 +static int ralink_wdt_open(struct inode *inode, struct file *file)
177 +{
178 +       u32 t;
179 +
180 +       if (test_and_set_bit(WDT_FLAGS_BUSY, &ralink_wdt_flags))
181 +               return -EBUSY;
182 +
183 +       clear_bit(WDT_FLAGS_EXPECT_CLOSE, &ralink_wdt_flags);
184 +
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);
191 +
192 +       ralink_wdt_enable();
193 +
194 +       return nonseekable_open(inode, file);
195 +}
196 +
197 +static int ralink_wdt_release(struct inode *inode, struct file *file)
198 +{
199 +       if (test_bit(WDT_FLAGS_EXPECT_CLOSE, &ralink_wdt_flags))
200 +               ralink_wdt_disable();
201 +       else {
202 +               pr_crit(DRIVER_NAME ": device closed unexpectedly, "
203 +                       "watchdog timer will not stop!\n");
204 +               ralink_wdt_keepalive();
205 +       }
206 +
207 +       clear_bit(WDT_FLAGS_BUSY, &ralink_wdt_flags);
208 +       clear_bit(WDT_FLAGS_EXPECT_CLOSE, &ralink_wdt_flags);
209 +
210 +       return 0;
211 +}
212 +
213 +static ssize_t rt_wdt_w32ite(struct file *file, const char *data,
214 +                               size_t len, loff_t *ppos)
215 +{
216 +       if (len) {
217 +               if (!nowayout) {
218 +                       size_t i;
219 +
220 +                       clear_bit(WDT_FLAGS_EXPECT_CLOSE, &ralink_wdt_flags);
221 +
222 +                       for (i = 0; i != len; i++) {
223 +                               char c;
224 +
225 +                               if (get_user(c, data + i))
226 +                                       return -EFAULT;
227 +
228 +                               if (c == 'V')
229 +                                       set_bit(WDT_FLAGS_EXPECT_CLOSE,
230 +                                               &ralink_wdt_flags);
231 +                       }
232 +               }
233 +
234 +               ralink_wdt_keepalive();
235 +       }
236 +
237 +       return len;
238 +}
239 +
240 +static const struct watchdog_info ralink_wdt_info = {
241 +       .options                = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING |
242 +                                 WDIOF_MAGICCLOSE,
243 +       .firmware_version       = 0,
244 +       .identity               = "RALINK watchdog",
245 +};
246 +
247 +static long ralink_wdt_ioctl(struct file *file, unsigned int cmd,
248 +                           unsigned long arg)
249 +{
250 +       void __user *argp = (void __user *)arg;
251 +       int __user *p = argp;
252 +       int err;
253 +       int t;
254 +
255 +       switch (cmd) {
256 +       case WDIOC_GETSUPPORT:
257 +               err = copy_to_user(argp, &ralink_wdt_info,
258 +                                  sizeof(ralink_wdt_info)) ? -EFAULT : 0;
259 +               break;
260 +
261 +       case WDIOC_GETSTATUS:
262 +               err = put_user(0, p);
263 +               break;
264 +
265 +       case WDIOC_KEEPALIVE:
266 +               ralink_wdt_keepalive();
267 +               err = 0;
268 +               break;
269 +
270 +       case WDIOC_SETTIMEOUT:
271 +               err = get_user(t, p);
272 +               if (err)
273 +                       break;
274 +
275 +               err = ralink_wdt_set_timeout(t);
276 +               if (err)
277 +                       break;
278 +
279 +               /* fallthrough */
280 +       case WDIOC_GETTIMEOUT:
281 +               err = put_user(ralink_wdt_timeout, p);
282 +               break;
283 +
284 +       default:
285 +               err = -ENOTTY;
286 +               break;
287 +       }
288 +
289 +       return err;
290 +}
291 +
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,
299 +};
300 +
301 +static struct miscdevice ralink_wdt_miscdev = {
302 +       .minor = WATCHDOG_MINOR,
303 +       .name = "watchdog",
304 +       .fops = &ralink_wdt_fops,
305 +};
306 +
307 +static int ralink_wdt_probe(struct platform_device *pdev)
308 +{
309 +       struct resource *res;
310 +       int err;
311 +
312 +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
313 +       if (!res) {
314 +               dev_err(&pdev->dev, "no memory resource found\n");
315 +               return -EINVAL;
316 +       }
317 +
318 +       ralink_wdt_base = ioremap(res->start, resource_size(res));
319 +       if (!ralink_wdt_base)
320 +               return -ENOMEM;
321 +
322 +       ralink_wdt_clk = clk_get(&pdev->dev, NULL);
323 +       if (IS_ERR(ralink_wdt_clk)) {
324 +               err = PTR_ERR(ralink_wdt_clk);
325 +               goto err_unmap;
326 +       }
327 +
328 +       err = clk_enable(ralink_wdt_clk);
329 +       if (err)
330 +               goto err_clk_put;
331 +
332 +       ralink_wdt_freq = clk_get_rate(ralink_wdt_clk) / RALINK_WDT_PRESCALE;
333 +       if (!ralink_wdt_freq) {
334 +               err = -EINVAL;
335 +               goto err_clk_disable;
336 +       }
337 +
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);
345 +       }
346 +
347 +       err = misc_register(&ralink_wdt_miscdev);
348 +       if (err) {
349 +               dev_err(&pdev->dev,
350 +                       "unable to register misc device, err=%d\n", err);
351 +               goto err_clk_disable;
352 +       }
353 +
354 +       return 0;
355 +
356 +err_clk_disable:
357 +       clk_disable(ralink_wdt_clk);
358 +err_clk_put:
359 +       clk_put(ralink_wdt_clk);
360 +err_unmap:
361 +       iounmap(ralink_wdt_base);
362 +       return err;
363 +}
364 +
365 +static int ralink_wdt_remove(struct platform_device *pdev)
366 +{
367 +       misc_deregister(&ralink_wdt_miscdev);
368 +       clk_disable(ralink_wdt_clk);
369 +       clk_put(ralink_wdt_clk);
370 +       iounmap(ralink_wdt_base);
371 +       return 0;
372 +}
373 +
374 +static void ralink_wdt_shutdown(struct platform_device *pdev)
375 +{
376 +       ralink_wdt_disable();
377 +}
378 +
379 +static const struct of_device_id ralink_wdt_match[] = {
380 +       { .compatible = "ralink,rt2880-wdt" },
381 +       {},
382 +};
383 +MODULE_DEVICE_TABLE(of, ralink_wdt_match);
384 +
385 +static struct platform_driver ralink_wdt_driver = {
386 +       .probe          = ralink_wdt_probe,
387 +       .remove         = ralink_wdt_remove,
388 +       .shutdown       = ralink_wdt_shutdown,
389 +       .driver         = {
390 +               .name           = DRIVER_NAME,
391 +               .owner          = THIS_MODULE,
392 +               .of_match_table = ralink_wdt_match,
393 +       },
394 +};
395 +
396 +module_platform_driver(ralink_wdt_driver);
397 +
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);
403 -- 
404 1.7.10.4
405