GPIO(7) Standards, Environments, and Macros GPIO(7)

gpio, dpioGeneral and Dedicated Purpose I/O

General Purpose I/Os (gpio) are a common type of interface found in various microprocessors, ASICs, and other types of devices. A GPIO is functionality that hardware exposes that allows software to observe and control the state of a particular pin (or set of pins) on the device. These hardware pins may be connected to any number of different things ranging from enable and control lines, sensors, LEDs, or even a more complex hardware peripheral.

Software control of a GPIO generally gives you the ability to do things like:

The OS provides a dedicated subsystem for manipulating and interfacing with GPIOs in hardware. The rest of this manual goes into details about that subsystem, ways to constrain GPIOs for safety, and what tooling is available.

Device drivers register with the kernel GPIO subsystem which the OS organizes as a series of named GPIO Controllers. Each controller itself then enumerates a series of GPIOs that have a name and controller-specific ID which is used to manipulate the GPIO. In general, while tooling requires IDs, names are what GPIO controllers guarantee the stability of.

Each GPIO has a series of attributes that describe the different properties of the GPIO. The attributes of a GPIO are specific to the device driver that provides it. While most GPIO controllers provide similar features, the specifics often vary quite particularly. Let's take the example of control over integrated pull-up and pull-down resistors. Some hardware offers a very simple option of indicating whether there is a pull-up, pull-down, or none while others require you to specify the strength of the resistor, which of course varies from part to part (and some devices only let you control the strength in one direction), and then some hardware actually lets you enable both the pull-up and pull-down.

For concepts which are trickier to deal with, such as the drive strength or rise and fall times of signals, this turns into a larger mess. And of course, the last thing that is important to keep in mind is that not all GPIOs on a given controller are necessarily the same. As such, the OS does not try to commonize the different attributes. Instead, for each attribute, the following information is provided:

  • The attribute's name. A colon character (“:”) separates the provider from the attribute itself.
  • The attribute's current value.
  • The set of possible values are for an attribute. This allows for discoverability of what is allowed.
  • Whether the attribute is read-only or read-write.

To make this concrete, here is an example of a GPIO's attributes as provided by the gpioadm(8) utility:

ATTR                  PERM  VALUE            POSSIBLE
name                  r-    EGPIO9_3         --
zen:pad_name          r-    BP_AGPIO9_3      --
zen:pin               r-    CY41             --
zen:pad_type          r-    gpio             --
zen:caps              r-    --               --
zen:output_driver     r-    push-pull        push-pull
zen:voltage           r-    3.3V             3.3V
zen:drive_strength    rw    40R              40R,80R
zen:output            rw    disabled         disabled,low,high
zen:input             r-    low              low,high
zen:debounce_mode     rw    none             none,keep-low-glitch,
                                             keep-high-glitch,
                                             remove-glitch
zen:debounce_unit     rw    61us             61us,244us,15.6ms,62.5ms
zen:debounce_count    rw    0x0              --
zen:trigger_mode      rw    edge/high        edge/high,edge/low,
                                             edge/both,level/high,
                                             level/low
zen:status            r-    --               --
zen:pull              rw    disabled         disabled,down,4k-up,
                                             8k-up
zen:raw_reg           r-    0x440000         --

Multiple attributes of a GPIO may be set at once.

Incorrect usage of GPIOs your system. The way that GPIOs are used will vary from product to product and just because a GPIO is used in some way on one system has no bearing on another. The GPIO subsystem on its own cannot know what the hardware designer intended for a pin and while one may have the ability to make a pin begin driving an output, if it is actually expected to be an input this can lead to two devices both trying to drive a connected signal and irreparably damaging both devices!

To deal with this more complicated reality and the fact that GPIO attributes vary wildly, the operating system has a second concept which are called Dedicated Purpose I/Os (DPIO) which is designed to be used more directly by both software in the platform (e.g. fault management) or by other end-user applications.

