/**
 * \addtogroup BSP
 * \{
 * \addtogroup DEVICES
 * \{
 * \addtogroup Timer0
 * \{
 */

/**
 *****************************************************************************************
 *
 * @file hw_timer0.h
 *
 * @brief Definition of API for the Timer0 Low Level Driver.
 *
 * Copyright (c) 2016, Dialog Semiconductor
 * All rights reserved.
 * Redistribution and use in source and binary forms, with or without modification, 
 * are permitted provided that the following conditions are met:
 * 1. Redistributions of source code must retain the above copyright notice, 
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation 
 *    and/or other materials provided with the distribution.
 * 3. Neither the name of the copyright holder nor the names of its contributors 
 *    may be used to endorse or promote products derived from this software without 
 *    specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
 * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
 * INDIRECT, INCIDENTAL,  SPECIAL,  EXEMPLARY,  OR CONSEQUENTIAL DAMAGES (INCLUDING, 
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 
 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 *   
 *****************************************************************************************
 */

#ifndef HW_TIMER0_H_
#define HW_TIMER0_H_

#if dg_configUSE_HW_TIMER0

#include <stdbool.h>
#include <stdint.h>
#include <black_orca.h>


/**
 * \brief Get the mask of a field of an TIMER0 register.
 *
 * \param [in] reg is the register to access
 * \param [in] field is the register field to access
 *
 */
#define HW_TIMER0_REG_FIELD_MASK(reg, field) \
        (GP_TIMERS_TIMER0_##reg##_REG_##field##_Msk)

/**
 * \brief Get the bit position of a field of an TIMER0 register.
 *
 * \param [in] reg is the register to access
 * \param [in] field is the register field to access
 *
 */
#define HW_TIMER0_REG_FIELD_POS(reg, field) \
        (GP_TIMERS_TIMER0_##reg##_REG_##field##_Pos)

/**
 * \brief Get the value of a field of an TIMER0 register.
 *
 * \param [in] reg is the register to access
 * \param [in] field is the register field to write
 *
 * \return the value of the register field
 *
 */
#define HW_TIMER0_REG_GETF(reg, field) \
        ((GP_TIMERS->TIMER0_##reg##_REG & (GP_TIMERS_TIMER0_##reg##_REG_##field##_Msk)) >> (GP_TIMERS_TIMER0_##reg##_REG_##field##_Pos))

/**
 * \brief Set the value of a field of an TIMER0 register.
 *
 * \param [in] reg is the register to access
 * \param [in] field is the register field to write
 * \param [in] new_val is the value to write
 *
 */
#define HW_TIMER0_REG_SETF(reg, field, new_val) \
        GP_TIMERS->TIMER0_##reg##_REG = ((GP_TIMERS->TIMER0_##reg##_REG & ~(GP_TIMERS_TIMER0_##reg##_REG_##field##_Msk)) | \
        ((GP_TIMERS_TIMER0_##reg##_REG_##field##_Msk) & ((new_val) << (GP_TIMERS_TIMER0_##reg##_REG_##field##_Pos))))


/**
 * \brief Clock source for timer
 *
 */
typedef enum {
        HW_TIMER0_CLK_SRC_SLOW = 0,        /**< 32kHz (slow) clock */
        HW_TIMER0_CLK_SRC_FAST = 1         /**< 2/4/8/16MHz (fast) clock */
} HW_TIMER0_CLK_SRC;

/**
 * \brief Fast clock division factor
 *
 */
typedef enum {
        HW_TIMER0_FAST_CLK_DIV_1 = 0,      /**< divide by 1 */
        HW_TIMER0_FAST_CLK_DIV_2,          /**< divide by 2 */
        HW_TIMER0_FAST_CLK_DIV_4,          /**< divide by 4 */
        HW_TIMER0_FAST_CLK_DIV_8,          /**< divide by 8 */
} HW_TIMER0_FAST_CLK_DIV;

/**
 * \brief PWM mode
 *
 */
typedef enum {
        HW_TIMER0_MODE_PWM = 0,            /**< PWM signal is '1' during high state */
        HW_TIMER0_MODE_CLOCK = 1           /**< PWM signal is clock divided by 2 during high state */
} HW_TIMER0_MODE;

/**
 * \brief Timer interrupt callback
 *
 */
typedef void (*hw_timer0_interrupt_cb)(void);

/*
 * \brief Timer configuration
 *
 */
typedef struct {
        HW_TIMER0_CLK_SRC       clk_src;        /**< clock source */
        HW_TIMER0_FAST_CLK_DIV  fast_clk_div;   /**< clock division factor (only applicable when fast clock is selected as source) */
        bool                    on_clock_div;   /**< enable ON-counter clock divider (clock for ON-counter is divided by 10) */
        uint16_t                on_reload;      /**< reload value for ON-counter */
        uint16_t                t0_reload_m;    /**< reload value for T0-counter M-register */
        uint16_t                t0_reload_n;    /**< reload value for T0-counter N-register */
} timer0_config;

/**
 * \brief Initialize the timer
 *
 * Turn on clock for timer and configure timer. After initialization both timer and its interrupt
 * are disabled. \p cfg can be NULL - no configuration is performed in such case.
 *
 * \param [in] cfg configuration
 *
 */
void hw_timer0_init(const timer0_config *cfg);

/**
 * \brief Timer configuration
 *
 * Shortcut to call appropriate configuration function. If \p cfg is NULL, this function does
 * nothing.
 *
 * \param [in] cfg configuration
 *
 */
void hw_timer0_configure(const timer0_config *cfg);

/**
 * \brief Register an interrupt handler
 *
 * \param [in] handler Function pointer to call when timer interrupt occurs
 *
 */
void hw_timer0_register_int(hw_timer0_interrupt_cb handler);

/**
 * \brief Unregister an interrupt handler
 *
 */
void hw_timer0_unregister_int(void);

/**
 * \brief Enable the timer
 *
 */
static inline void hw_timer0_enable(void)
{
        HW_TIMER0_REG_SETF(CTRL, TIM0_CTRL, 1);
}

/**
 * \brief Disable the timer
 *
 */
static inline void hw_timer0_disable(void)
{
        HW_TIMER0_REG_SETF(CTRL, TIM0_CTRL, 0);
}

/**
 * \brief Set clock source for timer
 *
 * \param [in] clk_src clock source
 *
 * \sa TIMER0_CLK_SRC
 *
 */
static inline void hw_timer0_set_clock_source(HW_TIMER0_CLK_SRC clk_src)
{
        HW_TIMER0_REG_SETF(CTRL, TIM0_CLK_SEL, clk_src);
}

/**
 * \brief Get current clock source for timer
 *
 * \return clock source
 *
 */
static inline HW_TIMER0_CLK_SRC hw_timer0_get_clock_source(void)
{
        return HW_TIMER0_REG_GETF(CTRL, TIM0_CLK_SEL);
}

/**
 * \brief Set fast clock division factor
 *
 * \param [in] div division factor
 *
 * \sa TIMER0_FAST_CLK_DIV
 *
 */
static inline void hw_timer0_set_fast_clock_div(HW_TIMER0_FAST_CLK_DIV div)
{
        REG_SETF(CRG_TOP, CLK_TMR_REG, TMR0_DIV, div);
}

/**
 * \brief Get current fast clock division factor
 *
 * \return division factor
 *
 */
static inline HW_TIMER0_FAST_CLK_DIV  hw_timer0_get_fast_clock_div(void)
{
        return REG_GETF(CRG_TOP, CLK_TMR_REG, TMR0_DIV);
}

/**
 * \brief Set state of clock divider for ON-counter
 *
 * Once enabled, ON-counter clock will be divided by 10.
 *
 * \param [in] enabled clock divider status
 *
 */
static inline void hw_timer0_set_on_clock_div(bool enabled)
{
        HW_TIMER0_REG_SETF(CTRL, TIM0_CLK_DIV, !enabled);
}

/**
 * \brief Get current state of clock divider for ON-counter
 *
 * \return clock divider status
 *
 */
static inline bool hw_timer0_get_on_clock_div(void)
{
        return !HW_TIMER0_REG_GETF(CTRL, TIM0_CLK_DIV);
}

/**
 * \brief Set PWM mode for timer
 *
 * \param [in] mode PWM mode
 *
 * \sa TIMER0_MODE
 *
 */
static inline void hw_timer0_set_pwm_mode(HW_TIMER0_MODE mode)
{
        HW_TIMER0_REG_SETF(CTRL, PWM_MODE, mode);
}

/**
 * \brief Get current PWM mode for timer
 *
 * \return PWM mode
 *
 */
static inline HW_TIMER0_MODE hw_timer0_get_pwm_mode(void)
{
        return HW_TIMER0_REG_GETF(CTRL, PWM_MODE);
}

/**
 * \brief Set reload value for T0-counter
 *
 * T0 counter value is decremented on each clock cycle. At the beginning it's loaded from M-register
 * and then, once reached zero, loaded from N-register (and then M and N again).
 *
 * PWM0 is high when counting down M-register and low when counting down N-register.
 * For PWM1 is the opposite.
 *
 * \param [in] m_value M-register reload value
 * \param [in] n_value N-register reload value
 *
 */
static inline void hw_timer0_set_t0_reload(uint16_t m_value, uint16_t n_value)
{
        GP_TIMERS->TIMER0_RELOAD_M_REG = m_value;
        GP_TIMERS->TIMER0_RELOAD_N_REG = n_value;
}

/**
 * \brief Set reload value for ON-counter
 *
 * ON counter value is decremented on each clock cycle. Once ON reaches zero it will remain zero
 * until T0 counter completes decrementing N-register value. When this happens, interrupt is
 * generated.
 *
 * \param [in] value reload value
 *
 * \sa hw_timer0_set_on_clock_div
 *
 */
static inline void hw_timer0_set_on_reload(uint16_t value)
{
        GP_TIMERS->TIMER0_ON_REG = value;
}

/**
 * \brief Get T0-counter value
 *
 * \return counter value
 *
 */
static inline uint16_t hw_timer0_get_t0(void)
{
        return (GP_TIMERS->TIMER0_RELOAD_M_REG);
}

/**
 * \brief Get ON-counter value
 *
 * \return counter value
 *
 */
static inline uint16_t hw_timer0_get_on(void)
{
        return (GP_TIMERS->TIMER0_ON_REG);
}

/**
 * \brief Freeze timer
 *
 */
static inline void hw_timer0_freeze(void)
{
        GPREG->SET_FREEZE_REG = GPREG_SET_FREEZE_REG_FRZ_SWTIM0_Msk;
}

/**
 * \brief Unfreeze timer
 *
 */
static inline void hw_timer0_unfreeze(void)
{
        GPREG->RESET_FREEZE_REG = GPREG_SET_FREEZE_REG_FRZ_SWTIM0_Msk;
}

#endif /* dg_configUSE_HW_TIMER0 */

#endif /* HW_TIMER0_H_ */

/**
 * \}
 * \}
 * \}
 */
