-
Notifications
You must be signed in to change notification settings - Fork 2k
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
cpu/sam0*: enhance power saving by switching EIC clock source during STANDBY mode #13415
Conversation
I understand the need for this PR but I think the EIC clock input source should be configurable by user (in periph_conf.h ?) Moreover this could also work for the BACKUP mode IIRC. Maybe we could create one define per sleep mode to allow clock source selection per sleep mode ?
With better names of course but that's the spirit. What do you think ? |
Thank you for your feedback! The EIC is located in the power domain PDTOP. If I understand the datasheet correctly, PDTOP is switched off in BACKUP mode. If we want wake-up events in BACKUP mode raised from external pins, we have to use some special pins that are connected to the RSTC. Long story short, we do not need Furthermore, I thought about the clock selection for IDLE mode. Can you come up with a use case were it is useful to drive the EIC with the slow clock? Is this really required? I would rather make the switch to ULP32K in STANDBY mode optional. This could be beneficial for applications where the current draw isn't a problem at all. (But on the other hand: why would they go into STANDBY mode?) I don't know, how many cycles are lost for the switch. We are also waiting for the EIC to sync our requested change into its clock domain ( |
Mind you that added configuration options add complexity as you end up with more configuration permutations. IMHO it should best be avoided if possible. Dynamic clock switching should yield the best of both world: You get to detect high frequency events in RUN mode and power savings in STANDBY and wake from BACKUP. |
I agree with @benpicco here. Power-Saving is always hard, especially if there are many configuration options. Therefore we should have less options and save as much power as possible. |
cpu/saml21/periph/pm.c
Outdated
#ifdef MODULE_PERIPH_GPIO_IRQ | ||
if (EIC->CTRLA.bit.ENABLE) { | ||
DEBUG_PUTS("pm_set(): reduce EIC clock speed"); | ||
/* CKSEL is enable-protected -> disable the EIC beforehand */ | ||
EIC->CTRLA.reg = 0; | ||
while (EIC->SYNCBUSY.bit.ENABLE) {} | ||
|
||
EIC->CTRLA.reg = EIC_CTRLA_ENABLE | EIC_CTRLA_CKSEL; | ||
while (EIC->SYNCBUSY.bit.ENABLE) {} | ||
} | ||
#endif |
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 code looks rather similar to the code below. Can we make it an inline function or are there already macros for enable-protected registers?
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 think it would make more sense for this to live inside sam0_common/periph/gpio.c
, this should work the same for all sam0s other than samd2x
- also in preparation to what is proposed in #13422
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.
How do you want to implement it within sam0_common/periph/gpio.c ?
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 would define a function in cpu/sam0_common/include/periph_cpu_common.h
that is called in cpu/caml21/periph/pm.c
and implemented in sam0_common/periph/gpio.c
?! I think this is the solution with the least overhead.
Another solution could be some kind of registry inside of periph/pm.c
that holds all required callbacks. sam0_common/periph/gpio.c
would register a callback on startup. But this would imply some overhead every time pm_set()
is called.
What solution would you prefer @benpicco?
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 had the first option in mind too.
I don't think a dynamic registry is needed here - something like what is used for auto_init would do. (Basically what you already did there)
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 was talking about a macro here to avoid code duplication as above. I guess there might be more use cases for that macro at some point.
Not sure why we switched to callbacks here?
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.
The code is more related to the GPIO peripheral that must be called upon PM changes. So from a logical point of view this make sense to me to put it in periph/gpio.c
.
I already have another PR in mind: We need to mux the SPI pins back to GPIO function before switching to STANDBY oder BACKUP mode. The concept of calling certain callbacks makes sense, imho. Otherwise I have to patch this also inside of pm.c
and clutters the whole power management.
What are your concerns regarding this callback implementation?
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.
That's my point. I am not talking about the callbacks in any way. I was reviewing the code as is. Which shows code duplication which can be solved by a nice macro and used elsewhere.
If you want to replace the above code with a callback, that's fine, but then the code will simply be added elsewhere and still be duplicated.
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.
Oh now I get you ... sry! Of course, I took your review serious and de-duplicated that bit. I implemented reenable_eic(clock)
. Thank you for your review. I really appreciate it!
@benpicco and I focused on the question where to put the implementation (with or without code de-duplication). That's why we started talking about something more or less out of focus. We already had some conversation about this topic in other PRs and issues.
You're right, so there is no need for configuration. |
I pushed a fixup ... could you have a look into it, @benpicco? Does this match with the concept you had in mind? |
b3a7164
to
f8a7394
Compare
I just force pushed a new approach. I think we should first discuss the overall concept: I wrapped the If the GPIO peripheral driver is compiled in, its callback functions are called and the driver has the opportunity to switch clocks of the EIC. If the proposed concept is accepted, I have another PR in mind touching Thank you all for your hints and feedback! |
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 tested this on same54-xpro
, saml21-xpro
and samr21-xpro
and I get power savings in STANDBY across the board.
On saml21-xpro
I get 1.8µA in STANDBY with full memory retention & wake on GPIO.
The structure of sam0_cortexm_sleep()
will allow to extend this approach to more peripherals in the future.
Please squash! |
b2f142c
to
442ddc1
Compare
Thank you very much! This merge helped me a lot to move closer to RIOT upstream with the project I am currently working on :) |
Contribution description
This is a follow up PR of issue #13406.
As discussed, the 16MHz clock is set to ONDEMAND mode, so the main clock is only active if it is actually required by the system.
The External Interrupt Controller (EIC) is clocked with the low power 32kHz clock in STANDBY mode.
Testing procedure
Issues/PRs references
Issue #13406