Skip to content

Conversation

gautierg-st
Copy link
Contributor

The goal of this PR is to add the support of the ADC prescaler located in RCC for some series (F1, F3, N6, U3).
Though already supported in F1 and F3, it was through a specific property of the RCC. To avoid increasing the complexity on this end, the prescaler is configured the same way as for a kernel clock, in the ADC clocks property in device tree.
This allows to remove the RCC specificities of F1 and F3.

This led to changes in the STM32 clock defines due to the fact that the N6 prescaler register field is 8-bit long, so the macros needed to be reworked to make it fit.

Lastly, since we can now have up to three different clocks in the clocks properties, instead of relying on ordering, the clock-names property is mandatory for all ADC instances. Names have been added in existing dtsi, dts and overlay files, but it shall be redefined if a clock source or a prescaler is added.

@zephyrbot zephyrbot added area: Boards/SoCs area: ADC Analog-to-Digital Converter (ADC) area: Clock Control Release Notes To be mentioned in the release notes area: Devicetree Bindings area: Tests Issues related to a particular existing or missing test platform: STM32 ST Micro STM32 labels Oct 16, 2025
Reworks the way the STM32_DT_CLOCK_SELECT builds its elements. Instead of
defining a mask, we define a width which is the number of bits of the
register field. This allows to gain space for higher values for the fields.

This larger space is necessary to add the selection of the ADC prescaler
on STM32N6 because it is an 8-bit long field.

The allowed width is from 1 to 8 (and internally stored as 0-7 to fit on
3 bits).

STM32_DT_CLKSEL_MASK_GET keeps the same name, since we still need the mask,
and returns the bitmask from the width with the BIT_MASK macro. Other
STM32_DT_CLKSEL_MASK_* macros are renamed with WIDTH.

All call to STM32_DT_CLOCK_SELECT are updated to reflect the change and use
a width instead of a mask.

Signed-off-by: Guillaume Gautier <[email protected]>
…and u3

This commit adds the RCC configurations for ADC prescaler for STM32F1, F3,
N6 and U3.

Signed-off-by: Guillaume Gautier <[email protected]>
To easily differentiate between the different clocks that can be configured
in device tree, make their naming mandatory, and explicit what the expected
names are.

Add these names in all dtsi and dts files that need them.

Signed-off-by: Guillaume Gautier <[email protected]>
Some series like F1, F3, N6 and U3 use an ADC prescaler defined in the RCC.
Instead of adding specific properties in the RCC driver, use the secondary
clock system to configure the prescaler.

The ADC driver now configures the clocks depending on their presence and
their name. Three clocks can be defined:
- the register clock (mandatory for all series)
- the kernel clock (depends on series)
- the prescaler value (depends on series)

Signed-off-by: Guillaume Gautier <[email protected]>
Now that the ADC prescaler are set within the driver using the clock
system, the specific rcc compatibles for F1 and F3 are no longer useful.
Replace them with the standard one (from which they were derived).

Signed-off-by: Guillaume Gautier <[email protected]>
Now that the ADC prescaler are set within the driver using the clock
system, remove the specific setting of the prescaler from the clock driver.

Signed-off-by: Guillaume Gautier <[email protected]>
Update STM32 ADC binding description now that the STM32F3 ADC asynchronous
prescaler is set through the clock property.

Signed-off-by: Guillaume Gautier <[email protected]>
Update the 4.3 migration guide to include the change made on the STM32
ADC clocks.

Signed-off-by: Guillaume Gautier <[email protected]>
Copy link

Copy link
Contributor

@mathieuchopstm mathieuchopstm left a comment

Choose a reason for hiding this comment

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

Maybe the STM32_DT_CLOCK_SELECT update should be done in a separate PR. Not too blocking for me though.

}

