/*****************************************************************************
* Copyright (c) 2003 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
*
*****************************************************************************/

#define DUDA_REGS       (1)
#define NS9XXX_REGS     (2)
#define NS9215_REGS     (3)

#include "Npttypes.h"

#if (PROCESSOR == ns9210) || (PROCESSOR == ns9215)
    #define REGISTER_SET  NS9215_REGS
#elif defined(BSP_ARM9)
    #define REGISTER_SET  NS9XXX_REGS
#else
    #define REGISTER_SET  DUDA_REGS
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "gpio.h"
#if (REGISTER_SET == NS9XXX_REGS) || (REGISTER_SET == NS9215_REGS)
    #include "mcreg_def.h"
    #include "scm_reg.h"
    #include "mc_isr.h"
#endif
#include "NA_ISR.h"
#include "reg_def.h"
#include "ncc_init.h"
#include "sysClockApi.h"
#include "timerPrivate.h"
#include "natimer.h"


#define PRESCALER_6_BIT     0
#define PRESCALER_9_BIT     1
#define PRESCALER_8_BIT     2

#define ENABLE_INTERRUPTS   TRUE
#define DISABLE_INTERRUPTS  FALSE


#if (REGISTER_SET == NS9215_REGS)
    #define SUPPORTS_GPIO               TRUE
    #define PRESCALER_TYPE              PRESCALER_8_BIT
    #define FIRST_INTERRUPT_IS_BOGUS    FALSE
    #define SUPPORTS_HW_ONESHOT         TRUE
    #define MAX_TIMERS                  10
    #define DEBUGGER_EFFECT             TIMER_CAN_BE_CONFIGURED_TO_HALT_IN_DEBUGGER
    #define MAX_PRESCALER_VALUE         256
    #define NS9XXX_INTERRUPT_IDS        TRUE
    #define CPU_CLOCK_MULTIPLIER        1
    #define SUPPORTED_SIZES             (TIMER_16_BIT | TIMER_32_BIT)
    #define NATURAL_SIZE                timer32Bit
    #define SUPPORTS_CASCADED_TIMERS    TRUE
    #define CLOCK_SOURCES               (TIMER_AHB_BUS_CLOCK | TIMER_EXTERNAL_CLOCK)
    #define SOME_INTERRUPTS_SHARED      FALSE
    #define DIRECTIONS                  (TIMER_UP | TIMER_DOWN)
#elif (REGISTER_SET == NS9XXX_REGS)
    #define SUPPORTS_GPIO               TRUE
    #define PRESCALER_TYPE              PRESCALER_6_BIT
    #define FIRST_INTERRUPT_IS_BOGUS    FALSE
    #define SUPPORTS_HW_ONESHOT         TRUE
    #define MAX_PRESCALER_VALUE         64
    #define NS9XXX_INTERRUPT_IDS       TRUE
    #define CPU_CLOCK_MULTIPLIER        1
    #define SUPPORTED_SIZES             (TIMER_16_BIT | TIMER_32_BIT)
    #define NATURAL_SIZE                timer32Bit
    #define SUPPORTS_CASCADED_TIMERS    TRUE
    #define CLOCK_SOURCES               (TIMER_CPU_CLOCK | TIMER_EXTERNAL_CLOCK)
    #define SOME_INTERRUPTS_SHARED      TRUE
    #define DIRECTIONS                  (TIMER_UP | TIMER_DOWN)
#ifdef NS9750
    #define MAX_TIMERS                  16
    #define DEBUGGER_EFFECT             TIMER_IS_NOT_HALTED_BY_DEBUGGER
#elif NS9360    
    #define MAX_TIMERS                  8
    #define DEBUGGER_EFFECT             TIMER_CAN_BE_CONFIGURED_TO_HALT_IN_DEBUGGER
#else
    #error Need to update for new chip type.
#endif    
#elif (REGISTER_SET == DUDA_REGS)
    #define SUPPORTS_GPIO               FALSE
    #define PRESCALER_TYPE              PRESCALER_9_BIT
    #define FIRST_INTERRUPT_IS_BOGUS    TRUE
    #define SUPPORTS_HW_ONESHOT         FALSE
    #define MAX_TIMERS                  2
    #define MAX_PRESCALER_VALUE         512
    #define NS9XXX_INTERRUPT_IDS       FALSE
    #define CPU_CLOCK_MULTIPLIER        8
    #define SUPPORTED_SIZES             (TIMER_26_BIT)
    #define NATURAL_SIZE                timer26Bit
    #define SUPPORTS_CASCADED_TIMERS    FALSE
    #define CLOCK_SOURCES               (TIMER_CPU_CLOCK | TIMER_CRYSTAL)
    #define SOME_INTERRUPTS_SHARED      FALSE
    #define DIRECTIONS                  TIMER_DOWN
    #define DEBUGGER_EFFECT             TIMER_IS_HALTED_BY_DEBUGGER
#else
    #error TODO:  Add support for new REGISTER_SET.
#endif

#if (SUPPORTS_HW_ONESHOT == FALSE)
    #if (FIRST_INTERRUPT_IS_BOGUS)
        #define SHUTDOWN_AFTER_THIS_NUMBER_OF_INTERRUPTS    2
    #else
        #define SHUTDOWN_AFTER_THIS_NUMBER_OF_INTERRUPTS    1
    #endif
#endif



/*ad*
 * Use this API to control the hardware timers on all processors
 * supported by NET+OS. Digi International implements two styles of timers.
 * Two 26-bit wide timers are implemented in the NS7520 and NET-50 processors.
 * These timers can use either the CPU clock or the crystal input as a clock,
 * and they always count down.
 *
 * The timers on other Digi International processors can be configured to be 16 or 32 bits
 * wide, and two timers can be cascaded together to create a 64-bit timer.
 * Timers on the NS9210 and NS9215 processors can use the AHB bus clock or an
 * external clock.  Timerson the NS9750 and NS9360 processors
 * can use the CPU clock or an external clock.  Timers can be
 * assigned GPIO pins; and these pins can be used to gate the timer, as an
 * external clock, or generate a timeout expiration signal.  These timers
 * can count up or down.
 *
 * Use a hardware timer by opening it with NATimerOpen and then configuring it with
 * NATimerConfigure.  Once configured, the timer can be started by calling
 * NATimerStart.  Use NATimerStop and NATimerClose to shut down and release
 * a timer.  You can install an ISR handler for the timer by setting the ISR
 * related fields in the timerPrivateConfigType structure passed to NATimerConfigure.
 *
 * In addition to the hardware timers, ThreadX provides software timers that are
 * accurate to the system clock frequency.  See the ThreadX section of the
 * online documentation for more information.
 *
 * @external
 * @overview Timer Driver
 * @category BSP:Device_Drivers:Timer
 * @include 	<natimer.h>
 * @library 	bsp
 * @platform all
 * @since 6.2
 * @see @link tx_timer_create
*ad*/
 



#define NANOSECONDS_PER_SECOND (1000000000LL)


#define GET_SEMAPHORE getSemaphore()
#define RELEASE_SEMAPHORE releaseSemaphore()

/*
 * This constant is the minimum number of instruction cycles per
 * timer period we will allow if a timer is being set up with an ISR.
 * NATimerConfigure will fail if the application specifies a 
 * shorter period. 
 *
 * Note that we assume the user knows what he is doing if he specifies
 * raw configuration values and so we allow anything.
 */  
#define MINIMUM_TICKS_PER_PERIOD    5000

/*ad*
 * These are the possible states for a timer.
 *
 * @category BSP:Device_Drivers:Timer 
 * @since 6.2
 *
 * @param   timerClosed             Timer is not in use.
 * @param   timerOpen               Timer has been reserved for use by an application.
 * @param   timerConfigured         Timer has been configured.
 * @param   timerRunning            Timer is running.
 * @param   timerConcatenated       Timer is the high word of a 64-bit timer.
*ad*/
typedef enum
{
    timerClosed = 0,
    timerOpen,
    timerConfigured,
    timerRunning,
    timerConcatenated
} timerStateType;



/*ad*
 * This is an extended version of timerConfigType which supports the additional
 * configuration options available on the NS9210 and NS9215 processors.  
 *
 * @external
 * @category BSP:Device_Drivers:Timer  
 * @since 6.2
 *
 * @param timeoutKey    Determines how the timeout configuration is stored.
 * @param timeout       Stores timeout settings.
 * @param gating        Determines if and how the timer is gated.  A timer is gated when an external
 *                      signal controls when it runs.
 * @param oneShot       Determines whether the timer runs once or repeatedly.  If you want the timer to 
 *                      stop the first time it expires, set this parameter to TRUE.  If you want the
 *                      timer to automatically reload and continue to run when it expires, set this
 *                      field to FALSE.
 * @param isr           Stores a pointer to an application ISR to be called when timer expires. 
 *                      To disable the timer interrupt, set this field to NULL.  Set this field to
 *                      the address of the application ISR to automatically install the ISR.  After
 *                      NATimerConfigure completes, the timer interrupt must be enabled by calling
 *                      NATimerInterruptEnable, and then the timer must be started by calling 
 *                      NATimerStart.
 * @param isrContext    Stores context information passed to application ISR.
 * @param haltInDebugger Determines if the timer should continue to run while the processor has 
 *                      been stopped in the debugger.  This field will be ignored on those
 *                      processors that do not support this feature.
 * @param captureMode   Stores the capture mode setting for this timer.
 * @param compareCount  Stores the compare count for the timer.  This is the value which
 *                      triggers the output signal when the captureMode is set to 
 *                      timerCompareModeToggleOutputOnMatch or timerCompareModePulseOutputOnMatch.
 *
 * @see @link timerConfigType
 * @see @link NATimerConfigure
*ad*/
typedef struct
{
    timerTimeoutUnionKeyType timeoutKey;
    timerTimeoutUnionType timeout;
    timerGatingType gating;
    BOOLEAN oneShot;
    timerIsrType isr;
    void *isrContext;
    BOOLEAN haltInDebugger;
    timerCaptureModeType captureMode;
    WORD64 compareCount;
} timerPrivateConfigType;


/*ad*
 * This structure holds information about the system timers.
 *
 * @category BSP:Device_Drivers:Timer 
 * @since 6.2
 *
 * @param   config                  Configuration settings for the timer.
 * @param   size                    Size of the timer.
 * @param   state                   State of the timer as described in the enum above.
 * @param   interruptAcked          Cleared at the start of an ISR, and set when the interrupt has been acked.
 * @param   isrInstalled            Set if an ISR has been installed for this timer.
 * @param   interruptCount          Number of interrupts generated by the timer.
*ad*/
typedef struct
{
    timerPrivateConfigType config;
    timerSizeType size;
    timerStateType state;
    BOOLEAN interruptAcked;
    BOOLEAN isrInstalled;
    #if (FIRST_INTERRUPT_IS_BOGUS || !SUPPORTS_HW_RELOAD)
        int interruptCount;
    #endif
} timerInfoType;

/*ad*
 * This array holds information about the system timers.
 *
 * @category BSP:Device_Drivers:Timer 
 * @since 6.2
*ad*/
static timerInfoType NATimerInfo[MAX_TIMERS];


/*ad*
 * This array is a lookup table that can be used to translate a timer handle
 * into the timer's interrupt ID.  The interrupt ID is used by MCInstallIsr().
 *
 * @category BSP:Device_Drivers:Timer 
 * @since 6.2
*ad*/
static int const NATimerIsrId[MAX_TIMERS] = 
{
    #if NS9XXX_INTERRUPT_IDS
        TIMER0_INTERRUPT,    
        TIMER1_INTERRUPT,    
        TIMER2_INTERRUPT,    
        TIMER3_INTERRUPT,    
        TIMER4_INTERRUPT,    
        TIMER5_INTERRUPT,    
        TIMER6_INTERRUPT,    
        TIMER7_INTERRUPT,
      #ifdef NS9750
        TIMER8_9_INTERRUPT,  
        TIMER8_9_INTERRUPT,  
        TIMER10_11_INTERRUPT,
        TIMER10_11_INTERRUPT,
        TIMER12_13_INTERRUPT,
        TIMER12_13_INTERRUPT,
        TIMER14_15_INTERRUPT,
        TIMER14_15_INTERRUPT
      #elif (REGISTER_SET == NS9215_REGS)
        TIMER8_INTERRUPT,
        TIMER9_INTERRUPT
      #endif
    #else
        TIMER1_INT,
        TIMER2_INT
    #endif
};

