Skip to content
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

rework GPIO configuration flags #11880

Closed
wants to merge 7 commits into from

Conversation

pabigot
Copy link
Collaborator

@pabigot pabigot commented Dec 5, 2018

This PR attempts to resolve the concerns and discussions of issue #10339. The following design goals were applied:

  • The semantics and behavior of existing macros must be retained wherever possible. The specific bit values of existing macros were not preserved. In some cases a feature that was indicated by an unset bit is now given its own bit.

  • It is not the scope of this PR to change existing code. As a result several legacy identifiers like GPIO_INT_ACTIVE_LOW remain present throughout the code. I have confirmed that replacing them with the recommended alternative results in a system that passes the sanity check. It will also conflict with any in-progress work that touches device trees. So: removing use of legacy identifiers is the responsibility of the board and driver maintainers, as is taking advantage of new features like GPIO_DIR_OUT_LOW.

  • Capabilities were designed to meet the requests in the issue, informed by existing use within Zephyr and the capabilities of Linux for gpio, pinctrl, and interrupt configuration options as well as highly configurable GPIO devices like the Semtech SX1509x.

  • The flag values remain placed in the dt-bindings include file because most existing DTS bindings set more than just active level and signal type, and there's no motivation to complicate things by attempting to separate out what we might think is relevant only to an application. Best practice, however, is probably to reduce the set of flags indicated in the bindings.

At this point I've run the sanity checks and on applications with Nordic hardware. I won't have non-Nordic platforms available to me until later this week.

I have not yet verified that generated documentation is formatted correctly.

drivers: gpio: fix misuse of u8_t where bool is intended

GPIO configuration flags will move and some that used to be in the low
8 bits are now higher, resulting in implicit constant conversion
overflows. Use a boolean data type to hold boolean values.

Signed-off-by: Peter A. Bigot pab@pabigot.com

:100644 100644 021f8d23a3 9f16f893de M drivers/gpio/gpio_dw.c
:100644 100644 46822c52c4 0e335fe40f M drivers/gpio/gpio_qmsi.c
:100644 100644 0ce5c5bab1 68fc8adb28 M drivers/gpio/gpio_qmsi_ss.c

dts-bindings: gpio: revise flags to support more complete control

Updated GPIO constants to provide more meaningful names and more
complete functionality. The intent is that the presence and
interpretation of existing flags remains unchanged, although their
bitwise representations has.

Added signal type and direction flags matching Linux device tree
bindings, allowing open drain and open source to be distinguished from
push-pull signals.

Extended GPIO_DIR to support bidirectional configuration and setting an
initial value for outputs.

Extended GPIO_INT to more clearly identify level and edge
configurations. Some redundancy is required to continue supporting the
existing single-bit GPIO_INT_DOUBLE_EDGE flag.

GPIO_INT_ACTIVE_foo renamed to GPIO_ACTIVE_foo since active level is
meaningful for non-interrupt inputs and output signals (e.g. RESETn is
an active low output). GPIO_INT_DEBOUNCE similarly renamed to
GPIO_DEBOUNCE as debounce applies to non-interrupt inputs. The legacy
macros are retained with their original values to avoid the need for
a global substitution change.

Other flags have changed bit representations but not interpretation.

Closes #10339

Signed-off-by: Peter A. Bigot pab@pabigot.com

:100644 100644 c365d449e3 97c2d3b110 M include/dt-bindings/gpio/gpio.h

@zephyrbot
Copy link
Collaborator

zephyrbot commented Dec 11, 2018

All checks are passing now.

Review history of this comment for details about previous failed status.
Note that some checks might have not completed yet.

@pabigot
Copy link
Collaborator Author

pabigot commented Dec 11, 2018

Fixed spelling in first commit, and added following:

dt-bindings: gpio: revise drive strength flags for generality

The documentation and design for drive strength was based on Nordic GPIO
capabilities, and presumed that the default state was standard while the
alternate state was high.

The Semtech SX15088/SX1509B GPIO extender went the other way, with
default being high and alternate being low.

Rework the flags so that low and high are distinct from default. Update
the two in-tree GPIO implementations that currently support drive
strength to make use of these flags.

Deprecate the ALT flag but define it to implement the documented
selection (high drive) in case it's being used by out-of-tree systems.

:100644 100644 ddb097b3d4 7654f7e763 M drivers/gpio/gpio_cc2650.c
:100644 100644 acf98fa2cd 6051268856 M drivers/gpio/gpio_nrfx.c
:100644 100644 449d4bce13 97e18dd361 M include/dt-bindings/gpio/gpio.h

@pabigot pabigot mentioned this pull request Dec 11, 2018
@codecov-io
Copy link

codecov-io commented Dec 11, 2018

Codecov Report

Merging #11880 into master will increase coverage by 5.56%.
The diff coverage is n/a.

Impacted file tree graph

@@            Coverage Diff             @@
##           master   #11880      +/-   ##
==========================================
+ Coverage   48.63%    54.2%   +5.56%     
==========================================
  Files         313      235      -78     
  Lines       46436    26221   -20215     
  Branches    10710     6262    -4448     
==========================================
- Hits        22585    14212    -8373     
+ Misses      19395     9412    -9983     
+ Partials     4456     2597    -1859
Impacted Files Coverage Δ
include/bluetooth/buf.h 0% <0%> (-100%) ⬇️
include/drivers/bluetooth/hci_driver.h 0% <0%> (-100%) ⬇️
include/bluetooth/hci.h 0% <0%> (-77.78%) ⬇️
include/misc/byteorder.h 56.81% <0%> (-40.91%) ⬇️
subsys/bluetooth/host/hci_core.c 2.67% <0%> (-38.73%) ⬇️
subsys/bluetooth/host/uuid.c 0% <0%> (-34.15%) ⬇️
include/bluetooth/bluetooth.h 0% <0%> (-31.58%) ⬇️
subsys/bluetooth/common/log.c 0% <0%> (-21.43%) ⬇️
lib/os/fdtable.c 31.46% <0%> (-11.24%) ⬇️
include/net/buf.h 88.88% <0%> (-11.12%) ⬇️
... and 159 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update c96c90a...241f9b4. Read the comment docs.

@pabigot pabigot force-pushed the issue/10339 branch 2 times, most recently from 042f44d to a53da19 Compare December 11, 2018 16:55
@pabigot
Copy link
Collaborator Author

pabigot commented Dec 11, 2018

Added example of use of the API. I've tested this on nrf52_pca20020 on an internal branch that depends on material not yet merged to master.

drivers: gpio: sx1509b: remove ability to configure by port

It seems highly unlikely that anybody would want to configure all pins
to the same configuration. Remove this feature to simplify the code
prior to rework.

Retain the ability to read and write all pins simultaneously as that has
use cases.

Signed-off-by: Peter A. Bigot pab@pabigot.com

:100644 100644 fb0270364d a2d848928e M drivers/gpio/gpio_sx1509b.c

drivers: gpio: sx1509b: transition to new GPIO configuration

Demonstrates an implementation that takes advantage of the new
flexibility in GPIO pin configuration. New features include support for
bi-directional GPIO and drive strength.

Signed-off-by: Peter A. Bigot pab@pabigot.com

:100644 100644 a2d848928e 8ca90d4485 M drivers/gpio/gpio_sx1509b.c

@pabigot
Copy link
Collaborator Author

pabigot commented Jan 3, 2019

Sorry for the blitz, but this has been open for four weeks with no reviews. I'd appreciate it if people would look at it, so we can discuss any remaining concerns on the API call next week. In particular, how might these changes affect your GPIO driver implementations?

@galak @psidhu @mnkp @b0661 @jfischer-phytec-iot @anangl @tbursztyka @MaureenHelm @pfalcon @carlescufi

@tbursztyka
Copy link
Collaborator

lgtm
However, I would advise to propose a patch that changes all drivers to use these new flags directly. Same for the samples. It will reduce the time frame for removing the deprecated macros.

Copy link
Member

@mnkp mnkp left a comment

Choose a reason for hiding this comment

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

There was a PR #6760 prepared by @b0661 which introduced a reworked GPIO flags along the pinctrl API. It was closed in a meantime without any comment. As I argued in #2288 having only GPIO driver would make design more straightforward. However, if we want to have DTS compatible with / similar to Linux DTS then it would probably make sense to have also pinctrl driver. If we decide in the future to go the direction #6760 pointed to it would make sense to use GPIO flags Linux DTS is using.

In any case DTS properties used by the Linux and flag names proposed by @b0661 in #6760 seem to be more consistent (they are not complete since they cover only pin control part).

As an example, to encode pin driving mode we have
PINCTRL_CONFIG_DRIVE_PUSH_PULL
PINCTRL_CONFIG_DRIVE_OPEN_DRAIN
PINCTRL_CONFIG_DRIVE_OPEN_SOURCE

rather than
GPIO_PUSH_PULL
GPIO_SINGLE_ENDED
GPIO_LINE_SOURCE
GPIO_LINE_DRAIN
GPIO_OPEN_DRAIN
GPIO_OPEN_SOURCE

In case of PINCTRL_* defines we could get rid of '_CONFIG' part and would need to replace 'PINCTRL' with 'GPIO'. 'PINCTRL_CONFIG_DRIVE_PUSH_PULL' would become 'GPIO_DRIVE_PUSH_PULL'.

Some names used by PINCTRL_ flags are however questionable, e.g. 'PINCTRL_CONFIG_OUTPUT_LOW' means "A '1' in the output buffer drives the output to low level" which is a bit confusing. In this case our ACTIVE_LOW flag is better.

