# Customize the kernel

The SoC and board device tree support are hosted in the kernel source at
`arch/arm64/boot/dts/qcom`.

## Develop the kernel

To customize the kernel and generate patches during kernel development, use the following kernel recipe:

# setup the workspace and get the sources following build guide
    # setup the environment
    
      MACHINE=<SoC>-<board>-<variant> DISTRO=qcom-wayland source setup-environment
    
    # create your own layer to host changes
      bitbake-layers create-layer ~/meta-mylayer
      bitbake-layers add-layer ~/meta-mylayer
    
    # use devtool to setup kernel source for development
      devtool modify linux-qcom-base
    
    # do the development , commit changes, build and test
      devtool build linux-qcom-base
      devtool build-image qcom-console-image
    
    # built images are produced in standard location
      ls build-qcom-wayland/tmp-glibc/deploy/images/<SoC>-<board>-<variant>/
    
    # generate the patches and update layer
      devtool finish linux-qcom-base ~/meta-mylayer
    Copy to clipboard

For more information about customizing the Qualcomm Linux kernel recipe, see [Kernel recipe](https://docs.qualcomm.com/doc/80-70018-3/topic/yocto-kernel-support.html#kernel-recipe).

Note

For the custom BSP variant, use `linux-qcom-custom`.

For more information about Yocto provisions, host patches, and how to apply
them, see [Yocto Project Linux Kernel
Development](https://docs.yoctoproject.org/1.5/kernel-dev/kernel-dev.html#applying-patches).

## Kernel standalone development

Linux kernel can also be compiled without Yocto build system support.

### Prerequisites

Provision the following dependencies to setup the kernel compilation process:

> 
> 
> - aarch64 toolchain
> - systemd-boot EFI stub to include it as a part of the UKI image
> - systemd ukify tool to package the kernel image, initramfs, and DTB into the UKI image
> - initramfs with UKI image
> - ESP image that carries the updated UKI and must be flashed to boot the device

Download the aarch64 toolchain from [ARM developer site](https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads).

Installing all the dependencies enables you to do incremental kernel builds without rebuilding the whole Yocto.

To build the Qualcomm^®^ Linux Yocto base see, [Getting started with Qualcomm Linux kernel](https://docs.qualcomm.com/doc/80-70018-3/topic/getting_started_chapter2.html#getting-started-chapter2).
After Yocto base is built for Qualcomm Linux, find ukify, EFI boot stub, initramfs, and efi.bin at the following directories:

> 
> 
> - ukify at `<build-path>/tmp-glibc/sysroots-components/x86_64/systemd-boot-native/usr/bin/ukify`
> - EFI boot stub at `<build-path>/tmp-glibc/deploy/images/<SoC>/linuxaa64.efi.stub`
> - initramfs at `<build-path>/tmp-glibc/deploy/images/<SoC>/initramfs-qcom-image-<SoC>.cpio.gz`
> - `efi.bin` is generated out of build and is present at `<build-path>/tmp-glibc/deploy/images/<SoC>/qcom-<BUILD_TYPE>/efi.bin`

You can use the custom initramfs. Access the initramfs for arm64 hosted at [Linaro Snapshots site](https://snapshots.linaro.org/member-builds/qcomlt/testimages/arm64/latest/), using the following command:

wget https://snapshots.linaro.org/member-builds/qcomlt/testimages/arm64/latest/initramfs-test-image-qemuarm64-*.rootfs.cpio.gz -O <download-path>/linaro-initramfs.cpio.gz
    Copy to clipboard

**Get the Linux kernel source**

Clone the Linux kernel source from the git repository hosted at
[CodeLinaro](https://git.codelinaro.org/clo/la/kernel/qcom).

To clone the repository and get the source code, run the following commands:

git clone https://git.codelinaro.org/clo/la/kernel/qcom kernel-src
      cd kernel-src/
      git checkout <released sha>
    # e.g.
      git checkout origin/kernel.qclinux.1.0.r2-rel
    Copy to clipboard

**Configure and build Linux kernel**

Install the toolchain and set the following path:

export PATH=<PATH_TO_ARM_TOOLCHAIN>/arm-gnu-toolchain-13.3.rel1-x86_64-aarch64-none-linux-gnu/bin:$PATH
    Copy to clipboard

Verify if the following command is functioning correctly:

aarch64-linux-gnu-gcc --version
    Copy to clipboard

After all the changes are completed, to build and configure the Linux kernel, run the following commands:

export CROSS_COMPILE=aarch64-linux-gnu-
    export ARCH=arm64
    make qcom_defconfig
    make -j8 dir-pkg INSTALL_MOD_STRIP=1
    Copy to clipboard

For more information about building the kernel and the image, see build kernel image.

To build the kernel and generate the images, use the `make -j8 dir-pkg INSTALL_MOD_STRIP=1` command.
The images get added to the `kernel-src/tar-install` directory.
The compiled files are present in the following directories:

> 
> 
> - Kernel image in the `kernel-src/arch/arm64/boot/Image` directory
> - DTBs are deployed in the `kernel-src/tar-install/boot/dtbs/` directory
> - Kernel modules in the `kernel-src/tar-install/lib/modules/` directory

**Update initramfs with compiled kernel modules**

Copy the initramfs file to the `kernel-src` directory and overlay the compiled modules on your initramfs.

Note

- You can use your own custom or publicly available initramfs to build the kernel and boot into a shell.
- If you need a full-featured rootfs, you must rely on the Yocto build.

To use your ramdisk, run the following command:

cp <download-path>/linaro-initramfs.cpio.gz ./initramfs-qcom-image.cpio.gz && (cd tar-install ; find lib/modules | cpio -o -H newc -R +0:+0 | gzip -9 >> ../initramfs-qcom-image.cpio.gz)
    Copy to clipboard

**Package UKI image**

After the Linux kernel image, DTB, and initramfs with all the kernel modules are ready, package the UKI image.

Note

> 
> 
> The ukify tool supports Python (3.10) or later. To run the ukify tool, install `pip install pefile` to support ukify tool execution.

To build the ukify utility, run the following commands:

cd build-qcom-wayland/
    Copy to clipboard

STUB = $PWD/tmp-glibc/deploy/images/<SoC>/linuxaa64.efi.stub
    Copy to clipboard

KERNEL_IMAGE = <STANDALONE_kernel-src>/arch/arm64/boot/Image
    Copy to clipboard

INITRD = <STANDALONE_kernel-src>/initramfs-qcom-image.cpio.gz
    Copy to clipboard

DTB = <STANDALONE_kernel-src>/arch/arm64/boot/dts/qcom/qcs6490-rb3gen2.dtb
    Copy to clipboard

KERNEL_VENDOR_CMDLINE = "root=/dev/disk/by-partlabel/system rw rootwait console=ttyMSM0,115200n8 earlycon qcom_geni_serial.con_enabled=1 kernel.sched_pelt_multiplier=4 mem_sleep_default=s2idle"
    Copy to clipboard

UKINAME = "uki.efi"
    Copy to clipboard

UKI_OUT=  <OUT_DIR>/$UKINAME
    Copy to clipboard

rm -f "${UKI_OUT}"
    Copy to clipboard

./tmp-glibc/sysroots-components/x86_64/systemd-boot-native/usr/bin/ukify build --efi-arch=aa64 --stub="${STUB}" --linux="${KERNEL_IMAGE}" --initrd="${INITRD}" --devicetree="${DTB}"  --cmdline="${KERNEL_VENDOR_CMDLINE}" --output="${UKI_OUT}"
    Copy to clipboard

The ukify build command generates the `uki.efi` image with the built kernel and other images.
Ignore the following warnings from ukify:

Kernel version not specified, starting autodetection
    Real-Mode Kernel Header magic not found
    + readelf --notes $KERNEL_IMAGE
    readelf: Error: Not an ELF file - it has the wrong magic bytes at the start
    Found uname version: 6.6.38-perf-gb09a1d09b89b-dirty
    Wrote unsigned $UKI_OUT
    Copy to clipboard

**Package ESP image**

When the UKI image is ready, update the ESP image. Then, the updated image is flashed onto the board and booted.
Use the following instructions to update `efi.bin` from the Yocto build with `uki.efi` from the standalone kernel.

Note

Overwrite the UKI image in EFI with the default image name in `efi.bin`.

To overwrite the UKI image, run the following commands:

sudo mount -t vfat efi.bin /mnt/
    cp uki.efi /mnt/EFI/Linux/linux-<SoC>.efi
    umount /mnt
    Copy to clipboard

To flash the EFI image and reboot, see [Bring up the device](https://docs.qualcomm.com/doc/80-70018-3/topic/getting_started_chapter2.html#flash-images-and-boot).

Note

To build additional out-of-tree kernel modules, rely on the full Yocto build mechanism.

## Configure the kernel

The Yocto build system is used for modifying the kernel configuration, while
invoking `menuconfig`.

To modify the kernel configuration, run the following commands:

MACHINE=<SoC>-<board>-<variant> DISTRO=qcom-wayland source setup-environment
     bitbake linux-qcom-base -c menuconfig
    
    # Above would update .config in kernel build directory build-qcom-wayland/tmp-glibc/work/<SoC>-<board>-<variant>/linux-qcom-base/6.6-r0/build/
    # one can create a config fragment for modifications made by issuing following
    
      bitbake linux-qcom-base -c diffconfig
    
    # Above would create fragment.cfg in build directory build-qcom-wayland/tmp-glibc/work/<SoC>-<board>-<variant>/linux-qcom-base/6.6-r0/
    Copy to clipboard

Alternatively, use the Devtool to modify the kernel configuration:

devtool modify linux-qcom-base
     devtool menuconfig linux-qcom-base
     devtool finish linux-qcom-base ~/meta-mylayer
    
    # this would create a config fragment as a patch and update in your meta layer
    Copy to clipboard

Note

For the custom BSP variant, use `linux-qcom-custom`.

For more information about Yocto-related details on kernel configuration, see
[Configuring the
Kernel](https://docs.yoctoproject.org/4.3.1/kernel-dev/common.html#configuring-the-kernel).

## Create a debug build

To create a debug build, pass `DEBUG_BUILD=1` as an argument in the shell:

# setup the build environment
      export SHELL=/bin/bash
    
      MACHINE=<SoC>-<board>-<variant> DISTRO=qcom-wayland QCOM_SELECTED_BSP=base source setup-environment
    
    # build qcom linux console image
      DEBUG_BUILD=1 bitbake qcom-console-image
    Copy to clipboard

Note

For the custom BSP variant, use `QCOM_SELECTED_BSP=custom`.

## Update the kernel command-line

To update the kernel command-line, modify the
Yocto configuration variable `KERNEL_CMDLINE_EXTRA` in the corresponding SoC-specific machine inclusion file. For example, `meta-qcom-hwe/conf/machine/include/qcom-<SoC>.inc`.

To update the kernel command-line, modify the following variable:

KERNEL_CMDLINE_EXTRA = "root=/dev/disk/by-partlabel/system rw rootwait console=ttyMSM0,115200n8 pcie_pme=nomsi earlycon"
    Copy to clipboard

## Platform device tree and kernel configuration

To update the DTB support in the kernel and select a DTB on boot, use the following procedure:

**DTB build support in kernel**

To integrate the device tree of a new platform into the kernel build, update the `Makefile`.

The following example shows customizing DTB for the QCS6490 SoC.
Replicate the following method to add new DTBs.

diff --git a/arch/arm64/boot/dts/qcom/Makefile b/arch/arm64/boot/dts/qcom/Makefile
    index 183aeba47193..a7815c774f7c 100644
    --- a/arch/arm64/boot/dts/qcom/Makefile
    +++ b/arch/arm64/boot/dts/qcom/Makefile
    dtb-$(CONFIG_ARCH_QCOM)        += qcs6490-addons-rb3gen2.dtb
    +dtb-$(CONFIG_ARCH_QCOM)        += qcs6490-my-board.dtb
    dtb-$(CONFIG_ARCH_QCOM)        += qcs6490-rb3gen2.dtb
    dtb-$(CONFIG_ARCH_QCOM)        += qcs404-evb-1000.dtb
    Copy to clipboard

**DTB inclusion in machine configuration**

The Yocto machine configuration is also updated to include the corresponding device tree blob. For example, to add the device tree for QCS6490 machine support, use the following file:

`meta-qcom-hwe/conf/machine/qcs6490-rb3gen2-core-kit.conf`:

OUT_OF_KERNEL_DTSO - qcs6490-rb3gen2-core-kit.conf
    # List of dtbs for corresponding supported qcs6490 platforms
    KERNEL_DEVICETREE = " \
                         qcom/qcs6490-my-board.dtb   \
                         qcom/qcs6490-addons-rb3gen2.dtb \
                         qcom/qcs6490-my-board.dtb   \
                         "

    # Additional list of DTBOs to be overylaid on top of base kernel devicetree
    # See how existing boards are managing it in the following example:
    # Format - KERNEL_TECH_DTBOS[<base-dtb-name>] = "<dtbo1 <dtbo2> ..."
    
    KERNEL_TECH_DTBOS[qcs6490-addons-rb3gen2] = " \
    qcm6490-graphics.dtbo qcm6490-wlan-rb3.dtbo \
    qcm6490-display-rb3.dtbo qcm6490-bt.dtbo \
    qcm6490-video.dtbo qcm6490-wlan-upstream.dtbo \
    Copy to clipboard

Note

See machine configuration files in the `meta-qcom-hwe/conf/machine/*.conf` directory for different SoCs.
The DTB filename for the custom BSP variant contains `addons`.

**DTB selection on boot**

The custom DTB is packaged as a part of the UKI image that’s updated in EFI to boot with the selected DTB.

Generate a UKI image using the ukify tool. The ukify tool is available as part of the Yocto build in the
`tmp-glibc/sysroots-components/x86_64/systemd-boot-native/usr/bin/ukify` build directory.

To generate the UKI image, run the following:

# Note - ukify tool need python 3.10 version or above
    
       ukify build --efi-arch=aa64  \
                --stub=<build-path>/tmp-glibc/deploy/images/<SoC>/linuxaa64.efi.stub \
                --linux=<build-path>/tmp-glibc/deploy/images/<SoC>/Image \
                --initrd=<build-path>/tmp-glibc/deploy/images/<SoC>/initramfs-qcom-image-<SoC>.cpio.gz \
                --cmdline="console=ttyMSM0,115200n8 earlycon qcom_geni_serial.con_enabled=1 kernel.sched_pelt_multiplier=4 mem_sleep_default=s2idle" \
                --devicetree=<build-path>/tmp-glibc/deploy/images/<SoC>/<SoC>-my-board.dtb \
                --output=./uki.efi
    Copy to clipboard

The ukify build command generates the `uki.efi` image with a custom
board DTB.

To update the `uki.efi` image in the ESP partition, do the following:

# Following may need sudo privilege
    
    # Take the yocto build generated efi.bin and mount it locally
      mount <build-path>/tmp-glibc/deploy/images/<SoC>/efi.bin  /mnt --options rw
    
    # Overwrite the uki.efi with one packaged above
      cp uki.efi /mnt/EFI/Linux/uki.efi
      umount /mnt
    
    # now efi.bin carries packaged uki.efi which can be flashed to the target and booted
    # UEFI shall now pick the <SoC>-my-board.dtb that is part of uki.efi image
    
    # reboot into fastboot and flash efi.bin
      fastboot flash efi <build-path>/tmp-glibc/deploy/images/<SoC>/efi.bin
    Copy to clipboard

For more information about the device tree specification, see [The Devicetree Specification](https://www.devicetree.org/specifications/).

For the Linux kernel documentation for device tree, see [Linux and the Devicetree](https://docs.kernel.org/devicetree/usage-model.html).

## Update the ESP images

To compile the systemd-boot boot manager and kernel images into a packaged UKI type-2 image file, use the following:

Yocto build for hardware SoC generates all the required images and package
boot images as `efi.bin` that’s flashed into the EFI partition. The `efi.bin` file consists of
systemd-boot boot manager and kernel images, which are packaged as UKI type-2 image format.

Rebuild the EFI image after updating the kernel source, configuration,
or DTS and flash the generated `efi.bin` to the EFI partition.

MACHINE=<SoC>-<board>-<variant> DISTRO=qcom-wayland source setup-environment
    
    # build qcom linux console image
      DEBUG_BUILD=1 bitbake qcom-console-image
    
    # build images are produced in following directory
      ls build-qcom-wayland/tmp-glibc/deploy/images/<SoC>-<qcom>-<variant>/efi.bin
    efi.bin
    
    # reboot into fastboot
      fastboot flash efi efi.bin
      fastboot flash dtb_a dtb.bin
    Copy to clipboard

For more information about the UKI type-2 image format, see  [Type #2 EFI Unified Kernel Images](https://uapi-group.org/specifications/specs/boot_loader_specification/#type-2-efi-unified-kernel-images).

For more information about ESP, see [Boot](https://docs.qualcomm.com/doc/80-70018-3/topic/features.html#boot) and [Bring up the device](https://docs.qualcomm.com/doc/80-70018-3/topic/getting_started_chapter2.html#bring-up-the-device).

## Customize the “initramfs” package

To update the initramfs package, modify the `PACKAGE_INSTALL` list in
`meta-qcom-hwe/recipes-kernel/images/initramfs-qcom-image.bbappend` file:

less meta-qcom-hwe/recipes-kernel/images/initramfs-qcom-image.bbappend
    
    # Add additional packages needed as part of initrd
    PACKAGE_INSTALL += " \
        e2fsprogs \
        e2fsprogs-e2fsck \
        e2fsprogs-mke2fs \
        e2fsprogs-resize2fs \
        e2fsprogs-tune2fs \
        ${VIRTUAL-RUNTIME_dev_manager} \
        os-release-initrd \
        "
    Copy to clipboard

## Add a kernel module

To compile out-of-tree kernel modules using the Yocto build system, use the following procedure:

1. Create a [``](https://docs.qualcomm.com/doc/80-70018-3/topic/customize.html#id1)Makefile``for out-of-tree kernel driver.

> 
> 
> The following is the sample `Makefile` for out-of-tree kernel driver:

all: modules
    obj-m := hello.o
    
    SRC := $(shell pwd)
    
    modules:
      $(MAKE) -C $(KERNEL_SRC) M=$(SRC) modules $(KBUILD_OPTIONS)
    
    modules_install:
      $(MAKE) -C $(KERNEL_SRC) M=$(SRC) modules_install
    Copy to clipboard

2. Integrate the module into the Yocto build system.

> 
> 
> See the following example to integrate the kernel modules using Yocto module class.

DESCRIPTION = "${SUMMARY}"
    LICENSE = "GPL-2.0-only"
    LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/${LICENSE};md5=801f80980d171dd6425610833a22dbe6"
    
    inherit module
    
    SRC_URI += "file://Makefile \
                file://hello.c  \
                file://COPYING  \
                "
    S = "${WORKDIR}"
    
    EXTRA_OEMAKE += "MACHINE='${MACHINE}'"
    MAKE_TARGETS = "modules"
    MODULES_INSTALL_TARGET = "modules_install"
    
    # Kernel module to be autoloaded
        KERNEL_MODULE_AUTOLOAD += "hello"
    
    # The inherit of module.bbclass will automatically name module packages with
    # "kernel-module-" prefix as required by the oe-core build environment.
    
    RPROVIDES_${PN} += "kernel-module-hello"
    Copy to clipboard

For more information about the out-of-tree module, see [Working with
Out-of-Tree
Modules](https://docs.yoctoproject.org/kernel-dev/common.html#working-with-out-of-tree-modules).

## Disable console log

Disable the kernel console logging in one of the following ways:

- Update the kernel command-line with `quiet`.

> 
> 
> For more information about kernel parameters, see [The kernel’s
> command-line
> parameters](https://www.kernel.org/doc/html/latest/admin-guide/kernel-parameters.html).
- Disable `CONFIG_SERIAL_EARLYCON` and `CONFIG_SERIAL_MSM_CONSOLE` in the kernel configuration.
- Add the `console=null` parameter to the kernel command-line arguments.

## 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’s 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-70018-3/topic/customize.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 don’t 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 ZRAM as a swap device

ZRAM is a compressed swap mechanism that creates a virtual block device
in RAM. ZRAM is enabled as a module within the kernel defconfig.

ZRAM is configured in the Yocto build in the
`recipes-extended/zram/zram/zram-swap-init-update` file.

To enable or configure ZRAM, do the following:

# check if zram module is loaded
      lsmod | grep zram
    
    # else load it
      modprobe zram
    
    # Configure /dev/zram0 size according to your RAM size
      echo 128M > /sys/block/zram0/disksize
    
    # activate swap
      mkswap /dev/zram0
      swapon /dev/zram0
    Copy to clipboard

To configure ZRAM as a swap device, see [zram: Compressed RAM-based
block
devices](https://www.kernel.org/doc/html/v6.6/admin-guide/blockdev/zram.html).

## Extend the memory map

To extend the memory map, adjust the addresses and sizes of memory
regions in a DTSI file.

Use the following information to extend the carved out regions:

Add the carved out region in the
`arch/arm64/boot/dts/qcom/<SoC>-<board>-<variant>.dts` file in the
**reserved-memory** node using the following syntax:

my_carveout_mem: my_carveout_mem@address {
          reg= <0x0 0xbase_address 0x0 0xsize>;
          no-map;
    }
    Copy to clipboard

For example:

my_carveout_mem: my_carveout_mem@d0800000 {
          reg= <0x0 0xd0800000 0x0 0x100000>;
          no-map;
    }
    Copy to clipboard

Table: Syntax for carved out region

| Variable | Description |
| --- | --- |
| my_carveout_mem@d0800000<br>    Copy to clipboard | Indicates the name of the device node. The convention mandates that you append the base address of the memory region to the name. |
| my_carveout_mem<br>    Copy to clipboard | Indicates the label assigned to this node that can be used to reference this node from within the other nodes in the device tree using phandles. |
| reg<br>    Copy to clipboard | Indicates the property, which is a 64‑bit value that defines the base and size of the memory region. |
| no-map<br>    Copy to clipboard | Indicates that this region is carved out and the kernel should remove the mapping from its addressable range. |

Note

- None of the regions should overlap. If you must increase a region size, shift all other subsequent regions, and configure them in the device tree to avoid overlapping.
- The kernel expects all memory region boundaries defined for kernel usage to be 1 MB.
- Existing carved out regions are protected by trusted firmware from kernel access. Decreasing their size or deleting them may result in an external abort, leading to a kernel crash.

**Add CMA region**

To add the CMA region in the `arch/arm64/boot/dts/qcom/<SoC>-<board>-<variant>.dts`
file under **reserved-memory** node, use the following syntax:

my_cma_mem: my_cma {
       compatible = "shared-dma-pool";
       alloc-ranges = <0x0 0x00000000 0x0 0xffffffff>;
       reusable;
       alignment = <0x0 0x400000>;
       size = <0x0 0x1400000>;
    };
    Copy to clipboard

Table : Syntax for adding CMA region

| Parameters | Description |
| --- | --- |
| my_cma_mem<br>    Copy to clipboard | Label for the CMA node and can be accessed as a phandle. The label `my_cma` is the name of the CMA region. |
| shared-dma-pool<br>    Copy to clipboard | Indicates that this region is a CMA area. |
| alloc-ranges<br>    Copy to clipboard | Indicates if the region should be within a certain memory limit, as some devices can’t access memory regions beyond the 32‑bit address limit. |
| reusable<br>    Copy to clipboard | Indicates that the kernel can use the memory in this region when it’s free. |
| alignment<br>    Copy to clipboard | Indicates any alignment requirements in this region. |
| size<br>    Copy to clipboard | Indicates the size of the region. |
| reg<br>    Copy to clipboard | This is an optional attribute that indicates the fixed region for memory allocation, in the absence of which memory is dynamically allocated at a random address. |

## Add custom CMA heaps

To create custom CMA regions using DMA-BUF heaps, use the existing DMA-BUF framework in the Qualcomm Linux kernel.

The Qualcomm Linux kernel exports the `cma_heap_add()` API for the custom DMA-BUF heaps.

/**
    * cma_heap_add - adds a CMA heap to dmabuf heaps
    * @cma:       pointer to the CMA pool to register the heap for
    * @data:      unused
    *
    * Returns 0 on success. Else, returns errno.
    */
    
    int cma_heap_add(struct cma *cma, void *data);
    Copy to clipboard

The DMA-BUF heap exported to the user space, has the same name as the phandle of the CMA region in the device tree.

To add a custom CMA heap, do the following:

1. To create a custom CMA region see [Contiguous memory allocator](https://docs.qualcomm.com/doc/80-70018-3/topic/features.html#memory-contiguous).
2. In the driver to which you are adding a custom heap:

    1. Parse the device tree for the newly added CMA region.
    2. Add a DMA-BUF heap associated with the driver.

int create_my_cma_heap(struct device *dev)
            {
               int rc = 0, idx = 0;
            
               rc = of_reserved_mem_device_init_by_idx(dev, dev->of_node, 0);  // Parse the devicetree for the cma region
            
               if (rc) {
                        pr_err("No reserved DMA memory, ret=%d\n", rc);
                        rc = -EINVAL;
                        goto err;
               }
            
               rc = cma_heap_add(dev->cma_area, NULL);  // Add a dmabuf heap associated with the cma region
            
               if (rc) {
                        pr_err("cma_heap_add failed, ret=%d\n", rc);
                        rc = -EINVAL;
                        goto err;
               }
            
            err:
               return rc;
            }
            Copy to clipboard

Note

Align CMA regions to the 4 MB address base and size to support page migration. The migration happens at 2 ^pageblock\_order^ pages at the page-block level. The page-block order is 10 in the Qualcomm Linux kernel.

The Qualcomm Linux kernel supports the standard Linux scheduler solution. The kernel uses the scheduler to choose the right CPU for task placement based on the CPU energy consumption.

### PELT multiplier

In the Linux kernel, the PELT half-life multiplier influences how the system adapts to changing workloads and adjusts CPU frequencies based on historical utilization data. It’s a configuration that speeds up the PELT clock and reduces the half-life time, resulting in faster system response.

Configure the PELT multiplier through the kernel command-line argument:

`kernel.sched_pelt_multiplier=[1, 2, 4] Default value: 1 (half life 32 msec), 2 (half life 16msec), 4(half life 8 msec)]`

### Utilization clamping

Utilization clamping is a scheduler feature that allows user space to manage the performance requirements of tasks.

To perform clamping, configure the following parameters:

- `UCLAMP_MIN`: If it’s set to any value `> 0`, the task demand is always greater than or equal to this value. If the actual task demand is more than this value, then the actual demand signal is used, but if the actual task demand is lower than this value, then `UCLAMP_MIN` is reported as the task demand.
- `UCLAMP_MAX`: If it’s set to any value `> 0`, the task demand is always less than or equal to this value. If the actual task demand is less than this value, then the actual demand signal is used, but if
actual task demand is more than this value, then `UCLAMP_MAX` is reported as task demand.

Note

`UCLAMP_MAX > UCLAMP_MIN`

Use the UCLAMP to influence the scheduler’s task placement decisions.

On a heterogeneous system, the scheduler uses the task demand or utilization signal (PELT signal) to classify a task as small or big. Based on the input from the task classification, the scheduler selects small (less computing power) or big (more computing power) CPU cores for task placement with a probable impact on power consumption.

For example, clamp nonimportant (trivial/background/housekeeping) tasks to lower values (`lower UCLAMP_MAX`) to influence the scheduler to place the tasks on the little cluster. Similarly, clamp the important/foreground/active tasks to higher values (`higher UCLAMP_MIN`) to influence the scheduler to place the tasks on the big cluster.

UCLAMP also allows the scheduler to control frequency guidance.

To meet the quick frequency ramp for a task, clamp the task to a higher demand (`UCLAMP_MIN`) value. The higher clamping helps to ramp up the frequency of the cluster to meet the performance requirements of the task.

For more information about the interface and configuration, see [Utilization Clamping](https://www.kernel.org/doc/html/v6.6/scheduler/sched-util-clamp.html).

## Change the default CPU frequency governor

The CPU frequency governor can be changed at runtime or set statically
during build compilation.

1. Kconfig configuration option: To set the CPU frequency governor,
enable the corresponding driver in the kernel defconfiguration file.

    To set `PERFROMANCE` governor as the default CPU frequency
governor, set `CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE=y` in the
defconfiguration file.
2. Kernel command-line option: To override the kernel configuration
option, add a `cpufreq.default_governor=performance` parameter to
the kernel command-line in the
`meta-qcom-hwe/conf/machine/include/qcom-<SoC>.inc` file to set the
appropriate CPU frequency governor.

## Customize cache and memory DVFS

The mapping between **CPU frequency** and **L3/DDR frequency** is
adjusted based on the power or performance requirements.

In DTSI, for each CPUx node, there is an
`operating-points-v2 = <&cpux_opp_table>` entry. The
`cpux_opp_table` holds a static mapping between CPU, L3, and DDR
frequencies.

For example:

cpu0_opp_300mhz: opp-300000000 {
       opp-hz = /bits/ 64 <300000000>;
       opp-peak-kBps = <800000 9600000>;
    };
    Copy to clipboard

When CPU 0 operates at 300 MHz, it votes for 9600000 to L3, which
translates to 300,000 Hz (9600000/w) L3 frequency. If the vote is for
800,000 Hz to DDR, this results in 200,000 Hz (800000 / w) DDR
frequency.

In the equation, ‘w’ represents how many bytes can be written in a
single cycle:

- For DDR, ‘w’ is 4 (each channel performs two transactions per cycle,
with each transaction being 2 bytes).
- For L3, ‘w’ is 32 (one transaction per cycle at 32 bytes per
transaction).

Note

These values are set per channel for DDR, and the mapping relates CPU frequency to the memory controller (MC) channel bandwidth. Adjusting this map table can impact power and performance characteristics.

For more information about the operating performance points (OPP) framework
and syntax, see [Generic OPP (Operating Performance Points)
Bindings](https://www.kernel.org/doc/Documentation/devicetree/bindings/opp/opp.txt).

## Configure the postboot settings

Postboot settings comprise completing initial setup, configuring memory, and CPU frequency parameters. These steps ensure proper configuration and functionality after the system boots up.

### Postboot framework

A postboot script file is used to configure system parameters on the Qualcomm Linux distribution.

Postboot settings are managed through a *systemd service* file.

For more information about systemd, see [systemd(1) - Linux manual page](https://www.man7.org/linux/man-pages/man1/systemd.1.html).

### What’s a systemd service?

A systemd service is a background process that starts or stops based on specific conditions.

A `systemd service` file is written to allow systemd to parse, understand, and run as instructed.

For more information about the background process, see [background process](https://linuxhandbook.com/run-process-background/).

### Basic structure of systemd service file

To modify the behavior of systemd based on the requirement, use the following procedure:

The following is the example of the systemd service file:

[Unit]
    SourcePath=/etc/initscripts/log_restrict.sh
    Description=QTI logging service
    
    [Service]
    ExecStart=/etc/initscripts/post_boot.sh
    
    [Install]
    WantedBy=multi-user.target
    Copy to clipboard

### The `[Unit]` section

In systemd, a unit refers to any resource that the system knows how to operate and manage. It encompasses services, sockets, devices, mount points, and groups of externally created processes.

Each unit is defined using a configuration file called a `unit` file, which has metadata and configuration details. The `[Unit]` section within a unit file provides information about the description
and relationships with other units.

The `[Unit]` section has the following fields:

Table: Supported fields for [Unit] section

| Fields | Description |
| --- | --- |
| Description<br>    Copy to clipboard | Human-readable title of the systemd service. |
| After<br>    Copy to clipboard | Set dependency on a service. For example, if you are configuring a WCN3960 web server, you want the server to  start after the network is online. |
| Before<br>    Copy to clipboard | Start current service before specified service. In this example, the WCN3960 web server is running before the service for the next cloud is started because the next cloud server depends on the WCN3960 web server. |

### The `[Service]` section

The `[Service]` section contains details of the execution and termination of service.

The `[Service]` section has the following fields:

Table: Supported fields for [Service] section

| Fields | Description |
| --- | --- |
| ExecStart<br>    Copy to clipboard | The command that you must run when the service starts. For example, a web server service to start. |
| ExecReload<br>    Copy to clipboard | The optional field that it specifies how a service is restarted. For services that perform disk I/O, it’s recommended to kill them and restart the service. Use the `ExecReload` field if you want to have a specific restart mechanism. |
| Type<br>    Copy to clipboard | Indicates the start-up type of a process for a specified systemd service. The options available are `simple`, `exec`, `forking`, `oneshot`, `dbus`, `notify`, and `idle`. |

For more information, see [Options](https://www.freedesktop.org/software/systemd/man/latest/systemd.service.html?ref=linuxhandbook.com#Options).

### The `[Install]` section

The `[Install]` section handles the installation of a systemd service or unit file. This is used when you run either the `systemctl enable` or `systemctl disable` command for enabling or disabling a service.

The `[Install]` section has the following fields:

- `WantedBy`: Similar to the `After` and `Before` fields of the
[The \[Unit\] section](https://docs.qualcomm.com/doc/80-70018-3/topic/customize.html#kernelexternaldocumentation-the-unit-section).
However, it’s used to specify systemd-equivalent **run levels**.

    The `default.target` is used when all the system initialization is
complete and you are asked to log in. Most user-facing services (like
WCN3960, cron, GNOME-stuff) use this target.

    The `shutdown.target` is to run the service just before the device
shuts down.

    The `multi-user.target` is to run the service at the time of system
boot, as the root user.

    It can be either a target or a service, for example
`network.target`.
- `RequiredBy`: This field is similar to `WantedBy`. However, the
field specifies **hard dependencies**. If a dependency fails, the
service also fails.

### Postboot script

The postboot script file is present in the core recipe of the `meta-qcom-hwe` layer.

Run the postboot script as a part of the previous systemd service. All postboot settings are hosted in a
`meta-qcom-hwe/recipes-core/initscripts/files/post_boot.sh` script.

#!/bin/sh
    # Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
    # SPDX-License-Identifier: BSD-3-Clause-Clear
    
    # Apply postboot settings
    Copy to clipboard

### Recipe for postboot systemd service

Use the `initscripts_1.0.bbappend` file to understand the recipe configuration procedure to install the `post_boot` script.

The recipe to install the postboot bash script in `/etc/init.d` is present in the `meta-qcom-hwe/recipes-core/initscripts/initscripts_1.0.bbappend` file.

# postboot
    
    inherit systemd externalsrc
    
    FILESEXTRAPATHS:prepend := "${THISDIR}/files:"
    SRC_URI:append = " \
       file://post_boot.sh \
       file://logging-restrictions.sh \
       file://log-restrict.service \
       file://post-boot.service \
       "
    
    do_install:append() {
       # postboot
       install -m 0755 ${WORKDIR}/post_boot.sh ${D}${sysconfdir}/initscripts/post_boot.sh
       install -m 0644 ${WORKDIR}/post-boot.service -D ${D}${systemd_unitdir}/system/post-boot.service
       ln -sf ${systemd_unitdir}/system/post-boot.service ${D}${systemd_unitdir}/system/multi-user.target.wants/post-boot.service
    }
    
    S = "${WORKDIR}"
    
    INITSCRIPT_PACKAGES =+ "${PN}-post-boot"
    INITSCRIPT_NAME:${PN}-post-boot = "post_boot.sh"
    
    PACKAGES =+ "${PN}-post-boot"
    FILES:${PN}-post-boot += "${systemd_unitdir}/system/post-boot.service ${systemd_unitdir}/system/multi-user.target.wants/post-boot.service ${sysconfdir}/initscripts/post_boot.sh"
    Copy to clipboard

### Scheduler DCVS settings

Scheduler DCVS settings are defined in the `post_boot script` file.

All postboot settings are present in the `meta-qcom-hwe/recipes-core/initscripts/files/post_boot.sh` script.

#!/bin/sh
    # Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
    # SPDX-License-Identifier: BSD-3-Clause-Clear
    Copy to clipboard

Last Published: Apr 10, 2025

[Previous Topic
Yocto support](https://docs.qualcomm.com/bundle/publicresource/80-70018-3/topics/yocto-kernel-support.md) [Next Topic
Debug the kernel](https://docs.qualcomm.com/bundle/publicresource/80-70018-3/topics/debug.md)