/*ad*
 * This constant indicates the first physical timer that shares interrupts
 * with another timer on the NS9750 and NS9775 processors.
 *
 * @category BSP:Device_Drivers:Timer 
 * @since 6.2
*ad*/
#define TIMER_MERCURY_FIRST_SHARED_INTERRUPT    8



/*ad*
 * ISR Context values for timer interrupts.  These values are used for timer
 * interrupt context information.  For timers 0-7, the context is the timer's
 * handle.  Timer's 8-15 share interrupt levels, their context is a value
 * that identifies which pair of timers is interrupting.  The ISR then examines
 * a register to determine which of the pair needs service.
 *
 * @category BSP:Device_Drivers:Timer
 * @since 6.2
 *
 * @name timerIsrContext    "Timer ISR Context Values"
 *
 * @param  TIMER_0_CONTEXT              context for timer 0, which is the timer's handle
 * @param  TIMER_1_CONTEXT              context for timer 1, which is the timer's handle
 * @param  TIMER_2_CONTEXT              context for timer 2, which is the timer's handle
 * @param  TIMER_3_CONTEXT              context for timer 3, which is the timer's handle
 * @param  TIMER_4_CONTEXT              context for timer 4, which is the timer's handle
 * @param  TIMER_5_CONTEXT              context for timer 5, which is the timer's handle
 * @param  TIMER_6_CONTEXT              context for timer 6, which is the timer's handle
 * @param  TIMER_7_CONTEXT              context for timer 7, which is the timer's handle
 * @param  TIMERS_8_AND_9_CONTEXT       context for timers 8 and 9
 * @param  TIMERS_10_AND_11_CONTEXT     context for timers 10 and 11
 * @param  TIMERS_12_AND_13_CONTEXT     context for timers 12 and 13
 * @param  TIMERS_14_AND_15_CONTEXT     context for timers 14 and 15
 *
 * @see @link NATimerContext
*ad*/
#define TIMER_0_CONTEXT             0
#define TIMER_1_CONTEXT             1
#define TIMER_2_CONTEXT             2
#define TIMER_3_CONTEXT             3
#define TIMER_4_CONTEXT             4
#define TIMER_5_CONTEXT             5
#define TIMER_6_CONTEXT             6
#define TIMER_7_CONTEXT             7
#if (REGISTER_SET == NS9XXX_REGS)
    #define TIMERS_8_AND_9_CONTEXT      8
    #define TIMERS_10_AND_11_CONTEXT    9
    #define TIMERS_12_AND_13_CONTEXT    10
    #define TIMERS_14_AND_15_CONTEXT    11

#elif (REGISTER_SET == NS9215_REGS)
    #define TIMER_8_CONTEXT             8
    #define TIMER_9_CONTEXT             9
    
#endif

#define TIMERS_HIGHEST_CONTEXT     MAX_TIMERS

/*ad*
 * This array is a look up table that translates a timer handle into a timer ISR context.
 *
 * @category BSP:Device_Drivers:Timer 
 * @since 6.2
 *
 * @see @link timerIsrContext
*ad*/
static int const NATimerContext[] =
{
    #if (MAX_TIMERS == 2)
        TIMER_0_CONTEXT,
        TIMER_1_CONTEXT,
    
    #else
        TIMER_0_CONTEXT,
        TIMER_1_CONTEXT,
        TIMER_2_CONTEXT,
        TIMER_3_CONTEXT,
        TIMER_4_CONTEXT,
        TIMER_5_CONTEXT,
        TIMER_6_CONTEXT,
        TIMER_7_CONTEXT,
    #if (REGISTER_SET == NS9XXX_REGS)
        TIMERS_8_AND_9_CONTEXT,
        TIMERS_8_AND_9_CONTEXT,
        TIMERS_10_AND_11_CONTEXT,
        TIMERS_10_AND_11_CONTEXT,
        TIMERS_12_AND_13_CONTEXT,
        TIMERS_12_AND_13_CONTEXT,
        TIMERS_14_AND_15_CONTEXT,
        TIMERS_14_AND_15_CONTEXT
    #elif (REGISTER_SET == NS9215_REGS)
        TIMER_8_CONTEXT,
        TIMER_9_CONTEXT
    #endif
            
    #endif
};
/*ad*
 * This array is used to keep track of which interrupts we have installed
 * ISRs on.  A value of zero indicates that we do not have an ISR installed
 * for that interrupt level.  A nonzero count indicates the number of 
 * timers that are sharing that ISR level (either 1 or 2).
 *
 * @category BSP:Device_Drivers:Timer 
 * @since 6.2
*ad*/
static int NATimerIsrInstallCount[TIMERS_HIGHEST_CONTEXT] = {0};

NATimerSemaphoreFunctionType NATimerGetSemaphoreFn = NULL;
NATimerSemaphoreFunctionType NATimerReleaseSemaphoreFn = NULL;


/*
 * This function calls the get semaphore function once it has been installed.  Until
 * then it does nothing.
 */
static void getSemaphore(void)
{
    if (NATimerGetSemaphoreFn)
    {
        NATimerGetSemaphoreFn();
    }
}

/*
 * This function calls the release semaphore function once it has been installed.  Until
 * then it does nothing.
 */
static void releaseSemaphore(void)
{
    if (NATimerReleaseSemaphoreFn)
    {
        NATimerReleaseSemaphoreFn();
    }
}

#if (REGISTER_SET == DUDA_REGS)

    static unsigned long int volatile * TCR_getAddress(timerHandleType const handle)
    {
        unsigned long int volatile * address[] = {NARM_T1CR_ADDR, NARM_T2CR_ADDR};
        
        return address[handle];
    }
    
    static unsigned long int volatile * getTimerStatusRegisterAddress(timerHandleType const handle)
    {
        unsigned long int volatile * address[] = {NARM_T1SR_ADDR, NARM_T2SR_ADDR};
        
        return address[handle];
    }

#endif


/*ad*
 * Returns the number of timers implemented in the processor.
 *
 * @category BSP:Device_Drivers:Timer
 * @since 6.2
 * @external
 *
 * @return  "Timer Count"   Number of general purpose timers implemented in the processor.
*ad*/
int NATimerCount(void)
{
    return MAX_TIMERS;
}




/*ad*
 * Determine if a handle is valid.  A handle is valid if it is in range and
 * the indicated timer is not closed.  A handle is actually an integer
 * index into the NATimerInfo array.
 *
 * @category BSP:Device_Drivers:Timer
 * @since 6.2
 *
 * @param handle    Timer handle to validate.
 *
 * @return FALSE                Handle is not valid.
 * @return TRUE                 Handle is valid.
*ad*/
static int NATimerIsHandleValid(timerHandleType handle)
{
    int returnValue = FALSE;

    if ((handle >= 0) && (handle < MAX_TIMERS))
    {
        if ((NATimerInfo[handle].state != timerClosed) && (NATimerInfo[handle].state != timerConcatenated))
        {
            returnValue = TRUE;
        }
    }
    
    return returnValue;
}


#if SUPPORTS_GPIO
/**
 * Determine if a timer has a GPIO input pin assigned to it.  Timers on the 
 * NS7520 and NET-50 do not have GPIO pins.  Timers on the NS9750/9775
 * may have pins assigned to them.  Look at the constants defined in
 * gpio.h to determine this.
 *
 * @category BSP:Device_Drivers:Timer
 * @since 6.2
 *
 * @param timerNumber       Physical timer number of the timer.
 *
 * @return FALSE                Timer does not have a GPIO pin.
 * @return TRUE                 Timer does have a GPIO pin.
*/
#if (REGISTER_SET == NS9215_REGS)
    int NATimerHasGpioInputPin(int const timerNumber)
    {
        int hasGpioPin = FALSE;
        static int const hasGpio[MAX_TIMERS] = 
        {
#ifdef NS9210
        /*
         * Timers 0-5 on NS9210 are not routed through GPIO MUX.  They do not
         * have any I/O pins.
         */
            FALSE,
            FALSE,
            FALSE,
            FALSE,
            FALSE,
#else
            (BSP_GPIO_MUX_TIMER_0_INPUT != BSP_GPIO_MUX_INTERNAL_USE_ONLY),
            (BSP_GPIO_MUX_TIMER_1_INPUT != BSP_GPIO_MUX_INTERNAL_USE_ONLY),
            (BSP_GPIO_MUX_TIMER_2_INPUT != BSP_GPIO_MUX_INTERNAL_USE_ONLY),
            (BSP_GPIO_MUX_TIMER_3_INPUT != BSP_GPIO_MUX_INTERNAL_USE_ONLY),
            (BSP_GPIO_MUX_TIMER_4_INPUT != BSP_GPIO_MUX_INTERNAL_USE_ONLY),
#endif
            (BSP_GPIO_MUX_TIMER_5_INPUT != BSP_GPIO_MUX_INTERNAL_USE_ONLY),
            (BSP_GPIO_MUX_TIMER_6_INPUT != BSP_GPIO_MUX_INTERNAL_USE_ONLY),
            (BSP_GPIO_MUX_TIMER_7_INPUT != BSP_GPIO_MUX_INTERNAL_USE_ONLY),
            (BSP_GPIO_MUX_TIMER_8_INPUT != BSP_GPIO_MUX_INTERNAL_USE_ONLY),
            (BSP_GPIO_MUX_TIMER_9_INPUT != BSP_GPIO_MUX_INTERNAL_USE_ONLY)
        };
        
        if ((timerNumber >= 0) && (timerNumber < MAX_TIMERS))
        {
            hasGpioPin = hasGpio[timerNumber];
        }
        
        return hasGpioPin;
    }


    int NATimerHasGpioOutputPin(int const timerNumber)
    {
        int hasGpioPin = FALSE;
        static int const hasGpio[MAX_TIMERS] = 
        {
#ifdef NS9210
        /*
         * Timers 0-5 on NS9210 are not routed through GPIO MUX.  They do not
         * have any I/O pins.
         */
            FALSE,
            FALSE,
            FALSE,
            FALSE,
            FALSE,
#else
            (BSP_GPIO_MUX_TIMER_0_OUTPUT != BSP_GPIO_MUX_INTERNAL_USE_ONLY),
            (BSP_GPIO_MUX_TIMER_1_OUTPUT != BSP_GPIO_MUX_INTERNAL_USE_ONLY),
            (BSP_GPIO_MUX_TIMER_2_OUTPUT != BSP_GPIO_MUX_INTERNAL_USE_ONLY),
            (BSP_GPIO_MUX_TIMER_3_OUTPUT != BSP_GPIO_MUX_INTERNAL_USE_ONLY),
            (BSP_GPIO_MUX_TIMER_4_OUTPUT != BSP_GPIO_MUX_INTERNAL_USE_ONLY),
#endif
            (BSP_GPIO_MUX_TIMER_5_OUTPUT != BSP_GPIO_MUX_INTERNAL_USE_ONLY),
            (BSP_GPIO_MUX_TIMER_6_OUTPUT != BSP_GPIO_MUX_INTERNAL_USE_ONLY),
            (BSP_GPIO_MUX_TIMER_7_OUTPUT != BSP_GPIO_MUX_INTERNAL_USE_ONLY),
            (BSP_GPIO_MUX_TIMER_8_OUTPUT != BSP_GPIO_MUX_INTERNAL_USE_ONLY),
            (BSP_GPIO_MUX_TIMER_9_OUTPUT != BSP_GPIO_MUX_INTERNAL_USE_ONLY)
        };
        
        if ((timerNumber >= 0) && (timerNumber < MAX_TIMERS))
        {
            hasGpioPin = hasGpio[timerNumber];
        }
        
        return hasGpioPin;
    }

