[Zephyr-devel] The topic-gpio branch has been merged to master


Piotr Mienkowski <piotr.mienkowski@...>
 

Hi all,

I would like to provide a bit more information about the recent change
to the GPIO API.

The main drive behind the rework was to support GPIO DTS bindings
https://github.com/torvalds/linux/blob/master/include/dt-bindings/gpio/gpio.h
as known from Linux DTS. That meant introduction of new flags:

- GPIO_ACTIVE_HIGH, GPIO_ACTIVE_LOW
- GPIO_OPEN_DRAIN, GPIO_OPEN_SOURCE
- GPIO_PULL_UP, GPIO_PULL_DOWN

All of the above flags are meant to be located in the DTS, typically at
the board level. This allows to decouple application / driver code from
the properties of the hardware they happen to work with.

As an example, if gpio pin is connected to a button it could be
described in the board DTS file as follows:

    button0: button_0 {
        gpios = <&gpio0 17 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>;
        label = "Push button 0";
    };

It means that the GPIO driver will need to enable a pull up on a pin and
that the pin is active low. All this is transparent to the application
which simply needs to pull in the DTS flags when configuring the pin. To
access the flags generated from the DTS at the application level it is
best to use the so called DT_ALIAS defines. The special DTS aliases node

aliases {
    sw0 = &button0;
};

is a convenient way to give generic application level names to specific
DTS nodes.

The resulting call in the application code could look like this

    gpio_pin_configure(dev_button, DT_ALIAS_SW0_GPIOS_PIN,
DT_ALIAS_SW0_GPIOS_FLAGS | GPIO_INPUT);

To access the flags at the driver level we should ideally be using the
so called DT_INST defines. But here the situation is a little bit more
complicated.

The two new flags that had the biggest impact on the shape of the new
GPIO API are GPIO_ACTIVE_HIGH and GPIO_ACTIVE_LOW. They define pin
active level known in the new API as logical pin level. From the
documentation

"If pin is configured as Active High, a low physical level will be
interpreted as logical value 0. If pin is configured as Active Low, a
low physical level will be interpreted as logical value 1."

At the application level the two flags encode cause -> effect
relationship. In the above example of a button the GPIO_ACTIVE_LOW flag
means that regardless of the specific design of the hardware button and
the circuitry surrounding it if the button is pressed the pin will see
low physical value. This relationship should be defined in DTS bindings.

This level of abstraction is useful not only when building a generic
application that reacts to the press of a button or has to turn on an
LED but also when building a driver for a well documented external
hardware module. Such module, e.g. a sensor connected over I2C bus, can
provide an active high interrupt pin that should be routed to a GPIO pin
on a SoC. A driver for the hardware module which is using pin logical
rather than pin physical level allows for the possibility that the
interrupt signal from the hardware module is routed to the GPIO pin via
an inverter, e.g. because external module is in a different power
domain. Once again the active level of the interrupt pin will be defined
in DTS and allows the driver code to be decoupled from the specifics of
the PCB design.

An application can get / set pin logical level by calling gpio_pin_get,
gpio_pin_set functions. It is still possible to access pin physical
level, if required, by calling gpio_pin_get_raw, gpio_pin_set_raw functions.

All these concepts are not entirely new. The old GPIO API provided flags
such as GPIO_INT_ACTIVE_LOW and GPIO_POL_INV which aimed to achieve
similar goal. However, they were not very well documented and not
consistently implemented.

Other modifications to the GPIO API include:

- Interrupt configuration was moved to the dedicated
gpio_pin_interrupt_configure() function. Configuring interrupts via
gpio_pin_configure() is still supported but this feature will be removed
in future releases.

- New set of flags allows to define arbitrary interrupt configuration
(if supported by the driver) based on pin physical or logical levels.
The include/drivers/gpio.h contains in fact two sets of interrupt
related flags, one targeted at the driver developers and the other at
the users of the API. Only the latter is documented at
https://docs.zephyrproject.org/latest/reference/peripherals/gpio.html
and should be used by the applications.

- New set of port functions that operate simultaneously on multiple pins
that belong to the same controller.

- New set of flags to configure pin as input, output or in/out as well
as set output initial state.

Majority of the old GPIO API has been deprecated and, to limit impact on
the code size, re-implemented in terms of the new API. While the care
was taken to preserve backward compatibility due to the scope of the
work it was not possible to fully achieve this goal.

We recommend to switch to the new GPIO API as soon as possible.

Areas where the deprecated API may behave differently to the original
old implementation are:

- Configuration of pin interrupts, especially involving
GPIO_INT_ACTIVE_LOW and GPIO_POL_INV flags.

- Behavior of gpio_pin_configure() when invoked without interrupt
related flags. In the new implementation of this deprecated
functionality the interrupts remain unmodified. In the original
implementation some of the GPIO drivers would disable the interrupts.

Regards,
Piotr