Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
143 changes: 134 additions & 9 deletions drivers/adc/adc_stm32.c
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@

#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 @@
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;
}
Comment on lines +1378 to +1381
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/* For revision Y we have no prescaler of 2 */
if (LL_DBGMCU_GetRevisionID() <= 0x1003) {
presc = 1;
}
#ifdef ADC_VER_V5_X
/* For revision Y we have no prescaler of 2 */
if (LL_DBGMCU_GetRevisionID() <= 0x1003) {
presc = 1;
}
#endif

This specificity is only present on STM32H742, H743, H750 and H753. So add an #ifdef to avoid conflict with other subseries (like H72x or H73x whose revisions are 0x1000 or 0x1001)

#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]);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another thing I just noticed, config->pclken[1] does not necessarily exist. It is currently possible to be in ASYNC but not defining a secondary clock source. In that case, PLL2_P will be used (default one). But I agree it is not a good practice.

Maybe you could add a check (not limited to H7) to generate a compile-time error if ASYNC is defined without an explicit secondary clock.

But you still have to update this line for cases in SYNC that may not have the secondary clock.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gautierg-st I did not understand the last line.. Why does it matter if pclken[1] is not set when running in SYNC mode ? AFAICS we only need to check pclken[1] when ASYNC mode is used ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well if you add global check, then yeah, your current code should be ok.

But thinking about it, I believe a simple check like the following could suffice:

clk_src = &config->pclken[0];
if (IS_ENABLED(STM32_ADC_DOMAIN_CLOCK_SUPPORT) && (config->pclk_len > 1) && !adc_stm32_is_clk_sync(config)) {
	clk_src = &config->pclken[1];
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well if you add global check, then yeah, your current code should be ok.

But thinking about it, I believe a simple check like the following could suffice:

clk_src = &config->pclken[0];
if (IS_ENABLED(STM32_ADC_DOMAIN_CLOCK_SUPPORT) && (config->pclk_len > 1) && !adc_stm32_is_clk_sync(config)) {
	clk_src = &config->pclken[1];
}

I have chosen for a compile error check as that code is running on H7 only anyway. Please check the current version ;)


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 @@
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 @@

#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 @@
#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,11 @@
_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) || (DT_INST_NUM_CLOCKS(x) > 1), \
"ASYNC clock mode defined without ASYNC clock defined in device tree");

Check notice on line 1753 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:1753 -#define ADC_STM32_CHECK_DT_CLOCK(x) \ - BUILD_ASSERT(IS_EQ(ADC_STM32_CLOCK(x), SYNC) || (DT_INST_NUM_CLOCKS(x) > 1), \ +#define ADC_STM32_CHECK_DT_CLOCK(x) \ + BUILD_ASSERT(IS_EQ(ADC_STM32_CLOCK(x), SYNC) || (DT_INST_NUM_CLOCKS(x) > 1), \

#if defined(CONFIG_ADC_STM32_DMA)

#define ADC_DMA_CHANNEL_INIT(index, src_dev, dest_dev) \
Expand Down Expand Up @@ -1769,6 +1893,7 @@
(/* 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 Expand Up @@ -1804,5 +1929,5 @@
&adc_stm32_data_##index, &adc_stm32_cfg_##index, \
POST_KERNEL, CONFIG_ADC_INIT_PRIORITY, \
&api_stm32_driver_api);

Check notice on line 1932 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:1932 -#define ADC_STM32_INIT(index) \ -ADC_STM32_CHECK_DT_CLOCK(index) \ - \ -PINCTRL_DT_INST_DEFINE(index); \ - \ -static const struct stm32_pclken pclken_##index[] = \ - STM32_DT_INST_CLOCKS(index); \ - \ -static const struct adc_stm32_cfg adc_stm32_cfg_##index = { \ - .base = (ADC_TypeDef *)DT_INST_REG_ADDR(index), \ - ADC_STM32_IRQ_FUNC(index) \ - .pclken = pclken_##index, \ - .pclk_len = DT_INST_NUM_CLOCKS(index), \ - .clk_prescaler = ADC_STM32_DT_PRESC(index), \ - .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(index), \ - .sequencer_type = DT_INST_PROP(index, st_adc_sequencer), \ - .sampling_time_table = DT_INST_PROP(index, sampling_times), \ - .num_sampling_time_common_channels = \ - DT_INST_PROP_OR(index, num_sampling_time_common_channels, 0),\ - .res_table_size = DT_INST_PROP_LEN(index, resolutions), \ - .res_table = DT_INST_PROP(index, resolutions), \ -}; \ - \ -static struct adc_stm32_data adc_stm32_data_##index = { \ - ADC_CONTEXT_INIT_TIMER(adc_stm32_data_##index, ctx), \ - ADC_CONTEXT_INIT_LOCK(adc_stm32_data_##index, ctx), \ - ADC_CONTEXT_INIT_SYNC(adc_stm32_data_##index, ctx), \ - ADC_DMA_CHANNEL(index, PERIPHERAL, MEMORY) \ -}; \ - \ -PM_DEVICE_DT_INST_DEFINE(index, adc_stm32_pm_action); \ - \ -DEVICE_DT_INST_DEFINE(index, \ - &adc_stm32_init, PM_DEVICE_DT_INST_GET(index), \ - &adc_stm32_data_##index, &adc_stm32_cfg_##index, \ - POST_KERNEL, CONFIG_ADC_INIT_PRIORITY, \ - &api_stm32_driver_api); +#define ADC_STM32_INIT(index) \ + ADC_STM32_CHECK_DT_CLOCK(index) \ + \ + PINCTRL_DT_INST_DEFINE(index); \ + \ + static const struct stm32_pclken pclken_##index[] = STM32_DT_INST_CLOCKS(index); \ + \ + static const struct adc_stm32_cfg adc_stm32_cfg_##index = { \ + .base = (ADC_TypeDef *)DT_INST_REG_ADDR(index), \ + ADC_STM32_IRQ_FUNC(index).pclken = pclken_##index, \ + .pclk_len = DT_INST_NUM_CLOCKS(index), \ + .clk_prescaler = ADC_STM32_DT_PRESC(index), \ + .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(index), \ + .sequencer_type = DT_INST_PROP(index, st_adc_sequencer), \ + .sampling_time_table = DT_INST_PROP(index, sampling_times), \ + .num_sampling_time_common_channels = \ + DT_INST_PROP_OR(index, num_sampling_time_common_channels, 0), \ + .res_table_size = DT_INST_PROP_LEN(index, resolutions), \ + .res_table = DT_INST_PROP(index, resolutions), \ + }; \ + \ + static struct adc_stm32_data adc_stm32_data_##index = { \ + ADC_CONTEXT_INIT_TIMER(adc_stm32_data_##index, ctx), \ + ADC_CONTEXT_INIT_LOCK(adc_stm32_data_##index, ctx), \ + ADC_CONTEXT_INIT_SYNC(adc_stm32_data_##index, ctx), \ + ADC_DMA_CHANNEL(index, PERIPHERAL, MEMORY)}; \ + \ +
DT_INST_FOREACH_STATUS_OKAY(ADC_STM32_INIT)
Loading