Bringing Up the LSM6DSL IMU on Raspberry Pi: DT Overlays, IIO, FIFO, and the No-IRQ Reality
Published at December 4, 2025 · 7 min read · Tags: linux iio raspberry-pi device-tree i2c imu embedded driver-development
Bringing Up the LSM6DSL IMU on Raspberry Pi: DT Overlays, IIO, FIFO, and the No-IRQ Reality
Published at December 4, 2025 · 7 min read · Tags: linux iio raspberry-pi device-tree i2c imu embedded driver-development
This post documents the full bring-up of the LSM6DSL IMU on a Raspberry Pi 5 over I²C, in a setup where the interrupt lines (INT1/INT2) are not routed to the board. This seemingly small detail has a major impact on how data can be acquired from the device, and it exposes several implicit assumptions in the Linux IIO subsystem.
Without an IRQ, FIFO reads must be performed by polling over I²C, with no tight synchronization to the sensor’s data-ready events. Although the LSM6DSL FIFO layout is deterministic (Gyro → Accel → …), user space has no guarantee of reading exactly on a frame boundary—especially in the presence of jitter, scheduling delays, or FIFO overruns. This can lead to misalignment, where partial frames are interpreted as full samples.
In contrast, when an IRQ is available and the driver operates in IIO triggered buffer mode, the kernel drains the FIFO precisely at each data-ready or watermark event, collecting full frames and pushing them into an internal ring buffer with consistent timestamps. User space then receives a clean, aligned and well-timed stream of samples.
The bring-up process revealed subtle but important interactions between:
And the key conclusion is simple:
👉 Without an interrupt source, the IIO buffer will not produce streaming data,
so a user-space FIFO polling engine becomes the only practical fallback for
continuous IMU acquisition.
This post walks through the entire process—what worked, what failed, how the driver behaves internally, and why the no-IRQ scenario fundamentally changes the data-acquisition model on Raspberry Pi.
The LSM6DSL IMU communicates over I²C through the st_lsm6dsx_i2c kernel driver, which integrates with the IIO subsystem to expose scan elements, triggers, and buffered data to user space. However, without a valid interrupt line (IRQ) defined in the device tree, the sensor never signals new data availability, causing the IIO buffer and trigger pipeline to stall — the driver loads, but no real samples reach user space.
The BerryIMU v4 does not connect INT1/INT2 to Raspberry Pi GPIO pins.
IIO depends entirely on events (watermark IRQs).
Without interrupts:
/dev/iio:deviceXThis is the root cause of the issue.
fragment@0 {
target-path = "/soc/i2c@1";
__overlay__ {
imu@6a {
compatible = "st,lsm6dsl";
reg = <0x6a>;
};
};
};
fragment@0 {
target-path = "/soc@107c000000/i2c@7d508200";
__overlay__ {
#address-cells = <1>;
#size-cells = <0>;
imu@6a {
compatible = "st,lsm6dsl";
reg = <0x6a>;
status = "okay";
};
};
};
dtc -@ -I dts -O dtb lsm6dsl-overlay.dts -o lsm6dsl.dtbo
sudo cp lsm6dsl.dtbo /boot/firmware/overlays/
And in config.txt:
dtoverlay=lsm6dsl
Overlay loading happens early in boot — so journalctl is essential:
sudo journalctl -k | grep -i overlay
sudo journalctl -k | grep -i fragment
sudo journalctl -k | grep -i failed
This revealed:
overlay: fragment@0: target path not found
overlay: Failed to apply overlay 'lsm6dsl'
This meant:
Fixing the target-path solved overlay loading (only this part).
After correcting the overlay or instantiating manually:
dmesg | grep -i lsm6
Typical probe failure:
st_lsm6dsx: loading out-of-tree module taints kernel.
st_lsm6dsx_i2c 13-006a: supply vdd not found, using dummy regulator
st_lsm6dsx_i2c 13-006a: supply vddio not found, using dummy regulator
st_lsm6dsx_i2c 13-006a: failed to read whoami register
Meaning:
Overlay does not create hardware — it only describes it.
Manual device instantiation:
echo lsm6dsl 0x6a | sudo tee /sys/bus/i2c/devices/i2c-1/new_device
Creates:
iio:device0 → gyro
iio:device1 → accel
But reading fails:
cat /dev/iio:device1
→ returns 0 bytes because no trigger is firing.
/sys/bus/iio/devices/iio:device1/in_accel_y_raw
Each read performs an I²C transaction → 1–40 ms.
This bypasses FIFO and is not suitable for streaming.
FIFO frames include:
IIO does NOT return a prepared struct — you must assemble samples manually
(based on scan_elements).
Causes:
Because the IIO pipeline cannot run without IRQ, the working solution is:
IMU FIFO → I²C → User-Space Poller → Application
Responsibilities:
This avoids IIO entirely — but works reliably.
Even though FIFO cannot work here, it is useful to understand IIO operation.
echo 1 > in_accel_x_en
echo 1 > in_accel_y_en
echo 1 > in_accel_z_en
echo 1 > in_timestamp_en
struct imu_sample {
int16_t accel_x;
int16_t accel_y;
int16_t accel_z;
uint64_t timestamp;
};
Order must match:
scan_elements/in_accel_x_index
scan_elements/in_accel_y_index
...
To debug and instrument the driver, an out-of-tree module was built.
drivers/iio/imu/st_lsm6dsx/
obj-m += st_lsm6dsx.o
st_lsm6dsx-y := \
st_lsm6dsx_core.o \
st_lsm6dsx_buffer.o \
st_lsm6dsx_shub.o \
st_lsm6dsx_i2c.o
KDIR := /lib/modules/$(shell uname -r)/build
all:
$(MAKE) -C $(KDIR) M=$(PWD) modules
clean:
$(MAKE) -C $(KDIR) M=$(PWD) clean
sudo cp st_lsm6dsx.ko /lib/modules/$(uname -r)/extra/
sudo depmod -a
sudo modprobe st_lsm6dsx
This allows fast iteration and printk-based debugging.
To simplify bring-up and testing during development, a small integration script was used to load all required kernel modules and bind the LSM6DSL IMU to the correct I²C bus at runtime. This avoids rebooting after every change to the overlay or the out-of-tree driver, and provides a repeatable initialization sequence.
lsm6-init.sh#!/bin/bash
BUS=1
ADDR=0x6a
CHIP=lsm6dsl
sudo modprobe industrialio
sudo modprobe industrialio-triggered-buffer
sudo modprobe kfifo_buf
sudo modprobe regmap-i2c
echo "Loading IMU driver"
modprobe st_lsm6dsx
sleep 0.3
echo "Binding IMU to I2C bus"
echo "$CHIP $ADDR" > /sys/bus/i2c/devices/i2c-$BUS/new_device
industrialio, industrialio-triggered-buffer, kfifo_buf).regmap-i2c backend used by many sensors including LSM6DSL.st_lsm6dsx module (out-of-tree version)./sys/bus/i2c/devices/i2c-1/new_device, bypassing the Device Tree.iio:device0 (gyro)iio:device1 (accel)This method is extremely useful when the Device Tree overlay is still under development, or when debugging probe failures without reboot cycles.
This bring-up showed that:
In Part 2, we implement the full C-based FIFO polling engine,
including timestamp handling and jitter measurement.