-
Notifications
You must be signed in to change notification settings - Fork 6.6k
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: timer: silabs: Add sleeptimer timer driver #80227
drivers: timer: silabs: Add sleeptimer timer driver #80227
Conversation
The following west manifest projects have been modified in this Pull Request:
Note: This message is automatically posted and updated by the Manifest GitHub Action. |
26fe225
to
4cd8cd1
Compare
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.
If I understand right, Silabs have 5 different counters (burtc, prortc, rtcc, sysrtc, timer). Depending of the SOC_FAMILY we use HAL choose the proper driver, that's it?
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.
Timer is a high-frequency counter, the others are low-frequency.
Prortc is internal to the radio (protocol rtc), and devices with this one also has rtcc. Prortc integrates with protimer, the high-frequency radio protocol timer.
Devices with sysrtc don't have a prortc, the protimer is instead integrated with a second channel group in the single sysrtc.
All devices have burtc (backup rtc).
The sleeptimer HAL API is backed by SYSRTC on devices that have that peripheral, and RTCC on the ones that don't (by default -- it can also be backed by other timers based on config options that we currently don't expose in Zephyr).
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.
I believe each of these counters should have its own driver and the instantiation of the right driver should done through the device tree.
However, this is clearly out of the scope of the current PR.
/* Set to true when timer is initialized */ | ||
static bool g_init; | ||
|
||
static sl_sleeptimer_timer_handle_t sys_timer; |
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.
I suggest to group all these variables in a struct (eg. struct sleeptimer_data
).
Then, when possible, I suggest to try to pass it as argument (eg. data
argument in sleeptimer_cb()
) rather than using the global declaration.
It is also possible to only use the syscalls definitions to get the global instance and call the real function:
void sys_clock_set_timeout(int32_t ticks, bool idle) {
return sleetimer_clock_set_timeout(g_sleeptimer_data, ticks, idle);
}
Thus, we can keep global variables in a small place.
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.
BTW, I tend to avoid storing value I am able to re-compute. Thus, the reviewer don't have to check all the places the fields is written in the code.
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.
Will take a look at this tomorrow, agree that it makes things a bit less messy. The current implementation is very close to the existing gecko_burtc_timer
.
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.
Addressed.
#include <zephyr/drivers/timer/system_timer.h> | ||
#include <zephyr/logging/log.h> | ||
|
||
#include <sl_atomic.h> |
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.
I don't see any call to sl_atomic
. Is it useful?
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.
Removed.
#define DT_RTC DT_COMPAT_GET_ANY_STATUS_OKAY(silabs_gecko_stimer) | ||
|
||
/* Ensure interrupt names don't expand to register interface struct pointers */ | ||
#undef RTCC |
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.
I don't see any reference to RTCC in this file.
(That's said this symbol is super intrusive and it should be fixed in the upstream)
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.
Yeah, it's annoying. The sleeptimer HAL requires us to connect the _IRQHandler function for the peripheral backing the implementation to the interrupt vector table, since Zephyr doesn't use the standard CMSIS vector table. Unfortunately, this function has a peripheral-specific name, rather than a generic alias like sl_sleeptimer_irq_handler
.
The "least bad" way of doing this I came up with (while avoiding HAL-isms in DT) was to encode the interrupt name in the interrupt-names
DT property, and use CONCAT(DT_IRQ(...), _IRQHandler)
to construct the correct function name. However, this causes unwanted macro expansion for RTCC
to the CMSIS struct pointer for the peripheral block. SYSRTC is unaffected, since its peripheral macro is SYSRTC
, but its interrupt name is SYSRTC_APP
. Hence the single #undef
, since as you note, we don't actually want or need the RTCC struct pointer in this driver.
We should work with upstream to get the interrupt handler aliased to sl_sleeptimer_irq_handler
, similar to how the HFXO manager driver already has a generic sl_hfxo_manager_irq_handler
symbol for its interrupt handler.
"(min is %u). Config: SYS_CLOCK_TICKS_PER_SEC is " | ||
"%d and timer frequency is %u", | ||
g_cyc_per_tick, MIN_DELAY_CYC, CONFIG_SYS_CLOCK_TICKS_PER_SEC, | ||
z_clock_hw_cycles_per_sec); |
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.
Is it also true if !CONFIG_TICKLESS_KERNEL
?
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.
I guess it's technically not true, since the timer isn't restarted/reconfigured, so the N clock cycle delay of writing to LF peripheral registers doesn't apply. I don't see any harm in keeping the restriction for now, though.
BUILD_ASSERT(CONFIG_SYS_CLOCK_TICKS_PER_SEC > 0, | ||
"Invalid CONFIG_SYS_CLOCK_TICKS_PER_SEC value"); | ||
|
||
g_cyc_per_tick = z_clock_hw_cycles_per_sec / CONFIG_SYS_CLOCK_TICKS_PER_SEC; |
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.
What happen when z_clock_hw_cycles_per_sec % CONFIG_SYS_CLOCK_TICKS_PER_SEC != 0
? I believe we don't need to be very accurate. However, if this division is not an integer, we will accumulate an error that could generate problem.
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.
Depending on the numbers this can actually be very inaccurate: #80149
But I wouldn't block this PR for that since all drivers are affected by this.
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.
An easy way to fix it would be to raise an assert (in this current driver) if z_clock_hw_cycles_per_sec % CONFIG_SYS_CLOCK_TICKS_PER_SEC != 0
.
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.
What's the value of z_clock_hw_cycles_per_sec
for this driver? For a tickless kernel, the default value of CONFIG_SYS_CLOCK_TICKS_PER_SEC
is 10000
.
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.
z_clock_hw_cycles_per_sec
is 32768, and CONFIG_SYS_CLOCK_TICKS_PER_SEC
is 1024 (default set in SoC-level Kconfig), so the default configuration cleanly divides with no loss of accuracy.
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.
While all SoCs seem to be doing that, it does violate the kconfig documentation:
Also the number of cycles per tick should be chosen so that 1 millisecond is exactly represented by an integral number of ticks
z_clock_hw_cycles_per_sec = sl_sleeptimer_get_timer_frequency(); | ||
|
||
BUILD_ASSERT(CONFIG_SYS_CLOCK_TICKS_PER_SEC > 0, | ||
"Invalid CONFIG_SYS_CLOCK_TICKS_PER_SEC value"); |
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.
Can this happen?
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.
For current SoCs, it cannot, since it gets a nonzero default value in SoC-level Kconfig. But for a new SoC where it isn't set by default, it's possible in the future.
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.
nitpick: I tend to group BUILD_ASSERT()
on top of the file to avoid to cluttering functions.
|
||
IRQ_CONNECT(DT_IRQ(DT_RTC, irq), DT_IRQ(DT_RTC, priority), | ||
CONCAT(DT_STRING_UPPER_TOKEN_BY_IDX(DT_RTC, interrupt_names, 0), _IRQHandler), | ||
0, 0); |
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.
sleeptimer
and stimer
look entangled. I wonder if this file shouldn't be merged with counter_gecko_stimer.c
. SILABS_SLEEPTIMER_TIMER
would become an option of COUNTER_GECKO_STIMER
.
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.
Yes, the counter API implementation called stimer
is another user of the sleeptimer. I agree it deserves to get looked at for a refactor at some point, but the role of the counter API and the timer API are different, since the timer API provides kernel/OS timebase, while the counter API is "userspace" timers, so I don't want to entangle that work into this. The sleeptimer-based OS timer is urgently needed to fix issues with sleep on SYSRTC-based devices, since the radio, power manager and OS don't play nicely together when they don't have the same understanding of "time until next wakeup from deep sleep".
The DT node "stimer0" represented two different hardware timers, RTCC and SYSRTC. Use the correct peripheral names for the nodes. Add interrupt names and missing interrupt numbers. Signed-off-by: Aksel Skauge Mellbye <aksel.mellbye@silabs.com>
4cd8cd1
to
7c9a83e
Compare
7c9a83e
to
5766705
Compare
Add OS timer implementation making use of the Sleeptimer HAL. Sleeptimer integrates tightly with the Silabs Power Manager HAL, and must be used as the OS timer to achieve optimal power consumption when using the radio. Signed-off-by: Aksel Skauge Mellbye <aksel.mellbye@silabs.com>
Disable BURTC timer in board defconfigs, as it's no longer used. Signed-off-by: Aksel Skauge Mellbye <aksel.mellbye@silabs.com>
Power Manager no longer requires the Counter driver. This seems to have been a hack to get the Sleeptimer HAL included in the build, as the Sleeptimer is the real dependency of the Silabs Power Manager HAL. Since Sleeptimer is now used for the OS timer, this hack is not needed. Signed-off-by: Aksel Skauge Mellbye <aksel.mellbye@silabs.com>
5766705
to
6b76afe
Compare
@asmellby even though it's a singleton, any reason why you didn't go for representing the sleep timer through a |
I see you're kind of building on top of "silabs,gecko-stimer", however would it e.g. make sense to have the sleep timer as a child node of that, or use a phandle to tie them together? |
No other timer driver creates a struct device, as far as I can tell. The timer API is also not "normal" looking -- it's global functions rather than an API struct with function pointers, and those functions don't give you a struct device pointer to work with either. |
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.
No other timer driver creates a struct device, as far as I can tell. The timer API is also not "normal" looking -- it's global functions rather than an API struct with function pointers, and those functions don't give you a struct device pointer to work with either.
Ok. Fair enough. I guess this can be revisited later if we want.
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.
I believe each of these counters should have its own driver and the instantiation of the right driver should done through the device tree.
However, this is clearly out of the scope of the current PR.
depends on DT_HAS_SILABS_GECKO_STIMER_ENABLED | ||
select SOC_SILABS_SLEEPTIMER | ||
select TICKLESS_CAPABLE | ||
select TIMER_READS_ITS_FREQUENCY_AT_RUNTIME |
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.
Should we add default y
?
Add timer driver using the Sleeptimer HAL for use as OS timer. The Sleeptimer is the unified system timer on EFR32, and integrates with power management and radio subsystems.
The driver passes kernel timer tests on both xG24 and xG27:
This PR is stacked on top of #79415 and will be rebased once that PR is merged. Only the last 4 commits are unique to this PR.