I have two general remarks:

  1. It's a long standing industry standard to use _SHIFT prefix to indicate bit field position. This convention is used by other parts of Zephyr API, Linux as well as various vendor provided header files. The GPIO API used _POS instead. Since we are reworking the flag names that's a good opportunity to align the naming.
  2. This rule is not written in stone but most of the register header files provided by vendors (heavy users of #define flags) do not provide _SHIFT and _MASK defines for single bit flags. _SHIFT and _MASK are mainly provided for bit fields only. As such meaning of a single bit flag is enforced to be 'feature enabled' if flag is present or 'default behavior' if flag is absent. Also some Zephyr APIs follow this rule. It makes it easier to test for the flag e.g. we have
	if (flags & GPIO_ACTIVE_HIGH) {
	}

rather than

	if ((flags & GPIO_ACTIVE_MASK) == GPIO_ACTIVE_HIGH) {
	}

the latter typically results in one more compare assembly instruction (depending on the flag values).

@b0661
Copy link
Collaborator

b0661 commented Jan 4, 2019

As @mnkp already mentioned, I'm in favour of using the Pinctrl definitions of Linux. This would ease the transition to a pinctl driver that acts as a backend to the GPIO driver and would allow to directly use the DTS Pinctrl definitions. I wrote a shim driver that translates from the current GPIO interface to my pinctrl driver. It showed Linux Pinctrl is feature complete with respect to the GPIO pin control interface.

The benefit of a third interface for pin control (not current Zephyr, not Linux, and not DTS compatible) seems to me less than to use the Linux Pinctrl definitions.

BTW: The Pinctrl driver is currently maintained OOT. The PR has to wait for the code generation issue to be decided/ solved.

@pabigot
Copy link
Collaborator Author

pabigot commented Jan 4, 2019

@mnkp I don't object to switching to _SHIFT from _POS, though _POS needs to remain as a legacy alias for anything where it already existed (a design goal of this PR is to not break existing code).

The CMSIS version of _Pos (_SHIFT) and _Msk (_MASK) is consistently provided regardless of whether the field is one or more than one bit. My preference is for consistency, but I am open to revisiting that. To discuss in the API call.

@b0661 The names like GPIO_OPEN_DRAIN that I used are directly from Linux's dt-bindings gpio.h. I'm not convinced those should be abandoned, especially before Zephyr gains in-tree pinctrl support.

I appreciate the feedback.

@b0661
Copy link
Collaborator

b0661 commented Jan 4, 2019

@pabigot

The names like GPIO_OPEN_DRAIN that I used are directly from Linux's dt-bindings gpio.h.

In Linux the pin control naming in the GPIO interface diverts from the naming in the Pinctrl interface !-). I'm in favour of the Pinctrl pin control naming as IMHO it is more versatile, complete and supported by DTS. Would be nice if we could avoid having different pin control defines to learn for DTS pinctrl and GPIO drivers.

I'm not convinced those should be abandoned, especially before Zephyr gains in-tree pinctrl support.

My main argument is not related to pinctrl support but to not re-invent the wheel if there is a proven complete set of pin control definitions supported by DTS already available.

include/dt-bindings/gpio/gpio.h Outdated Show resolved Hide resolved
include/dt-bindings/gpio/gpio.h Outdated Show resolved Hide resolved

/** Enable GPIO pin debounce. */
#define GPIO_DEBOUNCE (1U << GPIO_DEBOUNCE_POS)
Copy link
Member

Choose a reason for hiding this comment

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

Just a detail. I think it would be good to use unsigned values in all the shifts here. To avoid problems with undefined behavior in case the bit positions are altered in the future, or some definitions are modified after copying to some other module etc.

/** GPIO pin polarity is normal. */
#define GPIO_POL_NORMAL (0 << GPIO_POL_POS)
/** Mask to isolate the `GPIO_INT_LOW` and `GPIO_INT_HIGH` flags. */
#define GPIO_INT_DIR_MASK (0x03 << (GPIO_INT_POS + 2))
Copy link
Member

Choose a reason for hiding this comment

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

INT_DIR looks a bit strange to me (is it "interrupt direction"?). But will this mask be actually useful, where you see the need to isolate this two flags?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This would be used instead of GPIO_INT_DOUBLE_EDGE in conditions like this:

switch(flags & GPIO_INT_DIR_MASK) {
default: // invalid interrupt specification
case GPIO_INT_LOW: // detect low
case GPIO_INT_HIGH: // detect high
case GPIO_INT_LOW | GPIO_INT_HIGH: // detect both
}

GPIO_INT_DOUBLE_EDGE is currently equivalent to the last case, but it also allows some enhancements (e.g. its absence can indicate use of a low-precision level-based double edge detection approach on Nordic, useful when monitoring a large number of dry contacts).

Copy link
Member

Choose a reason for hiding this comment

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

But in the above code you could do (flags & (GPIO_INT_LOW | GPIO_INT_HIGH)), what would be equally readable (at least to me).

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I don't understand. All flags & (GPIO_INT_LOW | GPIO_INT_HIGH) tells you is that interrupts are supposed to be enabled. It doesn't let know how which direction to configure the hardware. (In the switch statement I didn't provide bodies for each case, but if they were there they'd all be different.)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

OK, I see; yes, you could do flags & (GPIO_INT_LOW | GPIO_INT_HIGH) instead of flags & GPIO_INT_DIR_MASK since they have the same effect. I think the flags & FOO_MASK idiom is more clear, as it's obvious you're interested in the FOO field without requiring the code extracting it to know all the bits that might be present in it, or how they combine to define distinct values.