if (config->has_pclken_pre) {
/* Set ADC RCC prescaler */
Copy link
Contributor

Choose a reason for hiding this comment

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

nit:

Suggested change
/* Set ADC RCC prescaler */
/* Configure ADC prescaler (at RCC level) */

Comment on lines +1981 to +1982
.pclken = {.bus = DT_INST_CLOCKS_CELL_BY_NAME(index, adcx, bus), \
.enr = DT_INST_CLOCKS_CELL_BY_NAME(index, adcx, bits)}, \
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: I would make a STM32-level macro for this. Basically all drivers are "wrong" today (div field left uninitialized) because they don't use the shared macro.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is a change for another PR. As you say, all drivers are impacted, so I'm not going to introduce such a change in this one. There are enough as it is.

(.pclken_ker = {.bus = DT_INST_CLOCKS_CELL_BY_NAME(index, adc_ker, bus), \
.enr = DT_INST_CLOCKS_CELL_BY_NAME(index, adc_ker, bits)}, \
.has_pclken_ker = true,), \
(.has_pclken_ker = false,)) \
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: could be IF_ENABLED() and leave has_pclken_ker not initialized => default to false.

But I don't mind the extra verbosity.

Copy link
Contributor

Choose a reason for hiding this comment

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

Nits on commit include: zephyr: dt-bindings: clock: stm32: use width instead of bitmask:

  1. commit title is not very intuitive. prefer the simpler rework STM32_DT_CLOCK_SELECT details?
  2. Since we're breaking STM32_DT_CLOCK_SELECT, I would suggest the new prototype to be instead STM32_DT_CLOCK_SELECT(val, high_bit, low_bit, reg)

[1] low_bit is merely a rename; high_bit would be the bitfield's MSB index - width would be computed internally as (high_bit - low_bit + 1). When copying RefMan, this would avoid the hurdle of computing bitfield width.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok with changing the commit title, but disagree with your prototype proposition. The rework was needed to gain space, make the macro more compact so that N6 prescaler could fit into it. Width is only 3-bit long, using another bit_pos would be 5-bit long. Reg would have to be reduced (not currently blocking since no reg are that long, but still, it could be needed one day).

