diff --git a/ChangeLog b/ChangeLog index 31a4ecc3f3..9e2325a903 100644 --- a/ChangeLog +++ b/ChangeLog @@ -36,4 +36,5 @@ * Add dirent.h, opendir(), readdir(), closedir(), etc. * Added 'ls' command to nsh + * Added C5471 watchdog driver diff --git a/arch/c5471/include/watchdog.h b/arch/c5471/include/watchdog.h new file mode 100644 index 0000000000..dcf5e8f67a --- /dev/null +++ b/arch/c5471/include/watchdog.h @@ -0,0 +1,63 @@ +/************************************************************ + * watchdog.h + * + * Copyright (C) 2007 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * 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. + * + ************************************************************/ + +#ifndef __ARCH_WATCHDOG_H +#define __ARCH_WATCHDOG_H + +/************************************************************ + * Included Files + ************************************************************/ + +/************************************************************ + * Definitions + ************************************************************/ + +/* IOCTL commands supported by the C5471 watchdog driver */ + +#define WDIOC_KEEPALIVE 0x5701 /* Restart the watchdog timer */ + +/************************************************************ + * Private Data + ************************************************************/ + +/************************************************************ + * Private Functions + ************************************************************/ + +/************************************************************ + * Public Functions + ************************************************************/ + +#endif /* __ARCH_WATCHDOG_H */ diff --git a/arch/c5471/src/Makefile b/arch/c5471/src/Makefile index ec91753fac..b282cbf8df 100644 --- a/arch/c5471/src/Makefile +++ b/arch/c5471/src/Makefile @@ -51,7 +51,7 @@ CSRCS = up_initialize.c up_initialstate.c up_idle.c up_doirq.c \ up_exit.c up_assert.c up_blocktask.c up_unblocktask.c \ up_releasepending.c up_reprioritizertr.c up_copystate.c \ up_schedulesigaction.c up_sigdeliver.c up_serial.c \ - up_delay.c up_allocateheap.c up_leds.c + up_delay.c up_allocateheap.c up_leds.c up_watchdog.c COBJS = $(CSRCS:.c=.o) SRCS = $(ASRCS) $(CSRCS) diff --git a/arch/c5471/src/up_internal.h b/arch/c5471/src/up_internal.h index 390a914546..ec801b17fa 100644 --- a/arch/c5471/src/up_internal.h +++ b/arch/c5471/src/up_internal.h @@ -142,6 +142,10 @@ extern void up_vectorfiq(void); extern void up_earlyserialinit(void); extern void up_serialinit(void); +/* Defined in up_watchdog.c */ + +extern void up_wdtinit(void); + /* Defined in up_timerisr.c */ extern void up_timerinit(void); diff --git a/arch/c5471/src/up_watchdog.c b/arch/c5471/src/up_watchdog.c new file mode 100644 index 0000000000..09dd94fff8 --- /dev/null +++ b/arch/c5471/src/up_watchdog.c @@ -0,0 +1,392 @@ +/************************************************************************** + * up_watchdog.c + * + * Copyright (C) 2007 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include "c5471.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; +}