#else /* if not a NS9215 or NS9210 */

    int NATimerHasGpioInputPin(int const timerNumber)
    {
        int hasGpioPin = FALSE;
        static int const hasGpio[MAX_TIMERS] = 
        {
            (BSP_GPIO_MUX_TIMER_0 != BSP_GPIO_MUX_INTERNAL_USE_ONLY),
            (BSP_GPIO_MUX_TIMER_1 != BSP_GPIO_MUX_INTERNAL_USE_ONLY),
            (BSP_GPIO_MUX_TIMER_2 != BSP_GPIO_MUX_INTERNAL_USE_ONLY),
            (BSP_GPIO_MUX_TIMER_3 != BSP_GPIO_MUX_INTERNAL_USE_ONLY),
            (BSP_GPIO_MUX_TIMER_4 != BSP_GPIO_MUX_INTERNAL_USE_ONLY),
            (BSP_GPIO_MUX_TIMER_5 != BSP_GPIO_MUX_INTERNAL_USE_ONLY),
            (BSP_GPIO_MUX_TIMER_6 != BSP_GPIO_MUX_INTERNAL_USE_ONLY),
            (BSP_GPIO_MUX_TIMER_7 != BSP_GPIO_MUX_INTERNAL_USE_ONLY),
#ifdef NS9750
            BSP_GPIO_MUX_TIMER_8 != BSP_GPIO_MUX_INTERNAL_USE_ONLY,
            BSP_GPIO_MUX_TIMER_9 != BSP_GPIO_MUX_INTERNAL_USE_ONLY,
            BSP_GPIO_MUX_TIMER_10 != BSP_GPIO_MUX_INTERNAL_USE_ONLY,
            BSP_GPIO_MUX_TIMER_11 != BSP_GPIO_MUX_INTERNAL_USE_ONLY,
            BSP_GPIO_MUX_TIMER_12 != BSP_GPIO_MUX_INTERNAL_USE_ONLY,
            BSP_GPIO_MUX_TIMER_13 != BSP_GPIO_MUX_INTERNAL_USE_ONLY,
            BSP_GPIO_MUX_TIMER_14 != BSP_GPIO_MUX_INTERNAL_USE_ONLY,
            BSP_GPIO_MUX_TIMER_15 != BSP_GPIO_MUX_INTERNAL_USE_ONLY
#endif            
        };
    
        if ((timerNumber >= 0) && (timerNumber < MAX_TIMERS))
        {
            hasGpioPin = hasGpio[timerNumber];
        }
        
        return hasGpioPin;
    }
    
    int NATimerHasGpioOutputPin(int const timerNumber)
    {
        int hasGpioPin = FALSE;
        static int const hasGpio[MAX_TIMERS] = 
        {
            (BSP_GPIO_MUX_TIMER_0 != BSP_GPIO_MUX_INTERNAL_USE_ONLY)
        #ifdef BSP_GPIO_MUX_PWM0
            || (BSP_GPIO_MUX_PWM0 != BSP_GPIO_MUX_INTERNAL_USE_ONLY)
        #endif
            ,
            (BSP_GPIO_MUX_TIMER_1 != BSP_GPIO_MUX_INTERNAL_USE_ONLY)
        #ifdef BSP_GPIO_MUX_PWM0
            || (BSP_GPIO_MUX_PWM0 != BSP_GPIO_MUX_INTERNAL_USE_ONLY)
        #endif
            ,
            (BSP_GPIO_MUX_TIMER_2 != BSP_GPIO_MUX_INTERNAL_USE_ONLY)
        #ifdef BSP_GPIO_MUX_PWM1
            || (BSP_GPIO_MUX_PWM1 != BSP_GPIO_MUX_INTERNAL_USE_ONLY)
        #endif
            ,            
            (BSP_GPIO_MUX_TIMER_3 != BSP_GPIO_MUX_INTERNAL_USE_ONLY)
        #ifdef BSP_GPIO_MUX_PWM1
            || (BSP_GPIO_MUX_PWM1 != BSP_GPIO_MUX_INTERNAL_USE_ONLY)
        #endif
            ,
            (BSP_GPIO_MUX_TIMER_4 != BSP_GPIO_MUX_INTERNAL_USE_ONLY)
        #ifdef BSP_GPIO_MUX_PWM2
            || (BSP_GPIO_MUX_PWM2 != BSP_GPIO_MUX_INTERNAL_USE_ONLY)
        #endif
            ,
            (BSP_GPIO_MUX_TIMER_5 != BSP_GPIO_MUX_INTERNAL_USE_ONLY)
        #ifdef BSP_GPIO_MUX_PWM2
            || (BSP_GPIO_MUX_PWM2 != BSP_GPIO_MUX_INTERNAL_USE_ONLY)
        #endif
            ,
            (BSP_GPIO_MUX_TIMER_6 != BSP_GPIO_MUX_INTERNAL_USE_ONLY)
        #ifdef BSP_GPIO_MUX_PWM3
            || (BSP_GPIO_MUX_PWM3 != BSP_GPIO_MUX_INTERNAL_USE_ONLY)
        #endif
            ,
            (BSP_GPIO_MUX_TIMER_7 != BSP_GPIO_MUX_INTERNAL_USE_ONLY)
        #ifdef BSP_GPIO_MUX_PWM3
            || (BSP_GPIO_MUX_PWM3 != BSP_GPIO_MUX_INTERNAL_USE_ONLY)
        #endif
            ,
#ifdef NS9750
            BSP_GPIO_MUX_TIMER_8 != BSP_GPIO_MUX_INTERNAL_USE_ONLY,
            BSP_GPIO_MUX_TIMER_9 != BSP_GPIO_MUX_INTERNAL_USE_ONLY,
            BSP_GPIO_MUX_TIMER_10 != BSP_GPIO_MUX_INTERNAL_USE_ONLY,
            BSP_GPIO_MUX_TIMER_11 != BSP_GPIO_MUX_INTERNAL_USE_ONLY,
            BSP_GPIO_MUX_TIMER_12 != BSP_GPIO_MUX_INTERNAL_USE_ONLY,
            BSP_GPIO_MUX_TIMER_13 != BSP_GPIO_MUX_INTERNAL_USE_ONLY,
            BSP_GPIO_MUX_TIMER_14 != BSP_GPIO_MUX_INTERNAL_USE_ONLY,
            BSP_GPIO_MUX_TIMER_15 != BSP_GPIO_MUX_INTERNAL_USE_ONLY
#endif            
        };
    
        if ((timerNumber >= 0) && (timerNumber < MAX_TIMERS))
        {
            hasGpioPin = hasGpio[timerNumber];
        }
        
        return hasGpioPin;
    }
    
#endif

    #define NATimerPrivateHasGpioOutputPin   NATimerHasGpioOutputPin
    #define NATimerPrivateHasGpioInputPin    NATimerHasGpioInputPin

#else
    int NATimerHasGpioInputPin(int const timerNumber)
    {
        NA_UNUSED_PARAM(timerNumber);
        
        return FALSE;
    }
    
    int NATimerHasGpioOutputPin(int const timerNumber)
    {
        NA_UNUSED_PARAM(timerNumber);
        
        return FALSE;
    }
    
    #define NATimerPrivateHasGpioInputPin(x)     FALSE
    #define NATimerPrivateHasGpioOutputPin(x)    FALSE
#endif




/*ad*
 * This routine is called by NATimerClose() to uninstall an ISR.   There
 * are a couple of complications.
 *
 * <list type = "unordered">
 *  <item>64-bit timers are created by concatenating two 32-bit timers
 *        together.  In this case, the ISR is installed on the high
 *        32-bit timer, and not on the lower one.</item>
 *  <item>Some timers share interrupts.  In this case, we have to keep
 *        track of how many timers are still using the interrupt.  We
 *        only uninstall the driver's ISR when the last timer sharing
 *        the interrupt level has been closed.</item>
 * </list>
 *
 * @category BSP:Device_Drivers:Timer
 * @since 6.2
 *
 * @param handle        handle of timer to uninstall ISR
*ad*/
static void NATimerUninstallIsr(timerHandleType const handle)
{
    if (NATimerInfo[handle].isrInstalled)
    {
        #if SUPPORTS_CASCADED_TIMERS
            if (NATimerInfo[handle].size == timer64Bit)
            {
                if (NATimerIsrInstallCount[NATimerContext[handle+1]] == 1)
                {
                    naIsrUninstall(NATimerIsrId[handle+1]);
                }
                NATimerIsrInstallCount[NATimerContext[handle+1]]--;
            }
            else
            {
                if (NATimerIsrInstallCount[NATimerContext[handle]] == 1)
                {
                    naIsrUninstall(NATimerIsrId[handle]);
                }
                NATimerIsrInstallCount[NATimerContext[handle]]--;
            }
        #else
            if (NATimerIsrInstallCount[NATimerContext[handle]] == 1)
            {
                naIsrUninstall(NATimerIsrId[handle]);
            }
            NATimerIsrInstallCount[NATimerContext[handle]]--;
        #endif
        NATimerInfo[handle].isrInstalled = FALSE;
    }
}



/*
 * This is an internal routine that does the hardware acknowledgement
 * of the interrupt.  Unlike NATimerInterruptAck(), this routine does
 * not get and release the semaphores and so can be called by routines
 * which do.
 */
static void NATimerPrivateInterruptAck(timerHandleType const handle)
{
    unsigned long int volatile * addr;
    
    #if (REGISTER_SET == NS9XXX_REGS) || (REGISTER_SET == NS9215_REGS)
        addr = NATimerInfo[handle].size == timer64Bit ? TCR_getAddress(handle+1) : TCR_getAddress(handle);
        if (narm_read_reg(NA_SCM_TCR_REG, addr, reloadEnable) == SCM_TCR_HALT)
        { /* need to disable timer before acking interrupt if timer is setup as a one-shot*/
            narm_write_reg(NA_SCM_TCR_REG, addr, enable, SCM_TCR_DISABLE);
        }
        narm_write_reg(NA_SCM_TCR_REG, addr, clearInterrupt, 1);
        narm_write_reg(NA_SCM_TCR_REG, addr, clearInterrupt, 0);
    #elif (REGISTER_SET == DUDA_REGS)
        addr = getTimerStatusRegisterAddress(handle);
        narm_write_reg(NARM_TMRSR_REG, addr, tip, TIMER_CLEAR_PENDING_INTERRUPT);
    #else
        #error TODO:  Update NATimerPrivateInterruptAck() with new register set
    #endif
}



/*
 * This is an internal routine that disables the timer 
 * in hardware.  It does not fiddle around with semaphores so it
 * can be shared by routines that do.
 */
static void NATimerPrivateDisable(timerHandleType const handle)
{
    unsigned long int volatile * const addr = TCR_getAddress(handle);
    
    #if (REGISTER_SET == NS9XXX_REGS) || (REGISTER_SET == NS9215_REGS)
        narm_write_reg(NA_SCM_TCR_REG, addr, enable, SCM_TCR_DISABLE);
        #if SUPPORTS_CASCADED_TIMERS
            if (NATimerInfo[handle].size == timer64Bit)
            {
                narm_write_reg (NA_SCM_TCR_REG, TCR_getAddress(handle+1), enable, SCM_TCR_DISABLE);
            }
        #endif
    #elif (REGISTER_SET == DUDA_REGS)
        narm_write_reg(NARM_TMRCR_REG, addr, te, TIMER_RESET_AND_DISABLE);
    #else
        #error TODO:  Update NATimerPrivateDisable() to support new register set
    #endif
}



/*
 * This is an internal routine that does enables or disables a timer's interrupt 
 * in hardware.  It does not fiddle around with semaphores so it
 * can be shared by routines that do.
 */
static void NATimerPrivateInterruptSet(timerHandleType const handle, BOOLEAN const enable)
{
    unsigned long int volatile * const addr = NATimerInfo[handle].size == timer64Bit ? TCR_getAddress(handle+1) : TCR_getAddress(handle);
    
    #if (REGISTER_SET == NS9XXX_REGS) || (REGISTER_SET == NS9215_REGS)
        if (enable)
        {
            narm_write_reg(NA_SCM_TCR_REG, addr, interruptSelect, SCM_TCR_GENERATE_IRQ);
        }
        else
        {
            narm_write_reg(NA_SCM_TCR_REG, addr, interruptSelect, SCM_TCR_INTERRUPT_DISABLE);
            #if SUPPORTS_CASCADED_TIMERS
                /*
                 * If this is a cascaded timer, then disable interrupts on both timers.  Only the
                 * upper timer should ever have interrupts enabled on it, and we already disabled
                 * it above.  Now disable interrupts on the lower timer (they should already be
                 * disabled) just to be sure.
                 */
                if (NATimerInfo[handle].size == timer64Bit)
                {
                    unsigned long int volatile * const addrLow = TCR_getAddress(handle);
                    narm_write_reg(NA_SCM_TCR_REG, addrLow, interruptSelect, SCM_TCR_INTERRUPT_DISABLE);
                }
            #endif
        }
    #elif (REGISTER_SET == DUDA_REGS)
        if (enable)
        {
            narm_write_reg(NARM_TMRCR_REG, addr, tie, TIMER_ENABLE_INTERRUPT);
        }
        else
        {
            narm_write_reg(NARM_TMRCR_REG, addr, tie, TIMER_DISABLE_INTERRUPT);
        }
    #else
        #error TODO:  Update NATimerPrivateInterruptSet() with new register set
    #endif
}


