Skip to main content
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.
For more information about additional Qualcomm SoC pinctrl drivers, see Pinctrl Drivers.
The following are the pinctrl data objects: Table: Pinctrl data objects
VariableDescription
static const struct pinctrl_pin_desc sc7280_pinsEnumerates all pins and their names
static const struct msm_pingroup sc7280_groupsDefines the available muxed functions for the group of GPIO pins
enum sc7280_functionsLists 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.
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";
    };

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.
  • 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;
    };
    
  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>;
    
  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.
    */
    
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;
      };
      
  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>;
    
  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);
    

Configure GPIOs to generate clock or pulse width modulation

Configure any GPIO with the GP_CLK as an alternate functionality to get clock or pulse width modulation (PWM).
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.
  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 */
+       };
+};
  1. Define device tree 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";
    +};
    
  2. 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__);
    +
    
  3. 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().
  4. 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
      
      sudo apt install binutils-aarch64-linux-gnu
      
    2. To download and extract the libgpiod source code from 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
      
      tar xvf libgpiod-1.6.4.tar.xz
      
      cd libgpiod-1.6.4
      
    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"
      
    4. To compile the library, run the following command:
      make
      
      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
      
      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
      
      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
      
      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
      
      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
      
      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
      
  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
    
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)
    
    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
    
  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
    
  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