/*
 *  Copyright (c) 1996-2007 Digi International Inc., All Rights Reserved
 *
 *  This software contains proprietary and confidential information of Digi
 *  International Inc.  By accepting transfer of this copy, Recipient agrees
 *  to retain this software in confidence, to prevent disclosure to others,
 *  and to make no use of this software other than that for which it was
 *  delivered.  This is an unpublished copyrighted work of Digi International
 *  Inc.  Except as permitted by federal law, 17 USC 117, copying is strictly
 *  prohibited.
 *
 *  Restricted Rights Legend
 *
 *  Use, duplication, or disclosure by the Government is subject to
 *  restrictions set forth in sub-paragraph (c)(1)(ii) of The Rights in
 *  Technical Data and Computer Software clause at DFARS 252.227-7031 or
 *  subparagraphs (c)(1) and (2) of the Commercial Computer Software -
 *  Restricted Rights at 48 CFR 52.227-19, as applicable.
 *
 *  Digi International Inc. 11001 Bren Road East, Minnetonka, MN 55343
 *
 * =======================================================================
 *  The routines that control the external watchdog chip are implemented 
 *  here.  We assume that a watchdog chip is on board and the WDI pin on the chip is
 *  connected to pin PORTF4 on the processor.  The watchdog is idle after 
 *  power up or a reset.  It does not start a timeout until after we start
 *  pulsing the WDI pin.  Once we start pulsing it, we have to pulse it
 *  fast enough to prevent the thing from timing out and resetting us. 
 *  The actual timeout varies from chip to chip from a minimum of .9 
 *  seconds to a maximum of 2.5 seconds.  The typical timeout value is
 *  suppose to be 1.6 seconds.
 *
 *  If BSP_WATCHDOG_TYPE is defined to be BSP_WATCHDOG_INTERNAL in bsp.h, these
 *  routines will enable the internal watchdog time circuit on NETARM chip to 
 *  to cause Reset. The watchdog is serviced by writing 0x5A and 0xA5 to the
 *  Software Service register using two separate write operations.
 *
 *  We provide routines that can be called to service the chip, and a
 *  routine that uses the ThreadX timer to service the chip.
 *
 *
 *
 * Edit Date/Ver   Edit Description
 * ==============  =======================================================
 *
 *
 */ 
#include "wd_gpio.h"
#include <reg_def.h>
#include <bsp_sys.h>
#include <tx_api.h>
#include <watchdog.h>
#include "sysClock.h"

/*ad*
 * Watchdog functions can be provided from an external watchdog chip or from
 * NETARM chips' internal watchdog circuit. 
 *
 * If external watchdog chip is used, the watchdog driver APIs configure and 
 * pulse the gpio pin connected with the watchdog chip. The watchdog is idle 
 * after power up or a reset.  It does not start a timeout until after we start 
 * pulsing the WDI pin.  Once we start pulsing it, we have to pulse it fast 
 * enough to prevent the thing from timing out and resetting us. 
 *
 * If internal watchdog is used, the driver APIs configure and enable the 
 * internal watchdog time circuit on NETARM chip to cause Reset. The watchdog 
 * is serviced by writing 0x5A and 0xA5 to the software service register using 
 * two separate write operations. 
 *
 * The driver uses the Threadx timer to periodically service the chip.
 * 
 * 
 *
 * @external
 * @category WatchdogDriver
 * @since 6.0
 * @overview Watchdog Driver
 *
*ad*/

/*
 * Dummy value for parameter passed to watchdog service function.
 */
#define NOT_USED        0


/*
 * The watchdog input bit.
 */
#define WATCHDOG_BIT        (1 << GPIO_WATCHDOG)



/*
 * This constant reflects the recommended watchdog service rate. We assume
 * timeout value for the watchdog chip can be anywhere from .9 to 2.5 
 * seconds, with the typical value being 1.6 seconds.  We recommend
 * servicing the device every 500 milliseconds.
 */
#define WATCHDOG_RECOMMENDED_SERVICE_INTERVAL (BSP_TICKS_PER_SECOND / 2)



/*
 * ThreadX Timer structure used to create periodic service function.
 */
static TX_TIMER watchdogTimer;

/*
 * This flag is set if the watdog timer service function has been
 * registered with ThreadX.
 */
static int serviceFunctionRegistered = FALSE;