/** Enable GPIO pin pull-up. */
#define GPIO_PUD_PULL_UP (1 << GPIO_PUD_POS)
/** GPIO pin polarity is inverted. */
Copy link
Member

Choose a reason for hiding this comment

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

Maybe it would be good to clarify on the occasion what the polarity inversion means? I recall I found it a bit confusing when reviewing the updated nRF GPIO driver, and I'm still not sure if I got it right. I assume that it's that bit of value 1 indicates the low state and bit of value 0 the high one, and this concerns reading inputs and writing outputs. But what about the interrupt triggers, are they also supposed to be applied inversely?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I'm just preserving the original comment (though that's not clear from the way the diff gets extracted). I don't believe the impact of using the flag can or should be generically described. The SX1509B has this feature, and it may not have the same behavior as Nordic or other devices (as I found with the drive strength flags, where the described behavior was wrong for SX1509B).

I will update the description with a note that the impact of the flag is driver-specific, including that it may be ignored.

@anangl
Copy link
Member

anangl commented Jan 7, 2019

@b0661 The names like GPIO_OPEN_DRAIN that I used are directly from Linux's dt-bindings gpio.h. I'm not convinced those should be abandoned, especially before Zephyr gains in-tree pinctrl support.

To me the set of definitions suggested by @mnkp, i.e.:
GPIO_DRIVE_PUSH_PULL
GPIO_DRIVE_OPEN_DRAIN
GPIO_DRIVE_OPEN_SOURCE
looks much better then the current proposal for the values related to driving modes. And it would be also more consistent - GPIO_LINE_SOURCE and GPIO_LINE_DRAIN make sense only in combination with GPIO_SINGLE_ENDED.

@pabigot pabigot force-pushed the issue/10339 branch 2 times, most recently from cecc5c1 to f60f9e3 Compare January 7, 2019 15:02
@pabigot
Copy link
Collaborator Author

pabigot commented Jan 7, 2019

The last two pushes rebased the initial PR onto current master, then applied the changes. The effective changes can be viewed from the last force-pushed link.

Below I've listed the changes I've made, and those I have not made. My hope is to discuss this in the API call tomorrow and get consensus so this can be merged. I have held work that requires a resolution to this issue.

@mnkp please take a look. I was uncertain from your comments exactly what changes resulted in a NACK, versus what you would have done differently and wanted reconsidered.

fixup: use _SHIFT instead of _POS for field offset defines

Note that the NXP HAL uses this suffix in combination with the GPIO_ prefix. There are currently no conflicts. (stm32_cube used _POS within the prefix, again without conflict.)

Previously defined _POS macros were:

  • GPIO_POL_POS
  • GPIO_PUD_POS
  • GPIO_DS_LOW_POS
  • GPIO_DS_HIGH_POS

None of those were used outside this file so the old macros have been eliminated rather than deprecated.

fixup: consistently use unsuffixed integer literals

This addresses @anangl's suggestion of using unsigned values in all the shifts here, except that I've changed my mind to use the unsuffixed values instead. The vast majority of existing code in Zephyr does not use the suffix, and I'm not going to add the suffix to hex constants:

#define GPIO_IO_MASK            (0x0FU << GPIO_IO_SHIFT)

even though it's technically necessary, because it just looks...bad.

fixup: remove misplaced doxygen grouping directive

Corrected, but I note that AFAICT the contents in this file are not processed by the doxygen infrastructure so I can't verify that they look right. Adding documentation generation is not in scope for this PR.

fixup: document that polarity configuration is driver-specific

nochange: remove _SHIFT and _MASK for single-bit fields.

I've kept these, though all are marked as internal API.

Where the field is single-bit this works fine:

if (flags & GPIO_ACTIVE_HIGH) { }

This would not work:

if (flags & GPIO_ACTIVE_LOW) { }

while this would:

if ((flags & GPIO_ACTIVE_MASK) == GPIO_ACTIVE_LOW) { }

I think enabling the more complete expression of the test by providing the _SHIFT and _MASK values for all fields regardless of size is more consistent.

nochange: use pinctrl definitions from Linux for GPIO flags

I think this is the biggest point of contention, but I'm staying with my initial implementation. This is mostly relevant to GPIO_PUSH_PULL, GPIO_OPEN_SOURCE, and GPIO_OPEN_DRAIN which were added, as changing pull-related flags is out of scope.

The opposing viewpoint refers to DTS and to pinctrl and suggests following Linux in its pinctrl DTS naming convention would be better. In opposition I note:

  • The primary role of these flags in Zephyr today is as arguments to the gpio_pin_configure() API call. That they may also be used in DT bindings is important, but this file originally was extracted from include/gpio.h to extend use of the flags to gpios bindings.
  • Not all SOCs support or need a pinctrl module, so a proposal to make the gpio API delegate to a pinctrl driver may not be viable.
  • Even if it is, Zephyr doesn't have a pinctrl module in tree, nor (AFAICT) an open proposal to add one, since drivers: add pinctrl device interface #6760 is closed.
  • The flags I've added match what Linux uses for GPIO configuration: conforming to existing practice is satisfied.