/*ad*
 * This routine is called by driverLowLevelIsrHandler() and is passed the
 * number of the interrupting timer.  It calls the application ISR and
 * acknowledges the interrupt.
 *
 * @category BSP:Device_Drivers:Timer
 * @since 6.2
 *
 * @param timerNumber       Number of timer that is interrupting
*ad*/
static void driverUpperLevelIsrHandler(int const timerNumber)
{
    timerHandleType const handle = NATimerInfo[timerNumber].state == timerConcatenated ? timerNumber - 1: timerNumber;
    
    #if FIRST_INTERRUPT_IS_BOGUS || (SUPPORTS_HW_ONESHOT == FALSE)
        /*
         * This counter only exists if we are dealing with the old style timers that
         * generate bogus interrupts and don't support the H/W oneshot feature.  We don't
         * want to do anything with this counter in an ISR if it is not needed, hense the
         * conditional compilation.
         */
        NATimerInfo[handle].interruptCount++;
    #endif
        
    if ((NATimerInfo[handle].state == timerOpen) || (NATimerInfo[handle].state == timerClosed) || (NATimerInfo[handle].config.isr == NULL))
    {
        /*
         * If we come here, then something is wrong.  Shutdown the timer.
         */
        NATimerPrivateInterruptSet(handle, DISABLE_INTERRUPTS);
        NATimerPrivateInterruptAck(handle);
        NATimerPrivateDisable(handle);
    }
    else
    {
        timerIsrCcodeType ccode;
        
        #if FIRST_INTERRUPT_IS_BOGUS
            if (NATimerInfo[handle].interruptCount == 1)
            {
                /*
                 * The NET-50's and NS7520's generate a bogus interrupt when they are
                 * first enabled.  Don't pass this onto the application code as it
                 * will just confuse it.
                 */
                NATimerPrivateInterruptAck(handle);
                return;
            }
        #endif
        
        NATimerInfo[handle].interruptAcked = FALSE;
        ccode = NATimerInfo[handle].config.isr(handle, NATimerInfo[handle].config.isrContext);
        if (!NATimerInfo[handle].interruptAcked)
        {   /* Ack the interrupt if the application ISR didn't. */
            NATimerPrivateInterruptAck(handle);
        }
        
        #if (SUPPORTS_HW_ONESHOT == FALSE)
            /*
             * The NET-50's and NS7520's do not implement the H/W one-shot feature, so
             * we have to emulate it in software.
             */
            if ((NATimerInfo[handle].config.oneShot) && (NATimerInfo[handle].interruptCount == SHUTDOWN_AFTER_THIS_NUMBER_OF_INTERRUPTS))
            {
                ccode = ackAndShutdown;
            }
        #endif
        if (ccode == ackAndShutdown)
        {
            NATimerPrivateInterruptSet(handle, DISABLE_INTERRUPTS);
            NATimerPrivateDisable(handle);
            NATimerInfo[handle].state = timerConfigured;
        }
    }
}


/*ad*
 * This routine is called by the NET+OS ISR driver when a timer interrupts.  
 * Timers 8-15 share interrupts.  This routine determines which timer requires
 * service and calls driverUpperLevelIsrHandler() to service it.
 *
 * @category BSP:Device_Drivers:Timer
 * @since 6.2
 *
 * @param isrContext    Set to the timer's handle for timers 0-7, or to a 
 *                      value that indicates which pair of timers is
 *                      generating the interrupt.
*ad*/
static void driverLowLevelIsrHandler(void * isrContext)
{
    int interruptIndex = (int) isrContext;

    #if (REGISTER_SET == NS9215_REGS) 
        driverUpperLevelIsrHandler(interruptIndex);
    #elif (REGISTER_SET == NS9XXX_REGS)
        switch (interruptIndex)
        {
            case TIMERS_8_AND_9_CONTEXT:
                if (narm_read_reg(NA_SCM_TISR_REG, NA_SCM_TISR_ADDRESS, timer8) == NA_SCM_TISR_INTERRUPTING)
                {
                    driverUpperLevelIsrHandler(8);
                }
                if (narm_read_reg(NA_SCM_TISR_REG, NA_SCM_TISR_ADDRESS, timer9) == NA_SCM_TISR_INTERRUPTING)
                {
                    driverUpperLevelIsrHandler(9);
                }
                break;
            case TIMERS_10_AND_11_CONTEXT:
                if (narm_read_reg(NA_SCM_TISR_REG, NA_SCM_TISR_ADDRESS, timer10) == NA_SCM_TISR_INTERRUPTING)
                {
                    driverUpperLevelIsrHandler(10);
                }
                if (narm_read_reg(NA_SCM_TISR_REG, NA_SCM_TISR_ADDRESS, timer11) == NA_SCM_TISR_INTERRUPTING)
                {
                    driverUpperLevelIsrHandler(11);
                }
                break;
            case TIMERS_12_AND_13_CONTEXT:
                if (narm_read_reg(NA_SCM_TISR_REG, NA_SCM_TISR_ADDRESS, timer12) == NA_SCM_TISR_INTERRUPTING)
                {
                    driverUpperLevelIsrHandler(12);
                }
                if (narm_read_reg(NA_SCM_TISR_REG, NA_SCM_TISR_ADDRESS, timer13) == NA_SCM_TISR_INTERRUPTING)
                {
                    driverUpperLevelIsrHandler(13);
                }
                break;
            case TIMERS_14_AND_15_CONTEXT:
                if (narm_read_reg(NA_SCM_TISR_REG, NA_SCM_TISR_ADDRESS, timer14) == NA_SCM_TISR_INTERRUPTING)
                {
                    driverUpperLevelIsrHandler(14);
                }
                if (narm_read_reg(NA_SCM_TISR_REG, NA_SCM_TISR_ADDRESS, timer15) == NA_SCM_TISR_INTERRUPTING)
                {
                    driverUpperLevelIsrHandler(15);
                }
                break;
            default:
                /* other timers do not share interrupts so we already know which one is interrupting*/
                driverUpperLevelIsrHandler(interruptIndex);
                break;
        }
    #elif (REGISTER_SET == DUDA_REGS)
        driverUpperLevelIsrHandler(interruptIndex);
    #else
        #error TODO:  Update driverLowLevelIsrHandler() for new register set
    #endif
}


/*ad*
 * Convert a period in nanoseconds into settings for the timer direction, prescaler, 
 * clock source, and reload value.
 *
 * @category BSP:Device_Drivers:Timer
 * @since 6.2
 *
 * @param period    Desired period for the timer.
 * @param raw       Pointer to structure we will load with raw configuration settings based on the desired period.
 * @param size      Width of the timer.
 * @param veryShort Pointer to BOOLEAN to set if we think the period is too short.
 *
 * @return  timerSuccess        The raw parameter has been updated with appropriate configuration settings.
 * @return  timerNotSupported   The H/W timer cannot support the desired period.
*ad*/
static timerCcodeType NATimerPeriod2Raw(WORD64 * const period, timerTimeoutRawType * raw, timerSizeType const size, BOOLEAN * veryShort)
{
    timerCcodeType ccode = timerSuccess;
    long long nanosecondsPerTickTimes100;
    long long ticksPerPeriod;
    unsigned long long mask = 0;

    raw->direction = down;
    raw->prescaler = 1;
    
    /*
     * Compute the number of CPU clock ticks needed for the desired timer period.
     */
    #if (REGISTER_SET == NS9215_REGS)
        raw->source = ahbClock;
        nanosecondsPerTickTimes100 = (100LL * NANOSECONDS_PER_SECOND) / (2*NAgetAhbClkFreq());
        ticksPerPeriod = (((1000LL * period->x) / nanosecondsPerTickTimes100) + 5LL) / 10LL;
        *veryShort = (ticksPerPeriod < MINIMUM_TICKS_PER_PERIOD);
    #elif (REGISTER_SET == NS9XXX_REGS)
        raw->source = cpuClock;
        nanosecondsPerTickTimes100 = (100LL * NANOSECONDS_PER_SECOND) / NAgetCpuClkFreq();
        ticksPerPeriod = (((1000LL * period->x) / nanosecondsPerTickTimes100) + 5LL) / 10LL;
        *veryShort = (ticksPerPeriod < MINIMUM_TICKS_PER_PERIOD);
    #elif (REGISTER_SET == DUDA_REGS)
        raw->source = crystal;
        nanosecondsPerTickTimes100 = (8 * (100LL * NANOSECONDS_PER_SECOND)) / NAgetSysOscFreq();

        ticksPerPeriod = ((((1000LL * period->x) / nanosecondsPerTickTimes100) + 5) / 10LL) - 1;
        { /* we use the fxtal clock on the older chips, but need do this calc based on the CPU clock*/
            long long unsigned minNanoseconds = MINIMUM_TICKS_PER_PERIOD * ((((10L * NANOSECONDS_PER_SECOND) / NAgetCpuClkFreq()) + 5L) / 10L);
            
            *veryShort = (period->x < minNanoseconds);
        }
    #else
        #error TODO:  Add support for new register set in NATimerPeriod2Raw().
    #endif
    if (ticksPerPeriod == 0)
    {
        ccode = timerNotSupported;
        goto exitCleanup;
    }
    
    switch (size)
    {
        #if SUPPORTS_CASCADED_TIMERS
            case timer64Bit:                        /* we are done if using a 64-bit timer*/
                raw->reloadValue.x = ticksPerPeriod;
                if (raw->reloadValue.s.msw == 0xffffffff)
                {                                   /* msw must be set to 1 more than desired period*/
                    ccode = timerNotSupported;      /* fail in error if it can't be incremented*/
                }
                goto exitCleanup;
        #endif
        #if (SUPPORTED_SIZES & TIMER_32_BIT)
            case timer32Bit:
                mask = ~((1LL << 32) - 1);
                break;
        #endif
        #if (SUPPORTED_SIZES & TIMER_26_BIT)
            case timer26Bit:                        /* only use 26-bits of timer even though physical timer*/
                mask = ~((1LL << 26) - 1);          /* is 32-bits wide to avoid surprizes when user runs */
                break;                              /* app on NS7520 or NET-50 system*/
        #endif
        #if (SUPPORTED_SIZES & TIMER_16_BIT)
            case timer16Bit:
                mask = ~((1LL << 16) - 1);
                break;
        #endif
        default:
            customizeErrorHandler(ERROR_TIMER, ERROR_TIMER_INTERNAL_DRIVER_ERROR);
            break;
    }
        
    /*
     * If the period is too large to fit in the timer, then try dividing down the clock
     * with the prescaler.
     */
    while ((ticksPerPeriod & mask) != 0)
    {
        #if (PRESCALER_TYPE == PRESCALER_6_BIT) || (PRESCALER_TYPE == PRESCALER_8_BIT)
            if (raw->prescaler == MAX_PRESCALER_VALUE)
            {
                ccode = timerNotSupported;
                goto exitCleanup;
            }
            raw->prescaler <<= 1;
            ticksPerPeriod >>= 1;
        #elif (PRESCALER_TYPE == PRESCALER_9_BIT)
            ticksPerPeriod >>= 9;
            raw->prescaler = 512;
            if (((ticksPerPeriod & mask) != 0) || (ticksPerPeriod == 0))
            {
                ccode = timerNotSupported;
                goto exitCleanup;
            }
        #else
            #error TODO:  Add code to NATimerPeriod2Raw() to support new prescaler type
        #endif
    }

    exitCleanup:
    raw->reloadValue.x = ticksPerPeriod;
    
    #if SUPPORTS_CASCADED_TIMERS
        if (size == timer64Bit)
        {
            raw->reloadValue.s.msw++;
        }
    #endif
    
    return ccode;
}
    
/*ad*
 * Program the timer settings related to the period, including the direction, reload value,
 * prescaler, and clock source.  User can give us clock period information in three different
 * formats.  Convert it into clock settings.
 *
 * @category BSP:Device_Drivers:Timer
 * @since 6.2
 *
 * @param handle    Timer handle.
 * @param config    Configuration settings for the timer.
 * @param size      Width of the timer.
 * @param veryShort Pointer to BOOLEAN to set if we think the period is too short.
 *
 * @return  timerSuccess            The raw parameter has been updated with appropriate configuration settings.
 * @return  timerNotSupported       The H/W timer cannot support the desired period.
 * @return  timerInvalidReloadValue Reload value is invalid for the timer.  A value of zero cannot be used, and
 *                                  the high word of a 64-bit timer must not be zero.
*ad*/
static timerCcodeType NATimerSetPeriod(timerHandleType const handle, timerPrivateConfigType * const config, timerSizeType const size, BOOLEAN * veryShort)
{
    unsigned long int volatile * const addr = TCR_getAddress(handle);
    #if SUPPORTS_CASCADED_TIMERS
        unsigned long int volatile * const addrHi = TCR_getAddress(handle+1);
    #endif
    timerTimeoutRawType raw;
    timerCcodeType ccode = timerSuccess;
    WORD64 longPeriod;
    unsigned long long mask;

    /*
     * Assume everything's hunky dory.
     */
    *veryShort = FALSE;
    
    switch (config->timeoutKey)
    {
        case timerRaw:
            raw = config->timeout.raw;
            switch (size)
            {
                #if (SUPPORTED_SIZES & TIMER_16_BIT)
                    case timer16Bit:
                        mask = ~((1LL << 16) - 1);
                        break;
                #endif
                #if (SUPPORTED_SIZES & TIMER_26_BIT)
                    case timer26Bit:
                        mask = ~((1LL << 26) - 1);
                        break;
                #endif
                #if (SUPPORTED_SIZES & TIMER_32_BIT)
                    case timer32Bit:
                        mask = ~((1LL << 32) - 1);
                        break;
                #endif
                #if SUPPORTS_CASCADED_TIMERS
                    case timer64Bit:
                        mask = 0LL;
                        if ((raw.reloadValue.s.msw == 0) && (raw.direction == down))
                        {
                            /*
                             * A reload value of 0 in the msw of a 64-bit timer will cause the timer
                             * to interrupt us continuously.
                             */
                            ccode = timerInvalidReloadValue;
                        }
                        break;
                #endif
                default:
                    ccode = timerNotSupported;
                    goto errorExit;
            }
            if (raw.direction == down)
            {
                if (((mask & raw.reloadValue.x) != 0) || (raw.reloadValue.x == 0))
                {
                    ccode = timerInvalidReloadValue;
                    break;
                }
            }
            else
            {
                if (((mask & raw.reloadValue.x) != 0) || (raw.reloadValue.x == ~mask))
                {
                    ccode = timerInvalidReloadValue;
                    break;
                }
            }
            break;
        case timerFrequency:                                /* convert freq into period, and that into raw config*/
            if (config->timeout.frequency > 0)
            {
                longPeriod.x = NANOSECONDS_PER_SECOND / config->timeout.frequency;
                ccode = NATimerPeriod2Raw(&longPeriod, &raw, size, veryShort);
            }
            else
            {
                ccode = timerNotSupported;
            }
            break;
        case timerPeriod:                                   /* convert period into raw config*/
            longPeriod.x = (config->timeout.period.seconds * NANOSECONDS_PER_SECOND) + config->timeout.period.nanoseconds;
            ccode = NATimerPeriod2Raw(&longPeriod, &raw, size, veryShort);
            break;
    }
    if (ccode != timerSuccess)
    {
        goto errorExit;
    }

    switch (raw.source)
    {
        #if (CLOCK_SOURCES & TIMER_CPU_CLOCK)
            case cpuClock:
                #if ((PRESCALER_TYPE == PRESCALER_6_BIT) && (REGISTER_SET == NS9XXX_REGS))
                    {
                        int iclock = SCM_TCR_CPU_CLOCK;
                        
                        switch (raw.prescaler)
                        {
                            case 1:
                                iclock = SCM_TCR_CPU_CLOCK;
                                break;
                            case 2:
                                iclock = SCM_TCR_CPU_CLOCK_2;
                                break;
                            case 4:
                                iclock = SCM_TCR_CPU_CLOCK_4;
                                break;
                            case 8:
                                iclock = SCM_TCR_CPU_CLOCK_8;
                                break;
                            case 16:
                                iclock = SCM_TCR_CPU_CLOCK_16;
                                break;
                            case 32:
                                iclock = SCM_TCR_CPU_CLOCK_32;
                                break;
                            case 64:
                                iclock = SCM_TCR_CPU_CLOCK_64;
                                break;
                            default:
                                ccode = timerInvalidPrescaler;
                                goto errorExit;
                        }
                        narm_write_reg (NA_SCM_TCR_REG, addr, clockSelect, iclock);
                    }
                #elif ((PRESCALER_TYPE == PRESCALER_9_BIT) && (REGISTER_SET == DUDA_REGS))
                    narm_write_reg (NARM_TMRCR_REG, addr, tclk, TIMER_SYSCLK_CLOCK);
                    if (raw.prescaler != 1)
                    {
                        ccode = timerInvalidPrescaler; /* prescaler not supported when using sysclock*/
                        goto errorExit;
                    }
                #else
                    #error TODO:  Update NATimerSetPeriod() for new prescaler/register set combination
                #endif
                break;
        #endif
        #if (CLOCK_SOURCES & TIMER_AHB_BUS_CLOCK)
            case ahbClock:
                #if ((PRESCALER_TYPE == PRESCALER_8_BIT) && (REGISTER_SET == NS9215_REGS))
                    {
                        int iclock = SCM_TCR_AHB_CLOCK;
                        
                        switch (raw.prescaler)
                        {
                            case 1:
                                iclock = SCM_TCR_AHB_CLOCK_TIMES_2;
                                break;
                            case 2:
                                iclock = SCM_TCR_AHB_CLOCK;
                                break;
                            case 4:
                                iclock = SCM_TCR_AHB_CLOCK_DIV_2;
                                break;
                            case 8:
                                iclock = SCM_TCR_AHB_CLOCK_DIV_4;
                                break;
                            case 16:
                                iclock = SCM_TCR_AHB_CLOCK_DIV_8;
                                break;
                            case 32:
                                iclock = SCM_TCR_AHB_CLOCK_DIV_16;
                                break;
                            case 64:
                                iclock = SCM_TCR_AHB_CLOCK_DIV_32;
                                break;
                            case 128:
                                iclock = SCM_TCR_AHB_CLOCK_DIV_64;
                                break;
                            case 256:
                                iclock = SCM_TCR_AHB_CLOCK_DIV_128;
                                break;
                            default:
                                ccode = timerInvalidPrescaler;
                                goto errorExit;
                        }
                        narm_write_reg (NA_SCM_TCR_REG, addr, clockSelect, iclock);
                    }
                #else
                    #error TODO:  Update NATimerSetPeriod() for new prescaler/register set combination
                #endif
                break;
        #endif
        #if (CLOCK_SOURCES & TIMER_CRYSTAL)
            case crystal:
                #if ((PRESCALER_TYPE == PRESCALER_9_BIT) && (REGISTER_SET == DUDA_REGS))
                    narm_write_reg (NARM_TMRCR_REG, addr, tclk, TIMER_CRYSTAL_CLOCK);
                    if (raw.prescaler == 512)
                    {
                        narm_write_reg (NARM_TMRCR_REG, addr, tpre, TIMER_512_PRESCALER);
                    }
                    else if (raw.prescaler == 1)
                    {
                        narm_write_reg (NARM_TMRCR_REG, addr, tpre, TIMER_NO_PRESCALER);
                    }
                    else
                    {
                        ccode = timerInvalidPrescaler;
                        goto errorExit;
                    }
                #else
                    #error TODO:  Update NATimerSetPeriod() to support new chip with crystal clock source
                #endif
                break;
        #endif
        case external:
            #if (CLOCK_SOURCES & TIMER_EXTERNAL_CLOCK)
                #if (REGISTER_SET == NS9XXX_REGS) || (REGISTER_SET == NS9215_REGS)
                    if (NATimerPrivateHasGpioInputPin(handle))
                    {
                        narm_write_reg (NA_SCM_TCR_REG, addr, clockSelect, SCM_TCR_EXTERNAL_EVENT);
					}
                    else
                    {
                        ccode = timerNoGpio;
					}
					break;
                #else
                    #error TODO:  Update NATimerSetPeriod() for new register set with external clock
                #endif
            #else
                ccode = timerNotSupported;
                goto errorExit;
            #endif
        default:
            ccode = timerNotSupported;
            goto errorExit;
    }
    
    #if (REGISTER_SET == NS9XXX_REGS) || (REGISTER_SET == NS9215_REGS)
        #if SUPPORTS_CASCADED_TIMERS
            if (size == timer64Bit)
            {
                narm_write_reg (NA_SCM_TCR_REG, addrHi, upDownSelect, raw.direction);
                narm_write_reg (NA_SCM_TRCR_REG, TRCR_getAddress(handle+1), counter, raw.reloadValue.s.msw);
            }
        #endif
        narm_write_reg (NA_SCM_TCR_REG, addr, upDownSelect, raw.direction);
        narm_write_reg (NA_SCM_TRCR_REG, TRCR_getAddress(handle), counter, raw.reloadValue.s.lsw);
    #elif (REGISTER_SET == DUDA_REGS)
        if (raw.direction == up)
        {
            ccode = timerNotSupported;
            goto errorExit;
        }
        narm_write_reg (NARM_TMRCR_REG, addr, itc, raw.reloadValue.s.lsw);
    #else
        #error TODO:  Update NATimerSetPeriod() to support new timer register set
    #endif
    errorExit:
    
    return ccode;
}