Besides, I don't think it's a hurdle to "calculate" the width of the bitfield from RefMan (most are under 4-bit long, that's pretty easy to read).

Copy link
Contributor

Choose a reason for hiding this comment

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

Ok with changing the commit title, but disagree with your prototype proposition. The rework was needed to gain space, make the macro more compact so that N6 prescaler could fit into it. Width is only 3-bit long, using another bit_pos would be 5-bit long. Reg would have to be reduced (not currently blocking since no reg are that long, but still, it could be needed one day).

I am not suggesting to change the storage format but merely how the macro accepts parameters. When I said:

width would be computed internally as (high_bit - low_bit + 1)

I meant that it would be computed internally as part of the macro (storage format is unchanged and contains width)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh, ok, my bad, I misunderstood your proposition.
I don't have strong opinions on either choice, but I'll wait for other opinions before making any potential changes. @etienne-lms, @erwango ?

Copy link
Member

Choose a reason for hiding this comment

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

What would be the benefit of high_bit, low_bit ? Readability ?

Copy link
Contributor

Choose a reason for hiding this comment

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

I think having the lowest and highest bit position as macro argument make sense, but I'm quite used the GENMASK() like macros. No strong opinion here.

Copy link
Contributor

Choose a reason for hiding this comment

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

What would be the benefit of high_bit, low_bit ? Readability ?

Basically yes. Gives the information directly from reading RefMan:

image

==> ((val), 31, 30, CCIPR) instead of having to derive size (as @gautierg-st said, it is often 1/2 bits so not too much of a hurdle, but still more error-prone than copy-paste from RM, and we're starting to see more and more "large" fields)

I think having the lowest and highest bit position as macro argument make sense, but I'm quite used the GENMASK() like macros. No strong opinion here.

GENMASK() takes a high/low argument as well so I don't understand your point 🤔

/**
* @brief Create a contiguous bitmask starting at bit position @p l
* and ending at position @p h.
*/
#define GENMASK(h, l) (((~0UL) - (1UL << (l)) + 1) & (~0UL >> (BITS_PER_LONG - 1 - (h))))

Copy link
Contributor

Choose a reason for hiding this comment

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

GENMASK() takes a high/low argument as well so I don't understand your point 🤔

I meant I'm fine with such API but there maybe other point of view hence no strong opinion of mine on which of high-bit/low-bit or bit-offset/bit-size couple is to be preferred as argument.

Comment on lines +74 to +93
/* ADCxx prescaler for all F3 but F37x: division factor */
#define ADCxx_PRE_DISABLED 0x0
#define ADCxx_PRE_DIV_1 0x10
#define ADCxx_PRE_DIV_2 0x11
#define ADCxx_PRE_DIV_4 0x12
#define ADCxx_PRE_DIV_6 0x13
#define ADCxx_PRE_DIV_8 0x14
#define ADCxx_PRE_DIV_10 0x15
#define ADCxx_PRE_DIV_12 0x16
#define ADCxx_PRE_DIV_16 0x17
#define ADCxx_PRE_DIV_32 0x18
#define ADCxx_PRE_DIV_64 0x19
#define ADCxx_PRE_DIV_128 0x1A
#define ADCxx_PRE_DIV_256 0x1B

/* ADC prescaler for F37x: division factor */
#define ADC_PRE_DIV_2 0
#define ADC_PRE_DIV_4 1
#define ADC_PRE_DIV_6 2
#define ADC_PRE_DIV_8 3
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe a separate stm32f37x_clock.h (as done for other series) should be introduced. This would allow for more consistency across series.

The definitions in this file can be gated behind a symbol that only it defines such that it overrides them cleanly, i.e.:

+#ifndef ZEPHYR_INCLUDE_DT_BINDINGS_CLOCK_STM32F37X_CLOCK_H_
+ /* STM32F37x has specific ADC prescaler definitions */
...

Expected names are the following:
- "adcx" for the enabling the register clock (mandatory)
- "adc_ker" for configuring the kernel clock (if applicable)
- "adc_pre" for configuring the prescaler (if applicable)
Copy link
Contributor

Choose a reason for hiding this comment

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

It should be mentioned somewhere that one prescaler may be shared by several ADCs.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's also the case for the kernel clock (at least sometimes, and it can even be shared with DAC), so it should be mentioned too.

#define MCO1_SEL(val) STM32_DT_CLOCK_SELECT((val), 3, 24, CFGR1_REG)
/* No MCO prescaler support on STM32F1 series. */

/* ADC prescaler : division factor */
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
/* ADC prescaler : division factor */
/* ADC prescaler: division factor */

But IDK if columns are very useful here?

Suggested change
/* ADC prescaler : division factor */
/* ADC prescaler division factor */

ditto all

On STM32F3x (except STM32F37x), this configures only the synchronous
prescaler (see properties adcXX-prescaler in st,stm32f3-rcc bindings to
set asynchronous prescaler).
prescaler (use the clock property to set asynchronous prescaler).
Copy link
Contributor

Choose a reason for hiding this comment

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

The STM32F37x also have a clock property for ADC_PRE, so isn't this incorrect? Or are both prescalers applied in that instance? The comment could use some additional clarification.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

STM32F37x has the same ADC as STM32F1 and uses the "stm32f1-adc" compatible which removes this property and the "st,adc-clock-source" property since they are not applicable for them.
So maybe "(except STM32F37x)" is not even worth mentioning since it doesn't use this compatible anyway.

Copy link
Contributor

@mathieuchopstm mathieuchopstm Oct 17, 2025

Choose a reason for hiding this comment

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

That is what I would have suggested. (removing (except STM32F37x))

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: ADC Analog-to-Digital Converter (ADC) area: Boards/SoCs area: Clock Control area: Devicetree Bindings area: Tests Issues related to a particular existing or missing test platform: STM32 ST Micro STM32 Release Notes To be mentioned in the release notes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants