Commit 9325fa36151fb9af39e697a6cd87e82667e2d8f9

Authored by Vitaly Wool
Committed by Wim Van Sebroeck
1 parent 92dd9994c3

[WATCHDOG] pnx4008: add watchdog support

Add watchdog support for Philips PNX4008 ARM board inlined.

Signed-off-by: Vitaly Wool <vitalywool@gmail.com>
Signed-off-by: Wim Van Sebroeck <wim@iguana.be>

Showing 5 changed files with 377 additions and 0 deletions Side-by-side Diff

arch/arm/mach-pnx4008/clock.c
... ... @@ -735,6 +735,16 @@
735 735 .enable_reg = UARTCLKCTRL_REG,
736 736 };
737 737  
  738 +static struct clk wdt_ck = {
  739 + .name = "wdt_ck",
  740 + .parent = &per_ck,
  741 + .flags = NEEDS_INITIALIZATION,
  742 + .round_rate = &on_off_round_rate,
  743 + .set_rate = &on_off_set_rate,
  744 + .enable_shift = 0,
  745 + .enable_reg = TIMCLKCTRL_REG,
  746 +};
  747 +
738 748 /* These clocks are visible outside this module
739 749 * and can be initialized
740 750 */
... ... @@ -765,6 +775,7 @@
765 775 &uart4_ck,
766 776 &uart5_ck,
767 777 &uart6_ck,
  778 + &wdt_ck,
768 779 };
769 780  
770 781 static int local_clk_enable(struct clk *clk)
drivers/char/watchdog/Kconfig
... ... @@ -172,6 +172,17 @@
172 172 Support for TI OMAP1610/OMAP1710/OMAP2420 watchdog. Say 'Y' here to
173 173 enable the OMAP1610/OMAP1710 watchdog timer.
174 174  
  175 +config PNX4008_WATCHDOG
  176 + tristate "PNX4008 Watchdog"
  177 + depends on WATCHDOG && ARCH_PNX4008
  178 + help
  179 + Say Y here if to include support for the watchdog timer
  180 + in the PNX4008 processor.
  181 + This driver can be built as a module by choosing M. The module
  182 + will be called pnx4008_wdt.
  183 +
  184 + Say N if you are unsure.
  185 +
175 186 # X86 (i386 + ia64 + x86_64) Architecture
176 187  
177 188 config ACQUIRE_WDT
drivers/char/watchdog/Makefile
... ... @@ -33,6 +33,7 @@
33 33 obj-$(CONFIG_SA1100_WATCHDOG) += sa1100_wdt.o
34 34 obj-$(CONFIG_MPCORE_WATCHDOG) += mpcore_wdt.o
35 35 obj-$(CONFIG_EP93XX_WATCHDOG) += ep93xx_wdt.o
  36 +obj-$(CONFIG_PNX4008_WATCHDOG) += pnx4008_wdt.o