/*ad*
 * This function configures a timer that has been opened with NATimerOpen.  The 
 * timer configuration is passed to this function through a parameter.  The timer
 * is ready for use, but not started, after this function completes.  Use 
 * the NATimerStart function to start the timer.
 *
 * @category BSP:Device_Drivers:Timer
 * @since 6.2
 *
 * @param handle    Timer handle acquired with NATimerOpen
 * @param config    Pointer to structure with timer's configuration settings
 *
 * @return timerSuccess         The timer has been configured and is ready for use.
 * @return timerBadHandle       The handle is invalid.
 * @return timerInvalidConfig   The configuration is invalid.
 * @return timerNotSupported    The configuration is not supported by the processor.
*ad*/
static timerCcodeType NATimerPrivateConfigure(timerHandleType const handle, timerPrivateConfigType * const config)
{
    timerCcodeType ccode = timerSuccess;
    BOOLEAN shortPeriod = FALSE;
    
    GET_SEMAPHORE;
    
    NA_ASSERT(NATimerIsHandleValid(handle));
    NA_ASSERT(config != NULL);
    
    if (NATimerIsHandleValid(handle))
    {
        unsigned long int volatile * const addr = TCR_getAddress(handle);
        #if SUPPORTS_CASCADED_TIMERS
            unsigned long int volatile * const addrHi = TCR_getAddress(handle+1);
        #endif
        NA_UNUSED_PARAM(addr);                                  /* addr might not be used due to conditional compilation*/
        
        if (NATimerInfo[handle].state != timerOpen)
        {
            ccode = timerAlreadyConfigured;
            goto errorExit;
        }
        NATimerInfo[handle].config = *config;
        
        NATimerPrivateDisable(handle);
        NATimerPrivateInterruptSet(handle, DISABLE_INTERRUPTS);
        
        if ((NATimerPrivateHasGpioInputPin(handle) == FALSE) && (config->gating != notGated))
        {
            ccode = timerNoGpio;
            goto errorExit;
        }
        #if (REGISTER_SET == NS9215_REGS)
            if (   (config->captureMode != timerNormalMode) 
                && (NATimerInfo[handle].size != timer16Bit))
            { /* capture and compare modes are only supported in 16-bit mode */
                ccode = timerInvalidConfig;
                goto errorExit;
            }
            if (   (NATimerPrivateHasGpioInputPin(handle) == FALSE) 
                && (   (config->captureMode > timerCompareModePulseOutputOnMatch)
                    && (config->captureMode < timerSquareWaveOutput)))
            {
                ccode = timerNoGpio;
                goto errorExit;
            }
            if (   (NATimerPrivateHasGpioOutputPin(handle) == FALSE) 
                && (   (config->captureMode == timerCompareModeToggleOutputOnMatch) 
                    || (config->captureMode == timerCompareModePulseOutputOnMatch)
                    || (config->captureMode == timerSquareWaveOutput)))
            {
                ccode = timerNoGpio;
                goto errorExit;
            }
            if (   (handle < 6)
                && (config->captureMode == timerSquareWaveOutput))
            { /* only timers 6-9 on NS9210/15 support square wave feature */
                ccode = timerInvalidConfig;
                goto errorExit;
            }

            if (   (config->gating != notGated) 
                && (config->captureMode > timerCompareModePulseOutputOnMatch))
            {
                ccode = timerInvalidConfig;
                goto errorExit;
            }
            if (   (config->timeoutKey == timerRaw) 
                && (config->timeout.raw.source == external)
                && (config->captureMode > timerCompareModePulseOutputOnMatch)
                && (config->captureMode < timerSquareWaveOutput))
            { /* cannot use capture mode if using input pin for external clock source */
                ccode = timerInvalidConfig;
                goto errorExit;
            }
        #else 
            if (config->captureMode != timerNormalMode)
            {   /* capture and compare modes are only supported on NS9215/NS9210 */
                ccode = timerInvalidConfig;
                goto errorExit;
            }
        #endif
        #if (REGISTER_SET == NS9XXX_REGS) || (REGISTER_SET == NS9215_REGS)
            if ((config->gating != notGated) && (config->timeoutKey == timerRaw) && (config->timeout.raw.source == external))
            {
                /*
                 * Timer cannot be both gated and use an external clock since the GPIO pin can only
                 * be assigned to one of those functions.
                 */
                ccode = timerInvalidConfig;
                goto errorExit;
            }
            switch (config->gating)
            {
                case notGated:
                    narm_write_reg (NA_SCM_TCR_REG, addr, mode, SCM_TCR_INT_TIMER);
                    #if (REGISTER_SET == NS9215_REGS)
                        if (config->captureMode == timerSquareWaveOutput)
                        {
                            /*
                             * Square wave output is setting is special because it is really a setting
                             * in the mode2 field.
                             */
                            narm_write_reg (NA_SCM_TCR_REG, addr, captureAndCompareMode, SCR_TCR_NORMAL);
                            narm_write_reg (NA_SCM_TCR_REG, TCR_getAddress(handle), mode2, SCR_TCR_CLOCK_MODE);
                        }
                        else
                        {
                            /*
                             * The timerCaptureModetype enum is defined to match the actual
                             * values used in the register, so we can just write config->captureMode
                             * into the field.
                             */
                            narm_write_reg (NA_SCM_TCR_REG, addr, captureAndCompareMode, config->captureMode);
                            /*
                             * The NS9215 only supports 16-bit compare counters.  We use a 64-bit word to store
                             * the compareCount for future proofing, and, for now, just and off the extra bits.
                             */
                            narm_write_reg (NA_SCM_TRCR_REG, TRCR_getAddress(handle), compareCount, config->compareCount.s.lsw & 0xffff);
                            narm_write_reg (NA_SCM_TCR_REG, TCR_getAddress(handle), mode2, SCR_TCR_USE_MODE1_VALUE);
                        }
                    #endif
                    break;
                case runWhenGateIsHigh:
                    #if SUPPORTS_GPIO
                        narm_write_reg (NA_SCM_TCR_REG, addr, mode, SCM_TCR_EXT_HIGH_LEVEL_GATED_TIMER);
                        break;
                    #else
                        ccode = timerNoGpio;
                        goto errorExit;
                    #endif
                case runWhenGateIsLow:
                    #if SUPPORTS_GPIO
                        narm_write_reg (NA_SCM_TCR_REG, addr, mode, SCM_TCR_EXT_LOW_LEVEL_GATED_TIMER);
                        break;
                    #else
                        ccode = timerNoGpio;
                        goto errorExit;
                    #endif
            }
            #if SUPPORTS_HW_ONESHOT
                if (config->oneShot)
                {
                    narm_write_reg(NA_SCM_TCR_REG, addr, reloadEnable, SCM_TCR_HALT);
                }
                else
                {
                    narm_write_reg(NA_SCM_TCR_REG, addr, reloadEnable, SCM_TCR_RESUME);
                }
            #endif
            switch (NATimerInfo[handle].size)
            {
                #if (SUPPORTED_SIZES & TIMER_16_BIT)
                    case timer16Bit:
                        narm_write_reg(NA_SCM_TCR_REG, addr, bitTimer, SCM_TCR_16_BIT_TIMER);
                        break;
                #endif
                #if (SUPPORTED_SIZES & TIMER_32_BIT)
                    case timer32Bit:
                        narm_write_reg(NA_SCM_TCR_REG, addr, bitTimer, SCM_TCR_32_BIT_TIMER);
                        break;
                #endif
                #if SUPPORTS_CASCADED_TIMERS
                    case timer64Bit:
                        {
                            narm_write_reg(NA_SCM_TCR_REG, addr, bitTimer, SCM_TCR_32_BIT_TIMER);
                            
                            /*
                             * If we are creating a 64-bit timer by concatenating 2 adjacent ones,
                             * then setup the high timer now.
                             */
                            narm_write_reg(NA_SCM_TCR_REG, addrHi, bitTimer, SCM_TCR_32_BIT_TIMER);
                            narm_write_reg (NA_SCM_TCR_REG, addrHi, mode, SCM_TCR_CONCATENATE);
                            #if (REGISTER_SET == NS9215_REGS)
                                narm_write_reg (NA_SCM_TCR_REG, addrHi, clockSelect, SCM_TCR_AHB_CLOCK_TIMES_2);
                            #elif (REGISTER_SET == NS9XXX_REGS)
                                narm_write_reg (NA_SCM_TCR_REG, addrHi, clockSelect, SCM_TCR_CPU_CLOCK);
                            #else
                                #error TODO:  Update NATimerConfigure() to support new register set
                            #endif
                            if (config->oneShot)
                            {
                                narm_write_reg(NA_SCM_TCR_REG, addrHi, reloadEnable, SCM_TCR_HALT);
                            }
                            else
                            {
                                narm_write_reg(NA_SCM_TCR_REG, addrHi, reloadEnable, SCM_TCR_RESUME);
                            }
                            #if (DEBUGGER_EFFECT == TIMER_CAN_BE_CONFIGURED_TO_HALT_IN_DEBUGGER)
                                if (config->haltInDebugger)
                                {
                                    narm_write_reg(NA_SCM_TCR_REG, addrHi, haltInDebugger, SCM_TCR_HALT_IN_DEBUGGER);
                                }
                                else
                                {
                                    narm_write_reg(NA_SCM_TCR_REG, addrHi, haltInDebugger, SCM_TCR_RUN_IN_DEBUGGER);
                                }
                            #endif
                        }
                        break;
                    #endif
                default:
                    customizeErrorHandler(ERROR_TIMER, ERROR_TIMER_INTERNAL_DRIVER_ERROR);
                    break;
            }
        #elif (REGISTER_SET == DUDA_REGS)
            if (config->gating != notGated)
            {
                ccode = timerNotSupported;
                goto errorExit;
            }
        #else
            #error TODO:  Update NATimerConfigure() to support new register set
        #endif
        ccode = NATimerSetPeriod(handle, config, NATimerInfo[handle].size, &shortPeriod);
        #if (DEBUGGER_EFFECT == TIMER_CAN_BE_CONFIGURED_TO_HALT_IN_DEBUGGER)
            if (config->haltInDebugger)
            {
                narm_write_reg(NA_SCM_TCR_REG, addr, haltInDebugger, SCM_TCR_HALT_IN_DEBUGGER);
            }
            else
            {
                narm_write_reg(NA_SCM_TCR_REG, addr, haltInDebugger, SCM_TCR_RUN_IN_DEBUGGER);
            }
        #endif
        if ((ccode == timerSuccess) && (config->isr))
        {
            if (shortPeriod)
            {
                /*
                 * Disallow extremely short period with an ISR.  It would kill us.
                 */
                ccode = timerInvalidConfig;
                goto errorExit;
            }
            #if (REGISTER_SET == NS9XXX_REGS) || (REGISTER_SET == NS9215_REGS)
                if (NATimerInfo[handle].size == timer64Bit)
                {
                    /*
                     * When using two timers to create a 64-bit timer, it is the higher numbered 
                     * timer (with the MSW) that should be configured to interrupt.
                     */
                    narm_write_reg(NA_SCM_TCR_REG, addrHi, clearInterrupt, 1);
                    narm_write_reg(NA_SCM_TCR_REG, addrHi, clearInterrupt, 0);
                    if (NATimerIsrInstallCount[NATimerContext[handle+1]] == 0)
                    {
                        if (naIsrInstall(NATimerIsrId[handle+1], (MC_ISR_HANDLER) driverLowLevelIsrHandler, (void *) NATimerContext[handle]) != SUCCESS)
                        {
                            ccode = timerInvalidConfig;
                        }
                    }
                    if (ccode == timerSuccess)
                    {
                        NATimerIsrInstallCount[NATimerContext[handle+1]]++;
                        NATimerInfo[handle].isrInstalled = TRUE;
                    }
                }
                else
                {
                    NATimerPrivateInterruptAck(handle);
                    if (NATimerIsrInstallCount[NATimerContext[handle]] == 0)
                    {
                        if (naIsrInstall(NATimerIsrId[handle], (MC_ISR_HANDLER) driverLowLevelIsrHandler, (void *) NATimerContext[handle]) != SUCCESS)
                        {
                            ccode = timerInvalidConfig;
                        }
                    }
                    if (ccode == timerSuccess)
                    {
                        NATimerIsrInstallCount[NATimerContext[handle]]++;
                        NATimerInfo[handle].isrInstalled = TRUE;
                    }
                }
            #elif (REGISTER_SET == DUDA_REGS)
                if (NATimerIsrInstallCount[NATimerContext[handle]] == 0)
                {
                    if (naIsrInstall(NATimerIsrId[handle], (NA_ISR_HANDLER) driverLowLevelIsrHandler, (void *) NATimerContext[handle]) != SUCCESS)
                    {
                        ccode = timerInvalidConfig;
                    }
                }
                if (ccode == timerSuccess)
                {
                    NATimerIsrInstallCount[NATimerContext[handle]]++;
                    NATimerInfo[handle].isrInstalled = TRUE;
                }
            #else
                #error TODO:  Update NATimerConfigure() to support new register set
            #endif
        }
    }
    else
    {
        ccode = timerBadHandle;
    }
    
    if (ccode == timerSuccess)
    {
        NATimerInfo[handle].state = timerConfigured;
    }
    
    errorExit:
    
    RELEASE_SEMAPHORE;
    return ccode;
}


/*ad*
 * This function configures a timer that has been opened with NATimerOpen.  The 
 * timer configuration is passed to this function through a parameter.  The timer
 * is ready for use, but not started, after this function completes.  Use 
 * the NATimerStart function to start the timer.  
 *
 * @category BSP:Device_Drivers:Timer
 * @since 6.2
 * @external
 * @deprecated 7.3
 *
 * @param handle    Timer handle acquired with NATimerOpen
 * @param config    Pointer to structure with timer's configuration settings
 *
 * @note        This function is deprecated.  New code should use NATimerConfig.
 *
 * @return timerSuccess         The timer has been configured and is ready for use.
 * @return timerBadHandle       The handle is invalid.
 * @return timerInvalidConfig   The configuration is invalid.
 * @return timerNotSupported    The configuration is not supported by the processor.
*ad*/
timerCcodeType NATimerConfigure(timerHandleType const handle, timerConfigType * const config)
{
    timerPrivateConfigType privateConfig;
    
    privateConfig.timeoutKey = config->timeoutKey;    
    privateConfig.timeout = config->timeout;       
    privateConfig.gating = config->gating;        
    privateConfig.oneShot = config->oneShot;       
    privateConfig.isr = config->isr;           
    privateConfig.isrContext = config->isrContext;    
    privateConfig.haltInDebugger = config->haltInDebugger;
    privateConfig.captureMode = timerNormalMode;
    privateConfig.compareCount.x = 0LL;

    return NATimerPrivateConfigure(handle, &privateConfig);
}




