Skip to content

Commit

Permalink
drivers: adc: stm32: Implement boost settings for STM32H7.
Browse files Browse the repository at this point in the history
After boost is implemented and enabled it allows for higher
sampling frequencies.

Signed-off-by: Bas van Loon <[email protected]>
  • Loading branch information
bas-archembedded committed Sep 12, 2024
1 parent 0cd3f56 commit f40e3ff
Showing 1 changed file with 136 additions and 9 deletions.
145 changes: 136 additions & 9 deletions drivers/adc/adc_stm32.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -1347,6 +1348,124 @@ 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) || \

Check notice on line 1353 in drivers/adc/adc_stm32.c

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

You may want to run clang-format on this change

drivers/adc/adc_stm32.c:1353 -#if defined(CONFIG_SOC_SERIES_STM32C0X) || \ - defined(CONFIG_SOC_SERIES_STM32G0X) || \ +#if defined(CONFIG_SOC_SERIES_STM32C0X) || defined(CONFIG_SOC_SERIES_STM32G0X) || \
(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
Expand All @@ -1360,6 +1479,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 */

Expand All @@ -1379,14 +1499,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_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),
Expand All @@ -1396,9 +1510,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)
Expand Down Expand Up @@ -1628,6 +1747,13 @@ static const struct adc_driver_api api_stm32_driver_api = {
_CONCAT(ADC_STM32_CLOCK_PREFIX(x), ADC_STM32_DIV(x))
#endif

/* Macro to check if the ADC instance clock setup is correct */
#define ADC_STM32_CHECK_DT_CLOCK(x) \
BUILD_ASSERT( \
IS_EQ(ADC_STM32_CLOCK(x), SYNC) || \
(IS_ENABLED(STM32_ADC_DOMAIN_CLOCK_SUPPORT) && DT_INST_NUM_CLOCKS(x) > 1), \
"ASYNC clock mode defined without ASYNC clock defined in device tree");

#if defined(CONFIG_ADC_STM32_DMA)

#define ADC_DMA_CHANNEL_INIT(index, src_dev, dest_dev) \
Expand Down Expand Up @@ -1769,6 +1895,7 @@ DT_INST_FOREACH_STATUS_OKAY(GENERATE_ISR)
(/* Required for other adc instances without dma */))

#define ADC_STM32_INIT(index) \
ADC_STM32_CHECK_DT_CLOCK(index) \
\
PINCTRL_DT_INST_DEFINE(index); \
\
Expand Down

0 comments on commit f40e3ff

Please sign in to comment.