/*
 *
 *  Function: void delay_one_microsecond(void)
 *
 *  Description:
 *
 *      A simple loop that guarentees at least a one microsecond delay
 *      one the NETA-50.
 *
 *  Parameters:
 *
 *      none
 *
 *  Return Values:
 *
 *      return value is just so compiler doesn't optimize out the loop
 *
 */
#if (BSP_WATCHDOG_TYPE == BSP_WATCHDOG_EXTERNAL)
static int delay_one_microsecond(void)
{
    unsigned timer, scratch = 0;

    for (timer = 0; timer < ONE_MICROSECOND; timer++)
    {
        scratch += timer;
    }

    return scratch;
}
#endif





/*ad*
 * Pulses the watchdog chip once.  This routine
 * reconfigures GPIO pin 4 for output if it has not already been
 * so configured.  The watchdog is serviced by setting the pin high, waiting
 * one microsecond, and then setting it low again.
 * 
 * If BSP_WATCHDOG_TYPE is defined to be BSP_WATCHDOG_INTERNAL in bsp.h, this
 * routine enables the internal watchdog time circuit on NETARM chip to 
 * to cause Reset. The watchdog is serviced by writing $5A abd $A5 to the
 * Software Service register using two separate write operations.
 *
 * @since 6.0
 * @external
 * @category WatchdogDriver
*ad*/
void watchdogServiceOnce(void)
{
    static int initialized = FALSE;         /* set if we have serviced watchdog before*/
    

#if (BSP_WATCHDOG_TYPE == BSP_WATCHDOG_EXTERNAL)
	TX_INTERRUPT_SAVE_AREA;
    if (!initialized)
    {
        unsigned temp;
        /*
         * The low level initialization routine configures GPIO pin B4 to be an input.
         * This tristates it so that we don't start sending pulses until we are ready
         * to.  Well, now we're ready to.  So, reconfigure the pin as an output and
         * set it low.
         */

		/* The low level initialization routine configures GPIO pin B4 to be an input.      
		 *where do I put the this low level initialization? here or gpio.c????????????????
         */
                                            /* set direction bit to output*/
        TX_DISABLE;
        temp = GPIO_PORTF | (GPIO_SET_OUTPUT << (GPIO_DIR_SHIFT + GPIO_WATCHDOG));
        temp &= ~GPIO_WATCHDOG;             /* make sure data bit is zero*/
        GPIO_PORTF = temp;                  /* write the new configuration setting*/
        TX_RESTORE;
        initialized = TRUE;                 /* set flag*/
        delay_one_microsecond();            /* give data bit time to settle down so we start with WDI low*/
    }

    TX_DISABLE;
    GPIO_PORTF |= WATCHDOG_BIT;             /* raise WDI bit*/
    TX_RESTORE;
    delay_one_microsecond();                /* minimum pulse width is one microsecond*/
    TX_DISABLE;
    GPIO_PORTF &= ~WATCHDOG_BIT;            /* lower WDI bit*/
    TX_RESTORE;
#else
	if (!initialized)
    {          
		narm_write_reg(NARM_GSCR_REG, NARM_GSCR_ADDR, swt, 2);  /* time out period is (2**24)/F_XTAL */
		narm_write_reg(NARM_GSCR_REG, NARM_GSCR_ADDR, swri, 2); /* program watch dog to create reset*/
		narm_write_reg(NARM_GSCR_REG, NARM_GSCR_ADDR, swe, 1);  /* enable the watchdog timer circuit*/
		initialized = TRUE;                 /* set flag*/
	 
    }

    *NARM_GSWSR_ADDR = 0x5A;
	*NARM_GSWSR_ADDR = 0xA5;

#endif

}


/*
 * This function returns the approximate watchdog timeout value in milliseconds 
 * set by the
 * *link watchdogServiceOnce and
 * *link service_watchdog functions.
 *
 * *external
 * *category BSP:Device_Drivers:WatchdogDriver
 * *since 7.4
 * *include 		"watchdog.h"
 * *platform 		all
 * *library 		bsp
 * *overview Watchdog Driver
 *
 * *return timeout      watchdog timeout value set by the watchdogServiceOnce
 *                      and service_watchdog functions.
 *
 */