/*ad*
 * This function configures a timer that has been opened with NATimerOpen.  The 
 * timer configuration is passed to through parameters.  This function replaces
 * the NATimerConfigure function.
 *
 * @category BSP:Device_Drivers:Timer
 * @since 6.2
 * @external
 *
 * @param handle        Timer handle acquired with NATimerOpen
 * @param timeoutKey    Set this field to indicate which field in the timeout union is used
 * @param timeout       You must load this parameter with the timeout value for the timer
 * @param gating        Set to indicate how the timer is gated
 * @param oneShot       Set to TRUE if the timer should be disabled when it reaches its 
 *                      terminal count
 * @param isr           Set to the Interrupt Service Routine for this timer, or to NULL if
 *                      no ISR will be used
 * @param isrContext    Set to a value which will be passed to the ISR routine
 * @param haltInDebugger    Set if the timer should stop running when the CPU is stopped in
 *                          the debugger
 * @param captureMode       Set to indicate which capture mode should be used
 * @param compareValue      Set to indicate which compare value should be used for capture mode
 *
 * @return timerSuccess         The timer has been configured and is ready for use.
 * @return timerBadHandle       The handle is invalid.
 * @return timerInvalidConfig   The configuration is invalid.
 * @return timerNotSupported    The configuration is not supported by the processor.
*ad*/
timerCcodeType NATimerConfig(timerHandleType const handle, timerTimeoutUnionKeyType timeoutKey, timerTimeoutUnionType timeout, timerGatingType gating, BOOLEAN oneShot, timerIsrType isr, void * isrContext, BOOLEAN haltInDebugger, timerCaptureModeType captureMode, WORD64 compareValue)
{
    timerPrivateConfigType privateConfig;
    
    privateConfig.timeoutKey = timeoutKey;    
    privateConfig.timeout = timeout;       
    privateConfig.gating = gating;        
    privateConfig.oneShot = oneShot;       
    privateConfig.isr = isr;           
    privateConfig.isrContext = isrContext;    
    privateConfig.haltInDebugger = haltInDebugger;
    privateConfig.captureMode = captureMode;
    privateConfig.compareCount = compareValue;

    return NATimerPrivateConfigure(handle, &privateConfig);
}


    

/*ad*
 * This function translates a handle acquired with NATimerOpen into a physical
 * timer number.  The number is 0-based.
 *
 * @category BSP:Device_Drivers:Timer
 * @since 6.2
 * @external
 *
 * @param handle        Timer handle acquired with NATimerOpen.
 * @param timerNumber   Pointer to int to load with physical timer number
 *
 * @return timerSuccess         The physical timer number has been stored in the int pointed to by timerNumber
 * @return timerBadHandle       The handle is invalid
*ad*/
timerCcodeType NATimerIdentify(timerHandleType const handle, int * timerNumber)
{
    timerCcodeType ccode = timerBadHandle;
    
    NA_ASSERT(timerNumber != NULL);
    NA_ASSERT(NATimerIsHandleValid(handle));
    
    if (NATimerIsHandleValid(handle))
    {
        *timerNumber = (int) handle;
        ccode = timerSuccess;
    }
    
    return ccode;
}

/*ad*
 * This function reads the current value of the timer/counter.
 *
 * @category BSP:Device_Drivers:Timer
 * @since 6.2
 * @external
 *
 * @param handle    Timer handle acquired with NATimerOpen
 * @param value     Pointer to buffer into which to write timer value
 *
 * @return timerSuccess         The timer value has been stored in the buffer pointed to by value.
 * @return timerBadHandle       The handle is invalid.
*ad*/
timerCcodeType NATimerRead(timerHandleType const handle, WORD64 * value)
{
    timerCcodeType ccode = timerBadHandle;
    
    NA_ASSERT(value != NULL);
    NA_ASSERT(NATimerIsHandleValid(handle));
    
    if (NATimerIsHandleValid(handle))
    {
        #if (REGISTER_SET == NS9XXX_REGS) || (REGISTER_SET == NS9215_REGS)
            timerInfoType *info = &NATimerInfo[handle];
            unsigned long int volatile * const addr = TRR_getAddress((int) handle);
            unsigned long int volatile * const addrHi = TRR_getAddress((int) handle+1);
            long long mask = 0;
        
            value->s.lsw = narm_read_reg (NA_SCM_TRR_REG, addr, counter);
            if (info->size == timer64Bit)
            {
                value->s.msw = narm_read_reg (NA_SCM_TRR_REG, addrHi, counter);
            }
            else
            {
                value->s.msw = 0;
            }
            #if (REGISTER_SET == NS9215_REGS)
                if (info->size == timer16Bit)
                {
                    /*
                     * Need to mask because NS9215 and NS9210 use upper half word for
                     * capture count when in 16-bit mode.
                     */
                    value->s.lsw &= 0xffff;         
                }
            #endif
            switch (info->size)
            {
                case timer16Bit:
                    mask = (1LL << 16) - 1;
                    break;
                case timer26Bit:
                    mask = (1LL << 26) - 1;
                    break;
                case timer32Bit:
                    mask = (1LL << 32) - 1;
                    break;
                case timer64Bit:
                    mask = ~0LL;
                    break;
                case timerNaturalSize:
                    customizeErrorHandler(ERROR_TIMER, ERROR_TIMER_INTERNAL_DRIVER_ERROR);
                    break;
            }
            value->x &= mask;
        #elif (REGISTER_SET == DUDA_REGS)
            unsigned long int volatile * const addr = getTimerStatusRegisterAddress(handle);
            
            value->s.msw = 0;
            value->s.lsw = narm_read_reg (NARM_TMRSR_REG, addr, ctc);
        #else
            #error TODO:  NATimerRead() to support new register set
        #endif
        ccode = timerSuccess;
    }
    
    return ccode;
}

/*ad*
 * This function closes a timer opened by NATimerOpen.  The timer is shut down,
 * the timer interrupt is disabled, the application ISR is uninstalled, and the 
 * timer is made available to other applications.
 *
 * @category BSP:Device_Drivers:Timer
 * @since 6.2
 * @external
 *
 * @param handle    Timer handle acquired with NATimerOpen
 *
 * @return timerSuccess         The timer has been shutdown and closed.
 * @return timerBadHandle       The handle is invalid.
*ad*/
timerCcodeType NATimerClose(timerHandleType const handle)
{
    timerCcodeType ccode = timerSuccess;
    
    /*
     * We must call NATimerStop() before getting the semaphore since it also
     * gets the semaphore.  NATimerStop() will take care of validating the
     * handle before it does anything.
     */
    NATimerStop(handle); 
    
    GET_SEMAPHORE;
    
    NA_ASSERT(NATimerIsHandleValid(handle));
    
    if (NATimerIsHandleValid(handle))
    {
        if (NATimerInfo[handle].config.isr != NULL)
        {
            NATimerInterruptDisable(handle);
            NATimerUninstallIsr(handle);
        }
        #if (((REGISTER_SET == NS9XXX_REGS) || (REGISTER_SET == NS9215_REGS)) && (SUPPORTS_CASCADED_TIMERS))
            if (NATimerInfo[handle].size == timer64Bit)
            {
                unsigned long int volatile * const addr = TRCR_getAddress(handle + 1);
    
                narm_write_reg (NA_SCM_TCR_REG, addr, mode, SCM_TCR_INT_TIMER);
                NATimerInfo[handle+1].state = timerClosed;
            }
        #endif
        NATimerInfo[handle].state = timerClosed;
    }
    else
    {
        ccode = timerBadHandle;
    }
    
    RELEASE_SEMAPHORE;
    return ccode;
}



/*ad*
 * This function reserves a timer for use by the application.  Set the parameter
 * timerNumber to TIMER_NEXT_AVAILABLE to open the next available timer that has
 * not been assigned a GPIO pin.  Otherwise, set timerNumber to the number of the
 * timer to open.
 *
 * @note    This function uses constants defined in gpio.h to determine
 *          which timers have been assigned GPIO pins.  It will not open a 
 *          timer that has been assigned a GPIO pin when timerNumber is set to 
 *          TIMER_NEXT_AVAILABLE. Set timerNumber to the physical timer number 
 *          to open one of these timers.
 *
 * @category BSP:Device_Drivers:Timer
 * @since 6.2
 * @external
 *
 * @param timerNumber   Physical timer number to open (0-based), or set to TIMER_NEXT_AVAILABLE 
 *                      to open any available timer that has not been assigned a GPIO pin.  
 * @param handle        Pointer to buffer to load with timer handle.
 * @param size          Load with the required size of the timer.
 *
 * @see @link TIMER_NEXT_AVAILABLE
 *
 * @return timerSuccess         The timer has been opened and handle has been loaded with 
 *                              the timer's handle.
 * @return timerOutOfRange      The processor does not support the specified physical timer number.
 * @return timerNotAvailable    The specified physical timer has already been opened by another application.
 * @return timerOutOfResources  No more timers of the specified size are available.
 * @return timerNotSupported    The processor does not support timers of the indicated size.
*ad*/
timerCcodeType NATimerOpen(int const timerNumber, timerHandleType * handle, timerSizeType const size)
{
    timerCcodeType ccode = timerSuccess;
    int physicalTimer = timerNumber;
    int timerCount = (size == timer64Bit) ? MAX_TIMERS - 1 : MAX_TIMERS;
    
    GET_SEMAPHORE;
    
    NA_ASSERT((timerNumber == TIMER_NEXT_AVAILABLE) || ((timerNumber >=0) && (timerNumber < MAX_TIMERS)));
    NA_ASSERT(handle != NULL);
    
    switch (size)
    {
        #if (SUPPORTED_SIZES & TIMER_16_BIT)
            case timer16Bit:
        #endif
        #if (SUPPORTED_SIZES & TIMER_26_BIT)
            case timer26Bit:
        #endif
        #if (SUPPORTED_SIZES & TIMER_32_BIT)
            case timer32Bit:
        #endif
        #if SUPPORTS_CASCADED_TIMERS
            case timer64Bit:
        #endif
        case timerNaturalSize:
            break;
        default:
            ccode = timerNotSupported;
            goto errorExit;
    }
    
    if (physicalTimer == TIMER_NEXT_AVAILABLE)
    {
        int timerIndex;
        
        for (timerIndex = 0; timerIndex < timerCount; timerIndex++)
        {
            if ((NATimerInfo[timerIndex].state == timerClosed) && (NATimerPrivateHasGpioInputPin(timerIndex) == FALSE) && (NATimerPrivateHasGpioOutputPin(timerIndex) == FALSE))
            {
                #if SUPPORTS_CASCADED_TIMERS
                    if (size == timer64Bit) 
                    {
                        if ((NATimerInfo[timerIndex+1].state == timerClosed) && (NATimerPrivateHasGpioInputPin(timerIndex+1) == FALSE) && (NATimerPrivateHasGpioOutputPin(timerIndex+1) == FALSE))
                        {
                            physicalTimer = timerIndex;
                            break;
                        }
                    }
                    else
                    {
                        physicalTimer = timerIndex;
                        break;
                    }
                #else
                    physicalTimer = timerIndex;
                    break;
                #endif
            }
        }
        
        if (physicalTimer == TIMER_NEXT_AVAILABLE)
        {
            ccode = timerOutOfResources;
            goto errorExit;
        }
    }
    else
    {
        if ((physicalTimer < timerCount) && (physicalTimer >= 0))
        {
            if (NATimerInfo[physicalTimer].state == timerClosed)
            {
                #if SUPPORTS_CASCADED_TIMERS
                    if (size == timer64Bit)
                    {
                        if (NATimerInfo[physicalTimer+1].state != timerClosed)
                        {
                            ccode = timerNotAvailable;
                            goto errorExit;
                        }
                    }
                #endif
            }
            else
            {
                ccode = timerNotAvailable;
                goto errorExit;
            }
        }
        else
        {
            ccode = timerOutOfRange;
            goto errorExit;
        }
    }
    
    NATimerInfo[physicalTimer].state = timerOpen;
    NATimerInfo[physicalTimer].isrInstalled = FALSE;
    if (size == timerNaturalSize)
    {
        NATimerInfo[physicalTimer].size = NATURAL_SIZE;
    }
    else
    {
        NATimerInfo[physicalTimer].size = size;
    }
    
    #if SUPPORTS_CASCADED_TIMERS
        if (size == timer64Bit)
        {
            NATimerInfo[physicalTimer+1].state = timerConcatenated;
        }
    #endif
    *handle = physicalTimer;

    errorExit:
    
    RELEASE_SEMAPHORE;
    return ccode;
}    
        
        
        
        
/*ad*
 * This function starts a timer that has been opened and configured.
 * The contents of the timer will be reloaded with
 * the initial count specified when it was configured.
 *
 * @category BSP:Device_Drivers:Timer
 * @since 6.2
 * @external
 *
 * @param handle    Timer handle acquired with NATimerOpen
 *
 * @return timerSuccess         The timer has been started.
 * @return timerBadHandle       The handle is invalid.
 * @return timerNotConfigured   The timer is not configured.
*ad*/
timerCcodeType NATimerStart(timerHandleType const handle)
{
    timerCcodeType ccode = timerBadHandle;
    
    /*
     * First make sure the timer has been stopped.  This will clear the timer enable bit
     * and thus make sure the timer reloads itself with the initial count set when it
     * was configured.  Since that's what we say we do in our autodoc comment, we better
     * make sure it happens, even if the user does not call NATimerStop() himself.
     */
    NATimerStop(handle);
    
    GET_SEMAPHORE;
    NA_ASSERT(NATimerIsHandleValid(handle));

    if (NATimerIsHandleValid(handle))
    {
        if ((NATimerInfo[handle].state == timerConfigured) || (NATimerInfo[handle].state == timerRunning))
        {
            #if (REGISTER_SET == NS9XXX_REGS)|| (REGISTER_SET == NS9215_REGS)
                /*
                 * Start the timer by setting the timer enable bit(s).  This has the side effect
                 * of reloading the timer with the initial count set when it was configured provided
                 * that enable bit(s) were clear, which we made sure of at the very start of this
                 * function.
                 */
                if (NATimerInfo[handle].size == timer64Bit)
                {
                    narm_write_reg (NA_SCM_TCR_REG, TCR_getAddress(handle+1), enable, SCM_TCR_ENABLE);
                }
                narm_write_reg (NA_SCM_TCR_REG, TCR_getAddress(handle), enable, SCM_TCR_ENABLE);
            #elif (REGISTER_SET == DUDA_REGS)
                narm_write_reg (NARM_TMRCR_REG, TCR_getAddress(handle), te, TIMER_ENABLE);
            #else
                #error TODO:  Update NATimerStart() with new register set
            #endif
            NATimerInfo[handle].state = timerRunning;
            ccode = timerSuccess;
        }
        else
        {
            ccode = timerNotConfigured;
        }
    }
    
    RELEASE_SEMAPHORE;
    return ccode;
}