A DPIO creates a named character device in /dev/dpio that has the following properties:

  • Reading the device (if allowed) returns the current pin's status. In addition, if the controller supports interrupts, the device can be polled through poll(2), port_get(3C), etc. for when its input changes.
  • Writing to the device (if allowed) changes the output level, allowing one to set it to one of disabled, low, or high.
  • The device can have a semantic name that reflects its actual usage and abstracts away the question of which pin on which GPIO controller it is found on. A DPIO's name is global to the system while a GPIO's name is scoped to a GPIO controller.
  • Exclusive access to the device can be granted by using the O_EXCL flag with open(2). If a process has exclusive access, then no one else will be able to open the device and read or write to it. Similarly, if one or more processes have the device open, a request for exclusive access will fail. In both cases EBUSY is returned by the underlying kgpio driver.
  • All other attributes of the underlying GPIO are frozen and cannot be manipulated.
  • The DPIO can also be constrained such that it can only be used by the kernel and accessible by layered opens through functions such as ldi_open_by_name(9F).

The GPIO and DPIO subsystems are evolving interfaces in the operating system. To discover controllers, GPIOs, and DPIOs, privileged users can use the gpioadm(8) utility. This allows for getting and setting the attributes of GPIOs as well as creating and destroying DPIOs from GPIOs.

gpioadm(8) is implemented in terms of a system internal library called libxpio which can be used to manipulate and get information about GPIOs and DPIOs. This is itself built on-top of private user/kernel interfaces provided by the kgpio(4D) driver.

The DPIO interface is provided in <sys/gpio/dpio.h>. All DPIOs show up under /dev/dpio in the file system and the character devices support the various read(2) and write(2) families of operations. Unlike normal files, the DPIO character devices are not seekable. Both reads and writes must be exactly 4 bytes in size. Reads return the dpio_input_t enumeration which currently has two values (though more may be added in the future):

This indicates that a logical low value was read in from the underlying GPIO.
This indicates that a logical high value was read in from the underlying GPIO.

It's worth noting that any hardware-specific changes to the input values such as polarity inversion control will have already been applied to the value. The actual thresholds for what constitutes a logic high and low are device dependent.

The write interface uses the dpio_output_t enumeration values which are:

This indicates that a logical low value should be driven on the pin.
This indicates that a logical high value should be driven on the pin.
Disables the generation of any output. This is particularly useful for open-drain based systems.

Like with reads, the voltage values are device dependent. In addition, someone who has the device open may use the DPIO_IOC_CUROUT ioctl to obtain information about what the current output value is set to.

All DPIO interfaces are still evolving and subject to change.

Many complex SoCs (system-on-chip) have a way of switching what internal peripheral is actually using a specific pin. This peripheral may be a GPIO or it could be an entirely different device like a SPI controller, serial port, I2C, or more. At this time, the GPIO subsystem on its own does not manipulate this underlying mux and consumers of the platform will need to be aware of that. In the future, where appropriate for the controller, this subsystem will be integrated with this underlying mux to create a more holistic and useful experience.

GPIO providers are kernel drivers that interface with a hardware GPIO controller. They expose the attributes, which are passed around as a series of nvlist_t (libnvpair(3LIB)) structures. Attributes are either passed as an nvlist string or uint32 and then each attribute also contains optional metadata around the attribute's permissions and what values are possible for this particular GPIO. Attributes are allowed to vary from GPIO to GPIO. libxpio provides logic to translate anything that isn't in a string-form into something that is human-readable. This was done to allow for providers and programmatic interfaces to work in a more natural way (e.g. using enumerations and other numeric values) while still providing consumers something useful to use.

The core kernel GPIO driver, kgpio(4D), takes care of dealing with the traditional character device interfaces, minor nodes, and all of the logic around creating, destroying, and managing DPIOs. This in turn allows the GPIO provider drivers to be simpler and focus on the interface to hardware.

poll(2), read(2), write(2), port_get(3C), libnvpair(3LIB), kgpio(4D), gpioadm(8), ldi_open_by_name(9F)

September 17, 2022 OmniOS