unsigned int watchdogTimeoutInMilliseconds(void)
{
    #define MILLISECONDS_IN_A_SECOND        (1000LL)
    #define VALUE_SET_IN_SWT                (1 << 24)
    #define UINT_MASK                       (0xffffffff)
    #define FXTALE                          (NAgetPllOutputClkFreq() / 5)
    
    /*
     * watchdogServiceOnce sets the watchdog timer to be 2^^24/FXTALE.  FXTALE is
     * 1/5 the PLL output frequency.  The value will be something like 1.525 or
     * 4.35 seconds.  
     *
     * We convert to milliseconds so that we don't lose the digits after the
     * decimal point.
     */
    return (unsigned int) (((VALUE_SET_IN_SWT * MILLISECONDS_IN_A_SECOND)/FXTALE) & UINT_MASK);
}





/*
 *
 *  Function: void service_watchdog(ULONG notUsed)
 *
 *  Description:
 *
 *      This function is registered with the ThreadX timer service to be 
 *      called periodically.  It calls watchdogServiceOnce() to service
 *      the watchdog chip.  This function exists because ThreadX timer
 *      functions are passed a parameter.  It makes no sense to make
 *      watchdogServiceOnce() accept this parameter, so this function
 *      is provided as a shell around watchdogServiceOnce().
 *
 *  Parameters:
 *
 *      notUsed         parameter from Thread timer service which we don't
 *                      need or want
 *
 *  Return Values:
 *
 *      none
 *
 */

static void service_watchdog(ULONG notUsed)
{
    notUsed++;                      /* suppress compiler warning*/

    watchdogServiceOnce();
}







/*ad*
 * Registers timer event with the ThreadX kernel
 *
 * The event services the watchdog as long as the kernel
 * is functioning.  The watchdog resets the processor
 * if it stops being services (if the kernel locks up).
 *
 * The service interval is 500 milliseconds.
 *
 * If the timer event is successfully registered, this function
 * calls watchdogServiceOnce to wake up the watchdog chip.
 *
 * @since 6.0
 * @external
 * @category WatchdogDriver
 *
 * @return WATCHDOG_SUCCESS                       The timer event was successfully created.
 * @return WATCHDOG_ALREADY_SERVICING_WATCHDOG	  The timer event is already created.
 * @return WATCHDOG_ERROR_CREATING_TIMER_TASK     error creating timer event.
 *
 * @see @link watchdog_api_errors
*ad*/

WATCHDOG_CCODE watchdogServicePeriodically(void)
{
    WATCHDOG_CCODE result = WATCHDOG_SUCCESS;

    if (serviceFunctionRegistered)          /* if we already did this*/
    {
        result = WATCHDOG_ALREADY_SERVICING_WATCHDOG;
    }
    else
    {
        if (tx_timer_create (&watchdogTimer, "watchdog", service_watchdog, 
            NOT_USED, WATCHDOG_RECOMMENDED_SERVICE_INTERVAL, 
            WATCHDOG_RECOMMENDED_SERVICE_INTERVAL, TX_AUTO_ACTIVATE) == TX_SUCCESS)
        {                                   /* if successfully started timer, set flag*/
            serviceFunctionRegistered = TRUE;
            watchdogServiceOnce();          /* wake up the watchdog chip*/
        }
        else                                /* if couldn't start timer*/
        {                                   /* set ccode to indicate result, don't wake up chip*/
            result = WATCHDOG_ERROR_CREATING_TIMER_TASK;
        }
    }

    return result;
}












/*ad*
 * Cancels the timer task registered by
 * watchdogServicePeriodically    This has the side effect
 * of causing the system to reset in about 1.6 seconds 
 * unless the application makes sure the watchdog chip is
 * serviced.
 *
 * If BSP_WATCHDOG_TYPE is defined to be BSP_WATCHDOG_INTERNAL in bsp.h, this
 * routine will cause the system to reset. the time out is depends on what
 * the value was set to bits SWT in System Control Register.
 *
 * @since 6.0
 * @external
 * @category WatchdogDriver
 *
 * @return WATCHDOG_SUCCESS               The timer event is canceled.
 * @return WATCHDOG_WAS_NOT_SERVICING	  The timer event was not created.
 *
 * @see @link watchdog_api_errors
*ad*/

WATCHDOG_CCODE watchdogStopServicing(void)
{
    WATCHDOG_CCODE result = WATCHDOG_SUCCESS;

    if (serviceFunctionRegistered)
    {
        tx_timer_deactivate(&watchdogTimer);
        tx_timer_delete(&watchdogTimer);
        serviceFunctionRegistered = FALSE;
    }
    else
    {
        result = WATCHDOG_WAS_NOT_SERVICING;
    }

    return result;
}




