summaryrefslogtreecommitdiffstats
path: root/nuttx/arch/arm/src/c5471/c5471_watchdog.c
diff options
context:
space:
mode:
Diffstat (limited to 'nuttx/arch/arm/src/c5471/c5471_watchdog.c')
-rw-r--r--nuttx/arch/arm/src/c5471/c5471_watchdog.c392
1 files changed, 392 insertions, 0 deletions
diff --git a/nuttx/arch/arm/src/c5471/c5471_watchdog.c b/nuttx/arch/arm/src/c5471/c5471_watchdog.c
new file mode 100644
index 0000000000..04b390a5c8
--- /dev/null
+++ b/nuttx/arch/arm/src/c5471/c5471_watchdog.c
@@ -0,0 +1,392 @@
+/**************************************************************************
+ * c5471/c5471_watchdog.c
+ *
+ * Copyright (C) 2007 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <spudmonkey@racsa.co.cr>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name Gregory Nutt nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ **************************************************************************/
+
+/**************************************************************************
+ * Included Files
+ **************************************************************************/
+
+#include <nuttx/config.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <debug.h>
+#include <errno.h>
+#include <nuttx/fs.h>
+#include <nuttx/irq.h>
+#include <arch/watchdog.h>
+#include "up_arch.h"
+
+/**************************************************************************
+ * Definitions
+ **************************************************************************/
+
+#undef CONFIG_SOFTWARE_TEST
+#undef CONFIG_SOFTWARE_REBOOT
+#undef CONFIG_WATCHDOG_STRICT
+
+#define MAX_WDT_USEC 353200
+#define MAX_PRESCALER 256
+#define C5471_TIMER_STOP 0
+
+#define C5471_TIMER_PRESCALER 0x07 /* Bits 0-2: Prescale value */
+#define C5471_TIMER_STARTBIT (1 << 3) /* Bit 3: Start timer bit */
+#define C5471_TIMER_AUTORELOAD (1 << 4) /* Bit 4: Auto-reload timer */
+#define C5471_TIMER_LOADTIM (0xffff << 5) /* Bits 20-5: Load timer value */
+#define C5471_TIMER_MODE (1 << 21) /* Bit 21: Timer mode */
+#define C5471_DISABLE_VALUE1 (0xf5 << 22) /* Bits 29-22: WD disable */
+#define C5471_DISABLE_VALUE2 (0xa0 << 22)
+
+#define CLOCK_KHZ 47500
+#define CLOCK_MHZx2 95
+
+/* Macros to manage access to to watchdog timer macros */
+
+#define c5471_wdt_cntl (*(volatile uint32*)C5471_TIMER0_CTRL)
+#define c5471_wdt_count (*(volatile uint32*)C5471_TIMER0_CNT)
+
+/**************************************************************************
+ * Private Types
+ **************************************************************************/
+
+/**************************************************************************
+ * Private Function Prototypes
+ **************************************************************************/
+
+/* Local implementation of timer interface */
+
+static inline unsigned int wdt_prescaletoptv(unsigned int prescale);
+
+static int wdt_setusec(uint32 usec);
+static int wdt_interrupt(int irq, void *context);
+
+static int wdt_open(struct file *filep);
+static int wdt_close(struct file *filep);
+static ssize_t wdt_read(struct file *filep, char *buffer, size_t buflen);
+static ssize_t wdt_write(struct file *filep, const char *buffer, size_t buflen);
+static int wdt_ioctl(struct file *filep, int cmd, uint32 arg);
+
+/**************************************************************************
+ * Private Data
+ **************************************************************************/
+
+static boolean g_wdtopen;
+
+struct file_operations g_wdtops =
+{
+ .open = wdt_open,
+ .close = wdt_close,
+ .read = wdt_read,
+ .write = wdt_write,
+ .ioctl = wdt_ioctl,
+};
+
+/**************************************************************************
+ * Private Functions
+ **************************************************************************/
+
+/**************************************************************************
+ * Name: wdt_prescaletoptv
+ **************************************************************************/
+
+static inline unsigned int wdt_prescaletoptv(unsigned int prescale)
+{
+ unsigned int ptv = 0;
+
+ if (prescale > 255)
+ {
+ ptv = 7;
+ }
+ else
+ {
+ unsigned int value = prescale >> 1;
+
+ /* 0: 0-2
+ * 1: 3-4
+ * 2: 5-8
+ * 3: 9-16
+ * 4: 17-32
+ * 5: 33-64
+ * 6: 65-128
+ * 7: 129-
+ */
+
+ while (value > 1)
+ {
+ value >>= 1;
+ ptv++;
+ }
+ }
+
+ dbg("prescale=%d -> ptv=%d\n", prescale, ptv);
+ return ptv;
+}
+
+/**************************************************************************
+ * Name: wdt_setusec
+ **************************************************************************/
+
+static int wdt_setusec(uint32 usec)
+{
+ /* prescaler: clock / prescaler = #clock ticks per counter in ptv
+ * divisor: #counts until the interrupt comes.
+ */
+
+ uint32 prescaler = MAX_PRESCALER;
+ uint32 divisor = 1;
+ uint32 mode;
+
+ dbg("usec=%d\n", usec);
+
+ /* Calculate a value of prescaler and divisor that will be able
+ * to count to the usec. It may not be exact or the best
+ * possible set, but it's a quick and simple algorithm.
+ *
+ * divisor max = 0x10000
+ * prescaler max = MAX_PRESCALER
+ */
+
+ do
+ {
+ divisor = (CLOCK_MHZx2 * usec) / (prescaler * 2);
+ dbg("divisor=0x%x prescaler=0x%x\n", divisor, prescaler);
+
+ if (divisor >= 0x10000)
+ {
+ if (prescaler == MAX_PRESCALER)
+ {
+ /* This is the max possible ~2.5 seconds. */
+
+ dbg("prescaler=0x%x too big!\n", prescaler);
+ return ERROR;
+ }
+
+ prescaler <<= 1;
+ if (prescaler > MAX_PRESCALER)
+ {
+ prescaler = MAX_PRESCALER;
+ }
+ }
+ }
+ while (divisor >= 0x10000);
+
+ dbg("prescaler=0x%x divisor=0x%x\n", prescaler, divisor);
+
+ mode = wdt_prescaletoptv(prescaler);
+ mode &= ~C5471_TIMER_AUTORELOAD; /* One shot mode. */
+ mode |= divisor << 5;
+ dbg("mode=0x%x\n", mode);
+
+ c5471_wdt_cntl = mode;
+
+ /* Now start the watchdog */
+
+ c5471_wdt_cntl |= C5471_TIMER_STARTBIT;
+ dbg("cntl_timer=0x%x\n", c5471_wdt_cntl);
+
+ return 0;
+}
+
+/**************************************************************************
+ * Private Functions
+ **************************************************************************/
+
+/**************************************************************************
+ * Name: wdt_interrupt
+ **************************************************************************/
+
+static int wdt_interrupt(int irq, void *context)
+{
+ dbg("expired\n");
+
+#if defined(CONFIG_SOFTWARE_REBOOT)
+# if defined(CONFIG_SOFTWARE_TEST)
+ dbg(" Test only\n");
+# else
+ dbg(" Re-booting\n");
+# warning "Add logic to reset CPU here"
+# endif
+#else
+ dbg(" No reboot\n");
+#endif
+ return OK;
+}
+
+/**************************************************************************
+ * Name: wdt_read
+ **************************************************************************/
+
+static ssize_t wdt_read(struct file *filep, char *buffer, size_t buflen)
+{
+ /* We are going to return "NNNNNNNN NNNNNNNN." The followig logic will
+ * not work if the user provides a buffer smaller than 18 bytes.
+ */
+
+ dbg("buflen=%d\n", buflen);
+ if (buflen >= 18)
+ {
+ sprintf(buffer, "#08x %08x\n", c5471_wdt_cntl, c5471_wdt_count);
+ return 18;
+ }
+ return 0;
+}
+
+/**************************************************************************
+ * Name: wdt_write
+ **************************************************************************/
+
+static ssize_t wdt_write(struct file *filep, const char *buffer, size_t buflen)
+{
+ dbg("buflen=%d\n", buflen);
+ if (buflen)
+ {
+ /* Reset the timer to the maximum delay */
+
+ wdt_setusec(MAX_WDT_USEC);
+ return 1;
+ }
+
+ return 0;
+}
+
+/**************************************************************************
+ * Name: wdt_ioctl
+ **************************************************************************/
+
+static int wdt_ioctl(struct file *filep, int cmd, uint32 arg)
+{
+ dbg("ioctl Call: cmd=0x%x arg=0x%x", cmd, arg);
+
+ /* Process the the IOCTL command (see arch/watchdog.h) */
+
+ switch(cmd)
+ {
+ case WDIOC_KEEPALIVE:
+ wdt_setusec(MAX_WDT_USEC);
+ break;
+
+ default:
+ *get_errno_ptr() = ENOTTY;
+ return ERROR;
+ }
+
+ return OK;
+}
+
+/**************************************************************************
+ * Name: wdt_open
+ **************************************************************************/
+
+static int wdt_open(struct file *filep)
+{
+ dbg("");
+
+ if (g_wdtopen)
+ {
+ *get_errno_ptr() = EBUSY;
+ }
+
+ /* This will automatically load the timer with its max
+ * count and start it running.
+ */
+
+ c5471_wdt_cntl = C5471_DISABLE_VALUE1;
+ c5471_wdt_cntl = C5471_DISABLE_VALUE2;
+
+ g_wdtopen = TRUE;
+ return OK;
+}
+
+/**************************************************************************
+ * Name: wdt_close
+ **************************************************************************/
+
+static int wdt_close(struct file *filep)
+{
+ dbg("");
+
+ /* The task controlling the watchdog has terminated. Take the timer
+ * the
+ * watchdog in interrupt mode -- we are going to reset unless the
+ * reopened again soon.
+ */
+
+#ifndef CONFIG_WATCHDOG_STRICT
+ c5471_wdt_cntl = C5471_TIMER_MODE;
+#endif
+
+ g_wdtopen = FALSE;
+ return 0;
+}
+
+/**************************************************************************
+ * Public Functions
+ **************************************************************************/
+
+/**************************************************************************
+ * Name: up_wdtinit
+ **************************************************************************/
+
+int up_wdtinit(void)
+{
+ int ret;
+
+ dbg("C547x Watchdog Driver\n");
+
+ /* Register as /dev/wdt */
+
+ ret = register_inode("/dev/wdt", &g_wdtops, 0666, NULL);
+ if (ret)
+ {
+ return ERROR;
+ }
+
+ /* Register for an interrupt level callback through wdt_interrupt */
+
+ dbg("Attach to IRQ=%d\n", C5471_IRQ_WATCHDOG);
+
+ /* Make sure that the timer is stopped */
+
+ c5471_wdt_cntl = C5471_TIMER_STOP;
+
+ /* Request the interrupt. */
+
+ ret = irq_attach(C5471_IRQ_WATCHDOG, wdt_interrupt);
+ if (ret)
+ {
+ unregister_inode("/dev/wdt");
+ return ERROR;
+ }
+
+ return OK;
+}