# Configure the pinctrl driver

The Pinctrl subsystem in the Qualcomm Linux kernel manages and configures pins used for general-purpose input/output (GPIO), interintegrated circuit (I2C), serial peripheral interface (SPI), and other hardware interfaces.

Pinctrl configurations, such as **pin muxing** and **pin groupings**,
are managed in the device-specific pinctrl drivers, where the drivers list all the available pins and functions.

For example, the corresponding driver for QCS6490 is available in the `kernel-src/drivers/pinctrl/qcom/pinctrl-sc7280.c` file.

Note

For more information about additional Qualcomm SoC pinctrl drivers, see [Pinctrl Drivers](https://github.com/torvalds/linux/tree/master/drivers/pinctrl/qcom).

The following are the pinctrl data objects:

Table: Pinctrl data objects

| Variable | Description |
| --- | --- |
| static const struct pinctrl_pin_desc sc7280_pins<br>    Copy to clipboard | Enumerates all pins and their names |
| static const struct msm_pingroup sc7280_groups<br>    Copy to clipboard | Defines the available muxed functions for the group of GPIO pins |
| enum sc7280_functions<br>    Copy to clipboard | Lists all the available functions as enum values |

For more information about the supported functions of the respective SoC
pinctrl binding documentation for QCS6490, see [pinctrl binding
documentation](https://www.kernel.org/doc/Documentation/devicetree/bindings/pinctrl/qcom%2Csc7280-pinctrl.yaml).

**Function selection**

For a `sc7280_functions` data object, one or multiple GPIO pins are
used as a function and must be registered to the device tree and passed
to the right device node.

During system boot, the kernel pinctrl infrastructure registers the
functions.

The following example shows the kernel configuration infrastructure:

tlmm: pinctrl@f100000 {
        compatible = "qcom,sc7280-pinctrl";
        :
        :
        :
        :
        qup_spi0_data_clk: qup-spi0-data-clk-state {
            pins = "gpio0", "gpio1", "gpio2";
            function = "qup00";
        };
    
        qup_spi0_cs: qup-spi0-cs-state {
            pins = "gpio3";
            function = "qup00";
        };
    
        qup_spi1_data_clk: qup-spi1-data-clk-state {
            pins = "gpio4", "gpio5", "gpio6";
            function = "qup01";
        };
    
        qup_spi1_cs: qup-spi1-cs-state {
            pins = "gpio7";
            function = "qup01";
        };
        :
        :
        :
        :
    
    };

        spi0: spi@980000 {
            compatible = "qcom,geni-spi";
            reg = <0 0x00980000 0 0x4000>;
            clocks = <&gcc GCC_QUPV3_WRAP0_S0_CLK>;
            clock-names = "se";
            pinctrl-names = "default";
            pinctrl-0 = <&qup_spi0_data_clk>, <&qup_spi0_cs>;
            interrupts = <GIC_SPI 601 IRQ_TYPE_LEVEL_HIGH>;
            #address-cells = <1>;
            #size-cells = <0>;
            power-domains = <&rpmhpd SC7280_CX>;
            operating-points-v2 = <&qup_opp_table>;
            interconnects = <&clk_virt MASTER_QUP_CORE_0 0 &clk_virt SLAVE_QUP_CORE_0 0>,
                    <&gem_noc MASTER_APPSS_PROC 0 &cnoc2 SLAVE_QUP_0 0>;
            interconnect-names = "qup-core", "qup-config";
            dmas = <&gpi_dma0 0 0 QCOM_GPI_SPI>,
                <&gpi_dma0 1 0 QCOM_GPI_SPI>;
            dma-names = "tx", "rx";
            status = "disabled";
        };
    Copy to clipboard

## Configure the GPIO usage

GPIO pin configuration requires the following two settings. The settings define a GPIO pin state and make those pins available for any input/output activity.

- Mux: The mux setting requires selecting the function name that is mapped from the set of available functions in the SoC-specific pinctrl driver.

    For more information about pinctrl, see [Pinctrl configuration](https://docs.qualcomm.com/doc/80-70020-3/topic/pinctrl-configuration.html#pinctrl-configuration).
- Configuration: The configuration aspect requires setting the drive strength and bias property.

The following examples show how the two settings define the GPIO pin using the following procedures:

1. Define the pin configuration in the device tree:

bt_en: bt-en-state {
           pins = "gpio85";
           function = "gpio";
           output-low;
           bias-disable;
        };
        Copy to clipboard
2. Configure the device node or intellectual property (IP) block in the device tree:

bluetooth: bluetooth {
           compatible = "qcom,wcn6750-bt";
           pinctrl-names = "default";
           pinctrl-0 = <&bt_en>, <&sw_ctrl>;
           enable-gpios = <&tlmm 85 GPIO_ACTIVE_HIGH>;
           swctrl-gpios = <&tlmm 86 GPIO_ACTIVE_HIGH>;
           vddaon-supply = <&vreg_s7b_0p9>;
           vddbtcxmx-supply = <&vreg_s7b_0p9>;
           vddrfacmn-supply = <&vreg_s7b_0p9>;
           vddrfa0p8-supply = <&vreg_s7b_0p9>;
           vddrfa1p7-supply = <&vreg_s1b_1p8>;
           vddrfa1p2-supply = <&vreg_s8b_1p2>;
           vddrfa2p2-supply = <&vreg_s1c_2p2>;
           vddasd-supply = <&vreg_l11c_2p8>;
           max-speed = <3200000>;
        Copy to clipboard
3. The driver code must use generic APIs to select and register their
GPIO configurations within the pinctrl configurations.

    The following is an example of available APIs:

> 
> 
> devm_gpiod_get_optional(&serdev->dev, "enable", GPIOD_OUT_LOW);
>         
>         /**
>         * devm_gpiod_get_optional - Resource-managed gpiod_get_optional()
>         * @dev: GPIO consumer
>         * @con_id: function within the GPIO consumer
>         * @flags: optional GPIO initialization flags
>         *
>         * Managed gpiod_get_optional(). GPIO descriptors returned from this function
>         * are automatically disposed on driver detach. See gpiod_get_optional() for
>         * detailed information about behavior and return values. */
>         
>          gpiod_set_value_cansleep(qcadev->bt_en, 0);
>         
>         /**
>         * gpiod_set_value_cansleep() - assign a gpio's value
>         * @desc: gpio whose value will be assigned
>         * @value: value to assign
>         *
>         * Set the logical value of the GPIO, i.e. taking its ACTIVE_LOW status into
>         * account
>         *
>         * This function is to be called from contexts that can sleep.
>         */
>         Copy to clipboard

**GPIO as interrupt request (IRQ)**

To set the GPIO as an IRQ, use the following procedure:

1. Configure the GPIO pin in the DTS file:

    1. Set the properties and the function for the GPIO pin.
    2. Set the pin to use GPIO 55 for the `qup_se_l3()` function with the following configurations:

qupv3_se3_rx: qupv3-se3-rx-state {
               pins = "gpio55";
               function = "qup03"; // To be taken from available from functions.
               drive-strength = <2>;
               bias-disable;
            };
            Copy to clipboard
2. Create a DT entry like the previous configuration for the device node where you want set the GPIO as an IRQ.

    In the following example, the GPIO55 is configured as an IRQ with the parent as a top-level mode multiplexer (TLMM) and the level is set to high.

interrupts-extended = <&tlmm 55 IRQ_TYPE_LEVEL_HIGH>;
        Copy to clipboard
3. The driver must read the value and register it as an interrupt to the
generic interrupt controller (GIC) using the `request_irq` API
specifying the interrupt service register (ISR) and IRQ flags.

irq_no = platform_get_irq(pdev, 1);
        Copy to clipboard

## Configure GPIOS to generate clock or pulse width modulation

Configure any GPIO with `GP_CLK` as an alternalte functionality to get clock or pulse width modulation (PWM).

Note

The following procedure is applicable to QCS6490 SoCs.

For more information about how to find a GPIO with the `GP_CLK` function, see the [Pin descriptions](https://docs.qualcomm.com/bundle/publicresource/topics/80-23889-1/pin-definitions.html#sub$pin-descriptions:~:text=and%20available%20configurations.-,Table%20%3A%20Pin%20descriptions%20%E2%80%93%20general%2Dpurpose%20input/output%20ports,-Pad%20number).

1. Add GPIO configuration node in `kernel/arch/arm64/boot/dts/qcom/sc7280.dtsi` file.

> 
> 
> +gpio_pwm_default: gpio_pwm_default {
>     +       mux {
>     +               pins = "gpio42";
>     +               function = "gcc_gp1";    // search "gcc_gp" in "kernel/drivers/pinctrl/qcom/pinctrl-sc7280.c", From this we can find out which GPIO's has GP_CLK functionality
>     +       };
>     +
>     +       config {
>     +               pins = "gpio42";
>     +               bias-disable; /* No PULL */
>     +               drive-strength = <8>; /* 2 MA */
>     +       };
>     +};
>     Copy to clipboard

2. Define devicetree node in `kernel/arch/arm64/boot/dts/qcom/sc7280.dtsi` file.

+beeper: beeper {
        +       compatible = "gpio-beeper";
        +       pinctrl-names = "default";
        +       pinctrl-0 = <&gpio_pwm_default>;
        +       clocks = <&clock_gcc GCC_GP1_CLK>; //clock_gcc is gcc clk device node, GCC_GP1_CLK index which defined in "kernel/include/dt-bindings/clock/qcom,gcc-sc7280.h"
        +       clock-names = "gpio-pwm-clk";
        +};
        Copy to clipboard
3. Add the following code in the device driver:

+#include <linux/clk.h>
        +#include <linux/io.h>
        ...
        + struct clk *pclk;
        + struct rcg_clk *gp1_rcg_clk;
        + int ret;
        +
        + pclk = devm_clk_get(&pdev->dev, "gpio-pwm-clk");
        + ret = clk_set_rate(pclk, 50000000); // please check the freq table in kernel/drivers/clk/qcom/gcc-sc7280.c, the freq can be found in the freq table of GCC_GP1_CLK.
        + if (ret)
        +     printk("clk set rate fail, ret = %d\n", ret);
        +
        + ret = clk_prepare_enable(pclk);  // By default this will enable clock as PWM with 50% duty cycle.
        + if (ret)
        +     printk("%s: clk_prepare error!!!\n", __func__);
        + else
        +     printk("%s: clk_prepare success!\n", __func__);
        +
        Copy to clipboard
4. If you do not use clock or PWM, call `clk_disable_unprepare()` to disable the clock to save power.

Note

Ensure to call `clk_prepare_enable()` first before calling `clk_disable_unprepare()`.
5. To generate the required duty cycle, call `clk_set_duty_cycle()` API after `clk_prepare_enable` API.

## Configure GPIOS from the user space

Use the `libgpiod` library from the user space to control the GPIOS for better performance.

1. To compile and push `libgpiod` library from the host computer, do the following:

    1. To install Arm^®^ (Arm64) toolchain, run the following commands:

sudo apt install gcc-aarch64-linux-gnu
            Copy to clipboard

sudo apt install binutils-aarch64-linux-gnu
            Copy to clipboard
    2. To download and extract the libgpiod source code from [libgpiod 1.6.4.tar.xz](https://www.kernel.org/pub/software/libs/libgpiod/libgpiod-1.6.4.tar.xz), run the following commands:

wget https://www.kernel.org/pub/software/libs/libgpiod/libgpiod-1.6.4.tar.xz
            Copy to clipboard

tar xvf libgpiod-1.6.4.tar.xz
            Copy to clipboard

cd libgpiod-1.6.4
            Copy to clipboard
    3. To configure the sources for static linking, run the following command:

./configure --enable-tools=yes --build x86_64-pc-linux-gnu --host aarch64-linux-gnu CFLAGS="-static -static-libgcc -Wl,-static,--start-group,/usr/lib/gcc-cross/aarch64-linux-gnu/7.5.0/libgcc.a,/usr/lib/gcc-cross/aarch64-linux-gnu/7.5.0/libgcc_eh.a,/usr/aarch64-linux-gnu/lib/libc.a,--end-group"
            Copy to clipboard
    4. To compile the library, run the following command:

make
            Copy to clipboard

Note

Compiling creates linked binaries.
    5. To build statically linked binaries, run the following commands:

aarch64-linux-gnu-gcc -static -o tools/gpiodetect tools/gpiodetect.o tools/tools-common.o -Wl,-L<ABSOLUTE_PATH_TO_LIBGPIOD>/libgpiod-1.6.4/lib/.libs,-lgpiod,-lpthread,-static
            Copy to clipboard

aarch64-linux-gnu-gcc -static -o tools/gpioget tools/gpioget.o tools/tools-common.o -Wl,-L<ABSOLUTE_PATH_TO_LIBGPIOD>/libgpiod-1.6.4/lib/.libs,-lgpiod,-lpthread,-static
            Copy to clipboard

aarch64-linux-gnu-gcc -static -o tools/gpioset tools/gpioset.o tools/tools-common.o -Wl,-L<ABSOLUTE_PATH_TO_LIBGPIOD>/libgpiod-1.6.4/lib/.libs,-lgpiod,-lpthread,-static
            Copy to clipboard

aarch64-linux-gnu-gcc -static -o tools/gpiofind tools/gpiofind.o tools/tools-common.o -Wl,-L<ABSOLUTE_PATH_TO_LIBGPIOD>/libgpiod-1.6.4/lib/.libs,-lgpiod,-lpthread,-static
            Copy to clipboard

aarch64-linux-gnu-gcc -static -o tools/gpioinfo tools/gpioinfo.o tools/tools-common.o -Wl,-L<ABSOLUTE_PATH_TO_LIBGPIOD>/libgpiod-1.6.4/lib/.libs,-lgpiod,-lpthread,-static
            Copy to clipboard

aarch64-linux-gnu-gcc -static -o tools/gpiomon tools/gpiomon.o tools/tools-common.o -Wl,-L<ABSOLUTE_PATH_TO_LIBGPIOD>/libgpiod-1.6.4/lib/.libs,-lgpiod,-lpthread,-static
            Copy to clipboard
2. To push the binaries to your device after compilation, run the `scp` command. For example:

scp gpioinfo root@<IP_address>:/path/to/directory/on/device
        Copy to clipboard

After pushing the binaries to your device, run the following commands on the device to interact with the GPIOs:

1. Use the `gpiodetect` and `gpioinfo` commands to list the GPIO chips and lines. The following example shows the GPIO chip information:

sh-5.2# ./gpiodetect
        gpiochip0 [c440000.spmi:pmic@8:pinctrl@c00] (12 lines)
        gpiochip1 [c440000.spmi:pmic@1:gpio@8800] (10 lines)
        gpiochip2 [c440000.spmi:pmic@2:gpio@8800] (9 lines)
        gpiochip3 [c440000.spmi:pmic@0:gpio@b000] (4 lines)
        gpiochip4 [f100000.pinctrl] (176 lines)
        gpiochip5 [33c0000.pinctrl] (15 lines)
        Copy to clipboard

    The following example shows the GPIO lines:

sh-5.2# ./gpioinfo gpiochip5
        gpiochip5 - 15 lines:
              line    0:      unnamed       unused   input   active-high
              line    1:      unnamed       unused   input   active-high
              line    2:      unnamed       unused   input   active-high
              line    3:      unnamed       unused   input   active-high
              line    4:      unnamed       unused   input   active-high
              line    5:      unnamed       unused   input   active-high
              line    6:      unnamed       unused   input   active-high
              line    7:      unnamed       unused   input   active-high
              line    8:      unnamed       unused   input   active-high
              line    9:      unnamed       unused   input   active-high
              line   10:      unnamed       unused   input   active-high
              line   11:      unnamed       unused   input   active-high
              line   12:      unnamed       unused   input   active-high
              line   13:      unnamed       unused   input   active-high
              line   14:      unnamed       unused   input   active-high
        Copy to clipboard
2. Use the `gpioset` command to set GPIO values. For example, to set `GPIO line 0` on `gpiochip4`, do the following:

sh-5.2# ./gpioset gpiochip4 0=1
        sh-5.2# ./gpioinfo gpiochip4
        gpiochip4 - 176 lines:
           line    0:      unnamed       unused   output  active-high
           line    1:      unnamed       unused   input   active-high
        Copy to clipboard
3. Use the `gpioget` command to read GPIO values. For example, to read the value of `GPIO line 0` on `gpiochip4`, do the following:

sh-5.2# ./gpioget gpiochip4 0
        1
        sh-5.2# ./gpioinfo gpiochip4
        gpiochip4 - 176 lines:
                 line    0:      unnamed       unused   input   active-high
                 line    1:      unnamed       unused   input   active-high
        Copy to clipboard

Last Published: Jul 02, 2025

[Previous Topic
Configure the dynamic voltage and frequency scaling (DVFS) goverors](https://docs.qualcomm.com/bundle/publicresource/80-70020-3/topics/dynamic-clock-and-voltage-scaling-dcvs.md) [Next Topic
Real-time (RT) kernel overview](https://docs.qualcomm.com/bundle/publicresource/80-70020-3/topics/real_time_kernel_overview.md)