/**
  *********************************************************************************
  *
  * @file    ald_tsense.c
  * @brief   TSENSE module driver.
  *
  * @version V1.0
  * @date    26 Jun 2019
  * @author  AE Team
  * @note
  *          Change Logs:
  *          Date            Author          Notes
  *          26 Jun 2019     AE Team         The first version
  *
  * Copyright (C) Shanghai Eastsoft Microelectronics Co. Ltd. All rights reserved.
  *
  * SPDX-License-Identifier: Apache-2.0
  *
  * Licensed under the Apache License, Version 2.0 (the License); you may
  * not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
  * www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an AS IS BASIS, WITHOUT
  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  **********************************************************************************
  */

#include "ald_conf.h"


/** @addtogroup ES32FXXX_ALD
  * @{
  */

/** @defgroup TSENSE TSENSE
  * @brief TSENSE module driver
  * @{
  */
#ifdef ALD_TSENSE


/** @defgroup TSENSE_Private_Variables TSENSE Private Variables
  * @{
  */
tsense_cbk __tsense_cbk;
/**
  * @}
  */

/** @defgroup TSENSE_Public_Functions TSENSE Public Functions
  * @{
  */

/** @addtogroup TSENSE_Public_Functions_Group1 Initialization functions
  * @brief Initialization functions
  *
  * @verbatim
  ==============================================================================
              ##### Initialization functions #####
  ==============================================================================
    [..]  This section provides functions allowing to initialize the TSENSE:
      (+) This parameters can be configured:
        (++) Update Cycle
        (++) Output Mode
        (++) Perscaler
      (+) Select TSENSE source clock(default LOSC)

    @endverbatim
  * @{
  */

/**
  * @brief  Initializes the TSENSE according to the specified
  * @retval None
  */
void ald_tsense_init(void)
{
	uint16_t tempt, temptinv;
	uint32_t tscic, tscicinv;

	TSENSE_UNLOCK();
	TSENSE->CR = 0;

	SET_BIT(TSENSE->CR, TSENSE_CR_CTN_MSK);
	SET_BIT(TSENSE->CR, TSENSE_CR_REQEN_MSK);
	MODIFY_REG(TSENSE->CR, TSENSE_CR_TSU_MSK, 0x4 << TSENSE_CR_TSU_POSS);
	MODIFY_REG(TSENSE->CR, TSENSE_CR_TOM_MSK, 0x3 << TSENSE_CR_TOM_POSS);
	MODIFY_REG(TSENSE->PSR, TSENSE_PSR_PRS_MSK, 0x1 << TSENSE_PSR_PRS_POSS);

	TSENSE->HTGR = 0x8564F;
	TSENSE->LTGR = 0x87037;
	tempt        = *(volatile uint16_t *)0x80348;
	temptinv     = *(volatile uint16_t *)0x8034A;
	tscic        = *(volatile uint32_t *)0x80350;
	tscicinv     = *(volatile uint32_t *)0x80358;

	if ((tempt == (uint16_t)(~temptinv)) && (tscic == (~tscicinv))) {
		TSENSE->TBDR    = tempt;
		TSENSE->TCALBDR = (tscic & 0x1FFFFFF) >> 6;
	}
	else {
		TSENSE->TBDR    = 0x1E00;
		TSENSE->TCALBDR = 0x1F849;
	}

	TSENSE_LOCK();
	return;
}

/**
  * @brief  Configure the TSENSE source.
  * @param  sel: TSENSE source type.
  * @retval None
  */
void ald_tsense_source_select(tsense_source_sel_t sel)
{
	assert_param(IS_TSENSE_SOURCE_SEL(sel));

	BKPC_UNLOCK();
	MODIFY_REG(BKPC->PCCR, BKPC_PCCR_TSENSECS_MSK, sel << BKPC_PCCR_TSENSECS_POSS);

	if (sel == TSENSE_SOURCE_LOSC) {
		SET_BIT(BKPC->CR, BKPC_CR_LOSCEN_MSK);
	}
	else if (sel == TSENSE_SOURCE_LRC) {
		SET_BIT(BKPC->CR, BKPC_CR_LRCEN_MSK);
	}
	else {
		; /* do nothing */
	}

	BKPC_LOCK();
	return;
}
/**
  * @}
  */

/** @addtogroup TSENSE_Public_Functions_Group2 Peripheral Control functions
  * @brief Peripheral Control functions
  *
  * @verbatim
  ==============================================================================
              ##### Peripheral Control functions #####
  ==============================================================================
  [..]  This section provides functions allowing to:
    (+) ald_tsense_get_value() API can get the current temperature.
    (+) ald_tsense_get_value_by_it() API can get the current temperature by interrupt.
    (+) ald_tsense_irq_handler() API can handle the interrupt request.

    @endverbatim
  * @{
  */

/**
  * @brief  Get the current temperature
  * @param  tsense: The value of current temperature.
  * @retval ALD status:
  *         @arg @ref OK    The value is valid
  *         @arg @ref ERROR The value is invalid
  */
ald_status_t ald_tsense_get_value(uint16_t *tsense)
{
	uint32_t tmp = 0;

	TSENSE_UNLOCK();
	SET_BIT(TSENSE->IFCR, TSENSE_IFCR_TSENSE_MSK);
	SET_BIT(TSENSE->CR, TSENSE_CR_EN_MSK);
	TSENSE_LOCK();

	while ((!(READ_BIT(TSENSE->IF, TSENSE_IF_TSENSE_MSK))) && (tmp++ < 1000000));

	if (tmp >= 1000000) {
		TSENSE_UNLOCK();
		CLEAR_BIT(TSENSE->CR, TSENSE_CR_EN_MSK);
		TSENSE_LOCK();
		return TIMEOUT;
	}

	TSENSE_UNLOCK();
	SET_BIT(TSENSE->IFCR, TSENSE_IFCR_TSENSE_MSK);
	TSENSE_LOCK();

	if (READ_BIT(TSENSE->DR, TSENSE_DR_ERR_MSK)) {
		TSENSE_UNLOCK();
		CLEAR_BIT(TSENSE->CR, TSENSE_CR_EN_MSK);
		TSENSE_LOCK();
		return ERROR;
	}

	*tsense = READ_BITS(TSENSE->DR, TSENSE_DR_DATA_MSK, TSENSE_DR_DATA_POSS);

	TSENSE_UNLOCK();
	CLEAR_BIT(TSENSE->CR, TSENSE_CR_EN_MSK);
	TSENSE_LOCK();

	return OK;
}

/**
  * @brief  Get the current temperature by interrupt
  * @param  cbk: The callback function
  * @retval None
  */
void ald_tsense_get_value_by_it(tsense_cbk cbk)
{
	__tsense_cbk = cbk;

	TSENSE_UNLOCK();
	SET_BIT(TSENSE->IFCR, TSENSE_IFCR_TSENSE_MSK);
	SET_BIT(TSENSE->IE, TSENSE_IE_TSENSE_MSK);
	SET_BIT(TSENSE->CR, TSENSE_CR_EN_MSK);
	TSENSE_LOCK();

	return;
}

/**
  * @brief  This function handles TSENSE interrupt request.
  * @retval None
  */
void ald_tsense_irq_handler(void)
{
	TSENSE_UNLOCK();
	SET_BIT(TSENSE->IFCR, TSENSE_IFCR_TSENSE_MSK);
	TSENSE_LOCK();

	if (__tsense_cbk == NULL) {
		TSENSE_UNLOCK();
		CLEAR_BIT(TSENSE->CR, TSENSE_CR_EN_MSK);
		TSENSE_LOCK();
		return;
	}

	if (READ_BIT(TSENSE->DR, TSENSE_DR_ERR_MSK)) {
		TSENSE_UNLOCK();
		CLEAR_BIT(TSENSE->CR, TSENSE_CR_EN_MSK);
		TSENSE_LOCK();
		__tsense_cbk(0, ERROR);

		return;
	}

	__tsense_cbk(READ_BITS(TSENSE->DR, TSENSE_DR_DATA_MSK, TSENSE_DR_DATA_POSS), OK);

	TSENSE_UNLOCK();
	SET_BIT(TSENSE->IFCR, TSENSE_IFCR_TSENSE_MSK);
	CLEAR_BIT(TSENSE->CR, TSENSE_CR_EN_MSK);
	TSENSE_LOCK();

	return;
}
/**
  * @}
  */
/**
  * @}
  */
#endif /* ALD_TSENSE */
/**
  * @}
  */

/**
  * @}
  */