36 37  
37 38 # X86 (i386 + ia64 + x86_64) Architecture
38 39 obj-$(CONFIG_ACQUIRE_WDT) += acquirewdt.o
drivers/char/watchdog/pnx4008_wdt.c
  1 +/*
  2 + * drivers/char/watchdog/pnx4008_wdt.c
  3 + *
  4 + * Watchdog driver for PNX4008 board
  5 + *
  6 + * Authors: Dmitry Chigirev <source@mvista.com>,
  7 + * Vitaly Wool <vitalywool@gmail.com>
  8 + * Based on sa1100 driver,
  9 + * Copyright (C) 2000 Oleg Drokin <green@crimea.edu>
  10 + *
  11 + * 2005-2006 (c) MontaVista Software, Inc. This file is licensed under
  12 + * the terms of the GNU General Public License version 2. This program
  13 + * is licensed "as is" without any warranty of any kind, whether express
  14 + * or implied.
  15 + */
  16 +
  17 +#include <linux/config.h>
  18 +#include <linux/module.h>
  19 +#include <linux/moduleparam.h>
  20 +#include <linux/types.h>
  21 +#include <linux/kernel.h>
  22 +#include <linux/fs.h>
  23 +#include <linux/miscdevice.h>
  24 +#include <linux/watchdog.h>
  25 +#include <linux/init.h>
  26 +#include <linux/bitops.h>
  27 +#include <linux/ioport.h>
  28 +#include <linux/device.h>
  29 +#include <linux/platform_device.h>
  30 +#include <linux/clk.h>
  31 +
  32 +#include <asm/hardware.h>
  33 +#include <asm/uaccess.h>
  34 +#include <asm/io.h>
  35 +
  36 +#define MODULE_NAME "PNX4008-WDT: "
  37 +
  38 +/* WatchDog Timer - Chapter 23 Page 207 */
  39 +
  40 +#define DEFAULT_HEARTBEAT 19
  41 +#define MAX_HEARTBEAT 60
  42 +
  43 +/* Watchdog timer register set definition */
  44 +#define WDTIM_INT(p) ((p) + 0x0)
  45 +#define WDTIM_CTRL(p) ((p) + 0x4)
  46 +#define WDTIM_COUNTER(p) ((p) + 0x8)
  47 +#define WDTIM_MCTRL(p) ((p) + 0xC)
  48 +#define WDTIM_MATCH0(p) ((p) + 0x10)
  49 +#define WDTIM_EMR(p) ((p) + 0x14)
  50 +#define WDTIM_PULSE(p) ((p) + 0x18)
  51 +#define WDTIM_RES(p) ((p) + 0x1C)
  52 +
  53 +/* WDTIM_INT bit definitions */
  54 +#define MATCH_INT 1
  55 +
  56 +/* WDTIM_CTRL bit definitions */
  57 +#define COUNT_ENAB 1
  58 +#define RESET_COUNT (1<<1)
  59 +#define DEBUG_EN (1<<2)
  60 +
  61 +/* WDTIM_MCTRL bit definitions */
  62 +#define MR0_INT 1
  63 +#undef RESET_COUNT0
  64 +#define RESET_COUNT0 (1<<2)
  65 +#define STOP_COUNT0 (1<<2)
  66 +#define M_RES1 (1<<3)
  67 +#define M_RES2 (1<<4)
  68 +#define RESFRC1 (1<<5)
  69 +#define RESFRC2 (1<<6)
  70 +
  71 +/* WDTIM_EMR bit definitions */
  72 +#define EXT_MATCH0 1
  73 +#define MATCH_OUTPUT_HIGH (2<<4) /*a MATCH_CTRL setting */
  74 +
  75 +/* WDTIM_RES bit definitions */
  76 +#define WDOG_RESET 1 /* read only */
  77 +
  78 +#define WDOG_COUNTER_RATE 13000000 /*the counter clock is 13 MHz fixed */
  79 +
  80 +#ifdef CONFIG_WATCHDOG_NOWAYOUT
  81 +static int nowayout = 1;
  82 +#else
  83 +static int nowayout = 0;
  84 +#endif
  85 +static int heartbeat = DEFAULT_HEARTBEAT;
  86 +
  87 +static unsigned long wdt_status;
  88 +#define WDT_IN_USE 0
  89 +#define WDT_OK_TO_CLOSE 1
  90 +#define WDT_REGION_INITED 2
  91 +#define WDT_DEVICE_INITED 3
  92 +
  93 +static unsigned long boot_status;
  94 +
  95 +static struct resource *wdt_mem;
  96 +static void __iomem *wdt_base;
  97 +struct clk *wdt_clk;
  98 +
  99 +static void wdt_enable(void)
  100 +{
  101 + if (wdt_clk)
  102 + clk_set_rate(wdt_clk, 1);
  103 +
  104 + /* stop counter, initiate counter reset */
  105 + __raw_writel(RESET_COUNT, WDTIM_CTRL(wdt_base));
  106 + /*wait for reset to complete. 100% guarantee event */
  107 + while (__raw_readl(WDTIM_COUNTER(wdt_base)));
  108 + /* internal and external reset, stop after that */
  109 + __raw_writel(M_RES2 | STOP_COUNT0 | RESET_COUNT0,
  110 + WDTIM_MCTRL(wdt_base));
  111 + /* configure match output */
  112 + __raw_writel(MATCH_OUTPUT_HIGH, WDTIM_EMR(wdt_base));
  113 + /* clear interrupt, just in case */
  114 + __raw_writel(MATCH_INT, WDTIM_INT(wdt_base));
  115 + /* the longest pulse period 65541/(13*10^6) seconds ~ 5 ms. */
  116 + __raw_writel(0xFFFF, WDTIM_PULSE(wdt_base));
  117 + __raw_writel(heartbeat * WDOG_COUNTER_RATE, WDTIM_MATCH0(wdt_base));
  118 + /*enable counter, stop when debugger active */
  119 + __raw_writel(COUNT_ENAB | DEBUG_EN, WDTIM_CTRL(wdt_base));
  120 +}
  121 +
  122 +static void wdt_disable(void)
  123 +{
  124 + __raw_writel(0, WDTIM_CTRL(wdt_base)); /*stop counter */
  125 + if (wdt_clk)
  126 + clk_set_rate(wdt_clk, 0);
  127 +}
  128 +
  129 +static int pnx4008_wdt_open(struct inode *inode, struct file *file)
  130 +{
  131 + if (test_and_set_bit(WDT_IN_USE, &wdt_status))
  132 + return -EBUSY;
  133 +
  134 + clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
  135 +
  136 + wdt_enable();
  137 +
  138 + return nonseekable_open(inode, file);
  139 +}
  140 +
  141 +static ssize_t
  142 +pnx4008_wdt_write(struct file *file, const char *data, size_t len,
  143 + loff_t * ppos)
  144 +{
  145 + /* Can't seek (pwrite) on this device */
  146 + if (ppos != &file->f_pos)
  147 + return -ESPIPE;
  148 +
  149 + if (len) {
  150 + if (!nowayout) {
  151 + size_t i;
  152 +
  153 + clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
  154 +
  155 + for (i = 0; i != len; i++) {
  156 + char c;
  157 +
  158 + if (get_user(c, data + i))
  159 + return -EFAULT;
  160 + if (c == 'V')
  161 + set_bit(WDT_OK_TO_CLOSE, &wdt_status);
  162 + }
  163 + }
  164 + wdt_enable();
  165 + }
  166 +
  167 + return len;
  168 +}
  169 +
  170 +static struct watchdog_info ident = {
  171 + .options = WDIOF_CARDRESET | WDIOF_MAGICCLOSE |
  172 + WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
  173 + .identity = "PNX4008 Watchdog",
  174 +};
  175 +
  176 +static int
  177 +pnx4008_wdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
  178 + unsigned long arg)
  179 +{
  180 + int ret = -ENOIOCTLCMD;
  181 + int time;
  182 +
  183 + switch (cmd) {
  184 + case WDIOC_GETSUPPORT:
  185 + ret = copy_to_user((struct watchdog_info *)arg, &ident,
  186 + sizeof(ident)) ? -EFAULT : 0;
  187 + break;
  188 +
  189 + case WDIOC_GETSTATUS:
  190 + ret = put_user(0, (int *)arg);
  191 + break;
  192 +
  193 + case WDIOC_GETBOOTSTATUS:
  194 + ret = put_user(boot_status, (int *)arg);
  195 + break;
  196 +
  197 + case WDIOC_SETTIMEOUT:
  198 + ret = get_user(time, (int *)arg);
  199 + if (ret)
  200 + break;
  201 +
  202 + if (time <= 0 || time > MAX_HEARTBEAT) {
  203 + ret = -EINVAL;
  204 + break;
  205 + }
  206 +
  207 + heartbeat = time;
  208 + wdt_enable();
  209 + /* Fall through */
  210 +
  211 + case WDIOC_GETTIMEOUT:
  212 + ret = put_user(heartbeat, (int *)arg);
  213 + break;
  214 +
  215 + case WDIOC_KEEPALIVE:
  216 + wdt_enable();
  217 + ret = 0;
  218 + break;
  219 + }
  220 + return ret;
  221 +}
  222 +
  223 +static int pnx4008_wdt_release(struct inode *inode, struct file *file)
  224 +{
  225 + if (!test_bit(WDT_OK_TO_CLOSE, &wdt_status))
  226 + printk(KERN_WARNING "WATCHDOG: Device closed unexpectdly\n");
  227 +
  228 + wdt_disable();
  229 + clear_bit(WDT_IN_USE, &wdt_status);
  230 + clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
  231 +
  232 + return 0;
  233 +}
  234 +
  235 +static struct file_operations pnx4008_wdt_fops = {
  236 + .owner = THIS_MODULE,
  237 + .llseek = no_llseek,
  238 + .write = pnx4008_wdt_write,
  239 + .ioctl = pnx4008_wdt_ioctl,
  240 + .open = pnx4008_wdt_open,
  241 + .release = pnx4008_wdt_release,
  242 +};
  243 +
  244 +static struct miscdevice pnx4008_wdt_miscdev = {
  245 + .minor = WATCHDOG_MINOR,
  246 + .name = "watchdog",
  247 + .fops = &pnx4008_wdt_fops,
  248 +};
  249 +
  250 +static int pnx4008_wdt_probe(struct platform_device *pdev)
  251 +{
  252 + int ret = 0, size;
  253 + struct resource *res;
  254 +
  255 + if (heartbeat < 1 || heartbeat > MAX_HEARTBEAT)
  256 + heartbeat = DEFAULT_HEARTBEAT;
  257 +
  258 + printk(KERN_INFO MODULE_NAME
  259 + "PNX4008 Watchdog Timer: heartbeat %d sec\n", heartbeat);
  260 +
  261 + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  262 + if (res == NULL) {
  263 + printk(KERN_INFO MODULE_NAME
  264 + "failed to get memory region resouce\n");
  265 + return -ENOENT;
  266 + }
  267 +
  268 + size = res->end - res->start + 1;
  269 + wdt_mem = request_mem_region(res->start, size, pdev->name);
  270 +
  271 + if (wdt_mem == NULL) {
  272 + printk(KERN_INFO MODULE_NAME "failed to get memory region\n");
  273 + return -ENOENT;
  274 + }
  275 + wdt_base = (void __iomem *)IO_ADDRESS(res->start);
  276 +
  277 + wdt_clk = clk_get(&pdev->dev, "wdt_ck");
  278 + if (!wdt_clk) {
  279 + release_resource(wdt_mem);
  280 + kfree(wdt_mem);
  281 + goto out;
  282 + } else
  283 + clk_set_rate(wdt_clk, 1);
  284 +
  285 + ret = misc_register(&pnx4008_wdt_miscdev);
  286 + if (ret < 0) {
  287 + printk(KERN_ERR MODULE_NAME "cannot register misc device\n");
  288 + release_resource(wdt_mem);
  289 + kfree(wdt_mem);
  290 + clk_set_rate(wdt_clk, 0);
  291 + } else {
  292 + boot_status = (__raw_readl(WDTIM_RES(wdt_base)) & WDOG_RESET) ?
  293 + WDIOF_CARDRESET : 0;
  294 + wdt_disable(); /*disable for now */
  295 + set_bit(WDT_DEVICE_INITED, &wdt_status);
  296 + }
  297 +
  298 +out:
  299 + return ret;
  300 +}
  301 +
  302 +static int pnx4008_wdt_remove(struct platform_device *pdev)
  303 +{
  304 + if (wdt_mem) {
  305 + release_resource(wdt_mem);
  306 + kfree(wdt_mem);
  307 + wdt_mem = NULL;
  308 + }
  309 + if (wdt_clk) {
  310 + clk_set_rate(wdt_clk, 0);
  311 + clk_put(wdt_clk);
  312 + wdt_clk = NULL;
  313 + }
  314 + misc_deregister(&pnx4008_wdt_miscdev);
  315 + return 0;
  316 +}
  317 +
  318 +static struct platform_driver platform_wdt_driver = {
  319 + .driver = {
  320 + .name = "watchdog",
  321 + },
  322 + .probe = pnx4008_wdt_probe,
  323 + .remove = pnx4008_wdt_remove,
  324 +};
  325 +
  326 +static int __init pnx4008_wdt_init(void)
  327 +{
  328 + return platform_driver_register(&platform_wdt_driver);
  329 +}
  330 +
  331 +static void __exit pnx4008_wdt_exit(void)
  332 +{
  333 + return platform_driver_unregister(&platform_wdt_driver);
  334 +}
  335 +
  336 +module_init(pnx4008_wdt_init);
  337 +module_exit(pnx4008_wdt_exit);
  338 +
  339 +MODULE_AUTHOR("MontaVista Software, Inc. <source@mvista.com>");
  340 +MODULE_DESCRIPTION("PNX4008 Watchdog Driver");
  341 +
  342 +module_param(heartbeat, int, 0);
  343 +MODULE_PARM_DESC(heartbeat,
  344 + "Watchdog heartbeat period in seconds from 1 to "
  345 + __MODULE_STRING(MAX_HEARTBEAT) ", default "
  346 + __MODULE_STRING(DEFAULT_HEARTBEAT));
  347 +
  348 +module_param(nowayout, int, 0);
  349 +MODULE_PARM_DESC(nowayout,
  350 + "Set to 1 to keep watchdog running after device release");
  351 +
  352 +MODULE_LICENSE("GPL");
  353 +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
include/asm-arm/arch-pnx4008/clock.h
... ... @@ -32,6 +32,7 @@
32 32 #define KEYCLKCTRL_REG (PWRMAN_VA_BASE + 0xb0)
33 33 #define TSCLKCTRL_REG (PWRMAN_VA_BASE + 0xb4)
34 34 #define PWMCLKCTRL_REG (PWRMAN_VA_BASE + 0xb8)
  35 +#define TIMCLKCTRL_REG (PWRMAN_VA_BASE + 0xbc)
35 36 #define SPICTRL_REG (PWRMAN_VA_BASE + 0xc4)
36 37 #define FLASHCLKCTRL_REG (PWRMAN_VA_BASE + 0xc8)
37 38 #define UART3CLK_REG (PWRMAN_VA_BASE + 0xd0)