-
Notifications
You must be signed in to change notification settings - Fork 6.5k
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 | ||
|
@@ -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 GitHub Actions / Run compliance checks on patch series (PR)You may want to run clang-format on this change
|
||
(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]); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 ? There was a problem hiding this comment. Choose a reason for hiding this commentThe 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:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
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 | ||
|
@@ -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 */ | ||
|
||
|
@@ -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), | ||
|
@@ -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) | ||
|
@@ -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 GitHub Actions / Run compliance checks on patch series (PR)You may want to run clang-format on this change
|
||
|
||
#if defined(CONFIG_ADC_STM32_DMA) | ||
|
||
#define ADC_DMA_CHANNEL_INIT(index, src_dev, dest_dev) \ | ||
|
@@ -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); \ | ||
\ | ||
|
@@ -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 GitHub Actions / Run compliance checks on patch series (PR)You may want to run clang-format on this change
|
||
DT_INST_FOREACH_STATUS_OKAY(ADC_STM32_INIT) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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)