The fact that drive-strength configuration came into Zephyr with flags defined solely on the way Nordic supports it caused me grief when I needed to add the feature to SX1509B. I trust Linux's abstraction of generic description of potential GPIO configuration capabilities more than any other source, so I've followed their lead for new material.

@pabigot
Copy link
Collaborator Author

pabigot commented Jan 7, 2019

The following for GPIO_INT_DOUBLE_EDGE will also be added once the tests complete.

- * @deprecated Replace tests with `GPIO_INT_LOW | GPIO_INT_HIGH`.
+ * @deprecated Replace tests with check that flags masked by
+ * `GPIO_INT_DIR` is equal to `GPIO_INT_LOW | GPIO_INT_HIGH`.

@pabigot
Copy link
Collaborator Author

pabigot commented Feb 1, 2019

As discussed in dev-review I've implemented the proposed compromise described in
#11880 (comment) with the exception that I changed the new API names from that proposal to the following:

  • gpio_pin_assert() sets the pin to the active level
  • gpio_pin_deassert() sets the pin to the inactive level
  • gpio_pin_is_asserted() checks the active level

I've updated the SX1509B driver.

I've also updated the Nordic driver for the purposes of testing the updated blinky. This driver still needs to be revisited: the existing code applies the configured interrupt level to read and write operations, which was probably never right, and the polarity inversion affects interrupts, which was unspecified but is now incorrect if that is intended to be the solution for active low support.

All other GPIO drivers need to support for GPIO_ACCESS_PIN_LOGICAL.

Differences shown in the force push.

Options to consider:

In-order commits:

  • 1533d306d8 drivers: gpio: fix misuse of u8_t where bool is intended
  • 3f44dbc4eb dt-bindings: gpio: revise flags to support more complete control
  • 50034f9cb2 dt-bindings: gpio: revise drive strength flags for generality
  • d4c46fb245 drivers: gpio: sx1509b: remove ability to configure by port
  • 037889c9eb drivers: gpio: sx1509b: transition to new GPIO configuration
  • dca42e60f8 drivers: gpio: esp32: correct constant test for interrupt trigger
  • d9ee010236 tests: gpio: basic_api: correct constant test for interrupt trigger
  • fa52e294a2 drivers: gpio: add API to support logical read/write of pins
  • 75a2061597 drivers: gpio: sx1509b: add support for GPIO_ACTIVE_LOW
  • 6fc6817b2c drivers: gpio: nrfx: add support for GPIO_ACTIVE_LOW
  • bb8ba684e8 samples: blinky: use new flags to turn on first iteration

@mnkp
Copy link
Member

mnkp commented Feb 1, 2019

* `gpio_pin_assert()` sets the pin to the active level
* `gpio_pin_deassert()` sets the pin to the inactive level
* `gpio_pin_is_asserted()` checks the active level

How this approach is going to scale for the functions operating on a port? Are we going to have gpio_port_is_asserted()? For now we want to deprecate the port functions, but we will reintroduce them later.

All other GPIO drivers need to support for GPIO_ACCESS_PIN_LOGICAL.

We shouldn't add extra GPIO_ACCESS_ operators. That's what makes writing GPIO drivers so cumbersome and the code slow. As you pointed out in #11917 we need to optimize the this API and make it use much less compute cycles. GPIO API functions should be a thin layer between user and a hardware. The whole concept of the GPIO_ACCESS_ operators will have to go.

Remaining issues with this PR:

  • The GPIO_POL_* flags are not deprecated even though their purpose overlaps with GPIO_ACTIVE_LOW flag.
  • GPIO_DS_DISCONNECT_LOW, GPIO_DS_DISCONNECT_HIGH are not deprecated even though their purpose overlaps with newly introduced GPIO_OPEN_SOURCE, GPIO_OPEN_DRAIN.
  • GPIO_DIR_IN/GPIO_IO_IN_ENABLED and GPIO_DIR_OUT/GPIO_IO_OUT_ENABLED occupy the same bit field / are defined twice. That's confusing. Overall the concept of configuring input / output mode is not compatible with the pinctlr which with all likelihood will be added to Zephyr. We will need to rework flags once again or won't be able to reuse GPIO driver code in pinctrl driver.

@pabigot
Copy link
Collaborator Author

pabigot commented Feb 1, 2019

* `gpio_pin_assert()` sets the pin to the active level
* `gpio_pin_deassert()` sets the pin to the inactive level
* `gpio_pin_is_asserted()` checks the active level

How this approach is going to scale for the functions operating on a port? Are we going to have gpio_port_is_asserted()? For now we want to deprecate the port functions, but we will reintroduce them later.

We'll have to figure out what makes sense when the new API for port operations is determined.

All other GPIO drivers need to support for GPIO_ACCESS_PIN_LOGICAL.

We shouldn't add extra GPIO_ACCESS_ operators. That's what makes writing GPIO drivers so cumbersome and the code slow. As you pointed out in #11917 we need to optimize the this API and make it use much less compute cycles. GPIO API functions should be a thin layer between user and a hardware. The whole concept of the GPIO_ACCESS_ operators will have to go.

