Bringing Up the LSM6DSL IMU on Raspberry Pi: DT Overlays, IIO, FIFO, and the No-IRQ Reality

Bringing Up the LSM6DSL IMU on Raspberry Pi: DT Overlays, IIO, FIFO, and the No-IRQ Reality

Introduction

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:

  • Device Tree overlay correctness
  • IIO triggers and scan elements
  • FIFO frame formatting
  • Kernel probe mechanics
  • Timing behavior in systems with no interrupt signaling

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.


Diagram — Where the Failure Happens

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.

flowchart LR %%{init: {'layout': {'algorithm': 'elk'}}}%% IMU["LSM6DSL IMU (FIFO)"] --> I2C["I²C Bus"] I2C --> Kernel["st_lsm6dsx_i2c Driver"] Kernel --> IIO["IIO Core\n(scan elements, triggers, buffer)"] IIO --> User["User Space"] Kernel -. missing IRQ .-> IIO

1. Hardware Reality — No IRQ ⇒ No IIO Streaming

The BerryIMU v4 does not connect INT1/INT2 to Raspberry Pi GPIO pins.

IIO depends entirely on events (watermark IRQs).
Without interrupts:

  • No trigger
  • No FIFO flush events
  • No sample push into /dev/iio:deviceX
  • Reads always return 0 bytes

This is the root cause of the issue.


2. Device Tree Overlay — Debugging a Wrong Target Path

Incorrect overlay (legacy Pi DT paths)

fragment@0 {
    target-path = "/soc/i2c@1";
    __overlay__ {
        imu@6a {
            compatible = "st,lsm6dsl";
            reg = <0x6a>;
        };
    };
};

Correct overlay for Raspberry Pi 5 (RP1 I²C controller)

fragment@0 {
    target-path = "/soc@107c000000/i2c@7d508200";
    __overlay__ {
        #address-cells = <1>;
        #size-cells = <0>;

        imu@6a {
            compatible = "st,lsm6dsl";
            reg = <0x6a>;
            status = "okay";
        };
    };
};

Building and loading

dtc -@ -I dts -O dtb lsm6dsl-overlay.dts -o lsm6dsl.dtbo
sudo cp lsm6dsl.dtbo /boot/firmware/overlays/

And in config.txt:

dtoverlay=lsm6dsl

3. Debugging Using journalctl — How We Found the Wrong Path

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:

  • Overlay syntax was valid
  • The target-path pointed to a non-existing node
  • No IMU node was added to the live device tree

Fixing the target-path solved overlay loading (only this part).


4. Verifying the Driver Probe with dmesg

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:

  • Driver loads
  • Regulators substituted correctly
  • I²C read fails → wrong address, bad wiring, or sensor unpowered

Overlay does not create hardware — it only describes it.


5. IIO Devices Appear — But /dev/iio Returns Zero Bytes

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.


6. sysfs Raw Reads Work — But Are Too Slow

/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.


7. LSM6DSL FIFO Structure — What Linux Expects

FIFO frames include:

  • Gyro XYZ
  • Accel XYZ
  • Optional timestamp

IIO does NOT return a prepared struct — you must assemble samples manually
(based on scan_elements).


8. Why Polling Without IRQ Creates Jitter

Causes:

  • Linux scheduler
  • I²C bus latency
  • FIFO producer/consumer mismatch
  • No deterministic watermark events

9. Final Solution — User-Space FIFO Polling

Because the IIO pipeline cannot run without IRQ, the working solution is:

IMU FIFO → I²C → User-Space Poller → Application

Responsibilities:

  • Poll FIFO_STATUS
  • Read FIFO_DATA_OUT
  • Parse IMU frame layout
  • Timestamp in software
  • Provide stable 200–400 Hz sampling

This avoids IIO entirely — but works reliably.


10. Working With the IIO Driver (When IRQ Exists)

Even though FIFO cannot work here, it is useful to understand IIO operation.

Enable scan elements

echo 1 > in_accel_x_en
echo 1 > in_accel_y_en
echo 1 > in_accel_z_en
echo 1 > in_timestamp_en

Build your matching C struct

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
...

11. Building the Driver Out-of-Tree

To debug and instrument the driver, an out-of-tree module was built.

Relevant kernel sources copied:

drivers/iio/imu/st_lsm6dsx/

Makefile

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

Install module

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.


IMU Integration Script — Loading the Driver and Binding the Device

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

What the script does

  • Loads the core IIO modules required for any buffered or triggered operation
    (industrialio, industrialio-triggered-buffer, kfifo_buf).
  • Loads the regmap-i2c backend used by many sensors including LSM6DSL.
  • Loads the custom-built st_lsm6dsx module (out-of-tree version).
  • Performs a manual device instantiation by writing to
    /sys/bus/i2c/devices/i2c-1/new_device, bypassing the Device Tree.
  • Ensures the IMU is registered as:
    • 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.

Conclusion

This bring-up showed that:

  • IIO is event-driven, not polling-based
  • Missing IRQs fundamentally break the FIFO → buffer → user pipeline
  • Device Tree overlays must match the RP1 hardware hierarchy precisely
  • Even when the driver loads, I²C may still fail (probe debugging is essential)
  • A custom user-space FIFO poller is the only reliable solution on BerryIMU v4

In Part 2, we implement the full C-based FIFO polling engine,
including timestamp handling and jitter measurement.


References

  1. ST LSM6DSL Datasheet
  2. Linux IIO Documentation
  3. Linux Device Tree Documentation
  4. Raspberry Pi RP1 Peripheral Docs
  5. BerryIMU Quick Start Guide
  6. i2c-tools
  7. LSM6DSL Linux Driver Source

Comments
comments powered by Disqus




Archives

2025 (9)
2022 (3)
2021 (9)
2020 (18)
2014 (4)