How to integrate a custom SPI kernel driver (LAN9252) into Yocto for Verdin AM62?

Hello,

I’m working with a Verdin AM62 on a Mallow carrier board, using a Yocto multimedia image.

I have written a custom driver for the LAN9252 SPI device which exports data via sysfs interfaces. On Raspberry Pi with Raspbian, I simply built the .ko file and loaded it using sudo insmod.

On Toradex with Yocto, I understand that I need to properly compile this as a module in the Yocto build.

Could you please guide me on the correct steps to:

  1. Add my out-of-tree kernel driver (lan9252.c) to the Yocto build and how exactly Makefile or bbappend file looks like.

  2. Apply a device tree overlay (to disable spidev and enable the LAN9252 node).

  3. Ensure the driver is automatically loaded at boot.

Thanks in advance!

Hi, I’m the Toradex AI Assistant.

I might make mistakes. Our support team will step in if needed. If I answered your question, please tick the “Solution” checkbox. For follow-up questions to me, please use @ToradexAI in your post.

Did you like my answer? :+1: :-1:


(1), (2), (3), (4)

Creating an Out-of-Tree Kernel Module for LAN9252

To add your custom LAN9252 driver to a Yocto build for the Verdin AM62, you’ll need to create a custom meta layer and recipe for your kernel module.

1. Create a Custom Meta Layer and Recipe

First, create a custom meta layer if you don’t have one already (1):

# Run `. export` or `source setup-environment` when building Torizon OS  
$ . export # will take you to the `build` directory  
$ bitbake-layers create-layer ../layers/meta-customer # inside the build directory  

(1)

Next, create the directory structure for your kernel module recipe (1):

$ cd ../layers/meta-customer/  
$ mkdir -p recipes-kernel/lan9252-driver/files

2. Create the Kernel Module Recipe

Create a recipe file recipes-kernel/lan9252-driver/lan9252-driver_1.0.bb (1):

SUMMARY = "LAN9252 SPI Kernel Driver"
LICENSE = "GPL-2.0"
LIC_FILES_CHKSUM = "file://COPYING;md5=12f884d2ae1ff87c09e5b7ccc2c4ca7e"

inherit module

SRC_URI = "file://lan9252.c \
           file://Makefile \
           file://COPYING \
          "

S = "${WORKDIR}"

# The inherit of module.bbclass will automatically name module packages with
# kernel-module-* prefix as required by the oe-core build environment.

(1)

3. Create the Makefile

Create recipes-kernel/lan9252-driver/files/Makefile:

obj-m := lan9252.o

SRC := $(shell pwd)

all:
	$(MAKE) -C $(KERNEL_SRC) M=$(SRC)

modules_install:
	$(MAKE) -C $(KERNEL_SRC) M=$(SRC) modules_install

clean:
	rm -f *.o *~ core .depend .*.cmd *.ko *.mod.c
	rm -f Module.markers Module.symvers modules.order
	rm -rf .tmp_versions Modules.symvers

(1)

Place your lan9252.c driver file in recipes-kernel/lan9252-driver/files/lan9252.c and add a COPYING file with the GPL-2.0 license text.

4. Add the Module to Your Image

Add the kernel module to your image by appending it to the IMAGE_INSTALL variable in your build/conf/local.conf file (1):

IMAGE_INSTALL:append = " kernel-module-lan9252"

(1)

5. Create a Device Tree Overlay

For the Verdin AM62, create a device tree overlay to disable spidev and enable your LAN9252 device. The overlay structure should follow this pattern (2):

/dts-v1/;
/plugin/;

#include <dt-bindings/gpio/gpio.h>
#include "k3-pinctrl.h"

/ {
    compatible = "toradex,verdin-am62";
};

&main_spi1 {
    status = "okay";
    #address-cells = <1>;
    #size-cells = <0>;
    
    lan9252@0 {
        compatible = "microchip,lan9252";
        reg = <0>;
        spi-max-frequency = <25000000>;
        status = "okay";
    };
};

(2)

6. Integrate the Overlay into Yocto

To add your overlay to the Yocto build, you can create a bbappend for the device tree overlays recipe. The approach depends on whether you’re using upstream or downstream images (4):

Create recipes-kernel/linux/device-tree-overlays_git.bbappend:

FILESEXTRAPATHS:prepend := "${THISDIR}/device-tree-overlays:"

CUSTOM_OVERLAYS_SOURCE = " \
    lan9252-verdin-am62_overlay.dts \
"
CUSTOM_OVERLAYS_BINARY = " \
    lan9252-verdin-am62_overlay.dtbo \
"

SRC_URI += " \
    file://lan9252-verdin-am62_overlay.dts \
"

TEZI_EXTERNAL_KERNEL_DEVICETREE += " \
    ${CUSTOM_OVERLAYS_BINARY} \
"

TEZI_EXTERNAL_KERNEL_DEVICETREE_BOOT = " \
    ${CUSTOM_OVERLAYS_BINARY} \
"

do_collect_overlays:prepend() {
    for DTS in ${CUSTOM_OVERLAYS_SOURCE}; do
        cp ${WORKDIR}/${DTS} ${S}
    done
}

(4)

7. Enable Automatic Loading

To ensure your driver loads automatically at boot, you can:

  1. Add module loading to your recipe by creating a configuration file
  2. Use device tree compatible matching - if your driver properly implements of_device_id matching, it will load automatically when the device tree node is present

Create recipes-kernel/lan9252-driver/files/lan9252.conf:

lan9252

And modify your recipe to install this configuration:

do_install:append() {
    install -d ${D}${sysconfdir}/modules-load.d
    install -m 0644 ${WORKDIR}/lan9252.conf ${D}${sysconfdir}/modules-load.d/
}

FILES:${PN} += "${sysconfdir}/modules-load.d/lan9252.conf"

8. Build Your Custom Image

After setting up the recipe and overlay, build your image (1):

$ bitbake <your-image>

(1)

This approach will properly integrate your LAN9252 driver into the Yocto build system, compile it as a kernel module, apply the necessary device tree changes, and ensure it loads automatically at boot.

Best regards, Toradex AI Assistant.

Hello @ToradexAI and community,

As a starting point, I created a custom Yocto layer with a very simple “Hello World” kernel module. The image builds fine after adding my layer to bblayers.conf.

So here is how the file structure looks like:
udaykumar@SYS-TK01:~/Verdin/layers$ tree meta-teckinsoft-lan9252/

meta-teckinsoft-lan9252/
├── conf
│   └── layer.conf
├── COPYING.MIT
├── README
└── recipes-kernel
    ├── hello-mod
    │   ├── files
    │   │   ├── hello.c
    │   │   └── Makefile
    │   └── hello-mod_1.0.bb
    └── linux
        ├── device-tree-overlays-ti
        │   └── verdin-am62-lan9252.dts
        └── device-tree-overlays-ti_%.bbappend

hello.c

#include <linux/module.h>
#include <linux/kernel.h>

static int __init hello_init(void)
{
    printk(KERN_INFO "Hello World from Verdin AM62!\n");
    return 0;
}

static void __exit hello_exit(void)
{
    printk(KERN_INFO "Goodbye from Verdin AM62!\n");
}

module_init(hello_init);
module_exit(hello_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Teckinsoft");
MODULE_DESCRIPTION("Simple Hello World kernel module");

Makefile

obj-m += hello.o

all:
    $(MAKE) -C $(KERNEL_SRC) M=$(PWD) modules

clean:
    $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean

hello-mod_1.0.bb

SUMMARY = "Simple Hello World kernel module"
DESCRIPTION = "A basic kernel module for testing Yocto build system"
LICENSE = "GPL-2.0-only"
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/GPL-2.0-only;md5=801f80980d171dd6425610833a22dbe6"

inherit module

SRC_URI = " \
    file://hello.c \
    file://Makefile \
"

S = "${WORKDIR}"

do_compile() {
    oe_runmake -C ${STAGING_KERNEL_DIR} M=${S} modules
}

do_install() {
    install -d ${D}${nonarch_base_libdir}/modules/${KERNEL_VERSION}/kernel/drivers/misc
    install -m 0644 ${S}/hello.ko ${D}${nonarch_base_libdir}/modules/${KERNEL_VERSION}/kernel/drivers/misc/
}

FILES:${PN} += "${nonarch_base_libdir}/modules/${KERNEL_VERSION}/kernel/drivers/misc/hello.ko"

RPROVIDES:${PN} += "kernel-module-hello"

Now I’d like to know:

  • How can I check on the target whether this module has been built and included in the image?
  • If it is not loaded automatically, what is the recommended way to manually load the module on the Verdin AM62?
  • Once loaded, how can I verify the printk messages (e.g. “Hello World from Verdin AM62!”) to confirm that the module is working?
  • is that with dmesg

Thanks in advance!

Thanks for your response.

Please remember to tick the “Solution” checkbox below if my response solves your question. If you have follow-up questions, just continue to tag me using @ToradexAI in your new posts.

Did you like my answer? :+1: :-1:


Verifying Your Hello World Kernel Module

Great work on creating your custom Yocto layer! Your module structure looks correct. Here’s how to verify and test your kernel module on the Verdin AM62:

1. Check if the Module is Built and Included

First, verify that your module was built and included in the image:

# Check if the module file exists
$ find /lib/modules -name "hello.ko"

(1)

You should see the module file at:

/lib/modules/<kernel-version>/kernel/drivers/misc/hello.ko

(1)

2. Load the Module Manually

To manually load your kernel module, use the modprobe command:

$ sudo modprobe hello

(2)

Alternatively, you can use insmod with the full path:

$ sudo insmod /lib/modules/$(uname -r)/kernel/drivers/misc/hello.ko

(1)

3. Verify the Module is Loaded

Check if your module is loaded using:

$ lsmod | grep hello

(2)

This should show your module in the list of loaded kernel modules:

Module                  Size  Used by
hello                  16384  0

(2)

4. View the printk Messages

Yes, you’re correct! Use dmesg to view the kernel log messages from your module:

$ dmesg | grep "Hello World"

(3)

You should see output like:

[  XXX.XXXXXX] Hello World from Verdin AM62!

(3)

To see the most recent kernel messages in real-time, you can also use:

$ dmesg -w

5. Unload the Module (for testing)

To test the exit message, unload the module:

$ sudo rmmod hello

(2)

Then check dmesg again to see the “Goodbye from Verdin AM62!” message:

$ dmesg | tail

(3)

Additional Verification Commands

You can also check the module information:

$ modinfo hello

This will show details about your module including the description, author, and license information you specified in your source code.

Troubleshooting Tips

If your module isn’t found:

  1. Verify that your layer was properly added to bblayers.conf
  2. Check that the module was included in your image recipe with IMAGE_INSTALL:append = " kernel-module-hello"
  3. Ensure the build completed successfully without errors

Your module structure and recipe look correct, so following these steps should allow you to verify that your Hello World kernel module is working properly on the Verdin AM62.

Best regards, Toradex AI Assistant.

Hello Community!

as i mentioned that in the 3rd message i followed the step mentioned in the site
load_hello_module
and then i added that to hello-mod to local.conf by using image_install_append
but its giving error like below

Sstate summary: Wanted 525 Local 516 Mirrors 0 Missed 9 Current 5761 (98% match, 99% complete)##############################################################################                 | ETA:  0:00:00
Removing 8 stale sstate objects for arch verdin_am62: 100% |#################################################################################################################################| Time: 0:00:00
NOTE: Executing Tasks
ERROR: tdx-reference-multimedia-image-1.0-r0 do_rootfs: Unable to install packages. Command '/home/udaykumar/Verdin/build/tmp/work/verdin_am62-tdx-linux/tdx-reference-multimedia-image/1.0/recipe-sysroot-native/usr/bin/opkg --volatile-cache -f /home/udaykumar/Verdin/build/tmp/work/verdin_am62-tdx-linux/tdx-reference-multimedia-image/1.0/opkg.conf -t /home/udaykumar/Verdin/build/tmp/work/verdin_am62-tdx-linux/tdx-reference-multimedia-image/1.0/temp/ipktemp/ -o /home/udaykumar/Verdin/build/tmp/work/verdin_am62-tdx-linux/tdx-reference-multimedia-image/1.0/rootfs  --force_postinstall --prefer-arch-to-version   install bash connman connman-client connman-plugin-ethernet connman-plugin-loopback connman-plugin-wifi coreutils evtest gpicview less libdrm libgpiod-tools libinput makedevs media-files mime-support nano net-tools opkg packagegroup-base-extended packagegroup-base-tdx-cli packagegroup-basic packagegroup-boot packagegroup-core-boot packagegroup-core-weston packagegroup-fsl-isp packagegroup-machine-tdx-cli packagegroup-tdx-cli packagegroup-tdx-graphical packagegroup-tdx-qt5 packagegroup-tpm2-tdx-cli packagegroup-wifi-fw-tdx-cli packagegroup-wifi-tdx-cli python3 python3-pip python3-pyqt5 python3-spidev run-postinsts spidev-test systemd-analyze timestamp-service udev-extraconf util-linux v4l-utils wayland-qtdemo-launch-analogclock weston weston-xwayland xterm' returned 1:
 * Solver encountered 1 problem(s):
 * Problem 1/1:
 *   - package packagegroup-base-1.0-r0.verdin_am62 requires packagegroup-machine-base, but none of the providers can be installed
 *   - package packagegroup-base-extended-1.0-r0.verdin_am62 requires packagegroup-base, but none of the providers can be installed
 *   - package packagegroup-machine-base-1.0-r0.verdin_am62 requires hello-mod, but none of the providers can be installed
 *   - conflicting requests
 *   - nothing provides kernel-module-hello-6.6.94-7.3.0-devel needed by hello-mod-1.0-r0.verdin_am62
 * 
 * Solution 1:
 *   - do not ask to install a package providing packagegroup-base-extended



ERROR: Logfile of failure stored in: /home/udaykumar/Verdin/build/tmp/work/verdin_am62-tdx-linux/tdx-reference-multimedia-image/1.0/temp/log.do_rootfs.1934745
ERROR: Task (/home/udaykumar/Verdin/build/../layers/meta-toradex-demos/recipes-images/images/tdx-reference-multimedia-image.bb:do_rootfs) failed with exit code '1'
NOTE: Tasks Summary: Attempted 13395 tasks of which 13384 didn't need to be rerun and 1 failed.
NOTE: Writing buildhistory
NOTE: Writing buildhistory took: 3 seconds
NOTE: Writing buildhistory
NOTE: Writing buildhistory took: 3 seconds

Summary: 1 task failed:
  /home/udaykumar/Verdin/build/../layers/meta-toradex-demos/recipes-images/images/tdx-reference-multimedia-image.bb:do_rootfs
    log: /home/udaykumar/Verdin/build/tmp/work/verdin_am62-tdx-linux/tdx-reference-multimedia-image/1.0/temp/log.do_rootfs.1934745
Summary: There was 1 ERROR message, returning a non-zero exit code.
udaykumar@SYS-TK01:~/Verdin/layers/meta-teckinsoft-carrier-board/conf/machine$ bitbake -e virtual/kernel | grep "^PV="

Kindly help where exactly i am doing wrong

Best regards.
Uday