-
Notifications
You must be signed in to change notification settings - Fork 6.8k
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
RFC: rework GPIO interrupt configuration #17102
Comments
Reworded description to remove my temporary confusion that a GPIO driver might control multiple ports. It'd be nice if that was true, but it's not. |
And with your proposal, if somebody takes advantage of enabling interrupt on all pins for itself, no other system could register a callback anymore. Same problem then. This issue of "taking advantage to is a non-issue as there is no way you can really fix it.
You are assuming wrong. Let's say a soft reset for peripherals that would be triggered via gpio pin, meaning each peripheral drivers would trigger a soft reset based on that pin event. That's a valid use case. You would have multiple listeners for the same pin. And it is just an example. And for the second question, configuring and enabling are 2 distinct things. You could have them in one go, or separate as it is right now: in the end it is just a matter of taste whether you prefer a function doing 1 thing or 2. It's been chosen to have functions doing one thing only, fine. In general, as long as there is no real life issues with an API, so anything besides assumptions, let's not change it. It is a costly move for zephyr and its users. Better be sure a change is worthy to be made before actually doing it. |
I'm not assuming anything: I'm raising a question to try to understand why things are the way they are. You've presented a reasonable use case for multiple listeners. That needs to be traded against the risks of having those listeners apply inconsistent configurations. Enabling and disabling each have multiple steps, one of which is registration. Whether and how to fragment the steps is a design decision. There is a cost to leaving a callback registered when it's disabled. My overall point is that we've discovered reasons to separate a pin's GPIO functionality from its interrupt functionality, so the API may be reworked anyway. Now is the time to revisit past decisions, see if they're still relevant, and ensure that any changes handle both existing and new use cases. |
@tbursztyka there is a real life issue for me. Currently the combined interrupt/ pin configuration muxes the pin to the GPIO function. In my case the GPIO pin is used as a HW CS (muxed to the SPI) for my board acting as a SPI slave. I still need an interrupt when the SPI chip select is asserted but I can not do a software based chip select handling for the SPI function. With the current API I can either have HW CS or the interrupt. This forces me to create an OOT GPIO driver with a changed API. |
@b0661 I don't see you issue being fixed by what's being proposed here (thus why these changes are not needed: they don't fix anything). Aren't you working on a complete rehearsal of the gpio/pin sub-sybstem in zephyr (pinctrl etc...)? |
Any changes related to @b0661's need may or may not be part of that PR, which primarily deals with GPIO device tree bindings. So I opened this to discuss what other interrupt configuration features might be worth considering. I understand your perspective and will take it into consideration if I'm involved in any related concrete proposals. |
I´m working on a pinctrl subsystem that relates to pin configuration. Interrupt configuration is not part of it and the pinctrl subsystem is blocked anyway by the missing of appropriate code generation (or the decision on that). So it is not in scope of this discussion. |
I somehow didn't realize earlier that it was possible to bind more than one callback structure to a given GPIO pin. And I don't think it would be reasonable to change this - @tbursztyka already provided a use case for such possibility. I can also imagine a need of having a common callback for a group of pins and separate specialized callbacks for individual pins from this group, and these callbacks added and removed from different modules (of course this can be also achieved in other ways but here we've got an API that already provides means for it). |
I'm still concerned about multiple devices changing the configuration of a pin without coordination and no way to detect or diagnose this situation. What if we do put the interrupt configuration and some coordination flags in the structure that holds the callback, keep the distinct register and enable steps, and pass a reference to the (registered) callback structure in the enable operation? Then, based on other flags in the callback-specific structure, we could validate the enable and configure operation. For example, we might have a "control" vs "observe" flag in the callback structure, where "observe" never changes the configuration but just ensures that the callback is invoked whenever the interrupt is generated according to the configuration set by the device that "controls" the pin. This handles the multiple consumer case without making them all responsible for configuration. (We could also have a "shared control" which allows them all to control with some defined semantics about compatible pin configuration and how to handle mixed enabled/disabled states, in case the multiple consumers can't rely on a single controller). I too have a case where a single pin has two roles, one an active-low signal where I want to detect on falling edge, and one where it's a square wave and I want to detect on rising and falling edge. Changing the mode involves an I2C command to a peripheral. I could handle that with two registered callbacks, with only one of them enabled (and so in control) at any time. |
LGTM. On every enable and disable operation the total (all currently enabled callbacks) interrupt configuration can be evaluated. Any enable request that would require an interrupt configuration that would jeopardize the current interrupt configuration can be rejected. First idea of a possible interface. The callback configuration is split from the callback management to allow for constant configuration data to save ram memory.
|
There would be no need to separate the callback and the configuration in 2 structs. Actually it's weird to see the handler being in the config part and not the callback. Just just the new attributes in gpio_callback. |
I agree; I don't think the configuration will be big enough to justify splitting it to save RAM. I'd also like to see a list of the configuration attributes that would move from GPIO configuration to interrupt configuration. I suspect that using a single word (instead of two masks) to define the behavior is more flexible, and would allow sharing flags (like logic vs line level, debounce, pull, ....) that might be common for both pin roles. |
Actually the gpio_callback struct is pure management data that could go away if the driver allocates it by itself and not forces the user to provide it. Currently it is also constraint to not be stack allocated.
I don´t understand why interrupt configuration should be mixed with pin characteristic configuration. These are two distinct functions.
This would mix interrupt configuration with pin configuration. The masks are purely for interrupt configuration and allow easy elaboration of the requested interrupt configuration for all selected pins of a gpio. |
I've updated #16648 to partially address issued discussed here. In particular I've added Since interrupt configuration can be quite complex: interrupt can be triggered on edge or level, logical or physical level I propose not to configure interrupts within a callback configuration but rather require user to do it via a dedicated
will be more of a filter. Execute the callback on the rising edge only even if the underlying interrupt may be configured to be triggered on both edges. Nevertheless, in case Such approach will be much easier to implement and understand. Having |
Moved from #16648 You are creating 6 functions to manage the callback on interrupt.
I think the same functionality can be achieved with a more minimal API:
Complexity of the driver stays the same as all the functionality is anyway needed. Benefit is that you may add several callbacks to a pin change and that the driver always knows about all callbacks (and the trigger_modes) for the pins. Misconfiguration or impossible configuration due to another callback requesting an incompatible trigger_mode can easily be detected. cb is a pointer to const to enable the callback struct data in Flash/ROM. |
As I have mentioned in my last comment the Regarding the proposed API:
As I already argued in my last comment that approach will result in a complex driver code that will also not be very transparent to the user trying to debug it. Let's take the following use case as an example where user always adds and removes callbacks on the same pin:
Again, we may want to configure and enable pin interrupt to use it as a wake up source, not necessarily to execute a callback. Having |
You are just moving the complexity from the driver developer to the application developer. I attached my OOT GPIO driver. You may have a look at gpio_xxx_config_callback in gpio_xxx.c to judge the complexity. gpio_xxx.zip
User (application developer) should not need to debug the driver code. With all the active callback struct data available to the driver the driver code can check any attempt to do an invalid interrupt configuration (see again gpio_xxx_config_callback in gpio_xxx.c).
Assign a NULL callback and you are done. Enabling an interrupt just for the purpose of getting an interrupt is not really related to GPIO.
This assumes there is a common need to have "fast" interrupt enable/disable to prevent the interrupt code execution all together only for a certain time. Otherwise that can be done by a flag in the callback. Anyway on shared interrupts this will only prevent interrupt execution if there is no other callback related to the shared interrupt. |
Not at all. We are talking about a very different approach to configuring interrupts. In the scenario involving Looking at the code in gpio_xxx.c, in function
If the new configuration is not identical to the existing one you simply return -EINVAL. This means the scenario I mentioned
is not supported by your code. It is if we stick with
It is very much related to GPIO. Assigning NULL pointer to configure interrupt without a callback works, it is also quite clumsy. Possibility of using GPIO pin interrupt as a wake up source is an important use case that we cannot overlook. We do need a possibility to easily enable and disable pin interrupt without the need to run complex interrupt configure functions. |
@mnkp Your proposed sequence use case:
is interesting and I'd like to explore it. First: for this to be validated as a use case we must assume that A, B, and C are from independent modules that cannot easily coordinate with each other: if they were all from related modules this could be handled by a single callback that demultiplexes to the appropriate condition-specific reactions. Agreed? On the surface, this might derive requirements for these features:
Second: is that the breakdown you envision, or are some of these combined (e.g. any registered callback is enabled, and disabling it unregisters it)? This approach is complex. The conditions for enabled callbacks together form the set of conditions for the interrupt. So changing the set of callbacks can change the interrupt conditions. This may be inconsistent with the expectation that there is one function that configures the interrupt. What part of the system is responsible for ensuring the two are compatible, given that configuration and registration changes may be interleaved from uncoordinated modules? I would like to see an outline of the data structures and functions that you propose, with clearly documented behavior. A possible outline would be:
The PR does not have this complete; for example you've stated that If you wish you could try to do that in the PR, but I think details would be lost. I'd rather see it at a more abstract level, as we've been doing in #15611 for the pin/port output operations. |
In my scenario callbacks have an expectation about the condition they are called. Thats why the callback struct also includes the pin mask and the pin trigger mode. In the scenario involving gpio_pin_interrupt_configure() callbacks have to be informed about the condition they are called and check by themselve whether this is an expected condition. In my scenario you always get the pin interrupt configuration the set of active callbacks needs - or you get an error return on callback enable. In the scenario involving gpio_pin_interrupt_configure() the application developer has to ensure to do a interrupt reconfiguration that is compatible to all callbacks currently registered and enabled. The decision is just how much abstraction does an application developer want or need from a GPIO driver. From my perspective I just want my callback to make my application work. I don´t want to fiddle with interrupt reconfiguration and management of callbacks possibly enabled by some other threads.
You are right. I just wanted to show the total complexity of the code. You may replace the check by
and it should be supported !-). I think this not a change in complexity.
I´m not against enable/disable pin interrupt functions. It´s just if all application code would do a enable callback, enable interrupt, ... disable interrupt, disable callback sequence it is just not worth to introduce it with some potential error in doing the sequence wrong (e.g. forgetting about the disable interrupt).
Yes. What I still do not understand is what shall the GPIO ISR do if the interrupt fires but there is no callback? |
I want to continue step back from solution details, and revisit an original proposal: The GPIO interrupt infrastructure only needs to support a single callback on any pin. So far we have two use cases for multiple callback capability:
Why is fanning out notifications the responsibility of the generic GPIO driver interrupt API? A soft reset could easily be implemented by having a service monitor the interrupt and distribute change notifications to whatever wants them. I have no idea what use case motivates three independent callbacks with different conditions. Multiple callbacks on one pin introduces significant complexity, because there is no longer a single source defining whether the interrupt is enabled and what conditions must produce a hardware notification. It also prevents constant-time enabling/disabling within the interrupt handler. Please justify why we need this capability. |
That one's easy: nothing. Presumably there are other side effects, like a latched register, that can be checked on boot to determine the cause of a wakeup. I have no problem with indicating "nothing" by providing a null function pointer in the callback configuration when the interrupt is configured. I do have a problem with recording fact-of an interrupt and expecting the infrastructure to invoke the callback when it's registered, if somebody's proposing that. This is another use case that needs to be explored at a conceptual level. |
At least for the interrupt enable this scenario can not be avoided as e.g. in STM32 the interrupt is shared between several pins. So even if you have one callback per pin you may have multiple callbacks per interrupt with all the downsides like managing interrupt enable and non constant-time enabling/disabling. |
Multiple callbacks per interrupt doesn't cause a problem; you can in theory have an array of callback pointers indexed by pin, so the per-pin cost is constant once a triggering pin has been identified. Even if you use a The question remains: why do we need to support multiple callbacks on a single pin? |
Before I answer individual questions I have some more input related to I looked a bit more into the issue. The NXP Given this complication and taking into account that there may be more drivers like |
Is a callback related to the pin, a pin mask or the GPIO port? |
Or specify that the pin configuration is provided in the callback structure, and that there's only one of those per pin, in which case
would make it a lot easier to understand what you're proposing, whether it works for us, and what needs to be provided to use it and implement it effectively. |
My intention at the moment is not to change much comparing to what we have. We can deprecate
as proposed by @b0661 with the difference that pin_trigger_mode will not influence the underlying pin interrupt configuration but merely encode on which interrupt edges the callback should be executed, i.e. pin_trigger_mode will function more like a filter. I mentioned that in my previous comment. |
In general I'm in favor of a simple API that does few things but does it well. The more advanced features should still be easy to support or supported but not necessarily included by default. If we had an API that supports a single callback per interrupt but user could enable multiple callbacks per interrupt via a Kconfig option that would be fine with me. |
I still think the pin_mask in the callback struct is also valuable. Adding the same callback to several pins (e.g. for bit banging on a multiple line connection) requires to call gpio_pin_interrupt_configure() for every pin. If the pins happen to be on the same shared interrupt the callback is inserted multiple times in the callback list. Using the pin_mask may make that more convenient (only one gpio_interrupt_configuration) and save some entries in the callback list of shared interrupts. Furthermore the management information about the pin the callback is interested in the callback list is now in the callback struct and does not occupy driver memory and does not have to be in RAM. |
So the drivers would still have to implement all the complexity of multiple callbacks per pin[*], but it'll only be used rarely for cases that nobody's justified as worth the effort. I don't think that's a good plan. [*] I assume you meant that rather than per interrupt. We know there can be multiple callbacks per invocation of an interrupt handler that's shared among multiple pins. |
I never mentioned we should remove it. |
I was just scanning this to understand exactly why we're deprecating the pin enable/disable callback operations in the new GPIO API. Removing the calls and replacing them with enable/disable interrupt is part of what happens in #20017. However, this is not a functional replacement for the existing infrastructure, which technically allows multiple callbacks to be associated with a single interrupt pin and controlled individually. That in turn exposes the concern that, to my knowledge, we do not have a task to document for release notes all the API changes that are incorporated into the topic branch and what they should be replaced by, nor do we have a roll-up list of all tasks that need to be completed before the topic branch is merged to master and placed firmly on the road to a release. #18530 converts drivers, #20017 converts in-tree users; those should be on that roll-up list. @carlescufi IMO we should get started on this in the next API telecon. |
Some of this done in the GPIO rework, there's no strong motivation for dealing with the rest at this time. Possibly worth revisiting when things other than GPIO require configuring interrupt callbacks. |
The existing GPIO API has a somewhat baroque approach to configuring interrupts and callbacks, the motivation for which is unclear:
gpio_callback
structure provides the handler to use in the callback, and specifies the pins for which the callback applies;gpio_{add,remove}_callback()
API adds or removesgpio_callback
objects from registry maintained in the driver but does not automatically enable what's been added;gpio_{en,dis}able_callback
API operates on pins in isolation of whether any callback handlers happen to be registered for them.The combination seems to allow for multiple drivers/application functions to request notification of interrupts occurring on a pin, but if somebody takes advantage of that any one of them can enable or disable the callback for all them.
This comment on work to clean up the GPIO API on proposes a change that moves the pin's interrupt-related configuration (viz. edge/level, high/low) from being performed through GPIO configuration into being provided in the callback structure.
So if the API may be changed, two questions:
Is there any motivation for allowing multiple consumers of interrupts on a single pin-in-driver?Yes, a soft-reset featureTentatively my answers would be "no", and "yes", which could be accomplished by using something like
gpio_enable_interrupt
to handle the registration, configuration, and enable operations andgpio_disable_interrupt
to disable, return the GPIO to its non-interrupt configuration, and remove the callback registration.It would be nice to have this along with the other improvements of #16648, although it's not directly related to device tree flags so probably belongs in a coordinated PR.
I would like to have consensus on the value and approach before anybody's asked to spend time on this.
The text was updated successfully, but these errors were encountered: