blob: 5b2de0bfb0cba1d90eada56cfe4b591e6cdc76fb [file] [log] [blame]
/**
\addtogroup BSP
\{
\addtogroup SYSTEM
\{
\addtogroup TCS_Handling
\{
*/
/**
****************************************************************************************
*
* @file sys_tcs.c
*
* @brief TCS HAndler
*
* 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.
*
*
****************************************************************************************
*/
#include <stdint.h>
#include "hw_cpm.h"
#include "sys_tcs.h"
#include "hw_gpadc.h"
#include "hw_gpio.h"
#define APPEND_TCS_LENGTH (0)
#define RADIO_AREA_SIZE (0x1000)
#define PERIPHERAL_AREA_SIZE (0xC4A)
#define IS_IN_AREA(addr, base, size) ((addr >= base) && (addr <= (base + size)))
#define ADC_SE_GAIN_ERROR(value) ((value) & 0x0000FFFF)
#define ADC_DIFF_GAIN_ERROR(value) (((value) & 0xFFFF0000) >> 16)
/*
* Global and / or retained variables
*/
uint32_t tcs_data[24 * 2 + APPEND_TCS_LENGTH] __RETAINED_UNINIT;
uint8_t tcs_length __RETAINED_UNINIT;
uint8_t area_offset[tcs_system + 1] __RETAINED;
uint8_t area_size_global[tcs_system + 1] __RETAINED;
uint16_t sys_tcs_xtal16m_settling_time __RETAINED;
const uint32_t *tcs_ptr __RETAINED;
bool sys_tcs_is_calibrated_chip __RETAINED_UNINIT; // Initial value: false
/*
* Local variables
*/
static const uint32_t uncalibrated_tcs_data[] =
{
/* BANDGAP_REG: (offset 0)
* [14]: BYPASS_COLD_BOOT_DISABLE = 0
* [13:10]: LDO_SLEEP_TRIM = 0x4
* [9:5]: BGR_ITRIM = 0x00
* [4:0]: BGR_TRIM = 0x13 (change until V12 is 1.2V)
*
* Especially for this register, merge the trimmed and preferred settings.
*/
(uint32_t) &CRG_TOP->BANDGAP_REG, 0x1013,
/* CLK_FREQ_TRIM_REG: (offset 2)
* [10:8]: COARSE_ADJ = 0x4
* [7:0]: FINE_ADJ = 0x60
*/
(uint32_t) &CRG_TOP->CLK_FREQ_TRIM_REG, 0x0460,
/* CLK_16M_REG: (offset 4)
* [15]: RC16M_STARTUP_DISABLE = 0
* [14]: XTAL16_HPASS_FLT_EN = 0
* [13]: XTAL16_SPIKE_FLT_BYPASS = 0
* [12:10]: XTAL16_AMP_TRIM = 0x5
* [9]: XTAL16_EXT_CLK_ENABLE = 0
* [8]: XTAL16_MAX_CURRENT = 0
* [7:5]: XTAL16_CUR_SET = 0x5
* [4:1]: RC16M_TRIM = 0x9
* [0]: RC16M_ENABLE = 0
*
* Apply the trimmed and a default preferred value. The correct one is applied
* later.
*/
(uint32_t) &CRG_TOP->CLK_16M_REG, 0x14B2,
/* CLK_32K_REG: (offset 6)
* [15:13]: 0
* [12]: XTAL32K_DISABLE_AMPREG0 = 0
* [11:8]: RC32K_TRIM = 0x07
* [7]: RC32K_ENABLE = 0x1 (reset value)
* [6:3]: XTAL32K_CUR = 0x3 (reset value)
* [2:1]: XTAL32K_RBIAS = 0x2 (reset value)
* [0]: XTAL32K_ENABLE = 0
*/
(uint32_t) &CRG_TOP->CLK_32K_REG, 0x79C,
/* CHARGER_CTRL2_REG: (offset 8)
* [15:13]: 0
* [12:8]: CURRENT_OFFSET_TRIM = 0x0C
* [7:4]: CHARGER_VFLOAT_ADJ = 0x5
* [3:0]: CURRENT_GAIN_TRIM = 0xA
*/
(uint32_t) &ANAMISC->CHARGER_CTRL2_REG, 0x0C5A,
};
#define UNCALIBRATED_DATA_NON_RETAINED_OFFSET (8)
#ifdef CONFIG_USE_BLE
#define UNCALIBRATED_DATA_BLE_SIZE (0)
#define UNCALIBRATED_DATA_BLE_OFFSET (0)
#endif
#ifdef CONFIG_USE_FTDF
#define UNCALIBRATED_DATA_FTDF_SIZE (0)
#define UNCALIBRATED_DATA_FTDF_OFFSET (0)
#endif
#define UNCALIBRATED_DATA_RADIO_SIZE (0)
#define UNCALIBRATED_DATA_RADIO_OFFSET (0)
#define UNCALIBRATED_DATA_CHARGER_SIZE (1 * 2)
#define UNCALIBRATED_DATA_CHARGER_OFFSET (8)
#define UNCALIBRATED_DATA_AUDIO_SIZE (0)
#define UNCALIBRATED_DATA_AUDIO_OFFSET (0)
#define UNCALIBRATED_DATA_SYSTEM_SIZE (0)
#define UNCALIBRATED_DATA_SYSTEM_OFFSET (0)
/*
* Forward declarations
*/
/*
* Function definitions
*/
/**
* \brief Apply a TCS <address, value> pair.
*
* \param [in] address the address of the register
* \param [in] value the value for this register
*
*/
static void apply_pair(uint32_t address, uint32_t value)
{
if (address & 0x2)
{
*(volatile uint16_t *)address = value;
}
else
{
*(volatile uint32_t *)address = value;
}
}
/**
* \brief Apply a TCS <address, value> entry of the TCS array.
*
* \param [in] address the address of the register
* \param [in] value the value for this register
*
*/
static void apply_entry(uint8_t index)
{
uint32_t address = tcs_ptr[index];
uint32_t value = tcs_ptr[index + 1];
if (address & 0x2)
{
*(volatile uint16_t *)address = value;
}
else
{
*(volatile uint32_t *)address = value;
}
}
/**
* \brief Store TCS <address, value> pair in the Global TCS array.
*
* \param [in] address the address of the register
* \param [in] value the value for this register
*
*/
static void store_in_array(uint32_t address, uint32_t value)
{
tcs_data[tcs_length] = address;
tcs_length++;
tcs_data[tcs_length] = value;
tcs_length++;
}
/**
* \brief Swaps the contents of two entries in the TCS array.
*
* \param [in] address the address of the register
* \param [in] value the value for this register
*
* \warning When this function is called, the RC16 must have been set as the system clock!
*
*/
static void swap_entries(uint32_t first_pos, uint32_t second_pos)
{
uint32_t address_stored;
uint32_t value_stored;
address_stored = tcs_data[second_pos];
value_stored = tcs_data[second_pos + 1];
tcs_data[second_pos] = tcs_data[first_pos];
tcs_data[second_pos + 1] = tcs_data[first_pos + 1];
tcs_data[first_pos] = address_stored;
tcs_data[first_pos + 1] = value_stored;
}
/**
* \brief Sort the registers of a specific Memory Map area of the chip to a continuous region in the
* TCS array.
*
* \param [in] area_base the base address of the Memory Map area
* \param [in] area_size the size of the Memory Map region in bytes
* \param [in] array_ptr the current position in the TCS array to start reading from (all previous
* positions have been sorted)
* \param [out] ptr the offset of the beginning of the sorted region in the TCS array
* \param [out] size the size of the sorted region in bytes
*
* \return The updated position in the TCS array to start reading next time
*/
static int sys_tcs_sort_area(uint32_t area_base, uint32_t area_size, uint32_t array_ptr, uint8_t *ptr, uint8_t *size)
{
uint32_t i = array_ptr;
uint32_t sort_start;
uint32_t sort_end;
if (array_ptr == tcs_length)
{
return array_ptr; // Nothing else is left in the array or tcs_length is zero
}
sort_start = array_ptr;
sort_end = array_ptr;
/* Make sure that the values in TCS don't overflow the allocated array */
ASSERT_WARNING(tcs_length <= sizeof(tcs_data) / sizeof(tcs_data[0]));
while (i < (tcs_length - 1))
{
if (IS_IN_AREA(tcs_data[i], area_base, area_size))
{
if (i == array_ptr)
{
if (sort_start == array_ptr)
{
sort_start = array_ptr;
sort_end = sort_start + 2;
}
}
else
{
// Copy it to the proper place.
if (i != sort_end)
{
swap_entries(sort_end, i);
}
sort_end += 2;
}
}
i += 2;
}
*ptr = sort_start;
*size = sort_end - sort_start;
return sort_end;
}
void sys_tcs_init(void)
{
tcs_length = 0;
sys_tcs_is_calibrated_chip = false;
hw_cpm_bod_enabled_in_tcs = 0;
}
bool sys_tcs_store_pair(uint32_t address, uint32_t value)
{
// If it stops here then tcs_length must become uint16_t.
ASSERT_WARNING((256 * sizeof(tcs_length)) >= (24 * 2 + APPEND_TCS_LENGTH));
// Check if the address is in Always-ON or of a retained register and apply it.
if (IS_IN_AREA(address, CRG_TOP_BASE, sizeof(CRG_TOP_Type))
|| IS_IN_AREA(address, TIMER1_BASE, sizeof(TIMER1_Type))
|| IS_IN_AREA(address, WAKEUP_BASE, sizeof(WAKEUP_Type))
|| IS_IN_AREA(address, DCDC_BASE, sizeof(DCDC_Type))
|| IS_IN_AREA(address, QSPIC_BASE, sizeof(QSPIC_Type))
|| IS_IN_AREA(address, CACHE_BASE, sizeof(CACHE_Type))
|| IS_IN_AREA(address, OTPC_BASE, sizeof(OTPC_Type)))
{
if (address == (uint32_t)&CRG_TOP->BANDGAP_REG)
{
sys_tcs_is_calibrated_chip = true;
}
else if (address == (uint32_t)&CRG_TOP->CLK_16M_REG)
{
value |= CRG_TOP_CLK_16M_REG_RC16M_ENABLE_Msk; // Make sure that RC16 is enabled!
}
else if (address == (uint32_t)&CRG_TOP->XTALRDY_CTRL_REG)
{
sys_tcs_xtal16m_settling_time = value;
}
else if (address == (uint32_t)&CRG_TOP->BOD_CTRL2_REG)
{
hw_cpm_bod_enabled_in_tcs = value;
/*
* Read the ADC Gain Error. Given that the Chip Configuration Section
* (CCS) of OTP is out of space, the values are stored in the TCS section
* using as address the read-only register SYS_STAT_REG.
* The 16 LSB of the 32bit data-value is the single ended gain error.
* The 16 MSB of the 32bit data-value is the differential gain error.
* Both gain error values should be interpreted as signed 16bit integers.
*/
}
else if (dg_configUSE_ADC_GAIN_ERROR_CORRECTION == 1)
{
if (address == (uint32_t)&CRG_TOP->SYS_STAT_REG)
{
hw_gpadc_store_se_gain_error(ADC_SE_GAIN_ERROR(value));
hw_gpadc_store_diff_gain_error(ADC_DIFF_GAIN_ERROR(value));
}
}
apply_pair(address, value);
}
else
{
store_in_array(address, value);
}
return sys_tcs_is_calibrated_chip;
}
void sys_tcs_sort_array(void)
{
int entry_ptr = 0;
int i;
/*
* Apply a default value to CLK_FREQ_TRIM_REG, if not set in TCS.
* [10:8]: COARSE_ADJ = 0x4
* [7:0]: FINE_ADJ = 0x60
*/
if (CRG_TOP->CLK_FREQ_TRIM_REG == 0)
{
CRG_TOP->CLK_FREQ_TRIM_REG = 0x0460;
}
if (sys_tcs_is_calibrated_chip)
{
#ifdef CONFIG_USE_BLE
entry_ptr = sys_tcs_sort_area(BLE_BASE, sizeof(BLE_Type), entry_ptr,
&area_offset[tcs_ble], &area_size[tcs_ble]);
#endif
#ifdef CONFIG_USE_FTDF
entry_ptr = sys_tcs_sort_area(FTDF_BASE, sizeof(FTDF_Type), entry_ptr,
&area_offset[tcs_ftdf], &area_size_global[tcs_ftdf]);
#endif
entry_ptr = sys_tcs_sort_area(RFCU_BASE, RADIO_AREA_SIZE, entry_ptr,
&area_offset[tcs_radio], &area_size_global[tcs_radio]);
entry_ptr = sys_tcs_sort_area((uint32_t)&ANAMISC->CHARGER_CTRL2_REG, 1, entry_ptr,
&area_offset[tcs_charger], &area_size_global[tcs_charger]);
entry_ptr = sys_tcs_sort_area(APU_BASE, sizeof(APU_Type), entry_ptr,
&area_offset[tcs_audio], &area_size_global[tcs_audio]);
// The rest should be System. Assert if not!
for (i = entry_ptr; i < tcs_length; i += 2)
{
if (IS_IN_AREA(tcs_data[i], UART_BASE, PERIPHERAL_AREA_SIZE))
{
ASSERT_WARNING(0);
}
if (IS_IN_AREA(tcs_data[i], CRG_TOP_BASE, 0x6100))
{
// It is ok (most probably).
}
else
{
ASSERT_WARNING(0);
}
}
area_offset[tcs_system] = entry_ptr;
area_size_global[tcs_system] = tcs_length - entry_ptr;
tcs_ptr = tcs_data;
}
else
{
tcs_ptr = uncalibrated_tcs_data;
for (i = 0; i < UNCALIBRATED_DATA_NON_RETAINED_OFFSET; i += 2)
{
apply_pair(tcs_ptr[i], tcs_ptr[i + 1]);
}
#ifdef CONFIG_USE_BLE
if (UNCALIBRATED_DATA_BLE_SIZE > 0)
{
area_offset[tcs_ble] = UNCALIBRATED_DATA_BLE_OFFSET;
area_size[tcs_ble] = UNCALIBRATED_DATA_BLE_SIZE;
}
#endif
#ifdef CONFIG_USE_FTDF
if (UNCALIBRATED_DATA_FTDF_SIZE > 0)
{
area_offset[tcs_ftdf] = UNCALIBRATED_DATA_FTDF_OFFSET;
area_size_global[tcs_ftdf] = UNCALIBRATED_DATA_FTDF_SIZE;
}
#endif
if (UNCALIBRATED_DATA_RADIO_SIZE > 0)
{
area_offset[tcs_radio] = UNCALIBRATED_DATA_RADIO_OFFSET;
area_size_global[tcs_radio] = UNCALIBRATED_DATA_RADIO_SIZE;
}
if (UNCALIBRATED_DATA_CHARGER_SIZE > 0)
{
area_offset[tcs_charger] = UNCALIBRATED_DATA_CHARGER_OFFSET;
area_size_global[tcs_charger] = UNCALIBRATED_DATA_CHARGER_SIZE;
}
if (UNCALIBRATED_DATA_AUDIO_SIZE > 0)
{
area_offset[tcs_audio] = UNCALIBRATED_DATA_AUDIO_OFFSET;
area_size_global[tcs_audio] = UNCALIBRATED_DATA_AUDIO_SIZE;
}
if (UNCALIBRATED_DATA_SYSTEM_SIZE > 0)
{
area_offset[tcs_system] = UNCALIBRATED_DATA_SYSTEM_OFFSET;
area_size_global[tcs_system] = UNCALIBRATED_DATA_SYSTEM_SIZE;
}
}
}
void sys_tcs_apply(sys_tcs_area_t area)
{
int i;
for (i = area_offset[area]; i < (area_offset[area] + area_size_global[area]); i += 2)
{
apply_entry(i);
}
}
/**
\}
\}
\}
*/