Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions drivers/sensor/tdk/icm45686/icm45686.h
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,15 @@ struct icm45686_stream {
bool fifo_full: 1;
} events;
} data;
/* Counts of interrupts dropped by each ignore path, summarized at
* most once per report interval. Per-instance so a multi-device
* system attributes and rate-limits each sensor independently.
*/
struct {
atomic_t busy_ignored;
atomic_t no_submission_ignored;
int64_t report_deadline;
} ignore_stats;
};

struct icm45686_data {
Expand Down
49 changes: 45 additions & 4 deletions drivers/sensor/tdk/icm45686/icm45686_stream.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,34 @@ enum icm45686_stream_state {
ICM45686_STREAM_BUSY = 2,
};

/*
* Both ignore paths in icm45686_event_handler can recur on every interrupt
* while a stream is stalled. Logging each occurrence floods the backend and
* can starve the very threads that would clear the stall, so count the
* occurrences and emit at most one summary per second.
*/
static void icm45686_report_ignored_events(struct icm45686_data *data)
{
int64_t now = k_uptime_get();
uint32_t busy;
uint32_t no_sub;

if (now < data->stream.ignore_stats.report_deadline) {
return;
}
data->stream.ignore_stats.report_deadline = now + 1000;

busy = (uint32_t)atomic_set(&data->stream.ignore_stats.busy_ignored, 0);
no_sub = (uint32_t)atomic_set(&data->stream.ignore_stats.no_submission_ignored, 0);

if (busy != 0U) {
LOG_WRN("Ignored %u interrupt(s): event while a stream was in progress", busy);
}
if (no_sub != 0U) {
LOG_WRN("Ignored %u interrupt(s): callback before a streaming submission", no_sub);
}
}

static struct sensor_stream_trigger *get_read_config_trigger(const struct sensor_read_config *cfg,
enum sensor_trigger_type trig)
{
Expand Down Expand Up @@ -187,13 +215,18 @@ static void icm45686_event_handler(const struct device *dev)
{
struct icm45686_data *data = dev->data;
const struct icm45686_config *cfg = dev->config;
const struct sensor_read_config *read_cfg = data->stream.iodev_sqe->sqe.iodev->data;
const struct sensor_read_config *read_cfg;
uint8_t val = 0;
uint64_t cycles;
int err;

if (!data->stream.iodev_sqe ||
FIELD_GET(RTIO_SQE_CANCELED, data->stream.iodev_sqe->sqe.flags)) {
if (!data->stream.iodev_sqe) {
(void)atomic_inc(&data->stream.ignore_stats.no_submission_ignored);
icm45686_report_ignored_events(data);
return;
}

if (FIELD_GET(RTIO_SQE_CANCELED, data->stream.iodev_sqe->sqe.flags)) {
LOG_WRN("Callback triggered with no streaming submission - Disabling interrupts");
(void)atomic_set(&data->stream.state, ICM45686_STREAM_OFF);
(void)gpio_pin_interrupt_configure_dt(&cfg->int_gpio, GPIO_INT_DISABLE);
Expand All @@ -211,9 +244,16 @@ static void icm45686_event_handler(const struct device *dev)
data->stream.settings.enabled.fifo_full = false;
return;
}
read_cfg = data->stream.iodev_sqe->sqe.iodev->data;

if (atomic_cas(&data->stream.state, ICM45686_STREAM_ON, ICM45686_STREAM_BUSY) == false) {
LOG_WRN("Event handler triggered while a stream is in progress! Ignoring");
/*
* A data-ready edge arrived while the previous readout was still
* in flight. Drop the event but intentionally leave the DRDY
* interrupt armed so the next edge after completion is serviced.
*/
(void)atomic_inc(&data->stream.ignore_stats.busy_ignored);
icm45686_report_ignored_events(data);
return;
}

Expand Down Expand Up @@ -570,6 +610,7 @@ int icm45686_stream_init(const struct device *dev)
LOG_ERR("Failed to configure interrupt");
}

memset(&int_config, INV_IMU_DISABLE, sizeof(int_config));
err = icm456xx_set_config_int(&data->driver, INV_IMU_INT1, &int_config);
if (err) {
LOG_ERR("Failed to disable all INTs");
Expand Down
15 changes: 15 additions & 0 deletions tests/drivers/sensor/icm45686_stream/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Copyright (c) 2026 CogniPilot Foundation
# SPDX-License-Identifier: Apache-2.0

cmake_minimum_required(VERSION 3.28.0)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(icm45686_stream)

target_include_directories(app PRIVATE
${ZEPHYR_BASE}/drivers/sensor/tdk/icm45686
${ZEPHYR_HAL_TDK_MODULE_DIR}
${ZEPHYR_HAL_TDK_MODULE_DIR}/common
${ZEPHYR_HAL_TDK_MODULE_DIR}/icm456xx
${ZEPHYR_HAL_TDK_MODULE_DIR}/icm456xx/icm456xx_h
)
target_sources(app PRIVATE src/main.c)
12 changes: 12 additions & 0 deletions tests/drivers/sensor/icm45686_stream/Kconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Copyright (c) 2026 CogniPilot Foundation
# SPDX-License-Identifier: Apache-2.0

source "Kconfig.zephyr"

# The test compiles the streaming translation unit directly, without a
# device-tree instance of the sensor, so the driver's own dependency chain
# for this symbol is not active. Provide the value here so the shared
# driver data layout selects its streaming fields.
config ICM45686_STREAM
bool
default y
5 changes: 5 additions & 0 deletions tests/drivers/sensor/icm45686_stream/prj.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
CONFIG_ZTEST=y
CONFIG_GPIO=y
CONFIG_SENSOR=y
CONFIG_SENSOR_ASYNC_API=y
CONFIG_SENSOR_CLOCK_SYSTEM=y
50 changes: 50 additions & 0 deletions tests/drivers/sensor/icm45686_stream/src/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Copyright (c) 2026 CogniPilot Foundation
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <zephyr/device.h>
#include <zephyr/ztest.h>

#include "icm45686_stream.c"

int icm45686_prep_reg_read_rtio_async(const struct icm45686_bus *bus, uint8_t reg, uint8_t *buf,
size_t size, struct rtio_sqe **out)
{
ARG_UNUSED(bus);
ARG_UNUSED(reg);
ARG_UNUSED(buf);
ARG_UNUSED(size);
ARG_UNUSED(out);
return -ENOTSUP;
}

int icm45686_prep_reg_write_rtio_async(const struct icm45686_bus *bus, uint8_t reg,
const uint8_t *buf, size_t size, struct rtio_sqe **out)
{
ARG_UNUSED(bus);
ARG_UNUSED(reg);
ARG_UNUSED(buf);
ARG_UNUSED(size);
ARG_UNUSED(out);
return -ENOTSUP;
}

ZTEST(icm45686_stream, test_early_irq_without_submission_is_ignored)
{
static const struct icm45686_config config;
struct icm45686_data data = {0};
const struct device dev = {
.name = "icm45686-test",
.config = &config,
.data = &data,
};

icm45686_event_handler(&dev);

zassert_is_null(data.stream.iodev_sqe);
zassert_equal(atomic_get(&data.stream.state), ICM45686_STREAM_OFF);
}

ZTEST_SUITE(icm45686_stream, NULL, NULL, NULL, NULL, NULL);
7 changes: 7 additions & 0 deletions tests/drivers/sensor/icm45686_stream/tests.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
tests:
drivers.sensor.icm45686_stream.early_irq:
tags:
- drivers
- sensor
platform_allow:
- native_sim
Loading