Building the ADIN1110 Linux Kernel Module on RPi

Building the ADIN1110 Linux Kernel Module on Raspberry Pi
This article shows how to build the ADIN1110 10BASE-T1L Ethernet MAC-PHY Linux driver as an external kernel module on a Raspberry Pi.
The goal is simple:
- use Raspberry Pi OS 64-bit
- use the running Raspberry Pi kernel
- download the matching ADIN1110 driver source
- build
adin1110.ko - install it into
/lib/modules - load it with
modprobe
This is useful when the driver is not available as a ready-to-use module in the installed Raspberry Pi kernel.
1. Why Build Only the Module?
Many tutorials explain how to rebuild the complete Raspberry Pi kernel.
That is useful for learning, but it is not always practical for deployment.
For many use cases, it is better to build only the required kernel module:
Manual setup for understanding.
Installer-based setup for deployment.
This approach avoids a full kernel rebuild and keeps the system close to the installed Raspberry Pi OS kernel.
2. Background
The ADIN1110 is a 10BASE-T1L Ethernet MAC-PHY from Analog Devices.
On Linux, the ADIN1110 driver is located in the kernel source tree under:
drivers/net/ethernet/adi/adin1110.c
Instead of rebuilding the complete Raspberry Pi kernel, the driver can be built as an external kernel module.
This is helpful for:
- development
- testing
- installer-based deployment
- Raspberry Pi based industrial Ethernet gateways
3. Related Work
There is already a good article that explains the ADIN1110 kernel configuration and overlay setup for Raspberry Pi:
https://www.hackster.io/pier8283/adin1110-linux-kernel-configuration-for-raspberry-pi-2f4a26
That article focuses on configuring and rebuilding the Raspberry Pi kernel.
This article focuses on a different approach:
Build only the ADIN1110 driver as an external kernel module.
This can be useful when the Raspberry Pi kernel itself should stay unchanged.
4. Requirements
This setup is intended for:
Raspberry Pi OS 64-bit
Raspberry Pi OS 64-bit is required because the needed networking kernel configuration is already available there.
The script also requires:
Linux kernel >= 6.12
Required tools:
uname
wget
make
gcc
sudo
The matching kernel build directory must exist:
/lib/modules/$(uname -r)/build
On Raspberry Pi OS this usually comes from the installed kernel headers.
5. Install Required Packages
Install the required build tools:
sudo apt update
sudo apt install -y raspberrypi-kernel-headers build-essential wget
Check that the kernel build directory exists:
ls -l /lib/modules/$(uname -r)/build
Expected result:
/lib/modules/.../build -> /usr/src/...
6. Build Script
Create a file:
mkdir -p scripts/adin1110
vim scripts/adin1110/build_adin1110_module.sh
Insert the following script:
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
die() {
echo "Error: $1" >&2
exit 1
}
say() {
step="$1"
msg="$2"
printf "%s: %s\n" "$step" "$msg"
}
require_command() {
cmd="$1"
command -v "$cmd" >/dev/null 2>&1 || die "Required command not found: $cmd"
}
STEP="STEP0"
say "$STEP" "Checking required tools"
require_command uname
require_command wget
require_command make
require_command gcc
require_command sudo
KVER="$(uname -r)"
KDIR="/lib/modules/$KVER/build"
say "$STEP" "Running kernel: $KVER"
say "$STEP" "Kernel build directory: $KDIR"
STEP="STEP1"
say "$STEP" "Checking kernel compatibility"
case "$KVER" in
6.1[2-9].*|6.[2-9]*)
;;
*)
die "Unsupported kernel version: $KVER (requires >= 6.12)"
;;
esac
[ -d "$KDIR" ] || die "Kernel build directory not found: $KDIR"
[ -f "$KDIR/include/generated/autoconf.h" ] || die "Missing autoconf.h in $KDIR"
[ -f "$KDIR/Makefile" ] || die "Missing kernel Makefile in $KDIR"
STEP="STEP2"
say "$STEP" "Preparing build directory"
BUILD_DIR="${REPO_ROOT}/build/adin1110"
rm -rf "$BUILD_DIR"
mkdir -p "$BUILD_DIR"
STEP="STEP3"
say "$STEP" "Downloading sources"
KMAJOR="$(echo "$KVER" | cut -d. -f1)"
KMINOR="$(echo "$KVER" | cut -d. -f2)"
KBRANCH="rpi-${KMAJOR}.${KMINOR}.y"
BASE_URL="https://raw.githubusercontent.com/raspberrypi/linux/${KBRANCH}/drivers/net/ethernet/adi"
say "$STEP" "Using Raspberry Pi kernel branch: $KBRANCH"
wget -nv -O "$BUILD_DIR/adin1110.c" "$BASE_URL/adin1110.c"
STEP="STEP4"
say "$STEP" "Creating Makefile"
printf 'obj-m := adin1110.o\n\nall:\n\t$(MAKE) -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules\n\nclean:\n\t$(MAKE) -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean\n' > "$BUILD_DIR/Makefile"
STEP="STEP5"
say "$STEP" "Building module"
make -C "$KDIR" M="$BUILD_DIR" modules
[ -f "$BUILD_DIR/adin1110.ko" ] || die "adin1110.ko was not created"
STEP="STEP6"
say "$STEP" "Installing module"
sudo install -D -m 644 "$BUILD_DIR/adin1110.ko" "/lib/modules/$KVER/updates/adin1110.ko"
sudo depmod "$KVER"
STEP="STEP7"
say "$STEP" "Done"
say "$STEP" "Try loading with:"
say "$STEP" "sudo modprobe adin1110"
Make the script executable:
chmod +x scripts/adin1110/build_adin1110_module.sh
Run it:
./scripts/adin1110/build_adin1110_module.sh
7. What the Script Does
The script performs the following steps:
- check the required build tools
- detect the running kernel version
- check the kernel build directory
- download the matching ADIN1110 driver source
- create a minimal module Makefile
- build
adin1110.ko - install the module
- run
depmod
8. STEP0: Check Required Tools
The script first checks that the required command line tools are available:
require_command uname
require_command wget
require_command make
require_command gcc
require_command sudo
It also reads the currently running kernel version:
KVER="$(uname -r)"
KDIR="/lib/modules/$KVER/build"
The module is always built against the currently running kernel.
This is important because Linux kernel modules must match the kernel they are loaded into.
9. STEP1: Check Kernel Compatibility
The script requires Linux kernel version 6.12 or newer:
case "$KVER" in
6.1[2-9].*|6.[2-9]*)
;;
*)
die "Unsupported kernel version: $KVER (requires >= 6.12)"
;;
esac
It also checks that the kernel build files exist:
[ -d "$KDIR" ] || die "Kernel build directory not found: $KDIR"
[ -f "$KDIR/include/generated/autoconf.h" ] || die "Missing autoconf.h in $KDIR"
[ -f "$KDIR/Makefile" ] || die "Missing kernel Makefile in $KDIR"
Without these files, external kernel module builds are not possible.
10. STEP2: Prepare the Build Directory
The module is built in:
build/adin1110
The directory is removed and recreated on every run:
BUILD_DIR="${REPO_ROOT}/build/adin1110"
rm -rf "$BUILD_DIR"
mkdir -p "$BUILD_DIR"
This avoids problems with old object files from previous builds.
11. STEP3: Download the ADIN1110 Driver Source
The script detects the kernel major and minor version:
KMAJOR="$(echo "$KVER" | cut -d. -f1)"
KMINOR="$(echo "$KVER" | cut -d. -f2)"
KBRANCH="rpi-${KMAJOR}.${KMINOR}.y"
For example:
6.12.x -> rpi-6.12.y
6.13.x -> rpi-6.13.y
Then it downloads the ADIN1110 driver from the matching Raspberry Pi kernel branch:
BASE_URL="https://raw.githubusercontent.com/raspberrypi/linux/${KBRANCH}/drivers/net/ethernet/adi"
wget -nv -O "$BUILD_DIR/adin1110.c" "$BASE_URL/adin1110.c"
This keeps the driver source close to the running Raspberry Pi kernel version.
12. STEP4: Create the Module Makefile
The script creates a minimal external kernel module Makefile:
obj-m := adin1110.o
all:
$(MAKE) -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
$(MAKE) -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
This tells the kernel build system to build:
adin1110.ko
from:
adin1110.c
13. STEP5: Build the Kernel Module
The module is built with:
make -C "$KDIR" M="$BUILD_DIR" modules
This means:
-C "$KDIR"
Use the running kernel build directory.
M="$BUILD_DIR"
Build the external module from the ADIN1110 build directory.
After the build, the script checks that the module exists:
[ -f "$BUILD_DIR/adin1110.ko" ] || die "adin1110.ko was not created"
The expected output file is:
build/adin1110/adin1110.ko
14. STEP6: Install the Module
The module is installed into:
/lib/modules/$(uname -r)/updates/adin1110.ko
The script uses:
sudo install -D -m 644 "$BUILD_DIR/adin1110.ko" "/lib/modules/$KVER/updates/adin1110.ko"
sudo depmod "$KVER"
depmod updates the kernel module dependency database.
After this step, the module can be loaded by name.
15. STEP7: Load the Module
Load the module with:
sudo modprobe adin1110
Check that it is loaded:
lsmod | grep adin1110
Check kernel messages:
dmesg | tail -n 100
16. Device Tree Overlay
The kernel module alone is not enough.
The Raspberry Pi also needs a matching Device Tree overlay that describes the ADIN1110 SPI device.
Typical information required in the overlay:
- SPI bus
- chip select
- interrupt GPIO
- reset GPIO, if used
- compatible string for ADIN1110
- SPI frequency
- MAC address
The MAC address must be provided in the Device Tree.
If no valid MAC address is configured, the driver may fail during initialization or print errors in dmesg.
Example structure:
/dts-v1/;
/plugin/;
/ {
compatible = "brcm,bcm2835";
fragment@0 {
target = <&spi0>;
__overlay__ {
status = "okay";
adin1110: ethernet@0 {
compatible = "adi,adin1110";
reg = <0>;
spi-max-frequency = <25000000>;
interrupt-parent = <&gpio>;
interrupts = <25 8>;
local-mac-address = [ 02 00 00 00 00 01 ];
status = "okay";
};
};
};
};
The exact GPIO numbers depend on the hardware design.
The MAC address above is only an example. For real products, each device should use a unique and valid MAC address.
Device Tree overlays can also be built directly on the Raspberry Pi target.
If there is interest, I may write a separate article about building and installing Raspberry Pi Device Tree overlays on the target system.
17. Test the Network Interface
After loading the module and enabling the overlay, reboot:
sudo reboot
After reboot, check network interfaces:
ip link
You should see a new Ethernet interface.
Example:
eth1
or:
end0
Bring the interface up:
sudo ip link set eth1 up
Check link state:
ip addr show eth1
dmesg | grep -i adin
18. Troubleshooting
Kernel headers missing
Error:
Kernel build directory not found
Fix:
sudo apt install -y raspberrypi-kernel-headers
Module does not load
Check:
sudo modprobe adin1110
dmesg | tail -n 100
Possible causes:
- wrong kernel version
- module built for another kernel
- missing dependency
- incompatible kernel source version
SPI device not detected
Check whether SPI is enabled:
ls /dev/spidev*
Enable SPI:
sudo raspi-config
Then enable:
Interface Options -> SPI
Reboot afterwards.
Device Tree overlay missing or wrong
If the module loads but no network interface appears, the Device Tree overlay is usually missing or incorrect.
Check:
dmesg | grep -i spi
dmesg | grep -i adin
MAC address missing
The ADIN1110 needs a valid MAC address.
If the Device Tree does not provide a MAC address, the driver may fail or report errors.
Check kernel messages:
dmesg | grep -i mac
dmesg | grep -i adin
Fix the Device Tree overlay and add a valid local-mac-address.
19. Clean Build
To remove the local build directory:
rm -rf build/adin1110
To remove the installed module:
sudo rm -f /lib/modules/$(uname -r)/updates/adin1110.ko
sudo depmod "$(uname -r)"
20. Source of the Build Script
The build script used in this article is based on the open BE-IIS installer project:
https://github.com/be-iis/be-iis-installer/
The installer does more than just build the ADIN1110 module.
It is intended to prepare BE-IIS HAT++ boards on Raspberry Pi systems. It can install the required modules, overlays and configuration files for supported HATs.
The goal is that the user does not have to do this manual work for normal deployment.
The manual process in this article is mainly useful for understanding what happens under the hood.
Everything is open and traceable.
21. BE-IIS HAT++ Installer
BE-IIS offers Raspberry Pi HAT++ boards for industrial interfaces, including Single Pair Ethernet boards.
For deployment, BE-IIS provides an installer that prepares the supported HATs automatically.
The installer is designed to:
- install required kernel modules
- install required Device Tree overlays
- prepare supported BE-IIS HAT++ boards
- detect supported HATs automatically
- reduce manual configuration work
This means that users of supported BE-IIS HAT++ boards normally do not need to build the module manually.
The manual steps in this article are still useful if you want to understand the Linux integration in detail.
22. Summary
This method builds the ADIN1110 driver as an external Raspberry Pi kernel module.
It avoids rebuilding the complete kernel and keeps the process simple:
./scripts/adin1110/build_adin1110_module.sh
sudo modprobe adin1110
The module build requires:
- Raspberry Pi OS 64-bit
- matching Raspberry Pi kernel headers
- Linux kernel 6.12 or newer
- ADIN1110 driver source from the matching Raspberry Pi kernel branch
- a correct Device Tree overlay
- a valid MAC address in the Device Tree
This approach is useful for manual setup, debugging and understanding the driver integration.
For deployment, the same logic is integrated into the open BE-IIS installer.
References
-
ADIN1110 10BASE-T1L Ethernet MAC-PHY, Analog Devices / DigiKey https://www.digikey.de/de/product-highlight/a/analog-devices/adin1110-10base-t1l-ethernet-mac-phy
-
ADIN1110 Linux kernel configuration for Raspberry Pi, Hackster.io https://www.hackster.io/pier8283/adin1110-linux-kernel-configuration-for-raspberry-pi-2f4a26
-
BE-IIS Installer https://github.com/be-iis/be-iis-installer/