/*ad*
 * This function stops a timer.  The timer count is not cleared by stopping
 * the timer, and can be read after this function completes.  However, calling 
 * NATimerStart will reload the timer with the initial count specified when it 
 * was configured.
 *
 * @note    This function cannot be called from within an ISR.  A timer ISR 
 *          should stop its timer by returning the value ackAndShutdown.
 *
 * @category BSP:Device_Drivers:Timer
 * @since 6.2
 * @external
 *
 * @param handle    Timer handle acquired with NATimerOpen
 *
 * @see @link timerIsrType
 *
 * @return timerSuccess         The timer has been shut down.
 * @return timerBadHandle       The handle is invalid.
*ad*/
timerCcodeType NATimerStop(timerHandleType const handle)
{
    timerCcodeType ccode = timerBadHandle;
    
    GET_SEMAPHORE;
    NA_ASSERT(NATimerIsHandleValid(handle));

    if (NATimerIsHandleValid(handle))
    {
        NATimerPrivateDisable(handle);
        if (NATimerInfo[handle].state == timerRunning)
        {
            NATimerInfo[handle].state = timerConfigured;
        }
        ccode = timerSuccess;
    }
    
    RELEASE_SEMAPHORE;
    return ccode;
}



/*ad*
 * The application must call this function to enable the timer's interrupt.  
 * No timer interrupts occur until after this function has been called.
 *
 * @note    If an interrupt is pending on the timer, the application's ISR 
 *          will be called before this function completes
 *
 * @category BSP:Device_Drivers:Timer
 * @since 6.2
 * @external
 *
 * @param handle    Timer handle acquired with NATimerOpen
 *
 * @return timerSuccess         Interrupts are enabled on the timer.
 * @return timerNoIsr           The application did not install an ISR.
 * @return timerBadHandle       The handle is invalid.
 * @return timerNotConfigured   The timer is not configured yet.
*ad*/
timerCcodeType NATimerInterruptEnable(timerHandleType const handle)
{
    timerCcodeType ccode = timerBadHandle;
    
    NA_ASSERT(NATimerIsHandleValid(handle));

    if (NATimerIsHandleValid(handle))
    {
        if (NATimerInfo[handle].config.isr == NULL)
        {
            ccode = timerNoIsr;
        }
        else if ((NATimerInfo[handle].state == timerConfigured) || (NATimerInfo[handle].state == timerRunning))
        {
            NATimerPrivateInterruptSet(handle, ENABLE_INTERRUPTS);
            #if (FIRST_INTERRUPT_IS_BOGUS || (SUPPORTS_HW_ONESHOT == FALSE))
                NATimerInfo[handle].interruptCount = 0;
            #endif
            naInterruptEnable(NATimerIsrId[handle]);
            ccode = timerSuccess;
        }
        else
        {
            ccode = timerNotConfigured;
        }
    }
    
    return ccode;
}






/*ad*
 * This function disables interrupts on the timer.
 *
 * @category BSP:Device_Drivers:Timer
 * @since 6.2
 * @external
 *
 * @param handle    Timer handle acquired with NATimerOpen
 *
 * @return timerSuccess         Interrupts have been disabled on the timer.
 * @return timerBadHandle       The handle is invalid.
*ad*/
timerCcodeType NATimerInterruptDisable(timerHandleType const handle)
{
    timerCcodeType ccode = timerBadHandle;
    
    NA_ASSERT(NATimerIsHandleValid(handle));

    if (NATimerIsHandleValid(handle))
    {
        NATimerPrivateInterruptSet(handle, DISABLE_INTERRUPTS);
        ccode = timerSuccess;
    }

    return ccode;
}


/*ad*
 * This function acknowledges an interrupt on a timer.  NATimerInterruptAck can be
 * called from an application ISR to clear the interrupt pending
 * flag in the timer.  
 *
 * @category BSP:Device_Drivers:Timer
 * @since 6.2
 * @external
 *
 * @param handle    Timer handle acquired with NATimerOpen
 *
 * @return timerSuccess         The interrupt pending flag has been cleared.
 * @return timerBadHandle       The handle is invalid.
*ad*/
timerCcodeType NATimerInterruptAck(timerHandleType const handle)
{
    timerCcodeType ccode = timerBadHandle;
    
    NA_ASSERT(NATimerIsHandleValid(handle));

    if (NATimerIsHandleValid(handle))
    {
        NATimerPrivateInterruptAck(handle);
        NATimerInfo[handle].interruptAcked = TRUE;
        ccode = timerSuccess;
    }
    
    return ccode;
}


/*ad*
 * This function returns information about the processors support for timers.
 *
 * @category BSP:Device_Drivers:Timer
 * @since 6.2
 * @external
 *
 * @param info      pointer to structure to write system timer information into
 *
 * @return timerSuccess         The function succeeded.
 * @return timerNullPointer     The info parameter was NULL.
*ad*/
timerCcodeType NATimerSystemInfo(timerProcessorInfoType * info)
{
    timerCcodeType ccode = timerNullPointer;
    
    NA_ASSERT(info != NULL);
    
    if (info != NULL)
    {
        info->size = SUPPORTED_SIZES;
        #if SUPPORTS_CASCADED_TIMERS
            info->size |= TIMER_64_BIT;
        #endif
        info->naturalSize = NATURAL_SIZE;
        #if SUPPORTS_GPIO
            info->gpio = TIMER_TERMINAL_COUNT | TIMER_GATED | TIMER_GPIO_CLOCK;
        #else
            info->gpio = 0;
        #endif
        #if SOME_INTERRUPTS_SHARED
            info->isr = TIMER_INTERRUPT | TIMER_SHARED_INTERRUPT;
        #else
            info->isr = TIMER_INTERRUPT;
        #endif
        info->source = CLOCK_SOURCES;
        info->direction = DIRECTIONS;
        info->debuggerEffect = DEBUGGER_EFFECT;
        ccode = timerSuccess;
    }
    
    return ccode;
}



/*ad*
 * This function returns information about a timer's ability to perform 
 * capture and compare operations.  In a capture operation, the timer 
 * records the value in the counter register when its GPIO input is 
 * asserted.  In a compare operation, the timer generates a signal on its
 * GPIO output when the timer's counter equals the value in the compare
 * register.
 *
 * This function reports that a timer supports an operation only if the
 * operation is both supported by the timer and if a GPIO pin has been
 * appropriately configured in gpio.h to support the operation.
 *
 * @category BSP:Device_Drivers:Timer
 * @since 6.2
 * @external
 *
 * @param handle            handle opened with NATimerOpen
 * @param modes             pointer to word to set to indicate which capture 
 *                          and compare modes are supported by the hardware
 * @param captureWordWidth  pointer to word to set with bits to indicate which
 *                          word lengths are supported by the capture operation
 * @param compareWordWidth  pointer to word to set with bits to indicate which
 *                          word lengths are supported by the compare operation
 *
 * @see @link timerCaptureCompareConstants
 * @see @link timerSizesConstants
 *
 * @return timerSuccess         The function succeeded.
 * @return timerBadHandle       The handle is invalid.
*ad*/
timerCcodeType NATimerCaptureInfo(timerHandleType const handle, unsigned * modes, unsigned * captureWordWidth, unsigned * compareWordWidth)
{
    timerCcodeType ccode = timerBadHandle;
    
    NA_ASSERT(NATimerIsHandleValid(handle));

    if (NATimerIsHandleValid(handle))
    {
        #if (REGISTER_SET == NS9215_REGS)
            if (captureWordWidth)
            {
                *captureWordWidth = TIMER_16_BIT;
            }
            if (compareWordWidth)
            {
                *compareWordWidth = TIMER_16_BIT;
            }
            if (modes)
            {
                *modes = 0;
                if (NATimerPrivateHasGpioInputPin(handle))
                {
                    *modes = TIMER_SUPPORTS_CAPTURE;
                }
                if (NATimerPrivateHasGpioOutputPin(handle))
                {
                    *modes |= TIMER_SUPPORTS_COMPARE;
                    if ((handle > 5) && (handle < 10))
                    {
                        *modes |= TIMER_CAN_GENERATE_SQUARE_WAVE;
                    }
                }
            }
        #elif ((REGISTER_SET == DUDA_REGS) || (REGISTER_SET == NS9XXX_REGS))
            if (captureWordWidth)
            {
                *captureWordWidth = 0;
            }
            if (compareWordWidth)
            {
                *compareWordWidth = 0;
            }
            if (modes)
            {
                *modes = 0;
            }
        #else
            #error NATimerCaptureInfo needs to be updated for new register set
        #endif
        
            ccode = timerSuccess;
        }
    return ccode;
}



/*ad*
 * This function reads the capture value.  When capture mode is enabled, the
 * timer will store the value of the timer's counter at the time a signal 
 * was received on the timer's input pin into the capture register.  This
 * function retrieves this value.
 *
 * @category BSP:Device_Drivers:Timer
 * @since 6.2
 * @external
 *
 * @param handle            handle opened with NATimerOpen
 * @param captureValue      pointer to word to load with capture value
 *
 * @return timerSuccess         The function succeeded.
 * @return timerBadHandle       The handle is invalid.
 * @return timerNullPointer     The captureValue argument is NULL.
 * @return timerNotSupported    The timer does not support capture operation.
*ad*/
timerCcodeType NATimerReadCaptureRegister(timerHandleType const handle, WORD64 * captureValue)
{
    timerCcodeType ccode = timerBadHandle;
    
    NA_ASSERT(NATimerIsHandleValid(handle));
    NA_ASSERT(captureValue != NULL);
    
    if (captureValue == NULL)
    {
        ccode = timerNullPointer;
        goto errorExit;
    }
    captureValue->x = 0LL;
    
    if (NATimerIsHandleValid(handle))
    {
        #if (REGISTER_SET == NS9215_REGS)
            captureValue->s.lsw = narm_read_reg (NA_SCM_TRR_REG, TRR_getAddress(handle), captureRead);
            ccode = timerSuccess;
        #elif ((REGISTER_SET == DUDA_REGS) || (REGISTER_SET == NS9XXX_REGS))
            ccode = timerNotSupported;
        #else
            #error NATimerReadCaptureRegister needs to be updated to support the new register set
        #endif
    }

errorExit:  
    return ccode;
}