Rearchitecting the core GPIO driver API is not in scope.

Remaining issues with this PR:

  • The GPIO_POL_* flags are not deprecated even though their purpose overlaps with GPIO_ACTIVE_LOW flag.

There was nothing in the header that specified how the flag was meant to be used; existing use does not match the current semantics of GPIO_ACTIVE_LOW; and the flag is useful for drivers that have a hardware inversion feature. I don't consider the purpose to overlap.

That repeated, if the consensus is to mark them deprecated that's fine.

  • GPIO_DS_DISCONNECT_LOW, GPIO_DS_DISCONNECT_HIGH are not deprecated even though their purpose overlaps with newly introduced GPIO_OPEN_SOURCE, GPIO_OPEN_DRAIN.

Their semantics is different as the drain capability is configured by setting the output signal (since the API was designed to support Nordic devices). This is probably something that should be in PINCTRL anyway.

  • GPIO_DIR_IN/GPIO_IO_IN_ENABLED and GPIO_DIR_OUT/GPIO_IO_OUT_ENABLED occupy the same bit field / are defined twice. That's confusing. Overall the concept of configuring input / output mode is not compatible with the pinctlr which with all likelihood will be added to Zephyr. We will need to rework flags once again or won't be able to reuse GPIO driver code in pinctrl driver.

We'll have to address that when we have something to integrate. For now, this is the only place we can specify direction, and we need at least three, possibly four, combinations to fully support functionality and control power consumption.

A reasonable plan is to replace GPIO with an implementation compatible with PINCTRL whenever the latter gets attention, especially given the desires to remove things like drain/pull from the GPIO API and to change the driver structure away from access operators.

@pabigot pabigot added this to the v1.14.0 milestone Feb 1, 2019
@mnkp
Copy link
Member

mnkp commented Feb 4, 2019

How this approach is going to scale for the functions operating on a port? Are we going to have gpio_port_is_asserted()? For now we want to deprecate the port functions, but we will reintroduce them later.

We'll have to figure out what makes sense when the new API for port operations is determined.

Introducing a second set of functions to have the first set read/write logical pin value and the second set to read/write physical pin state makes sense. While making the driver code honor GPIO_ACTIVE_LOW flag is a simple, not computationally expensive operation there may be cases where user will not want to do that. But not caring about the names is a shortsighted approach. gpio_pin_assert() works nicely for the pins. I'm not so sure about the ports. Also, the new port functions will have to be named something different that gpio_port since it would match the old deprecated names. In Linux API function that reads logical pin value is called gpiod_get_value and the one that reads a physical state gpiod_get_raw_value. While we decided to comply with Linux DTS we don't need to comply with Linux API so that's just an example of a naming convention.

Rearchitecting the core GPIO driver API is not in scope.

To add support for the new functions we don't need to re-architect GPIO core. It's enough to add the new functionality so it does not depend on the old implementation. There are a lot of gpio drivers to update and test. It doesn't make sense to do all this work twice.

  • The GPIO_POL_* flags are not deprecated even though their purpose overlaps with GPIO_ACTIVE_LOW flag.

There was nothing in the header that specified how the flag was meant to be used;

And that's the primary reason why it should be deprecated. The main goal of this PR is to clean up GPIO flags. If there is a flag which is not well specified we either need to clarify its meaning or deprecate it. It really doesn't make sense to spend all this work only to have the API as ambiguous as it was before. Is there a use case which is not going to be covered by the remaining flags?

  • GPIO_DS_DISCONNECT_LOW, GPIO_DS_DISCONNECT_HIGH are not deprecated even though their purpose overlaps with newly introduced GPIO_OPEN_SOURCE, GPIO_OPEN_DRAIN.

Their semantics is different as the drain capability is configured by setting the output signal (since the API was designed to support Nordic devices). This is probably something that should be in PINCTRL anyway.

How exactly is the semantics of the flag different? What will be the functional difference in the implementation of GPIO_OPEN_DRAIN and GPIO_DS_DISCONNECT_HIGH flags for the Nordic driver?

  • GPIO_DIR_IN/GPIO_IO_IN_ENABLED and GPIO_DIR_OUT/GPIO_IO_OUT_ENABLED occupy the same bit field / are defined twice. That's confusing. Overall the concept of configuring input / output mode is not compatible with the pinctlr which with all likelihood will be added to Zephyr.

We'll have to address that when we have something to integrate.

Once again, that's a very shortsighted view. We know enough about the requirements of pinctrl driver to come up with matching implementation of GPIO driver.

A reasonable plan is to replace GPIO with an implementation compatible with PINCTRL whenever the latter gets attention, especially given the desires to remove things like drain/pull from the GPIO API and to change the driver structure away from access operators.

We've decided to honor Linux DTS and that puts some limits on how we design Zephyr drivers. E.g open drain functionality can be specified by both gpio and pinctrl nodes. As such it needs to be supported by gpio and pinctrl driver.

@pabigot
Copy link
Collaborator Author

pabigot commented Feb 4, 2019

@mnkp When I created this PR two months ago I described the constraints on my solution. They're right there in the introductory comment.

