-
Notifications
You must be signed in to change notification settings - Fork 6.7k
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
gpio/pinctrl: GPIO and introduce PINCTRL API to support gpio, pinctrl DTS nodes #15611
Comments
Pull-up and pull-down input configuration flags appear to be missing.
Some devices (Nordic GPIO, Semtex SX1509B) support drive strength configuration with very different capabilities and defaults. Slew rate was also mentioned. Unless every potential hardware capability can be anticipated with a generic API, there should be some mechanism to pass driver-implementation-specific flags along with the standard ones so vendor extensions can be controlled by applications that are aware of the underlying hardware. It's unclear whether the flags used in the device-tree The rework should clearly define at what point the flags specified in the device tree will be applied: when the GPIO driver is initialized (e.g. to come up in a low-power mode) or when whatever uses the corresponding GPIO is initialized (which would likely be a very different configuration). It may be useful to provide multiple sets of configuration flags for a specific GPIO, perhaps by leveraging the PINMUX concept. Functions in the new GPIO and PINCTRL driver API should clearly document exactly what is meant by each flag, and exactly what behavior is expected from drivers that are given flags that represent a configuration that is not supported by the hardware (ignore, error, make up something, ...). The existing solution failed to do this, and the results have been unsatisfactory. |
If this is the output configuration it should be renamed to GPIO_OUTPUT_ACTIVE_LOW/HIGH to avoid any misinterpretation.
How is push/pull operation configured?
Why not have a set of generic debounce times that may fit a lot of SoCs (e.g. DEBOUNCE_NONE, DEBOUNCE_SHORT, DEBOUNCE_MEDIUM, DEBOUNCE_LONG). What short/ medium/ long means is dependent on the SoC and may even be specified in the DTS.
GPIO_INT_EDGE_BOTH is just a waste of configuration space, it should be the combination of the GPIO_INT_EDGE_RISING and GPIO_INT_EDGE_FALLING flags. |
Added to the issue description.
I'm not sure how they made it there. Removed.
I think we need to discuss drive strength, slew rate and debouncing configuration a bit more. In Linux DTS these are configured with a parameter.
If we want to stay true to the promise that Zephyr DTS description is compatible with Linux DTS then we would need to configure these parameters via a dedicated functions taking respected parameter as an argument. The GPIO_INPUT_DEBOUNCE flag would need to be removed. Setting drive strength in mA doesn't seem to be very practical, on the other hand that's probably the only way to do it in a generic way. We could provide some SoC specific convenience macros that would encode available drive strengths in mA as an easy to understand name.
The DTS configuration has to follow the rules specified in GPIO bindings. These are documented in Linux DTS GPIO bindings.
According to the Linux DTS automatic configuration of GPIO pins is achieved by means of gpio-hog property.
That's independent from GPIO API but surly needs to be clarified.
Yes, fully agree. This should be taken care of in the PR. |
The flag is honored by
By default (no flags) output is configured in push/pull mode. I've updated the description.
I discuss the configuration of debounce time in the previous comment.
Indeed, that was not well explained. I propose to have to have two sets of flags. The base set used by the drivers, fully sufficient to configure an interrupt. Since their usage may not be intuitive to the end user / may involve too much typing the second set are convenience flags. This is a single identifier with a straightforward name which combines and may be used in place of a set of base flags. I've updated the description. |
@mnkp You replied to my comment:
I am not convinced that
And now I'm really puzzled regarding what would be the best way to handle GPIOs polarity inversion. 🙂 Anyway, I still think that the influence of the |
Hi @mnkp, I wanted to clarify my remarks about fast access functions during today's meeting. I think that the My main point is that it would also be useful for other device drivers that need fast access to GPIOs built into the SoC to have a generic API for accessing the output registers for these devices. In other words, something like this:
To be equivalent to:
except without the overhead, so that the bit manipulation on The inspiration is from the FastLED library's pin abstraction (https://github.com/FastLED/FastLED/blob/master/fastpin.h; also see PORTING.md in the same repository), which works on a variety of Arm SoCs, ESPs, and AVRs at the very least. Of course, there are problems to consider:
Your thoughts are welcome. Thanks. |
@mbolivar thanks for the proposal. I was hoping that removing the
with default Zephyr compiler optimization options the implementation takes 11 assembly instructions (for comparison the I believe though that letting an application directly access the |
Yes, I understand this is not a universally supported register type. However, I believe that the existence of the fastled library shows that there is a small set of "types" of GPIO output registers, which we can support through a common API. E.g. if
I'm not sure I can see clearly how some of these details would work in practice, but I look forward to seeing more details if you have them. Thanks for your response! |
@mnkp Regarding the question I was trying to signal yesterday on slack. It's about configuring interrupts for handling a button, for instance. An application will get the flags defined for the button in DTS through a a generated macro, like |
After taking another look, I've got one more doubt. Why do we change the required behavior of |
The current interrupt configuration done in the Zephyr version of the DTS is not standard and indeed it may be best to remove it. We can always add it back in the future, possibly in a standard form, if the need arises. The easiest way to have the application configure interrupts in a portable way is to define them in terms of pin active, inactive state as in: LEVEL_INACTIVE, LEVEL_ACTIVE, EDGE_TO_ACTIVE, EDGE_TO_INACTIVE. A general remark regarding DTS: |
I'll copy paste my answer from #12651.
In short we don't change the behavior of |
I cannot entirely agree with that. As I recall, one of the reasons for making this GPIO flags cleanup was the need to replace |
@mnkp @anangl
|
@carlescufi |
@anangl @carlescufi In any case, I think you're running into the same problems that killed #11880: we can be backward compatible, or we can match Linux/DTS API. Because Or so I believe. I'm not sure everybody believes they're incompatible, and if they are I don't know that there's agreement on which requirement should take priority. If not, that should be clearly settled first. |
@pabigot @anangl @mnkp
The advantage with
that way we match the classical "active level low/high` way of describing the logical levels.
|
Thanks @carlescufi for the proposal. Nice selection! Since we are getting into personal preference territory regarding naming I'll make my statement. I like very much the first proposal: |
While I agree with @pabigot that |
I would prefer to avoid using the term "level" in the API identifiers. AFAICT the API in #16648 has the traditional If this is for the "fast" version of the API I'd rather see that as a separate, parallel API with a different prefix, e.g. |
How is the masked write not clear? It corresponds directly to hardware implementations with explicit set and clear port registers, which allow you to change the state of a pin without a read-modify-write cycle. I don't see how a bit set in both I think I'd rather see something like:
And if you really want an invert, you can add:
|
So that'd be:
Other than the naming conflict when both line and logic level operations are supported I don't have a big problem with this. The original motivation for the API I proposed was to resolve the conflict when having a |
Here is my proposal based on everything that has been said so far, and taking into account the following:
Standard APIs:
Fast/descriptor-based APIs:
|
I don't believe we have consensus on the port set API. There have been three proposals:
(1) is existing practice, so should be the fallback if we can't agree on a change. (2) seemed to get some approval and interest, but one rejection. (3) hasn't really been discussed. I prefer (2), because the set-clear-toggle bit mutation operation has general applicability for flags and register values outside of GPIO, and I am going to use it in other situations where it's more clear. I could live with the others. So how about we vote? I'm going to post separate comments for each choice; sorry for the blitz. Click thumbs-up to prefer this API. Click thumbs-down if you think it's an absolutely horrible idea and would want to block a PR that includes it. Vote up on one choice, and down on any of them you want. We'll figure out how to tally the votes when we have some. |
Click thumbs-up to prefer this API. Click thumbs-down if you think it's an absolutely horrible idea.
|
Click thumbs-up to prefer this API. Click thumbs-down if you think it's an absolutely horrible idea.
|
Click thumbs-up to prefer this API. Click thumbs-down if you think it's an absolutely horrible idea.
|
This comment has been minimized.
This comment has been minimized.
Click thumbs-up to prefer an API that combines AP1 and API3. Click thumbs-down if you think it's an absolutely horrible idea.
|
Reviewing some of the material above there's another consensus decision that needs to be summarized: handling of line vs logic level taking into account GPIO and interrupt configuration and the impact of adding line/logic level API on the use of existing A clarified terminology summary from the pin/port access API summary above:
Logic level derives from line level based on a configured active level:
I.e. active level is a binary state. Informally, if a pin is configured active high the value representations of logic level and line level are equivalent; otherwise the logic level is the inverse of the line level. In support of formalizing this, define these two functions for any pin
where standard C conversion between integer and boolean value spaces is assumed and We have also introduced a distinction between GPIO mode and interrupt mode for a pin. Using the proposed API of #16648:
This summarizes my understanding of what we've decided; again, this needs to be validated. The effect of active level on pin mode is specified by this rule:
My recollection is that in early discussions this was not true (possibly because it was mixed with Abstracting away from public API names we have an inspect operation Proposed detailed semantics for
NOTE: The requirements above are carefully worded to specify the behavior when a pin is configured for both input and output. There are devices (e.g. SX1509B) where bidirectional configuration is supported and the input signal may be different from the configured output signal. This proposed behavior must be validated, because it fails to provide a way to read the configured output of a signal that is bidirectional. Also note that this requires that a driver be able to provide the last configured output value; it's possible some peripherals don't provide a way to read that back. With the above, we then have:
Where "all situations" covers GPIO and interrupt configuration, direction, and active level. For mutation operations:
Finally we need the following to deal with the legacy parts of the API:
Without this people may continue to use these flags to enable peripheral-specific inversion capabilities, resulting in cross-platform inconsistencies. (I would rather retain these flags and specify that their use has a target-specific impact on the behavior of |
@pabigot
really necessary, will such functionality be useful? Maybe we could remove it and hence simplify the API by allowing using
I am not convinced that we should distinct such two modes for pins, and basing on them disallow parts of API. I'd rather instead set a requirement for |
@anangl Thanks for the careful reading. For the semantics of interrupt versus GPIO configuration I represented my understanding based on the goal of moving towards PINMUX functionality where interrupt functions are separated (to some degree) from GPIO functions. From earlier discussions I understood that on some hardware a pin configured to be in interrupt mode may not support GPIO operations. I haven't encountered that myself, but I defer to @mnkp to precisely define the two roles and the functions used to control them. For reading the configured output state of a pin: if it is to be possible to toggle a pin state, as with flashing an LED, you need to be able to read the current setting so you know what value to write to change that state. That could be done by:
In my APIs I usually go with (B), but (C) is OK too and simplifies GPIO. Thumbs up here to go with (C), removing explicit toggle support from the GPIO API. |
@pabigot Both (B) and (C) are OK to me, my point was just to not go with (A). (B) seems to be a bit better option, since there are pieces of hardware providing dedicated registers for toggling (no need to read the output state then). It shouldn't be that hard to agree on how to support toggling in the GPIO API. |
You are an optimist. A minimal impact way to do it is to define toggle as the behavior specified when a bit is set in both the Edit: Actually, if we specify the behavior of Edit2: In fact this specification also defines the behavior when the driver knows the operation will not change the current configuration (viz., it must not appear to occur). This is relevant to drivers like SX1509B where an I2C transaction can be eliminated. That tips my vote to (B) provided though this mechanism. |
I agree and I think this is a good way to go. Such semantics may seem a bit "exotic" at first glance, but after you think of it a bit, you realize that it is a good way of utilizing a parameter combination that has to be checked anyway, but instead of returning an error, providing a nice feature for it. To me this is mainly a question of describing it properly in the documentation, so that it is clear to users. What you propose in your first edit seems reasonable.
Yes. That convinces me even more that this option is a better choice. |
API meeting: Standard APIs:
Fast/descriptor-based APIs:
@mnkp the above is from the API meeting, are you happy with that? |
I'd suggest:
|
All of the APIs above have a baseline assumption that you are working on a 32-bit machine, and can only set or clear up to 32 bits at a time. what happens when you move to a 64-bit machine? What happens if you are working on a 16-bit native machine, and it has to extend all of the parameters up to 32-bits? You are passing in 32-bit ints, but getting back native machine ints (which may or may not be 32-bits). |
The baseline assumption is that no GPIO device has more than 32 pins. There's one existing device that has more, and it has to be split into separate devices. Yes, if there's a 16-bit target this would cause overhead, but Zephyr pretty strongly expects a minimum 32-bit system. I generally prefer native int types but for a cross-platform system they introduce complexity. Plain |
The That said, I agree that we should discuss / reconsider the need to enforce a fixed 32-bit types in Zephyr's API. This issue has been neglected for a while. |
In the API meeting last week I explicitly asked whether anybody had an issue with requiring a 32-bit value to represent the set of pins supported by a single driver and people were OK with it. For future-proofing and clarity of argument purpose I'd be happy to see |
tl;dr: The facts have been collected; I've changed my mind; and I now propose revising the specification so So I've been spending some time with #16648 and based on experience would like to propose a change. I originally promoted the I've now implemented that on top of #16648. The effect is that it reduces code size slightly (102 By on nRF52) and slightly reduces the size of the driver structure. It also incurs a noticeable overhead. Below are times, in microseconds, for each step of a series of operations. 16648 is the original implementation using four basis functions, Basis is one that replaces them with one core implementation, NoTgl is Basis with the branch test that supports toggling in
When measured at this level the time for each operation in Basis ranges from 43% to 96% more than for 16648, ignoring the toggle operation which is slightly faster in Basis. When measured at a higher level (the average time for several thousand iterations of a function call that wraps entire sequence) the Basis approach takes 36% longer. If the original Analysis suggests that most of the increased cost in the basis function approach is due to branch conditions that deal with toggle support. The implementation also handled inversion in the basis function, which slightly confounds the results: the 16648 approach of doing it inline allows optimization using the integer constant parameters that aren't visible in the driver. Nonetheless, the need to check for toggle in each basis function invocation adds enough cost that reconsideration is appropriate. Given this evidence I withdraw the proposal that driver implementations should use the basis function for simplicity: it is, indeed, too expensive. As a result there is no motivation for defining Instead the semantics of The New column above is the result of this change, replacing the My branch #18689 has been updated to make this change. BTW: I will be adding the function that does performance testing to the GPIO test in #18689, but right now it depends on devicetree capability not yet merged to master. |
Now that new GPIO API has been implemented and merged, I think we can open the floor to pinctrl API. |
Introduction
As a general requirement we want Zephyr DTS to adhere to the specification as well as to use bindings compatible with those of the Linux kernel. To support gpio, pinctrl DTS node bindings the current GPIO driver needs to be reworked. We also need to introduce PINCTRL driver.
A GPIO driver is used to configure and control the pins directly by the application. A PINCTRL driver is used to configure the pins and route the signals to the internal hardware modules on the SoC.
Requirement to support gpio, pinctrl DTS node bindings used by the Linux kernel doesn't imply that we have to provide the same GPIO, PINCTRL API like Linux does. In fact, the APIs can be quite different. We only need to process configuration data provided by the gpio, pinctrl DTS nodes in an easy and convenient manner.
Problem description
Unlike virtually any other driver e.g. I2C, RTC, CAN, UART which functionality is confined to a single hardware module the pin configuration and control functions can be spread among multiple SoC components. The implementation can vary significantly between vendors or even SoC families.
As an example both Intel Quark and NXP/Freescale K64 have separate GPIO and pinctrl modules however functionality assigned to each module by the respective SoC family is quite different. I.e. in one case debouncing is controlled by GPIO in the other by pinctrl module.
Pin functions controlled by
Intel Quark GPIO module: value, direction, interrupts, debouncing, source (pio/ext)
Intel Quark pinctrl module: muxing, pullup, slew rate
K64 GPIO module: value, direction
K64 pinctrl module: muxing, pullup/pulldown, slew rate, open drain, drive strength, interrupts, debouncing, source (pio/ext)
Proposed change
The driver API should hide implementation details and make it possible to write portable code. I.e. it would be impractical to expect user writing application code to know if they need to use PINCTRL or GPIO driver to configure an output as an open drain. As such both GPIO and PINCTRL API should provide means to fully configure pin properties such as open drain, drive strength, input debounce, pull up or schmitt-trigger mode.
Update GPIO and introduce PINCTRL API to let Zephyr drivers and application code configure pins using data provided by gpio, pinctrl DTS nodes. The API should use naming convention / configuration options which directly relate to the gpio, (gpio.h) and pinctrl bindings defined by Linux DTS. Zephyr implementation should preserve the meaning of Linux defined configuration options. E.g. relation between DTS pinctrl node property
drive-open-drain
and corresponding feature of the Zephyr API should be straightforward to identify and have the same effect.Detailed RFC
GPIO/PINCTRL driver should use the following flags as a parameter to *_configure function:
gpio_pin_write
/gpio_pin_read
functions should write/read the logical pin value.Linux DTS does not specify interrupt configuration within GPIO/PINCTRL node but rather a dedicated IRQ chip node. Zephyr GPIO API shall provide means to directly configure interrupts. Following base flags are proposed:
To make interrupt configuration easier for the end user the following convenience flags are proposed:
Where each of the convenience flags is defined as a combination of a base flag. E.g.
New API functions
Following two functions are added to let the user directly control the pin level ignoring GPIO_ACTIVE_LOW flag. Writing value 1 to a pin will always set it to high output state, writing value 0 will always set it to low output state.
Pin Controller API: TBD
Dependencies
When a new PINCTRL API is introduced the current PINMUX API can be deprecated.
Concerns and Unresolved Questions
The GPIO and PINCTRL drivers will significantly overlap their functionality since many driver features (e.g. pin drive strength, pull-up, pull-down, ability to configure output as on open source) need to be provided by both drivers. A better choice may be merging the two APIs into one.
The text was updated successfully, but these errors were encountered: