Advertisement

Linux device driver development: The pin control subsystem

John Madieu

September 09, 2018

John MadieuSeptember 09, 2018

Editor's Note: The embedded Linux kernel already play a vital role in embedded systems and stands to grow in importance in serving the diverse requirements of the Internet of Things (IoT). In turn, device drivers provide the critical link between applications and IoT devices themselves. In Linux Device Drivers Development, author John Madieu offers a comprehensive look at development of these drivers, combining detailed explanation with plenty of code samples. 

This excerpt, Chapter 14 from the book, focuses pin control and GPIOs — an area of particular importance to embedded systems developers looking to interact with custom hardware devices. This first installment of this excerpt introduces the pin control subsystem. 

Adapted from Linux Device Drivers Development, by John Madieu.


Chapter 14. Pin Control and GPIO Subsystem
By John Madieu

Most embedded Linux driver and kernel engineers write using GPIOs or play with pins multiplexing. By pins, I mean outgoing line of component. SoC does multiplex pins, meaning that a pin may have several functions, for example, MX6QDL_PAD_SD3_DAT1 in arch/arm/boot/dts/imx6dl-pinfunc.h can be either an SD3 data line 1, UART1's cts/rts, Flexcan2's Rx, or normal GPIO.

The mechanism by which one choses the mode a pin should work on is called pin muxing. The system responsible for is called the pin controller. In the second part of the chapter, we will discuss the General Purpose Input Output (GPIO), which is a special function (mode) in which a pin can operate.

In this chapter, we will:

  • Walk through the pin control subsystem, and see how one can declare their nodes in DT

  • Explore both legacy integer-based GPIO interfaces, as well as the new descriptor- based interface API

  • Deal with GPIO mapped to IRQ

  • Handle sysfs interfaces dedicated to GPIOs

Pin control subsystem

The Pin control (pinctrl) subsystem allows managing pin muxing. In the DT, devices that need pins to be multiplexed in a certain way must declare the pin control configuration they need.

The pinctrl subsystem provides:

  • Pin multiplexing, which allows for reusing the same pin for different purposes, such as one pin being a UART TX pin, GPIO line, or HSI data line. Multiplexing can affect groups of pins or individual pins.

  • Pin configuration, applying electronic properties of pins such as pull-up, pull- down, driver strength, debounce period, and so on.

The purpose of this book is limited to using functions exported by the pin controller driver, and does not not how to write a pin controller driver.

Pinctrl and the device tree

The pinctrl is nothing but a way to gather pins (not only GPIO), and pass them to the driver. The pin controller driver is responsible for parsing pin descriptions in the DT and applying their configuration in the chip. The driver usually needs a set of two nested nodes to describe group of pins configurations. The first node describes the function of the group (what purpose the group will be used for), the second holds the pins configuration.

How pin groups are assigned in the DT heavily depends on the platform, and thus the pin controller driver. Every pin control state is given an integer ID starting at 0 and contiguous. One can use a name property, which will be mapped on top of IDs, so that the same name always points to the same ID.

Each client device's own binding determines the set of states that must be defined in its DT node, and whether to define the set of state IDs that must be provided, or whether to define the set of state names that must be provided. In any case, a pin configuration node can be assigned to a device by means of two properties:

  • pinctrl-<ID>: This allows for giving the list of pinctrl configurations needed for a certain state of the device. It is a list of phandles, each of which points to a pin configuration node. These referenced pin configuration nodes must be child nodes of the pin controller that they configure. Multiple entries may exist in this list so that multiple pin controllers may be configured, or so that a state may be built from multiple nodes for a single pin controller, each contributing part of the overall configuration.

  • pinctrl-name: This allows for giving a name to each state in a list. List entry 0 defines the name for integer state ID 0, list entry 1 for state ID 1, and so on. The state ID 0 is commonly given the name default. The list of standardized states can be found in include/linux/pinctrl/pinctrl-state.h.

The following is an excerpt of DT, showing some device nodes, along with their pin control nodes:

In the preceding example, a pin configuration is given in the form <PIN_FUNCTION> <PIN_SETTING>. For example:

     MX6QDL_PAD_DISP0_DAT15__GPIO5_IO09 0x80000000

MX6QDL_PAD_DISP0_DAT15__GPIO5_IO09 represents the pin function, which is GPIO in this case, and 0x80000000 represents the pin settings. For this line,

     MX6QDL_PAD_EIM_D25__UART3_RX_DATA 0x1b0b1

MX6QDL_PAD_EIM_D25__UART3_RX_DATA represents the pin function, which is the RX line of UART3, and 0x1b0b1 represent is settings.

The pin function is a macro whose value is meaningful for pin controller driver only. These are generally defined in header files located in arch/<arch>/boot/dts/. If one uses a UDOO quad, for example, which has an i.MX6 quad core (ARM), the pin function header would be arch/arm/boot/dts/imx6q-pinfunc.h. The following is the macro corresponding to the fifth line of the GPIO5 controller:

     #define MX6QDL_PAD_DISP0_DAT11__GPIO5_IO05  0x19c 0x4b0 0x000 0x5 0x0

<PIN_SETTING> can be used to set up things like pull-ups, pull-downs, keepers, drive strength, and so on. How it should be specified depends on the pin controller binding, and the meaning of its value depends on the SoC data sheet, generally in the IOMUX section. On i.MX6 IOMUXC, only lower than 17 bits are used for this purpose.

These preceding nodes are called from the corresponding driver-specific node. Moreover, these pins are configured during corresponding driver initialization. Prior to selecting a pin group state, one must get the pin control first using the pinctrl_get() function, call pinctrl_lookup_state() in order to check whether the requested state exist or not, and finally pinctrl_select_state() to apply the state.

The following is a sample that shows how to get a pincontrol and apply its default configuration:

One usually performs such steps during driver initialization. The suitable place for this code could be within the probe() function.

pinctrl_select_state() internally calls pinmux_enable_setting(), which in turn calls the pin_request() on each pin in the pin control node.

A pin control can be released with the pinctrl_put() function. One can use the resource- managed version of the API. That said, one can use pinctrl_get_select(), given the name of the state to select, in order to configure pinmux. The function is defined in include/linux/pinctrl/consumer.h as follows:

   static struct pinctrl *pinctrl_get_select(struct device *dev,
                                const char *name)

where *name is the state name as written in pinctrl-name property. If the name of the state is default, one can just call pinctr_get_select_default() function, which is a wrapper around pinctl_get_select():

   static struct pinctrl * pinctrl_get_select_default(
                                   struct device *dev)
   {
      return pinctrl_get_select(dev, PINCTRL_STATE_DEFAULT);
   }

Let us see a real example in a board-specific dts file (am335x-evm.dts):

   dcan1: d_can@481d0000 {
       status = "okay";
       pinctrl-names = "default";
       pinctrl-0 = <&d_can1_pins>;
   };

And in the corresponding driver:

   pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
   if (IS_ERR(pinctrl))
       dev_warn(&pdev->dev,"pins are not configured from the driver");

The pin control core will automatically claim the default pinctrl state for us when the device is probed. If one defines an init state, the pinctrl core will automatically set pinctrl to this state before the probe() function, and then switch to the default state after probe() (unless the driver explicitly changed states already).

The next installment will discuss the GPIO subsystem.

Reprinted with permission from Packt Publishing. Copyright © 2017 Packt Publishing


John Madieu is an embedded Linux and kernel engineer living in France, in Paris. His main activities consist of developing drivers and Board Support Packages (BSP) for companies in domains such as automation, transport, healthcare, energy, and the military. John works at EXPEMB, a French company that is a pioneer in electronical board design based on computer-on-module, and in embedded Linux solutions. He is an open source and embedded systems enthusiast, convinced that it is only by sharing knowledge that one learns more.

 

Loading comments...