In response to your concerns I've added an API that implements support for negative logic via GPIO_ACTIVE_LOW, even though that's contrary to my goal of not requiring driver changes. I'm not terribly happy about that, but it's what you and maybe some of the rest of the Zephyr community seemed to want.

I understand that you wish I had done something much more significant and in line with how you would have solved the problem. I even agree in principal with much of what you've proposed. But that was not what I agreed to do.

I've donated as much time to this PR as I can afford. It stands or falls either as-is or---since you don't like them---with the commits for the new API dropped. Personally, I'd prefer dropped.

@mnkp
Copy link
Member

mnkp commented Feb 5, 2019

I've donated as much time to this PR as I can afford. It stands or falls either as-is or---since you don't like them---with the commits for the new API dropped. Personally, I'd prefer dropped.

I appreciate your effort however, as I've stated multiple times already, I believe that this PR should not be accepted without resolving all the problems. As it stands, it introduces just as many issues as it tries to fix.

GPIO configuration flags will move and some that used to be in the low
8 bits are now higher, resulting in implicit constant conversion
overflows.  Use a boolean data type to hold boolean values.

Signed-off-by: Peter A. Bigot <pab@pabigot.com>
Updated GPIO constants to provide more meaningful names and more
complete functionality.  The intent is that the presence and
interpretation of existing flags remains unchanged, although their
bitwise representations has.

Added signal type and direction flags matching Linux device tree
bindings, allowing open drain and open source to be distinguished from
push-pull signals.

Extended GPIO_DIR to support bidirectional configuration and setting an
initial value for outputs.

Extended GPIO_INT to more clearly identify level and edge
configurations.  Some redundancy is required to continue supporting the
existing single-bit GPIO_INT_DOUBLE_EDGE flag.

GPIO_INT_ACTIVE_foo renamed to GPIO_ACTIVE_foo since active level is
meaningful for non-interrupt inputs and output signals (e.g. RESETn is
an active low output).  GPIO_INT_DEBOUNCE similarly renamed to
GPIO_DEBOUNCE as debounce applies to non-interrupt inputs.  The legacy
macros are retained with their original values to avoid the need for
a global substitution change.

Other flags have changed bit representations but not interpretation.

Closes zephyrproject-rtos#10339

Signed-off-by: Peter A. Bigot <pab@pabigot.com>
The documentation and design for drive strength was based on Nordic GPIO
capabilities, and presumed that the default state was standard while the
alternate state was high.

The Semtech SX15088/SX1509B GPIO extender went the other way, with
default being high and alternate being low.

Rework the flags so that low and high are distinct from default.  Update
the two in-tree GPIO implementations that currently support drive
strength to make use of these flags.

Deprecate the ALT flag but define it to implement the documented
selection (high drive) in case it's being used by out-of-tree systems.

Signed-off-by: Peter A. Bigot <pab@pabigot.com>
It seems highly unlikely that anybody would want to configure all pins
to the same configuration.  Remove this feature to simplify the code
prior to rework.

Retain the ability to read and write all pins simultaneously as that has
use cases.

Signed-off-by: Peter A. Bigot <pab@pabigot.com>
Demonstrates an implementation that takes advantage of the new
flexibility in GPIO pin configuration.  New features include support for
bi-directional GPIO and drive strength.

Signed-off-by: Peter A. Bigot <pab@pabigot.com>
Interrupts default to trigger on level for historical reasons, so use of
GPIO_INT_LEVEL` as a mask results in a zero value.  Use a mask macro to
isolate the trigger configuration.

Signed-off-by: Peter A. Bigot <pab@pabigot.com>
Interrupts default to trigger on level for historical reasons, so use of
GPIO_INT_LEVEL` as a mask results in a zero value.  Use a mask macro to
isolate the trigger configuration.

Signed-off-by: Peter A. Bigot <pab@pabigot.com>
@pabigot
Copy link
Collaborator Author

pabigot commented Feb 5, 2019

I've rebased this, and removed the commits that added new API that would require driver support.

For overall goals and intent behind this PR please see the introductory comment. In summary, this attempts to clean up the flags to provide more extensibility to driver implementations that wish to support them, without impacting any in-tree use or changing the interpretation of flags like GPIO_POL_INV which were poorly described and have conflicting implementations.

  • ed85ff7 drivers: gpio: fix misuse of u8_t where bool is intended
  • c3d8936 dt-bindings: gpio: revise flags to support more complete control
  • c85f776 dt-bindings: gpio: revise drive strength flags for generality
  • d5ade36 drivers: gpio: sx1509b: remove ability to configure by port
  • 13ff5f8 drivers: gpio: sx1509b: transition to new GPIO configuration
  • e0e3d38 drivers: gpio: esp32: correct constant test for interrupt trigger
  • 241f9b4 tests: gpio: basic_api: correct constant test for interrupt trigger

@mnkp
Copy link
Member

mnkp commented Feb 6, 2019

