drivers: adc: stm32: Implement boost settings for STM32H7.

After boost is implemented and enabled it allows for higher
sampling frequencies.

Signed-off-by: Bas van Loon <bas@arch-embedded.com>
This commit is contained in:
Bas van Loon 2024-04-10 17:27:16 +02:00 committed by Alberto Escolar
parent b29f9f21bc
commit 1adb779ba5
1 changed files with 129 additions and 10 deletions

View File

@ -49,6 +49,7 @@ LOG_MODULE_REGISTER(adc_stm32);
#if defined(CONFIG_SOC_SERIES_STM32H7X) || defined(CONFIG_SOC_SERIES_STM32H7RSX)
#include <zephyr/dt-bindings/memory-attr/memory-attr-arm.h>
#include <stm32_ll_system.h>
#endif
#ifdef CONFIG_NOCACHE_MEMORY
@ -1353,6 +1354,125 @@ static int adc_stm32_channel_setup(const struct device *dev,
return 0;
}
#if defined(CONFIG_SOC_SERIES_STM32C0X) || \
defined(CONFIG_SOC_SERIES_STM32G0X) || \
defined(CONFIG_SOC_SERIES_STM32L0X) || \
defined(CONFIG_SOC_SERIES_STM32U0X) || \
(defined(CONFIG_SOC_SERIES_STM32WBX) && defined(ADC_SUPPORT_2_5_MSPS)) || \
defined(CONFIG_SOC_SERIES_STM32WLX)
#define ADC_STM32_HAS_INDIVIDUAL_CLOCKS
#endif
#if defined(CONFIG_SOC_SERIES_STM32H7X) || defined(ADC_STM32_HAS_INDIVIDUAL_CLOCKS)
static bool adc_stm32_is_clk_sync(const struct adc_stm32_cfg *config)
{
if (config->clk_prescaler == LL_ADC_CLOCK_SYNC_PCLK_DIV1 ||
config->clk_prescaler == LL_ADC_CLOCK_SYNC_PCLK_DIV2 ||
config->clk_prescaler == LL_ADC_CLOCK_SYNC_PCLK_DIV4) {
return true;
}
return false;
}
#endif
#if defined(CONFIG_SOC_SERIES_STM32H7X)
static inline int adc_stm32_get_input_freq_prescaler(void)
{
int presc = 2;
#ifdef ADC_VER_V5_X
/* For revision Y we have no prescaler of 2 */
if (LL_DBGMCU_GetRevisionID() <= 0x1003) {
presc = 1;
}
#endif
return presc;
}
static int adc_stm32_get_clock_prescaler(const struct adc_stm32_cfg *config)
{
switch (config->clk_prescaler) {
case LL_ADC_CLOCK_SYNC_PCLK_DIV1:
case LL_ADC_CLOCK_ASYNC_DIV1:
return 1;
case LL_ADC_CLOCK_SYNC_PCLK_DIV2:
case LL_ADC_CLOCK_ASYNC_DIV2:
return 2;
case LL_ADC_CLOCK_SYNC_PCLK_DIV4:
case LL_ADC_CLOCK_ASYNC_DIV4:
return 4;
case LL_ADC_CLOCK_ASYNC_DIV6:
return 6;
case LL_ADC_CLOCK_ASYNC_DIV8:
return 8;
case LL_ADC_CLOCK_ASYNC_DIV10:
return 10;
case LL_ADC_CLOCK_ASYNC_DIV12:
return 12;
case LL_ADC_CLOCK_ASYNC_DIV16:
return 16;
case LL_ADC_CLOCK_ASYNC_DIV32:
return 32;
case LL_ADC_CLOCK_ASYNC_DIV64:
return 64;
case LL_ADC_CLOCK_ASYNC_DIV128:
return 128;
case LL_ADC_CLOCK_ASYNC_DIV256:
return 256;
default:
return -EINVAL;
}
}
static int adc_stm32h7_setup_boost(const struct adc_stm32_cfg *config, ADC_TypeDef *adc,
const struct device *clk)
{
clock_control_subsys_t clk_src;
uint32_t input_freq;
uint32_t boost;
int presc;
/* Get the input frequency */
clk_src = (clock_control_subsys_t)(adc_stm32_is_clk_sync(config) ? &config->pclken[0]
: &config->pclken[1]);
if (clock_control_get_rate(clk, clk_src, &input_freq) != 0) {
LOG_ERR("Failed to get ADC clock frequency");
return -EIO;
}
/* Adjust the pre-scaler value so that we can divide down the clock */
presc = adc_stm32_get_clock_prescaler(config);
if (presc < 0) {
LOG_ERR("Invalid clock prescaler value");
return presc;
}
input_freq /= presc * adc_stm32_get_input_freq_prescaler();
if (input_freq <= KHZ(6250)) {
boost = LL_ADC_BOOST_MODE_6MHZ25;
} else if (input_freq <= KHZ(12500)) {
boost = LL_ADC_BOOST_MODE_12MHZ5;
} else if (input_freq <= MHZ(20)) {
boost = LL_ADC_BOOST_MODE_20MHZ;
} else if (input_freq <= MHZ(25)) {
boost = LL_ADC_BOOST_MODE_25MHZ;
} else if (input_freq <= MHZ(50)) {
boost = LL_ADC_BOOST_MODE_50MHZ;
} else {
LOG_WRN("ADC clock frequency too high %u", input_freq);
return -ERANGE;
}
LL_ADC_SetBoostMode(adc, boost);
return 0;
}
#endif
/* This symbol takes the value 1 if one of the device instances */
/* is configured in dts with a domain clock */
#if STM32_DT_INST_DEV_DOMAIN_CLOCK_SUPPORT
@ -1366,6 +1486,7 @@ static int adc_stm32_set_clock(const struct device *dev)
const struct adc_stm32_cfg *config = dev->config;
const struct device *const clk = DEVICE_DT_GET(STM32_CLOCK_CONTROL_NODE);
ADC_TypeDef *adc = (ADC_TypeDef *)config->base;
int ret = 0;
ARG_UNUSED(adc); /* Necessary to avoid warnings on some series */
@ -1385,15 +1506,8 @@ static int adc_stm32_set_clock(const struct device *dev)
#if defined(CONFIG_SOC_SERIES_STM32F0X)
LL_ADC_SetClock(adc, config->clk_prescaler);
#elif defined(CONFIG_SOC_SERIES_STM32C0X) || \
defined(CONFIG_SOC_SERIES_STM32G0X) || \
defined(CONFIG_SOC_SERIES_STM32L0X) || \
defined(CONFIG_SOC_SERIES_STM32U0X) || \
(defined(CONFIG_SOC_SERIES_STM32WBX) && defined(ADC_SUPPORT_2_5_MSPS)) || \
defined(CONFIG_SOC_SERIES_STM32WLX)
if ((config->clk_prescaler == LL_ADC_CLOCK_SYNC_PCLK_DIV1) ||
(config->clk_prescaler == LL_ADC_CLOCK_SYNC_PCLK_DIV2) ||
(config->clk_prescaler == LL_ADC_CLOCK_SYNC_PCLK_DIV4)) {
#elif defined(ADC_STM32_HAS_INDIVIDUAL_CLOCKS)
if (adc_stm32_is_clk_sync(config)) {
LL_ADC_SetClock(adc, config->clk_prescaler);
} else {
LL_ADC_SetCommonClock(__LL_ADC_COMMON_INSTANCE(adc),
@ -1403,9 +1517,14 @@ static int adc_stm32_set_clock(const struct device *dev)
#elif !DT_HAS_COMPAT_STATUS_OKAY(st_stm32f1_adc)
LL_ADC_SetCommonClock(__LL_ADC_COMMON_INSTANCE(adc),
config->clk_prescaler);
#ifdef CONFIG_SOC_SERIES_STM32H7X
/* Set boost according to input frequency */
ret = adc_stm32h7_setup_boost(config, adc, clk);
#endif
#endif
return 0;
return ret;
}
static int adc_stm32_init(const struct device *dev)