ramips: raeth: indicate dropped packets in stats
[openwrt.git] / target / linux / cns21xx / patches-2.6.37 / 006-arm-add-fa-watchdog-driver.patch
1 --- a/drivers/watchdog/Kconfig
2 +++ b/drivers/watchdog/Kconfig
3 @@ -331,6 +331,13 @@ config IMX2_WDT
4           To compile this driver as a module, choose M here: the
5           module will be called imx2_wdt.
6  
7 +config FA_WATCHDOG
8 +       tristate "Faraday watchdog"
9 +       depends on ARCH_GEMINI
10 +       help
11 +         Say Y here if you want support for the built-in watchdog timer
12 +         found in some Faraday FA526 based SoCs.
13 +
14  # AVR32 Architecture
15  
16  config AT32AP700X_WDT
17 --- a/drivers/watchdog/Makefile
18 +++ b/drivers/watchdog/Makefile
19 @@ -49,6 +49,7 @@ obj-$(CONFIG_NUC900_WATCHDOG) += nuc900_
20  obj-$(CONFIG_ADX_WATCHDOG) += adx_wdt.o
21  obj-$(CONFIG_TS72XX_WATCHDOG) += ts72xx_wdt.o
22  obj-$(CONFIG_IMX2_WDT) += imx2_wdt.o
23 +obj-$(CONFIG_FA_WATCHDOG) += fa_wdt.o
24  
25  # AVR32 Architecture
26  obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o
27 --- /dev/null
28 +++ b/drivers/watchdog/fa_wdt.c
29 @@ -0,0 +1,411 @@
30 +/*
31 + *  Watchdog driver for SoCs based on the Faraday FA526 core
32 + *
33 + *  Copyright (C) 2009 Paulius Zaleckas <paulius.zaleckas@teltonika.lt>
34 + *  Copyright (C) 2010 Gabor Juhos <juhosg@openwrt.org>
35 + *
36 + * This program is free software; you can redistribute it and/or modify
37 + * it under the terms of the GNU General Public License version 2 as
38 + * published by the Free Software Foundation.
39 + */
40 +
41 +#include <linux/kernel.h>
42 +#include <linux/init.h>
43 +#include <linux/io.h>
44 +#include <linux/fs.h>
45 +#include <linux/notifier.h>
46 +#include <linux/reboot.h>
47 +#include <linux/uaccess.h>
48 +#include <linux/miscdevice.h>
49 +#include <linux/platform_device.h>
50 +#include <linux/watchdog.h>
51 +#include <linux/slab.h>
52 +#include <linux/fa_wdt.h>
53 +
54 +#define FA_WDCOUNTER           0x0
55 +#define FA_WDLOAD              0x4
56 +#define FA_WDRESTART           0x8
57 +
58 +#define WDRESTART_MAGIC                0x5AB9
59 +
60 +#define FA_WDCR                        0xC
61 +
62 +#define WDCR_CLOCK_5MHZ                (1 << 4)
63 +#define WDCR_SYS_RST           (1 << 1)
64 +#define WDCR_ENABLE            (1 << 0)
65 +
66 +#define WDT_DEFAULT_TIMEOUT    13
67 +
68 +/* status bits */
69 +#define WDT_ACTIVE             0
70 +#define WDT_OK_TO_CLOSE                1
71 +
72 +static unsigned int timeout = WDT_DEFAULT_TIMEOUT;
73 +static int nowayout = WATCHDOG_NOWAYOUT;
74 +
75 +static DEFINE_SPINLOCK(fa_wdt_lock);
76 +
77 +static struct platform_device *fa_wdt_dev;
78 +
79 +struct fa_wdt_struct {
80 +       struct resource         *res;
81 +       struct device           *dev;
82 +       void __iomem            *base;
83 +       unsigned long           status;
84 +       unsigned int            clock;
85 +       unsigned int            max_timeout;
86 +};
87 +
88 +static const struct watchdog_info fa_wdt_info = {
89 +       .identity       = "Faraday watchdog",
90 +       .options        = WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING |
91 +                         WDIOF_SETTIMEOUT,
92 +};
93 +
94 +/* Disable the watchdog. */
95 +static void fa_wdt_stop(struct fa_wdt_struct *fa_wdt)
96 +{
97 +       spin_lock(&fa_wdt_lock);
98 +
99 +       __raw_writel(0, fa_wdt->base + FA_WDCR);
100 +
101 +       clear_bit(WDT_ACTIVE, &fa_wdt->status);
102 +
103 +       spin_unlock(&fa_wdt_lock);
104 +}
105 +
106 +/* Service the watchdog */
107 +static void fa_wdt_service(struct fa_wdt_struct *fa_wdt)
108 +{
109 +       __raw_writel(WDRESTART_MAGIC, fa_wdt->base + FA_WDRESTART);
110 +}
111 +
112 +/* Enable and reset the watchdog. */
113 +static void fa_wdt_start(struct fa_wdt_struct *fa_wdt)
114 +{
115 +       spin_lock(&fa_wdt_lock);
116 +
117 +       __raw_writel(timeout * fa_wdt->clock,
118 +                    fa_wdt->base + FA_WDLOAD);
119 +
120 +       fa_wdt_service(fa_wdt);
121 +
122 +       /* set clock before enabling */
123 +       __raw_writel(WDCR_CLOCK_5MHZ | WDCR_SYS_RST,
124 +                       fa_wdt->base + FA_WDCR);
125 +
126 +       __raw_writel(WDCR_CLOCK_5MHZ | WDCR_SYS_RST | WDCR_ENABLE,
127 +                       fa_wdt->base + FA_WDCR);
128 +
129 +       set_bit(WDT_ACTIVE, &fa_wdt->status);
130 +
131 +       spin_unlock(&fa_wdt_lock);
132 +}
133 +
134 +/* Watchdog device is opened, and watchdog starts running. */
135 +static int fa_wdt_open(struct inode *inode, struct file *file)
136 +{
137 +       struct fa_wdt_struct *fa_wdt = platform_get_drvdata(fa_wdt_dev);
138 +
139 +       if (test_bit(WDT_ACTIVE, &fa_wdt->status))
140 +               return -EBUSY;
141 +
142 +       file->private_data = fa_wdt;
143 +
144 +       fa_wdt_start(fa_wdt);
145 +
146 +       return nonseekable_open(inode, file);
147 +}
148 +
149 +/* Close the watchdog device. */
150 +static int fa_wdt_close(struct inode *inode, struct file *file)
151 +{
152 +       struct fa_wdt_struct *fa_wdt = file->private_data;
153 +
154 +       /* Disable the watchdog if possible */
155 +       if (test_bit(WDT_OK_TO_CLOSE, &fa_wdt->status))
156 +               fa_wdt_stop(fa_wdt);
157 +       else
158 +               dev_warn(fa_wdt->dev, "Device closed unexpectedly - timer will not stop\n");
159 +
160 +       return 0;
161 +}
162 +
163 +/* Handle commands from user-space. */
164 +static long fa_wdt_ioctl(struct file *file, unsigned int cmd,
165 +                        unsigned long arg)
166 +{
167 +       struct fa_wdt_struct *fa_wdt = file->private_data;
168 +
169 +       int value;
170 +
171 +       switch (cmd) {
172 +       case WDIOC_KEEPALIVE:
173 +               fa_wdt_service(fa_wdt);
174 +               return 0;
175 +
176 +       case WDIOC_GETSUPPORT:
177 +               return copy_to_user((struct watchdog_info *)arg, &fa_wdt_info,
178 +                       sizeof(fa_wdt_info)) ? -EFAULT : 0;
179 +
180 +       case WDIOC_SETTIMEOUT:
181 +               if (get_user(value, (int *)arg))
182 +                       return -EFAULT;
183 +
184 +               if ((value < 1) || (value > fa_wdt->max_timeout))
185 +                       return -EINVAL;
186 +
187 +               timeout = value;
188 +
189 +               /* restart wdt to use new timeout */
190 +               fa_wdt_stop(fa_wdt);
191 +               fa_wdt_start(fa_wdt);
192 +
193 +               /* Fall through */
194 +       case WDIOC_GETTIMEOUT:
195 +               return put_user(timeout, (int *)arg);
196 +
197 +       case WDIOC_GETTIMELEFT:
198 +               value = __raw_readl(fa_wdt->base + FA_WDCOUNTER);
199 +               return put_user(value / fa_wdt->clock, (int *)arg);
200 +
201 +       default:
202 +               return -ENOTTY;
203 +       }
204 +}
205 +
206 +/* Refresh the watchdog whenever device is written to. */
207 +static ssize_t fa_wdt_write(struct file *file, const char *data,
208 +                           size_t len, loff_t *ppos)
209 +{
210 +       struct fa_wdt_struct *fa_wdt = file->private_data;
211 +
212 +       if (len) {
213 +               if (!nowayout) {
214 +                       size_t i;
215 +
216 +                       clear_bit(WDT_OK_TO_CLOSE, &fa_wdt->status);
217 +                       for (i = 0; i != len; i++) {
218 +                               char c;
219 +
220 +                               if (get_user(c, data + i))
221 +                                       return -EFAULT;
222 +                               if (c == 'V')
223 +                                       set_bit(WDT_OK_TO_CLOSE,
224 +                                               &fa_wdt->status);
225 +                       }
226 +               }
227 +               fa_wdt_service(fa_wdt);
228 +       }
229 +
230 +       return len;
231 +}
232 +
233 +static int fa_wdt_notify_sys(struct notifier_block *this,
234 +                            unsigned long code, void *unused)
235 +{
236 +       struct fa_wdt_struct *fa_wdt = platform_get_drvdata(fa_wdt_dev);
237 +
238 +       if (code == SYS_DOWN || code == SYS_HALT)
239 +               fa_wdt_stop(fa_wdt);
240 +
241 +       return NOTIFY_DONE;
242 +}
243 +
244 +static struct notifier_block fa_wdt_notifier = {
245 +       .notifier_call = fa_wdt_notify_sys,
246 +};
247 +
248 +static const struct file_operations fa_wdt_fops = {
249 +       .owner          = THIS_MODULE,
250 +       .llseek         = no_llseek,
251 +       .unlocked_ioctl = fa_wdt_ioctl,
252 +       .open           = fa_wdt_open,
253 +       .release        = fa_wdt_close,
254 +       .write          = fa_wdt_write,
255 +};
256 +
257 +static struct miscdevice fa_wdt_miscdev = {
258 +       .minor          = WATCHDOG_MINOR,
259 +       .name           = "watchdog",
260 +       .fops           = &fa_wdt_fops,
261 +};
262 +
263 +static void fa_wdt_shutdown(struct platform_device *pdev)
264 +{
265 +       struct fa_wdt_struct *fa_wdt = platform_get_drvdata(pdev);
266 +
267 +       fa_wdt_stop(fa_wdt);
268 +}
269 +
270 +static int __devinit fa_wdt_probe(struct platform_device *pdev)
271 +{
272 +       int ret;
273 +       int res_size;
274 +       struct resource *res;
275 +       void __iomem *base;
276 +       struct fa_wdt_struct *fa_wdt;
277 +       struct fa_wdt_platform_data *pdata;
278 +
279 +       pdata = pdev->dev.platform_data;
280 +       if (!pdata) {
281 +               dev_err(&pdev->dev, "no platform data specified\n");
282 +               return -EINVAL;
283 +       }
284 +
285 +       if (!pdata->clock) {
286 +               dev_err(&pdev->dev, "invalid clock value\n");
287 +               return -EINVAL;
288 +       }
289 +
290 +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
291 +       if (!res) {
292 +               dev_err(&pdev->dev, "can't get device resources\n");
293 +               return -ENODEV;
294 +       }
295 +
296 +       res_size = resource_size(res);
297 +       if (!request_mem_region(res->start, res_size, res->name)) {
298 +               dev_err(&pdev->dev, "can't allocate %d bytes at %d address\n",
299 +                       res_size, res->start);
300 +               return -ENOMEM;
301 +       }
302 +
303 +       base = ioremap(res->start, res_size);
304 +       if (!base) {
305 +               dev_err(&pdev->dev, "ioremap failed\n");
306 +               ret = -EIO;
307 +               goto fail0;
308 +       }
309 +
310 +       fa_wdt = kzalloc(sizeof(struct fa_wdt_struct), GFP_KERNEL);
311 +       if (!fa_wdt) {
312 +               dev_err(&pdev->dev, "can't allocate interface\n");
313 +               ret = -ENOMEM;
314 +               goto fail1;
315 +       }
316 +
317 +       /* Setup fa_wdt driver structure */
318 +       fa_wdt->base = base;
319 +       fa_wdt->res = res;
320 +       fa_wdt->dev = &pdev->dev;
321 +       fa_wdt->clock = pdata->clock;
322 +       fa_wdt->max_timeout = 0xffffffffU / pdata->clock;
323 +
324 +       /* Set up platform driver data */
325 +       platform_set_drvdata(pdev, fa_wdt);
326 +       fa_wdt_dev = pdev;
327 +
328 +       if (fa_wdt_miscdev.parent) {
329 +               ret = -EBUSY;
330 +               goto fail2;
331 +       }
332 +
333 +       ret = register_reboot_notifier(&fa_wdt_notifier);
334 +       if (ret)
335 +               goto fail2;
336 +
337 +       fa_wdt_miscdev.parent = &pdev->dev;
338 +
339 +       ret = misc_register(&fa_wdt_miscdev);
340 +       if (ret)
341 +               goto fail3;
342 +
343 +       return 0;
344 +
345 +fail3:
346 +       unregister_reboot_notifier(&fa_wdt_notifier);
347 +fail2:
348 +       platform_set_drvdata(pdev, NULL);
349 +       kfree(fa_wdt);
350 +fail1:
351 +       iounmap(base);
352 +fail0:
353 +       release_mem_region(res->start, res_size);
354 +
355 +       return ret;
356 +}
357 +
358 +static int __devexit fa_wdt_remove(struct platform_device *pdev)
359 +{
360 +       struct fa_wdt_struct *fa_wdt = platform_get_drvdata(pdev);
361 +
362 +       platform_set_drvdata(pdev, NULL);
363 +       misc_deregister(&fa_wdt_miscdev);
364 +       unregister_reboot_notifier(&fa_wdt_notifier);
365 +       fa_wdt_dev = NULL;
366 +       iounmap(fa_wdt->base);
367 +       release_mem_region(fa_wdt->res->start, resource_size(fa_wdt->res));
368 +
369 +       kfree(fa_wdt);
370 +
371 +       return 0;
372 +}
373 +
374 +#ifdef CONFIG_PM
375 +static int fa_wdt_suspend(struct platform_device *pdev, pm_message_t message)
376 +{
377 +       struct fa_wdt_struct *fa_wdt = platform_get_drvdata(pdev);
378 +       unsigned int reg;
379 +
380 +       reg = __raw_readw(fa_wdt->base + FA_WDCR);
381 +       reg &= ~(WDCR_WDENABLE);
382 +       __raw_writel(reg, fa_wdt->base + FA_WDCR);
383 +
384 +       return 0;
385 +}
386 +
387 +static int fa_wdt_resume(struct platform_device *pdev)
388 +{
389 +       struct fa_wdt_struct *fa_wdt = platform_get_drvdata(pdev);
390 +       unsigned int reg;
391 +
392 +       if (fa_wdt->status) {
393 +               reg = __raw_readw(fa_wdt->base + FA_WDCR);
394 +               reg |= WDCR_WDENABLE;
395 +               __raw_writel(reg, fa_wdt->base + FA_WDCR);
396 +       }
397 +
398 +       return 0;
399 +}
400 +#else
401 +#define fa_wdt_suspend NULL
402 +#define fa_wdt_resume  NULL
403 +#endif
404 +
405 +static struct platform_driver fa_wdt_driver = {
406 +       .probe          = fa_wdt_probe,
407 +       .remove         = __devexit_p(fa_wdt_remove),
408 +       .shutdown       = fa_wdt_shutdown,
409 +       .suspend        = fa_wdt_suspend,
410 +       .resume         = fa_wdt_resume,
411 +       .driver         = {
412 +               .name   = "fa-wdt",
413 +               .owner  = THIS_MODULE,
414 +       },
415 +};
416 +
417 +static int __init fa_wdt_init(void)
418 +{
419 +       return platform_driver_probe(&fa_wdt_driver, fa_wdt_probe);
420 +}
421 +
422 +static void __exit fa_wdt_exit(void)
423 +{
424 +       platform_driver_unregister(&fa_wdt_driver);
425 +}
426 +
427 +module_init(fa_wdt_init);
428 +module_exit(fa_wdt_exit);
429 +
430 +module_param(timeout, uint, 0);
431 +MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds");
432 +
433 +module_param(nowayout, int, 0);
434 +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started");
435 +
436 +MODULE_AUTHOR("Paulius Zaleckas");
437 +MODULE_DESCRIPTION("Watchdog driver for Faraday FA526 based SoCs");
438 +MODULE_LICENSE("GPL v2");
439 +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
440 +MODULE_ALIAS("platform:fa-wdt");
441 --- /dev/null
442 +++ b/include/linux/fa_wdt.h
443 @@ -0,0 +1,13 @@
444 +/*
445 + * Platform data definition for the Faraday watchdog driver
446 + */
447 +
448 +#ifndef _FA_WDT_H
449 +#define _FA_WDT_H
450 +
451 +struct fa_wdt_platform_data {
452 +       unsigned int    clock;
453 +};
454 +
455 +#endif /* _FA_WDT_H */
456 +