Since the behavior of GPIO_ACTIVE_LOW was rolled back to the previous implementation here is a reminder of issues with this approach:

  • GPIO_ACTIVE_LOW was taken over from the Linux DTS. However, implementation proposed in this PR redefines its meaning. In Linux DTS GPIO_ACTIVE_LOW defines if pin works with positive or negative logic. This is straightforward to implement in a driver, may be supported by hardware, and results in a compact code.
  • As proposed in this PR user is required to query DT from within an application code to find out if a pin was configured as GPIO_ACTIVE_LOW. Then application is requied to handle pin signal inversion. It forces user to repeat the same code all over the source code wherever signal inversion is required or build his own utility functions to abstract the problem. This is cumbersome, error prone and a bad architectural design.

Remaining issues, mainly poorly specified and overlapping functionality, were listed here.

@anangl
Copy link
Member

anangl commented Feb 6, 2019

  • As proposed in this PR user is required to query DT from within an application code to find out if a pin was configured as GPIO_ACTIVE_LOW. Then application is requied to handle pin signal inversion. It forces user to repeat the same code all over the source code wherever signal inversion is required or build his own utility functions to abstract the problem. This is cumbersome, error prone and a bad architectural design.

To address this, we could provide simple wrappers that take the ..._GPIO_FLAGS value from DTS and accordingly modify values passed to gpio_pin_write and obtained from gpio_pin_read.
For instance:

static inline int gpio_pin_assert(struct device *port, u32_t pin,
				  int flags)
{
	u32_t active_value =
		(((flags & GPIO_ACTIVE_MASK) == GPIO_ACTIVE_LOW) ? 0 : 1);
	return gpio_write(port, GPIO_ACCESS_BY_PIN, pin, active_value);
}

and call it from the application like this:

	gpio_pin_assert(sw0_gpio_dev,
			DT_GPIO_KEYS_SW0_GPIO_PIN,
			DT_GPIO_KEYS_SW0_GPIO_FLAGS);

Would it be that cumbersome and error prone?

Similarly, we could provide macros which would help in configuring the interrupts to be triggered on active/inactive level or "to active/inactive" edge, to address @dwagenk's #10339 (comment).

@nordic-krch
Copy link
Contributor

my general comments to that PR or maybe more to flags are following:

  • i would prefer to split flags into 2 files. In this file keep only flags which are dedicated to be used by the api. All partial flags move to something like gpio_internal.h. As a user, i would like to see the list of available options. I don't care how they are build. I can even see something like #define GPIO_DIR_OUT_LOW __GPIO_INTERNAL_DIR_OUT_LOW. Shifting, masking is not important in the API as long as it works.
  • i would prefer to see flag like GPIO_POL_INV with clear explanation what that means. To me it means applying virtual or physical inverter on a pin. In that case GPIO_POL_INV would apply not only to reading and writing but also to interrupt configuration.
  • i'm not convinced about using ACTIVE_LOW ACTIVE_HIGH terminology in gpio driver. led or button or chip select can have ACTIVE_n property but on gpio driver level there is no active pin state. Because of that i prefer polarity inversion flag.

@mnkp
Copy link
Member

mnkp commented Feb 6, 2019

  • i'm not convinced about using ACTIVE_LOW ACTIVE_HIGH terminology in gpio driver. led or button or chip select can have ACTIVE_n property but on gpio driver level there is no active pin state. Because of that i prefer polarity inversion flag.

ACTIVE_LOW ACTIVE_HIGH names come from Linux DTS, otherwise their purpose is very much the same as Zephyr's GPIO_POL_*. In principle it's the same thing with a different name (*). Linux documents the flag usage in Active-High and Active-Low part of "Common GPIO Properties" chapter. If we didn't choose to use Linux DTS there would be no need to deprecate GPIO_POL_* flags. Theoretically we could also map include/dt-bindings/gpio/gpio.h to an independent set of Zephyr's defines in include/gpio.h. But I'm not sure if we need this extra layer.

(*) In Linux GPIO_ACTIVE_LOW flag is influencing the logical pin value as determined by gpiod_get_value, gpiod_set_value functions. It has no influence on interrupts, which always work with a physical pin level. In Zephyr the specification of GPIO_POL_INV flag was ambiguous and, depending on the driver implementation, the flag may or may not invert interrupt level/edge.

As for the proposal from @anangl to use

static inline int gpio_pin_assert(struct device *port, u32_t pin, int flags)

this could work. But also in this case we require application to keep around flags value every time we want to write/read from the pin, even if this is required only during the pin configuration phase. The approach does not allow inverting pin value in hardware (when supported). Also, since we introduced GPIO_POL_INV flag ourselves, it shows that having gpio driver invert pin polarity is actually a useful functionality.

@pabigot
Copy link
Collaborator Author

pabigot commented Feb 6, 2019

TSC decision 2019-02-06

GPIO PRs: Merge one or leave as-is for 1.14?
Resolution: Address as a whole for 1.15, including DTS and pin control
Motion - Anas; Second - Maureen; All in Favor (no opposed, abstentions)

This PR was not intended to address the whole issue, so it is no longer relevant.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area: API Changes to public APIs area: GPIO
Projects
None yet
Development

Successfully merging this pull request may close these issues.

gpio: Cleanup flags